Merge "BUG 720 - YANG leaf as JSON input *<*:* couldn't be saved"
[controller.git] / opendaylight / md-sal / sal-clustering-commons / src / main / java / org / opendaylight / controller / cluster / datastore / node / utils / serialization / PathArgumentSerializer.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8
9 package org.opendaylight.controller.cluster.datastore.node.utils.serialization;
10
11 import com.google.common.base.Preconditions;
12 import org.opendaylight.controller.cluster.datastore.node.utils.NodeIdentifierFactory;
13 import org.opendaylight.controller.cluster.datastore.node.utils.QNameFactory;
14 import org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages;
15 import org.opendaylight.yangtools.yang.common.QName;
16 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
17 import java.util.ArrayList;
18 import java.util.Arrays;
19 import java.util.Collections;
20 import java.util.HashMap;
21 import java.util.HashSet;
22 import java.util.List;
23 import java.util.Map;
24 import java.util.Map.Entry;
25 import java.util.Set;
26 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.PathArgumentType.getSerializablePathArgumentType;
27
28 public class PathArgumentSerializer {
29     private static final String REVISION_ARG = "?revision=";
30     private static final Map<Class<?>, PathArgumentAttributesGetter> pathArgumentAttributesGetters = new HashMap<>();
31
32     public static NormalizedNodeMessages.PathArgument serialize(QNameSerializationContext context,
33             YangInstanceIdentifier.PathArgument pathArgument){
34         Preconditions.checkNotNull(context, "context should not be null");
35         Preconditions.checkNotNull(pathArgument, "pathArgument should not be null");
36
37         QName nodeType = null;
38         if (!(pathArgument instanceof YangInstanceIdentifier.AugmentationIdentifier)) {
39             nodeType = pathArgument.getNodeType();
40         }
41
42         NormalizedNodeMessages.PathArgument.Builder builder =
43             NormalizedNodeMessages.PathArgument.newBuilder();
44
45         NormalizedNodeMessages.PathArgument serializablePathArgument =
46             builder
47                 .setIntType(getSerializablePathArgumentType(pathArgument))
48                 .setNodeType(encodeQName(context, nodeType))
49                 .addAllAttribute(getPathArgumentAttributes(context, pathArgument))
50                 .build();
51
52         return serializablePathArgument;
53     }
54
55
56     public static YangInstanceIdentifier.PathArgument deSerialize(QNameDeSerializationContext context,
57             NormalizedNodeMessages.PathArgument pathArgument){
58         Preconditions.checkNotNull(context, "context should not be null");
59         Preconditions.checkNotNull(pathArgument, "pathArgument should not be null");
60
61         return parsePathArgument(context, pathArgument);
62     }
63
64
65     private static interface PathArgumentAttributesGetter {
66         Iterable<? extends NormalizedNodeMessages.PathArgumentAttribute> get(
67                 QNameSerializationContext context, YangInstanceIdentifier.PathArgument pathArgument);
68     }
69
70     static {
71         pathArgumentAttributesGetters.put(YangInstanceIdentifier.NodeWithValue.class, new PathArgumentAttributesGetter() {
72             @Override
73             public Iterable<? extends NormalizedNodeMessages.PathArgumentAttribute> get(
74                     QNameSerializationContext context, YangInstanceIdentifier.PathArgument pathArgument) {
75
76                 YangInstanceIdentifier.NodeWithValue identifier
77                     = (YangInstanceIdentifier.NodeWithValue) pathArgument;
78
79                 NormalizedNodeMessages.PathArgumentAttribute attribute =
80                     buildAttribute(context, null, identifier.getValue());
81
82                 return Arrays.asList(attribute);
83             }
84         });
85
86         pathArgumentAttributesGetters.put(YangInstanceIdentifier.NodeIdentifierWithPredicates.class, new PathArgumentAttributesGetter() {
87             @Override
88             public Iterable<? extends NormalizedNodeMessages.PathArgumentAttribute> get(
89                     QNameSerializationContext context, YangInstanceIdentifier.PathArgument pathArgument) {
90
91                 YangInstanceIdentifier.NodeIdentifierWithPredicates identifier
92                     = (YangInstanceIdentifier.NodeIdentifierWithPredicates) pathArgument;
93
94                 Map<QName, Object> keyValues = identifier.getKeyValues();
95                 List<NormalizedNodeMessages.PathArgumentAttribute> attributes =
96                         new ArrayList<>(keyValues.size());
97                 for (Entry<QName, Object> e : keyValues.entrySet()) {
98                     NormalizedNodeMessages.PathArgumentAttribute attribute =
99                         buildAttribute(context, e.getKey(), e.getValue());
100
101                     attributes.add(attribute);
102                 }
103
104                 return attributes;
105             }
106         });
107
108         pathArgumentAttributesGetters.put(YangInstanceIdentifier.AugmentationIdentifier.class, new PathArgumentAttributesGetter() {
109             @Override
110             public Iterable<? extends NormalizedNodeMessages.PathArgumentAttribute> get(
111                     QNameSerializationContext context, YangInstanceIdentifier.PathArgument pathArgument) {
112
113                 YangInstanceIdentifier.AugmentationIdentifier identifier
114                     = (YangInstanceIdentifier.AugmentationIdentifier) pathArgument;
115
116                 Set<QName> possibleChildNames = identifier.getPossibleChildNames();
117                 List<NormalizedNodeMessages.PathArgumentAttribute> attributes =
118                         new ArrayList<>(possibleChildNames.size());
119                 for (QName key : possibleChildNames) {
120                     Object value = key;
121                     NormalizedNodeMessages.PathArgumentAttribute attribute =
122                         buildAttribute(context, key, value);
123
124                     attributes.add(attribute);
125                 }
126
127                 return attributes;
128             }
129         });
130
131
132         pathArgumentAttributesGetters.put(YangInstanceIdentifier.NodeIdentifier.class, new PathArgumentAttributesGetter() {
133             @Override
134             public Iterable<? extends NormalizedNodeMessages.PathArgumentAttribute> get(
135                     QNameSerializationContext context, YangInstanceIdentifier.PathArgument pathArgument) {
136                 return Collections.emptyList();
137             }
138         });
139     }
140
141     private static NormalizedNodeMessages.PathArgumentAttribute buildAttribute(
142             QNameSerializationContext context, QName name, Object value) {
143         NormalizedNodeMessages.PathArgumentAttribute.Builder builder =
144             NormalizedNodeMessages.PathArgumentAttribute.newBuilder();
145
146         builder.setName(encodeQName(context, name));
147         ValueSerializer.serialize(builder, context, value);
148
149         return builder.build();
150
151     }
152
153     private static NormalizedNodeMessages.QName.Builder encodeQName(QNameSerializationContext context,
154             QName qName) {
155         if(qName == null) {
156             return NormalizedNodeMessages.QName.getDefaultInstance().toBuilder();
157         }
158         NormalizedNodeMessages.QName.Builder qNameBuilder =
159             NormalizedNodeMessages.QName.newBuilder();
160
161         qNameBuilder.setNamespace(context.addNamespace(qName.getNamespace()));
162
163         qNameBuilder.setRevision(context.addRevision(qName.getRevision()));
164
165         qNameBuilder.setLocalName(context.addLocalName(qName.getLocalName()));
166
167         return qNameBuilder;
168     }
169
170     private static Iterable<? extends NormalizedNodeMessages.PathArgumentAttribute> getPathArgumentAttributes(
171             QNameSerializationContext context, YangInstanceIdentifier.PathArgument pathArgument) {
172
173         return pathArgumentAttributesGetters.get(pathArgument.getClass()).get(context, pathArgument);
174     }
175
176
177     private static String qNameToString(QNameDeSerializationContext context,
178         NormalizedNodeMessages.QName qName){
179         // If this serializer is used qName cannot be null (see encodeQName)
180         // adding null check only in case someone tried to deSerialize a protocol buffer node
181         // that was not serialized using the PathArgumentSerializer
182 //        Preconditions.checkNotNull(qName, "qName should not be null");
183 //        Preconditions.checkArgument(qName.getNamespace() != -1, "qName.namespace should be valid");
184
185         String namespace = context.getNamespace(qName.getNamespace());
186         String localName = context.getLocalName(qName.getLocalName());
187         StringBuilder sb;
188         if(qName.getRevision() != -1){
189             String revision = context.getRevision(qName.getRevision());
190             sb = new StringBuilder(namespace.length() + REVISION_ARG.length() + revision.length() +
191                     localName.length() + 2);
192             sb.append('(').append(namespace).append(REVISION_ARG).append(
193                 revision).append(')').append(localName);
194         } else {
195             sb = new StringBuilder(namespace.length() + localName.length() + 2);
196             sb.append('(').append(namespace).append(')').append(localName);
197         }
198
199         return sb.toString();
200     }
201
202     /**
203      * Parse a protocol buffer PathArgument and return an MD-SAL PathArgument
204      *
205      * @param pathArgument protocol buffer PathArgument
206      * @return MD-SAL PathArgument
207      */
208     private static YangInstanceIdentifier.PathArgument parsePathArgument(
209             QNameDeSerializationContext context, NormalizedNodeMessages.PathArgument pathArgument) {
210
211         switch(PathArgumentType.values()[pathArgument.getIntType()]){
212             case NODE_IDENTIFIER_WITH_VALUE : {
213
214                 YangInstanceIdentifier.NodeWithValue nodeWithValue =
215                     new YangInstanceIdentifier.NodeWithValue(
216                         QNameFactory.create(qNameToString(context, pathArgument.getNodeType())),
217                         parseAttribute(context, pathArgument.getAttribute(0)));
218
219                 return nodeWithValue;
220             }
221
222             case NODE_IDENTIFIER_WITH_PREDICATES : {
223
224                 YangInstanceIdentifier.NodeIdentifierWithPredicates
225                     nodeIdentifierWithPredicates =
226                     new YangInstanceIdentifier.NodeIdentifierWithPredicates(
227                         QNameFactory.create(qNameToString(context, pathArgument.getNodeType())),
228                         toAttributesMap(context, pathArgument.getAttributeList()));
229
230                 return nodeIdentifierWithPredicates;
231             }
232
233             case AUGMENTATION_IDENTIFIER: {
234
235                 Set<QName> qNameSet = new HashSet<>();
236
237                 for(NormalizedNodeMessages.PathArgumentAttribute attribute : pathArgument.getAttributeList()){
238                     qNameSet.add(QNameFactory.create(qNameToString(context, attribute.getName())));
239                 }
240
241                 return new YangInstanceIdentifier.AugmentationIdentifier(qNameSet);
242
243             }
244             default: {
245                 return NodeIdentifierFactory.getArgument(qNameToString(context,
246                     pathArgument.getNodeType()));
247             }
248
249         }
250     }
251
252     private static Map<QName, Object> toAttributesMap(
253             QNameDeSerializationContext context,
254             List<NormalizedNodeMessages.PathArgumentAttribute> attributesList) {
255
256         Map<QName, Object> map;
257         if(attributesList.size() == 1) {
258             NormalizedNodeMessages.PathArgumentAttribute attribute = attributesList.get(0);
259             NormalizedNodeMessages.QName name = attribute.getName();
260             Object value = parseAttribute(context, attribute);
261             map = Collections.singletonMap(QNameFactory.create(qNameToString(context, name)), value);
262         } else {
263             map = new HashMap<>();
264
265             for(NormalizedNodeMessages.PathArgumentAttribute attribute : attributesList){
266                 NormalizedNodeMessages.QName name = attribute.getName();
267                 Object value = parseAttribute(context, attribute);
268
269                 map.put(QNameFactory.create(qNameToString(context, name)), value);
270             }
271         }
272
273         return map;
274     }
275
276     private static Object parseAttribute(QNameDeSerializationContext context,
277             NormalizedNodeMessages.PathArgumentAttribute attribute){
278         return ValueSerializer.deSerialize(context, attribute);
279     }
280
281 }