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.yangtools.yang.binding.Augmentable;
26 import org.opendaylight.yangtools.yang.binding.Augmentation;
27 import org.opendaylight.yangtools.yang.binding.DataContainer;
28 import org.opendaylight.yangtools.yang.binding.DataObject;
29 import org.opendaylight.yangtools.yang.binding.Identifiable;
30 import org.opendaylight.yangtools.yang.binding.Identifier;
31 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
32 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.IdentifiableItem;
33 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.Item;
34 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument;
37 public final class DataObjectReadingUtil {
39 private static final DataObjectReadingStrategy REAUSABLE_AUGMENTATION_READING_STRATEGY =
40 new AugmentationReadingStrategy();
42 private DataObjectReadingUtil() {
43 throw new UnsupportedOperationException("Utility class. Instantion is not allowed.");
47 * Read data from parent at specified path.
50 * Parent object on which read operation will be performed
52 * Path, to parent object.
54 * Path, which is nested to parent, and should be read.
55 * @return Value of object.
57 public static <T extends DataObject, P extends DataObject> Map<InstanceIdentifier<T>, T> readData(
58 final P parent, final InstanceIdentifier<P> parentPath, final InstanceIdentifier<T> childPath) {
59 checkArgument(parent != null, "Parent must not be null.");
60 checkArgument(parentPath != null, "Parent path must not be null");
61 checkArgument(childPath != null, "Child path must not be null");
62 checkArgument(parentPath.containsWildcarded(childPath), "Parent object must be parent of child.");
64 List<PathArgument> pathArgs = subList(parentPath.getPathArguments(), childPath.getPathArguments());
65 @SuppressWarnings("rawtypes")
66 Map<InstanceIdentifier, DataContainer> lastFound = Collections.singletonMap(parentPath, parent);
67 for (PathArgument pathArgument : pathArgs) {
68 @SuppressWarnings("rawtypes")
69 final ImmutableMap.Builder<InstanceIdentifier, DataContainer> potentialBuilder = ImmutableMap.builder();
70 for (@SuppressWarnings("rawtypes") Entry<InstanceIdentifier, DataContainer> entry : lastFound.entrySet()) {
71 potentialBuilder.putAll(readData(entry, pathArgument));
73 lastFound = potentialBuilder.build();
74 if (lastFound.isEmpty()) {
75 return Collections.emptyMap();
78 @SuppressWarnings({ "unchecked", "rawtypes" })
79 final Map<InstanceIdentifier<T>, T> result = (Map) lastFound;
83 @SuppressWarnings("rawtypes")
84 private static Map<InstanceIdentifier, DataContainer> readData(final Entry<InstanceIdentifier, DataContainer> entry,
85 final PathArgument pathArgument) {
86 return readData(entry.getValue(), entry.getKey(), pathArgument);
89 public static <T extends DataObject> Optional<T> readData(final DataObject source, final Class<T> child) {
90 checkArgument(source != null, "Object should not be null.");
91 checkArgument(child != null, "Child type should not be null");
92 Class<? extends DataContainer> parentClass = source.getImplementedInterface();
94 @SuppressWarnings("unchecked")
95 T potential = (T) resolveReadStrategy(parentClass, child).read(source, child);
96 return Optional.ofNullable(potential);
99 @SuppressWarnings("rawtypes")
100 private static Map<InstanceIdentifier, DataContainer> readData(final DataContainer parent,
101 final InstanceIdentifier parentPath, final PathArgument child) {
102 checkArgument(parent != null, "Object should not be null.");
103 checkArgument(child != null, "Child argument should not be null");
104 Class<? extends DataContainer> parentClass = parent.getImplementedInterface();
105 return resolveReadStrategy(parentClass, child.getType()).readUsingPathArgument(parent, child, parentPath);
108 private static DataObjectReadingStrategy resolveReadStrategy(final Class<? extends DataContainer> parentClass,
109 final Class<? extends DataContainer> type) {
111 // FIXME: Add caching of strategies
112 return createReadStrategy(parentClass, type);
115 private static DataObjectReadingStrategy createReadStrategy(final Class<? extends DataContainer> parent,
116 final Class<? extends DataContainer> child) {
118 if (Augmentable.class.isAssignableFrom(parent) && Augmentation.class.isAssignableFrom(child)) {
119 return REAUSABLE_AUGMENTATION_READING_STRATEGY;
123 * FIXME Ensure that this strategies also works for children of cases.
124 * Possible edge-case is : Parent container uses grouping foo case is
125 * added by augmentation also uses foo.
127 if (Identifiable.class.isAssignableFrom(child)) {
128 @SuppressWarnings("unchecked")
129 final Class<? extends Identifiable<?>> identifiableClass = (Class<? extends Identifiable<?>>) child;
130 return new ListItemReadingStrategy(parent, identifiableClass);
132 return new ContainerReadingStrategy(parent, child);
135 @SuppressWarnings("rawtypes")
136 private abstract static class DataObjectReadingStrategy {
138 private final Class<? extends DataContainer> parentType;
139 private final Class<? extends DataContainer> childType;
140 private final Method getterMethod;
142 @SuppressWarnings("unchecked")
143 DataObjectReadingStrategy(final Class parentType, final Class childType) {
144 checkArgument(DataContainer.class.isAssignableFrom(parentType));
145 checkArgument(DataContainer.class.isAssignableFrom(childType));
146 this.parentType = parentType;
147 this.childType = childType;
148 this.getterMethod = resolveGetterMethod(parentType, childType);
151 @SuppressWarnings("unchecked")
152 DataObjectReadingStrategy(final Class parentType, final Class childType, final Method getter) {
153 this.parentType = parentType;
154 this.childType = childType;
155 this.getterMethod = getter;
158 @SuppressWarnings("unused")
159 protected Class<? extends DataContainer> getParentType() {
163 protected Class<? extends DataContainer> getChildType() {
167 protected Method getGetterMethod() {
171 public abstract Map<InstanceIdentifier, DataContainer> readUsingPathArgument(DataContainer parent,
172 PathArgument childArgument, InstanceIdentifier targetBuilder);
174 public abstract DataContainer read(DataContainer parent, Class<?> child);
176 private static Method resolveGetterMethod(final Class<? extends DataContainer> parent, final Class<?> child) {
177 String methodName = "get" + child.getSimpleName();
179 return parent.getMethod(methodName);
180 } catch (NoSuchMethodException e) {
181 throw new IllegalArgumentException(e);
182 } catch (SecurityException e) {
183 throw new IllegalStateException(e);
189 @SuppressWarnings("rawtypes")
190 private static final class ContainerReadingStrategy extends DataObjectReadingStrategy {
191 ContainerReadingStrategy(final Class<? extends DataContainer> parent,
192 final Class<? extends DataContainer> child) {
193 super(parent, child);
194 checkArgument(child.isAssignableFrom(getGetterMethod().getReturnType()));
198 public Map<InstanceIdentifier, DataContainer> readUsingPathArgument(final DataContainer parent,
199 final PathArgument childArgument, final InstanceIdentifier parentPath) {
200 final DataContainer result = read(parent, childArgument.getType());
201 if (result != null) {
202 @SuppressWarnings("unchecked")
203 InstanceIdentifier childPath = parentPath.child(childArgument.getType());
204 return Collections.singletonMap(childPath, result);
206 return Collections.emptyMap();
210 public DataContainer read(final DataContainer parent, final Class<?> child) {
212 Object potentialData = getGetterMethod().invoke(parent);
213 checkState(potentialData instanceof DataContainer);
214 return (DataContainer) potentialData;
216 } catch (IllegalAccessException | InvocationTargetException e) {
217 throw new IllegalArgumentException(e);
222 @SuppressWarnings("rawtypes")
223 private static final class ListItemReadingStrategy extends DataObjectReadingStrategy {
224 ListItemReadingStrategy(final Class<? extends DataContainer> parent, final Class child) {
225 super(parent, child);
226 checkArgument(Iterable.class.isAssignableFrom(getGetterMethod().getReturnType()));
230 public DataContainer read(final DataContainer parent, final Class<?> child) {
231 // This will always fail since we do not have key.
235 @SuppressWarnings("unchecked")
237 public Map<InstanceIdentifier, DataContainer> readUsingPathArgument(final DataContainer parent,
238 final PathArgument childArgument, final InstanceIdentifier builder) {
240 Object potentialList = getGetterMethod().invoke(parent);
241 if (potentialList instanceof Iterable) {
242 final Iterable<Identifiable> dataList = (Iterable<Identifiable>) potentialList;
243 return childArgument instanceof IdentifiableItem
244 ? readUsingIdentifiableItem(dataList, (IdentifiableItem) childArgument, builder)
245 : readAll(dataList, builder);
247 } catch (InvocationTargetException | IllegalArgumentException | IllegalAccessException e) {
248 throw new IllegalStateException(e);
250 return Collections.emptyMap();
253 private Map<InstanceIdentifier, DataContainer> readAll(final Iterable<Identifiable> dataList,
254 final InstanceIdentifier parentPath) {
255 Builder<InstanceIdentifier, DataContainer> result = ImmutableMap.builder();
256 for (Identifiable item : dataList) {
257 @SuppressWarnings("unchecked")
258 InstanceIdentifier childPath = parentPath.child(getChildType(), item.key());
259 result.put(childPath, (DataContainer) item);
261 return result.build();
264 @SuppressWarnings("unchecked")
265 private static Map<InstanceIdentifier, DataContainer> readUsingIdentifiableItem(
266 final Iterable<Identifiable> dataList, final IdentifiableItem childArgument,
267 final InstanceIdentifier parentPath) {
268 final Identifier<?> key = childArgument.getKey();
269 for (Identifiable item : dataList) {
270 if (key.equals(item.key()) && item instanceof DataContainer) {
271 checkState(childArgument.getType().isInstance(item),
272 "Found child is not instance of requested type");
273 InstanceIdentifier childPath = parentPath
274 .child(childArgument.getType(), item.key());
275 return Collections.singletonMap(childPath, (DataContainer) item);
278 return Collections.emptyMap();
283 private static final class AugmentationReadingStrategy extends DataObjectReadingStrategy {
284 AugmentationReadingStrategy() {
285 super(Augmentable.class, Augmentation.class, null);
288 @SuppressWarnings("rawtypes")
290 public Map<InstanceIdentifier, DataContainer> readUsingPathArgument(final DataContainer parent,
291 final PathArgument childArgument, final InstanceIdentifier builder) {
292 checkArgument(childArgument instanceof Item, "Path Argument must be Item without keys");
293 DataContainer aug = read(parent, childArgument.getType());
295 return Collections.emptyMap();
298 @SuppressWarnings("unchecked")
299 final InstanceIdentifier childPath = builder.child(childArgument.getType());
300 return Collections.singletonMap(childPath, aug);
304 public DataContainer read(final DataContainer parent, final Class<?> child) {
305 checkArgument(Augmentation.class.isAssignableFrom(child), "Child must be Augmentation.");
306 checkArgument(parent instanceof Augmentable<?>, "Parent must be Augmentable.");
308 @SuppressWarnings({ "rawtypes", "unchecked" })
309 Augmentation potential = ((Augmentable) parent).augmentation(child);
310 checkState(potential instanceof DataContainer, "Readed augmention must be data object");
311 return (DataContainer) potential;
316 * Create sublist view of child from element on [size-of-parent] position to last element.
318 * @return sublist view of child argument
319 * @throws IllegalArgumentException
320 * if parent argument is bigger than child
322 private static <P, C> List<C> subList(final Iterable<P> parent, final Iterable<C> child) {
323 Iterator<P> parentIt = parent.iterator();
324 List<C> result = new ArrayList<>();
325 for (C arg : child) {
326 if (parentIt.hasNext()) {
332 if (parentIt.hasNext()) {
333 throw new IllegalArgumentException("Parent argument is bigger than child.");