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; 028 029import org.apache.commons.lang.StringUtils; 030import org.codehaus.jackson.JsonGenerator; 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.thoughtworks.xstream.io.json.JsonWriter; 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(Schema, JsonWriter)}. 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 * 088 * </p> 089 * 090 * @since 7.2 091 */ 092@Setup(mode = SINGLETON, priority = REFERENCE) 093public class SchemaJsonWriter extends ExtensibleEntityJsonWriter<Schema> { 094 095 public static final String ENTITY_TYPE = "schema"; 096 097 /** 098 * @since 8.10 099 */ 100 public static final String FETCH_FIELDS = "fields"; 101 102 public SchemaJsonWriter() { 103 super(ENTITY_TYPE, Schema.class); 104 } 105 106 @Override 107 protected void writeEntityBody(Schema schema, JsonGenerator jg) throws IOException { 108 jg.writeStringField("name", schema.getName()); 109 String prefix = schema.getNamespace().prefix; 110 if (StringUtils.isNotBlank(prefix)) { 111 jg.writeStringField("prefix", prefix); 112 // backward compat for old schema writers 113 jg.writeStringField("@prefix", prefix); 114 } 115 jg.writeObjectFieldStart("fields"); 116 for (Field field : schema.getFields()) { 117 writeField(jg, field); 118 } 119 jg.writeEndObject(); 120 } 121 122 protected void writeField(JsonGenerator jg, Field field) throws IOException { 123 if (!field.getType().isComplexType()) { 124 if (field.getType().isListType()) { 125 ListType lt = (ListType) field.getType(); 126 if (lt.getFieldType().isComplexType()) { 127 if (lt.getFieldType().getName().equals("content")) { 128 jg.writeStringField(field.getName().getLocalName(), "blob[]"); 129 } else { 130 jg.writeObjectFieldStart(field.getName().getLocalName()); 131 jg.writeStringField("type", "complex[]"); 132 jg.writeObjectFieldStart("fields"); 133 ComplexType cplXType = (ComplexType) lt.getField().getType(); 134 for (Field subField : cplXType.getFields()) { 135 writeField(jg, subField); 136 } 137 jg.writeEndObject(); 138 jg.writeEndObject(); 139 } 140 } else { 141 final boolean isFetchFields = ctx.getFetched(ENTITY_TYPE).contains(FETCH_FIELDS); 142 Type type = lt.getFieldType(); 143 while (!(type instanceof PrimitiveType)) { 144 type = type.getSuperType(); 145 } 146 doWriteField(jg, field, type.getName() + "[]", isFetchFields); 147 } 148 } else { 149 final boolean isFetchFields = ctx.getFetched(ENTITY_TYPE).contains(FETCH_FIELDS); 150 Type type = field.getType(); 151 while (!(type instanceof PrimitiveType)) { 152 type = type.getSuperType(); 153 } 154 doWriteField(jg, field, type.getName(), isFetchFields); 155 } 156 } else { 157 if (field.getType().getName().equals("content")) { 158 jg.writeStringField(field.getName().getLocalName(), "blob"); 159 } else { 160 jg.writeObjectFieldStart(field.getName().getLocalName()); 161 ComplexType cplXType = (ComplexType) field.getType(); 162 jg.writeObjectFieldStart("fields"); 163 for (Field subField : cplXType.getFields()) { 164 writeField(jg, subField); 165 } 166 jg.writeEndObject(); 167 jg.writeStringField("type", "complex"); 168 jg.writeEndObject(); 169 } 170 } 171 } 172 173 /** 174 * @since 8.10 175 */ 176 protected void doWriteField(JsonGenerator jg, Field field, String typeValue, boolean extended) throws IOException { 177 if (extended) { 178 jg.writeObjectFieldStart(field.getName().getLocalName()); 179 jg.writeStringField("type", typeValue); 180 Writer<Constraint> constraintWriter = registry.getWriter(ctx, Constraint.class, APPLICATION_JSON_TYPE); 181 OutputStream out = new OutputStreamWithJsonWriter(jg); 182 jg.writeArrayFieldStart("constraints"); 183 for (Constraint c : field.getConstraints()) { 184 constraintWriter.write(c, Constraint.class, Constraint.class, APPLICATION_JSON_TYPE, out); 185 } 186 jg.writeEndArray(); 187 jg.writeEndObject(); 188 } else { 189 jg.writeStringField(field.getName().getLocalName(), typeValue); 190 } 191 192 } 193 194}