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