Merge "Using the value in the installedSwView data structure to populate the node...
[controller.git] / opendaylight / md-sal / sal-rest-connector / src / main / java / org / opendaylight / controller / sal / rest / impl / JsonMapper.java
1 package org.opendaylight.controller.sal.rest.impl;
2
3 import static com.google.common.base.Preconditions.checkNotNull;
4
5 import java.io.IOException;
6 import java.util.*;
7
8 import javax.activation.UnsupportedDataTypeException;
9
10 import org.opendaylight.controller.sal.restconf.impl.ControllerContext;
11 import org.opendaylight.yangtools.yang.common.QName;
12 import org.opendaylight.yangtools.yang.data.api.*;
13 import org.opendaylight.yangtools.yang.model.api.*;
14 import org.opendaylight.yangtools.yang.model.api.type.*;
15
16 import com.google.common.base.Preconditions;
17 import com.google.gson.stream.JsonWriter;
18
19 class JsonMapper {
20
21     private final Set<LeafListSchemaNode> foundLeafLists = new HashSet<>();
22     private final Set<ListSchemaNode> foundLists = new HashSet<>();
23
24     public void write(JsonWriter writer, CompositeNode data, DataNodeContainer schema) throws IOException {
25         Preconditions.checkNotNull(writer);
26         Preconditions.checkNotNull(data);
27         Preconditions.checkNotNull(schema);
28
29         writer.beginObject();
30
31         if (schema instanceof ContainerSchemaNode) {
32             writeContainer(writer, data, (ContainerSchemaNode) schema);
33         } else if (schema instanceof ListSchemaNode) {
34             writeList(writer, null, data, (ListSchemaNode) schema);
35         } else {
36             throw new UnsupportedDataTypeException(
37                     "Schema can be ContainerSchemaNode or ListSchemaNode. Other types are not supported yet.");
38         }
39
40         writer.endObject();
41
42         foundLeafLists.clear();
43         foundLists.clear();
44     }
45
46     private void writeChildrenOfParent(JsonWriter writer, CompositeNode parent, DataNodeContainer parentSchema)
47             throws IOException {
48         checkNotNull(parent);
49         checkNotNull(parentSchema);
50
51         for (Node<?> child : parent.getChildren()) {
52             DataSchemaNode childSchema = findFirstSchemaForNode(child, parentSchema.getChildNodes());
53
54             if (childSchema == null) {
55                 throw new UnsupportedDataTypeException("Probably the data node \"" + child.getNodeType().getLocalName()
56                         + "\" is not conform to schema");
57             }
58
59             if (childSchema instanceof ContainerSchemaNode) {
60                 Preconditions.checkState(child instanceof CompositeNode,
61                         "Data representation of Container should be CompositeNode - " + child.getNodeType());
62                 writeContainer(writer, (CompositeNode) child, (ContainerSchemaNode) childSchema);
63             } else if (childSchema instanceof ListSchemaNode) {
64                 if (!foundLists.contains(childSchema)) {
65                     Preconditions.checkState(child instanceof CompositeNode,
66                             "Data representation of List should be CompositeNode - " + child.getNodeType());
67                     foundLists.add((ListSchemaNode) childSchema);
68                     writeList(writer, parent, (CompositeNode) child, (ListSchemaNode) childSchema);
69                 }
70             } else if (childSchema instanceof LeafListSchemaNode) {
71                 if (!foundLeafLists.contains(childSchema)) {
72                     Preconditions.checkState(child instanceof SimpleNode<?>,
73                             "Data representation of LeafList should be SimpleNode - " + child.getNodeType());
74                     foundLeafLists.add((LeafListSchemaNode) childSchema);
75                     writeLeafList(writer, parent, (SimpleNode<?>) child, (LeafListSchemaNode) childSchema);
76                 }
77             } else if (childSchema instanceof LeafSchemaNode) {
78                 Preconditions.checkState(child instanceof SimpleNode<?>,
79                         "Data representation of LeafList should be SimpleNode - " + child.getNodeType());
80                 writeLeaf(writer, (SimpleNode<?>) child, (LeafSchemaNode) childSchema);
81             } else {
82                 throw new UnsupportedDataTypeException("Schema can be ContainerSchemaNode, ListSchemaNode, "
83                         + "LeafListSchemaNode, or LeafSchemaNode. Other types are not supported yet.");
84             }
85         }
86
87         for (Node<?> child : parent.getChildren()) {
88             DataSchemaNode childSchema = findFirstSchemaForNode(child, parentSchema.getChildNodes());
89             if (childSchema instanceof LeafListSchemaNode) {
90                 foundLeafLists.remove((LeafListSchemaNode) childSchema);
91             } else if (childSchema instanceof ListSchemaNode) {
92                 foundLists.remove((ListSchemaNode) childSchema);
93             }
94         }
95     }
96
97     private DataSchemaNode findFirstSchemaForNode(Node<?> node, Set<DataSchemaNode> dataSchemaNode) {
98         for (DataSchemaNode dsn : dataSchemaNode) {
99             if (node.getNodeType().getLocalName().equals(dsn.getQName().getLocalName())) {
100                 return dsn;
101             } else if (dsn instanceof ChoiceNode) {
102                 for (ChoiceCaseNode choiceCase : ((ChoiceNode) dsn).getCases()) {
103                     DataSchemaNode foundDsn = findFirstSchemaForNode(node, choiceCase.getChildNodes());
104                     if (foundDsn != null) {
105                         return foundDsn;
106                     }
107                 }
108             }
109         }
110         return null;
111     }
112
113     private void writeContainer(JsonWriter writer, CompositeNode node, ContainerSchemaNode schema) throws IOException {
114         writeName(node, schema, writer);
115         writer.beginObject();
116         writeChildrenOfParent(writer, node, schema);
117         writer.endObject();
118     }
119
120     private void writeList(JsonWriter writer, CompositeNode nodeParent, CompositeNode node, ListSchemaNode schema)
121             throws IOException {
122         writeName(node, schema, writer);
123         writer.beginArray();
124
125         if (nodeParent != null) {
126             List<CompositeNode> nodeLists = nodeParent.getCompositesByName(node.getNodeType());
127             for (CompositeNode nodeList : nodeLists) {
128                 writer.beginObject();
129                 writeChildrenOfParent(writer, nodeList, schema);
130                 writer.endObject();
131             }
132         } else {
133             writer.beginObject();
134             writeChildrenOfParent(writer, node, schema);
135             writer.endObject();
136         }
137
138         writer.endArray();
139     }
140
141     private void writeLeafList(JsonWriter writer, CompositeNode nodeParent, SimpleNode<?> node,
142             LeafListSchemaNode schema) throws IOException {
143         writeName(node, schema, writer);
144         writer.beginArray();
145
146         List<SimpleNode<?>> nodeLeafLists = nodeParent.getSimpleNodesByName(node.getNodeType());
147         for (SimpleNode<?> nodeLeafList : nodeLeafLists) {
148             writeValueOfNodeByType(writer, nodeLeafList, schema.getType(), schema);
149         }
150         writer.endArray();
151     }
152
153     private void writeLeaf(JsonWriter writer, SimpleNode<?> node, LeafSchemaNode schema) throws IOException {
154         writeName(node, schema, writer);
155         writeValueOfNodeByType(writer, node, schema.getType(), schema);
156     }
157
158     private void writeValueOfNodeByType(JsonWriter writer, SimpleNode<?> node, TypeDefinition<?> type,
159             DataSchemaNode schema) throws IOException {
160
161         String value = String.valueOf(node.getValue());
162         TypeDefinition<?> baseType = resolveBaseTypeFrom(type);
163
164         // TODO check InstanceIdentifierTypeDefinition,
165         // IdentityrefTypeDefinition
166         if (baseType instanceof IdentityrefTypeDefinition) {
167             if (node.getValue() instanceof QName) {
168                 QName qName = (QName) node.getValue();
169
170                 ControllerContext contContext = ControllerContext.getInstance();
171                 String moduleName = contContext.findModuleByNamespace(qName.getNamespace());
172
173                 writer.value(moduleName + ":" + qName.getLocalName());
174             }
175
176         } else if (baseType instanceof LeafrefTypeDefinition) {
177             ControllerContext contContext = ControllerContext.getInstance();
178             LeafSchemaNode lfSchemaNode = contContext.resolveTypeFromLeafref((LeafrefTypeDefinition) baseType, schema);
179             if (lfSchemaNode != null) {
180                 writeValueOfNodeByType(writer, node, lfSchemaNode.getType(), lfSchemaNode);
181             } else {
182                 writer.value(value);
183             }
184         } else if (baseType instanceof InstanceIdentifierTypeDefinition) {
185             writer.value(((InstanceIdentifierTypeDefinition) baseType).getPathStatement().toString());
186         } else if (baseType instanceof UnionTypeDefinition) {
187             processTypeIsUnionType(writer, (UnionTypeDefinition) baseType, value);
188         } else if (baseType instanceof DecimalTypeDefinition || baseType instanceof IntegerTypeDefinition
189                 || baseType instanceof UnsignedIntegerTypeDefinition) {
190             writer.value(new NumberForJsonWriter(value));
191         } else if (baseType instanceof BooleanTypeDefinition) {
192             writer.value(Boolean.parseBoolean(value));
193         } else if (baseType instanceof EmptyTypeDefinition) {
194             writeEmptyDataTypeToJson(writer);
195         } else {
196             writer.value(value.equals("null") ? "" : value);
197         }
198     }
199
200     private void processTypeIsUnionType(JsonWriter writer, UnionTypeDefinition unionType, String value)
201             throws IOException {
202         if (value == null) {
203             writeEmptyDataTypeToJson(writer);
204         } else if ((isNumber(value))
205                 && containsType(unionType, UnsignedIntegerTypeDefinition.class, IntegerTypeDefinition.class,
206                         DecimalTypeDefinition.class)) {
207             writer.value(new NumberForJsonWriter(value));
208         } else if (isBoolean(value) && containsType(unionType, BooleanTypeDefinition.class)) {
209             writer.value(Boolean.parseBoolean(value));
210         } else {
211             writer.value(value);
212         }
213     }
214
215     private boolean isBoolean(String value) {
216         if (value.equals("true") || value.equals("false")) {
217             return true;
218         }
219         return false;
220     }
221
222     private void writeEmptyDataTypeToJson(JsonWriter writer) throws IOException {
223         writer.beginArray();
224         writer.nullValue();
225         writer.endArray();
226     }
227
228     private boolean isNumber(String value) {
229         try {
230             Double.valueOf(value);
231         } catch (NumberFormatException e) {
232             return false;
233         }
234         return true;
235     }
236
237     private boolean containsType(UnionTypeDefinition unionType, Class<?>... searchedTypes) {
238         List<TypeDefinition<?>> allUnionSubtypes = resolveAllUnionSubtypesFrom(unionType);
239
240         for (TypeDefinition<?> unionSubtype : allUnionSubtypes) {
241             for (Class<?> searchedType : searchedTypes) {
242                 if (searchedType.isInstance(unionSubtype)) {
243                     return true;
244                 }
245             }
246         }
247         return false;
248     }
249
250     private List<TypeDefinition<?>> resolveAllUnionSubtypesFrom(UnionTypeDefinition inputType) {
251         List<TypeDefinition<?>> result = new ArrayList<>();
252         for (TypeDefinition<?> subtype : inputType.getTypes()) {
253             TypeDefinition<?> resolvedSubtype = subtype;
254
255             resolvedSubtype = resolveBaseTypeFrom(subtype);
256
257             if (resolvedSubtype instanceof UnionTypeDefinition) {
258                 List<TypeDefinition<?>> subtypesFromRecursion = resolveAllUnionSubtypesFrom((UnionTypeDefinition) resolvedSubtype);
259                 result.addAll(subtypesFromRecursion);
260             } else {
261                 result.add(resolvedSubtype);
262             }
263         }
264
265         return result;
266     }
267
268     private TypeDefinition<?> resolveBaseTypeFrom(TypeDefinition<?> type) {
269         return type.getBaseType() != null ? resolveBaseTypeFrom(type.getBaseType()) : type;
270     }
271
272     private void writeName(Node<?> node, DataSchemaNode schema, JsonWriter writer) throws IOException {
273         String nameForOutput = node.getNodeType().getLocalName();
274         if (schema.isAugmenting()) {
275             ControllerContext contContext = ControllerContext.getInstance();
276             CharSequence moduleName;
277             moduleName = contContext.toRestconfIdentifier(schema.getQName());
278             if (moduleName != null) {
279                 nameForOutput = moduleName.toString();
280             }
281         }
282         writer.name(nameForOutput);
283     }
284
285     private static final class NumberForJsonWriter extends Number {
286
287         private static final long serialVersionUID = -3147729419814417666L;
288         private final String value;
289
290         public NumberForJsonWriter(String value) {
291             this.value = value;
292         }
293
294         @Override
295         public int intValue() {
296             throw new IllegalStateException("Should not be invoked");
297         }
298
299         @Override
300         public long longValue() {
301             throw new IllegalStateException("Should not be invoked");
302         }
303
304         @Override
305         public float floatValue() {
306             throw new IllegalStateException("Should not be invoked");
307         }
308
309         @Override
310         public double doubleValue() {
311             throw new IllegalStateException("Should not be invoked");
312         }
313
314         @Override
315         public String toString() {
316             return value;
317         }
318
319     }
320
321 }