Merge "Removing { } from NormalizedNodeJsonBodyWriter"
[controller.git] / opendaylight / md-sal / sal-rest-connector / src / main / java / org / opendaylight / controller / sal / rest / impl / JsonToCompositeNodeReader.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.controller.sal.rest.impl;
9
10 import com.google.common.base.Splitter;
11 import com.google.common.collect.Iterators;
12 import com.google.gson.JsonElement;
13 import com.google.gson.JsonObject;
14 import com.google.gson.JsonPrimitive;
15 import com.google.gson.stream.JsonReader;
16 import java.io.InputStream;
17 import java.io.InputStreamReader;
18 import java.net.URI;
19 import java.util.Iterator;
20 import java.util.Map.Entry;
21 import java.util.Set;
22 import org.opendaylight.controller.sal.rest.gson.JsonParser;
23 import org.opendaylight.controller.sal.rest.impl.RestUtil.PrefixMapingFromJson;
24 import org.opendaylight.controller.sal.restconf.impl.CompositeNodeWrapper;
25 import org.opendaylight.controller.sal.restconf.impl.EmptyNodeWrapper;
26 import org.opendaylight.controller.sal.restconf.impl.IdentityValuesDTO;
27 import org.opendaylight.controller.sal.restconf.impl.SimpleNodeWrapper;
28 import org.slf4j.Logger;
29 import org.slf4j.LoggerFactory;
30
31 /**
32  * @deprecated class will be removed in Lithium release
33  */
34 @Deprecated
35 class JsonToCompositeNodeReader {
36     private static final Logger LOG = LoggerFactory.getLogger(JsonToCompositeNodeReader.class);
37     private static final Splitter COLON_SPLITTER = Splitter.on(':');
38
39     private JsonToCompositeNodeReader() {
40
41     }
42
43     public static CompositeNodeWrapper read(final InputStream entityStream) throws UnsupportedFormatException {
44         final JsonParser parser = new JsonParser();
45
46         final JsonElement rootElement = parser.parse(new JsonReader(new InputStreamReader(entityStream)));
47         if (rootElement.isJsonNull()) {
48             // no content, so return null to indicate no input
49             return null;
50         }
51
52         if (!rootElement.isJsonObject()) {
53             throw new UnsupportedFormatException("Root element of Json has to be Object");
54         }
55
56         final Set<Entry<String, JsonElement>> entrySetsOfRootJsonObject = rootElement.getAsJsonObject().entrySet();
57         if (entrySetsOfRootJsonObject.size() != 1) {
58             throw new UnsupportedFormatException("Json Object should contain one element");
59         }
60
61         final Entry<String, JsonElement> childEntry = entrySetsOfRootJsonObject.iterator().next();
62         final String firstElementName = childEntry.getKey();
63         final JsonElement firstElementType = childEntry.getValue();
64         if (firstElementType.isJsonObject()) {
65             // container in yang
66             return createStructureWithRoot(firstElementName, firstElementType.getAsJsonObject());
67         }
68         if (firstElementType.isJsonArray()) {
69             // list in yang
70             if (firstElementType.getAsJsonArray().size() == 1) {
71                 final JsonElement firstElementInArray = firstElementType.getAsJsonArray().get(0);
72                 if (firstElementInArray.isJsonObject()) {
73                     return createStructureWithRoot(firstElementName, firstElementInArray.getAsJsonObject());
74                 }
75                 throw new UnsupportedFormatException(
76                         "Array as the first element in Json Object can have only Object element");
77             }
78         }
79         throw new UnsupportedFormatException(
80                 "First element in Json Object has to be \"Object\" or \"Array with one Object element\". Other scenarios are not supported yet.");
81     }
82
83     private static CompositeNodeWrapper createStructureWithRoot(final String rootObjectName, final JsonObject rootObject) {
84         final CompositeNodeWrapper firstNode = new CompositeNodeWrapper(getNamespaceFor(rootObjectName),
85                 getLocalNameFor(rootObjectName));
86         for (final Entry<String, JsonElement> childOfFirstNode : rootObject.entrySet()) {
87             addChildToParent(childOfFirstNode.getKey(), childOfFirstNode.getValue(), firstNode);
88         }
89         return firstNode;
90     }
91
92     private static void addChildToParent(final String childName, final JsonElement childType,
93             final CompositeNodeWrapper parent) {
94         if (childType.isJsonObject()) {
95             final CompositeNodeWrapper child = new CompositeNodeWrapper(getNamespaceFor(childName),
96                     getLocalNameFor(childName));
97             parent.addValue(child);
98             for (final Entry<String, JsonElement> childOfChild : childType.getAsJsonObject().entrySet()) {
99                 addChildToParent(childOfChild.getKey(), childOfChild.getValue(), child);
100             }
101         } else if (childType.isJsonArray()) {
102             if (childType.getAsJsonArray().size() == 1 && childType.getAsJsonArray().get(0).isJsonNull()) {
103                 parent.addValue(new EmptyNodeWrapper(getNamespaceFor(childName), getLocalNameFor(childName)));
104
105             } else {
106                 for (final JsonElement childOfChildType : childType.getAsJsonArray()) {
107                     addChildToParent(childName, childOfChildType, parent);
108                 }
109             }
110         } else if (childType.isJsonPrimitive()) {
111             final JsonPrimitive childPrimitive = childType.getAsJsonPrimitive();
112             final String value = childPrimitive.getAsString().trim();
113             parent.addValue(new SimpleNodeWrapper(getNamespaceFor(childName), getLocalNameFor(childName),
114                     resolveValueOfElement(value)));
115         } else {
116             LOG.debug("Ignoring unhandled child type {}", childType);
117         }
118     }
119
120     /**
121      * Transform input value to URI instance.
122      *
123      * Input string has to be in format moduleName:localName. moduleName part is then transformed to URI instance.
124      * If moduleName part contains character like "<" or ">" then null value is returned because they
125      * aren't valid URI characters.
126      *
127      * @param jsonElementName
128      *  value in format moduleName:localName
129      * @return
130      */
131     private static URI getNamespaceFor(final String jsonElementName) {
132         final Iterator<String> it = COLON_SPLITTER.split(jsonElementName).iterator();
133
134         // The string needs to be in form "moduleName:localName"
135         if (it.hasNext()) {
136             final String maybeURI = it.next();
137             if (Iterators.size(it) == 1) {
138                 try {
139                     return URI.create(maybeURI);
140                 } catch (final IllegalArgumentException e) {
141                     LOG.debug("Value {} couldn't be interpreted as URI.", maybeURI);
142                 }
143             }
144         }
145
146         return null;
147     }
148
149     private static String getLocalNameFor(final String jsonElementName) {
150         final Iterator<String> it = COLON_SPLITTER.split(jsonElementName).iterator();
151
152         // The string needs to me in form "moduleName:localName"
153         final String ret = Iterators.get(it, 1, null);
154         return ret != null && !it.hasNext() ? ret : jsonElementName;
155     }
156
157     private static Object resolveValueOfElement(final String value) {
158         // it could be instance-identifier Built-In Type
159         if (!value.isEmpty() && value.charAt(0) == '/') {
160             final IdentityValuesDTO resolvedValue = RestUtil.asInstanceIdentifier(value, new PrefixMapingFromJson());
161             if (resolvedValue != null) {
162                 return resolvedValue;
163             }
164         }
165
166         // it could be identityref Built-In Type therefore it is necessary to look at value as module_name:local_name
167         final URI namespace = getNamespaceFor(value);
168         if (namespace != null) {
169             return new IdentityValuesDTO(namespace.toString(), getLocalNameFor(value), null, value);
170         }
171
172         // it is not "prefix:value" but just "value"
173         return value;
174     }
175
176 }