001/*
002 * (C) Copyright 2015 Nuxeo SA (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl-2.1.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     Nicolas Chapurlat <nchapurlat@nuxeo.com>
016 */
017
018package org.nuxeo.ecm.core.io.marshallers.json;
019
020import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
021import static javax.ws.rs.core.MediaType.APPLICATION_JSON_TYPE;
022
023import java.io.IOException;
024import java.io.OutputStream;
025import java.lang.reflect.Type;
026
027import javax.inject.Inject;
028import javax.ws.rs.core.MediaType;
029
030import org.codehaus.jackson.JsonGenerator;
031import org.nuxeo.ecm.core.io.registry.MarshallerRegistry;
032import org.nuxeo.ecm.core.io.registry.MarshallingException;
033import org.nuxeo.ecm.core.io.registry.Writer;
034import org.nuxeo.ecm.core.io.registry.context.RenderingContext;
035import org.nuxeo.ecm.core.io.registry.reflect.Supports;
036
037/**
038 * Base class for Json {@link Writer}.
039 * <p>
040 * This class provides a easy way to produce json and also provides the current context: {@link AbstractJsonWriter#ctx}.
041 * It provides you a {@link JsonGenerator} to manage the marshalling.
042 * </p>
043 * <p>
044 * The use of this class optimize the JsonFactory usage especially when aggregating marshallers.
045 * </p>
046 *
047 * @param <EntityType> The Java type to marshall as Json.
048 * @since 7.2
049 */
050@Supports(APPLICATION_JSON)
051public abstract class AbstractJsonWriter<EntityType> implements Writer<EntityType> {
052
053    /**
054     * The current {@link RenderingContext}.
055     */
056    @Inject
057    protected RenderingContext ctx;
058
059    /**
060     * The marshaller registry.
061     */
062    @Inject
063    protected MarshallerRegistry registry;
064
065    @Override
066    public boolean accept(Class<?> clazz, Type genericType, MediaType mediatype) {
067        return true;
068    }
069
070    @Override
071    public void write(EntityType entity, Class<?> clazz, Type genericType, MediaType mediatype, OutputStream out)
072            throws IOException {
073        JsonGenerator jg = getGenerator(out, true);
074        write(entity, jg);
075        jg.flush();
076    }
077
078    /**
079     * Implement this method to writes the entity in the provided {@link JsonGenerator}.
080     * <p>
081     * This method implementation can use injected properties.
082     * </p>
083     * <p>
084     * The {@link JsonGenerator}'s flushing is done by this abstract class, it's also not not necessary to flush it. Do
085     * not close the provided {@link JsonGenerator}. It may be used is another marshaller calling this one.
086     * </p>
087     *
088     * @param entity The entity to marshall as Json.
089     * @param jg The {@link JsonGenerator} used to produce Json output.
090     * @since 7.2
091     */
092    public abstract void write(EntityType entity, JsonGenerator jg) throws IOException;
093
094    /**
095     * Delegates writing of an entity to the {@link MarshallerRegistry}. This will work if a Json {@link Writer} is
096     * registered in the registry for the given clazz.
097     *
098     * @param fieldName The name of the Json field in which the entity will be wrote.
099     * @param entity The entity to write.
100     * @param jg The {@link JsonGenerator} used to write the given entity.
101     * @since 7.2
102     */
103    protected void writeEntityField(String fieldName, Object entity, JsonGenerator jg) throws IOException {
104        jg.writeFieldName(fieldName);
105        writeEntity(entity, jg);
106    }
107
108    /**
109     * Delegates writing of an entity to the {@link MarshallerRegistry}. This will work if a Json {@link Writer} is
110     * registered in the registry for the given clazz.
111     *
112     * @param entity The entity to write.
113     * @param jg The {@link JsonGenerator} used to write the given entity.
114     * @since 7.2
115     */
116    protected void writeEntity(Object entity, JsonGenerator jg) throws IOException {
117        writeEntity(entity, new OutputStreamWithJsonWriter(jg));
118    }
119
120    /**
121     * Delegates writing of an entity to the {@link MarshallerRegistry}. This will work if a Json {@link Writer} is
122     * registered in the registry for the given clazz.
123     *
124     * @param entity The entity to write.
125     * @param out The {@link OutputStream} in which the given entity will be wrote.
126     * @throws IOException If some i/o error append while writing entity.
127     * @since 7.2
128     */
129    protected <ObjectType> void writeEntity(ObjectType entity, OutputStream out) throws IOException {
130        @SuppressWarnings("unchecked")
131        Class<ObjectType> clazz = (Class<ObjectType>) entity.getClass();
132        Writer<ObjectType> writer = registry.getWriter(ctx, clazz, APPLICATION_JSON_TYPE);
133        if (writer == null) {
134            throw new MarshallingException("Unable to get a writer for Java type " + entity.getClass()
135                    + " and mimetype " + APPLICATION_JSON_TYPE);
136        }
137        writer.write(entity, entity.getClass(), entity.getClass(), APPLICATION_JSON_TYPE, out);
138    }
139
140    /**
141     * Get the current Json generator or create it if none was found.
142     *
143     * @param out The {@link OutputStream} on which the generator will generate Json.
144     * @param getCurrentIfAvailable If true, try to get the current generator in the context.
145     * @return The created generator.
146     * @since 7.2
147     */
148    protected JsonGenerator getGenerator(OutputStream out, boolean getCurrentIfAvailable) throws IOException {
149        if (getCurrentIfAvailable && out instanceof OutputStreamWithJsonWriter) {
150            OutputStreamWithJsonWriter casted = (OutputStreamWithJsonWriter) out;
151            return casted.getJsonGenerator();
152        }
153        return JsonFactoryProvider.get().createJsonGenerator(out);
154    }
155
156}