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 * Nuxeo - initial API and implementation 018 * 019 */ 020 021package org.nuxeo.ecm.automation.core.util; 022 023import java.io.IOException; 024import java.util.ArrayList; 025import java.util.Calendar; 026import java.util.HashMap; 027import java.util.Iterator; 028import java.util.List; 029import java.util.Map; 030 031import org.nuxeo.ecm.core.api.Blob; 032import org.nuxeo.ecm.core.schema.types.ComplexType; 033import org.nuxeo.ecm.core.schema.types.Field; 034import org.nuxeo.ecm.core.schema.types.ListType; 035import org.nuxeo.ecm.core.schema.types.SimpleType; 036import org.nuxeo.ecm.core.schema.types.Type; 037import org.nuxeo.ecm.core.schema.types.primitives.DateType; 038 039import com.fasterxml.jackson.core.JsonParser; 040import com.fasterxml.jackson.databind.JsonNode; 041import com.fasterxml.jackson.databind.ObjectMapper; 042import com.fasterxml.jackson.databind.node.ArrayNode; 043import com.fasterxml.jackson.databind.node.ObjectNode; 044 045/** 046 * Helper to handle Complex types decoding from a JSON encoded String entries of a property file 047 * 048 * @author Tiry (tdelprat@nuxeo.com) 049 * @since 5.5 050 */ 051public class ComplexTypeJSONDecoder { 052 053 private static final ObjectMapper mapper = new ObjectMapper(); 054 055 protected static List<JSONBlobDecoder> blobDecoders = new ArrayList<>(); 056 static { 057 mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true); 058 blobDecoders.add(new JSONStringBlobDecoder()); 059 blobDecoders.add(new JSONObjectBlobDecoder()); 060 blobDecoders.add(new JSONManagedBlobDecoder()); 061 } 062 063 public static void registerBlobDecoder(JSONBlobDecoder blobDecoder) { 064 blobDecoders.add(blobDecoder); 065 } 066 067 public static List<Object> decodeList(ListType lt, String json) throws IOException { 068 ArrayNode jsonArray = (ArrayNode) mapper.readTree(json); 069 return decodeList(lt, jsonArray); 070 } 071 072 public static List<Object> decodeList(ListType lt, ArrayNode jsonArray) { 073 List<Object> result = new ArrayList<Object>(); 074 Type currentObjectType = lt.getFieldType(); 075 for (int i = 0; i < jsonArray.size(); i++) { 076 JsonNode node = jsonArray.get(i); 077 if (node.isArray()) { 078 result.add(decodeList((ListType) currentObjectType, (ArrayNode) node)); 079 } else if (node.isObject()) { 080 result.add(decode((ComplexType) currentObjectType, (ObjectNode) node)); 081 } else if (node.isTextual()) { 082 result.add(node.textValue()); 083 } else if (node.isNumber()) { 084 result.add(node.numberValue()); 085 } else if (node.isBoolean()) { 086 result.add(node.booleanValue()); 087 } 088 } 089 return result; 090 } 091 092 public static Object decode(ComplexType ct, String json) throws IOException { 093 ObjectNode jsonObject = (ObjectNode) mapper.readTree(json); 094 return decode(ct, jsonObject); 095 } 096 097 public static Object decode(ComplexType ct, ObjectNode jsonObject) { 098 099 Map<String, Object> result = new HashMap<String, Object>(); 100 101 String jsonType = ""; 102 if (jsonObject.has("type")) { 103 jsonType = jsonObject.get("type").textValue(); 104 } 105 if (jsonType.equals("blob") || ct.getName().equals("content")) { 106 return getBlobFromJSON(jsonObject); 107 } 108 109 Iterator<Map.Entry<String, JsonNode>> it = jsonObject.fields(); 110 111 while (it.hasNext()) { 112 Map.Entry<String, JsonNode> nodeEntry = it.next(); 113 if (ct.hasField(nodeEntry.getKey())) { 114 115 Field field = ct.getField(nodeEntry.getKey()); 116 Type fieldType = field.getType(); 117 if (fieldType.isSimpleType()) { 118 Object value; 119 if (fieldType == DateType.INSTANCE && nodeEntry.getValue().isIntegralNumber()) { 120 value = Calendar.getInstance(); 121 ((Calendar) value).setTimeInMillis(nodeEntry.getValue().asLong()); 122 } else { 123 value = ((SimpleType) fieldType).decode(nodeEntry.getValue().asText()); 124 } 125 result.put(nodeEntry.getKey(), value); 126 } else { 127 JsonNode subNode = nodeEntry.getValue(); 128 if (subNode.isArray()) { 129 result.put(nodeEntry.getKey(), decodeList(((ListType) fieldType), (ArrayNode) subNode)); 130 } else { 131 result.put(nodeEntry.getKey(), decode(((ComplexType) fieldType), (ObjectNode) subNode)); 132 } 133 } 134 } 135 } 136 137 return result; 138 } 139 140 public static Blob getBlobFromJSON(ObjectNode jsonObject) { 141 Blob blob = null; 142 for (JSONBlobDecoder blobDecoder : blobDecoders) { 143 blob = blobDecoder.getBlobFromJSON(jsonObject); 144 if (blob != null) { 145 return blob; 146 } 147 } 148 return blob; 149 } 150 151}