Merge "Refactor Subnet.isSubnetOf - reduce number of 'if' statements. Added unitests."
[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.net.URI;
7 import java.util.HashSet;
8 import java.util.List;
9 import java.util.Set;
10
11 import javax.activation.UnsupportedDataTypeException;
12
13 import org.opendaylight.controller.sal.restconf.impl.ControllerContext;
14 import org.opendaylight.controller.sal.restconf.impl.IdentityValuesDTO;
15 import org.opendaylight.controller.sal.restconf.impl.IdentityValuesDTO.IdentityValue;
16 import org.opendaylight.controller.sal.restconf.impl.RestCodec;
17 import org.opendaylight.yangtools.yang.common.QName;
18 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
19 import org.opendaylight.yangtools.yang.data.api.Node;
20 import org.opendaylight.yangtools.yang.data.api.SimpleNode;
21 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
22 import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
23 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
24 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
25 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
26 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
27 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
28 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
29 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
30 import org.opendaylight.yangtools.yang.model.api.type.BooleanTypeDefinition;
31 import org.opendaylight.yangtools.yang.model.api.type.DecimalTypeDefinition;
32 import org.opendaylight.yangtools.yang.model.api.type.EmptyTypeDefinition;
33 import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
34 import org.opendaylight.yangtools.yang.model.api.type.IntegerTypeDefinition;
35 import org.opendaylight.yangtools.yang.model.api.type.UnsignedIntegerTypeDefinition;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
38
39 import com.google.common.base.Preconditions;
40 import com.google.gson.stream.JsonWriter;
41
42 class JsonMapper {
43
44     private final Set<LeafListSchemaNode> foundLeafLists = new HashSet<>();
45     private final Set<ListSchemaNode> foundLists = new HashSet<>();
46     private final Logger logger = LoggerFactory.getLogger(JsonMapper.class); 
47
48     public void write(JsonWriter writer, CompositeNode data, DataNodeContainer schema) throws IOException {
49         Preconditions.checkNotNull(writer);
50         Preconditions.checkNotNull(data);
51         Preconditions.checkNotNull(schema);
52
53         writer.beginObject();
54
55         if (schema instanceof ContainerSchemaNode) {
56             writeContainer(writer, data, (ContainerSchemaNode) schema);
57         } else if (schema instanceof ListSchemaNode) {
58             writeList(writer, null, data, (ListSchemaNode) schema);
59         } else {
60             throw new UnsupportedDataTypeException(
61                     "Schema can be ContainerSchemaNode or ListSchemaNode. Other types are not supported yet.");
62         }
63
64         writer.endObject();
65
66         foundLeafLists.clear();
67         foundLists.clear();
68     }
69
70     private void writeChildrenOfParent(JsonWriter writer, CompositeNode parent, DataNodeContainer parentSchema)
71             throws IOException {
72         checkNotNull(parent);
73         checkNotNull(parentSchema);
74
75         for (Node<?> child : parent.getChildren()) {
76             DataSchemaNode childSchema = findFirstSchemaForNode(child, parentSchema.getChildNodes());
77
78             if (childSchema == null) {
79                 throw new UnsupportedDataTypeException("Probably the data node \"" + child.getNodeType().getLocalName()
80                         + "\" is not conform to schema");
81             }
82
83             if (childSchema instanceof ContainerSchemaNode) {
84                 Preconditions.checkState(child instanceof CompositeNode,
85                         "Data representation of Container should be CompositeNode - " + child.getNodeType());
86                 writeContainer(writer, (CompositeNode) child, (ContainerSchemaNode) childSchema);
87             } else if (childSchema instanceof ListSchemaNode) {
88                 if (!foundLists.contains(childSchema)) {
89                     Preconditions.checkState(child instanceof CompositeNode,
90                             "Data representation of List should be CompositeNode - " + child.getNodeType());
91                     foundLists.add((ListSchemaNode) childSchema);
92                     writeList(writer, parent, (CompositeNode) child, (ListSchemaNode) childSchema);
93                 }
94             } else if (childSchema instanceof LeafListSchemaNode) {
95                 if (!foundLeafLists.contains(childSchema)) {
96                     Preconditions.checkState(child instanceof SimpleNode<?>,
97                             "Data representation of LeafList should be SimpleNode - " + child.getNodeType());
98                     foundLeafLists.add((LeafListSchemaNode) childSchema);
99                     writeLeafList(writer, parent, (SimpleNode<?>) child, (LeafListSchemaNode) childSchema);
100                 }
101             } else if (childSchema instanceof LeafSchemaNode) {
102                 Preconditions.checkState(child instanceof SimpleNode<?>,
103                         "Data representation of LeafList should be SimpleNode - " + child.getNodeType());
104                 writeLeaf(writer, (SimpleNode<?>) child, (LeafSchemaNode) childSchema);
105             } else {
106                 throw new UnsupportedDataTypeException("Schema can be ContainerSchemaNode, ListSchemaNode, "
107                         + "LeafListSchemaNode, or LeafSchemaNode. Other types are not supported yet.");
108             }
109         }
110
111         for (Node<?> child : parent.getChildren()) {
112             DataSchemaNode childSchema = findFirstSchemaForNode(child, parentSchema.getChildNodes());
113             if (childSchema instanceof LeafListSchemaNode) {
114                 foundLeafLists.remove((LeafListSchemaNode) childSchema);
115             } else if (childSchema instanceof ListSchemaNode) {
116                 foundLists.remove((ListSchemaNode) childSchema);
117             }
118         }
119     }
120
121     private DataSchemaNode findFirstSchemaForNode(Node<?> node, Set<DataSchemaNode> dataSchemaNode) {
122         for (DataSchemaNode dsn : dataSchemaNode) {
123             if (node.getNodeType().getLocalName().equals(dsn.getQName().getLocalName())) {
124                 return dsn;
125             } else if (dsn instanceof ChoiceNode) {
126                 for (ChoiceCaseNode choiceCase : ((ChoiceNode) dsn).getCases()) {
127                     DataSchemaNode foundDsn = findFirstSchemaForNode(node, choiceCase.getChildNodes());
128                     if (foundDsn != null) {
129                         return foundDsn;
130                     }
131                 }
132             }
133         }
134         return null;
135     }
136
137     private void writeContainer(JsonWriter writer, CompositeNode node, ContainerSchemaNode schema) throws IOException {
138         writeName(node, schema, writer);
139         writer.beginObject();
140         writeChildrenOfParent(writer, node, schema);
141         writer.endObject();
142     }
143
144     private void writeList(JsonWriter writer, CompositeNode nodeParent, CompositeNode node, ListSchemaNode schema)
145             throws IOException {
146         writeName(node, schema, writer);
147         writer.beginArray();
148
149         if (nodeParent != null) {
150             List<CompositeNode> nodeLists = nodeParent.getCompositesByName(node.getNodeType());
151             for (CompositeNode nodeList : nodeLists) {
152                 writer.beginObject();
153                 writeChildrenOfParent(writer, nodeList, schema);
154                 writer.endObject();
155             }
156         } else {
157             writer.beginObject();
158             writeChildrenOfParent(writer, node, schema);
159             writer.endObject();
160         }
161
162         writer.endArray();
163     }
164
165     private void writeLeafList(JsonWriter writer, CompositeNode nodeParent, SimpleNode<?> node,
166             LeafListSchemaNode schema) throws IOException {
167         writeName(node, schema, writer);
168         writer.beginArray();
169
170         List<SimpleNode<?>> nodeLeafLists = nodeParent.getSimpleNodesByName(node.getNodeType());
171         for (SimpleNode<?> nodeLeafList : nodeLeafLists) {
172             writeValueOfNodeByType(writer, nodeLeafList, schema.getType(), schema);
173         }
174         writer.endArray();
175     }
176
177     private void writeLeaf(JsonWriter writer, SimpleNode<?> node, LeafSchemaNode schema) throws IOException {
178         writeName(node, schema, writer);
179         writeValueOfNodeByType(writer, node, schema.getType(), schema);
180     }
181
182     private void writeValueOfNodeByType(JsonWriter writer, SimpleNode<?> node, TypeDefinition<?> type,
183             DataSchemaNode schema) throws IOException {
184
185         TypeDefinition<?> baseType = RestUtil.resolveBaseTypeFrom(type);
186
187         // TODO check InstanceIdentifierTypeDefinition
188         if (baseType instanceof IdentityrefTypeDefinition) {
189             if (node.getValue() instanceof QName) {
190                 IdentityValuesDTO valueDTO = (IdentityValuesDTO) RestCodec.from(baseType).serialize(node.getValue());
191                 IdentityValue valueFromDTO = valueDTO.getValuesWithNamespaces().get(0);
192                 String moduleName = ControllerContext.getInstance().findModuleByNamespace(URI.create(valueFromDTO.getNamespace()));
193                 writer.value(moduleName + ":" + valueFromDTO.getValue());
194             } else {
195                 logger.debug("Value of " + baseType.getQName().getNamespace() + ":"
196                         + baseType.getQName().getLocalName() + " is not instance of " + QName.class + " but is " + node.getValue().getClass());
197                 writer.value(String.valueOf(node.getValue()));
198             }
199         } else if (baseType instanceof DecimalTypeDefinition || baseType instanceof IntegerTypeDefinition
200                 || baseType instanceof UnsignedIntegerTypeDefinition) {
201             writer.value(new NumberForJsonWriter((String) RestCodec.from(baseType).serialize(node.getValue())));
202         } else if (baseType instanceof BooleanTypeDefinition) {
203             writer.value(Boolean.parseBoolean((String) RestCodec.from(baseType).serialize(node.getValue())));
204         } else if (baseType instanceof EmptyTypeDefinition) {
205             writeEmptyDataTypeToJson(writer);
206         } else {
207             String value = String.valueOf(RestCodec.from(baseType).serialize(node.getValue()));
208             if (value == null) {
209                 value = String.valueOf(node.getValue());
210             }
211             writer.value(value.equals("null") ? "" : value);
212         }
213     }
214
215     private void writeEmptyDataTypeToJson(JsonWriter writer) throws IOException {
216         writer.beginArray();
217         writer.nullValue();
218         writer.endArray();
219     }
220
221     private void writeName(Node<?> node, DataSchemaNode schema, JsonWriter writer) throws IOException {
222         String nameForOutput = node.getNodeType().getLocalName();
223         if (schema.isAugmenting()) {
224             ControllerContext contContext = ControllerContext.getInstance();
225             CharSequence moduleName;
226             moduleName = contContext.toRestconfIdentifier(schema.getQName());
227             if (moduleName != null) {
228                 nameForOutput = moduleName.toString();
229             }
230         }
231         writer.name(nameForOutput);
232     }
233
234     private static final class NumberForJsonWriter extends Number {
235
236         private static final long serialVersionUID = -3147729419814417666L;
237         private final String value;
238
239         public NumberForJsonWriter(String value) {
240             this.value = value;
241         }
242
243         @Override
244         public int intValue() {
245             throw new IllegalStateException("Should not be invoked");
246         }
247
248         @Override
249         public long longValue() {
250             throw new IllegalStateException("Should not be invoked");
251         }
252
253         @Override
254         public float floatValue() {
255             throw new IllegalStateException("Should not be invoked");
256         }
257
258         @Override
259         public double doubleValue() {
260             throw new IllegalStateException("Should not be invoked");
261         }
262
263         @Override
264         public String toString() {
265             return value;
266         }
267
268     }
269
270 }