package com.temenos.useragent.generic.mediatype; /* * #%L * useragent-generic-java * %% * Copyright (C) 2012 - 2016 Temenos Holdings N.V. * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * #L% */ import static com.temenos.useragent.generic.mediatype.HalJsonUtil.initRepresentationFactory; import static com.temenos.useragent.generic.mediatype.PropertyNameUtil.extractIndex; import static com.temenos.useragent.generic.mediatype.PropertyNameUtil.extractPropertyName; import static com.temenos.useragent.generic.mediatype.PropertyNameUtil.flattenPropertyName; import static com.temenos.useragent.generic.mediatype.PropertyNameUtil.isPropertyNameWithIndex; import java.io.IOException; import java.io.InputStream; import java.io.StringReader; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.TreeMap; import org.apache.commons.io.IOUtils; import org.json.JSONArray; import org.json.JSONObject; import com.temenos.useragent.generic.Link; import com.temenos.useragent.generic.internal.EntityHandler; import com.theoryinpractise.halbuilder.api.ReadableRepresentation; import com.theoryinpractise.halbuilder.api.Representation; import com.theoryinpractise.halbuilder.api.RepresentationFactory; /** * An {@link EntityHandler entity handler} implementation for * <i>application/hal+json</i> media type. * <p> * * @see <a href="https://tools.ietf.org/html/draft-kelly-json-hal-08"</a> JSON * Hypertext Application Language * </p> * @author ssethupathi * */ public class HalJsonEntityHandler implements EntityHandler { private ReadableRepresentation representation; private JSONObject jsonObject; @Override public String getId() { return ""; } @Override public List<Link> getLinks() { return HalJsonUtil.extractLinks(representation); } @Override public String getValue(String fqPropertyName) { String[] pathParts = flattenPropertyName(fqPropertyName); JSONObject parent = identifyParentProperty(fqPropertyName, pathParts); if (parent != null) { String parentPropertyName = pathParts[pathParts.length - 1]; return parent.optString(parentPropertyName, null); } return null; } @Override public void setValue(String fqPropertyName, String value) { checkAndInitialiseEntity(); String[] pathParts = flattenPropertyName(fqPropertyName); JSONObject parent = checkAndCreateParent(fqPropertyName, pathParts); if (parent != null) { String parentPropertyName = pathParts[pathParts.length - 1]; parent.put(parentPropertyName, value); } } @Override public int getCount(String fqPropertyName) { String[] pathParts = flattenPropertyName(fqPropertyName); JSONObject parent = identifyParentProperty(fqPropertyName, pathParts); if (parent != null) { String parentPropertyName = pathParts[pathParts.length - 1]; if (parent.optJSONArray(parentPropertyName) != null) { return parent.optJSONArray(parentPropertyName).length(); } } return 0; } @Override public void remove(String fqPropertyName) { String[] pathParts = flattenPropertyName(fqPropertyName); JSONObject parent = identifyParentProperty(fqPropertyName, pathParts); if (parent == null) { return; } String childName = pathParts[pathParts.length - 1]; if (isPropertyNameWithIndex(childName)) { JSONArray jsonArr = parent .optJSONArray(extractPropertyName(childName)); if (jsonArr != null) { jsonArr.remove(extractIndex(childName)); } } else { parent.remove(childName); } } @Override public void setContent(InputStream stream) { if (stream == null) { throw new IllegalArgumentException("Input stream is null"); } String content = null; try { content = IOUtils.toString(stream); } catch (IOException e) { throw new IllegalArgumentException(e); } RepresentationFactory factory = initRepresentationFactory(); representation = factory.readRepresentation( RepresentationFactory.HAL_JSON, new StringReader(content)); jsonObject = new JSONObject(content); } @Override public InputStream getContent() { RepresentationFactory factory = initRepresentationFactory(); Representation r = factory.newRepresentation(); Map<String,Object> root = new TreeMap<String,Object>(); addAllChildren(factory, root, null, jsonObject); for (String key : root.keySet()) { r.withProperty(key, root.get(key)); } return IOUtils.toInputStream(r.toString( RepresentationFactory.HAL_JSON, RepresentationFactory.PRETTY_PRINT)); } private void addAllChildren(RepresentationFactory factory, Map<String, Object> parent, String name, Object jObject) { if (jObject != null && jObject instanceof JSONArray) { addJSONArrayChildren(factory, parent, name, (JSONArray) jObject); } else if (jObject != null && jObject instanceof JSONObject) { addJSONObjectChildren(factory, parent, name, (JSONObject) jObject); } else { parent.put(name, jObject); } } private void addJSONObjectChildren(RepresentationFactory factory, Map<String, Object> parent, String name, JSONObject jsonObject) { String[] fieldNames = JSONObject.getNames(jsonObject); if (fieldNames == null) { return; } Map<String, Object> children = new TreeMap<String, Object>(); for (String fieldName : fieldNames) { addAllChildren(factory, children, fieldName, (jsonObject).get(fieldName)); } if (name == null) { parent.putAll(children); } else { parent.put(name, children); } } private void addJSONArrayChildren(RepresentationFactory factory, Map<String, Object> parent, String name, JSONArray jsonArray) { List<Object> jObjects = new ArrayList<Object>(); for (int i = 0; i < jsonArray.length(); i++) { Map<String, Object> children = new TreeMap<String, Object>(); if (jsonArray.get(i) != null && jsonArray.get(i) instanceof JSONArray) { addAllChildren(factory, children, null, jsonArray.getJSONArray(i)); } else { addAllChildren(factory, children, null, jsonArray.getJSONObject(i)); } if (!children.isEmpty()) { jObjects.add(children); } } if (!jObjects.isEmpty()) { parent.put(name, jObjects); } } // identify the existing last parent in the path private JSONObject identifyParentProperty(String fqPropertyName, String[] pathParts) { int pathIndex = 0; JSONObject parent = jsonObject; while (pathIndex < (pathParts.length - 1)) { String pathPart = pathParts[pathIndex]; ensurePropertyNameIsWithIndex(pathPart, fqPropertyName); parent = identifySpecificChild(parent, extractPropertyName(pathPart), extractIndex(pathPart)); pathIndex++; } return parent; } // identify the existing specific/indexed child private JSONObject identifySpecificChild(Object parent, String propertyName, int index) { if (parent instanceof JSONArray) { return ((JSONArray) parent).optJSONObject(index); } else if (parent instanceof JSONObject) { JSONObject parentJsonObj = (JSONObject) parent; JSONObject jsonObj = parentJsonObj.optJSONObject(propertyName); if (jsonObj == null) { if (parentJsonObj.optJSONArray(propertyName) != null) { return parentJsonObj.optJSONArray(propertyName) .optJSONObject(index); } } return jsonObj; } else if (parent == null) { return null; } throw new RuntimeException("Unexpected type " + parent.getClass().getName()); } // identify the existing last parent or create new in the path private JSONObject checkAndCreateParent(String fqPropertyName, String[] pathParts) { int pathIndex = 0; JSONObject parent = jsonObject; while (pathIndex < (pathParts.length - 1)) { String pathPart = pathParts[pathIndex]; ensurePropertyNameIsWithIndex(pathPart, fqPropertyName); parent = checkAndCreateChild(parent, extractPropertyName(pathPart), extractIndex(pathPart)); pathIndex++; } return parent; } // identify the existing specific/indexed child or create new private JSONObject checkAndCreateChild(JSONObject parent, String propertyName, int index) { JSONArray jsonArr = getValidJsonArr(parent, propertyName, index); if (jsonArr.length() == 0) { jsonArr.put(new JSONObject()); } else if (jsonArr.length() == index) { jsonArr = HalJsonUtil.cloneLastChild(jsonArr); } return jsonArr.getJSONObject(index); } private JSONArray getValidJsonArr(JSONObject parent, String propertyName, int index) { JSONArray jsonArr = parent.optJSONArray(propertyName); if (jsonArr == null) { parent.put(propertyName, new JSONArray()); } jsonArr = parent.getJSONArray(propertyName); if (index > jsonArr.length()) { throw new IllegalArgumentException( "Invalid index '" + index + "' to set value to. Only supported indexs are between '0' and '" + jsonArr.length() + "'"); } return jsonArr; } private void ensurePropertyNameIsWithIndex(String elementName, String fqPropertyName) { if (!isPropertyNameWithIndex(elementName)) { throw new IllegalArgumentException("Invalid part '" + elementName + "' in fully qualified property name '" + fqPropertyName + "'"); } } private void checkAndInitialiseEntity() { if (jsonObject == null) { jsonObject = new JSONObject(); } } }