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;
25 import com.google.common.base.Preconditions;
26 import com.google.common.collect.Maps;
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.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;
56 * NormalizedNodeSerializer can be used to convert a Normalized node to and and
57 * from a protocol buffer message.
61 public class NormalizedNodeSerializer {
64 * Serialize a NormalizedNode into a protocol buffer message
66 * The significant things to be aware of the Serialization process are
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
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
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
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
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();
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);
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.
123 * @param pathArgument
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);
133 public static class Serializer extends QNameSerializationContextImpl
134 implements NormalizedNodeSerializationContext {
136 private final NormalizedNode<?, ?> node;
138 private NormalizedNodeMessages.InstanceIdentifier serializedPath;
140 private Serializer(final NormalizedNode<?, ?> node) {
144 public NormalizedNodeMessages.InstanceIdentifier getSerializedPath() {
145 return serializedPath;
148 public NormalizedNodeMessages.Node serialize() {
149 return this.serialize(node).addAllCode(getCodes()).build();
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();
158 private NormalizedNodeMessages.Node.Builder serialize(
159 final NormalizedNode<?, ?> node) {
160 NormalizedNodeMessages.Node.Builder builder =
161 NormalizedNodeMessages.Node.newBuilder();
163 builder.setPathArgument(PathArgumentSerializer.serialize(this, node.getIdentifier()));
164 Integer nodeType = getSerializableNodeType(node).ordinal();
165 builder.setIntType(nodeType);
166 Object value = node.getValue();
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
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()){
181 ValueSerializer.serialize(builder, this, value);
183 } else if (value instanceof Iterable) {
184 Iterable<?> iterable = (Iterable<?>) value;
186 for (Object o : iterable) {
187 if (o instanceof NormalizedNode) {
188 builder.addChild(serialize((NormalizedNode<?, ?>) o));
192 } else if (value instanceof NormalizedNode) {
194 builder.addChild(serialize((NormalizedNode<?, ?>) value));
198 ValueSerializer.serialize(builder, this, value);
205 public static class DeSerializer extends QNameDeSerializationContextImpl
206 implements NormalizedNodeDeSerializationContext {
207 private static final Map<NormalizedNodeType, DeSerializationFunction> DESERIALIZATION_FUNCTIONS;
209 final EnumMap<NormalizedNodeType, DeSerializationFunction> m = new EnumMap<>(NormalizedNodeType.class);
211 m.put(CONTAINER_NODE_TYPE, new DeSerializationFunction() {
213 public NormalizedNode<?, ?> apply(final DeSerializer deSerializer, final NormalizedNodeMessages.Node node) {
214 DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> builder = Builders.containerBuilder()
215 .withNodeIdentifier(deSerializer.toNodeIdentifier(node.getPathArgument()));
217 return deSerializer.buildDataContainer(builder, node);
220 m.put(LEAF_NODE_TYPE, new DeSerializationFunction() {
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()));
226 return deSerializer.buildNormalizedNode(builder, node);
229 m.put(MAP_NODE_TYPE, new DeSerializationFunction() {
231 public NormalizedNode<?, ?> apply(final DeSerializer deSerializer, final NormalizedNodeMessages.Node node) {
232 return deSerializer.buildCollectionNode(Builders.mapBuilder(), node);
235 m.put(MAP_ENTRY_NODE_TYPE, new DeSerializationFunction() {
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()));
242 return deSerializer.buildDataContainer(builder, node);
245 m.put(AUGMENTATION_NODE_TYPE, new DeSerializationFunction() {
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()));
252 return deSerializer.buildDataContainer(builder, node);
255 m.put(LEAF_SET_NODE_TYPE, new DeSerializationFunction() {
257 public NormalizedNode<?, ?> apply(final DeSerializer deSerializer, final NormalizedNodeMessages.Node node) {
258 return deSerializer.buildListNode(Builders.leafSetBuilder(), node);
261 m.put(LEAF_SET_ENTRY_NODE_TYPE, new DeSerializationFunction() {
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()));
268 return deSerializer.buildNormalizedNode(builder, node);
271 m.put(CHOICE_NODE_TYPE, new DeSerializationFunction() {
273 public NormalizedNode<?, ?> apply(final DeSerializer deSerializer, final NormalizedNodeMessages.Node node) {
274 DataContainerNodeBuilder<NodeIdentifier, ChoiceNode> builder = Builders.choiceBuilder()
275 .withNodeIdentifier(deSerializer.toNodeIdentifier(node.getPathArgument()));
277 return deSerializer.buildDataContainer(builder, node);
280 m.put(ORDERED_LEAF_SET_NODE_TYPE, new DeSerializationFunction() {
282 public NormalizedNode<?, ?> apply(final DeSerializer deSerializer, final NormalizedNodeMessages.Node node) {
283 return deSerializer.buildListNode(Builders.orderedLeafSetBuilder(), node);
286 m.put(ORDERED_MAP_NODE_TYPE, new DeSerializationFunction() {
288 public NormalizedNode<?, ?> apply(final DeSerializer deSerializer, final NormalizedNodeMessages.Node node) {
289 return deSerializer.buildCollectionNode(Builders.orderedMapBuilder(), node);
292 m.put(UNKEYED_LIST_NODE_TYPE, new DeSerializationFunction() {
294 public NormalizedNode<?, ?> apply(final DeSerializer deSerializer, final NormalizedNodeMessages.Node node) {
295 return deSerializer.buildCollectionNode(Builders.unkeyedListBuilder(), node);
298 m.put(UNKEYED_LIST_ENTRY_NODE_TYPE, new DeSerializationFunction() {
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()));
305 return deSerializer.buildDataContainer(builder, node);
308 m.put(ANY_XML_NODE_TYPE, new DeSerializationFunction() {
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()));
314 return deSerializer.buildNormalizedNode(builder, node);
318 DESERIALIZATION_FUNCTIONS = Maps.immutableEnumMap(m);
321 private final NormalizedNodeMessages.Node node;
322 private final NormalizedNodeMessages.InstanceIdentifier path;
323 private YangInstanceIdentifier deserializedPath;
325 public DeSerializer(final NormalizedNodeMessages.InstanceIdentifier path,
326 final NormalizedNodeMessages.Node node) {
327 super(node.getCodeList());
332 public YangInstanceIdentifier getDeserializedPath() {
333 return deserializedPath;
336 public NormalizedNode<?, ?> deSerialize() {
337 NormalizedNode<?, ?> deserializedNode = deSerialize(node);
339 deserializedPath = InstanceIdentifierUtils.fromSerializable(path, this);
342 return deserializedNode;
345 private NormalizedNode<?, ?> deSerialize(final NormalizedNodeMessages.Node node){
346 Preconditions.checkNotNull(node, "node should not be null");
348 DeSerializationFunction deSerializationFunction = DESERIALIZATION_FUNCTIONS.get(
349 NormalizedNodeType.values()[node.getIntType()]);
351 return deSerializationFunction.apply(this, node);
355 private NormalizedNode<?, ?> buildCollectionNode(
356 final CollectionNodeBuilder builder,
357 final NormalizedNodeMessages.Node node) {
359 builder.withNodeIdentifier(toNodeIdentifier(node.getPathArgument()));
361 for(NormalizedNodeMessages.Node child : node.getChildList()){
362 builder.withChild(deSerialize(child));
365 return builder.build();
369 private NormalizedNode<?, ?> buildListNode(
370 final ListNodeBuilder<Object, LeafSetEntryNode<Object>> builder,
371 final NormalizedNodeMessages.Node node) {
372 builder.withNodeIdentifier(toNodeIdentifier(node.getPathArgument()));
374 for(NormalizedNodeMessages.Node child : node.getChildList()){
375 builder.withChild((LeafSetEntryNode<Object>) deSerialize(child));
378 return builder.build();
381 private NormalizedNode<?, ?> buildDataContainer(final DataContainerNodeBuilder<?, ?> builder, final NormalizedNodeMessages.Node node){
383 for(NormalizedNodeMessages.Node child : node.getChildList()){
384 builder.withChild((DataContainerChild<?, ?>) deSerialize(child));
387 //TODO : Also handle attributes
389 return builder.build();
392 private NormalizedNode<?, ?> buildNormalizedNode(final NormalizedNodeAttrBuilder builder, final NormalizedNodeMessages.Node node){
394 builder.withValue(ValueSerializer.deSerialize(this, node));
396 //TODO : Also handle attributes
398 return builder.build();
402 private NodeIdentifierWithPredicates toNodeIdentifierWithPredicates(
403 final NormalizedNodeMessages.PathArgument path) {
404 return (NodeIdentifierWithPredicates) PathArgumentSerializer.deSerialize(this, path);
407 private AugmentationIdentifier toAugmentationIdentifier(final NormalizedNodeMessages.PathArgument path) {
408 return (AugmentationIdentifier) PathArgumentSerializer.deSerialize(this, path);
411 @SuppressWarnings("unchecked")
412 private <T> NodeWithValue<T> toNodeWithValue(final NormalizedNodeMessages.PathArgument path) {
413 return (NodeWithValue<T>) PathArgumentSerializer.deSerialize(this, path);
416 private NodeIdentifier toNodeIdentifier(final NormalizedNodeMessages.PathArgument path){
417 return (NodeIdentifier) PathArgumentSerializer.deSerialize(this, path);
420 public YangInstanceIdentifier.PathArgument deSerialize(final NormalizedNodeMessages.PathArgument pathArgument) {
421 return PathArgumentSerializer.deSerialize(this, pathArgument);
424 private interface DeSerializationFunction {
425 NormalizedNode<?, ?> apply(DeSerializer deserializer, NormalizedNodeMessages.Node node);