001/*
002 * (C) Copyright 2015 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     Nicolas Chapurlat <nchapurlat@nuxeo.com>
018 */
019
020package org.nuxeo.ecm.core.io.marshallers.json;
021
022import static javax.ws.rs.core.MediaType.APPLICATION_JSON_TYPE;
023
024import java.io.IOException;
025import java.io.InputStream;
026import java.lang.reflect.Type;
027import java.util.ArrayList;
028import java.util.Iterator;
029import java.util.List;
030
031import org.apache.commons.lang3.reflect.TypeUtils;
032import org.nuxeo.ecm.core.io.registry.MarshallerRegistry;
033import org.nuxeo.ecm.core.io.registry.Reader;
034
035import com.fasterxml.jackson.databind.JsonNode;
036
037/**
038 * Base class to convert json as {@link List}.
039 * <p>
040 * It follow the classic Nuxeo list format :
041 *
042 * <pre>
043 * {
044 *   "entity-type": "GIVEN_ENTITY_TYPE",
045 *   "entries": [
046 *     {...}, &lt;-- A {@link Reader} must be able to manage this format.
047 *     {...},
048 *     ...
049 *     {...}
050 *   ]
051 * }
052 * </pre>
053 *
054 * <p>
055 * This reader delegates the unmarshalling of entries to the {@link MarshallerRegistry}. A Json {@link Reader}
056 * compatible with the required type and the json format must be registered.
057 *
058 * @param <EntityType> The type of the element of this list.
059 * @since 7.2
060 */
061public abstract class DefaultListJsonReader<EntityType> extends EntityJsonReader<List<EntityType>> {
062
063    /**
064     * The Java type of the element of this list.
065     */
066    private final Class<EntityType> elClazz;
067
068    /**
069     * The generic type of the element of this list.
070     */
071    private final Type elGenericType;
072
073    /**
074     * Use this constructor if the element of the list are not based on Java generic type.
075     *
076     * @param entityType The list "entity-type".
077     * @param elClazz The class of the element of the list.
078     */
079    public DefaultListJsonReader(String entityType, Class<EntityType> elClazz) {
080        super(entityType);
081        this.elClazz = elClazz;
082        elGenericType = elClazz;
083    }
084
085    /**
086     * Use this constructor if the element of the list are based on Java generic type.
087     *
088     * @param entityType The list "entity-type".
089     * @param elClazz The class of the element of the list.
090     * @param elGenericType The generic type of the list (you can use {@link TypeUtils#parameterize(Class, Type...) to
091     *            generate it}
092     */
093    public DefaultListJsonReader(String entityType, Class<EntityType> elClazz, Type elGenericType) {
094        super(entityType);
095        this.elClazz = elClazz;
096        this.elGenericType = elGenericType;
097    }
098
099    @Override
100    protected List<EntityType> readEntity(JsonNode jn) throws IOException {
101        Reader<EntityType> entryReader = registry.getReader(ctx, elClazz, elGenericType, APPLICATION_JSON_TYPE);
102        List<EntityType> result = new ArrayList<>();
103        JsonNode entriesNode = jn.get("entries");
104        if (entriesNode != null && !entriesNode.isNull() && entriesNode.isArray()) {
105            JsonNode entryNode = null;
106            Iterator<JsonNode> it = entriesNode.elements();
107            while (it.hasNext()) {
108                entryNode = it.next();
109                try (InputStream in = new InputStreamWithJsonNode(entryNode)) {
110                    EntityType doc = entryReader.read(elClazz, elClazz, APPLICATION_JSON_TYPE, in);
111                    result.add(doc);
112                }
113            }
114        }
115        return result;
116    }
117
118}