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.DataNormalizer;
20 import org.opendaylight.yangtools.concepts.util.ClassLoaderUtils;
21 import org.opendaylight.yangtools.yang.binding.Augmentation;
22 import org.opendaylight.yangtools.yang.binding.DataContainer;
23 import org.opendaylight.yangtools.yang.binding.DataObject;
24 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
25 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.Item;
26 import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
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.collect.ImmutableList;
46 import com.google.common.collect.Iterables;
48 public class BindingToNormalizedNodeCodec implements SchemaContextListener {
50 private static final Logger LOG = LoggerFactory.getLogger(BindingToNormalizedNodeCodec.class);
52 private final BindingIndependentMappingService bindingToLegacy;
53 private DataNormalizer legacyToNormalized;
55 public BindingToNormalizedNodeCodec(final BindingIndependentMappingService mappingService) {
57 this.bindingToLegacy = mappingService;
60 public org.opendaylight.yangtools.yang.data.api.InstanceIdentifier toNormalized(
61 final InstanceIdentifier<? extends DataObject> binding) {
63 // Used instance-identifier codec do not support serialization of last path
64 // argument if it is Augmentation (behaviour expected by old datastore)
65 // in this case, we explicitly check if last argument is augmentation
66 // to process it separately
67 if (isAugmentationIdentifier(binding)) {
68 return toNormalizedAugmented(binding);
70 return toNormalizedImpl(binding);
73 public Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>> toNormalizedNode(
74 final InstanceIdentifier<? extends DataObject> bindingPath, final DataObject bindingObject) {
75 return toNormalizedNode(toEntry(bindingPath, bindingObject));
79 public Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>> toNormalizedNode(
80 final Entry<org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject>, DataObject> binding) {
81 Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> legacyEntry = bindingToLegacy
83 Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>> normalizedEntry = legacyToNormalized
84 .toNormalized(legacyEntry);
85 LOG.trace("Serialization of {}, Legacy Representation: {}, Normalized Representation: {}", binding,
86 legacyEntry, normalizedEntry);
87 if (Augmentation.class.isAssignableFrom(binding.getKey().getTargetType())) {
89 for (DataContainerChild<? extends PathArgument, ?> child : ((DataContainerNode<?>) normalizedEntry
90 .getValue()).getValue()) {
91 if (child instanceof AugmentationNode) {
92 ImmutableList<PathArgument> childArgs = ImmutableList.<PathArgument> builder()
93 .addAll(normalizedEntry.getKey().getPath()).add(child.getIdentifier()).build();
94 org.opendaylight.yangtools.yang.data.api.InstanceIdentifier childPath = new org.opendaylight.yangtools.yang.data.api.InstanceIdentifier(
96 return new SimpleEntry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>>(
102 return normalizedEntry;
106 public InstanceIdentifier<? extends DataObject> toBinding(
107 final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized)
108 throws DeserializationException {
110 PathArgument lastArgument = Iterables.getLast(normalized.getPath());
111 // Used instance-identifier codec do not support serialization of last path
112 // argument if it is AugmentationIdentifier (behaviour expected by old datastore)
113 // in this case, we explicitly check if last argument is augmentation
114 // to process it separately
115 if (lastArgument instanceof AugmentationIdentifier) {
116 return toBindingAugmented(normalized);
118 return toBindingImpl(normalized);
121 private InstanceIdentifier<? extends DataObject> toBindingAugmented(
122 final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized) throws DeserializationException {
123 InstanceIdentifier<? extends DataObject> potential = toBindingImpl(normalized);
124 // Shorthand check, if codec already supports deserialization
125 // of AugmentationIdentifier we will return
126 if(isAugmentationIdentifier(potential)) {
130 AugmentationIdentifier lastArgument = (AugmentationIdentifier) Iterables.getLast(normalized.getPath());
132 // Here we employ small trick - Binding-aware Codec injects an pointer to augmentation class
133 // if child is referenced - so we will reference child and then shorten path.
134 for (QName child : lastArgument.getPossibleChildNames()) {
135 org.opendaylight.yangtools.yang.data.api.InstanceIdentifier childPath = new org.opendaylight.yangtools.yang.data.api.InstanceIdentifier(
136 ImmutableList.<PathArgument> builder()
137 .addAll(normalized.getPath()).add(new NodeIdentifier(child)).build());
140 InstanceIdentifier<? extends DataObject> potentialPath = shortenToLastAugment(toBindingImpl(childPath));
141 return potentialPath;
142 } catch (Exception e) {
143 LOG.trace("Unable to deserialize aug. child path for {}",childPath,e);
146 return toBindingImpl(normalized);
149 private InstanceIdentifier<? extends DataObject> toBindingImpl(
150 final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized)
151 throws DeserializationException {
152 org.opendaylight.yangtools.yang.data.api.InstanceIdentifier legacyPath;
154 legacyPath = legacyToNormalized.toLegacy(normalized);
155 } catch (DataNormalizationException e) {
156 throw new IllegalStateException("Could not denormalize path.", e);
158 LOG.trace("InstanceIdentifier Path Deserialization: Legacy representation {}, Normalized representation: {}",
159 legacyPath, normalized);
160 return bindingToLegacy.fromDataDom(legacyPath);
163 private static final Entry<org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject>, DataObject> toEntry(
164 final org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject> key,
165 final DataObject value) {
166 return new SimpleEntry<org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject>, DataObject>(
170 public DataObject toBinding(final InstanceIdentifier<?> path, final NormalizedNode<?, ?> normalizedNode)
171 throws DeserializationException {
172 CompositeNode legacy = null;
173 if(isAugmentationIdentifier(path) && normalizedNode instanceof AugmentationNode) {
174 QName augIdentifier = BindingReflections.findQName(path.getTargetType());
175 ContainerNode virtualNode = Builders.containerBuilder() //
176 .withNodeIdentifier(new NodeIdentifier(augIdentifier)) //
177 .withChild((DataContainerChild<?, ?>) normalizedNode) //
179 legacy = (CompositeNode) DataNormalizer.toLegacy(virtualNode);
181 legacy = (CompositeNode) DataNormalizer.toLegacy(normalizedNode);
184 return bindingToLegacy.dataObjectFromDataDom(path, legacy);
187 public DataNormalizer getDataNormalizer() {
188 return legacyToNormalized;
191 public Entry<org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject>, DataObject> toBinding(
192 final Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, ? extends NormalizedNode<?, ?>> normalized)
193 throws DeserializationException {
194 InstanceIdentifier<? extends DataObject> bindingPath = toBinding(normalized.getKey());
195 DataObject bindingData = toBinding(bindingPath, normalized.getValue());
196 return toEntry(bindingPath, bindingData);
200 public void onGlobalContextUpdated(final SchemaContext arg0) {
201 legacyToNormalized = new DataNormalizer(arg0);
204 private org.opendaylight.yangtools.yang.data.api.InstanceIdentifier toNormalizedAugmented(
205 final InstanceIdentifier<?> augPath) {
206 org.opendaylight.yangtools.yang.data.api.InstanceIdentifier processed = toNormalizedImpl(augPath);
207 // If used instance identifier codec added supports for deserialization
208 // of last AugmentationIdentifier we will just reuse it
209 if(isAugmentationIdentifier(processed)) {
212 // Here we employ small trick - DataNormalizer injecst augmentation identifier if child is
213 // also part of the path (since using a child we can safely identify augmentation)
214 // so, we scan augmentation for children add it to path
215 // and use original algorithm, then shorten it to last augmentation
216 for (@SuppressWarnings("rawtypes") Class augChild : getAugmentationChildren(augPath.getTargetType())) {
217 @SuppressWarnings("unchecked")
218 InstanceIdentifier<?> childPath = augPath.child(augChild);
219 org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized = toNormalizedImpl(childPath);
220 org.opendaylight.yangtools.yang.data.api.InstanceIdentifier potentialDiscovered = shortenToLastAugmentation(normalized);
221 if (potentialDiscovered != null) {
222 return potentialDiscovered;
230 private org.opendaylight.yangtools.yang.data.api.InstanceIdentifier shortenToLastAugmentation(
231 final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized) {
233 int foundPosition = -1;
234 for (PathArgument arg : normalized.getPath()) {
236 if (arg instanceof AugmentationIdentifier) {
237 foundPosition = position;
240 if (foundPosition > 0) {
241 return new org.opendaylight.yangtools.yang.data.api.InstanceIdentifier(normalized.getPath().subList(0,
247 private InstanceIdentifier<? extends DataObject> shortenToLastAugment(
248 final InstanceIdentifier<? extends DataObject> binding) {
250 int foundPosition = -1;
251 for(org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument arg : binding.getPathArguments()) {
253 if (isAugmentation(arg.getType())) {
254 foundPosition = position;
257 return InstanceIdentifier.create(Iterables.limit(binding.getPathArguments(), foundPosition));
262 private org.opendaylight.yangtools.yang.data.api.InstanceIdentifier toNormalizedImpl(
263 final InstanceIdentifier<? extends DataObject> binding) {
264 final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier legacyPath = bindingToLegacy
266 final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized = legacyToNormalized
267 .toNormalized(legacyPath);
271 @SuppressWarnings("unchecked")
272 private Iterable<Class<? extends DataObject>> getAugmentationChildren(final Class<?> targetType) {
273 List<Class<? extends DataObject>> ret = new LinkedList<>();
274 for (Method method : targetType.getMethods()) {
275 Class<?> entity = getYangModeledType(method);
276 if (entity != null) {
277 ret.add((Class<? extends DataObject>) entity);
283 @SuppressWarnings({ "rawtypes", "unchecked" })
284 private Class<? extends DataObject> getYangModeledType(final Method method) {
285 if (method.getName().equals("getClass") || !method.getName().startsWith("get")
286 || method.getParameterTypes().length > 0) {
290 Class<?> returnType = method.getReturnType();
291 if (DataContainer.class.isAssignableFrom(returnType)) {
292 return (Class) returnType;
293 } else if (List.class.isAssignableFrom(returnType)) {
295 return ClassLoaderUtils.withClassLoader(method.getDeclaringClass().getClassLoader(),
296 new Callable<Class>() {
298 @SuppressWarnings("rawtypes")
300 public Class call() throws Exception {
301 Type listResult = ClassLoaderUtils.getFirstGenericParameter(method
302 .getGenericReturnType());
303 if (listResult instanceof Class
304 && DataObject.class.isAssignableFrom((Class) listResult)) {
305 return (Class<?>) listResult;
311 } catch (Exception e) {
312 LOG.debug("Could not get YANG modeled entity for {}", method, e);
320 @SuppressWarnings({ "unchecked", "rawtypes" })
321 private static InstanceIdentifier<?> toWildcarded(final InstanceIdentifier<?> orig) {
322 List<org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument> wildArgs = new LinkedList<>();
323 for (org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument arg : orig.getPathArguments()) {
324 wildArgs.add(new Item(arg.getType()));
326 return InstanceIdentifier.create(wildArgs);
330 private static boolean isAugmentation(final Class<? extends DataObject> type) {
331 return Augmentation.class.isAssignableFrom(type);
334 private static boolean isAugmentationIdentifier(final InstanceIdentifier<?> path) {
335 return Augmentation.class.isAssignableFrom(path.getTargetType());
338 private boolean isAugmentationIdentifier(final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier processed) {
339 return Iterables.getLast(processed.getPath()) instanceof AugmentationIdentifier;