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 com.google.common.collect.Maps;
28 import java.util.EnumMap;
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;
57 * NormalizedNodeSerializer can be used to convert a Normalized node to and and
58 * from a protocol buffer message.
62 public class NormalizedNodeSerializer {
65 * Serialize a NormalizedNode into a protocol buffer message
67 * The significant things to be aware of the Serialization process are
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
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
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
83 * @param node the node
84 * @return a NormalizedNodeMessages.Node
86 public static NormalizedNodeMessages.Node serialize(final NormalizedNode<?, ?> node) {
87 Preconditions.checkNotNull(node, "node should not be null");
88 return new Serializer(node).serialize();
91 public static Serializer newSerializer(final NormalizedNode<?, ?> node) {
92 Preconditions.checkNotNull(node, "node should not be null");
93 return new Serializer(node);
97 * DeSerialize a protocol buffer message back into a NormalizedNode.
99 * @param node the node
100 * @return a NormalizedNode
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();
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.
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);
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);
129 public static class Serializer extends QNameSerializationContextImpl
130 implements NormalizedNodeSerializationContext {
132 private final NormalizedNode<?, ?> node;
134 private NormalizedNodeMessages.InstanceIdentifier serializedPath;
136 private Serializer(final NormalizedNode<?, ?> node) {
140 public NormalizedNodeMessages.InstanceIdentifier getSerializedPath() {
141 return serializedPath;
144 public NormalizedNodeMessages.Node serialize() {
145 return this.serialize(node).addAllCode(getCodes()).build();
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();
154 private NormalizedNodeMessages.Node.Builder serialize(final NormalizedNode<?, ?> fromNode) {
155 NormalizedNodeMessages.Node.Builder builder =
156 NormalizedNodeMessages.Node.newBuilder();
158 builder.setPathArgument(PathArgumentSerializer.serialize(this, fromNode.getIdentifier()));
159 Integer nodeType = getSerializableNodeType(fromNode).ordinal();
160 builder.setIntType(nodeType);
161 Object value = fromNode.getValue();
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
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()) {
176 ValueSerializer.serialize(builder, this, value);
178 } else if (value instanceof Iterable) {
179 Iterable<?> iterable = (Iterable<?>) value;
181 for (Object o : iterable) {
182 if (o instanceof NormalizedNode) {
183 builder.addChild(serialize((NormalizedNode<?, ?>) o));
187 } else if (value instanceof NormalizedNode) {
189 builder.addChild(serialize((NormalizedNode<?, ?>) value));
193 ValueSerializer.serialize(builder, this, value);
200 @SuppressWarnings("rawtypes")
201 public static class DeSerializer extends QNameDeSerializationContextImpl
202 implements NormalizedNodeDeSerializationContext {
203 private static final Map<NormalizedNodeType, DeSerializationFunction> DESERIALIZATION_FUNCTIONS;
206 final EnumMap<NormalizedNodeType, DeSerializationFunction> m = new EnumMap<>(NormalizedNodeType.class);
208 m.put(CONTAINER_NODE_TYPE, (deSerializer, node) -> {
209 DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> builder = Builders.containerBuilder()
210 .withNodeIdentifier(deSerializer.toNodeIdentifier(node.getPathArgument()));
212 return deSerializer.buildDataContainer(builder, node);
214 m.put(LEAF_NODE_TYPE, (deSerializer, node) -> {
215 NormalizedNodeAttrBuilder<NodeIdentifier, Object, LeafNode<Object>> builder = Builders.leafBuilder()
216 .withNodeIdentifier(deSerializer.toNodeIdentifier(node.getPathArgument()));
218 return deSerializer.buildNormalizedNode(builder, node);
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()));
226 return deSerializer.buildDataContainer(builder, node);
228 m.put(AUGMENTATION_NODE_TYPE, (deSerializer, node) -> {
229 DataContainerNodeBuilder<AugmentationIdentifier, AugmentationNode> builder =
230 Builders.augmentationBuilder().withNodeIdentifier(
231 deSerializer.toAugmentationIdentifier(node.getPathArgument()));
233 return deSerializer.buildDataContainer(builder, node);
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()));
242 return deSerializer.buildNormalizedNode(builder, node);
244 m.put(CHOICE_NODE_TYPE, (deSerializer, node) -> {
245 DataContainerNodeBuilder<NodeIdentifier, ChoiceNode> builder = Builders.choiceBuilder()
246 .withNodeIdentifier(deSerializer.toNodeIdentifier(node.getPathArgument()));
248 return deSerializer.buildDataContainer(builder, node);
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()));
261 return deSerializer.buildDataContainer(builder, node);
263 m.put(ANY_XML_NODE_TYPE, (deSerializer, node) -> {
264 NormalizedNodeAttrBuilder<NodeIdentifier, DOMSource, AnyXmlNode> builder = Builders.anyXmlBuilder()
265 .withNodeIdentifier(deSerializer.toNodeIdentifier(node.getPathArgument()));
267 return deSerializer.buildNormalizedNode(builder, node);
270 DESERIALIZATION_FUNCTIONS = Maps.immutableEnumMap(m);
273 private final NormalizedNodeMessages.Node node;
274 private final NormalizedNodeMessages.InstanceIdentifier path;
275 private YangInstanceIdentifier deserializedPath;
277 public DeSerializer(final NormalizedNodeMessages.InstanceIdentifier path,
278 final NormalizedNodeMessages.Node node) {
279 super(node.getCodeList());
284 public YangInstanceIdentifier getDeserializedPath() {
285 return deserializedPath;
288 public NormalizedNode<?, ?> deSerialize() {
289 NormalizedNode<?, ?> deserializedNode = deSerialize(node);
291 deserializedPath = InstanceIdentifierUtils.fromSerializable(path, this);
294 return deserializedNode;
297 private NormalizedNode<?, ?> deSerialize(final NormalizedNodeMessages.Node fromNode) {
298 Preconditions.checkNotNull(fromNode, "node should not be null");
300 DeSerializationFunction deSerializationFunction = DESERIALIZATION_FUNCTIONS.get(
301 NormalizedNodeType.values()[fromNode.getIntType()]);
303 return deSerializationFunction.apply(this, fromNode);
306 public YangInstanceIdentifier.PathArgument deSerialize(final NormalizedNodeMessages.PathArgument pathArgument) {
307 return PathArgumentSerializer.deSerialize(this, pathArgument);
310 @SuppressWarnings("unchecked")
311 private NormalizedNode<?, ?> buildCollectionNode(final CollectionNodeBuilder builder,
312 final NormalizedNodeMessages.Node fromNode) {
314 builder.withNodeIdentifier(toNodeIdentifier(fromNode.getPathArgument()));
316 for (NormalizedNodeMessages.Node child : fromNode.getChildList()) {
317 builder.withChild(deSerialize(child));
320 return builder.build();
324 @SuppressWarnings("unchecked")
325 private NormalizedNode<?, ?> buildListNode(final ListNodeBuilder<Object, LeafSetEntryNode<Object>> builder,
326 final NormalizedNodeMessages.Node fromNode) {
327 builder.withNodeIdentifier(toNodeIdentifier(fromNode.getPathArgument()));
329 for (NormalizedNodeMessages.Node child : fromNode.getChildList()) {
330 builder.withChild((LeafSetEntryNode<Object>) deSerialize(child));
333 return builder.build();
336 private NormalizedNode<?, ?> buildDataContainer(final DataContainerNodeBuilder<?, ?> builder,
337 final NormalizedNodeMessages.Node fromNode) {
339 for (NormalizedNodeMessages.Node child : fromNode.getChildList()) {
340 builder.withChild((DataContainerChild<?, ?>) deSerialize(child));
343 //TODO : Also handle attributes
345 return builder.build();
348 @SuppressWarnings("unchecked")
349 private NormalizedNode<?, ?> buildNormalizedNode(final NormalizedNodeAttrBuilder builder,
350 final NormalizedNodeMessages.Node fromNode) {
352 builder.withValue(ValueSerializer.deSerialize(this, fromNode));
354 //TODO : Also handle attributes
356 return builder.build();
360 private NodeIdentifierWithPredicates toNodeIdentifierWithPredicates(
361 final NormalizedNodeMessages.PathArgument fromPath) {
362 return (NodeIdentifierWithPredicates) PathArgumentSerializer.deSerialize(this, fromPath);
365 private AugmentationIdentifier toAugmentationIdentifier(final NormalizedNodeMessages.PathArgument fromPath) {
366 return (AugmentationIdentifier) PathArgumentSerializer.deSerialize(this, fromPath);
369 @SuppressWarnings("unchecked")
370 private <T> NodeWithValue<T> toNodeWithValue(final NormalizedNodeMessages.PathArgument fromPath) {
371 return (NodeWithValue<T>) PathArgumentSerializer.deSerialize(this, fromPath);
374 private NodeIdentifier toNodeIdentifier(final NormalizedNodeMessages.PathArgument fromPath) {
375 return (NodeIdentifier) PathArgumentSerializer.deSerialize(this, fromPath);
378 private interface DeSerializationFunction {
379 NormalizedNode<?, ?> apply(DeSerializer deserializer, NormalizedNodeMessages.Node node);