/******************************************************************************* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * *******************************************************************************/ package org.apache.wink.common.internal.providers.entity.json; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.annotation.Annotation; import java.lang.reflect.Array; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Queue; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.ws.rs.Consumes; import javax.ws.rs.Produces; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.ext.MessageBodyReader; import javax.ws.rs.ext.MessageBodyWriter; import javax.ws.rs.ext.Provider; import javax.ws.rs.ext.Providers; import org.apache.wink.common.internal.providers.entity.xml.AbstractJAXBCollectionProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @Provider @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) public class JAXBArrayJSONProvider extends AbstractJAXBCollectionProvider implements MessageBodyReader<Object[]>, MessageBodyWriter<Object[]> { protected volatile MessageBodyReader<Object> readerProvider = null; protected volatile MessageBodyWriter<Object> writerProvider = null; private static final Logger logger = LoggerFactory .getLogger(JAXBArrayJSONProvider.class); @Context Providers injectedProviders; public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) { Class<?> theType = getParameterizedTypeClassForRead(type, genericType, false); if (theType != null) return (type.isArray() && isJAXBObject(theType, genericType) && !isJAXBElement(theType, genericType)); return false; } public long getSize(Object[] t, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) { return -1; } @SuppressWarnings("unchecked") public Object[] readFrom(Class<Object[]> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, String> httpHeaders, InputStream entityStream) throws IOException, WebApplicationException { Class<?> theType = getParameterizedTypeClassForRead(type, genericType, false); if (this.readerProvider == null) { this.readerProvider = injectedProviders.getMessageBodyReader((Class<Object>)theType, theType, annotations, mediaType); if (logger.isTraceEnabled()) { logger .trace("readerProvider was {} of type {}", System.identityHashCode(readerProvider), readerProvider.getClass().getName()); //$NON-NLS-1$ } } Queue<String> queue = new LinkedList<String>(); List<Object> collection = new ArrayList<Object>(); Pattern p = Pattern.compile("\\S"); //$NON-NLS-1$ Matcher m = null; int next = entityStream.read(); while (next != -1) { m = p.matcher("" + (char)next); //$NON-NLS-1$ if (m.matches() && (char)next != '[') throw new WebApplicationException(500); else if (!m.matches()) next = (char)entityStream.read(); else { // we found the first non-whitespace character is '['. Read the // next character and begin parsing next = entityStream.read(); break; } } // parse the content and deserialize the JSON Object one by one String objectString = ""; //$NON-NLS-1$ while (next != -1) { if (((char)next != ',') || ((char)next == ',' && !queue.isEmpty())) objectString += (char)next; if ((char)next == '{') queue.offer("" + (char)next); //$NON-NLS-1$ else if ((char)next == '}') { queue.poll(); if (queue.isEmpty()) { collection.add(this.readerProvider .readFrom((Class<Object>)theType, theType, annotations, mediaType, httpHeaders, new ByteArrayInputStream(objectString.getBytes()))); objectString = ""; //$NON-NLS-1$ } } next = entityStream.read(); } return (Object[])getArray(theType, collection); } @SuppressWarnings("unchecked") protected static <T> Object getArray(Class<T> type, List<?> collection) { T[] ret = (T[])Array.newInstance(type, collection.size()); int i = 0; for (Object o : collection) { ret[i++] = (T)o; } return ret; } @SuppressWarnings("unchecked") public void writeTo(Object[] t, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException { Class<?> theType = getParameterizedTypeClassForWrite(type, genericType, false); if (this.writerProvider == null) { this.writerProvider = injectedProviders.getMessageBodyWriter((Class<Object>)theType, theType, annotations, mediaType); if (logger.isTraceEnabled()) { logger .trace("writerProvider was {} of type {}", System.identityHashCode(writerProvider), writerProvider.getClass().getName()); //$NON-NLS-1$ } } entityStream.write("[".getBytes()); //$NON-NLS-1$ int i = 0; for (Object o : t) { this.writerProvider.writeTo(o, theType, theType, annotations, mediaType, httpHeaders, entityStream); if ((++i) != t.length) entityStream.write(",".getBytes()); //$NON-NLS-1$ } entityStream.write("]".getBytes()); //$NON-NLS-1$ } public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) { Class<?> theType = getParameterizedTypeClassForWrite(type, genericType, false); if (theType != null) return (isJAXBObject(theType, genericType) && !isJAXBElement(theType, genericType)); return false; } }