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