2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.controller.md.sal.binding.impl;
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;
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;
46 import com.google.common.base.Optional;
47 import com.google.common.collect.ImmutableList;
48 import com.google.common.collect.Iterables;
50 public class BindingToNormalizedNodeCodec implements SchemaContextListener {
52 private static final Logger LOG = LoggerFactory.getLogger(BindingToNormalizedNodeCodec.class);
54 private final BindingIndependentMappingService bindingToLegacy;
55 private DataNormalizer legacyToNormalized;
57 public BindingToNormalizedNodeCodec(final BindingIndependentMappingService mappingService) {
59 this.bindingToLegacy = mappingService;
62 public org.opendaylight.yangtools.yang.data.api.InstanceIdentifier toNormalized(
63 final InstanceIdentifier<? extends DataObject> binding) {
65 // Used instance-identifier codec do not support serialization of last
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);
73 return toNormalizedImpl(binding);
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));
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
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())) {
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(
99 return new SimpleEntry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>>(
105 return normalizedEntry;
111 * Returns a Binding-Aware instance identifier from normalized
112 * instance-identifier if it is possible to create representation.
114 * Returns Optional.absent for cases where target is mixin node except
118 public Optional<InstanceIdentifier<? extends DataObject>> toBinding(
119 final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized)
120 throws DeserializationException {
122 PathArgument lastArgument = Iterables.getLast(normalized.getPath());
123 // Used instance-identifier codec do not support serialization of last
125 // argument if it is AugmentationIdentifier (behaviour expected by old
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);
132 return toBindingImpl(normalized);
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())) {
145 int normalizedCount = getAugmentationCount(normalized);
146 AugmentationIdentifier lastArgument = (AugmentationIdentifier) Iterables.getLast(normalized.getPath());
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
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))
157 if (!isNotRepresentable(childPath)) {
158 InstanceIdentifier<? extends DataObject> potentialPath = shortenToLastAugment(toBindingImpl(
160 int potentialAugmentCount = getAugmentationCount(potentialPath);
161 if(potentialAugmentCount == normalizedCount) {
162 return Optional.<InstanceIdentifier<? extends DataObject>> of(potentialPath);
165 } catch (Exception e) {
166 LOG.trace("Unable to deserialize aug. child path for {}", childPath, e);
169 return Optional.absent();
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;
178 if (isNotRepresentable(normalized)) {
179 return Optional.absent();
181 legacyPath = legacyToNormalized.toLegacy(normalized);
182 } catch (DataNormalizationException e) {
183 throw new IllegalStateException("Could not denormalize path.", e);
185 LOG.trace("InstanceIdentifier Path Deserialization: Legacy representation {}, Normalized representation: {}",
186 legacyPath, normalized);
187 return Optional.<InstanceIdentifier<? extends DataObject>> of(bindingToLegacy.fromDataDom(legacyPath));
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) {
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);
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>(
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) //
228 legacy = (CompositeNode) DataNormalizer.toLegacy(virtualNode);
230 legacy = (CompositeNode) DataNormalizer.toLegacy(normalizedNode);
233 return bindingToLegacy.dataObjectFromDataDom(path, legacy);
236 public DataNormalizer getDataNormalizer() {
237 return legacyToNormalized;
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);
250 return Optional.of(toEntry(bindingPath, bindingData));
252 return Optional.absent();
257 public void onGlobalContextUpdated(final SchemaContext arg0) {
258 legacyToNormalized = new DataNormalizer(arg0);
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)) {
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
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;
288 private org.opendaylight.yangtools.yang.data.api.InstanceIdentifier shortenToLastAugmentation(
289 final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized) {
291 int foundPosition = -1;
292 for (PathArgument arg : normalized.getPath()) {
294 if (arg instanceof AugmentationIdentifier) {
295 foundPosition = position;
298 if (foundPosition > 0) {
299 return new org.opendaylight.yangtools.yang.data.api.InstanceIdentifier(normalized.getPath().subList(0,
305 private InstanceIdentifier<? extends DataObject> shortenToLastAugment(
306 final InstanceIdentifier<? extends DataObject> binding) {
308 int foundPosition = -1;
309 for (org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument arg : binding.getPathArguments()) {
311 if (isAugmentation(arg.getType())) {
312 foundPosition = position;
315 return InstanceIdentifier.create(Iterables.limit(binding.getPathArguments(), foundPosition));
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
322 final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized = legacyToNormalized
323 .toNormalized(legacyPath);
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);
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) {
346 Class<?> returnType = method.getReturnType();
347 if (DataContainer.class.isAssignableFrom(returnType)) {
348 return (Class) returnType;
349 } else if (List.class.isAssignableFrom(returnType)) {
351 return ClassLoaderUtils.withClassLoader(method.getDeclaringClass().getClassLoader(),
352 new Callable<Class>() {
354 @SuppressWarnings("rawtypes")
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;
367 } catch (Exception e) {
368 LOG.debug("Could not get YANG modeled entity for {}", method, e);
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()));
382 return InstanceIdentifier.create(wildArgs);
385 private static boolean isAugmentation(final Class<? extends DataObject> type) {
386 return Augmentation.class.isAssignableFrom(type);
389 private static boolean isAugmentationIdentifier(final InstanceIdentifier<?> potential) {
390 return Augmentation.class.isAssignableFrom(potential.getTargetType());
393 private boolean isAugmentationIdentifier(final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier processed) {
394 return Iterables.getLast(processed.getPath()) instanceof AugmentationIdentifier;
397 private static int getAugmentationCount(final InstanceIdentifier<?> potential) {
399 for(org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument arg : potential.getPathArguments()) {
400 if(isAugmentation(arg.getType())) {
408 private static int getAugmentationCount(final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier potential) {
410 for(PathArgument arg : potential.getPath()) {
411 if(arg instanceof AugmentationIdentifier) {