2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
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
9 package org.opendaylight.controller.cluster.datastore.node.utils.serialization;
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;
26 import com.google.common.base.Preconditions;
27 import java.util.EnumMap;
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;
55 * NormalizedNodeSerializer can be used to convert a Normalized node to and and
56 * from a protocol buffer message.
60 public class NormalizedNodeSerializer {
63 * Serialize a NormalizedNode into a protocol buffer message
65 * The significant things to be aware of the Serialization process are
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
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
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
85 public static NormalizedNodeMessages.Node serialize(NormalizedNode<?, ?> node){
86 Preconditions.checkNotNull(node, "node should not be null");
87 return new Serializer(node).serialize();
90 public static Serializer newSerializer(NormalizedNode<?, ?> node) {
91 Preconditions.checkNotNull(node, "node should not be null");
92 return new Serializer(node);
96 * DeSerialize a protocol buffer message back into a NormalizedNode
101 public static NormalizedNode<?, ?> deSerialize(NormalizedNodeMessages.Node node) {
102 Preconditions.checkNotNull(node, "node should not be null");
103 return new DeSerializer(null, node).deSerialize();
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);
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.
122 * @param pathArgument
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);
132 public static class Serializer extends QNameSerializationContextImpl
133 implements NormalizedNodeSerializationContext {
135 private final NormalizedNode<?, ?> node;
137 private NormalizedNodeMessages.InstanceIdentifier serializedPath;
139 private Serializer(NormalizedNode<?, ?> node) {
143 public NormalizedNodeMessages.InstanceIdentifier getSerializedPath() {
144 return serializedPath;
147 public NormalizedNodeMessages.Node serialize() {
148 return this.serialize(node).addAllCode(getCodes()).build();
151 public NormalizedNodeMessages.Node serialize(YangInstanceIdentifier path) {
152 Builder builder = serialize(node);
153 serializedPath = InstanceIdentifierUtils.toSerializable(path, this);
154 return builder.addAllCode(getCodes()).build();
157 private NormalizedNodeMessages.Node.Builder serialize(
158 NormalizedNode<?, ?> node) {
159 NormalizedNodeMessages.Node.Builder builder =
160 NormalizedNodeMessages.Node.newBuilder();
162 builder.setPathArgument(PathArgumentSerializer.serialize(this, node.getIdentifier()));
163 Integer nodeType = getSerializableNodeType(node).ordinal();
164 builder.setIntType(nodeType);
165 Object value = node.getValue();
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
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()){
180 ValueSerializer.serialize(builder, this, value);
182 } else if (value instanceof Iterable) {
183 Iterable<?> iterable = (Iterable<?>) value;
185 for (Object o : iterable) {
186 if (o instanceof NormalizedNode) {
187 builder.addChild(serialize((NormalizedNode<?, ?>) o));
191 } else if (value instanceof NormalizedNode) {
193 builder.addChild(serialize((NormalizedNode<?, ?>) value));
197 ValueSerializer.serialize(builder, this, value);
204 public static class DeSerializer extends QNameDeSerializationContextImpl
205 implements NormalizedNodeDeSerializationContext {
206 private static Map<NormalizedNodeType, DeSerializationFunction>
207 deSerializationFunctions = new EnumMap<>(NormalizedNodeType.class);
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();
219 .withNodeIdentifier(deSerializer.toNodeIdentifier(
220 node.getPathArgument()));
222 return deSerializer.buildDataContainer(builder, node);
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();
237 .withNodeIdentifier(deSerializer.toNodeIdentifier(
238 node.getPathArgument()));
240 return deSerializer.buildNormalizedNode(builder, node);
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();
253 return deSerializer.buildCollectionNode(builder, node);
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();
265 builder.withNodeIdentifier(deSerializer.toNodeIdentifierWithPredicates(
266 node.getPathArgument()));
268 return deSerializer.buildDataContainer(builder, node);
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();
280 builder.withNodeIdentifier(
281 deSerializer.toAugmentationIdentifier(
282 node.getPathArgument()));
284 return deSerializer.buildDataContainer(builder, node);
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();
296 return deSerializer.buildListNode(builder, node);
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();
308 builder.withNodeIdentifier(deSerializer.toNodeWithValue(
309 node.getPathArgument()));
311 return deSerializer.buildNormalizedNode(builder, node);
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>
322 Builders.choiceBuilder();
325 .withNodeIdentifier(deSerializer.toNodeIdentifier(
326 node.getPathArgument()));
328 return deSerializer.buildDataContainer(builder, node);
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>>
339 Builders.orderedLeafSetBuilder();
341 return deSerializer.buildListNode(builder, node);
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>
354 Builders.orderedMapBuilder();
356 return deSerializer.buildCollectionNode(builder, node);
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>
367 Builders.unkeyedListBuilder();
369 return deSerializer.buildCollectionNode(builder, node);
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>
380 Builders.unkeyedListEntryBuilder();
383 .withNodeIdentifier(deSerializer.toNodeIdentifier(
384 node.getPathArgument()));
386 return deSerializer.buildDataContainer(builder, node);
390 deSerializationFunctions.put(ANY_XML_NODE_TYPE,
391 new DeSerializationFunction() {
393 @Override public NormalizedNode<?, ?> apply(
394 DeSerializer deSerializer,
395 NormalizedNodeMessages.Node node) {
396 NormalizedNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifier, DOMSource, AnyXmlNode>
398 Builders.anyXmlBuilder();
401 .withNodeIdentifier(deSerializer.toNodeIdentifier(
402 node.getPathArgument()));
404 return deSerializer.buildNormalizedNode(builder, node);
410 private final NormalizedNodeMessages.Node node;
411 private final NormalizedNodeMessages.InstanceIdentifier path;
412 private YangInstanceIdentifier deserializedPath;
414 public DeSerializer(NormalizedNodeMessages.InstanceIdentifier path,
415 NormalizedNodeMessages.Node node) {
416 super(node.getCodeList());
421 public YangInstanceIdentifier getDeserializedPath() {
422 return deserializedPath;
425 public NormalizedNode<?, ?> deSerialize() {
426 NormalizedNode<?, ?> deserializedNode = deSerialize(node);
428 deserializedPath = InstanceIdentifierUtils.fromSerializable(path, this);
431 return deserializedNode;
434 private NormalizedNode<?, ?> deSerialize(NormalizedNodeMessages.Node node){
435 Preconditions.checkNotNull(node, "node should not be null");
437 DeSerializationFunction deSerializationFunction = deSerializationFunctions.get(
438 NormalizedNodeType.values()[node.getIntType()]);
440 return deSerializationFunction.apply(this, node);
444 private NormalizedNode<?, ?> buildCollectionNode(
445 CollectionNodeBuilder builder,
446 NormalizedNodeMessages.Node node) {
448 builder.withNodeIdentifier(toNodeIdentifier(node.getPathArgument()));
450 for(NormalizedNodeMessages.Node child : node.getChildList()){
451 builder.withChild(deSerialize(child));
454 return builder.build();
458 private NormalizedNode<?, ?> buildListNode(
459 ListNodeBuilder<Object, LeafSetEntryNode<Object>> builder,
460 NormalizedNodeMessages.Node node) {
461 builder.withNodeIdentifier(toNodeIdentifier(node.getPathArgument()));
463 for(NormalizedNodeMessages.Node child : node.getChildList()){
464 builder.withChild((LeafSetEntryNode<Object>) deSerialize(child));
467 return builder.build();
470 private NormalizedNode<?, ?> buildDataContainer(DataContainerNodeBuilder builder, NormalizedNodeMessages.Node node){
472 for(NormalizedNodeMessages.Node child : node.getChildList()){
473 builder.withChild((DataContainerChild<?, ?>) deSerialize(child));
476 //TODO : Also handle attributes
478 return builder.build();
481 private NormalizedNode<?, ?> buildNormalizedNode(NormalizedNodeAttrBuilder builder, NormalizedNodeMessages.Node node){
483 builder.withValue(ValueSerializer.deSerialize(this, node));
485 //TODO : Also handle attributes
487 return builder.build();
492 private YangInstanceIdentifier.NodeIdentifierWithPredicates toNodeIdentifierWithPredicates(
493 NormalizedNodeMessages.PathArgument path) {
494 return (YangInstanceIdentifier.NodeIdentifierWithPredicates) PathArgumentSerializer.deSerialize(this, path);
497 private YangInstanceIdentifier.AugmentationIdentifier toAugmentationIdentifier(
498 NormalizedNodeMessages.PathArgument path) {
499 return (YangInstanceIdentifier.AugmentationIdentifier) PathArgumentSerializer.deSerialize(this, path);
502 private YangInstanceIdentifier.NodeWithValue toNodeWithValue(
503 NormalizedNodeMessages.PathArgument path) {
504 return (YangInstanceIdentifier.NodeWithValue) PathArgumentSerializer.deSerialize(
508 private YangInstanceIdentifier.NodeIdentifier toNodeIdentifier(NormalizedNodeMessages.PathArgument path){
509 return (YangInstanceIdentifier.NodeIdentifier) PathArgumentSerializer.deSerialize(
513 public YangInstanceIdentifier.PathArgument deSerialize(
514 NormalizedNodeMessages.PathArgument pathArgument) {
515 return PathArgumentSerializer.deSerialize(this, pathArgument);
518 private static interface DeSerializationFunction {
519 NormalizedNode<?, ?> apply(DeSerializer deserializer, NormalizedNodeMessages.Node node);