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.util.AbstractMap.SimpleEntry;
12 import java.util.Collection;
13 import java.util.HashSet;
14 import java.util.Iterator;
15 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.BindingMapping;
25 import org.opendaylight.yangtools.yang.binding.DataObject;
26 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
27 import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
28 import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
29 import org.opendaylight.yangtools.yang.common.QName;
30 import org.opendaylight.yangtools.yang.common.QNameModule;
31 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
32 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
33 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
34 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.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.AugmentationSchema;
44 import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
45 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
46 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
47 import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
48 import org.slf4j.Logger;
49 import org.slf4j.LoggerFactory;
51 import com.google.common.base.Function;
52 import com.google.common.base.Optional;
53 import com.google.common.base.Preconditions;
54 import com.google.common.base.Predicate;
55 import com.google.common.collect.ImmutableList;
56 import com.google.common.collect.Iterables;
58 public class BindingToNormalizedNodeCodec implements SchemaContextListener {
60 private static final Logger LOG = LoggerFactory.getLogger(BindingToNormalizedNodeCodec.class);
62 private final BindingIndependentMappingService bindingToLegacy;
63 private DataNormalizer legacyToNormalized;
65 public BindingToNormalizedNodeCodec(final BindingIndependentMappingService mappingService) {
67 this.bindingToLegacy = mappingService;
70 public org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier toNormalized(
71 final InstanceIdentifier<? extends DataObject> binding) {
73 // Used instance-identifier codec do not support serialization of last
75 // argument if it is Augmentation (behaviour expected by old datastore)
76 // in this case, we explicitly check if last argument is augmentation
77 // to process it separately
78 if (isAugmentationIdentifier(binding)) {
79 return toNormalizedAugmented(binding);
81 return toNormalizedImpl(binding);
84 public Entry<org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier, NormalizedNode<?, ?>> toNormalizedNode(
85 final InstanceIdentifier<? extends DataObject> bindingPath, final DataObject bindingObject) {
86 return toNormalizedNode(toBindingEntry(bindingPath, bindingObject));
90 public Entry<org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier, NormalizedNode<?, ?>> toNormalizedNode(
91 final Entry<org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject>, DataObject> binding) {
92 Entry<org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier, CompositeNode> legacyEntry = bindingToLegacy
94 Entry<org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier, NormalizedNode<?, ?>> normalizedEntry = legacyToNormalized
95 .toNormalized(legacyEntry);
96 LOG.trace("Serialization of {}, Legacy Representation: {}, Normalized Representation: {}", binding,
97 legacyEntry, normalizedEntry);
98 if (isAugmentation(binding.getKey().getTargetType())) {
100 for (DataContainerChild<? extends PathArgument, ?> child : ((DataContainerNode<?>) normalizedEntry
101 .getValue()).getValue()) {
102 if (child instanceof AugmentationNode) {
103 ImmutableList<PathArgument> childArgs = ImmutableList.<PathArgument> builder()
104 .addAll(normalizedEntry.getKey().getPathArguments()).add(child.getIdentifier()).build();
105 org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier childPath = org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier
107 return toDOMEntry(childPath, child);
112 return normalizedEntry;
118 * Returns a Binding-Aware instance identifier from normalized
119 * instance-identifier if it is possible to create representation.
121 * Returns Optional.absent for cases where target is mixin node except
125 public Optional<InstanceIdentifier<? extends DataObject>> toBinding(
126 final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier normalized)
127 throws DeserializationException {
129 PathArgument lastArgument = Iterables.getLast(normalized.getPathArguments());
130 // Used instance-identifier codec do not support serialization of last
132 // argument if it is AugmentationIdentifier (behaviour expected by old
134 // in this case, we explicitly check if last argument is augmentation
135 // to process it separately
136 if (lastArgument instanceof AugmentationIdentifier) {
137 return toBindingAugmented(normalized);
139 return toBindingImpl(normalized);
142 private Optional<InstanceIdentifier<? extends DataObject>> toBindingAugmented(
143 final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier normalized)
144 throws DeserializationException {
145 Optional<InstanceIdentifier<? extends DataObject>> potential = toBindingImpl(normalized);
146 // Shorthand check, if codec already supports deserialization
147 // of AugmentationIdentifier we will return
148 if (potential.isPresent() && isAugmentationIdentifier(potential.get())) {
152 int normalizedCount = getAugmentationCount(normalized);
153 AugmentationIdentifier lastArgument = (AugmentationIdentifier) Iterables.getLast(normalized.getPathArguments());
155 // Here we employ small trick - Binding-aware Codec injects an pointer
156 // to augmentation class
157 // if child is referenced - so we will reference child and then shorten
159 LOG.trace("Looking for candidates to match {}", normalized);
160 for (QName child : lastArgument.getPossibleChildNames()) {
161 org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier childPath = normalized.node(child);
163 if (isNotRepresentable(childPath)) {
164 LOG.trace("Path {} is not BI-representable, skipping it", childPath);
167 } catch (DataNormalizationException e) {
168 LOG.warn("Failed to denormalize path {}, skipping it", childPath, e);
172 Optional<InstanceIdentifier<? extends DataObject>> baId = toBindingImpl(childPath);
173 if (!baId.isPresent()) {
174 LOG.debug("No binding-aware identifier found for path {}, skipping it", childPath);
178 InstanceIdentifier<? extends DataObject> potentialPath = shortenToLastAugment(baId.get());
179 int potentialAugmentCount = getAugmentationCount(potentialPath);
180 if (potentialAugmentCount == normalizedCount) {
181 LOG.trace("Found matching path {}", potentialPath);
182 return Optional.<InstanceIdentifier<? extends DataObject>> of(potentialPath);
185 LOG.trace("Skipping mis-matched potential path {}", potentialPath);
188 LOG.trace("Failed to find augmentation matching {}", normalized);
189 return Optional.absent();
192 private Optional<InstanceIdentifier<? extends DataObject>> toBindingImpl(
193 final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier normalized)
194 throws DeserializationException {
195 org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier legacyPath;
198 if (isNotRepresentable(normalized)) {
199 return Optional.absent();
201 legacyPath = legacyToNormalized.toLegacy(normalized);
202 } catch (DataNormalizationException e) {
203 throw new IllegalStateException("Could not denormalize path.", e);
205 LOG.trace("InstanceIdentifier Path Deserialization: Legacy representation {}, Normalized representation: {}",
206 legacyPath, normalized);
207 return Optional.<InstanceIdentifier<? extends DataObject>> of(bindingToLegacy.fromDataDom(legacyPath));
210 private boolean isNotRepresentable(final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier normalized)
211 throws DataNormalizationException {
212 DataNormalizationOperation<?> op = findNormalizationOperation(normalized);
213 if (op.isMixin() && op.getIdentifier() instanceof NodeIdentifier) {
222 private DataNormalizationOperation<?> findNormalizationOperation(
223 final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier normalized)
224 throws DataNormalizationException {
225 DataNormalizationOperation<?> current = legacyToNormalized.getRootOperation();
226 for (PathArgument arg : normalized.getPathArguments()) {
227 current = current.getChild(arg);
232 private static final Entry<org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject>, DataObject> toBindingEntry(
233 final org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject> key,
234 final DataObject value) {
235 return new SimpleEntry<org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject>, DataObject>(
239 private static final Entry<org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier, NormalizedNode<?, ?>> toDOMEntry(
240 final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier key, final NormalizedNode<?, ?> value) {
241 return new SimpleEntry<org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier, NormalizedNode<?, ?>>(key,
245 public DataObject toBinding(final InstanceIdentifier<?> path, final NormalizedNode<?, ?> normalizedNode)
246 throws DeserializationException {
247 CompositeNode legacy = null;
248 if (isAugmentationIdentifier(path) && normalizedNode instanceof AugmentationNode) {
249 QName augIdentifier = BindingReflections.findQName(path.getTargetType());
250 ContainerNode virtualNode = Builders.containerBuilder() //
251 .withNodeIdentifier(new NodeIdentifier(augIdentifier)) //
252 .withChild((DataContainerChild<?, ?>) normalizedNode) //
254 legacy = (CompositeNode) DataNormalizer.toLegacy(virtualNode);
256 legacy = (CompositeNode) DataNormalizer.toLegacy(normalizedNode);
259 return bindingToLegacy.dataObjectFromDataDom(path, legacy);
262 public DataNormalizer getDataNormalizer() {
263 return legacyToNormalized;
266 public Optional<Entry<org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject>, DataObject>> toBinding(
267 final Entry<org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier, ? extends NormalizedNode<?, ?>> normalized)
268 throws DeserializationException {
269 Optional<InstanceIdentifier<? extends DataObject>> potentialPath = toBinding(normalized.getKey());
270 if (potentialPath.isPresent()) {
271 InstanceIdentifier<? extends DataObject> bindingPath = potentialPath.get();
272 DataObject bindingData = toBinding(bindingPath, normalized.getValue());
273 if (bindingData == null) {
274 LOG.warn("Failed to deserialize {} to Binding format. Binding path is: {}", normalized, bindingPath);
276 return Optional.of(toBindingEntry(bindingPath, bindingData));
278 return Optional.absent();
283 public void onGlobalContextUpdated(final SchemaContext arg0) {
284 legacyToNormalized = new DataNormalizer(arg0);
287 private org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier toNormalizedAugmented(
288 final InstanceIdentifier<?> augPath) {
289 org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier processed = toNormalizedImpl(augPath);
290 // If used instance identifier codec added supports for deserialization
291 // of last AugmentationIdentifier we will just reuse it
292 if (isAugmentationIdentifier(processed)) {
295 Optional<org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier> additionalSerialized;
296 additionalSerialized = toNormalizedAugmentedUsingChildContainers(augPath, processed);
298 if (additionalSerialized.isPresent()) {
299 return additionalSerialized.get();
301 additionalSerialized = toNormalizedAugmentedUsingChildLeafs(augPath, processed);
302 if (additionalSerialized.isPresent()) {
303 return additionalSerialized.get();
305 throw new IllegalStateException("Unabled to construct augmentation identfier for " + augPath);
309 * Tries to find correct augmentation identifier using children leafs
311 * This method uses normalized Instance Identifier of parent node to fetch
312 * schema and {@link BindingReflections#getModuleInfo(Class)} to learn about
313 * augmentation namespace, specificly, in which module it was defined.
315 * Then it uses it to filter all available augmentations for parent by
316 * module. After that it walks augmentations in particular module and
317 * pick-up first which at least one leaf name matches supplied augmentation.
318 * We could do this safely since YANG explicitly states that no any existing
319 * augmentations must differ in leaf fully qualified names.
323 * Binding Aware Path which ends with augment
328 private Optional<org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier> toNormalizedAugmentedUsingChildLeafs(
329 final InstanceIdentifier<?> augPath,
330 final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier parentPath) {
332 DataNormalizationOperation<?> parentOp = legacyToNormalized.getOperation(parentPath);
333 if(!parentOp.getDataSchemaNode().isPresent()) {
334 return Optional.absent();
336 DataSchemaNode parentSchema = parentOp.getDataSchemaNode().get();
337 if (parentSchema instanceof AugmentationTarget) {
338 Set<AugmentationSchema> augmentations = ((AugmentationTarget) parentSchema).getAvailableAugmentations();
339 LOG.info("Augmentations for {}, {}", augPath, augmentations);
340 Optional<AugmentationSchema> schema = findAugmentation(augPath.getTargetType(), augmentations);
341 if (schema.isPresent()) {
342 AugmentationIdentifier augmentationIdentifier = DataNormalizationOperation
343 .augmentationIdentifierFrom(schema.get());
344 return Optional.of(parentPath.node(augmentationIdentifier));
347 } catch (DataNormalizationException e) {
348 throw new IllegalArgumentException(e);
350 return Optional.absent();
354 * Creates instance identifier for augmentation child, tries to serialize it
355 * Instance Identifier is then shortened to last augmentation.
357 * This is for situations, where underlying codec is implementing hydrogen
358 * style DOM APIs (which did not supported {@link AugmentationIdentifier}.)
362 * Path to parent node
365 @SuppressWarnings("rawtypes")
366 private Optional<org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier> toNormalizedAugmentedUsingChildContainers(
367 final InstanceIdentifier<?> augPath,
368 final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier parentPath) {
369 for (Class augChild : BindingReflections.getChildrenClasses(augPath.getTargetType())) {
370 @SuppressWarnings("unchecked")
371 InstanceIdentifier<?> childPath = augPath.child(augChild);
372 org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier normalized = toNormalizedImpl(childPath);
373 org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier potentialDiscovered = shortenToLastAugmentation(
374 normalized, parentPath);
375 if (potentialDiscovered != null) {
376 return Optional.of(potentialDiscovered);
379 return Optional.absent();
382 private Optional<AugmentationSchema> findAugmentation(final Class<?> targetType,
383 final Set<AugmentationSchema> augmentations) {
384 YangModuleInfo moduleInfo;
386 moduleInfo = BindingReflections.getModuleInfo(targetType);
387 } catch (Exception e) {
388 throw new IllegalStateException(e);
390 Iterable<AugmentationSchema> filtered = filteredByModuleInfo(augmentations,
391 BindingReflections.getModuleQName(moduleInfo).getModule());
393 Set<String> targetTypeGetters = getYangModeledGetters(targetType);
394 for (AugmentationSchema schema : filtered) {
395 for (DataSchemaNode child : schema.getChildNodes()) {
396 String getterName = "get" + BindingMapping.getClassName(child.getQName());
397 if (targetTypeGetters.contains(getterName)) {
398 return Optional.of(schema);
402 return Optional.absent();
405 private static Iterable<AugmentationSchema> filteredByModuleInfo(final Iterable<AugmentationSchema> augmentations,
406 final QNameModule module) {
407 return Iterables.filter(augmentations, new Predicate<AugmentationSchema>() {
409 public boolean apply(final AugmentationSchema schema) {
410 final Collection<DataSchemaNode> childNodes = schema.getChildNodes();
411 return !childNodes.isEmpty() && module.equals(Iterables.get(childNodes, 0).getQName().getModule());
416 public static final Set<String> getYangModeledGetters(final Class<?> targetType) {
417 HashSet<String> ret = new HashSet<String>();
418 for (Method method : targetType.getMethods()) {
419 if (isYangModeledGetter(method)) {
420 ret.add(method.getName());
428 * Returns true if supplied method represent getter for YANG modeled value
431 * Method to be tested
432 * @return true if method represent getter for YANG Modeled value.
434 private static final boolean isYangModeledGetter(final Method method) {
435 return !method.getName().equals("getClass") && !method.getName().equals("getImplementedInterface")
436 && method.getName().startsWith("get") && method.getParameterTypes().length == 0;
439 private org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier shortenToLastAugmentation(
440 final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier normalized,
441 final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier parentPath) {
442 int parentSize = Iterables.size(parentPath.getPathArguments());
444 int foundPosition = -1;
445 for (PathArgument arg : normalized.getPathArguments()) {
447 if (arg instanceof AugmentationIdentifier) {
448 foundPosition = position;
451 if (foundPosition > 0 && foundPosition > parentSize) {
452 Iterable<PathArgument> shortened = Iterables.limit(normalized.getPathArguments(), foundPosition);
453 return org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.create(shortened);
458 private InstanceIdentifier<? extends DataObject> shortenToLastAugment(
459 final InstanceIdentifier<? extends DataObject> binding) {
461 int foundPosition = -1;
462 for (org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument arg : binding.getPathArguments()) {
464 if (isAugmentation(arg.getType())) {
465 foundPosition = position;
468 return InstanceIdentifier.create(Iterables.limit(binding.getPathArguments(), foundPosition));
471 private org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier toNormalizedImpl(
472 final InstanceIdentifier<? extends DataObject> binding) {
473 final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier legacyPath = bindingToLegacy
475 final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier normalized = legacyToNormalized
476 .toNormalized(legacyPath);
480 private static boolean isAugmentation(final Class<? extends DataObject> type) {
481 return Augmentation.class.isAssignableFrom(type);
484 private static boolean isAugmentationIdentifier(final InstanceIdentifier<?> potential) {
485 return Augmentation.class.isAssignableFrom(potential.getTargetType());
488 private boolean isAugmentationIdentifier(final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier processed) {
489 return Iterables.getLast(processed.getPathArguments()) instanceof AugmentationIdentifier;
492 private static int getAugmentationCount(final InstanceIdentifier<?> potential) {
494 for (org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument arg : potential.getPathArguments()) {
495 if (isAugmentation(arg.getType())) {
503 private static int getAugmentationCount(final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier potential) {
505 for (PathArgument arg : potential.getPathArguments()) {
506 if (arg instanceof AugmentationIdentifier) {
513 @SuppressWarnings({ "rawtypes", "unchecked" })
514 public <T extends DataObject> Function<Optional<NormalizedNode<?, ?>>, Optional<T>> deserializeFunction(final InstanceIdentifier<T> path) {
515 return new DeserializeFunction(this, path);
518 private static class DeserializeFunction<T extends DataObject> implements Function<Optional<NormalizedNode<?, ?>>, Optional<T>> {
520 private final BindingToNormalizedNodeCodec codec;
521 private final InstanceIdentifier<?> path;
523 public DeserializeFunction(final BindingToNormalizedNodeCodec codec, final InstanceIdentifier<?> path) {
525 this.codec = Preconditions.checkNotNull(codec, "Codec must not be null");
526 this.path = Preconditions.checkNotNull(path, "Path must not be null");
529 @SuppressWarnings("rawtypes")
532 public Optional apply(@Nullable final Optional<NormalizedNode<?, ?>> normalizedNode) {
533 if (normalizedNode.isPresent()) {
534 final DataObject dataObject;
536 dataObject = codec.toBinding(path, normalizedNode.get());
537 } catch (DeserializationException e) {
538 LOG.warn("Failed to create dataobject from node {}", normalizedNode.get(), e);
539 throw new IllegalStateException("Failed to create dataobject", e);
542 if (dataObject != null) {
543 return Optional.of(dataObject);
546 return Optional.absent();
551 * Returns an default object according to YANG schema for supplied path.
553 * @param path DOM Path
554 * @return Node with defaults set on.
556 public NormalizedNode<?, ?> getDefaultNodeFor(final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier path) {
557 Iterator<PathArgument> iterator = path.getPathArguments().iterator();
558 DataNormalizationOperation<?> currentOp = legacyToNormalized.getRootOperation();
559 while (iterator.hasNext()) {
560 PathArgument currentArg = iterator.next();
562 currentOp = currentOp.getChild(currentArg);
563 } catch (DataNormalizationException e) {
564 throw new IllegalArgumentException(String.format("Invalid child encountered in path %s", path), e);
567 return currentOp.createDefault(path.getLastPathArgument());