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