Fix CS warnings in sal-clustering-commons and enable enforcement
[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 com.google.common.collect.Maps;
28 import java.util.EnumMap;
29 import java.util.Map;
30 import javax.xml.transform.dom.DOMSource;
31 import org.opendaylight.controller.cluster.datastore.util.InstanceIdentifierUtils;
32 import org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages;
33 import org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.Node.Builder;
34 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
35 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
36 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
37 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
38 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
39 import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode;
40 import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
41 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
42 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
43 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
44 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
45 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
46 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
47 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
48 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
49 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
50 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
51 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
52 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
53 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.ListNodeBuilder;
54 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeAttrBuilder;
55
56 /**
57  * NormalizedNodeSerializer can be used to convert a Normalized node to and and
58  * from a protocol buffer message.
59  *
60  *
61  */
62 public class NormalizedNodeSerializer {
63
64     /**
65      * Serialize a NormalizedNode into a protocol buffer message
66      * <p/>
67      * The significant things to be aware of the Serialization process are
68      * <ul>
69      *     <li>Repeated strings like namespaces, revisions and localNames are
70      *     compressed to codes and stored in the top level of the normalized
71      *     node protocol buffer message
72      *     </li>
73      *     <li>All value types are encoded for each leaf value. This is so that
74      *     the deSerialization process does not need to use the schema context to
75      *     figure out how to decode values
76      *     </li>
77      * </ul>
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 the node
84      * @return a NormalizedNodeMessages.Node
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 the node
100      * @return a NormalizedNode
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     /**
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     public static YangInstanceIdentifier.PathArgument deSerialize(final NormalizedNodeMessages.Node node,
117             final NormalizedNodeMessages.PathArgument pathArgument) {
118         Preconditions.checkNotNull(node, "node should not be null");
119         Preconditions.checkNotNull(pathArgument, "pathArgument should not be null");
120         return new DeSerializer(null, node).deSerialize(pathArgument);
121     }
122
123     public static DeSerializer newDeSerializer(final NormalizedNodeMessages.InstanceIdentifier path,
124             final NormalizedNodeMessages.Node node) {
125         Preconditions.checkNotNull(node, "node should not be null");
126         return new DeSerializer(path, node);
127     }
128
129     public static class Serializer extends QNameSerializationContextImpl
130                                    implements NormalizedNodeSerializationContext {
131
132         private final NormalizedNode<?, ?> node;
133
134         private NormalizedNodeMessages.InstanceIdentifier serializedPath;
135
136         private Serializer(final NormalizedNode<?, ?> node) {
137             this.node = node;
138         }
139
140         public NormalizedNodeMessages.InstanceIdentifier getSerializedPath() {
141             return serializedPath;
142         }
143
144         public NormalizedNodeMessages.Node serialize() {
145             return this.serialize(node).addAllCode(getCodes()).build();
146         }
147
148         public NormalizedNodeMessages.Node serialize(final YangInstanceIdentifier path) {
149             Builder builder = serialize(node);
150             serializedPath = InstanceIdentifierUtils.toSerializable(path, this);
151             return builder.addAllCode(getCodes()).build();
152         }
153
154         private NormalizedNodeMessages.Node.Builder serialize(final NormalizedNode<?, ?> fromNode) {
155             NormalizedNodeMessages.Node.Builder builder =
156                 NormalizedNodeMessages.Node.newBuilder();
157
158             builder.setPathArgument(PathArgumentSerializer.serialize(this, fromNode.getIdentifier()));
159             Integer nodeType = getSerializableNodeType(fromNode).ordinal();
160             builder.setIntType(nodeType);
161             Object value = fromNode.getValue();
162
163             // We need to do a specific check of the type of the node here
164             // because if we looked at the value type alone we will not be
165             // able to distinguish if the value was supposed to be added
166             // as a child or whether the value should be added as a value of the
167             // node.
168             // One use case where this check is necessary when you have a node
169             // with a bits value. In that case the value of the node is a Set
170             // which is also a Collection. Without the following check being
171             // done first the code would flow into the Collection if condition
172             // and the Set would be added as child nodes
173             if (nodeType == NormalizedNodeType.LEAF_NODE_TYPE.ordinal()
174                     || nodeType == NormalizedNodeType.LEAF_SET_ENTRY_NODE_TYPE.ordinal()) {
175
176                 ValueSerializer.serialize(builder, this, value);
177
178             } else if (value instanceof Iterable) {
179                 Iterable<?> iterable = (Iterable<?>) value;
180
181                 for (Object o : iterable) {
182                     if (o instanceof NormalizedNode) {
183                         builder.addChild(serialize((NormalizedNode<?, ?>) o));
184                     }
185                 }
186
187             } else if (value instanceof NormalizedNode) {
188
189                 builder.addChild(serialize((NormalizedNode<?, ?>) value));
190
191             } else {
192
193                 ValueSerializer.serialize(builder, this, value);
194             }
195
196             return builder;
197         }
198     }
199
200     @SuppressWarnings("rawtypes")
201     public static class DeSerializer extends QNameDeSerializationContextImpl
202                                      implements NormalizedNodeDeSerializationContext {
203         private static final Map<NormalizedNodeType, DeSerializationFunction> DESERIALIZATION_FUNCTIONS;
204
205         static {
206             final EnumMap<NormalizedNodeType, DeSerializationFunction> m = new EnumMap<>(NormalizedNodeType.class);
207
208             m.put(CONTAINER_NODE_TYPE, (deSerializer, node) -> {
209                 DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> builder = Builders.containerBuilder()
210                         .withNodeIdentifier(deSerializer.toNodeIdentifier(node.getPathArgument()));
211
212                 return deSerializer.buildDataContainer(builder, node);
213             });
214             m.put(LEAF_NODE_TYPE, (deSerializer, node) -> {
215                 NormalizedNodeAttrBuilder<NodeIdentifier, Object, LeafNode<Object>> builder = Builders.leafBuilder()
216                         .withNodeIdentifier(deSerializer.toNodeIdentifier(node.getPathArgument()));
217
218                 return deSerializer.buildNormalizedNode(builder, node);
219             });
220             m.put(MAP_NODE_TYPE, (deSerializer, node) -> deSerializer.buildCollectionNode(Builders.mapBuilder(), node));
221             m.put(MAP_ENTRY_NODE_TYPE, (deSerializer, node) -> {
222                 DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder =
223                         Builders.mapEntryBuilder().withNodeIdentifier(deSerializer.toNodeIdentifierWithPredicates(
224                             node.getPathArgument()));
225
226                 return deSerializer.buildDataContainer(builder, node);
227             });
228             m.put(AUGMENTATION_NODE_TYPE, (deSerializer, node) -> {
229                 DataContainerNodeBuilder<AugmentationIdentifier, AugmentationNode> builder =
230                         Builders.augmentationBuilder().withNodeIdentifier(
231                             deSerializer.toAugmentationIdentifier(node.getPathArgument()));
232
233                 return deSerializer.buildDataContainer(builder, node);
234             });
235             m.put(LEAF_SET_NODE_TYPE, (deSerializer, node)
236                 -> deSerializer.buildListNode(Builders.leafSetBuilder(), node));
237             m.put(LEAF_SET_ENTRY_NODE_TYPE, (deSerializer, node) -> {
238                 NormalizedNodeAttrBuilder<NodeWithValue, Object, LeafSetEntryNode<Object>> builder =
239                         Builders.leafSetEntryBuilder().withNodeIdentifier(deSerializer.toNodeWithValue(
240                             node.getPathArgument()));
241
242                 return deSerializer.buildNormalizedNode(builder, node);
243             });
244             m.put(CHOICE_NODE_TYPE, (deSerializer, node) -> {
245                 DataContainerNodeBuilder<NodeIdentifier, ChoiceNode> builder = Builders.choiceBuilder()
246                         .withNodeIdentifier(deSerializer.toNodeIdentifier(node.getPathArgument()));
247
248                 return deSerializer.buildDataContainer(builder, node);
249             });
250             m.put(ORDERED_LEAF_SET_NODE_TYPE, (deSerializer, node)
251                 -> deSerializer.buildListNode(Builders.orderedLeafSetBuilder(), node));
252             m.put(ORDERED_MAP_NODE_TYPE, (deSerializer, node)
253                 -> deSerializer.buildCollectionNode(Builders.orderedMapBuilder(), node));
254             m.put(UNKEYED_LIST_NODE_TYPE, (deSerializer, node)
255                 -> deSerializer.buildCollectionNode(Builders.unkeyedListBuilder(), node));
256             m.put(UNKEYED_LIST_ENTRY_NODE_TYPE, (deSerializer, node) -> {
257                 DataContainerNodeAttrBuilder<NodeIdentifier, UnkeyedListEntryNode> builder =
258                         Builders.unkeyedListEntryBuilder().withNodeIdentifier(deSerializer.toNodeIdentifier(
259                             node.getPathArgument()));
260
261                 return deSerializer.buildDataContainer(builder, node);
262             });
263             m.put(ANY_XML_NODE_TYPE, (deSerializer, node) -> {
264                 NormalizedNodeAttrBuilder<NodeIdentifier, DOMSource, AnyXmlNode> builder = Builders.anyXmlBuilder()
265                         .withNodeIdentifier(deSerializer.toNodeIdentifier(node.getPathArgument()));
266
267                 return deSerializer.buildNormalizedNode(builder, node);
268             });
269
270             DESERIALIZATION_FUNCTIONS = Maps.immutableEnumMap(m);
271         }
272
273         private final NormalizedNodeMessages.Node node;
274         private final NormalizedNodeMessages.InstanceIdentifier path;
275         private YangInstanceIdentifier deserializedPath;
276
277         public DeSerializer(final NormalizedNodeMessages.InstanceIdentifier path,
278                 final NormalizedNodeMessages.Node node) {
279             super(node.getCodeList());
280             this.path = path;
281             this.node = node;
282         }
283
284         public YangInstanceIdentifier getDeserializedPath() {
285             return deserializedPath;
286         }
287
288         public NormalizedNode<?, ?> deSerialize() {
289             NormalizedNode<?, ?> deserializedNode = deSerialize(node);
290             if (path != null) {
291                 deserializedPath = InstanceIdentifierUtils.fromSerializable(path, this);
292             }
293
294             return deserializedNode;
295         }
296
297         private NormalizedNode<?, ?> deSerialize(final NormalizedNodeMessages.Node fromNode) {
298             Preconditions.checkNotNull(fromNode, "node should not be null");
299
300             DeSerializationFunction deSerializationFunction = DESERIALIZATION_FUNCTIONS.get(
301                     NormalizedNodeType.values()[fromNode.getIntType()]);
302
303             return deSerializationFunction.apply(this, fromNode);
304         }
305
306         public YangInstanceIdentifier.PathArgument deSerialize(final NormalizedNodeMessages.PathArgument pathArgument) {
307             return PathArgumentSerializer.deSerialize(this, pathArgument);
308         }
309
310         @SuppressWarnings("unchecked")
311         private NormalizedNode<?, ?> buildCollectionNode(final CollectionNodeBuilder builder,
312                 final NormalizedNodeMessages.Node fromNode) {
313
314             builder.withNodeIdentifier(toNodeIdentifier(fromNode.getPathArgument()));
315
316             for (NormalizedNodeMessages.Node child : fromNode.getChildList()) {
317                 builder.withChild(deSerialize(child));
318             }
319
320             return builder.build();
321         }
322
323
324         @SuppressWarnings("unchecked")
325         private NormalizedNode<?, ?> buildListNode(final ListNodeBuilder<Object, LeafSetEntryNode<Object>> builder,
326                 final NormalizedNodeMessages.Node fromNode) {
327             builder.withNodeIdentifier(toNodeIdentifier(fromNode.getPathArgument()));
328
329             for (NormalizedNodeMessages.Node child : fromNode.getChildList()) {
330                 builder.withChild((LeafSetEntryNode<Object>) deSerialize(child));
331             }
332
333             return builder.build();
334         }
335
336         private NormalizedNode<?, ?> buildDataContainer(final DataContainerNodeBuilder<?, ?> builder,
337                 final NormalizedNodeMessages.Node fromNode) {
338
339             for (NormalizedNodeMessages.Node child : fromNode.getChildList()) {
340                 builder.withChild((DataContainerChild<?, ?>) deSerialize(child));
341             }
342
343             //TODO : Also handle attributes
344
345             return builder.build();
346         }
347
348         @SuppressWarnings("unchecked")
349         private NormalizedNode<?, ?> buildNormalizedNode(final NormalizedNodeAttrBuilder builder,
350                 final NormalizedNodeMessages.Node fromNode) {
351
352             builder.withValue(ValueSerializer.deSerialize(this, fromNode));
353
354             //TODO : Also handle attributes
355
356             return builder.build();
357
358         }
359
360         private NodeIdentifierWithPredicates toNodeIdentifierWithPredicates(
361                 final NormalizedNodeMessages.PathArgument fromPath) {
362             return (NodeIdentifierWithPredicates) PathArgumentSerializer.deSerialize(this, fromPath);
363         }
364
365         private AugmentationIdentifier toAugmentationIdentifier(final NormalizedNodeMessages.PathArgument fromPath) {
366             return (AugmentationIdentifier) PathArgumentSerializer.deSerialize(this, fromPath);
367         }
368
369         @SuppressWarnings("unchecked")
370         private <T> NodeWithValue<T> toNodeWithValue(final NormalizedNodeMessages.PathArgument fromPath) {
371             return (NodeWithValue<T>) PathArgumentSerializer.deSerialize(this, fromPath);
372         }
373
374         private NodeIdentifier toNodeIdentifier(final NormalizedNodeMessages.PathArgument fromPath) {
375             return (NodeIdentifier) PathArgumentSerializer.deSerialize(this, fromPath);
376         }
377
378         private interface DeSerializationFunction {
379             NormalizedNode<?, ?> apply(DeSerializer deserializer, NormalizedNodeMessages.Node node);
380         }
381     }
382 }