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 com.google.common.base.Preconditions;
12 import org.opendaylight.controller.cluster.datastore.util.InstanceIdentifierUtils;
13 import org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages;
14 import org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.Node.Builder;
15 import org.opendaylight.yangtools.yang.data.api.Node;
16 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
17 import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode;
18 import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
19 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
20 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
21 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
22 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
23 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
24 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
25 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
26 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
27 import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode;
28 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
29 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
30 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
31 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
32 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
33 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
34 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.ListNodeBuilder;
35 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeAttrBuilder;
36 import java.util.EnumMap;
38 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.ANY_XML_NODE_TYPE;
39 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.AUGMENTATION_NODE_TYPE;
40 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.CHOICE_NODE_TYPE;
41 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.CONTAINER_NODE_TYPE;
42 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.LEAF_NODE_TYPE;
43 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.LEAF_SET_ENTRY_NODE_TYPE;
44 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.LEAF_SET_NODE_TYPE;
45 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.MAP_ENTRY_NODE_TYPE;
46 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.MAP_NODE_TYPE;
47 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.ORDERED_LEAF_SET_NODE_TYPE;
48 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.ORDERED_MAP_NODE_TYPE;
49 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.UNKEYED_LIST_ENTRY_NODE_TYPE;
50 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.UNKEYED_LIST_NODE_TYPE;
51 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.getSerializableNodeType;
54 * NormalizedNodeSerializer can be used to convert a Normalized node to and and
55 * from a protocol buffer message.
59 public class NormalizedNodeSerializer {
62 * Serialize a NormalizedNode into a protocol buffer message
64 * The significant things to be aware of the Serialization process are
66 * <li>Repeated strings like namespaces, revisions and localNames are
67 * compressed to codes and stored in the top level of the normalized
68 * node protocol buffer message
70 * <li>All value types are encoded for each leaf value. This is so that
71 * the deSerialization process does not need to use the schema context to
72 * figure out how to decode values
76 * One question which may arise is why not use something like gzip to
77 * compress the protocol buffer message instead of rolling our own
78 * encoding scheme. This has to be explored further as it is a more
84 public static NormalizedNodeMessages.Node serialize(NormalizedNode<?, ?> node){
85 Preconditions.checkNotNull(node, "node should not be null");
86 return new Serializer(node).serialize();
89 public static Serializer newSerializer(NormalizedNode<?, ?> node) {
90 Preconditions.checkNotNull(node, "node should not be null");
91 return new Serializer(node);
95 * DeSerialize a protocol buffer message back into a NormalizedNode
100 public static NormalizedNode<?, ?> deSerialize(NormalizedNodeMessages.Node node) {
101 Preconditions.checkNotNull(node, "node should not be null");
102 return new DeSerializer(null, node).deSerialize();
105 public static DeSerializer newDeSerializer(NormalizedNodeMessages.InstanceIdentifier path,
106 NormalizedNodeMessages.Node node) {
107 Preconditions.checkNotNull(node, "node should not be null");
108 return new DeSerializer(path, node);
112 * DeSerialize a PathArgument which is in the protocol buffer format into
113 * a yang PathArgument. The protocol buffer path argument is specially
114 * encoded and can only be interpreted in the context of a top level
115 * serialized NormalizedNode protocol buffer message. The reason for this
116 * is that during the NormalizedNode serialization process certain repeated
117 * strings are encoded into a "codes" list and the actual strings are
118 * replaced by an integer which is an index into that list.
121 * @param pathArgument
124 public static YangInstanceIdentifier.PathArgument deSerialize(NormalizedNodeMessages.Node node,
125 NormalizedNodeMessages.PathArgument pathArgument){
126 Preconditions.checkNotNull(node, "node should not be null");
127 Preconditions.checkNotNull(pathArgument, "pathArgument should not be null");
128 return new DeSerializer(null, node).deSerialize(pathArgument);
131 public static class Serializer extends QNameSerializationContextImpl
132 implements NormalizedNodeSerializationContext {
134 private final NormalizedNode<?, ?> node;
136 private NormalizedNodeMessages.InstanceIdentifier serializedPath;
138 private Serializer(NormalizedNode<?, ?> node) {
142 public NormalizedNodeMessages.InstanceIdentifier getSerializedPath() {
143 return serializedPath;
146 public NormalizedNodeMessages.Node serialize() {
147 return this.serialize(node).addAllCode(getCodes()).build();
150 public NormalizedNodeMessages.Node serialize(YangInstanceIdentifier path) {
151 Builder builder = serialize(node);
152 serializedPath = InstanceIdentifierUtils.toSerializable(path, this);
153 return builder.addAllCode(getCodes()).build();
156 private NormalizedNodeMessages.Node.Builder serialize(
157 NormalizedNode<?, ?> node) {
158 NormalizedNodeMessages.Node.Builder builder =
159 NormalizedNodeMessages.Node.newBuilder();
161 builder.setPathArgument(PathArgumentSerializer.serialize(this, node.getIdentifier()));
162 Integer nodeType = getSerializableNodeType(node).ordinal();
163 builder.setIntType(nodeType);
164 Object value = node.getValue();
166 // We need to do a specific check of the type of the node here
167 // because if we looked at the value type alone we will not be
168 // able to distinguish if the value was supposed to be added
169 // as a child or whether the value should be added as a value of the
171 // One use case where this check is necessary when you have a node
172 // with a bits value. In that case the value of the node is a Set
173 // which is also a Collection. Without the following check being
174 // done first the code would flow into the Collection if condition
175 // and the Set would be added as child nodes
176 if(nodeType == NormalizedNodeType.LEAF_NODE_TYPE.ordinal() ||
177 nodeType == NormalizedNodeType.LEAF_SET_ENTRY_NODE_TYPE.ordinal()){
179 ValueSerializer.serialize(builder, this, value);
181 } else if (value instanceof Iterable) {
182 Iterable<?> iterable = (Iterable<?>) value;
184 for (Object o : iterable) {
185 if (o instanceof NormalizedNode) {
186 builder.addChild(serialize((NormalizedNode<?, ?>) o));
190 } else if (value instanceof NormalizedNode) {
192 builder.addChild(serialize((NormalizedNode<?, ?>) value));
196 ValueSerializer.serialize(builder, this, value);
203 public static class DeSerializer extends QNameDeSerializationContextImpl
204 implements NormalizedNodeDeSerializationContext {
205 private static Map<NormalizedNodeType, DeSerializationFunction>
206 deSerializationFunctions = new EnumMap<>(NormalizedNodeType.class);
209 deSerializationFunctions.put(CONTAINER_NODE_TYPE,
210 new DeSerializationFunction() {
211 @Override public NormalizedNode<?, ?> apply(
212 DeSerializer deSerializer,
213 NormalizedNodeMessages.Node node) {
214 DataContainerNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifier, ContainerNode>
215 builder = Builders.containerBuilder();
218 .withNodeIdentifier(deSerializer.toNodeIdentifier(
219 node.getPathArgument()));
221 return deSerializer.buildDataContainer(builder, node);
227 deSerializationFunctions.put(LEAF_NODE_TYPE,
228 new DeSerializationFunction() {
229 @Override public NormalizedNode<?, ?> apply(
230 DeSerializer deSerializer,
231 NormalizedNodeMessages.Node node) {
232 NormalizedNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifier, Object, LeafNode<Object>>
233 builder = Builders.leafBuilder();
236 .withNodeIdentifier(deSerializer.toNodeIdentifier(
237 node.getPathArgument()));
239 return deSerializer.buildNormalizedNode(builder, node);
244 deSerializationFunctions.put(MAP_NODE_TYPE,
245 new DeSerializationFunction() {
246 @Override public NormalizedNode<?, ?> apply(
247 DeSerializer deSerializer,
248 NormalizedNodeMessages.Node node) {
249 CollectionNodeBuilder<MapEntryNode, MapNode>
250 builder = Builders.mapBuilder();
252 return deSerializer.buildCollectionNode(builder, node);
256 deSerializationFunctions.put(MAP_ENTRY_NODE_TYPE,
257 new DeSerializationFunction() {
258 @Override public NormalizedNode<?, ?> apply(
259 DeSerializer deSerializer,
260 NormalizedNodeMessages.Node node) {
261 DataContainerNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifierWithPredicates, MapEntryNode>
262 builder = Builders.mapEntryBuilder();
264 builder.withNodeIdentifier(deSerializer.toNodeIdentifierWithPredicates(
265 node.getPathArgument()));
267 return deSerializer.buildDataContainer(builder, node);
271 deSerializationFunctions.put(AUGMENTATION_NODE_TYPE,
272 new DeSerializationFunction() {
273 @Override public NormalizedNode<?, ?> apply(
274 DeSerializer deSerializer,
275 NormalizedNodeMessages.Node node) {
276 DataContainerNodeBuilder<YangInstanceIdentifier.AugmentationIdentifier, AugmentationNode>
277 builder = Builders.augmentationBuilder();
279 builder.withNodeIdentifier(
280 deSerializer.toAugmentationIdentifier(
281 node.getPathArgument()));
283 return deSerializer.buildDataContainer(builder, node);
287 deSerializationFunctions.put(LEAF_SET_NODE_TYPE,
288 new DeSerializationFunction() {
289 @Override public NormalizedNode<?, ?> apply(
290 DeSerializer deSerializer,
291 NormalizedNodeMessages.Node node) {
292 ListNodeBuilder<Object, LeafSetEntryNode<Object>>
293 builder = Builders.leafSetBuilder();
295 return deSerializer.buildListNode(builder, node);
299 deSerializationFunctions.put(LEAF_SET_ENTRY_NODE_TYPE,
300 new DeSerializationFunction() {
301 @Override public NormalizedNode<?, ?> apply(
302 DeSerializer deSerializer,
303 NormalizedNodeMessages.Node node) {
304 NormalizedNodeAttrBuilder<YangInstanceIdentifier.NodeWithValue, Object, LeafSetEntryNode<Object>>
305 builder = Builders.leafSetEntryBuilder();
307 builder.withNodeIdentifier(deSerializer.toNodeWithValue(
308 node.getPathArgument()));
310 return deSerializer.buildNormalizedNode(builder, node);
314 deSerializationFunctions.put(CHOICE_NODE_TYPE,
315 new DeSerializationFunction() {
316 @Override public NormalizedNode<?, ?> apply(
317 DeSerializer deSerializer,
318 NormalizedNodeMessages.Node node) {
319 DataContainerNodeBuilder<YangInstanceIdentifier.NodeIdentifier, ChoiceNode>
321 Builders.choiceBuilder();
324 .withNodeIdentifier(deSerializer.toNodeIdentifier(
325 node.getPathArgument()));
327 return deSerializer.buildDataContainer(builder, node);
331 deSerializationFunctions.put(ORDERED_LEAF_SET_NODE_TYPE,
332 new DeSerializationFunction() {
333 @Override public NormalizedNode<?, ?> apply(
334 DeSerializer deSerializer,
335 NormalizedNodeMessages.Node node) {
336 ListNodeBuilder<Object, LeafSetEntryNode<Object>>
338 Builders.orderedLeafSetBuilder();
340 return deSerializer.buildListNode(builder, node);
346 deSerializationFunctions.put(ORDERED_MAP_NODE_TYPE,
347 new DeSerializationFunction() {
348 @Override public NormalizedNode<?, ?> apply(
349 DeSerializer deSerializer,
350 NormalizedNodeMessages.Node node) {
351 CollectionNodeBuilder<MapEntryNode, OrderedMapNode>
353 Builders.orderedMapBuilder();
355 return deSerializer.buildCollectionNode(builder, node);
359 deSerializationFunctions.put(UNKEYED_LIST_NODE_TYPE,
360 new DeSerializationFunction() {
361 @Override public NormalizedNode<?, ?> apply(
362 DeSerializer deSerializer,
363 NormalizedNodeMessages.Node node) {
364 CollectionNodeBuilder<UnkeyedListEntryNode, UnkeyedListNode>
366 Builders.unkeyedListBuilder();
368 return deSerializer.buildCollectionNode(builder, node);
372 deSerializationFunctions.put(UNKEYED_LIST_ENTRY_NODE_TYPE,
373 new DeSerializationFunction() {
374 @Override public NormalizedNode<?, ?> apply(
375 DeSerializer deSerializer,
376 NormalizedNodeMessages.Node node) {
377 DataContainerNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifier, UnkeyedListEntryNode>
379 Builders.unkeyedListEntryBuilder();
382 .withNodeIdentifier(deSerializer.toNodeIdentifier(
383 node.getPathArgument()));
385 return deSerializer.buildDataContainer(builder, node);
389 deSerializationFunctions.put(ANY_XML_NODE_TYPE,
390 new DeSerializationFunction() {
392 @Override public NormalizedNode<?, ?> apply(
393 DeSerializer deSerializer,
394 NormalizedNodeMessages.Node node) {
395 NormalizedNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifier, Node<?>, AnyXmlNode>
397 Builders.anyXmlBuilder();
400 .withNodeIdentifier(deSerializer.toNodeIdentifier(
401 node.getPathArgument()));
403 return deSerializer.buildNormalizedNode(builder, node);
409 private final NormalizedNodeMessages.Node node;
410 private final NormalizedNodeMessages.InstanceIdentifier path;
411 private YangInstanceIdentifier deserializedPath;
413 public DeSerializer(NormalizedNodeMessages.InstanceIdentifier path,
414 NormalizedNodeMessages.Node node) {
415 super(node.getCodeList());
420 public YangInstanceIdentifier getDeserializedPath() {
421 return deserializedPath;
424 public NormalizedNode<?, ?> deSerialize() {
425 NormalizedNode<?, ?> deserializedNode = deSerialize(node);
427 deserializedPath = InstanceIdentifierUtils.fromSerializable(path, this);
430 return deserializedNode;
433 private NormalizedNode<?, ?> deSerialize(NormalizedNodeMessages.Node node){
434 Preconditions.checkNotNull(node, "node should not be null");
436 DeSerializationFunction deSerializationFunction = deSerializationFunctions.get(
437 NormalizedNodeType.values()[node.getIntType()]);
439 return deSerializationFunction.apply(this, node);
443 private NormalizedNode<?, ?> buildCollectionNode(
444 CollectionNodeBuilder builder,
445 NormalizedNodeMessages.Node node) {
447 builder.withNodeIdentifier(toNodeIdentifier(node.getPathArgument()));
449 for(NormalizedNodeMessages.Node child : node.getChildList()){
450 builder.withChild(deSerialize(child));
453 return builder.build();
457 private NormalizedNode<?, ?> buildListNode(
458 ListNodeBuilder<Object, LeafSetEntryNode<Object>> builder,
459 NormalizedNodeMessages.Node node) {
460 builder.withNodeIdentifier(toNodeIdentifier(node.getPathArgument()));
462 for(NormalizedNodeMessages.Node child : node.getChildList()){
463 builder.withChild((LeafSetEntryNode<Object>) deSerialize(child));
466 return builder.build();
469 private NormalizedNode<?, ?> buildDataContainer(DataContainerNodeBuilder builder, NormalizedNodeMessages.Node node){
471 for(NormalizedNodeMessages.Node child : node.getChildList()){
472 builder.withChild((DataContainerChild<?, ?>) deSerialize(child));
475 //TODO : Also handle attributes
477 return builder.build();
480 private NormalizedNode<?, ?> buildNormalizedNode(NormalizedNodeAttrBuilder builder, NormalizedNodeMessages.Node node){
482 builder.withValue(ValueSerializer.deSerialize(this, node));
484 //TODO : Also handle attributes
486 return builder.build();
491 private YangInstanceIdentifier.NodeIdentifierWithPredicates toNodeIdentifierWithPredicates(
492 NormalizedNodeMessages.PathArgument path) {
493 return (YangInstanceIdentifier.NodeIdentifierWithPredicates) PathArgumentSerializer.deSerialize(this, path);
496 private YangInstanceIdentifier.AugmentationIdentifier toAugmentationIdentifier(
497 NormalizedNodeMessages.PathArgument path) {
498 return (YangInstanceIdentifier.AugmentationIdentifier) PathArgumentSerializer.deSerialize(this, path);
501 private YangInstanceIdentifier.NodeWithValue toNodeWithValue(
502 NormalizedNodeMessages.PathArgument path) {
503 return (YangInstanceIdentifier.NodeWithValue) PathArgumentSerializer.deSerialize(
507 private YangInstanceIdentifier.NodeIdentifier toNodeIdentifier(NormalizedNodeMessages.PathArgument path){
508 return (YangInstanceIdentifier.NodeIdentifier) PathArgumentSerializer.deSerialize(
512 public YangInstanceIdentifier.PathArgument deSerialize(
513 NormalizedNodeMessages.PathArgument pathArgument) {
514 return PathArgumentSerializer.deSerialize(this, pathArgument);
517 private static interface DeSerializationFunction {
518 NormalizedNode<?, ?> apply(DeSerializer deserializer, NormalizedNodeMessages.Node node);