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.Iterator;
14 import java.util.LinkedList;
15 import java.util.List;
16 import java.util.Map.Entry;
18 import javax.annotation.Nullable;
20 import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationException;
21 import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationOperation;
22 import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizer;
23 import org.opendaylight.yangtools.yang.binding.Augmentation;
24 import org.opendaylight.yangtools.yang.binding.DataContainer;
25 import org.opendaylight.yangtools.yang.binding.DataObject;
26 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
27 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.Item;
28 import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
29 import org.opendaylight.yangtools.yang.binding.util.ClassLoaderUtils;
30 import org.opendaylight.yangtools.yang.common.QName;
31 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
32 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.AugmentationIdentifier;
33 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifier;
34 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
35 import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
36 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
37 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
38 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
39 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
40 import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService;
41 import org.opendaylight.yangtools.yang.data.impl.codec.DeserializationException;
42 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
43 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
44 import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
48 import com.google.common.base.Function;
49 import com.google.common.base.Optional;
50 import com.google.common.base.Preconditions;
51 import com.google.common.base.Supplier;
52 import com.google.common.collect.ImmutableList;
53 import com.google.common.collect.Iterables;
55 public class BindingToNormalizedNodeCodec implements SchemaContextListener {
57 private static final Logger LOG = LoggerFactory.getLogger(BindingToNormalizedNodeCodec.class);
59 private final BindingIndependentMappingService bindingToLegacy;
60 private DataNormalizer legacyToNormalized;
62 public BindingToNormalizedNodeCodec(final BindingIndependentMappingService mappingService) {
64 this.bindingToLegacy = mappingService;
67 public org.opendaylight.yangtools.yang.data.api.InstanceIdentifier toNormalized(
68 final InstanceIdentifier<? extends DataObject> binding) {
70 // Used instance-identifier codec do not support serialization of last
72 // argument if it is Augmentation (behaviour expected by old datastore)
73 // in this case, we explicitly check if last argument is augmentation
74 // to process it separately
75 if (isAugmentationIdentifier(binding)) {
76 return toNormalizedAugmented(binding);
78 return toNormalizedImpl(binding);
81 public Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>> toNormalizedNode(
82 final InstanceIdentifier<? extends DataObject> bindingPath, final DataObject bindingObject) {
83 return toNormalizedNode(toBindingEntry(bindingPath, bindingObject));
87 public Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>> toNormalizedNode(
88 final Entry<org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject>, DataObject> binding) {
89 Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> legacyEntry = bindingToLegacy
91 Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>> normalizedEntry = legacyToNormalized
92 .toNormalized(legacyEntry);
93 LOG.trace("Serialization of {}, Legacy Representation: {}, Normalized Representation: {}", binding,
94 legacyEntry, normalizedEntry);
95 if (isAugmentation(binding.getKey().getTargetType())) {
97 for (DataContainerChild<? extends PathArgument, ?> child : ((DataContainerNode<?>) normalizedEntry
98 .getValue()).getValue()) {
99 if (child instanceof AugmentationNode) {
100 ImmutableList<PathArgument> childArgs = ImmutableList.<PathArgument> builder()
101 .addAll(normalizedEntry.getKey().getPathArguments()).add(child.getIdentifier()).build();
102 org.opendaylight.yangtools.yang.data.api.InstanceIdentifier childPath = org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.create(
104 return toDOMEntry(childPath, child);
109 return normalizedEntry;
115 * Returns a Binding-Aware instance identifier from normalized
116 * instance-identifier if it is possible to create representation.
118 * Returns Optional.absent for cases where target is mixin node except
122 public Optional<InstanceIdentifier<? extends DataObject>> toBinding(
123 final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized)
124 throws DeserializationException {
126 PathArgument lastArgument = Iterables.getLast(normalized.getPathArguments());
127 // Used instance-identifier codec do not support serialization of last
129 // argument if it is AugmentationIdentifier (behaviour expected by old
131 // in this case, we explicitly check if last argument is augmentation
132 // to process it separately
133 if (lastArgument instanceof AugmentationIdentifier) {
134 return toBindingAugmented(normalized);
136 return toBindingImpl(normalized);
139 private Optional<InstanceIdentifier<? extends DataObject>> toBindingAugmented(
140 final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized)
141 throws DeserializationException {
142 Optional<InstanceIdentifier<? extends DataObject>> potential = toBindingImpl(normalized);
143 // Shorthand check, if codec already supports deserialization
144 // of AugmentationIdentifier we will return
145 if (potential.isPresent() && isAugmentationIdentifier(potential.get())) {
149 int normalizedCount = getAugmentationCount(normalized);
150 AugmentationIdentifier lastArgument = (AugmentationIdentifier) Iterables.getLast(normalized.getPathArguments());
152 // Here we employ small trick - Binding-aware Codec injects an pointer
153 // to augmentation class
154 // if child is referenced - so we will reference child and then shorten
156 LOG.trace("Looking for candidates to match {}", normalized);
157 for (QName child : lastArgument.getPossibleChildNames()) {
158 org.opendaylight.yangtools.yang.data.api.InstanceIdentifier childPath = normalized.node(child);
160 if (isNotRepresentable(childPath)) {
161 LOG.trace("Path {} is not BI-representable, skipping it", childPath);
164 } catch (DataNormalizationException e) {
165 LOG.warn("Failed to denormalize path {}, skipping it", childPath, e);
169 Optional<InstanceIdentifier<? extends DataObject>> baId = toBindingImpl(childPath);
170 if (!baId.isPresent()) {
171 LOG.debug("No binding-aware identifier found for path {}, skipping it", childPath);
175 InstanceIdentifier<? extends DataObject> potentialPath = shortenToLastAugment(baId.get());
176 int potentialAugmentCount = getAugmentationCount(potentialPath);
177 if (potentialAugmentCount == normalizedCount) {
178 LOG.trace("Found matching path {}", potentialPath);
179 return Optional.<InstanceIdentifier<? extends DataObject>> of(potentialPath);
182 LOG.trace("Skipping mis-matched potential path {}", potentialPath);
185 LOG.trace("Failed to find augmentation matching {}", normalized);
186 return Optional.absent();
189 private Optional<InstanceIdentifier<? extends DataObject>> toBindingImpl(
190 final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized)
191 throws DeserializationException {
192 org.opendaylight.yangtools.yang.data.api.InstanceIdentifier legacyPath;
195 if (isNotRepresentable(normalized)) {
196 return Optional.absent();
198 legacyPath = legacyToNormalized.toLegacy(normalized);
199 } catch (DataNormalizationException e) {
200 throw new IllegalStateException("Could not denormalize path.", e);
202 LOG.trace("InstanceIdentifier Path Deserialization: Legacy representation {}, Normalized representation: {}",
203 legacyPath, normalized);
204 return Optional.<InstanceIdentifier<? extends DataObject>> of(bindingToLegacy.fromDataDom(legacyPath));
207 private boolean isNotRepresentable(final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized)
208 throws DataNormalizationException {
209 DataNormalizationOperation<?> op = findNormalizationOperation(normalized);
210 if( op.isMixin() && op.getIdentifier() instanceof NodeIdentifier) {
219 private DataNormalizationOperation<?> findNormalizationOperation(
220 final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized)
221 throws DataNormalizationException {
222 DataNormalizationOperation<?> current = legacyToNormalized.getRootOperation();
223 for (PathArgument arg : normalized.getPathArguments()) {
224 current = current.getChild(arg);
229 private static final Entry<org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject>, DataObject> toBindingEntry(
230 final org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject> key,
231 final DataObject value) {
232 return new SimpleEntry<org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject>, DataObject>(
236 private static final Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>> toDOMEntry(
237 final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier key,
238 final NormalizedNode<?, ?> value) {
239 return new SimpleEntry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>>(
243 public DataObject toBinding(final InstanceIdentifier<?> path, final NormalizedNode<?, ?> normalizedNode)
244 throws DeserializationException {
245 CompositeNode legacy = null;
246 if (isAugmentationIdentifier(path) && normalizedNode instanceof AugmentationNode) {
247 QName augIdentifier = BindingReflections.findQName(path.getTargetType());
248 ContainerNode virtualNode = Builders.containerBuilder() //
249 .withNodeIdentifier(new NodeIdentifier(augIdentifier)) //
250 .withChild((DataContainerChild<?, ?>) normalizedNode) //
252 legacy = (CompositeNode) DataNormalizer.toLegacy(virtualNode);
254 legacy = (CompositeNode) DataNormalizer.toLegacy(normalizedNode);
257 return bindingToLegacy.dataObjectFromDataDom(path, legacy);
260 public DataNormalizer getDataNormalizer() {
261 return legacyToNormalized;
264 public Optional<Entry<org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject>, DataObject>> toBinding(
265 final Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, ? extends NormalizedNode<?, ?>> normalized)
266 throws DeserializationException {
267 Optional<InstanceIdentifier<? extends DataObject>> potentialPath = toBinding(normalized.getKey());
268 if (potentialPath.isPresent()) {
269 InstanceIdentifier<? extends DataObject> bindingPath = potentialPath.get();
270 DataObject bindingData = toBinding(bindingPath, normalized.getValue());
271 if (bindingData == null) {
272 LOG.warn("Failed to deserialize {} to Binding format. Binding path is: {}", normalized, bindingPath);
274 return Optional.of(toBindingEntry(bindingPath, bindingData));
276 return Optional.absent();
281 public void onGlobalContextUpdated(final SchemaContext arg0) {
282 legacyToNormalized = new DataNormalizer(arg0);
285 private org.opendaylight.yangtools.yang.data.api.InstanceIdentifier toNormalizedAugmented(
286 final InstanceIdentifier<?> augPath) {
287 org.opendaylight.yangtools.yang.data.api.InstanceIdentifier processed = toNormalizedImpl(augPath);
288 // If used instance identifier codec added supports for deserialization
289 // of last AugmentationIdentifier we will just reuse it
290 if (isAugmentationIdentifier(processed)) {
293 // Here we employ small trick - DataNormalizer injects augmentation
294 // identifier if child is
295 // also part of the path (since using a child we can safely identify
297 // so, we scan augmentation for children add it to path
298 // and use original algorithm, then shorten it to last augmentation
299 for (@SuppressWarnings("rawtypes")
300 Class augChild : getAugmentationChildren(augPath.getTargetType())) {
301 @SuppressWarnings("unchecked")
302 InstanceIdentifier<?> childPath = augPath.child(augChild);
303 org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized = toNormalizedImpl(childPath);
304 org.opendaylight.yangtools.yang.data.api.InstanceIdentifier potentialDiscovered = shortenToLastAugmentation(normalized);
305 if (potentialDiscovered != null) {
306 return potentialDiscovered;
312 private org.opendaylight.yangtools.yang.data.api.InstanceIdentifier shortenToLastAugmentation(
313 final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized) {
315 int foundPosition = -1;
316 for (PathArgument arg : normalized.getPathArguments()) {
318 if (arg instanceof AugmentationIdentifier) {
319 foundPosition = position;
322 if (foundPosition > 0) {
323 Iterable<PathArgument> shortened = Iterables.limit(normalized.getPathArguments(), foundPosition);
324 return org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.create(shortened);
329 private InstanceIdentifier<? extends DataObject> shortenToLastAugment(
330 final InstanceIdentifier<? extends DataObject> binding) {
332 int foundPosition = -1;
333 for (org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument arg : binding.getPathArguments()) {
335 if (isAugmentation(arg.getType())) {
336 foundPosition = position;
339 return InstanceIdentifier.create(Iterables.limit(binding.getPathArguments(), foundPosition));
342 private org.opendaylight.yangtools.yang.data.api.InstanceIdentifier toNormalizedImpl(
343 final InstanceIdentifier<? extends DataObject> binding) {
344 final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier legacyPath = bindingToLegacy
346 final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized = legacyToNormalized
347 .toNormalized(legacyPath);
351 @SuppressWarnings("unchecked")
352 private Iterable<Class<? extends DataObject>> getAugmentationChildren(final Class<?> targetType) {
353 List<Class<? extends DataObject>> ret = new LinkedList<>();
354 for (Method method : targetType.getMethods()) {
355 Class<?> entity = getYangModeledType(method);
356 if (entity != null) {
357 ret.add((Class<? extends DataObject>) entity);
363 @SuppressWarnings({ "rawtypes", "unchecked" })
364 private Class<? extends DataObject> getYangModeledType(final Method method) {
365 if (method.getName().equals("getClass") || !method.getName().startsWith("get")
366 || method.getParameterTypes().length > 0) {
370 Class<?> returnType = method.getReturnType();
371 if (DataContainer.class.isAssignableFrom(returnType)) {
372 return (Class) returnType;
373 } else if (List.class.isAssignableFrom(returnType)) {
375 return ClassLoaderUtils.withClassLoader(method.getDeclaringClass().getClassLoader(),
376 new Supplier<Class>() {
379 Type listResult = ClassLoaderUtils.getFirstGenericParameter(method
380 .getGenericReturnType());
381 if (listResult instanceof Class
382 && DataObject.class.isAssignableFrom((Class) listResult)) {
383 return (Class<?>) listResult;
389 } catch (Exception e) {
390 LOG.debug("Could not get YANG modeled entity for {}", method, e);
398 @SuppressWarnings({ "unchecked", "rawtypes" })
399 private static InstanceIdentifier<?> toWildcarded(final InstanceIdentifier<?> orig) {
400 List<org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument> wildArgs = new LinkedList<>();
401 for (org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument arg : orig.getPathArguments()) {
402 wildArgs.add(new Item(arg.getType()));
404 return InstanceIdentifier.create(wildArgs);
407 private static boolean isAugmentation(final Class<? extends DataObject> type) {
408 return Augmentation.class.isAssignableFrom(type);
411 private static boolean isAugmentationIdentifier(final InstanceIdentifier<?> potential) {
412 return Augmentation.class.isAssignableFrom(potential.getTargetType());
415 private boolean isAugmentationIdentifier(final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier processed) {
416 return Iterables.getLast(processed.getPathArguments()) instanceof AugmentationIdentifier;
419 private static int getAugmentationCount(final InstanceIdentifier<?> potential) {
421 for(org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument arg : potential.getPathArguments()) {
422 if(isAugmentation(arg.getType())) {
430 private static int getAugmentationCount(final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier potential) {
432 for(PathArgument arg : potential.getPathArguments()) {
433 if(arg instanceof AugmentationIdentifier) {
440 public Function<Optional<NormalizedNode<?, ?>>, Optional<DataObject>> deserializeFunction(final InstanceIdentifier<?> path) {
441 return new DeserializeFunction(this, path);
444 private static class DeserializeFunction implements Function<Optional<NormalizedNode<?, ?>>, Optional<DataObject>> {
446 private final BindingToNormalizedNodeCodec codec;
447 private final InstanceIdentifier<?> path;
449 public DeserializeFunction(final BindingToNormalizedNodeCodec codec, final InstanceIdentifier<?> path) {
451 this.codec = Preconditions.checkNotNull(codec, "Codec must not be null");
452 this.path = Preconditions.checkNotNull(path, "Path must not be null");
457 public Optional<DataObject> apply(@Nullable final Optional<NormalizedNode<?, ?>> normalizedNode) {
458 if (normalizedNode.isPresent()) {
459 final DataObject dataObject;
461 dataObject = codec.toBinding(path, normalizedNode.get());
462 } catch (DeserializationException e) {
463 LOG.warn("Failed to create dataobject from node {}", normalizedNode.get(), e);
464 throw new IllegalStateException("Failed to create dataobject", e);
467 if (dataObject != null) {
468 return Optional.of(dataObject);
471 return Optional.absent();
476 * Returns an default object according to YANG schema for supplied path.
478 * @param path DOM Path
479 * @return Node with defaults set on.
481 public NormalizedNode<?, ?> getDefaultNodeFor(final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier path) {
482 Iterator<PathArgument> iterator = path.getPathArguments().iterator();
483 DataNormalizationOperation<?> currentOp = legacyToNormalized.getRootOperation();
484 while (iterator.hasNext()) {
485 PathArgument currentArg = iterator.next();
487 currentOp = currentOp.getChild(currentArg);
488 } catch (DataNormalizationException e) {
489 throw new IllegalArgumentException(String.format("Invalid child encountered in path %s", path), e);
492 return currentOp.createDefault(path.getLastPathArgument());