/*
* (C) Copyright 2015 Nuxeo SA (http://nuxeo.com/) and others.
*
* Licensed 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.
*
* Contributors:
* Nicolas Chapurlat <nchapurlat@nuxeo.com>
*/
package org.nuxeo.ecm.core.io.marshallers.json;
import static javax.ws.rs.core.MediaType.APPLICATION_JSON_TYPE;
import static org.nuxeo.ecm.core.io.registry.MarshallingConstants.ENTITY_FIELD_NAME;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.commons.lang3.reflect.TypeUtils;
import org.codehaus.jackson.JsonGenerator;
import org.nuxeo.ecm.automation.core.util.Paginable;
import org.nuxeo.ecm.core.api.DocumentModelList;
import org.nuxeo.ecm.core.io.registry.MarshallerRegistry;
import org.nuxeo.ecm.core.io.registry.Writer;
import org.nuxeo.ecm.core.io.registry.context.RenderingContext;
import org.nuxeo.ecm.platform.query.api.Aggregate;
import org.nuxeo.ecm.platform.query.api.Bucket;
import org.nuxeo.ecm.platform.query.api.QuickFilter;
/**
* Base class to convert {@link List} as json.
* <p>
* It follow the classic Nuxeo list format :
*
* <pre>
* {
* "entity-type": "GIVEN_ENTITY_TYPE",
* <-- pagination info if available are here.
* "entries": [
* {...}, <-- A {@link Writer} must be able to manage this format.
* {...},
* ...
* {...}
* ]
* }
* </pre>
* <p>
* This list generates pagination information if the list is a {@link Paginable}.
* </p>
* <p>
* This reader delegates the marshalling of entries to the {@link MarshallerRegistry}. A Json {@link Writer} compatible
* with the required type must be registered.
* </p>
*
* @param <EntityType> The type of the element of this list.
* @since 7.2
*/
public abstract class DefaultListJsonWriter<EntityType> extends AbstractJsonWriter<List<EntityType>> {
/**
* The "entity-type" of the list.
*/
private final String entityType;
/**
* The Java type of the element of this list.
*/
private final Class<EntityType> elClazz;
/**
* The generic type of the element of this list.
*/
private final Type elGenericType;
/**
* Use this constructor if the element of the list are not based on Java generic type.
*
* @param entityType The list "entity-type".
* @param elClazz The class of the element of the list.
*/
public DefaultListJsonWriter(String entityType, Class<EntityType> elClazz) {
super();
this.entityType = entityType;
this.elClazz = elClazz;
this.elGenericType = elClazz;
}
/**
* Use this constructor if the element of the list are based on Java generic type.
*
* @param entityType The list "entity-type".
* @param elClazz The class of the element of the list.
* @param elGenericType The generic type of the list (you can use {@link TypeUtils#parameterize(Class, Type...) to
* generate it}
*/
public DefaultListJsonWriter(String entityType, Class<EntityType> elClazz, Type elGenericType) {
super();
this.entityType = entityType;
this.elClazz = elClazz;
this.elGenericType = elGenericType;
}
@Override
public void write(List<EntityType> list, JsonGenerator jg) throws IOException {
jg.writeStartObject();
ctx.setParameterValues(RenderingContext.RESPONSE_HEADER_ENTITY_TYPE_KEY, this.entityType);
jg.writeStringField(ENTITY_FIELD_NAME, entityType);
writePaginationInfos(list, jg);
Writer<EntityType> documentWriter = registry.getWriter(ctx, elClazz, elGenericType, APPLICATION_JSON_TYPE);
jg.writeArrayFieldStart("entries");
for (EntityType entity : list) {
documentWriter.write(entity, elClazz, elClazz, APPLICATION_JSON_TYPE, new OutputStreamWithJsonWriter(jg));
}
jg.writeEndArray();
extend(list, jg);
jg.writeEndObject();
}
private void writePaginationInfos(List<EntityType> list, JsonGenerator jg) throws IOException {
if (list instanceof Paginable) {
Paginable<?> paginable = (Paginable<?>) list;
jg.writeBooleanField("isPaginable", true);
jg.writeNumberField("resultsCount", paginable.getResultsCount());
jg.writeNumberField("pageSize", paginable.getPageSize());
jg.writeNumberField("maxPageSize", paginable.getMaxPageSize());
jg.writeNumberField("currentPageSize", paginable.getCurrentPageSize());
jg.writeNumberField("currentPageIndex", paginable.getCurrentPageIndex());
jg.writeNumberField("numberOfPages", paginable.getNumberOfPages());
jg.writeBooleanField("isPreviousPageAvailable", paginable.isPreviousPageAvailable());
jg.writeBooleanField("isNextPageAvailable", paginable.isNextPageAvailable());
jg.writeBooleanField("isLastPageAvailable", paginable.isLastPageAvailable());
jg.writeBooleanField("isSortable", paginable.isSortable());
jg.writeBooleanField("hasError", paginable.hasError());
jg.writeStringField("errorMessage", paginable.getErrorMessage());
// compat fields
if (paginable instanceof DocumentModelList) {
jg.writeNumberField("totalSize", ((DocumentModelList) paginable).totalSize());
}
jg.writeNumberField("pageIndex", paginable.getCurrentPageIndex());
jg.writeNumberField("pageCount", paginable.getNumberOfPages());
if (paginable.hasAggregateSupport()) {
Map<String, Aggregate<? extends Bucket>> aggregates = paginable.getAggregates();
if (aggregates != null && !paginable.getAggregates().isEmpty()) {
jg.writeObjectFieldStart("aggregations");
for (Entry<String, Aggregate<? extends Bucket>> e : aggregates.entrySet()) {
writeEntityField(e.getKey(), e.getValue(), jg);
}
jg.writeEndObject();
}
}
List<QuickFilter> qfs = paginable.getActiveQuickFilters();
List<QuickFilter> aqfs = paginable.getAvailableQuickFilters();
if (aqfs != null && !aqfs.isEmpty()) {
jg.writeArrayFieldStart("quickFilters");
for (QuickFilter aqf : aqfs) {
jg.writeStartObject();
jg.writeStringField("name", aqf.getName());
jg.writeBooleanField("active", qfs.contains(aqf));
jg.writeEndObject();
}
jg.writeEndArray();
}
}
}
/**
* Override this method to write additional information in the list.
*
* @param list The list to marshal.
* @param jg The {@link JsonGenerator} which point inside the list object at the end of standard properties.
* @since 7.2
*/
protected void extend(List<EntityType> list, JsonGenerator jg) throws IOException {
}
}