Merge changes I05e784af,Ie975a2ec
[controller.git] / opendaylight / md-sal / sal-binding-broker / src / main / java / org / opendaylight / controller / md / sal / binding / impl / BindingToNormalizedNodeCodec.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 package org.opendaylight.controller.md.sal.binding.impl;
9
10 import java.lang.reflect.Method;
11 import java.lang.reflect.Type;
12 import java.util.AbstractMap.SimpleEntry;
13 import java.util.LinkedList;
14 import java.util.List;
15 import java.util.Map.Entry;
16 import java.util.concurrent.Callable;
17
18 import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationException;
19 import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizer;
20 import org.opendaylight.yangtools.concepts.util.ClassLoaderUtils;
21 import org.opendaylight.yangtools.yang.binding.Augmentation;
22 import org.opendaylight.yangtools.yang.binding.DataContainer;
23 import org.opendaylight.yangtools.yang.binding.DataObject;
24 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
25 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.Item;
26 import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
27 import org.opendaylight.yangtools.yang.common.QName;
28 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
29 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.AugmentationIdentifier;
30 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifier;
31 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
32 import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
33 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
34 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
35 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
36 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
37 import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService;
38 import org.opendaylight.yangtools.yang.data.impl.codec.DeserializationException;
39 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
40 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
41 import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
44
45 import com.google.common.collect.ImmutableList;
46 import com.google.common.collect.Iterables;
47
48 public class BindingToNormalizedNodeCodec implements SchemaContextListener {
49
50     private static final Logger LOG = LoggerFactory.getLogger(BindingToNormalizedNodeCodec.class);
51
52     private final BindingIndependentMappingService bindingToLegacy;
53     private DataNormalizer legacyToNormalized;
54
55     public BindingToNormalizedNodeCodec(final BindingIndependentMappingService mappingService) {
56         super();
57         this.bindingToLegacy = mappingService;
58     }
59
60     public org.opendaylight.yangtools.yang.data.api.InstanceIdentifier toNormalized(
61             final InstanceIdentifier<? extends DataObject> binding) {
62
63         // Used instance-identifier codec do not support serialization of last path
64         // argument if it is Augmentation (behaviour expected by old datastore)
65         // in this case, we explicitly check if last argument is augmentation
66         // to process it separately
67         if (isAugmentationIdentifier(binding)) {
68             return toNormalizedAugmented(binding);
69         }
70         return toNormalizedImpl(binding);
71     }
72
73     public Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>> toNormalizedNode(
74             final InstanceIdentifier<? extends DataObject> bindingPath, final DataObject bindingObject) {
75         return toNormalizedNode(toEntry(bindingPath, bindingObject));
76
77     }
78
79     public Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>> toNormalizedNode(
80             final Entry<org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject>, DataObject> binding) {
81         Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> legacyEntry = bindingToLegacy
82                 .toDataDom(binding);
83         Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>> normalizedEntry = legacyToNormalized
84                 .toNormalized(legacyEntry);
85         LOG.trace("Serialization of {}, Legacy Representation: {}, Normalized Representation: {}", binding,
86                 legacyEntry, normalizedEntry);
87         if (Augmentation.class.isAssignableFrom(binding.getKey().getTargetType())) {
88
89             for (DataContainerChild<? extends PathArgument, ?> child : ((DataContainerNode<?>) normalizedEntry
90                     .getValue()).getValue()) {
91                 if (child instanceof AugmentationNode) {
92                     ImmutableList<PathArgument> childArgs = ImmutableList.<PathArgument> builder()
93                             .addAll(normalizedEntry.getKey().getPath()).add(child.getIdentifier()).build();
94                     org.opendaylight.yangtools.yang.data.api.InstanceIdentifier childPath = new org.opendaylight.yangtools.yang.data.api.InstanceIdentifier(
95                             childArgs);
96                     return new SimpleEntry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>>(
97                             childPath, child);
98                 }
99             }
100
101         }
102         return normalizedEntry;
103
104     }
105
106     public InstanceIdentifier<? extends DataObject> toBinding(
107             final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized)
108             throws DeserializationException {
109
110         PathArgument lastArgument = Iterables.getLast(normalized.getPath());
111         // Used instance-identifier codec do not support serialization of last path
112         // argument if it is AugmentationIdentifier (behaviour expected by old datastore)
113         // in this case, we explicitly check if last argument is augmentation
114         // to process it separately
115         if (lastArgument instanceof AugmentationIdentifier) {
116             return toBindingAugmented(normalized);
117         }
118         return toBindingImpl(normalized);
119     }
120
121     private InstanceIdentifier<? extends DataObject> toBindingAugmented(
122             final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized) throws DeserializationException {
123         InstanceIdentifier<? extends DataObject> potential = toBindingImpl(normalized);
124         // Shorthand check, if codec already supports deserialization
125         // of AugmentationIdentifier we will return
126         if(isAugmentationIdentifier(potential)) {
127             return potential;
128         }
129
130         AugmentationIdentifier lastArgument = (AugmentationIdentifier) Iterables.getLast(normalized.getPath());
131
132         // Here we employ small trick - Binding-aware Codec injects an pointer to augmentation class
133         // if child is referenced - so we will reference child and then shorten path.
134         for (QName child : lastArgument.getPossibleChildNames()) {
135             org.opendaylight.yangtools.yang.data.api.InstanceIdentifier childPath = new org.opendaylight.yangtools.yang.data.api.InstanceIdentifier(
136                     ImmutableList.<PathArgument> builder()
137                     .addAll(normalized.getPath()).add(new NodeIdentifier(child)).build());
138             try {
139
140                 InstanceIdentifier<? extends DataObject> potentialPath = shortenToLastAugment(toBindingImpl(childPath));
141                 return potentialPath;
142             } catch (Exception e) {
143                 LOG.trace("Unable to deserialize aug. child path for {}",childPath,e);
144             }
145         }
146         return toBindingImpl(normalized);
147     }
148
149     private InstanceIdentifier<? extends DataObject> toBindingImpl(
150             final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized)
151             throws DeserializationException {
152         org.opendaylight.yangtools.yang.data.api.InstanceIdentifier legacyPath;
153         try {
154             legacyPath = legacyToNormalized.toLegacy(normalized);
155         } catch (DataNormalizationException e) {
156             throw new IllegalStateException("Could not denormalize path.", e);
157         }
158         LOG.trace("InstanceIdentifier Path Deserialization: Legacy representation {}, Normalized representation: {}",
159                 legacyPath, normalized);
160         return bindingToLegacy.fromDataDom(legacyPath);
161     }
162
163     private static final Entry<org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject>, DataObject> toEntry(
164             final org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject> key,
165             final DataObject value) {
166         return new SimpleEntry<org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject>, DataObject>(
167                 key, value);
168     }
169
170     public DataObject toBinding(final InstanceIdentifier<?> path, final NormalizedNode<?, ?> normalizedNode)
171             throws DeserializationException {
172         CompositeNode legacy = null;
173         if(isAugmentationIdentifier(path) && normalizedNode instanceof AugmentationNode) {
174             QName augIdentifier = BindingReflections.findQName(path.getTargetType());
175             ContainerNode virtualNode = Builders.containerBuilder() //
176                     .withNodeIdentifier(new NodeIdentifier(augIdentifier)) //
177                     .withChild((DataContainerChild<?, ?>) normalizedNode) //
178                     .build();
179             legacy = (CompositeNode) DataNormalizer.toLegacy(virtualNode);
180         } else {
181             legacy = (CompositeNode) DataNormalizer.toLegacy(normalizedNode);
182         }
183
184         return bindingToLegacy.dataObjectFromDataDom(path, legacy);
185     }
186
187     public DataNormalizer getDataNormalizer() {
188         return legacyToNormalized;
189     }
190
191     public Entry<org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject>, DataObject> toBinding(
192             final Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, ? extends NormalizedNode<?, ?>> normalized)
193             throws DeserializationException {
194         InstanceIdentifier<? extends DataObject> bindingPath = toBinding(normalized.getKey());
195         DataObject bindingData = toBinding(bindingPath, normalized.getValue());
196         return toEntry(bindingPath, bindingData);
197     }
198
199     @Override
200     public void onGlobalContextUpdated(final SchemaContext arg0) {
201         legacyToNormalized = new DataNormalizer(arg0);
202     }
203
204     private org.opendaylight.yangtools.yang.data.api.InstanceIdentifier toNormalizedAugmented(
205             final InstanceIdentifier<?> augPath) {
206         org.opendaylight.yangtools.yang.data.api.InstanceIdentifier processed = toNormalizedImpl(augPath);
207         // If used instance identifier codec added supports for deserialization
208         // of last AugmentationIdentifier we will just reuse it
209         if(isAugmentationIdentifier(processed)) {
210             return processed;
211         }
212         // Here we employ small trick - DataNormalizer injecst augmentation identifier if child is
213         // also part of the path (since using a child we can safely identify augmentation)
214         // so, we scan augmentation for children add it to path
215         // and use original algorithm, then shorten it to last augmentation
216         for (@SuppressWarnings("rawtypes") Class augChild : getAugmentationChildren(augPath.getTargetType())) {
217             @SuppressWarnings("unchecked")
218             InstanceIdentifier<?> childPath = augPath.child(augChild);
219             org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized = toNormalizedImpl(childPath);
220             org.opendaylight.yangtools.yang.data.api.InstanceIdentifier potentialDiscovered = shortenToLastAugmentation(normalized);
221             if (potentialDiscovered != null) {
222                 return potentialDiscovered;
223             }
224         }
225         return processed;
226     }
227
228
229
230     private org.opendaylight.yangtools.yang.data.api.InstanceIdentifier shortenToLastAugmentation(
231             final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized) {
232         int position = 0;
233         int foundPosition = -1;
234         for (PathArgument arg : normalized.getPath()) {
235             position++;
236             if (arg instanceof AugmentationIdentifier) {
237                 foundPosition = position;
238             }
239         }
240         if (foundPosition > 0) {
241             return new org.opendaylight.yangtools.yang.data.api.InstanceIdentifier(normalized.getPath().subList(0,
242                     foundPosition));
243         }
244         return null;
245     }
246
247     private InstanceIdentifier<? extends DataObject> shortenToLastAugment(
248             final InstanceIdentifier<? extends DataObject> binding) {
249         int position = 0;
250         int foundPosition = -1;
251         for(org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument arg : binding.getPathArguments()) {
252             position++;
253             if (isAugmentation(arg.getType())) {
254                 foundPosition = position;
255             }
256         }
257         return InstanceIdentifier.create(Iterables.limit(binding.getPathArguments(), foundPosition));
258     }
259
260
261
262     private org.opendaylight.yangtools.yang.data.api.InstanceIdentifier toNormalizedImpl(
263             final InstanceIdentifier<? extends DataObject> binding) {
264         final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier legacyPath = bindingToLegacy
265                 .toDataDom(binding);
266         final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized = legacyToNormalized
267                 .toNormalized(legacyPath);
268         return normalized;
269     }
270
271     @SuppressWarnings("unchecked")
272     private Iterable<Class<? extends DataObject>> getAugmentationChildren(final Class<?> targetType) {
273         List<Class<? extends DataObject>> ret = new LinkedList<>();
274         for (Method method : targetType.getMethods()) {
275             Class<?> entity = getYangModeledType(method);
276             if (entity != null) {
277                 ret.add((Class<? extends DataObject>) entity);
278             }
279         }
280         return ret;
281     }
282
283     @SuppressWarnings({ "rawtypes", "unchecked" })
284     private Class<? extends DataObject> getYangModeledType(final Method method) {
285         if (method.getName().equals("getClass") || !method.getName().startsWith("get")
286                 || method.getParameterTypes().length > 0) {
287             return null;
288         }
289
290         Class<?> returnType = method.getReturnType();
291         if (DataContainer.class.isAssignableFrom(returnType)) {
292             return (Class) returnType;
293         } else if (List.class.isAssignableFrom(returnType)) {
294             try {
295                 return ClassLoaderUtils.withClassLoader(method.getDeclaringClass().getClassLoader(),
296                         new Callable<Class>() {
297
298                             @SuppressWarnings("rawtypes")
299                             @Override
300                             public Class call() throws Exception {
301                                 Type listResult = ClassLoaderUtils.getFirstGenericParameter(method
302                                         .getGenericReturnType());
303                                 if (listResult instanceof Class
304                                         && DataObject.class.isAssignableFrom((Class) listResult)) {
305                                     return (Class<?>) listResult;
306                                 }
307                                 return null;
308                             }
309
310                         });
311             } catch (Exception e) {
312                 LOG.debug("Could not get YANG modeled entity for {}", method, e);
313                 return null;
314             }
315
316         }
317         return null;
318     }
319
320     @SuppressWarnings({ "unchecked", "rawtypes" })
321     private static InstanceIdentifier<?> toWildcarded(final InstanceIdentifier<?> orig) {
322         List<org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument> wildArgs = new LinkedList<>();
323         for (org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument arg : orig.getPathArguments()) {
324             wildArgs.add(new Item(arg.getType()));
325         }
326         return InstanceIdentifier.create(wildArgs);
327     }
328
329
330     private static boolean isAugmentation(final Class<? extends DataObject> type) {
331         return Augmentation.class.isAssignableFrom(type);
332     }
333
334     private static boolean isAugmentationIdentifier(final InstanceIdentifier<?> path) {
335         return Augmentation.class.isAssignableFrom(path.getTargetType());
336     }
337
338     private boolean isAugmentationIdentifier(final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier processed) {
339         return Iterables.getLast(processed.getPath()) instanceof AugmentationIdentifier;
340     }
341 }