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;
13 import org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages;
14 import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil;
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;
38 import java.util.ArrayList;
39 import java.util.Date;
40 import java.util.EnumMap;
41 import java.util.HashMap;
42 import java.util.List;
45 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.ANY_XML_NODE_TYPE;
46 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.AUGMENTATION_NODE_TYPE;
47 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.CHOICE_NODE_TYPE;
48 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.CONTAINER_NODE_TYPE;
49 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.LEAF_NODE_TYPE;
50 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.LEAF_SET_ENTRY_NODE_TYPE;
51 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.LEAF_SET_NODE_TYPE;
52 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.MAP_ENTRY_NODE_TYPE;
53 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.MAP_NODE_TYPE;
54 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.ORDERED_LEAF_SET_NODE_TYPE;
55 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.ORDERED_MAP_NODE_TYPE;
56 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.UNKEYED_LIST_ENTRY_NODE_TYPE;
57 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.UNKEYED_LIST_NODE_TYPE;
58 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.getSerializableNodeType;
61 * NormalizedNodeSerializer can be used to convert a Normalized node to and and
62 * from a protocol buffer message.
66 public class NormalizedNodeSerializer {
69 * Serialize a NormalizedNode into a protocol buffer message
71 * The significant things to be aware of the Serialization process are
73 * <li>Repeated strings like namespaces, revisions and localNames are
74 * compressed to codes and stored in the top level of the normalized
75 * node protocol buffer message
77 * <li>All value types are encoded for each leaf value. This is so that
78 * the deSerialization process does not need to use the schema context to
79 * figure out how to decode values
83 * One question which may arise is why not use something like gzip to
84 * compress the protocol buffer message instead of rolling our own
85 * encoding scheme. This has to be explored further as it is a more
91 public static NormalizedNodeMessages.Node serialize(NormalizedNode node){
92 Preconditions.checkNotNull(node, "node should not be null");
93 return new Serializer(node).serialize();
98 * DeSerialize a protocol buffer message back into a NormalizedNode
103 public static NormalizedNode deSerialize(NormalizedNodeMessages.Node node){
104 return new DeSerializer(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.
117 * @param pathArgument
120 public static YangInstanceIdentifier.PathArgument deSerialize(NormalizedNodeMessages.Node node, NormalizedNodeMessages.PathArgument pathArgument){
121 Preconditions.checkNotNull(node, "node should not be null");
122 Preconditions.checkNotNull(pathArgument, "pathArgument should not be null");
123 return new DeSerializer(node).deSerialize(pathArgument);
126 private static class Serializer implements NormalizedNodeSerializationContext {
128 private final NormalizedNode node;
130 private final Map<Object, Integer> codeMap = new HashMap<>();
131 private final List<String> codes = new ArrayList<>();
133 private Serializer(NormalizedNode node) {
137 private NormalizedNodeMessages.Node serialize() {
138 return this.serialize(node).addAllCode(codes).build();
141 private NormalizedNodeMessages.Node.Builder serialize(
142 NormalizedNode node) {
143 NormalizedNodeMessages.Node.Builder builder =
144 NormalizedNodeMessages.Node.newBuilder();
146 builder.setPathArgument(PathArgumentSerializer.serialize(this, node.getIdentifier()));
147 Integer nodeType = getSerializableNodeType(node).ordinal();
148 builder.setIntType(nodeType);
149 Object value = node.getValue();
151 // We need to do a specific check of the type of the node here
152 // because if we looked at the value type alone we will not be
153 // able to distinguish if the value was supposed to be added
154 // as a child or whether the value should be added as a value of the
156 // One use case where this check is necessary when you have a node
157 // with a bits value. In that case the value of the node is a Set
158 // which is also a Collection. Without the following check being
159 // done first the code would flow into the Collection if condition
160 // and the Set would be added as child nodes
161 if(nodeType == NormalizedNodeType.LEAF_NODE_TYPE.ordinal() ||
162 nodeType == NormalizedNodeType.LEAF_SET_ENTRY_NODE_TYPE.ordinal()){
164 ValueSerializer.serialize(builder, this, value);
166 } else if (value instanceof Iterable) {
167 Iterable iterable = (Iterable) value;
169 for (Object o : iterable) {
170 if (o instanceof NormalizedNode) {
171 builder.addChild(serialize((NormalizedNode) o));
175 } else if (value instanceof NormalizedNode) {
177 builder.addChild(serialize((NormalizedNode) value));
181 ValueSerializer.serialize(builder, this, value);
188 @Override public int addNamespace(URI namespace) {
189 int namespaceInt = getCode(namespace);
191 if(namespaceInt == -1) {
192 namespaceInt = addCode(namespace, namespace.toString());
197 @Override public int addRevision(Date revision) {
198 if(revision == null){
202 int revisionInt = getCode(revision);
203 if(revisionInt == -1) {
204 String formattedRevision =
205 SimpleDateFormatUtil.getRevisionFormat().format(revision);
206 revisionInt = addCode(revision, formattedRevision);
211 @Override public int addLocalName(String localName) {
212 int localNameInt = getCode(localName);
213 if(localNameInt == -1) {
214 localNameInt = addCode(localName, localName.toString());
220 public int addCode(Object code, String codeStr){
221 int count = codes.size();
223 codeMap.put(code, Integer.valueOf(count));
227 public int getCode(Object code){
228 if(codeMap.containsKey(code)){
229 return codeMap.get(code);
235 private static class DeSerializer implements NormalizedNodeDeSerializationContext {
236 private static Map<NormalizedNodeType, DeSerializationFunction>
237 deSerializationFunctions = new EnumMap<>(NormalizedNodeType.class);
240 deSerializationFunctions.put(CONTAINER_NODE_TYPE,
241 new DeSerializationFunction() {
242 @Override public NormalizedNode apply(
243 DeSerializer deSerializer,
244 NormalizedNodeMessages.Node node) {
245 DataContainerNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifier, ContainerNode>
246 builder = Builders.containerBuilder();
249 .withNodeIdentifier(deSerializer.toNodeIdentifier(
250 node.getPathArgument()));
252 return deSerializer.buildDataContainer(builder, node);
258 deSerializationFunctions.put(LEAF_NODE_TYPE,
259 new DeSerializationFunction() {
260 @Override public NormalizedNode apply(
261 DeSerializer deSerializer,
262 NormalizedNodeMessages.Node node) {
263 NormalizedNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifier, Object, LeafNode<Object>>
264 builder = Builders.leafBuilder();
267 .withNodeIdentifier(deSerializer.toNodeIdentifier(
268 node.getPathArgument()));
270 return deSerializer.buildNormalizedNode(builder, node);
275 deSerializationFunctions.put(MAP_NODE_TYPE,
276 new DeSerializationFunction() {
277 @Override public NormalizedNode apply(
278 DeSerializer deSerializer,
279 NormalizedNodeMessages.Node node) {
280 CollectionNodeBuilder<MapEntryNode, MapNode>
281 builder = Builders.mapBuilder();
283 return deSerializer.buildCollectionNode(builder, node);
287 deSerializationFunctions.put(MAP_ENTRY_NODE_TYPE,
288 new DeSerializationFunction() {
289 @Override public NormalizedNode apply(
290 DeSerializer deSerializer,
291 NormalizedNodeMessages.Node node) {
292 DataContainerNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifierWithPredicates, MapEntryNode>
293 builder = Builders.mapEntryBuilder();
295 builder.withNodeIdentifier(deSerializer.toNodeIdentifierWithPredicates(
296 node.getPathArgument()));
298 return deSerializer.buildDataContainer(builder, node);
302 deSerializationFunctions.put(AUGMENTATION_NODE_TYPE,
303 new DeSerializationFunction() {
304 @Override public NormalizedNode apply(
305 DeSerializer deSerializer,
306 NormalizedNodeMessages.Node node) {
307 DataContainerNodeBuilder<YangInstanceIdentifier.AugmentationIdentifier, AugmentationNode>
308 builder = Builders.augmentationBuilder();
310 builder.withNodeIdentifier(
311 deSerializer.toAugmentationIdentifier(
312 node.getPathArgument()));
314 return deSerializer.buildDataContainer(builder, node);
318 deSerializationFunctions.put(LEAF_SET_NODE_TYPE,
319 new DeSerializationFunction() {
320 @Override public NormalizedNode apply(
321 DeSerializer deSerializer,
322 NormalizedNodeMessages.Node node) {
323 ListNodeBuilder<Object, LeafSetEntryNode<Object>>
324 builder = Builders.leafSetBuilder();
326 return deSerializer.buildListNode(builder, node);
330 deSerializationFunctions.put(LEAF_SET_ENTRY_NODE_TYPE,
331 new DeSerializationFunction() {
332 @Override public NormalizedNode apply(
333 DeSerializer deSerializer,
334 NormalizedNodeMessages.Node node) {
335 NormalizedNodeAttrBuilder<YangInstanceIdentifier.NodeWithValue, Object, LeafSetEntryNode<Object>>
336 builder = Builders.leafSetEntryBuilder();
338 builder.withNodeIdentifier(deSerializer.toNodeWithValue(
339 node.getPathArgument()));
341 return deSerializer.buildNormalizedNode(builder, node);
345 deSerializationFunctions.put(CHOICE_NODE_TYPE,
346 new DeSerializationFunction() {
347 @Override public NormalizedNode apply(
348 DeSerializer deSerializer,
349 NormalizedNodeMessages.Node node) {
350 DataContainerNodeBuilder<YangInstanceIdentifier.NodeIdentifier, ChoiceNode>
352 Builders.choiceBuilder();
355 .withNodeIdentifier(deSerializer.toNodeIdentifier(
356 node.getPathArgument()));
358 return deSerializer.buildDataContainer(builder, node);
362 deSerializationFunctions.put(ORDERED_LEAF_SET_NODE_TYPE,
363 new DeSerializationFunction() {
364 @Override public NormalizedNode apply(
365 DeSerializer deSerializer,
366 NormalizedNodeMessages.Node node) {
367 ListNodeBuilder<Object, LeafSetEntryNode<Object>>
369 Builders.orderedLeafSetBuilder();
371 return deSerializer.buildListNode(builder, node);
377 deSerializationFunctions.put(ORDERED_MAP_NODE_TYPE,
378 new DeSerializationFunction() {
379 @Override public NormalizedNode apply(
380 DeSerializer deSerializer,
381 NormalizedNodeMessages.Node node) {
382 CollectionNodeBuilder<MapEntryNode, OrderedMapNode>
384 Builders.orderedMapBuilder();
386 return deSerializer.buildCollectionNode(builder, node);
390 deSerializationFunctions.put(UNKEYED_LIST_NODE_TYPE,
391 new DeSerializationFunction() {
392 @Override public NormalizedNode apply(
393 DeSerializer deSerializer,
394 NormalizedNodeMessages.Node node) {
395 CollectionNodeBuilder<UnkeyedListEntryNode, UnkeyedListNode>
397 Builders.unkeyedListBuilder();
399 return deSerializer.buildCollectionNode(builder, node);
403 deSerializationFunctions.put(UNKEYED_LIST_ENTRY_NODE_TYPE,
404 new DeSerializationFunction() {
405 @Override public NormalizedNode apply(
406 DeSerializer deSerializer,
407 NormalizedNodeMessages.Node node) {
408 DataContainerNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifier, UnkeyedListEntryNode>
410 Builders.unkeyedListEntryBuilder();
413 .withNodeIdentifier(deSerializer.toNodeIdentifier(
414 node.getPathArgument()));
416 return deSerializer.buildDataContainer(builder, node);
420 deSerializationFunctions.put(ANY_XML_NODE_TYPE,
421 new DeSerializationFunction() {
423 @Override public NormalizedNode apply(
424 DeSerializer deSerializer,
425 NormalizedNodeMessages.Node node) {
426 NormalizedNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifier, Node<?>, AnyXmlNode>
428 Builders.anyXmlBuilder();
431 .withNodeIdentifier(deSerializer.toNodeIdentifier(
432 node.getPathArgument()));
434 return deSerializer.buildNormalizedNode(builder, node);
440 private final NormalizedNodeMessages.Node node;
442 public DeSerializer(NormalizedNodeMessages.Node node){
446 public NormalizedNode deSerialize(){
447 return deSerialize(node);
450 private NormalizedNode deSerialize(NormalizedNodeMessages.Node node){
451 Preconditions.checkNotNull(node, "node should not be null");
453 DeSerializationFunction deSerializationFunction = deSerializationFunctions.get(
454 NormalizedNodeType.values()[node.getIntType()]);
456 return deSerializationFunction.apply(this, node);
460 private NormalizedNode buildCollectionNode(
461 CollectionNodeBuilder builder,
462 NormalizedNodeMessages.Node node) {
464 builder.withNodeIdentifier(toNodeIdentifier(node.getPathArgument()));
466 for(NormalizedNodeMessages.Node child : node.getChildList()){
467 builder.withChild(deSerialize(child));
470 return builder.build();
474 private NormalizedNode buildListNode(
475 ListNodeBuilder<Object, LeafSetEntryNode<Object>> builder,
476 NormalizedNodeMessages.Node node) {
477 builder.withNodeIdentifier(toNodeIdentifier(node.getPathArgument()));
479 for(NormalizedNodeMessages.Node child : node.getChildList()){
480 builder.withChild((LeafSetEntryNode<Object>) deSerialize(child));
483 return builder.build();
486 private NormalizedNode buildDataContainer(DataContainerNodeBuilder builder, NormalizedNodeMessages.Node node){
488 for(NormalizedNodeMessages.Node child : node.getChildList()){
489 builder.withChild((DataContainerChild<?, ?>) deSerialize(child));
492 //TODO : Also handle attributes
494 return builder.build();
497 private NormalizedNode buildNormalizedNode(NormalizedNodeAttrBuilder builder, NormalizedNodeMessages.Node node){
499 builder.withValue(ValueSerializer.deSerialize(this, node));
501 //TODO : Also handle attributes
503 return builder.build();
508 private YangInstanceIdentifier.NodeIdentifierWithPredicates toNodeIdentifierWithPredicates(
509 NormalizedNodeMessages.PathArgument path) {
510 return (YangInstanceIdentifier.NodeIdentifierWithPredicates) PathArgumentSerializer.deSerialize(this, path);
513 private YangInstanceIdentifier.AugmentationIdentifier toAugmentationIdentifier(
514 NormalizedNodeMessages.PathArgument path) {
515 return (YangInstanceIdentifier.AugmentationIdentifier) PathArgumentSerializer.deSerialize(this, path);
518 private YangInstanceIdentifier.NodeWithValue toNodeWithValue(
519 NormalizedNodeMessages.PathArgument path) {
520 return (YangInstanceIdentifier.NodeWithValue) PathArgumentSerializer.deSerialize(
524 private YangInstanceIdentifier.NodeIdentifier toNodeIdentifier(NormalizedNodeMessages.PathArgument path){
525 return (YangInstanceIdentifier.NodeIdentifier) PathArgumentSerializer.deSerialize(
529 @Override public String getNamespace(int namespace) {
530 return node.getCode(namespace);
533 @Override public String getRevision(int revision) {
534 return node.getCode(revision);
537 @Override public String getLocalName(int localName) {
538 return node.getCode(localName);
541 public YangInstanceIdentifier.PathArgument deSerialize(
542 NormalizedNodeMessages.PathArgument pathArgument) {
543 return PathArgumentSerializer.deSerialize(this, pathArgument);
546 private static interface DeSerializationFunction {
547 NormalizedNode apply(DeSerializer deserializer, NormalizedNodeMessages.Node node);