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