846f949375dabc21a112e0dc9c1268e6b04148c4
[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.DataNormalizationOperation;
20 import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizer;
21 import org.opendaylight.yangtools.concepts.util.ClassLoaderUtils;
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.common.QName;
29 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
30 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.AugmentationIdentifier;
31 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifier;
32 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
33 import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
34 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
35 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
36 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
37 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
38 import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService;
39 import org.opendaylight.yangtools.yang.data.impl.codec.DeserializationException;
40 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
41 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
42 import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
45
46 import com.google.common.base.Optional;
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         for (QName child : lastArgument.getPossibleChildNames()) {
153             org.opendaylight.yangtools.yang.data.api.InstanceIdentifier childPath = new org.opendaylight.yangtools.yang.data.api.InstanceIdentifier(
154                     ImmutableList.<PathArgument> builder().addAll(normalized.getPath()).add(new NodeIdentifier(child))
155                             .build());
156             try {
157                 if (!isNotRepresentable(childPath)) {
158                     InstanceIdentifier<? extends DataObject> potentialPath = shortenToLastAugment(toBindingImpl(
159                             childPath).get());
160                     int potentialAugmentCount = getAugmentationCount(potentialPath);
161                     if(potentialAugmentCount == normalizedCount) {
162                         return Optional.<InstanceIdentifier<? extends DataObject>> of(potentialPath);
163                     }
164                 }
165             } catch (Exception e) {
166                 LOG.trace("Unable to deserialize aug. child path for {}", childPath, e);
167             }
168         }
169         return Optional.absent();
170     }
171
172     private Optional<InstanceIdentifier<? extends DataObject>> toBindingImpl(
173             final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized)
174             throws DeserializationException {
175         org.opendaylight.yangtools.yang.data.api.InstanceIdentifier legacyPath;
176
177         try {
178             if (isNotRepresentable(normalized)) {
179                 return Optional.absent();
180             }
181             legacyPath = legacyToNormalized.toLegacy(normalized);
182         } catch (DataNormalizationException e) {
183             throw new IllegalStateException("Could not denormalize path.", e);
184         }
185         LOG.trace("InstanceIdentifier Path Deserialization: Legacy representation {}, Normalized representation: {}",
186                 legacyPath, normalized);
187         return Optional.<InstanceIdentifier<? extends DataObject>> of(bindingToLegacy.fromDataDom(legacyPath));
188     }
189
190     private boolean isNotRepresentable(final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized)
191             throws DataNormalizationException {
192         DataNormalizationOperation<?> op = findNormalizationOperation(normalized);
193         if( op.isMixin() && op.getIdentifier() instanceof NodeIdentifier) {
194             return true;
195         }
196         if(op.isLeaf()) {
197             return true;
198         }
199         return false;
200     }
201
202     private DataNormalizationOperation<?> findNormalizationOperation(
203             final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized)
204             throws DataNormalizationException {
205         DataNormalizationOperation<?> current = legacyToNormalized.getRootOperation();
206         for (PathArgument arg : normalized.getPath()) {
207             current = current.getChild(arg);
208         }
209         return current;
210     }
211
212     private static final Entry<org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject>, DataObject> toEntry(
213             final org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject> key,
214             final DataObject value) {
215         return new SimpleEntry<org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject>, DataObject>(
216                 key, value);
217     }
218
219     public DataObject toBinding(final InstanceIdentifier<?> path, final NormalizedNode<?, ?> normalizedNode)
220             throws DeserializationException {
221         CompositeNode legacy = null;
222         if (isAugmentationIdentifier(path) && normalizedNode instanceof AugmentationNode) {
223             QName augIdentifier = BindingReflections.findQName(path.getTargetType());
224             ContainerNode virtualNode = Builders.containerBuilder() //
225                     .withNodeIdentifier(new NodeIdentifier(augIdentifier)) //
226                     .withChild((DataContainerChild<?, ?>) normalizedNode) //
227                     .build();
228             legacy = (CompositeNode) DataNormalizer.toLegacy(virtualNode);
229         } else {
230             legacy = (CompositeNode) DataNormalizer.toLegacy(normalizedNode);
231         }
232
233         return bindingToLegacy.dataObjectFromDataDom(path, legacy);
234     }
235
236     public DataNormalizer getDataNormalizer() {
237         return legacyToNormalized;
238     }
239
240     public Optional<Entry<org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject>, DataObject>> toBinding(
241             final Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, ? extends NormalizedNode<?, ?>> normalized)
242             throws DeserializationException {
243         Optional<InstanceIdentifier<? extends DataObject>> potentialPath = toBinding(normalized.getKey());
244         if (potentialPath.isPresent()) {
245             InstanceIdentifier<? extends DataObject> bindingPath = potentialPath.get();
246             DataObject bindingData = toBinding(bindingPath, normalized.getValue());
247             if (bindingData == null) {
248                 LOG.warn("Failed to deserialize {} to Binding format. Binding path is: {}", normalized, bindingPath);
249             }
250             return Optional.of(toEntry(bindingPath, bindingData));
251         } else {
252             return Optional.absent();
253         }
254     }
255
256     @Override
257     public void onGlobalContextUpdated(final SchemaContext arg0) {
258         legacyToNormalized = new DataNormalizer(arg0);
259     }
260
261     private org.opendaylight.yangtools.yang.data.api.InstanceIdentifier toNormalizedAugmented(
262             final InstanceIdentifier<?> augPath) {
263         org.opendaylight.yangtools.yang.data.api.InstanceIdentifier processed = toNormalizedImpl(augPath);
264         // If used instance identifier codec added supports for deserialization
265         // of last AugmentationIdentifier we will just reuse it
266         if (isAugmentationIdentifier(processed)) {
267             return processed;
268         }
269         // Here we employ small trick - DataNormalizer injects augmentation
270         // identifier if child is
271         // also part of the path (since using a child we can safely identify
272         // augmentation)
273         // so, we scan augmentation for children add it to path
274         // and use original algorithm, then shorten it to last augmentation
275         for (@SuppressWarnings("rawtypes")
276         Class augChild : getAugmentationChildren(augPath.getTargetType())) {
277             @SuppressWarnings("unchecked")
278             InstanceIdentifier<?> childPath = augPath.child(augChild);
279             org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized = toNormalizedImpl(childPath);
280             org.opendaylight.yangtools.yang.data.api.InstanceIdentifier potentialDiscovered = shortenToLastAugmentation(normalized);
281             if (potentialDiscovered != null) {
282                 return potentialDiscovered;
283             }
284         }
285         return processed;
286     }
287
288     private org.opendaylight.yangtools.yang.data.api.InstanceIdentifier shortenToLastAugmentation(
289             final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized) {
290         int position = 0;
291         int foundPosition = -1;
292         for (PathArgument arg : normalized.getPath()) {
293             position++;
294             if (arg instanceof AugmentationIdentifier) {
295                 foundPosition = position;
296             }
297         }
298         if (foundPosition > 0) {
299             return new org.opendaylight.yangtools.yang.data.api.InstanceIdentifier(normalized.getPath().subList(0,
300                     foundPosition));
301         }
302         return null;
303     }
304
305     private InstanceIdentifier<? extends DataObject> shortenToLastAugment(
306             final InstanceIdentifier<? extends DataObject> binding) {
307         int position = 0;
308         int foundPosition = -1;
309         for (org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument arg : binding.getPathArguments()) {
310             position++;
311             if (isAugmentation(arg.getType())) {
312                 foundPosition = position;
313             }
314         }
315         return InstanceIdentifier.create(Iterables.limit(binding.getPathArguments(), foundPosition));
316     }
317
318     private org.opendaylight.yangtools.yang.data.api.InstanceIdentifier toNormalizedImpl(
319             final InstanceIdentifier<? extends DataObject> binding) {
320         final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier legacyPath = bindingToLegacy
321                 .toDataDom(binding);
322         final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized = legacyToNormalized
323                 .toNormalized(legacyPath);
324         return normalized;
325     }
326
327     @SuppressWarnings("unchecked")
328     private Iterable<Class<? extends DataObject>> getAugmentationChildren(final Class<?> targetType) {
329         List<Class<? extends DataObject>> ret = new LinkedList<>();
330         for (Method method : targetType.getMethods()) {
331             Class<?> entity = getYangModeledType(method);
332             if (entity != null) {
333                 ret.add((Class<? extends DataObject>) entity);
334             }
335         }
336         return ret;
337     }
338
339     @SuppressWarnings({ "rawtypes", "unchecked" })
340     private Class<? extends DataObject> getYangModeledType(final Method method) {
341         if (method.getName().equals("getClass") || !method.getName().startsWith("get")
342                 || method.getParameterTypes().length > 0) {
343             return null;
344         }
345
346         Class<?> returnType = method.getReturnType();
347         if (DataContainer.class.isAssignableFrom(returnType)) {
348             return (Class) returnType;
349         } else if (List.class.isAssignableFrom(returnType)) {
350             try {
351                 return ClassLoaderUtils.withClassLoader(method.getDeclaringClass().getClassLoader(),
352                         new Callable<Class>() {
353
354                             @SuppressWarnings("rawtypes")
355                             @Override
356                             public Class call() throws Exception {
357                                 Type listResult = ClassLoaderUtils.getFirstGenericParameter(method
358                                         .getGenericReturnType());
359                                 if (listResult instanceof Class
360                                         && DataObject.class.isAssignableFrom((Class) listResult)) {
361                                     return (Class<?>) listResult;
362                                 }
363                                 return null;
364                             }
365
366                         });
367             } catch (Exception e) {
368                 LOG.debug("Could not get YANG modeled entity for {}", method, e);
369                 return null;
370             }
371
372         }
373         return null;
374     }
375
376     @SuppressWarnings({ "unchecked", "rawtypes" })
377     private static InstanceIdentifier<?> toWildcarded(final InstanceIdentifier<?> orig) {
378         List<org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument> wildArgs = new LinkedList<>();
379         for (org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument arg : orig.getPathArguments()) {
380             wildArgs.add(new Item(arg.getType()));
381         }
382         return InstanceIdentifier.create(wildArgs);
383     }
384
385     private static boolean isAugmentation(final Class<? extends DataObject> type) {
386         return Augmentation.class.isAssignableFrom(type);
387     }
388
389     private static boolean isAugmentationIdentifier(final InstanceIdentifier<?> potential) {
390         return Augmentation.class.isAssignableFrom(potential.getTargetType());
391     }
392
393     private boolean isAugmentationIdentifier(final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier processed) {
394         return Iterables.getLast(processed.getPath()) instanceof AugmentationIdentifier;
395     }
396
397     private static int getAugmentationCount(final InstanceIdentifier<?> potential) {
398         int count = 0;
399         for(org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument arg : potential.getPathArguments()) {
400             if(isAugmentation(arg.getType())) {
401                 count++;
402             }
403
404         }
405         return count;
406     }
407
408     private static int getAugmentationCount(final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier potential) {
409         int count = 0;
410         for(PathArgument arg : potential.getPath()) {
411             if(arg instanceof AugmentationIdentifier) {
412                 count++;
413             }
414         }
415         return count;
416     }
417 }