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;
17 import javax.annotation.Nullable;
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;
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;
54 public class BindingToNormalizedNodeCodec implements SchemaContextListener {
56 private static final Logger LOG = LoggerFactory.getLogger(BindingToNormalizedNodeCodec.class);
58 private final BindingIndependentMappingService bindingToLegacy;
59 private DataNormalizer legacyToNormalized;
61 public BindingToNormalizedNodeCodec(final BindingIndependentMappingService mappingService) {
63 this.bindingToLegacy = mappingService;
66 public org.opendaylight.yangtools.yang.data.api.InstanceIdentifier toNormalized(
67 final InstanceIdentifier<? extends DataObject> binding) {
69 // Used instance-identifier codec do not support serialization of last
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);
77 return toNormalizedImpl(binding);
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));
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
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())) {
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(
103 return toDOMEntry(childPath, child);
108 return normalizedEntry;
114 * Returns a Binding-Aware instance identifier from normalized
115 * instance-identifier if it is possible to create representation.
117 * Returns Optional.absent for cases where target is mixin node except
121 public Optional<InstanceIdentifier<? extends DataObject>> toBinding(
122 final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized)
123 throws DeserializationException {
125 PathArgument lastArgument = Iterables.getLast(normalized.getPathArguments());
126 // Used instance-identifier codec do not support serialization of last
128 // argument if it is AugmentationIdentifier (behaviour expected by old
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);
135 return toBindingImpl(normalized);
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())) {
148 int normalizedCount = getAugmentationCount(normalized);
149 AugmentationIdentifier lastArgument = (AugmentationIdentifier) Iterables.getLast(normalized.getPathArguments());
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
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))
161 if (isNotRepresentable(childPath)) {
162 LOG.trace("Path {} is not BI-representable, skipping it", childPath);
165 } catch (DataNormalizationException e) {
166 LOG.warn("Failed to denormalize path {}, skipping it", childPath, e);
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);
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);
183 LOG.trace("Skipping mis-matched potential path {}", potentialPath);
186 LOG.trace("Failed to find augmentation matching {}", normalized);
187 return Optional.absent();
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;
196 if (isNotRepresentable(normalized)) {
197 return Optional.absent();
199 legacyPath = legacyToNormalized.toLegacy(normalized);
200 } catch (DataNormalizationException e) {
201 throw new IllegalStateException("Could not denormalize path.", e);
203 LOG.trace("InstanceIdentifier Path Deserialization: Legacy representation {}, Normalized representation: {}",
204 legacyPath, normalized);
205 return Optional.<InstanceIdentifier<? extends DataObject>> of(bindingToLegacy.fromDataDom(legacyPath));
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) {
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);
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>(
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<?, ?>>(
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) //
253 legacy = (CompositeNode) DataNormalizer.toLegacy(virtualNode);
255 legacy = (CompositeNode) DataNormalizer.toLegacy(normalizedNode);
258 return bindingToLegacy.dataObjectFromDataDom(path, legacy);
261 public DataNormalizer getDataNormalizer() {
262 return legacyToNormalized;
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);
275 return Optional.of(toBindingEntry(bindingPath, bindingData));
277 return Optional.absent();
282 public void onGlobalContextUpdated(final SchemaContext arg0) {
283 legacyToNormalized = new DataNormalizer(arg0);
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)) {
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
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;
313 private org.opendaylight.yangtools.yang.data.api.InstanceIdentifier shortenToLastAugmentation(
314 final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized) {
316 int foundPosition = -1;
317 for (PathArgument arg : normalized.getPathArguments()) {
319 if (arg instanceof AugmentationIdentifier) {
320 foundPosition = position;
323 if (foundPosition > 0) {
324 Iterable<PathArgument> shortened = Iterables.limit(normalized.getPathArguments(), foundPosition);
325 return org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.create(shortened);
330 private InstanceIdentifier<? extends DataObject> shortenToLastAugment(
331 final InstanceIdentifier<? extends DataObject> binding) {
333 int foundPosition = -1;
334 for (org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument arg : binding.getPathArguments()) {
336 if (isAugmentation(arg.getType())) {
337 foundPosition = position;
340 return InstanceIdentifier.create(Iterables.limit(binding.getPathArguments(), foundPosition));
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
347 final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized = legacyToNormalized
348 .toNormalized(legacyPath);
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);
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) {
371 Class<?> returnType = method.getReturnType();
372 if (DataContainer.class.isAssignableFrom(returnType)) {
373 return (Class) returnType;
374 } else if (List.class.isAssignableFrom(returnType)) {
376 return ClassLoaderUtils.withClassLoader(method.getDeclaringClass().getClassLoader(),
377 new Supplier<Class>() {
380 Type listResult = ClassLoaderUtils.getFirstGenericParameter(method
381 .getGenericReturnType());
382 if (listResult instanceof Class
383 && DataObject.class.isAssignableFrom((Class) listResult)) {
384 return (Class<?>) listResult;
390 } catch (Exception e) {
391 LOG.debug("Could not get YANG modeled entity for {}", method, e);
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()));
405 return InstanceIdentifier.create(wildArgs);
408 private static boolean isAugmentation(final Class<? extends DataObject> type) {
409 return Augmentation.class.isAssignableFrom(type);
412 private static boolean isAugmentationIdentifier(final InstanceIdentifier<?> potential) {
413 return Augmentation.class.isAssignableFrom(potential.getTargetType());
416 private boolean isAugmentationIdentifier(final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier processed) {
417 return Iterables.getLast(processed.getPathArguments()) instanceof AugmentationIdentifier;
420 private static int getAugmentationCount(final InstanceIdentifier<?> potential) {
422 for(org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument arg : potential.getPathArguments()) {
423 if(isAugmentation(arg.getType())) {
431 private static int getAugmentationCount(final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier potential) {
433 for(PathArgument arg : potential.getPathArguments()) {
434 if(arg instanceof AugmentationIdentifier) {
441 public Function<Optional<NormalizedNode<?, ?>>, Optional<DataObject>> deserializeFunction(final InstanceIdentifier<?> path) {
442 return new DeserializeFunction(this, path);
445 private static class DeserializeFunction implements Function<Optional<NormalizedNode<?, ?>>, Optional<DataObject>> {
447 private final BindingToNormalizedNodeCodec codec;
448 private final InstanceIdentifier<?> path;
450 public DeserializeFunction(final BindingToNormalizedNodeCodec codec, final InstanceIdentifier<?> path) {
452 this.codec = Preconditions.checkNotNull(codec, "Codec must not be null");
453 this.path = Preconditions.checkNotNull(path, "Path must not be null");
458 public Optional<DataObject> apply(@Nullable final Optional<NormalizedNode<?, ?>> normalizedNode) {
459 if (normalizedNode.isPresent()) {
460 final DataObject dataObject;
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);
468 if (dataObject != null) {
469 return Optional.of(dataObject);
472 return Optional.absent();