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