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_TYPE; 021import static org.nuxeo.ecm.core.io.registry.MarshallingConstants.ENTITY_FIELD_NAME; 022 023import java.io.IOException; 024import java.lang.reflect.Type; 025import java.util.List; 026import java.util.Map; 027 028import org.apache.commons.lang3.reflect.TypeUtils; 029import org.codehaus.jackson.JsonGenerator; 030import org.nuxeo.ecm.automation.core.util.Paginable; 031import org.nuxeo.ecm.core.api.DocumentModelList; 032import org.nuxeo.ecm.core.io.registry.MarshallerRegistry; 033import org.nuxeo.ecm.core.io.registry.Writer; 034import org.nuxeo.ecm.platform.query.api.Aggregate; 035import org.nuxeo.ecm.platform.query.api.Bucket; 036 037/** 038 * Base class to convert {@link List} as json. 039 * <p> 040 * It follow the classic Nuxeo list format : 041 * 042 * <pre> 043 * { 044 * "entity-type": "GIVEN_ENTITY_TYPE", 045 * <-- pagination info if available are here. 046 * "entries": [ 047 * {...}, <-- A {@link Writer} must be able to manage this format. 048 * {...}, 049 * ... 050 * {...} 051 * ] 052 * } 053 * </pre> 054 * <p> 055 * This list generates pagination information if the list is a {@link Paginable}. 056 * </p> 057 * <p> 058 * This reader delegates the marshalling of entries to the {@link MarshallerRegistry}. A Json {@link Writer} compatible 059 * with the required type must be registered. 060 * </p> 061 * 062 * @param <EntityType> The type of the element of this list. 063 * @since 7.2 064 */ 065public abstract class DefaultListJsonWriter<EntityType> extends AbstractJsonWriter<List<EntityType>> { 066 067 /** 068 * The "entity-type" of the list. 069 */ 070 private final String entityType; 071 072 /** 073 * The Java type of the element of this list. 074 */ 075 private final Class<EntityType> elClazz; 076 077 /** 078 * The generic type of the element of this list. 079 */ 080 private final Type elGenericType; 081 082 /** 083 * Use this constructor if the element of the list are not based on Java generic type. 084 * 085 * @param entityType The list "entity-type". 086 * @param elClazz The class of the element of the list. 087 */ 088 public DefaultListJsonWriter(String entityType, Class<EntityType> elClazz) { 089 super(); 090 this.entityType = entityType; 091 this.elClazz = elClazz; 092 this.elGenericType = elClazz; 093 } 094 095 /** 096 * Use this constructor if the element of the list are based on Java generic type. 097 * 098 * @param entityType The list "entity-type". 099 * @param elClazz The class of the element of the list. 100 * @param elGenericType The generic type of the list (you can use {@link TypeUtils#parameterize(Class, Type...) to 101 * generate it} 102 */ 103 public DefaultListJsonWriter(String entityType, Class<EntityType> elClazz, Type elGenericType) { 104 super(); 105 this.entityType = entityType; 106 this.elClazz = elClazz; 107 this.elGenericType = elGenericType; 108 } 109 110 @Override 111 public void write(List<EntityType> list, JsonGenerator jg) throws IOException { 112 jg.writeStartObject(); 113 jg.writeStringField(ENTITY_FIELD_NAME, entityType); 114 writePaginationInfos(list, jg); 115 Writer<EntityType> documentWriter = registry.getWriter(ctx, elClazz, elGenericType, APPLICATION_JSON_TYPE); 116 jg.writeArrayFieldStart("entries"); 117 for (EntityType entity : list) { 118 documentWriter.write(entity, elClazz, elClazz, APPLICATION_JSON_TYPE, new OutputStreamWithJsonWriter(jg)); 119 } 120 jg.writeEndArray(); 121 extend(list, jg); 122 jg.writeEndObject(); 123 } 124 125 private void writePaginationInfos(List<EntityType> list, JsonGenerator jg) throws IOException { 126 if (list instanceof Paginable) { 127 Paginable<?> paginable = (Paginable<?>) list; 128 jg.writeBooleanField("isPaginable", true); 129 jg.writeNumberField("resultsCount", paginable.getResultsCount()); 130 jg.writeNumberField("pageSize", paginable.getPageSize()); 131 jg.writeNumberField("maxPageSize", paginable.getMaxPageSize()); 132 jg.writeNumberField("currentPageSize", paginable.getCurrentPageSize()); 133 jg.writeNumberField("currentPageIndex", paginable.getCurrentPageIndex()); 134 jg.writeNumberField("numberOfPages", paginable.getNumberOfPages()); 135 jg.writeBooleanField("isPreviousPageAvailable", paginable.isPreviousPageAvailable()); 136 jg.writeBooleanField("isNextPageAvailable", paginable.isNextPageAvailable()); 137 jg.writeBooleanField("isLastPageAvailable", paginable.isLastPageAvailable()); 138 jg.writeBooleanField("isSortable", paginable.isSortable()); 139 jg.writeBooleanField("hasError", paginable.hasError()); 140 jg.writeStringField("errorMessage", paginable.getErrorMessage()); 141 // compat fields 142 if (paginable instanceof DocumentModelList) { 143 jg.writeNumberField("totalSize", ((DocumentModelList) paginable).totalSize()); 144 } 145 jg.writeNumberField("pageIndex", paginable.getCurrentPageIndex()); 146 jg.writeNumberField("pageCount", paginable.getNumberOfPages()); 147 if (paginable.hasAggregateSupport()) { 148 Map<String, Aggregate<? extends Bucket>> aggregates = paginable.getAggregates(); 149 if (aggregates != null && !paginable.getAggregates().isEmpty()) { 150 jg.writeObjectField("aggregations", paginable.getAggregates()); 151 } 152 } 153 } 154 } 155 156 /** 157 * Override this method to write additional information in the list. 158 * 159 * @param list The list to marshal. 160 * @param jg The {@link JsonGenerator} which point inside the list object at the end of standard properties. 161 * @since 7.2 162 */ 163 protected void extend(List<EntityType> list, JsonGenerator jg) throws IOException { 164 } 165 166}