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.protobuff.messages.common.NormalizedNodeMessages;
13 import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil;
14 import org.opendaylight.yangtools.yang.data.api.Node;
15 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
16 import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode;
17 import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
18 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
19 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
20 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
21 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
22 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
23 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
24 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
25 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
26 import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode;
27 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
28 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
29 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
30 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
31 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
32 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
33 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.ListNodeBuilder;
34 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeAttrBuilder;
37 import java.util.ArrayList;
38 import java.util.Date;
39 import java.util.HashMap;
40 import java.util.List;
43 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.ANY_XML_NODE_TYPE;
44 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.AUGMENTATION_NODE_TYPE;
45 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.CHOICE_NODE_TYPE;
46 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.CONTAINER_NODE_TYPE;
47 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.LEAF_NODE_TYPE;
48 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.LEAF_SET_ENTRY_NODE_TYPE;
49 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.LEAF_SET_NODE_TYPE;
50 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.MAP_ENTRY_NODE_TYPE;
51 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.MAP_NODE_TYPE;
52 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.ORDERED_LEAF_SET_NODE_TYPE;
53 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.ORDERED_MAP_NODE_TYPE;
54 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.UNKEYED_LIST_ENTRY_NODE_TYPE;
55 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.UNKEYED_LIST_NODE_TYPE;
56 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.getSerializableNodeType;
59 * NormalizedNodeSerializer can be used to convert a Normalized node to and and
60 * from a protocol buffer message.
64 public class NormalizedNodeSerializer {
67 * Serialize a NormalizedNode into a protocol buffer message
69 * The significant things to be aware of the Serialization process are
71 * <li>Repeated strings like namespaces, revisions and localNames are
72 * compressed to codes and stored in the top level of the normalized
73 * node protocol buffer message
75 * <li>All value types are encoded for each leaf value. This is so that
76 * the deSerialization process does not need to use the schema context to
77 * figure out how to decode values
81 * One question which may arise is why not use something like gzip to
82 * compress the protocol buffer message instead of rolling our own
83 * encoding scheme. This has to be explored further as it is a more
89 public static NormalizedNodeMessages.Node serialize(NormalizedNode node){
90 Preconditions.checkNotNull(node, "node should not be null");
91 return new Serializer(node).serialize();
96 * DeSerialize a protocol buffer message back into a NormalizedNode
101 public static NormalizedNode deSerialize(NormalizedNodeMessages.Node node){
102 return new DeSerializer(node).deSerialize();
106 * DeSerialize a PathArgument which is in the protocol buffer format into
107 * a yang PathArgument. The protocol buffer path argument is specially
108 * encoded and can only be interpreted in the context of a top level
109 * serialized NormalizedNode protocol buffer message. The reason for this
110 * is that during the NormalizedNode serialization process certain repeated
111 * strings are encoded into a "codes" list and the actual strings are
112 * replaced by an integer which is an index into that list.
115 * @param pathArgument
118 public static YangInstanceIdentifier.PathArgument deSerialize(NormalizedNodeMessages.Node node, NormalizedNodeMessages.PathArgument pathArgument){
119 Preconditions.checkNotNull(node, "node should not be null");
120 Preconditions.checkNotNull(pathArgument, "pathArgument should not be null");
121 return new DeSerializer(node).deSerialize(pathArgument);
124 private static class Serializer implements NormalizedNodeSerializationContext {
126 private final NormalizedNode node;
128 private final Map<Object, Integer> codeMap = new HashMap<>();
129 private final List<String> codes = new ArrayList<>();
131 private Serializer(NormalizedNode node) {
135 private NormalizedNodeMessages.Node serialize() {
136 return this.serialize(node).addAllCode(codes).build();
139 private NormalizedNodeMessages.Node.Builder serialize(
140 NormalizedNode node) {
141 NormalizedNodeMessages.Node.Builder builder =
142 NormalizedNodeMessages.Node.newBuilder();
144 builder.setPathArgument(PathArgumentSerializer.serialize(this, node.getIdentifier()));
145 Integer nodeType = getSerializableNodeType(node).ordinal();
146 builder.setIntType(nodeType);
147 Object value = node.getValue();
149 // We need to do a specific check of the type of the node here
150 // because if we looked at the value type alone we will not be
151 // able to distinguish if the value was supposed to be added
152 // as a child or whether the value should be added as a value of the
154 // One use case where this check is necessary when you have a node
155 // with a bits value. In that case the value of the node is a Set
156 // which is also a Collection. Without the following check being
157 // done first the code would flow into the Collection if condition
158 // and the Set would be added as child nodes
159 if(nodeType == NormalizedNodeType.LEAF_NODE_TYPE.ordinal() ||
160 nodeType == NormalizedNodeType.LEAF_SET_ENTRY_NODE_TYPE.ordinal()){
162 ValueSerializer.serialize(builder, this, value);
164 } else if (value instanceof Iterable) {
165 Iterable iterable = (Iterable) value;
167 for (Object o : iterable) {
168 if (o instanceof NormalizedNode) {
169 builder.addChild(serialize((NormalizedNode) o));
173 } else if (value instanceof NormalizedNode) {
175 builder.addChild(serialize((NormalizedNode) value));
179 ValueSerializer.serialize(builder, this, value);
186 @Override public int addNamespace(URI namespace) {
187 int namespaceInt = getCode(namespace);
189 if(namespaceInt == -1) {
190 namespaceInt = addCode(namespace, namespace.toString());
195 @Override public int addRevision(Date revision) {
196 if(revision == null){
200 int revisionInt = getCode(revision);
201 if(revisionInt == -1) {
202 String formattedRevision =
203 SimpleDateFormatUtil.getRevisionFormat().format(revision);
204 revisionInt = addCode(revision, formattedRevision);
209 @Override public int addLocalName(String localName) {
210 int localNameInt = getCode(localName);
211 if(localNameInt == -1) {
212 localNameInt = addCode(localName, localName.toString());
218 public int addCode(Object code, String codeStr){
219 int count = codes.size();
221 codeMap.put(code, Integer.valueOf(count));
225 public int getCode(Object code){
226 if(codeMap.containsKey(code)){
227 return codeMap.get(code);
233 private static class DeSerializer implements NormalizedNodeDeSerializationContext {
234 private static Map<NormalizedNodeType, DeSerializationFunction>
235 deSerializationFunctions = new HashMap<>();
238 deSerializationFunctions.put(CONTAINER_NODE_TYPE,
239 new DeSerializationFunction() {
240 @Override public NormalizedNode apply(
241 DeSerializer deSerializer,
242 NormalizedNodeMessages.Node node) {
243 DataContainerNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifier, ContainerNode>
244 builder = Builders.containerBuilder();
247 .withNodeIdentifier(deSerializer.toNodeIdentifier(
248 node.getPathArgument()));
250 return deSerializer.buildDataContainer(builder, node);
256 deSerializationFunctions.put(LEAF_NODE_TYPE,
257 new DeSerializationFunction() {
258 @Override public NormalizedNode apply(
259 DeSerializer deSerializer,
260 NormalizedNodeMessages.Node node) {
261 NormalizedNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifier, Object, LeafNode<Object>>
262 builder = Builders.leafBuilder();
265 .withNodeIdentifier(deSerializer.toNodeIdentifier(
266 node.getPathArgument()));
268 return deSerializer.buildNormalizedNode(builder, node);
273 deSerializationFunctions.put(MAP_NODE_TYPE,
274 new DeSerializationFunction() {
275 @Override public NormalizedNode apply(
276 DeSerializer deSerializer,
277 NormalizedNodeMessages.Node node) {
278 CollectionNodeBuilder<MapEntryNode, MapNode>
279 builder = Builders.mapBuilder();
281 return deSerializer.buildCollectionNode(builder, node);
285 deSerializationFunctions.put(MAP_ENTRY_NODE_TYPE,
286 new DeSerializationFunction() {
287 @Override public NormalizedNode apply(
288 DeSerializer deSerializer,
289 NormalizedNodeMessages.Node node) {
290 DataContainerNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifierWithPredicates, MapEntryNode>
291 builder = Builders.mapEntryBuilder();
293 builder.withNodeIdentifier(deSerializer.toNodeIdentifierWithPredicates(
294 node.getPathArgument()));
296 return deSerializer.buildDataContainer(builder, node);
300 deSerializationFunctions.put(AUGMENTATION_NODE_TYPE,
301 new DeSerializationFunction() {
302 @Override public NormalizedNode apply(
303 DeSerializer deSerializer,
304 NormalizedNodeMessages.Node node) {
305 DataContainerNodeBuilder<YangInstanceIdentifier.AugmentationIdentifier, AugmentationNode>
306 builder = Builders.augmentationBuilder();
308 builder.withNodeIdentifier(
309 deSerializer.toAugmentationIdentifier(
310 node.getPathArgument()));
312 return deSerializer.buildDataContainer(builder, node);
316 deSerializationFunctions.put(LEAF_SET_NODE_TYPE,
317 new DeSerializationFunction() {
318 @Override public NormalizedNode apply(
319 DeSerializer deSerializer,
320 NormalizedNodeMessages.Node node) {
321 ListNodeBuilder<Object, LeafSetEntryNode<Object>>
322 builder = Builders.leafSetBuilder();
324 return deSerializer.buildListNode(builder, node);
328 deSerializationFunctions.put(LEAF_SET_ENTRY_NODE_TYPE,
329 new DeSerializationFunction() {
330 @Override public NormalizedNode apply(
331 DeSerializer deSerializer,
332 NormalizedNodeMessages.Node node) {
333 NormalizedNodeAttrBuilder<YangInstanceIdentifier.NodeWithValue, Object, LeafSetEntryNode<Object>>
334 builder = Builders.leafSetEntryBuilder();
336 builder.withNodeIdentifier(deSerializer.toNodeWithValue(
337 node.getPathArgument()));
339 return deSerializer.buildNormalizedNode(builder, node);
343 deSerializationFunctions.put(CHOICE_NODE_TYPE,
344 new DeSerializationFunction() {
345 @Override public NormalizedNode apply(
346 DeSerializer deSerializer,
347 NormalizedNodeMessages.Node node) {
348 DataContainerNodeBuilder<YangInstanceIdentifier.NodeIdentifier, ChoiceNode>
350 Builders.choiceBuilder();
353 .withNodeIdentifier(deSerializer.toNodeIdentifier(
354 node.getPathArgument()));
356 return deSerializer.buildDataContainer(builder, node);
360 deSerializationFunctions.put(ORDERED_LEAF_SET_NODE_TYPE,
361 new DeSerializationFunction() {
362 @Override public NormalizedNode apply(
363 DeSerializer deSerializer,
364 NormalizedNodeMessages.Node node) {
365 ListNodeBuilder<Object, LeafSetEntryNode<Object>>
367 Builders.orderedLeafSetBuilder();
369 return deSerializer.buildListNode(builder, node);
375 deSerializationFunctions.put(ORDERED_MAP_NODE_TYPE,
376 new DeSerializationFunction() {
377 @Override public NormalizedNode apply(
378 DeSerializer deSerializer,
379 NormalizedNodeMessages.Node node) {
380 CollectionNodeBuilder<MapEntryNode, OrderedMapNode>
382 Builders.orderedMapBuilder();
384 return deSerializer.buildCollectionNode(builder, node);
388 deSerializationFunctions.put(UNKEYED_LIST_NODE_TYPE,
389 new DeSerializationFunction() {
390 @Override public NormalizedNode apply(
391 DeSerializer deSerializer,
392 NormalizedNodeMessages.Node node) {
393 CollectionNodeBuilder<UnkeyedListEntryNode, UnkeyedListNode>
395 Builders.unkeyedListBuilder();
397 return deSerializer.buildCollectionNode(builder, node);
401 deSerializationFunctions.put(UNKEYED_LIST_ENTRY_NODE_TYPE,
402 new DeSerializationFunction() {
403 @Override public NormalizedNode apply(
404 DeSerializer deSerializer,
405 NormalizedNodeMessages.Node node) {
406 DataContainerNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifier, UnkeyedListEntryNode>
408 Builders.unkeyedListEntryBuilder();
411 .withNodeIdentifier(deSerializer.toNodeIdentifier(
412 node.getPathArgument()));
414 return deSerializer.buildDataContainer(builder, node);
418 deSerializationFunctions.put(ANY_XML_NODE_TYPE,
419 new DeSerializationFunction() {
421 @Override public NormalizedNode apply(
422 DeSerializer deSerializer,
423 NormalizedNodeMessages.Node node) {
424 NormalizedNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifier, Node<?>, AnyXmlNode>
426 Builders.anyXmlBuilder();
429 .withNodeIdentifier(deSerializer.toNodeIdentifier(
430 node.getPathArgument()));
432 return deSerializer.buildNormalizedNode(builder, node);
438 private final NormalizedNodeMessages.Node node;
440 public DeSerializer(NormalizedNodeMessages.Node node){
444 public NormalizedNode deSerialize(){
445 return deSerialize(node);
448 private NormalizedNode deSerialize(NormalizedNodeMessages.Node node){
449 Preconditions.checkNotNull(node, "node should not be null");
450 DeSerializationFunction deSerializationFunction =
451 Preconditions.checkNotNull(deSerializationFunctions.get(NormalizedNodeType.values()[node.getIntType()]), "Unknown type " + node);
453 return deSerializationFunction.apply(this, node);
457 private NormalizedNode buildCollectionNode(
458 CollectionNodeBuilder builder,
459 NormalizedNodeMessages.Node node) {
461 builder.withNodeIdentifier(toNodeIdentifier(node.getPathArgument()));
463 for(NormalizedNodeMessages.Node child : node.getChildList()){
464 builder.withChild(deSerialize(child));
467 return builder.build();
471 private NormalizedNode buildListNode(
472 ListNodeBuilder<Object, LeafSetEntryNode<Object>> builder,
473 NormalizedNodeMessages.Node node) {
474 builder.withNodeIdentifier(toNodeIdentifier(node.getPathArgument()));
476 for(NormalizedNodeMessages.Node child : node.getChildList()){
477 builder.withChild((LeafSetEntryNode<Object>) deSerialize(child));
480 return builder.build();
483 private NormalizedNode buildDataContainer(DataContainerNodeBuilder builder, NormalizedNodeMessages.Node node){
485 for(NormalizedNodeMessages.Node child : node.getChildList()){
486 builder.withChild((DataContainerChild<?, ?>) deSerialize(child));
489 //TODO : Also handle attributes
491 return builder.build();
494 private NormalizedNode buildNormalizedNode(NormalizedNodeAttrBuilder builder, NormalizedNodeMessages.Node node){
496 builder.withValue(ValueSerializer.deSerialize(this, node));
498 //TODO : Also handle attributes
500 return builder.build();
505 private YangInstanceIdentifier.NodeIdentifierWithPredicates toNodeIdentifierWithPredicates(
506 NormalizedNodeMessages.PathArgument path) {
507 return (YangInstanceIdentifier.NodeIdentifierWithPredicates) PathArgumentSerializer.deSerialize(this, path);
510 private YangInstanceIdentifier.AugmentationIdentifier toAugmentationIdentifier(
511 NormalizedNodeMessages.PathArgument path) {
512 return (YangInstanceIdentifier.AugmentationIdentifier) PathArgumentSerializer.deSerialize(this, path);
515 private YangInstanceIdentifier.NodeWithValue toNodeWithValue(
516 NormalizedNodeMessages.PathArgument path) {
517 return (YangInstanceIdentifier.NodeWithValue) PathArgumentSerializer.deSerialize(
521 private YangInstanceIdentifier.NodeIdentifier toNodeIdentifier(NormalizedNodeMessages.PathArgument path){
522 return (YangInstanceIdentifier.NodeIdentifier) PathArgumentSerializer.deSerialize(
526 @Override public String getNamespace(int namespace) {
527 return node.getCode(namespace);
530 @Override public String getRevision(int revision) {
531 return node.getCode(revision);
534 @Override public String getLocalName(int localName) {
535 return node.getCode(localName);
538 public YangInstanceIdentifier.PathArgument deSerialize(
539 NormalizedNodeMessages.PathArgument pathArgument) {
540 return PathArgumentSerializer.deSerialize(this, pathArgument);
543 private static interface DeSerializationFunction {
544 NormalizedNode apply(DeSerializer deserializer, NormalizedNodeMessages.Node node);