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 org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationException;
18 import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationOperation;
19 import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizer;
20 import org.opendaylight.yangtools.yang.binding.Augmentation;
21 import org.opendaylight.yangtools.yang.binding.DataContainer;
22 import org.opendaylight.yangtools.yang.binding.DataObject;
23 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
24 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.Item;
25 import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
26 import org.opendaylight.yangtools.yang.binding.util.ClassLoaderUtils;
27 import org.opendaylight.yangtools.yang.common.QName;
28 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
29 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.AugmentationIdentifier;
30 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifier;
31 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
32 import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
33 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
34 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
35 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
36 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
37 import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService;
38 import org.opendaylight.yangtools.yang.data.impl.codec.DeserializationException;
39 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
40 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
41 import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
45 import com.google.common.base.Optional;
46 import com.google.common.base.Supplier;
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 LOG.trace("Looking for candidates to match {}", normalized);
153 for (QName child : lastArgument.getPossibleChildNames()) {
154 org.opendaylight.yangtools.yang.data.api.InstanceIdentifier childPath = new org.opendaylight.yangtools.yang.data.api.InstanceIdentifier(
155 ImmutableList.<PathArgument> builder().addAll(normalized.getPath()).add(new NodeIdentifier(child))
158 if (isNotRepresentable(childPath)) {
159 LOG.trace("Path {} is not BI-representable, skipping it", childPath);
162 } catch (DataNormalizationException e) {
163 LOG.warn("Failed to denormalize path {}, skipping it", childPath, e);
167 Optional<InstanceIdentifier<? extends DataObject>> baId = toBindingImpl(childPath);
168 if (!baId.isPresent()) {
169 LOG.debug("No binding-aware identifier found for path {}, skipping it", childPath);
173 InstanceIdentifier<? extends DataObject> potentialPath = shortenToLastAugment(baId.get());
174 int potentialAugmentCount = getAugmentationCount(potentialPath);
175 if (potentialAugmentCount == normalizedCount) {
176 LOG.trace("Found matching path {}", potentialPath);
177 return Optional.<InstanceIdentifier<? extends DataObject>> of(potentialPath);
180 LOG.trace("Skipping mis-matched potential path {}", potentialPath);
183 LOG.trace("Failed to find augmentation matching {}", normalized);
184 return Optional.absent();
187 private Optional<InstanceIdentifier<? extends DataObject>> toBindingImpl(
188 final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized)
189 throws DeserializationException {
190 org.opendaylight.yangtools.yang.data.api.InstanceIdentifier legacyPath;
193 if (isNotRepresentable(normalized)) {
194 return Optional.absent();
196 legacyPath = legacyToNormalized.toLegacy(normalized);
197 } catch (DataNormalizationException e) {
198 throw new IllegalStateException("Could not denormalize path.", e);
200 LOG.trace("InstanceIdentifier Path Deserialization: Legacy representation {}, Normalized representation: {}",
201 legacyPath, normalized);
202 return Optional.<InstanceIdentifier<? extends DataObject>> of(bindingToLegacy.fromDataDom(legacyPath));
205 private boolean isNotRepresentable(final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized)
206 throws DataNormalizationException {
207 DataNormalizationOperation<?> op = findNormalizationOperation(normalized);
208 if( op.isMixin() && op.getIdentifier() instanceof NodeIdentifier) {
217 private DataNormalizationOperation<?> findNormalizationOperation(
218 final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized)
219 throws DataNormalizationException {
220 DataNormalizationOperation<?> current = legacyToNormalized.getRootOperation();
221 for (PathArgument arg : normalized.getPath()) {
222 current = current.getChild(arg);
227 private static final Entry<org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject>, DataObject> toEntry(
228 final org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject> key,
229 final DataObject value) {
230 return new SimpleEntry<org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject>, DataObject>(
234 public DataObject toBinding(final InstanceIdentifier<?> path, final NormalizedNode<?, ?> normalizedNode)
235 throws DeserializationException {
236 CompositeNode legacy = null;
237 if (isAugmentationIdentifier(path) && normalizedNode instanceof AugmentationNode) {
238 QName augIdentifier = BindingReflections.findQName(path.getTargetType());
239 ContainerNode virtualNode = Builders.containerBuilder() //
240 .withNodeIdentifier(new NodeIdentifier(augIdentifier)) //
241 .withChild((DataContainerChild<?, ?>) normalizedNode) //
243 legacy = (CompositeNode) DataNormalizer.toLegacy(virtualNode);
245 legacy = (CompositeNode) DataNormalizer.toLegacy(normalizedNode);
248 return bindingToLegacy.dataObjectFromDataDom(path, legacy);
251 public DataNormalizer getDataNormalizer() {
252 return legacyToNormalized;
255 public Optional<Entry<org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject>, DataObject>> toBinding(
256 final Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, ? extends NormalizedNode<?, ?>> normalized)
257 throws DeserializationException {
258 Optional<InstanceIdentifier<? extends DataObject>> potentialPath = toBinding(normalized.getKey());
259 if (potentialPath.isPresent()) {
260 InstanceIdentifier<? extends DataObject> bindingPath = potentialPath.get();
261 DataObject bindingData = toBinding(bindingPath, normalized.getValue());
262 if (bindingData == null) {
263 LOG.warn("Failed to deserialize {} to Binding format. Binding path is: {}", normalized, bindingPath);
265 return Optional.of(toEntry(bindingPath, bindingData));
267 return Optional.absent();
272 public void onGlobalContextUpdated(final SchemaContext arg0) {
273 legacyToNormalized = new DataNormalizer(arg0);
276 private org.opendaylight.yangtools.yang.data.api.InstanceIdentifier toNormalizedAugmented(
277 final InstanceIdentifier<?> augPath) {
278 org.opendaylight.yangtools.yang.data.api.InstanceIdentifier processed = toNormalizedImpl(augPath);
279 // If used instance identifier codec added supports for deserialization
280 // of last AugmentationIdentifier we will just reuse it
281 if (isAugmentationIdentifier(processed)) {
284 // Here we employ small trick - DataNormalizer injects augmentation
285 // identifier if child is
286 // also part of the path (since using a child we can safely identify
288 // so, we scan augmentation for children add it to path
289 // and use original algorithm, then shorten it to last augmentation
290 for (@SuppressWarnings("rawtypes")
291 Class augChild : getAugmentationChildren(augPath.getTargetType())) {
292 @SuppressWarnings("unchecked")
293 InstanceIdentifier<?> childPath = augPath.child(augChild);
294 org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized = toNormalizedImpl(childPath);
295 org.opendaylight.yangtools.yang.data.api.InstanceIdentifier potentialDiscovered = shortenToLastAugmentation(normalized);
296 if (potentialDiscovered != null) {
297 return potentialDiscovered;
303 private org.opendaylight.yangtools.yang.data.api.InstanceIdentifier shortenToLastAugmentation(
304 final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized) {
306 int foundPosition = -1;
307 for (PathArgument arg : normalized.getPath()) {
309 if (arg instanceof AugmentationIdentifier) {
310 foundPosition = position;
313 if (foundPosition > 0) {
314 return new org.opendaylight.yangtools.yang.data.api.InstanceIdentifier(normalized.getPath().subList(0,
320 private InstanceIdentifier<? extends DataObject> shortenToLastAugment(
321 final InstanceIdentifier<? extends DataObject> binding) {
323 int foundPosition = -1;
324 for (org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument arg : binding.getPathArguments()) {
326 if (isAugmentation(arg.getType())) {
327 foundPosition = position;
330 return InstanceIdentifier.create(Iterables.limit(binding.getPathArguments(), foundPosition));
333 private org.opendaylight.yangtools.yang.data.api.InstanceIdentifier toNormalizedImpl(
334 final InstanceIdentifier<? extends DataObject> binding) {
335 final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier legacyPath = bindingToLegacy
337 final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized = legacyToNormalized
338 .toNormalized(legacyPath);
342 @SuppressWarnings("unchecked")
343 private Iterable<Class<? extends DataObject>> getAugmentationChildren(final Class<?> targetType) {
344 List<Class<? extends DataObject>> ret = new LinkedList<>();
345 for (Method method : targetType.getMethods()) {
346 Class<?> entity = getYangModeledType(method);
347 if (entity != null) {
348 ret.add((Class<? extends DataObject>) entity);
354 @SuppressWarnings({ "rawtypes", "unchecked" })
355 private Class<? extends DataObject> getYangModeledType(final Method method) {
356 if (method.getName().equals("getClass") || !method.getName().startsWith("get")
357 || method.getParameterTypes().length > 0) {
361 Class<?> returnType = method.getReturnType();
362 if (DataContainer.class.isAssignableFrom(returnType)) {
363 return (Class) returnType;
364 } else if (List.class.isAssignableFrom(returnType)) {
366 return ClassLoaderUtils.withClassLoader(method.getDeclaringClass().getClassLoader(),
367 new Supplier<Class>() {
370 Type listResult = ClassLoaderUtils.getFirstGenericParameter(method
371 .getGenericReturnType());
372 if (listResult instanceof Class
373 && DataObject.class.isAssignableFrom((Class) listResult)) {
374 return (Class<?>) listResult;
380 } catch (Exception e) {
381 LOG.debug("Could not get YANG modeled entity for {}", method, e);
389 @SuppressWarnings({ "unchecked", "rawtypes" })
390 private static InstanceIdentifier<?> toWildcarded(final InstanceIdentifier<?> orig) {
391 List<org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument> wildArgs = new LinkedList<>();
392 for (org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument arg : orig.getPathArguments()) {
393 wildArgs.add(new Item(arg.getType()));
395 return InstanceIdentifier.create(wildArgs);
398 private static boolean isAugmentation(final Class<? extends DataObject> type) {
399 return Augmentation.class.isAssignableFrom(type);
402 private static boolean isAugmentationIdentifier(final InstanceIdentifier<?> potential) {
403 return Augmentation.class.isAssignableFrom(potential.getTargetType());
406 private boolean isAugmentationIdentifier(final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier processed) {
407 return Iterables.getLast(processed.getPath()) instanceof AugmentationIdentifier;
410 private static int getAugmentationCount(final InstanceIdentifier<?> potential) {
412 for(org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument arg : potential.getPathArguments()) {
413 if(isAugmentation(arg.getType())) {
421 private static int getAugmentationCount(final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier potential) {
423 for(PathArgument arg : potential.getPath()) {
424 if(arg instanceof AugmentationIdentifier) {