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.yangtools.yang.binding.util;
10 import static com.google.common.base.Preconditions.checkArgument;
11 import static com.google.common.base.Preconditions.checkState;
13 import com.google.common.base.Optional;
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 org.opendaylight.yangtools.yang.binding.Augmentable;
25 import org.opendaylight.yangtools.yang.binding.Augmentation;
26 import org.opendaylight.yangtools.yang.binding.DataContainer;
27 import org.opendaylight.yangtools.yang.binding.DataObject;
28 import org.opendaylight.yangtools.yang.binding.Identifiable;
29 import org.opendaylight.yangtools.yang.binding.Identifier;
30 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
31 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.IdentifiableItem;
32 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.Item;
33 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument;
35 public final class DataObjectReadingUtil {
37 private static final DataObjectReadingStrategy REAUSABLE_AUGMENTATION_READING_STRATEGY =
38 new AugmentationReadingStrategy();
40 private DataObjectReadingUtil() {
41 throw new UnsupportedOperationException("Utility class. Instantion is not allowed.");
45 * Read data from parent at specified path.
48 * Parent object on which read operation will be performed
50 * Path, to parent object.
52 * Path, which is nested to parent, and should be read.
53 * @return Value of object.
55 public static <T extends DataObject, P extends DataObject> Map<InstanceIdentifier<T>, T> readData(
56 final P parent, final InstanceIdentifier<P> parentPath, final InstanceIdentifier<T> childPath) {
57 checkArgument(parent != null, "Parent must not be null.");
58 checkArgument(parentPath != null, "Parent path must not be null");
59 checkArgument(childPath != null, "Child path must not be null");
60 checkArgument(parentPath.containsWildcarded(childPath), "Parent object must be parent of child.");
62 List<PathArgument> pathArgs = subList(parentPath.getPathArguments(), childPath.getPathArguments());
63 @SuppressWarnings("rawtypes")
64 Map<InstanceIdentifier, DataContainer> lastFound = Collections.singletonMap(parentPath, parent);
65 for (PathArgument pathArgument : pathArgs) {
66 @SuppressWarnings("rawtypes")
67 final ImmutableMap.Builder<InstanceIdentifier, DataContainer> potentialBuilder = ImmutableMap.builder();
68 for (@SuppressWarnings("rawtypes") Entry<InstanceIdentifier, DataContainer> entry : lastFound.entrySet()) {
69 potentialBuilder.putAll(readData(entry, pathArgument));
71 lastFound = potentialBuilder.build();
72 if (lastFound.isEmpty()) {
73 return Collections.emptyMap();
76 @SuppressWarnings({ "unchecked", "rawtypes" })
77 final Map<InstanceIdentifier<T>, T> result = (Map) lastFound;
81 @SuppressWarnings("rawtypes")
82 private static Map<InstanceIdentifier, DataContainer> readData(final Entry<InstanceIdentifier, DataContainer> entry,
83 final PathArgument pathArgument) {
84 return readData(entry.getValue(), entry.getKey(), pathArgument);
87 public static <T extends DataObject> Optional<T> readData(final DataObject source, final Class<T> child) {
88 checkArgument(source != null, "Object should not be null.");
89 checkArgument(child != null, "Child type should not be null");
90 Class<? extends DataContainer> parentClass = source.getImplementedInterface();
92 @SuppressWarnings("unchecked")
93 T potential = (T) resolveReadStrategy(parentClass, child).read(source, child);
94 return Optional.fromNullable(potential);
97 @SuppressWarnings("rawtypes")
98 private static Map<InstanceIdentifier, DataContainer> readData(final DataContainer parent,
99 final InstanceIdentifier parentPath, final PathArgument child) {
100 checkArgument(parent != null, "Object should not be null.");
101 checkArgument(child != null, "Child argument should not be null");
102 Class<? extends DataContainer> parentClass = parent.getImplementedInterface();
103 return resolveReadStrategy(parentClass, child.getType()).readUsingPathArgument(parent, child, parentPath);
106 private static DataObjectReadingStrategy resolveReadStrategy(final Class<? extends DataContainer> parentClass,
107 final Class<? extends DataContainer> type) {
109 // FIXME: Add caching of strategies
110 return createReadStrategy(parentClass, type);
113 private static DataObjectReadingStrategy createReadStrategy(final Class<? extends DataContainer> parent,
114 final Class<? extends DataContainer> child) {
116 if (Augmentable.class.isAssignableFrom(parent) && Augmentation.class.isAssignableFrom(child)) {
117 return REAUSABLE_AUGMENTATION_READING_STRATEGY;
121 * FIXME Ensure that this strategies also works for children of cases.
122 * Possible edge-case is : Parent container uses grouping foo case is
123 * added by augmentation also uses foo.
125 if (Identifiable.class.isAssignableFrom(child)) {
126 @SuppressWarnings("unchecked")
127 final Class<? extends Identifiable<?>> identifiableClass = (Class<? extends Identifiable<?>>) child;
128 return new ListItemReadingStrategy(parent, identifiableClass);
130 return new ContainerReadingStrategy(parent, child);
133 @SuppressWarnings("rawtypes")
134 private abstract static class DataObjectReadingStrategy {
136 private final Class<? extends DataContainer> parentType;
137 private final Class<? extends DataContainer> childType;
138 private final Method getterMethod;
140 @SuppressWarnings("unchecked")
141 DataObjectReadingStrategy(final Class parentType, final Class childType) {
142 checkArgument(DataContainer.class.isAssignableFrom(parentType));
143 checkArgument(DataContainer.class.isAssignableFrom(childType));
144 this.parentType = parentType;
145 this.childType = childType;
146 this.getterMethod = resolveGetterMethod(parentType, childType);
149 @SuppressWarnings("unchecked")
150 DataObjectReadingStrategy(final Class parentType, final Class childType, final Method getter) {
151 this.parentType = parentType;
152 this.childType = childType;
153 this.getterMethod = getter;
156 @SuppressWarnings("unused")
157 protected Class<? extends DataContainer> getParentType() {
161 protected Class<? extends DataContainer> getChildType() {
165 protected Method getGetterMethod() {
169 public abstract Map<InstanceIdentifier, DataContainer> readUsingPathArgument(DataContainer parent,
170 PathArgument childArgument, InstanceIdentifier targetBuilder);
172 public abstract DataContainer read(DataContainer parent, Class<?> child);
174 private static Method resolveGetterMethod(final Class<? extends DataContainer> parent, final Class<?> child) {
175 String methodName = "get" + child.getSimpleName();
177 return parent.getMethod(methodName);
178 } catch (NoSuchMethodException e) {
179 throw new IllegalArgumentException(e);
180 } catch (SecurityException e) {
181 throw new IllegalStateException(e);
187 @SuppressWarnings("rawtypes")
188 private static final class ContainerReadingStrategy extends DataObjectReadingStrategy {
189 ContainerReadingStrategy(final Class<? extends DataContainer> parent,
190 final Class<? extends DataContainer> child) {
191 super(parent, child);
192 checkArgument(child.isAssignableFrom(getGetterMethod().getReturnType()));
196 public Map<InstanceIdentifier, DataContainer> readUsingPathArgument(final DataContainer parent,
197 final PathArgument childArgument, final InstanceIdentifier parentPath) {
198 final DataContainer result = read(parent, childArgument.getType());
199 if (result != null) {
200 @SuppressWarnings("unchecked")
201 InstanceIdentifier childPath = parentPath.child(childArgument.getType());
202 return Collections.singletonMap(childPath, result);
204 return Collections.emptyMap();
208 public DataContainer read(final DataContainer parent, final Class<?> child) {
210 Object potentialData = getGetterMethod().invoke(parent);
211 checkState(potentialData instanceof DataContainer);
212 return (DataContainer) potentialData;
214 } catch (IllegalAccessException | InvocationTargetException e) {
215 throw new IllegalArgumentException(e);
220 @SuppressWarnings("rawtypes")
221 private static final class ListItemReadingStrategy extends DataObjectReadingStrategy {
222 ListItemReadingStrategy(final Class<? extends DataContainer> parent, final Class child) {
223 super(parent, child);
224 checkArgument(Iterable.class.isAssignableFrom(getGetterMethod().getReturnType()));
228 public DataContainer read(final DataContainer parent, final Class<?> child) {
229 // This will always fail since we do not have key.
233 @SuppressWarnings("unchecked")
235 public Map<InstanceIdentifier, DataContainer> readUsingPathArgument(final DataContainer parent,
236 final PathArgument childArgument, final InstanceIdentifier builder) {
238 Object potentialList = getGetterMethod().invoke(parent);
239 if (potentialList instanceof Iterable) {
240 final Iterable<Identifiable> dataList = (Iterable<Identifiable>) potentialList;
241 return childArgument instanceof IdentifiableItem
242 ? readUsingIdentifiableItem(dataList, (IdentifiableItem) childArgument, builder)
243 : readAll(dataList, builder);
245 } catch (InvocationTargetException | IllegalArgumentException | IllegalAccessException e) {
246 throw new IllegalStateException(e);
248 return Collections.emptyMap();
251 private Map<InstanceIdentifier, DataContainer> readAll(final Iterable<Identifiable> dataList,
252 final InstanceIdentifier parentPath) {
253 Builder<InstanceIdentifier, DataContainer> result = ImmutableMap.builder();
254 for (Identifiable item : dataList) {
255 @SuppressWarnings("unchecked")
256 InstanceIdentifier childPath = parentPath.child(getChildType(), item.key());
257 result.put(childPath, (DataContainer) item);
259 return result.build();
262 @SuppressWarnings("unchecked")
263 private static Map<InstanceIdentifier, DataContainer> readUsingIdentifiableItem(
264 final Iterable<Identifiable> dataList, final IdentifiableItem childArgument,
265 final InstanceIdentifier parentPath) {
266 final Identifier<?> key = childArgument.getKey();
267 for (Identifiable item : dataList) {
268 if (key.equals(item.key()) && item instanceof DataContainer) {
269 checkState(childArgument.getType().isInstance(item),
270 "Found child is not instance of requested type");
271 InstanceIdentifier childPath = parentPath
272 .child(childArgument.getType(), item.key());
273 return Collections.singletonMap(childPath, (DataContainer) item);
276 return Collections.emptyMap();
281 private static final class AugmentationReadingStrategy extends DataObjectReadingStrategy {
282 AugmentationReadingStrategy() {
283 super(Augmentable.class, Augmentation.class, null);
286 @SuppressWarnings("rawtypes")
288 public Map<InstanceIdentifier, DataContainer> readUsingPathArgument(final DataContainer parent,
289 final PathArgument childArgument, final InstanceIdentifier builder) {
290 checkArgument(childArgument instanceof Item, "Path Argument must be Item without keys");
291 DataContainer aug = read(parent, childArgument.getType());
293 return Collections.emptyMap();
296 @SuppressWarnings("unchecked")
297 final InstanceIdentifier childPath = builder.child(childArgument.getType());
298 return Collections.singletonMap(childPath, aug);
302 public DataContainer read(final DataContainer parent, final Class<?> child) {
303 checkArgument(Augmentation.class.isAssignableFrom(child), "Child must be Augmentation.");
304 checkArgument(parent instanceof Augmentable<?>, "Parent must be Augmentable.");
306 @SuppressWarnings({ "rawtypes", "unchecked" })
307 Augmentation potential = ((Augmentable) parent).augmentation(child);
308 checkState(potential instanceof DataContainer, "Readed augmention must be data object");
309 return (DataContainer) potential;
314 * Create sublist view of child from element on [size-of-parent] position to last element.
316 * @return sublist view of child argument
317 * @throws IllegalArgumentException
318 * if parent argument is bigger than child
320 private static <P, C> List<C> subList(final Iterable<P> parent, final Iterable<C> child) {
321 Iterator<P> parentIt = parent.iterator();
322 List<C> result = new ArrayList<>();
323 for (C arg : child) {
324 if (parentIt.hasNext()) {
330 if (parentIt.hasNext()) {
331 throw new IllegalArgumentException("Parent argument is bigger than child.");