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