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