BUG 720 - YANG leaf as JSON input *<*:* couldn't be saved
[controller.git] / opendaylight / md-sal / sal-clustering-commons / src / main / java / org / opendaylight / controller / cluster / datastore / node / utils / serialization / NormalizedNodeSerializer.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
9 package org.opendaylight.controller.cluster.datastore.node.utils.serialization;
10
11 import com.google.common.base.Preconditions;
12
13 import org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages;
14 import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil;
15 import org.opendaylight.yangtools.yang.data.api.Node;
16 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
17 import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode;
18 import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
19 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
20 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
21 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
22 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
23 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
24 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
25 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
26 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
27 import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode;
28 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
29 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
30 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
31 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
32 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
33 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
34 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.ListNodeBuilder;
35 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeAttrBuilder;
36
37 import java.net.URI;
38 import java.util.ArrayList;
39 import java.util.Date;
40 import java.util.EnumMap;
41 import java.util.HashMap;
42 import java.util.List;
43 import java.util.Map;
44
45 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.ANY_XML_NODE_TYPE;
46 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.AUGMENTATION_NODE_TYPE;
47 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.CHOICE_NODE_TYPE;
48 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.CONTAINER_NODE_TYPE;
49 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.LEAF_NODE_TYPE;
50 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.LEAF_SET_ENTRY_NODE_TYPE;
51 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.LEAF_SET_NODE_TYPE;
52 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.MAP_ENTRY_NODE_TYPE;
53 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.MAP_NODE_TYPE;
54 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.ORDERED_LEAF_SET_NODE_TYPE;
55 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.ORDERED_MAP_NODE_TYPE;
56 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.UNKEYED_LIST_ENTRY_NODE_TYPE;
57 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.UNKEYED_LIST_NODE_TYPE;
58 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.getSerializableNodeType;
59
60 /**
61  * NormalizedNodeSerializer can be used to convert a Normalized node to and and
62  * from a protocol buffer message.
63  *
64  *
65  */
66 public class NormalizedNodeSerializer {
67
68     /**
69      * Serialize a NormalizedNode into a protocol buffer message
70      * <p>
71      * The significant things to be aware of the Serialization process are
72      * <ul>
73      *     <li>Repeated strings like namespaces, revisions and localNames are
74      *     compressed to codes and stored in the top level of the normalized
75      *     node protocol buffer message
76      *     </li>
77      *     <li>All value types are encoded for each leaf value. This is so that
78      *     the deSerialization process does not need to use the schema context to
79      *     figure out how to decode values
80      *     </li>
81      * </ul>
82      *
83      * One question which may arise is why not use something like gzip to
84      * compress the protocol buffer message instead of rolling our own
85      * encoding scheme. This has to be explored further as it is a more
86      * general solution.
87      *
88      * @param node
89      * @return
90      */
91     public static NormalizedNodeMessages.Node serialize(NormalizedNode node){
92         Preconditions.checkNotNull(node, "node should not be null");
93         return new Serializer(node).serialize();
94     }
95
96
97     /**
98      * DeSerialize a protocol buffer message back into a NormalizedNode
99      *
100      * @param node
101      * @return
102      */
103     public static NormalizedNode deSerialize(NormalizedNodeMessages.Node node){
104         return new DeSerializer(node).deSerialize();
105     }
106
107     /**
108      * DeSerialize a PathArgument which is in the protocol buffer format into
109      * a yang PathArgument. The protocol buffer path argument is specially
110      * encoded and can only be interpreted in the context of a top level
111      * serialized NormalizedNode protocol buffer message. The reason for this
112      * is that during the NormalizedNode serialization process certain repeated
113      * strings are encoded into a "codes" list and the actual strings are
114      * replaced by an integer which is an index into that list.
115      *
116      * @param node
117      * @param pathArgument
118      * @return
119      */
120     public static YangInstanceIdentifier.PathArgument deSerialize(NormalizedNodeMessages.Node node, NormalizedNodeMessages.PathArgument pathArgument){
121         Preconditions.checkNotNull(node, "node should not be null");
122         Preconditions.checkNotNull(pathArgument, "pathArgument should not be null");
123         return new DeSerializer(node).deSerialize(pathArgument);
124     }
125
126     private static class Serializer implements NormalizedNodeSerializationContext {
127
128         private final NormalizedNode node;
129
130         private final Map<Object, Integer> codeMap = new HashMap<>();
131         private final List<String> codes = new ArrayList<>();
132
133         private Serializer(NormalizedNode node) {
134             this.node = node;
135         }
136
137         private NormalizedNodeMessages.Node serialize() {
138             return this.serialize(node).addAllCode(codes).build();
139         }
140
141         private NormalizedNodeMessages.Node.Builder serialize(
142             NormalizedNode node) {
143             NormalizedNodeMessages.Node.Builder builder =
144                 NormalizedNodeMessages.Node.newBuilder();
145
146             builder.setPathArgument(PathArgumentSerializer.serialize(this, node.getIdentifier()));
147             Integer nodeType = getSerializableNodeType(node).ordinal();
148             builder.setIntType(nodeType);
149             Object value = node.getValue();
150
151             // We need to do a specific check of the type of the node here
152             // because if we looked at the value type alone we will not be
153             // able to distinguish if the value was supposed to be added
154             // as a child or whether the value should be added as a value of the
155             // node.
156             // One use case where this check is necessary when you have a node
157             // with a bits value. In that case the value of the node is a Set
158             // which is also a Collection. Without the following check being
159             // done first the code would flow into the Collection if condition
160             // and the Set would be added as child nodes
161             if(nodeType == NormalizedNodeType.LEAF_NODE_TYPE.ordinal() ||
162                nodeType == NormalizedNodeType.LEAF_SET_ENTRY_NODE_TYPE.ordinal()){
163
164                 ValueSerializer.serialize(builder, this, value);
165
166             } else if (value instanceof Iterable) {
167                 Iterable iterable = (Iterable) value;
168
169                 for (Object o : iterable) {
170                     if (o instanceof NormalizedNode) {
171                         builder.addChild(serialize((NormalizedNode) o));
172                     }
173                 }
174
175             } else if (value instanceof NormalizedNode) {
176
177                 builder.addChild(serialize((NormalizedNode) value));
178
179             } else {
180
181                 ValueSerializer.serialize(builder, this, value);
182             }
183
184             return builder;
185         }
186
187
188         @Override public int addNamespace(URI namespace) {
189             int namespaceInt = getCode(namespace);
190
191             if(namespaceInt == -1) {
192                 namespaceInt = addCode(namespace, namespace.toString());
193             }
194             return namespaceInt;
195         }
196
197         @Override public int addRevision(Date revision) {
198             if(revision == null){
199                 return -1;
200             }
201
202             int revisionInt = getCode(revision);
203             if(revisionInt == -1) {
204                 String formattedRevision =
205                     SimpleDateFormatUtil.getRevisionFormat().format(revision);
206                 revisionInt = addCode(revision, formattedRevision);
207             }
208             return revisionInt;
209         }
210
211         @Override public int addLocalName(String localName) {
212             int localNameInt = getCode(localName);
213             if(localNameInt == -1) {
214                 localNameInt = addCode(localName, localName.toString());
215             }
216             return localNameInt;
217
218         }
219
220         public int addCode(Object code, String codeStr){
221             int count = codes.size();
222             codes.add(codeStr);
223             codeMap.put(code, Integer.valueOf(count));
224             return count;
225         }
226
227         public int getCode(Object code){
228             if(codeMap.containsKey(code)){
229                 return codeMap.get(code);
230             }
231             return -1;
232         }
233     }
234
235     private static class DeSerializer implements NormalizedNodeDeSerializationContext {
236         private static Map<NormalizedNodeType, DeSerializationFunction>
237             deSerializationFunctions = new EnumMap<>(NormalizedNodeType.class);
238
239         static {
240             deSerializationFunctions.put(CONTAINER_NODE_TYPE,
241                 new DeSerializationFunction() {
242                     @Override public NormalizedNode apply(
243                         DeSerializer deSerializer,
244                         NormalizedNodeMessages.Node node) {
245                         DataContainerNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifier, ContainerNode>
246                             builder = Builders.containerBuilder();
247
248                         builder
249                             .withNodeIdentifier(deSerializer.toNodeIdentifier(
250                                 node.getPathArgument()));
251
252                         return deSerializer.buildDataContainer(builder, node);
253
254                     }
255
256                 });
257
258             deSerializationFunctions.put(LEAF_NODE_TYPE,
259                 new DeSerializationFunction() {
260                     @Override public NormalizedNode apply(
261                         DeSerializer deSerializer,
262                         NormalizedNodeMessages.Node node) {
263                         NormalizedNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifier, Object, LeafNode<Object>>
264                             builder = Builders.leafBuilder();
265
266                         builder
267                             .withNodeIdentifier(deSerializer.toNodeIdentifier(
268                                 node.getPathArgument()));
269
270                         return deSerializer.buildNormalizedNode(builder, node);
271
272                     }
273                 });
274
275             deSerializationFunctions.put(MAP_NODE_TYPE,
276                 new DeSerializationFunction() {
277                     @Override public NormalizedNode apply(
278                         DeSerializer deSerializer,
279                         NormalizedNodeMessages.Node node) {
280                         CollectionNodeBuilder<MapEntryNode, MapNode>
281                             builder = Builders.mapBuilder();
282
283                         return deSerializer.buildCollectionNode(builder, node);
284                     }
285                 });
286
287             deSerializationFunctions.put(MAP_ENTRY_NODE_TYPE,
288                 new DeSerializationFunction() {
289                     @Override public NormalizedNode apply(
290                         DeSerializer deSerializer,
291                         NormalizedNodeMessages.Node node) {
292                         DataContainerNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifierWithPredicates, MapEntryNode>
293                             builder = Builders.mapEntryBuilder();
294
295                         builder.withNodeIdentifier(deSerializer.toNodeIdentifierWithPredicates(
296                             node.getPathArgument()));
297
298                         return deSerializer.buildDataContainer(builder, node);
299                     }
300                 });
301
302             deSerializationFunctions.put(AUGMENTATION_NODE_TYPE,
303                 new DeSerializationFunction() {
304                     @Override public NormalizedNode apply(
305                         DeSerializer deSerializer,
306                         NormalizedNodeMessages.Node node) {
307                         DataContainerNodeBuilder<YangInstanceIdentifier.AugmentationIdentifier, AugmentationNode>
308                             builder = Builders.augmentationBuilder();
309
310                         builder.withNodeIdentifier(
311                             deSerializer.toAugmentationIdentifier(
312                                 node.getPathArgument()));
313
314                         return deSerializer.buildDataContainer(builder, node);
315                     }
316                 });
317
318             deSerializationFunctions.put(LEAF_SET_NODE_TYPE,
319                 new DeSerializationFunction() {
320                     @Override public NormalizedNode apply(
321                         DeSerializer deSerializer,
322                         NormalizedNodeMessages.Node node) {
323                         ListNodeBuilder<Object, LeafSetEntryNode<Object>>
324                             builder = Builders.leafSetBuilder();
325
326                         return deSerializer.buildListNode(builder, node);
327                     }
328                 });
329
330             deSerializationFunctions.put(LEAF_SET_ENTRY_NODE_TYPE,
331                 new DeSerializationFunction() {
332                     @Override public NormalizedNode apply(
333                         DeSerializer deSerializer,
334                         NormalizedNodeMessages.Node node) {
335                         NormalizedNodeAttrBuilder<YangInstanceIdentifier.NodeWithValue, Object, LeafSetEntryNode<Object>>
336                             builder = Builders.leafSetEntryBuilder();
337
338                         builder.withNodeIdentifier(deSerializer.toNodeWithValue(
339                             node.getPathArgument()));
340
341                         return deSerializer.buildNormalizedNode(builder, node);
342                     }
343                 });
344
345             deSerializationFunctions.put(CHOICE_NODE_TYPE,
346                 new DeSerializationFunction() {
347                     @Override public NormalizedNode apply(
348                         DeSerializer deSerializer,
349                         NormalizedNodeMessages.Node node) {
350                         DataContainerNodeBuilder<YangInstanceIdentifier.NodeIdentifier, ChoiceNode>
351                             builder =
352                             Builders.choiceBuilder();
353
354                         builder
355                             .withNodeIdentifier(deSerializer.toNodeIdentifier(
356                                 node.getPathArgument()));
357
358                         return deSerializer.buildDataContainer(builder, node);
359                     }
360                 });
361
362             deSerializationFunctions.put(ORDERED_LEAF_SET_NODE_TYPE,
363                 new DeSerializationFunction() {
364                     @Override public NormalizedNode apply(
365                         DeSerializer deSerializer,
366                         NormalizedNodeMessages.Node node) {
367                         ListNodeBuilder<Object, LeafSetEntryNode<Object>>
368                             builder =
369                             Builders.orderedLeafSetBuilder();
370
371                         return deSerializer.buildListNode(builder, node);
372
373
374                     }
375                 });
376
377             deSerializationFunctions.put(ORDERED_MAP_NODE_TYPE,
378                 new DeSerializationFunction() {
379                     @Override public NormalizedNode apply(
380                         DeSerializer deSerializer,
381                         NormalizedNodeMessages.Node node) {
382                         CollectionNodeBuilder<MapEntryNode, OrderedMapNode>
383                             builder =
384                             Builders.orderedMapBuilder();
385
386                         return deSerializer.buildCollectionNode(builder, node);
387                     }
388                 });
389
390             deSerializationFunctions.put(UNKEYED_LIST_NODE_TYPE,
391                 new DeSerializationFunction() {
392                     @Override public NormalizedNode apply(
393                         DeSerializer deSerializer,
394                         NormalizedNodeMessages.Node node) {
395                         CollectionNodeBuilder<UnkeyedListEntryNode, UnkeyedListNode>
396                             builder =
397                             Builders.unkeyedListBuilder();
398
399                         return deSerializer.buildCollectionNode(builder, node);
400                     }
401                 });
402
403             deSerializationFunctions.put(UNKEYED_LIST_ENTRY_NODE_TYPE,
404                 new DeSerializationFunction() {
405                     @Override public NormalizedNode apply(
406                         DeSerializer deSerializer,
407                         NormalizedNodeMessages.Node node) {
408                         DataContainerNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifier, UnkeyedListEntryNode>
409                             builder =
410                             Builders.unkeyedListEntryBuilder();
411
412                         builder
413                             .withNodeIdentifier(deSerializer.toNodeIdentifier(
414                                 node.getPathArgument()));
415
416                         return deSerializer.buildDataContainer(builder, node);
417                     }
418                 });
419
420             deSerializationFunctions.put(ANY_XML_NODE_TYPE,
421                 new DeSerializationFunction() {
422
423                     @Override public NormalizedNode apply(
424                         DeSerializer deSerializer,
425                         NormalizedNodeMessages.Node node) {
426                         NormalizedNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifier, Node<?>, AnyXmlNode>
427                             builder =
428                             Builders.anyXmlBuilder();
429
430                         builder
431                             .withNodeIdentifier(deSerializer.toNodeIdentifier(
432                                 node.getPathArgument()));
433
434                         return deSerializer.buildNormalizedNode(builder, node);
435                     }
436                 });
437
438         }
439
440         private final NormalizedNodeMessages.Node node;
441
442         public DeSerializer(NormalizedNodeMessages.Node node){
443             this.node = node;
444         }
445
446         public NormalizedNode deSerialize(){
447             return deSerialize(node);
448         }
449
450         private NormalizedNode deSerialize(NormalizedNodeMessages.Node node){
451             Preconditions.checkNotNull(node, "node should not be null");
452
453             DeSerializationFunction deSerializationFunction = deSerializationFunctions.get(
454                     NormalizedNodeType.values()[node.getIntType()]);
455
456             return deSerializationFunction.apply(this, node);
457         }
458
459
460         private NormalizedNode buildCollectionNode(
461             CollectionNodeBuilder builder,
462             NormalizedNodeMessages.Node node) {
463
464             builder.withNodeIdentifier(toNodeIdentifier(node.getPathArgument()));
465
466             for(NormalizedNodeMessages.Node child : node.getChildList()){
467                 builder.withChild(deSerialize(child));
468             }
469
470             return builder.build();
471         }
472
473
474         private NormalizedNode buildListNode(
475             ListNodeBuilder<Object, LeafSetEntryNode<Object>> builder,
476             NormalizedNodeMessages.Node node) {
477             builder.withNodeIdentifier(toNodeIdentifier(node.getPathArgument()));
478
479             for(NormalizedNodeMessages.Node child : node.getChildList()){
480                 builder.withChild((LeafSetEntryNode<Object>) deSerialize(child));
481             }
482
483             return builder.build();
484         }
485
486         private NormalizedNode buildDataContainer(DataContainerNodeBuilder builder, NormalizedNodeMessages.Node node){
487
488             for(NormalizedNodeMessages.Node child : node.getChildList()){
489                 builder.withChild((DataContainerChild<?, ?>) deSerialize(child));
490             }
491
492             //TODO : Also handle attributes
493
494             return builder.build();
495         }
496
497         private NormalizedNode buildNormalizedNode(NormalizedNodeAttrBuilder builder, NormalizedNodeMessages.Node node){
498
499             builder.withValue(ValueSerializer.deSerialize(this, node));
500
501             //TODO : Also handle attributes
502
503             return builder.build();
504
505         }
506
507
508         private YangInstanceIdentifier.NodeIdentifierWithPredicates toNodeIdentifierWithPredicates(
509             NormalizedNodeMessages.PathArgument path) {
510             return (YangInstanceIdentifier.NodeIdentifierWithPredicates) PathArgumentSerializer.deSerialize(this, path);
511         }
512
513         private YangInstanceIdentifier.AugmentationIdentifier toAugmentationIdentifier(
514             NormalizedNodeMessages.PathArgument path) {
515             return (YangInstanceIdentifier.AugmentationIdentifier) PathArgumentSerializer.deSerialize(this, path);
516         }
517
518         private YangInstanceIdentifier.NodeWithValue toNodeWithValue(
519             NormalizedNodeMessages.PathArgument path) {
520             return (YangInstanceIdentifier.NodeWithValue) PathArgumentSerializer.deSerialize(
521                 this, path);
522         }
523
524         private YangInstanceIdentifier.NodeIdentifier toNodeIdentifier(NormalizedNodeMessages.PathArgument path){
525             return (YangInstanceIdentifier.NodeIdentifier) PathArgumentSerializer.deSerialize(
526                 this, path);
527         }
528
529         @Override public String getNamespace(int namespace) {
530             return node.getCode(namespace);
531         }
532
533         @Override public String getRevision(int revision) {
534             return node.getCode(revision);
535         }
536
537         @Override public String getLocalName(int localName) {
538             return node.getCode(localName);
539         }
540
541         public YangInstanceIdentifier.PathArgument deSerialize(
542             NormalizedNodeMessages.PathArgument pathArgument) {
543             return PathArgumentSerializer.deSerialize(this, pathArgument);
544         }
545
546         private static interface DeSerializationFunction {
547             NormalizedNode apply(DeSerializer deserializer, NormalizedNodeMessages.Node node);
548         }
549     }
550 }