Merge "Cosmetics: check in pom.xml files as _sort_pom_ wants them to be"
[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
17 import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationException;
18 import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationOperation;
19 import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizer;
20 import org.opendaylight.yangtools.yang.binding.Augmentation;
21 import org.opendaylight.yangtools.yang.binding.DataContainer;
22 import org.opendaylight.yangtools.yang.binding.DataObject;
23 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
24 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.Item;
25 import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
26 import org.opendaylight.yangtools.yang.binding.util.ClassLoaderUtils;
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.base.Optional;
46 import com.google.common.base.Supplier;
47 import com.google.common.collect.ImmutableList;
48 import com.google.common.collect.Iterables;
49
50 public class BindingToNormalizedNodeCodec implements SchemaContextListener {
51
52     private static final Logger LOG = LoggerFactory.getLogger(BindingToNormalizedNodeCodec.class);
53
54     private final BindingIndependentMappingService bindingToLegacy;
55     private DataNormalizer legacyToNormalized;
56
57     public BindingToNormalizedNodeCodec(final BindingIndependentMappingService mappingService) {
58         super();
59         this.bindingToLegacy = mappingService;
60     }
61
62     public org.opendaylight.yangtools.yang.data.api.InstanceIdentifier toNormalized(
63             final InstanceIdentifier<? extends DataObject> binding) {
64
65         // Used instance-identifier codec do not support serialization of last
66         // path
67         // argument if it is Augmentation (behaviour expected by old datastore)
68         // in this case, we explicitly check if last argument is augmentation
69         // to process it separately
70         if (isAugmentationIdentifier(binding)) {
71             return toNormalizedAugmented(binding);
72         }
73         return toNormalizedImpl(binding);
74     }
75
76     public Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>> toNormalizedNode(
77             final InstanceIdentifier<? extends DataObject> bindingPath, final DataObject bindingObject) {
78         return toNormalizedNode(toEntry(bindingPath, bindingObject));
79
80     }
81
82     public Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>> toNormalizedNode(
83             final Entry<org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject>, DataObject> binding) {
84         Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> legacyEntry = bindingToLegacy
85                 .toDataDom(binding);
86         Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>> normalizedEntry = legacyToNormalized
87                 .toNormalized(legacyEntry);
88         LOG.trace("Serialization of {}, Legacy Representation: {}, Normalized Representation: {}", binding,
89                 legacyEntry, normalizedEntry);
90         if (Augmentation.class.isAssignableFrom(binding.getKey().getTargetType())) {
91
92             for (DataContainerChild<? extends PathArgument, ?> child : ((DataContainerNode<?>) normalizedEntry
93                     .getValue()).getValue()) {
94                 if (child instanceof AugmentationNode) {
95                     ImmutableList<PathArgument> childArgs = ImmutableList.<PathArgument> builder()
96                             .addAll(normalizedEntry.getKey().getPath()).add(child.getIdentifier()).build();
97                     org.opendaylight.yangtools.yang.data.api.InstanceIdentifier childPath = new org.opendaylight.yangtools.yang.data.api.InstanceIdentifier(
98                             childArgs);
99                     return new SimpleEntry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>>(
100                             childPath, child);
101                 }
102             }
103
104         }
105         return normalizedEntry;
106
107     }
108
109     /**
110      *
111      * Returns a Binding-Aware instance identifier from normalized
112      * instance-identifier if it is possible to create representation.
113      *
114      * Returns Optional.absent for cases where target is mixin node except
115      * augmentation.
116      *
117      */
118     public Optional<InstanceIdentifier<? extends DataObject>> toBinding(
119             final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized)
120             throws DeserializationException {
121
122         PathArgument lastArgument = Iterables.getLast(normalized.getPath());
123         // Used instance-identifier codec do not support serialization of last
124         // path
125         // argument if it is AugmentationIdentifier (behaviour expected by old
126         // datastore)
127         // in this case, we explicitly check if last argument is augmentation
128         // to process it separately
129         if (lastArgument instanceof AugmentationIdentifier) {
130             return toBindingAugmented(normalized);
131         }
132         return toBindingImpl(normalized);
133     }
134
135     private Optional<InstanceIdentifier<? extends DataObject>> toBindingAugmented(
136             final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized)
137             throws DeserializationException {
138         Optional<InstanceIdentifier<? extends DataObject>> potential = toBindingImpl(normalized);
139         // Shorthand check, if codec already supports deserialization
140         // of AugmentationIdentifier we will return
141         if (potential.isPresent() && isAugmentationIdentifier(potential.get())) {
142             return potential;
143         }
144
145         int normalizedCount = getAugmentationCount(normalized);
146         AugmentationIdentifier lastArgument = (AugmentationIdentifier) Iterables.getLast(normalized.getPath());
147
148         // Here we employ small trick - Binding-aware Codec injects an pointer
149         // to augmentation class
150         // if child is referenced - so we will reference child and then shorten
151         // path.
152         LOG.trace("Looking for candidates to match {}", normalized);
153         for (QName child : lastArgument.getPossibleChildNames()) {
154             org.opendaylight.yangtools.yang.data.api.InstanceIdentifier childPath = new org.opendaylight.yangtools.yang.data.api.InstanceIdentifier(
155                     ImmutableList.<PathArgument> builder().addAll(normalized.getPath()).add(new NodeIdentifier(child))
156                             .build());
157             try {
158                 if (isNotRepresentable(childPath)) {
159                     LOG.trace("Path {} is not BI-representable, skipping it", childPath);
160                     continue;
161                 }
162             } catch (DataNormalizationException e) {
163                 LOG.warn("Failed to denormalize path {}, skipping it", childPath, e);
164                 continue;
165             }
166
167             Optional<InstanceIdentifier<? extends DataObject>> baId = toBindingImpl(childPath);
168             if (!baId.isPresent()) {
169                 LOG.debug("No binding-aware identifier found for path {}, skipping it", childPath);
170                 continue;
171             }
172
173             InstanceIdentifier<? extends DataObject> potentialPath = shortenToLastAugment(baId.get());
174             int potentialAugmentCount = getAugmentationCount(potentialPath);
175             if (potentialAugmentCount == normalizedCount) {
176                 LOG.trace("Found matching path {}", potentialPath);
177                 return Optional.<InstanceIdentifier<? extends DataObject>> of(potentialPath);
178             }
179
180             LOG.trace("Skipping mis-matched potential path {}", potentialPath);
181         }
182
183         LOG.trace("Failed to find augmentation matching {}", normalized);
184         return Optional.absent();
185     }
186
187     private Optional<InstanceIdentifier<? extends DataObject>> toBindingImpl(
188             final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized)
189             throws DeserializationException {
190         org.opendaylight.yangtools.yang.data.api.InstanceIdentifier legacyPath;
191
192         try {
193             if (isNotRepresentable(normalized)) {
194                 return Optional.absent();
195             }
196             legacyPath = legacyToNormalized.toLegacy(normalized);
197         } catch (DataNormalizationException e) {
198             throw new IllegalStateException("Could not denormalize path.", e);
199         }
200         LOG.trace("InstanceIdentifier Path Deserialization: Legacy representation {}, Normalized representation: {}",
201                 legacyPath, normalized);
202         return Optional.<InstanceIdentifier<? extends DataObject>> of(bindingToLegacy.fromDataDom(legacyPath));
203     }
204
205     private boolean isNotRepresentable(final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized)
206             throws DataNormalizationException {
207         DataNormalizationOperation<?> op = findNormalizationOperation(normalized);
208         if( op.isMixin() && op.getIdentifier() instanceof NodeIdentifier) {
209             return true;
210         }
211         if(op.isLeaf()) {
212             return true;
213         }
214         return false;
215     }
216
217     private DataNormalizationOperation<?> findNormalizationOperation(
218             final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized)
219             throws DataNormalizationException {
220         DataNormalizationOperation<?> current = legacyToNormalized.getRootOperation();
221         for (PathArgument arg : normalized.getPath()) {
222             current = current.getChild(arg);
223         }
224         return current;
225     }
226
227     private static final Entry<org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject>, DataObject> toEntry(
228             final org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject> key,
229             final DataObject value) {
230         return new SimpleEntry<org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject>, DataObject>(
231                 key, value);
232     }
233
234     public DataObject toBinding(final InstanceIdentifier<?> path, final NormalizedNode<?, ?> normalizedNode)
235             throws DeserializationException {
236         CompositeNode legacy = null;
237         if (isAugmentationIdentifier(path) && normalizedNode instanceof AugmentationNode) {
238             QName augIdentifier = BindingReflections.findQName(path.getTargetType());
239             ContainerNode virtualNode = Builders.containerBuilder() //
240                     .withNodeIdentifier(new NodeIdentifier(augIdentifier)) //
241                     .withChild((DataContainerChild<?, ?>) normalizedNode) //
242                     .build();
243             legacy = (CompositeNode) DataNormalizer.toLegacy(virtualNode);
244         } else {
245             legacy = (CompositeNode) DataNormalizer.toLegacy(normalizedNode);
246         }
247
248         return bindingToLegacy.dataObjectFromDataDom(path, legacy);
249     }
250
251     public DataNormalizer getDataNormalizer() {
252         return legacyToNormalized;
253     }
254
255     public Optional<Entry<org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject>, DataObject>> toBinding(
256             final Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, ? extends NormalizedNode<?, ?>> normalized)
257             throws DeserializationException {
258         Optional<InstanceIdentifier<? extends DataObject>> potentialPath = toBinding(normalized.getKey());
259         if (potentialPath.isPresent()) {
260             InstanceIdentifier<? extends DataObject> bindingPath = potentialPath.get();
261             DataObject bindingData = toBinding(bindingPath, normalized.getValue());
262             if (bindingData == null) {
263                 LOG.warn("Failed to deserialize {} to Binding format. Binding path is: {}", normalized, bindingPath);
264             }
265             return Optional.of(toEntry(bindingPath, bindingData));
266         } else {
267             return Optional.absent();
268         }
269     }
270
271     @Override
272     public void onGlobalContextUpdated(final SchemaContext arg0) {
273         legacyToNormalized = new DataNormalizer(arg0);
274     }
275
276     private org.opendaylight.yangtools.yang.data.api.InstanceIdentifier toNormalizedAugmented(
277             final InstanceIdentifier<?> augPath) {
278         org.opendaylight.yangtools.yang.data.api.InstanceIdentifier processed = toNormalizedImpl(augPath);
279         // If used instance identifier codec added supports for deserialization
280         // of last AugmentationIdentifier we will just reuse it
281         if (isAugmentationIdentifier(processed)) {
282             return processed;
283         }
284         // Here we employ small trick - DataNormalizer injects augmentation
285         // identifier if child is
286         // also part of the path (since using a child we can safely identify
287         // augmentation)
288         // so, we scan augmentation for children add it to path
289         // and use original algorithm, then shorten it to last augmentation
290         for (@SuppressWarnings("rawtypes")
291         Class augChild : getAugmentationChildren(augPath.getTargetType())) {
292             @SuppressWarnings("unchecked")
293             InstanceIdentifier<?> childPath = augPath.child(augChild);
294             org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized = toNormalizedImpl(childPath);
295             org.opendaylight.yangtools.yang.data.api.InstanceIdentifier potentialDiscovered = shortenToLastAugmentation(normalized);
296             if (potentialDiscovered != null) {
297                 return potentialDiscovered;
298             }
299         }
300         return processed;
301     }
302
303     private org.opendaylight.yangtools.yang.data.api.InstanceIdentifier shortenToLastAugmentation(
304             final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized) {
305         int position = 0;
306         int foundPosition = -1;
307         for (PathArgument arg : normalized.getPath()) {
308             position++;
309             if (arg instanceof AugmentationIdentifier) {
310                 foundPosition = position;
311             }
312         }
313         if (foundPosition > 0) {
314             return new org.opendaylight.yangtools.yang.data.api.InstanceIdentifier(normalized.getPath().subList(0,
315                     foundPosition));
316         }
317         return null;
318     }
319
320     private InstanceIdentifier<? extends DataObject> shortenToLastAugment(
321             final InstanceIdentifier<? extends DataObject> binding) {
322         int position = 0;
323         int foundPosition = -1;
324         for (org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument arg : binding.getPathArguments()) {
325             position++;
326             if (isAugmentation(arg.getType())) {
327                 foundPosition = position;
328             }
329         }
330         return InstanceIdentifier.create(Iterables.limit(binding.getPathArguments(), foundPosition));
331     }
332
333     private org.opendaylight.yangtools.yang.data.api.InstanceIdentifier toNormalizedImpl(
334             final InstanceIdentifier<? extends DataObject> binding) {
335         final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier legacyPath = bindingToLegacy
336                 .toDataDom(binding);
337         final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized = legacyToNormalized
338                 .toNormalized(legacyPath);
339         return normalized;
340     }
341
342     @SuppressWarnings("unchecked")
343     private Iterable<Class<? extends DataObject>> getAugmentationChildren(final Class<?> targetType) {
344         List<Class<? extends DataObject>> ret = new LinkedList<>();
345         for (Method method : targetType.getMethods()) {
346             Class<?> entity = getYangModeledType(method);
347             if (entity != null) {
348                 ret.add((Class<? extends DataObject>) entity);
349             }
350         }
351         return ret;
352     }
353
354     @SuppressWarnings({ "rawtypes", "unchecked" })
355     private Class<? extends DataObject> getYangModeledType(final Method method) {
356         if (method.getName().equals("getClass") || !method.getName().startsWith("get")
357                 || method.getParameterTypes().length > 0) {
358             return null;
359         }
360
361         Class<?> returnType = method.getReturnType();
362         if (DataContainer.class.isAssignableFrom(returnType)) {
363             return (Class) returnType;
364         } else if (List.class.isAssignableFrom(returnType)) {
365             try {
366                 return ClassLoaderUtils.withClassLoader(method.getDeclaringClass().getClassLoader(),
367                         new Supplier<Class>() {
368                             @Override
369                             public Class get() {
370                                 Type listResult = ClassLoaderUtils.getFirstGenericParameter(method
371                                         .getGenericReturnType());
372                                 if (listResult instanceof Class
373                                         && DataObject.class.isAssignableFrom((Class) listResult)) {
374                                     return (Class<?>) listResult;
375                                 }
376                                 return null;
377                             }
378
379                         });
380             } catch (Exception e) {
381                 LOG.debug("Could not get YANG modeled entity for {}", method, e);
382                 return null;
383             }
384
385         }
386         return null;
387     }
388
389     @SuppressWarnings({ "unchecked", "rawtypes" })
390     private static InstanceIdentifier<?> toWildcarded(final InstanceIdentifier<?> orig) {
391         List<org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument> wildArgs = new LinkedList<>();
392         for (org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument arg : orig.getPathArguments()) {
393             wildArgs.add(new Item(arg.getType()));
394         }
395         return InstanceIdentifier.create(wildArgs);
396     }
397
398     private static boolean isAugmentation(final Class<? extends DataObject> type) {
399         return Augmentation.class.isAssignableFrom(type);
400     }
401
402     private static boolean isAugmentationIdentifier(final InstanceIdentifier<?> potential) {
403         return Augmentation.class.isAssignableFrom(potential.getTargetType());
404     }
405
406     private boolean isAugmentationIdentifier(final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier processed) {
407         return Iterables.getLast(processed.getPath()) instanceof AugmentationIdentifier;
408     }
409
410     private static int getAugmentationCount(final InstanceIdentifier<?> potential) {
411         int count = 0;
412         for(org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument arg : potential.getPathArguments()) {
413             if(isAugmentation(arg.getType())) {
414                 count++;
415             }
416
417         }
418         return count;
419     }
420
421     private static int getAugmentationCount(final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier potential) {
422         int count = 0;
423         for(PathArgument arg : potential.getPath()) {
424             if(arg instanceof AugmentationIdentifier) {
425                 count++;
426             }
427         }
428         return count;
429     }
430 }