/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2010-2015 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* http://glassfish.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package org.glassfish.jersey.media.htmljson;
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.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.client.WebTarget;
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 org.openide.util.lookup.ServiceProvider;
import org.openide.util.lookup.ServiceProviders;
import net.java.html.BrwsrCtx;
import net.java.html.json.Model;
import net.java.html.json.Models;
import net.java.html.json.Property;
/**
* Implementation of Jersey's message body reader and writer that
* can handle reading and writing of JSON models generated by {@link Model}
* annotation provided by
* <a target="_blank" href="http://bck2brwsr.apidesign.org/javadoc/net.java.html.json/">net.java.html.json</a>
* library. Include
* this JAR in your project and you can then use your
* model classes as Jersey's entities.
* <p>
* <pre>
* {@link Model @Model}(className="Query", properties={
* {@link Property @Property}(name="items", type=Item.<b>class</b>, array=true)
* })
* <b>class</b> QueryImpl {
*
* {@link Model @Model}(className="Item", properties={
* {@link Property @Property}(name="id", type=String.<b>class</b>),
* {@link Property @Property}(name="kind", type=Kind.<b>class</b>)
* })
* <b>class</b> ItemImpl {
* }
*
* <b>enum</b> Kind {
* GOOD, BAD
* }
*
* <b>public static</b> List{@code <Item>} doQuery() {
* {@link WebTarget} target = ...;
* Query q = target.request(MediaType.APPLICATION_JSON).get().readEntity(Query.<b>class</b>);
* return q.getItems();
* }
* }
* </pre>
*
* @author Jaroslav Tulach (jtulach at netbeans.org)
*/
@ServiceProviders({
@ServiceProvider(service = MessageBodyWriter.class),
@ServiceProvider(service = MessageBodyReader.class)
})
public final class HtmlJsonProvider implements MessageBodyWriter<Object>, MessageBodyReader<Object> {
@Override
public boolean isWriteable(Class clazz, Type type, Annotation[] antns, MediaType mt) {
if (!mt.isCompatible(MediaType.APPLICATION_JSON_TYPE)) {
return false;
}
if (clazz.isArray()) {
return Models.isModel(clazz.getComponentType());
}
if (java.util.List.class.isAssignableFrom(clazz)) {
if (type instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) type;
Type[] args = pt.getActualTypeArguments();
if (args.length == 1 && args[0] instanceof Class) {
return Models.isModel((Class<?>) args[0]);
}
}
}
return Models.isModel(clazz);
}
@Override
public long getSize(Object t, Class type, Type type1, Annotation[] antns, MediaType mt) {
return -1;
}
@Override
public void writeTo(Object t, Class type, Type type1, Annotation[] antns, MediaType mt, MultivaluedMap mm, OutputStream out)
throws IOException, WebApplicationException {
dump(t, out);
}
private void dump(Object t, OutputStream out) throws IOException {
if (t instanceof Object[]) {
Object[] arr = (Object[]) t;
out.write('[');
for (int i = 0; i < arr.length; i++) {
if (i > 0) {
out.write(',');
}
dump(arr[i], out);
}
out.write(']');
} else {
out.write(t.toString().getBytes("UTF-8"));
}
}
@Override
public boolean isReadable(Class<?> type, Type type1, Annotation[] antns, MediaType mt) {
return isWriteable(type, type1, antns, mt);
}
@Override
public Object readFrom(Class<Object> clazz,
Type type, Annotation[] antns, MediaType mt,
MultivaluedMap<String, String> mm,
InputStream in) throws IOException, WebApplicationException {
BrwsrCtx def = BrwsrCtx.findDefault(HtmlJsonProvider.class);
if (clazz.isArray()) {
List<Object> res = new ArrayList<>();
final Class<?> cmp = clazz.getComponentType();
Models.parse(def, cmp, in, res);
Object[] arr = (Object[]) Array.newInstance(cmp, res.size());
return res.toArray(arr);
}
if (clazz.isAssignableFrom(java.util.List.class)
&& type instanceof ParameterizedType
&& ((ParameterizedType) type).getActualTypeArguments().length == 1
&& ((ParameterizedType) type).getActualTypeArguments()[0] instanceof Class) {
List<Object> res = new ArrayList<>();
final Class<?> cmp = (Class<?>) ((ParameterizedType) type).getActualTypeArguments()[0];
Models.parse(def, cmp, in, res);
return res;
}
return Models.parse(def, clazz, in);
}
}