001/* 002 * (C) Copyright 2006-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 * Bogdan Stefanescu 018 */ 019package org.nuxeo.ecm.automation.core.util; 020 021import org.codehaus.jackson.JsonGenerationException; 022import org.codehaus.jackson.JsonGenerator; 023import org.nuxeo.common.utils.URIUtils; 024import org.nuxeo.ecm.core.api.Blob; 025import org.nuxeo.ecm.core.api.PropertyException; 026import org.nuxeo.ecm.core.api.model.Property; 027import org.nuxeo.ecm.core.api.model.impl.ArrayProperty; 028import org.nuxeo.ecm.core.api.model.impl.ComplexProperty; 029import org.nuxeo.ecm.core.api.model.impl.ListProperty; 030import org.nuxeo.ecm.core.api.model.impl.primitives.BlobProperty; 031import org.nuxeo.ecm.core.schema.types.ComplexTypeImpl; 032import org.nuxeo.ecm.core.schema.types.ListType; 033import org.nuxeo.ecm.core.schema.types.primitives.*; 034 035import java.io.IOException; 036import java.io.UnsupportedEncodingException; 037import java.util.Calendar; 038import java.util.Date; 039 040/** 041 * Helper to marshaling properties into JSON. 042 * 043 * @since 7.1 044 */ 045public class JSONPropertyWriter { 046 047 /** Utility class. */ 048 private JSONPropertyWriter() { 049 } 050 051 /** 052 * Converts the value of the given core property to JSON. The given filesBaseUrl is the baseUrl that can be used to 053 * locate blob content and is useful to generate blob URLs. 054 */ 055 public static void writePropertyValue(JsonGenerator jg, Property prop, DateTimeFormat dateTimeFormat, 056 String filesBaseUrl) throws PropertyException, JsonGenerationException, IOException { 057 if (prop.isScalar()) { 058 writeScalarPropertyValue(jg, prop, dateTimeFormat); 059 } else if (prop.isList()) { 060 writeListPropertyValue(jg, prop, dateTimeFormat, filesBaseUrl); 061 } else { 062 if (prop.isPhantom()) { 063 jg.writeNull(); 064 } else if (prop instanceof BlobProperty) { // a blob 065 writeBlobPropertyValue(jg, prop, filesBaseUrl); 066 } else { // a complex property 067 writeMapPropertyValue(jg, (ComplexProperty) prop, dateTimeFormat, filesBaseUrl); 068 } 069 } 070 } 071 072 @SuppressWarnings("boxing") 073 protected static void writeScalarPropertyValue(JsonGenerator jg, Property prop, DateTimeFormat dateTimeFormat) 074 throws PropertyException, IOException { 075 org.nuxeo.ecm.core.schema.types.Type type = prop.getType(); 076 Object v = prop.getValue(); 077 if (v == null) { 078 jg.writeNull(); 079 } else { 080 if (type instanceof BooleanType) { 081 jg.writeBoolean((Boolean) v); 082 } else if (type instanceof LongType) { 083 jg.writeNumber((Long) v); 084 } else if (type instanceof DoubleType) { 085 jg.writeNumber((Double) v); 086 } else if (type instanceof IntegerType) { 087 jg.writeNumber((Integer) v); 088 } else if (type instanceof BinaryType) { 089 jg.writeBinary((byte[]) v); 090 } else if (type instanceof DateType && dateTimeFormat == DateTimeFormat.TIME_IN_MILLIS) { 091 if (v instanceof Date) { 092 jg.writeNumber(((Date) v).getTime()); 093 } else if (v instanceof Calendar) { 094 jg.writeNumber(((Calendar) v).getTimeInMillis()); 095 } else { 096 throw new PropertyException("Unknown class for DateType: " + v.getClass().getName() + ", " + v); 097 } 098 } else { 099 jg.writeString(type.encode(v)); 100 } 101 } 102 } 103 104 protected static void writeListPropertyValue(JsonGenerator jg, Property prop, DateTimeFormat dateTimeFormat, 105 String filesBaseUrl) throws PropertyException, JsonGenerationException, IOException { 106 jg.writeStartArray(); 107 if (prop instanceof ArrayProperty) { 108 Object[] ar = (Object[]) prop.getValue(); 109 if (ar == null) { 110 jg.writeEndArray(); 111 return; 112 } 113 org.nuxeo.ecm.core.schema.types.Type type = ((ListType) prop.getType()).getFieldType(); 114 for (Object o : ar) { 115 jg.writeString(type.encode(o)); 116 } 117 } else { 118 ListProperty listp = (ListProperty) prop; 119 for (Property p : listp.getChildren()) { 120 writePropertyValue(jg, p, dateTimeFormat, filesBaseUrl); 121 } 122 } 123 jg.writeEndArray(); 124 } 125 126 protected static void writeMapPropertyValue(JsonGenerator jg, ComplexProperty prop, DateTimeFormat dateTimeFormat, 127 String filesBaseUrl) throws JsonGenerationException, IOException, PropertyException { 128 jg.writeStartObject(); 129 for (Property p : prop.getChildren()) { 130 jg.writeFieldName(p.getName()); 131 writePropertyValue(jg, p, dateTimeFormat, filesBaseUrl); 132 } 133 jg.writeEndObject(); 134 } 135 136 protected static void writeBlobPropertyValue(JsonGenerator jg, Property prop, String filesBaseUrl) 137 throws PropertyException, JsonGenerationException, IOException { 138 Blob blob = (Blob) prop.getValue(); 139 if (blob == null) { 140 jg.writeNull(); 141 return; 142 } 143 jg.writeStartObject(); 144 String v = blob.getFilename(); 145 if (v == null) { 146 jg.writeNullField("name"); 147 } else { 148 jg.writeStringField("name", v); 149 } 150 v = blob.getMimeType(); 151 if (v == null) { 152 jg.writeNullField("mime-type"); 153 } else { 154 jg.writeStringField("mime-type", v); 155 } 156 v = blob.getEncoding(); 157 if (v == null) { 158 jg.writeNullField("encoding"); 159 } else { 160 jg.writeStringField("encoding", v); 161 } 162 v = blob.getDigest(); 163 if (v == null) { 164 jg.writeNullField("digest"); 165 } else { 166 jg.writeStringField("digest", v); 167 } 168 jg.writeStringField("length", Long.toString(blob.getLength())); 169 if (filesBaseUrl != null) { 170 jg.writeStringField("data", getBlobUrl(prop, filesBaseUrl)); 171 } 172 jg.writeEndObject(); 173 } 174 175 /** 176 * Gets the full URL of where a blob can be downloaded. 177 * 178 * @since 5.9.3 179 */ 180 private static String getBlobUrl(Property prop, String filesBaseUrl) throws UnsupportedEncodingException, 181 PropertyException { 182 StringBuilder blobUrlBuilder = new StringBuilder(filesBaseUrl); 183 blobUrlBuilder.append(prop.getSchema().getName()); 184 blobUrlBuilder.append(":"); 185 String canonicalXPath = ComplexTypeImpl.canonicalXPath(prop.getPath().substring(1)); 186 blobUrlBuilder.append(canonicalXPath); 187 blobUrlBuilder.append("/"); 188 String filename = ((Blob) prop.getValue()).getFilename(); 189 if (filename != null) { 190 blobUrlBuilder.append(URIUtils.quoteURIPathComponent(filename, true)); 191 } 192 return blobUrlBuilder.toString(); 193 } 194 195}