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.mdsal.binding.spec.util;
10 import static com.google.common.base.Preconditions.checkArgument;
11 import static com.google.common.base.Preconditions.checkState;
13 import com.google.common.annotations.Beta;
14 import com.google.common.collect.ImmutableMap;
15 import com.google.common.collect.ImmutableMap.Builder;
16 import java.lang.reflect.InvocationTargetException;
17 import java.lang.reflect.Method;
18 import java.util.ArrayList;
19 import java.util.Collections;
20 import java.util.Iterator;
21 import java.util.List;
23 import java.util.Map.Entry;
24 import java.util.Optional;
25 import org.opendaylight.mdsal.binding.spec.naming.BindingMapping;
26 import org.opendaylight.yangtools.yang.binding.Augmentable;
27 import org.opendaylight.yangtools.yang.binding.Augmentation;
28 import org.opendaylight.yangtools.yang.binding.DataContainer;
29 import org.opendaylight.yangtools.yang.binding.DataObject;
30 import org.opendaylight.yangtools.yang.binding.Identifiable;
31 import org.opendaylight.yangtools.yang.binding.Identifier;
32 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
33 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.IdentifiableItem;
34 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.Item;
35 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument;
38 public final class DataObjectReadingUtil {
40 private static final DataObjectReadingStrategy REAUSABLE_AUGMENTATION_READING_STRATEGY =
41 new AugmentationReadingStrategy();
43 private DataObjectReadingUtil() {
48 * Read data from parent at specified path.
51 * Parent object on which read operation will be performed
53 * Path, to parent object.
55 * Path, which is nested to parent, and should be read.
56 * @return Value of object.
58 public static <T extends DataObject, P extends DataObject> Map<InstanceIdentifier<T>, T> readData(
59 final P parent, final InstanceIdentifier<P> parentPath, final InstanceIdentifier<T> childPath) {
60 checkArgument(parent != null, "Parent must not be null.");
61 checkArgument(parentPath != null, "Parent path must not be null");
62 checkArgument(childPath != null, "Child path must not be null");
63 checkArgument(parentPath.containsWildcarded(childPath), "Parent object must be parent of child.");
65 List<PathArgument> pathArgs = subList(parentPath.getPathArguments(), childPath.getPathArguments());
66 @SuppressWarnings("rawtypes")
67 Map<InstanceIdentifier, DataContainer> lastFound = Collections.singletonMap(parentPath, parent);
68 for (PathArgument pathArgument : pathArgs) {
69 @SuppressWarnings("rawtypes")
70 final ImmutableMap.Builder<InstanceIdentifier, DataContainer> potentialBuilder = ImmutableMap.builder();
71 for (@SuppressWarnings("rawtypes") Entry<InstanceIdentifier, DataContainer> entry : lastFound.entrySet()) {
72 potentialBuilder.putAll(readData(entry, pathArgument));
74 lastFound = potentialBuilder.build();
75 if (lastFound.isEmpty()) {
76 return Collections.emptyMap();
79 @SuppressWarnings({ "unchecked", "rawtypes" })
80 final Map<InstanceIdentifier<T>, T> result = (Map) lastFound;
84 @SuppressWarnings("rawtypes")
85 private static Map<InstanceIdentifier, DataContainer> readData(final Entry<InstanceIdentifier, DataContainer> entry,
86 final PathArgument pathArgument) {
87 return readData(entry.getValue(), entry.getKey(), pathArgument);
90 public static <T extends DataObject> Optional<T> readData(final DataObject source, final Class<T> child) {
91 checkArgument(source != null, "Object should not be null.");
92 checkArgument(child != null, "Child type should not be null");
93 Class<? extends DataContainer> parentClass = source.implementedInterface();
95 @SuppressWarnings("unchecked")
96 T potential = (T) resolveReadStrategy(parentClass, child).read(source, child);
97 return Optional.ofNullable(potential);
100 @SuppressWarnings("rawtypes")
101 private static Map<InstanceIdentifier, DataContainer> readData(final DataContainer parent,
102 final InstanceIdentifier parentPath, final PathArgument child) {
103 checkArgument(parent != null, "Object should not be null.");
104 checkArgument(child != null, "Child argument should not be null");
105 Class<? extends DataContainer> parentClass = parent.implementedInterface();
106 return resolveReadStrategy(parentClass, child.getType()).readUsingPathArgument(parent, child, parentPath);
109 private static DataObjectReadingStrategy resolveReadStrategy(final Class<? extends DataContainer> parentClass,
110 final Class<? extends DataContainer> type) {
112 // FIXME: Add caching of strategies
113 return createReadStrategy(parentClass, type);
116 private static DataObjectReadingStrategy createReadStrategy(final Class<? extends DataContainer> parent,
117 final Class<? extends DataContainer> child) {
119 if (Augmentable.class.isAssignableFrom(parent) && Augmentation.class.isAssignableFrom(child)) {
120 return REAUSABLE_AUGMENTATION_READING_STRATEGY;
124 * FIXME Ensure that this strategies also works for children of cases.
125 * Possible edge-case is : Parent container uses grouping foo case is
126 * added by augmentation also uses foo.
128 if (Identifiable.class.isAssignableFrom(child)) {
129 @SuppressWarnings("unchecked")
130 final Class<? extends Identifiable<?>> identifiableClass = (Class<? extends Identifiable<?>>) child;
131 return new ListItemReadingStrategy(parent, identifiableClass);
133 return new ContainerReadingStrategy(parent, child);
136 @SuppressWarnings("rawtypes")
137 private abstract static class DataObjectReadingStrategy {
139 private final Class<? extends DataContainer> parentType;
140 private final Class<? extends DataContainer> childType;
141 private final Method getterMethod;
143 @SuppressWarnings("unchecked")
144 DataObjectReadingStrategy(final Class parentType, final Class childType) {
145 checkArgument(DataContainer.class.isAssignableFrom(parentType));
146 checkArgument(DataContainer.class.isAssignableFrom(childType));
147 this.parentType = parentType;
148 this.childType = childType;
149 this.getterMethod = resolveGetterMethod(parentType, childType);
152 @SuppressWarnings("unchecked")
153 DataObjectReadingStrategy(final Class parentType, final Class childType, final Method getter) {
154 this.parentType = parentType;
155 this.childType = childType;
156 this.getterMethod = getter;
159 @SuppressWarnings("unused")
160 protected Class<? extends DataContainer> getParentType() {
164 protected Class<? extends DataContainer> getChildType() {
168 protected Method getGetterMethod() {
172 public abstract Map<InstanceIdentifier, DataContainer> readUsingPathArgument(DataContainer parent,
173 PathArgument childArgument, InstanceIdentifier targetBuilder);
175 public abstract DataContainer read(DataContainer parent, Class<?> child);
177 private static Method resolveGetterMethod(final Class<? extends DataContainer> parent, final Class<?> child) {
178 String methodName = BindingMapping.GETTER_PREFIX + child.getSimpleName();
180 return parent.getMethod(methodName);
181 } catch (NoSuchMethodException e) {
182 throw new IllegalArgumentException(e);
183 } catch (SecurityException e) {
184 throw new IllegalStateException(e);
190 @SuppressWarnings("rawtypes")
191 private static final class ContainerReadingStrategy extends DataObjectReadingStrategy {
192 ContainerReadingStrategy(final Class<? extends DataContainer> parent,
193 final Class<? extends DataContainer> child) {
194 super(parent, child);
195 checkArgument(child.isAssignableFrom(getGetterMethod().getReturnType()));
199 public Map<InstanceIdentifier, DataContainer> readUsingPathArgument(final DataContainer parent,
200 final PathArgument childArgument, final InstanceIdentifier parentPath) {
201 final DataContainer result = read(parent, childArgument.getType());
202 if (result != null) {
203 @SuppressWarnings("unchecked")
204 InstanceIdentifier childPath = parentPath.child(childArgument.getType());
205 return Collections.singletonMap(childPath, result);
207 return Collections.emptyMap();
211 public DataContainer read(final DataContainer parent, final Class<?> child) {
213 Object potentialData = getGetterMethod().invoke(parent);
214 checkState(potentialData instanceof DataContainer);
215 return (DataContainer) potentialData;
217 } catch (IllegalAccessException | InvocationTargetException e) {
218 throw new IllegalArgumentException(e);
223 @SuppressWarnings("rawtypes")
224 private static final class ListItemReadingStrategy extends DataObjectReadingStrategy {
225 ListItemReadingStrategy(final Class<? extends DataContainer> parent, final Class child) {
226 super(parent, child);
227 checkArgument(Iterable.class.isAssignableFrom(getGetterMethod().getReturnType()));
231 public DataContainer read(final DataContainer parent, final Class<?> child) {
232 // This will always fail since we do not have key.
236 @SuppressWarnings("unchecked")
238 public Map<InstanceIdentifier, DataContainer> readUsingPathArgument(final DataContainer parent,
239 final PathArgument childArgument, final InstanceIdentifier builder) {
241 Object potentialList = getGetterMethod().invoke(parent);
242 if (potentialList instanceof Iterable) {
243 final Iterable<Identifiable> dataList = (Iterable<Identifiable>) potentialList;
244 return childArgument instanceof IdentifiableItem
245 ? readUsingIdentifiableItem(dataList, (IdentifiableItem) childArgument, builder)
246 : readAll(dataList, builder);
248 } catch (InvocationTargetException | IllegalArgumentException | IllegalAccessException e) {
249 throw new IllegalStateException(e);
251 return Collections.emptyMap();
254 private Map<InstanceIdentifier, DataContainer> readAll(final Iterable<Identifiable> dataList,
255 final InstanceIdentifier parentPath) {
256 Builder<InstanceIdentifier, DataContainer> result = ImmutableMap.builder();
257 for (Identifiable item : dataList) {
258 @SuppressWarnings("unchecked")
259 InstanceIdentifier childPath = parentPath.child(getChildType(), item.key());
260 result.put(childPath, (DataContainer) item);
262 return result.build();
265 @SuppressWarnings("unchecked")
266 private static Map<InstanceIdentifier, DataContainer> readUsingIdentifiableItem(
267 final Iterable<Identifiable> dataList, final IdentifiableItem childArgument,
268 final InstanceIdentifier parentPath) {
269 final Identifier<?> key = childArgument.getKey();
270 for (Identifiable item : dataList) {
271 if (key.equals(item.key()) && item instanceof DataContainer) {
272 checkState(childArgument.getType().isInstance(item),
273 "Found child is not instance of requested type");
274 InstanceIdentifier childPath = parentPath
275 .child(childArgument.getType(), item.key());
276 return Collections.singletonMap(childPath, (DataContainer) item);
279 return Collections.emptyMap();
284 private static final class AugmentationReadingStrategy extends DataObjectReadingStrategy {
285 AugmentationReadingStrategy() {
286 super(Augmentable.class, Augmentation.class, null);
289 @SuppressWarnings("rawtypes")
291 public Map<InstanceIdentifier, DataContainer> readUsingPathArgument(final DataContainer parent,
292 final PathArgument childArgument, final InstanceIdentifier builder) {
293 checkArgument(childArgument instanceof Item, "Path Argument must be Item without keys");
294 DataContainer aug = read(parent, childArgument.getType());
296 return Collections.emptyMap();
299 @SuppressWarnings("unchecked")
300 final InstanceIdentifier childPath = builder.child(childArgument.getType());
301 return Collections.singletonMap(childPath, aug);
305 public DataContainer read(final DataContainer parent, final Class<?> child) {
306 checkArgument(Augmentation.class.isAssignableFrom(child), "Child must be Augmentation.");
307 checkArgument(parent instanceof Augmentable<?>, "Parent must be Augmentable.");
309 @SuppressWarnings({ "rawtypes", "unchecked" })
310 Augmentation potential = ((Augmentable) parent).augmentation(child);
311 checkState(potential instanceof DataContainer, "Readed augmention must be data object");
312 return (DataContainer) potential;
317 * Create sublist view of child from element on [size-of-parent] position to last element.
319 * @return sublist view of child argument
320 * @throws IllegalArgumentException
321 * if parent argument is bigger than child
323 private static <P, C> List<C> subList(final Iterable<P> parent, final Iterable<C> child) {
324 Iterator<P> parentIt = parent.iterator();
325 List<C> result = new ArrayList<>();
326 for (C arg : child) {
327 if (parentIt.hasNext()) {
333 if (parentIt.hasNext()) {
334 throw new IllegalArgumentException("Parent argument is bigger than child.");