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 AugmentationIdentifier lastArgument = (AugmentationIdentifier) Iterables.getLast(normalized.getPath());
147 // Here we employ small trick - Binding-aware Codec injects an pointer
148 // to augmentation class
149 // if child is referenced - so we will reference child and then shorten
151 for (QName child : lastArgument.getPossibleChildNames()) {
152 org.opendaylight.yangtools.yang.data.api.InstanceIdentifier childPath = new org.opendaylight.yangtools.yang.data.api.InstanceIdentifier(
153 ImmutableList.<PathArgument> builder().addAll(normalized.getPath()).add(new NodeIdentifier(child))
156 if (!isChoiceOrCasePath(childPath)) {
157 InstanceIdentifier<? extends DataObject> potentialPath = shortenToLastAugment(toBindingImpl(
159 return Optional.<InstanceIdentifier<? extends DataObject>> of(potentialPath);
161 } catch (Exception e) {
162 LOG.trace("Unable to deserialize aug. child path for {}", childPath, e);
165 return toBindingImpl(normalized);
168 private Optional<InstanceIdentifier<? extends DataObject>> toBindingImpl(
169 final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized)
170 throws DeserializationException {
171 org.opendaylight.yangtools.yang.data.api.InstanceIdentifier legacyPath;
174 if (isChoiceOrCasePath(normalized)) {
175 return Optional.absent();
177 legacyPath = legacyToNormalized.toLegacy(normalized);
178 } catch (DataNormalizationException e) {
179 throw new IllegalStateException("Could not denormalize path.", e);
181 LOG.trace("InstanceIdentifier Path Deserialization: Legacy representation {}, Normalized representation: {}",
182 legacyPath, normalized);
183 return Optional.<InstanceIdentifier<? extends DataObject>> of(bindingToLegacy.fromDataDom(legacyPath));
186 private boolean isChoiceOrCasePath(final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized)
187 throws DataNormalizationException {
188 DataNormalizationOperation<?> op = findNormalizationOperation(normalized);
189 return op.isMixin() && op.getIdentifier() instanceof NodeIdentifier;
192 private DataNormalizationOperation<?> findNormalizationOperation(
193 final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized)
194 throws DataNormalizationException {
195 DataNormalizationOperation<?> current = legacyToNormalized.getRootOperation();
196 for (PathArgument arg : normalized.getPath()) {
197 current = current.getChild(arg);
202 private static final Entry<org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject>, DataObject> toEntry(
203 final org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject> key,
204 final DataObject value) {
205 return new SimpleEntry<org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject>, DataObject>(
209 public DataObject toBinding(final InstanceIdentifier<?> path, final NormalizedNode<?, ?> normalizedNode)
210 throws DeserializationException {
211 CompositeNode legacy = null;
212 if (isAugmentationIdentifier(path) && normalizedNode instanceof AugmentationNode) {
213 QName augIdentifier = BindingReflections.findQName(path.getTargetType());
214 ContainerNode virtualNode = Builders.containerBuilder() //
215 .withNodeIdentifier(new NodeIdentifier(augIdentifier)) //
216 .withChild((DataContainerChild<?, ?>) normalizedNode) //
218 legacy = (CompositeNode) DataNormalizer.toLegacy(virtualNode);
220 legacy = (CompositeNode) DataNormalizer.toLegacy(normalizedNode);
223 return bindingToLegacy.dataObjectFromDataDom(path, legacy);
226 public DataNormalizer getDataNormalizer() {
227 return legacyToNormalized;
230 public Optional<Entry<org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject>, DataObject>> toBinding(
231 final Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, ? extends NormalizedNode<?, ?>> normalized)
232 throws DeserializationException {
233 Optional<InstanceIdentifier<? extends DataObject>> potentialPath = toBinding(normalized.getKey());
234 if (potentialPath.isPresent()) {
235 InstanceIdentifier<? extends DataObject> bindingPath = potentialPath.get();
236 DataObject bindingData = toBinding(bindingPath, normalized.getValue());
237 if (bindingData == null) {
238 LOG.warn("Failed to deserialize {} to Binding format. Binding path is: {}", normalized, bindingPath);
240 return Optional.of(toEntry(bindingPath, bindingData));
242 return Optional.absent();
247 public void onGlobalContextUpdated(final SchemaContext arg0) {
248 legacyToNormalized = new DataNormalizer(arg0);
251 private org.opendaylight.yangtools.yang.data.api.InstanceIdentifier toNormalizedAugmented(
252 final InstanceIdentifier<?> augPath) {
253 org.opendaylight.yangtools.yang.data.api.InstanceIdentifier processed = toNormalizedImpl(augPath);
254 // If used instance identifier codec added supports for deserialization
255 // of last AugmentationIdentifier we will just reuse it
256 if (isAugmentationIdentifier(processed)) {
259 // Here we employ small trick - DataNormalizer injecst augmentation
260 // identifier if child is
261 // also part of the path (since using a child we can safely identify
263 // so, we scan augmentation for children add it to path
264 // and use original algorithm, then shorten it to last augmentation
265 for (@SuppressWarnings("rawtypes")
266 Class augChild : getAugmentationChildren(augPath.getTargetType())) {
267 @SuppressWarnings("unchecked")
268 InstanceIdentifier<?> childPath = augPath.child(augChild);
269 org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized = toNormalizedImpl(childPath);
270 org.opendaylight.yangtools.yang.data.api.InstanceIdentifier potentialDiscovered = shortenToLastAugmentation(normalized);
271 if (potentialDiscovered != null) {
272 return potentialDiscovered;
278 private org.opendaylight.yangtools.yang.data.api.InstanceIdentifier shortenToLastAugmentation(
279 final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized) {
281 int foundPosition = -1;
282 for (PathArgument arg : normalized.getPath()) {
284 if (arg instanceof AugmentationIdentifier) {
285 foundPosition = position;
288 if (foundPosition > 0) {
289 return new org.opendaylight.yangtools.yang.data.api.InstanceIdentifier(normalized.getPath().subList(0,
295 private InstanceIdentifier<? extends DataObject> shortenToLastAugment(
296 final InstanceIdentifier<? extends DataObject> binding) {
298 int foundPosition = -1;
299 for (org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument arg : binding.getPathArguments()) {
301 if (isAugmentation(arg.getType())) {
302 foundPosition = position;
305 return InstanceIdentifier.create(Iterables.limit(binding.getPathArguments(), foundPosition));
308 private org.opendaylight.yangtools.yang.data.api.InstanceIdentifier toNormalizedImpl(
309 final InstanceIdentifier<? extends DataObject> binding) {
310 final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier legacyPath = bindingToLegacy
312 final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized = legacyToNormalized
313 .toNormalized(legacyPath);
317 @SuppressWarnings("unchecked")
318 private Iterable<Class<? extends DataObject>> getAugmentationChildren(final Class<?> targetType) {
319 List<Class<? extends DataObject>> ret = new LinkedList<>();
320 for (Method method : targetType.getMethods()) {
321 Class<?> entity = getYangModeledType(method);
322 if (entity != null) {
323 ret.add((Class<? extends DataObject>) entity);
329 @SuppressWarnings({ "rawtypes", "unchecked" })
330 private Class<? extends DataObject> getYangModeledType(final Method method) {
331 if (method.getName().equals("getClass") || !method.getName().startsWith("get")
332 || method.getParameterTypes().length > 0) {
336 Class<?> returnType = method.getReturnType();
337 if (DataContainer.class.isAssignableFrom(returnType)) {
338 return (Class) returnType;
339 } else if (List.class.isAssignableFrom(returnType)) {
341 return ClassLoaderUtils.withClassLoader(method.getDeclaringClass().getClassLoader(),
342 new Callable<Class>() {
344 @SuppressWarnings("rawtypes")
346 public Class call() throws Exception {
347 Type listResult = ClassLoaderUtils.getFirstGenericParameter(method
348 .getGenericReturnType());
349 if (listResult instanceof Class
350 && DataObject.class.isAssignableFrom((Class) listResult)) {
351 return (Class<?>) listResult;
357 } catch (Exception e) {
358 LOG.debug("Could not get YANG modeled entity for {}", method, e);
366 @SuppressWarnings({ "unchecked", "rawtypes" })
367 private static InstanceIdentifier<?> toWildcarded(final InstanceIdentifier<?> orig) {
368 List<org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument> wildArgs = new LinkedList<>();
369 for (org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument arg : orig.getPathArguments()) {
370 wildArgs.add(new Item(arg.getType()));
372 return InstanceIdentifier.create(wildArgs);
375 private static boolean isAugmentation(final Class<? extends DataObject> type) {
376 return Augmentation.class.isAssignableFrom(type);
379 private static boolean isAugmentationIdentifier(final InstanceIdentifier<?> potential) {
380 return Augmentation.class.isAssignableFrom(potential.getTargetType());
383 private boolean isAugmentationIdentifier(final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier processed) {
384 return Iterables.getLast(processed.getPath()) instanceof AugmentationIdentifier;