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