001/* 002 * (C) Copyright 2015-2018 Nuxeo (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.types; 021 022import static javax.ws.rs.core.MediaType.APPLICATION_JSON_TYPE; 023import static org.nuxeo.ecm.core.io.registry.reflect.Instantiations.SINGLETON; 024import static org.nuxeo.ecm.core.io.registry.reflect.Priorities.REFERENCE; 025 026import java.io.IOException; 027import java.io.OutputStream; 028import java.util.Set; 029 030import org.apache.commons.lang3.StringUtils; 031import org.nuxeo.ecm.core.io.marshallers.json.ExtensibleEntityJsonWriter; 032import org.nuxeo.ecm.core.io.marshallers.json.OutputStreamWithJsonWriter; 033import org.nuxeo.ecm.core.io.marshallers.json.enrichers.AbstractJsonEnricher; 034import org.nuxeo.ecm.core.io.registry.Writer; 035import org.nuxeo.ecm.core.io.registry.reflect.Setup; 036import org.nuxeo.ecm.core.schema.types.ComplexType; 037import org.nuxeo.ecm.core.schema.types.Field; 038import org.nuxeo.ecm.core.schema.types.ListType; 039import org.nuxeo.ecm.core.schema.types.PrimitiveType; 040import org.nuxeo.ecm.core.schema.types.Schema; 041import org.nuxeo.ecm.core.schema.types.Type; 042import org.nuxeo.ecm.core.schema.types.constraints.Constraint; 043 044import com.fasterxml.jackson.core.JsonGenerator; 045 046/** 047 * Convert {@link Schema} to Json. 048 * <p> 049 * This marshaller is enrichable: register class implementing {@link AbstractJsonEnricher} and managing {@link Schema}. 050 * </p> 051 * <p> 052 * This marshaller is also extensible: extend it and simply override 053 * {@link ExtensibleEntityJsonWriter#extend(Object, JsonGenerator)}. 054 * </p> 055 * <p> 056 * Format is: 057 * 058 * <pre> 059 * {@code 060 * { 061 * "entity-type":"schema", 062 * "name": "SCHEMA_NAME", 063 * "prefix: "SCHEMA_PREFIX", <- only if there's a prefix 064 * "fields", { 065 * "PRIMITIVE_FIELD_LOCAL_NAME": "FIELD_TYPE", <- where field type is {@link Type#getName()} (string, boolean, integer, ...) 066 * "PRIMITIVE_LIST_LOCAL_NAME": "FIELD_TYPE[]" <- where field type is {@link Type#getName()} (string, boolean, integer, ...) 067 * "COMPLEX_FIELD_LOCAL_NAME" : { 068 * "type": "complex", 069 * "fields": { 070 * loop the same format 071 * } 072 * }, 073 * "COMPLEX_LIST_FIELD_LOCAL_NAME" : { 074 * "type": "complex[]", 075 * "fields": { 076 * loop the same format 077 * } 078 * }, 079 * "CONTENT_FIELD": "blob", 080 * "CONTENT_LIST_FIELD": "blob[]", 081 * ... 082 * } 083 * <-- contextParameters if there are enrichers activated 084 * <-- additional property provided by extend() method 085 * } 086 * </pre> 087 * </p> 088 * 089 * @since 7.2 090 */ 091@Setup(mode = SINGLETON, priority = REFERENCE) 092public class SchemaJsonWriter extends ExtensibleEntityJsonWriter<Schema> { 093 094 public static final String ENTITY_TYPE = "schema"; 095 096 /** 097 * @since 8.10 098 */ 099 public static final String FETCH_FIELDS = "fields"; 100 101 public SchemaJsonWriter() { 102 super(ENTITY_TYPE, Schema.class); 103 } 104 105 @Override 106 protected void writeEntityBody(Schema schema, JsonGenerator jg) throws IOException { 107 jg.writeStringField("name", schema.getName()); 108 String prefix = schema.getNamespace().prefix; 109 if (StringUtils.isNotBlank(prefix)) { 110 jg.writeStringField("prefix", prefix); 111 // backward compat for old schema writers 112 jg.writeStringField("@prefix", prefix); 113 } 114 jg.writeObjectFieldStart("fields"); 115 for (Field field : schema.getFields()) { 116 writeField(jg, field); 117 } 118 jg.writeEndObject(); 119 } 120 121 protected void writeField(JsonGenerator jg, Field field) throws IOException { 122 if (!field.getType().isComplexType()) { 123 if (field.getType().isListType()) { 124 ListType lt = (ListType) field.getType(); 125 if (lt.getFieldType().isComplexType()) { 126 if (lt.getFieldType().getName().equals("content")) { 127 jg.writeStringField(field.getName().getLocalName(), "blob[]"); 128 } else { 129 jg.writeObjectFieldStart(field.getName().getLocalName()); 130 jg.writeStringField("type", "complex[]"); 131 jg.writeObjectFieldStart("fields"); 132 ComplexType cplXType = (ComplexType) lt.getField().getType(); 133 for (Field subField : cplXType.getFields()) { 134 writeField(jg, subField); 135 } 136 jg.writeEndObject(); 137 jg.writeEndObject(); 138 } 139 } else { 140 doWriteField(jg, field); 141 } 142 } else { 143 doWriteField(jg, field); 144 } 145 } else { 146 if (field.getType().getName().equals("content")) { 147 jg.writeStringField(field.getName().getLocalName(), "blob"); 148 } else { 149 jg.writeObjectFieldStart(field.getName().getLocalName()); 150 ComplexType cplXType = (ComplexType) field.getType(); 151 jg.writeObjectFieldStart("fields"); 152 for (Field subField : cplXType.getFields()) { 153 writeField(jg, subField); 154 } 155 jg.writeEndObject(); 156 jg.writeStringField("type", "complex"); 157 jg.writeEndObject(); 158 } 159 } 160 } 161 162 /** 163 * @since 8.10 164 */ 165 protected void doWriteField(JsonGenerator jg, Field field) throws IOException { 166 final boolean extended = ctx.getFetched(ENTITY_TYPE).contains(FETCH_FIELDS); 167 String typeValue; 168 Set<Constraint> itemConstraints = null; 169 if (field.getType().isListType()) { 170 ListType lt = (ListType) field.getType(); 171 Type type = lt.getFieldType(); 172 itemConstraints = type.getConstraints(); 173 while (!(type instanceof PrimitiveType)) { 174 type = type.getSuperType(); 175 } 176 typeValue = type.getName() + "[]"; 177 } else { 178 Type type = field.getType(); 179 while (!(type instanceof PrimitiveType)) { 180 type = type.getSuperType(); 181 } 182 typeValue = type.getName(); 183 } 184 if (extended) { 185 jg.writeObjectFieldStart(field.getName().getLocalName()); 186 jg.writeStringField("type", typeValue); 187 Writer<Constraint> constraintWriter = registry.getWriter(ctx, Constraint.class, APPLICATION_JSON_TYPE); 188 OutputStream out = new OutputStreamWithJsonWriter(jg); 189 jg.writeArrayFieldStart("constraints"); 190 for (Constraint c : field.getConstraints()) { 191 constraintWriter.write(c, Constraint.class, Constraint.class, APPLICATION_JSON_TYPE, out); 192 } 193 jg.writeEndArray(); 194 if (itemConstraints != null) { 195 jg.writeArrayFieldStart("itemConstraints"); 196 for (Constraint c : itemConstraints) { 197 constraintWriter.write(c, Constraint.class, Constraint.class, APPLICATION_JSON_TYPE, out); 198 } 199 jg.writeEndArray(); 200 } 201 jg.writeEndObject(); 202 } else { 203 jg.writeStringField(field.getName().getLocalName(), typeValue); 204 } 205 206 } 207 208}