5a1b42fd8009d074613407088c94359bac5dc6d9
[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.HashSet;
7 import java.util.List;
8 import java.util.Set;
9
10 import javax.activation.UnsupportedDataTypeException;
11
12 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
13 import org.opendaylight.yangtools.yang.data.api.Node;
14 import org.opendaylight.yangtools.yang.data.api.SimpleNode;
15 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
16 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
17 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
18 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
19 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
20 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
21 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
22 import org.opendaylight.yangtools.yang.model.api.type.BooleanTypeDefinition;
23 import org.opendaylight.yangtools.yang.model.api.type.DecimalTypeDefinition;
24 import org.opendaylight.yangtools.yang.model.api.type.EmptyTypeDefinition;
25 import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
26 import org.opendaylight.yangtools.yang.model.api.type.IntegerTypeDefinition;
27 import org.opendaylight.yangtools.yang.model.api.type.UnsignedIntegerTypeDefinition;
28
29 import com.google.common.base.Preconditions;
30 import com.google.gson.stream.JsonWriter;
31
32 class JsonMapper {
33     
34     private final Set<LeafListSchemaNode> foundLeafLists = new HashSet<>();
35     private final Set<ListSchemaNode> foundLists = new HashSet<>();
36     
37     public void write(JsonWriter writer, CompositeNode data, DataNodeContainer schema) throws IOException {
38         Preconditions.checkNotNull(writer);
39         Preconditions.checkNotNull(data);
40         Preconditions.checkNotNull(schema);
41
42         writer.beginObject();
43         
44         if (schema instanceof ContainerSchemaNode) {
45             writeContainer(writer, data, (ContainerSchemaNode) schema);
46         } else if (schema instanceof ListSchemaNode) {
47             writeList(writer, data, (ListSchemaNode) schema);
48         } else {
49             throw new UnsupportedDataTypeException(
50                     "Schema can be ContainerSchemaNode or ListSchemaNode. Other types are not supported yet.");
51         }
52         
53         writer.endObject();
54         
55         foundLeafLists.clear();
56         foundLists.clear();
57     }
58
59     private void writeChildrenOfParent(JsonWriter writer, CompositeNode parent, DataNodeContainer parentSchema) throws IOException {
60         checkNotNull(parent);
61         checkNotNull(parentSchema);
62         
63         for (Node<?> child : parent.getChildren()) {
64             DataSchemaNode childSchema = findFirstSchemaForNode(child, parentSchema.getChildNodes());
65             if (childSchema == null) {
66                 throw new UnsupportedDataTypeException("Probably the data node \"" + child.getNodeType().getLocalName()
67                         + "\" is not conform to schema");
68             }
69             
70             if (childSchema instanceof ContainerSchemaNode) {
71                 Preconditions.checkState(child instanceof CompositeNode,
72                         "Data representation of Container should be CompositeNode - " + child.getNodeType());
73                 writeContainer(writer, (CompositeNode) child, (ContainerSchemaNode) childSchema);
74             } else if (childSchema instanceof ListSchemaNode) {
75                 if (!foundLists.contains(childSchema)) {
76                     Preconditions.checkState(child instanceof CompositeNode,
77                             "Data representation of List should be CompositeNode - " + child.getNodeType());
78                     foundLists.add((ListSchemaNode) childSchema);
79                     writeList(writer, (CompositeNode) child, (ListSchemaNode) childSchema);
80                 }
81             } else if (childSchema instanceof LeafListSchemaNode) {
82                 if (!foundLeafLists.contains(childSchema)) {
83                     Preconditions.checkState(child instanceof SimpleNode<?>,
84                             "Data representation of LeafList should be SimpleNode - " + child.getNodeType());
85                     foundLeafLists.add((LeafListSchemaNode) childSchema);
86                     writeLeafList(writer, (SimpleNode<?>) child, (LeafListSchemaNode) childSchema);
87                 }
88             } else if (childSchema instanceof LeafSchemaNode) {
89                 Preconditions.checkState(child instanceof SimpleNode<?>,
90                         "Data representation of LeafList should be SimpleNode - " + child.getNodeType());
91                 writeLeaf(writer, (SimpleNode<?>) child, (LeafSchemaNode) childSchema);
92             } else {
93                 throw new UnsupportedDataTypeException("Schema can be ContainerSchemaNode, ListSchemaNode, "
94                         + "LeafListSchemaNode, or LeafSchemaNode. Other types are not supported yet.");
95             }
96         }
97         
98         for (Node<?> child : parent.getChildren()) {
99             DataSchemaNode childSchema = findFirstSchemaForNode(child, parentSchema.getChildNodes());
100             if (childSchema instanceof LeafListSchemaNode) {
101                 foundLeafLists.remove((LeafListSchemaNode) childSchema);
102             } else if (childSchema instanceof ListSchemaNode) {
103                 foundLists.remove((ListSchemaNode) childSchema);
104             }
105         }
106     }
107     
108     private DataSchemaNode findFirstSchemaForNode(Node<?> node, Set<DataSchemaNode> dataSchemaNode) {
109         for (DataSchemaNode dsn : dataSchemaNode) {
110             if (node.getNodeType().getLocalName().equals(dsn.getQName().getLocalName())) {
111                 return dsn;
112             }
113         }
114         return null;
115     }
116     
117     private void writeContainer(JsonWriter writer, CompositeNode node, ContainerSchemaNode schema) throws IOException {
118         writer.name(node.getNodeType().getLocalName());
119         writer.beginObject();
120         writeChildrenOfParent(writer, node, schema);
121         writer.endObject();
122     }
123     
124     private void writeList(JsonWriter writer, CompositeNode node, ListSchemaNode schema) throws IOException {
125             writer.name(node.getNodeType().getLocalName());
126             writer.beginArray();
127             
128             if (node.getParent() != null) {
129                 CompositeNode parent = node.getParent();
130                 List<CompositeNode> nodeLists = parent.getCompositesByName(node.getNodeType());
131                 for (CompositeNode nodeList : nodeLists) {
132                     writer.beginObject();
133                     writeChildrenOfParent(writer, nodeList, schema);
134                     writer.endObject();
135                 }
136             } else {
137                 writer.beginObject();
138                 writeChildrenOfParent(writer, node, schema);
139                 writer.endObject();
140             }
141             
142             writer.endArray();
143     }
144     
145     private void writeLeafList(JsonWriter writer, SimpleNode<?> node, LeafListSchemaNode schema) throws IOException {
146             writer.name(node.getNodeType().getLocalName());
147             writer.beginArray();
148             
149             CompositeNode parent = node.getParent();
150             List<SimpleNode<?>> nodeLeafLists = parent.getSimpleNodesByName(node.getNodeType());
151             for (SimpleNode<?> nodeLeafList : nodeLeafLists) {
152                 writeValueOfNodeByType(writer, nodeLeafList, schema.getType());
153             }
154             
155             writer.endArray();
156     }
157     
158     private void writeLeaf(JsonWriter writer, SimpleNode<?> node, LeafSchemaNode schema) throws IOException {
159         writer.name(node.getNodeType().getLocalName());
160         writeValueOfNodeByType(writer, node, schema.getType());
161     }
162     
163     private void writeValueOfNodeByType(JsonWriter writer, SimpleNode<?> node, TypeDefinition<?> type) throws IOException {
164         if (!(node.getValue() instanceof String)) {
165             throw new IllegalStateException("Value in SimpleNode should be type String");
166         }
167         
168         String value = (String) node.getValue();
169         // TODO check Leafref, InstanceIdentifierTypeDefinition, IdentityrefTypeDefinition, UnionTypeDefinition
170         if (type.getBaseType() != null) {
171             writeValueOfNodeByType(writer, node, type.getBaseType());
172         } else if (type instanceof InstanceIdentifierTypeDefinition) {
173             writer.value(((InstanceIdentifierTypeDefinition) type).getPathStatement().toString());
174         } else if (type instanceof DecimalTypeDefinition 
175                 || type instanceof IntegerTypeDefinition
176                 || type instanceof UnsignedIntegerTypeDefinition) {
177             writer.value(new NumberForJsonWriter(value));
178         } else if (type instanceof BooleanTypeDefinition) {
179             writer.value(Boolean.parseBoolean(value));
180         } else if (type instanceof EmptyTypeDefinition) {
181             writer.beginArray();
182             writer.nullValue();
183             writer.endArray();
184         } else {
185             writer.value(value != null ? value : "");
186         }
187     }
188     
189     private static final class NumberForJsonWriter extends Number {
190         
191         private static final long serialVersionUID = -3147729419814417666L;
192         private final String value;
193         
194         public NumberForJsonWriter(String value) {
195             this.value = value;
196         }
197
198         @Override
199         public int intValue() {
200             throw new IllegalStateException("Should not be invoked");
201         }
202
203         @Override
204         public long longValue() {
205             throw new IllegalStateException("Should not be invoked");
206         }
207
208         @Override
209         public float floatValue() {
210             throw new IllegalStateException("Should not be invoked");
211         }
212
213         @Override
214         public double doubleValue() {
215             throw new IllegalStateException("Should not be invoked");
216         }
217
218         @Override
219         public String toString() {
220             return value;
221         }
222         
223     }
224
225 }