1 package org.opendaylight.yangtools.yang.binding.util;
3 import static com.google.common.base.Preconditions.checkArgument;
4 import static com.google.common.base.Preconditions.checkState;
6 import java.lang.reflect.InvocationTargetException;
7 import java.lang.reflect.Method;
8 import java.util.Collections;
11 import java.util.Map.Entry;
13 import org.opendaylight.yangtools.yang.binding.Augmentable;
14 import org.opendaylight.yangtools.yang.binding.Augmentation;
15 import org.opendaylight.yangtools.yang.binding.DataContainer;
16 import org.opendaylight.yangtools.yang.binding.DataObject;
17 import org.opendaylight.yangtools.yang.binding.Identifiable;
18 import org.opendaylight.yangtools.yang.binding.Identifier;
19 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
20 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.IdentifiableItem;
21 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.Item;
22 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument;
24 import com.google.common.base.Optional;
25 import com.google.common.collect.ImmutableMap;
26 import com.google.common.collect.ImmutableMap.Builder;
28 public class DataObjectReadingUtil {
30 private DataObjectReadingUtil() {
31 throw new UnsupportedOperationException("Utility class. Instantion is not allowed.");
37 * Parent object on which read operation will be performed
39 * Path, to parent object.
41 * Path, which is nested to parent, and should be readed.
42 * @return Value of object.
44 public static final <T extends DataObject, P extends DataObject> Map<InstanceIdentifier<T>, T> readData(P parent,
45 InstanceIdentifier<P> parentPath, InstanceIdentifier<T> childPath) {
46 checkArgument(parent != null, "Parent must not be null.");
47 checkArgument(parentPath != null, "Parent path must not be null");
48 checkArgument(childPath != null, "Child path must not be null");
49 checkArgument(parentPath.containsWildcarded(childPath), "Parent object must be parent of child.");
51 final int commonOffset = parentPath.getPath().size();
52 final int lastIndex = childPath.getPath().size();
53 List<PathArgument> pathArgs = childPath.getPath().subList(commonOffset, lastIndex);
55 @SuppressWarnings("rawtypes")
56 Map<InstanceIdentifier, DataContainer> lastFound = Collections
57 .<InstanceIdentifier, DataContainer> singletonMap(parentPath, parent);
58 for (PathArgument pathArgument : pathArgs) {
59 @SuppressWarnings("rawtypes")
60 final ImmutableMap.Builder<InstanceIdentifier, DataContainer> potentialBuilder = ImmutableMap.builder();
61 for (@SuppressWarnings("rawtypes")
62 Entry<InstanceIdentifier, DataContainer> entry : lastFound.entrySet()) {
63 potentialBuilder.putAll(readData(entry, pathArgument));
65 lastFound = potentialBuilder.build();
66 if (lastFound.isEmpty()) {
67 return Collections.emptyMap();
70 @SuppressWarnings({ "unchecked", "rawtypes" })
71 final Map<InstanceIdentifier<T>, T> result = (Map) lastFound;
75 @SuppressWarnings("rawtypes")
76 private static Map<InstanceIdentifier, DataContainer> readData(Entry<InstanceIdentifier, DataContainer> entry,
77 PathArgument pathArgument) {
78 return readData(entry.getValue(), entry.getKey(), pathArgument);
81 public static final <T extends DataObject> Optional<T> readData(DataObject source, Class<T> child) {
82 checkArgument(source != null, "Object should not be null.");
83 checkArgument(child != null, "Child type should not be null");
84 Class<? extends DataContainer> parentClass = source.getImplementedInterface();
86 @SuppressWarnings("unchecked")
87 T potential = (T) resolveReadStrategy(parentClass, child).read(source, child);
88 return Optional.fromNullable(potential);
91 @SuppressWarnings("rawtypes")
92 private static final Map<InstanceIdentifier, DataContainer> readData(DataContainer parent,
93 InstanceIdentifier parentPath, PathArgument child) {
94 checkArgument(parent != null, "Object should not be null.");
95 checkArgument(child != null, "Child argument should not be null");
96 Class<? extends DataContainer> parentClass = parent.getImplementedInterface();
97 return resolveReadStrategy(parentClass, child.getType()).readUsingPathArgument(parent, child, parentPath);
100 private static DataObjectReadingStrategy resolveReadStrategy(Class<? extends DataContainer> parentClass,
101 Class<? extends DataContainer> type) {
103 DataObjectReadingStrategy strategy = createReadStrategy(parentClass, type);
104 // FIXME: Add caching of strategies
108 private static DataObjectReadingStrategy createReadStrategy(Class<? extends DataContainer> parent,
109 Class<? extends DataContainer> child) {
111 if (Augmentable.class.isAssignableFrom(parent) && Augmentation.class.isAssignableFrom(child)) {
112 return REAUSABLE_AUGMENTATION_READING_STRATEGY;
116 * FIXME Ensure that this strategies also works for children of cases.
117 * Possible edge-case is : Parent container uses grouping foo case is
118 * added by augmentation also uses foo.
120 if (Identifiable.class.isAssignableFrom(child)) {
121 @SuppressWarnings("unchecked")
122 final Class<? extends Identifiable<?>> identifiableClass = (Class<? extends Identifiable<?>>) child;
123 return new ListItemReadingStrategy(parent, identifiableClass);
125 return new ContainerReadingStrategy(parent, child);
128 private static Method resolveGetterMethod(Class<? extends DataContainer> parent, Class<?> child) {
129 String methodName = "get" + child.getSimpleName();
131 return parent.getMethod(methodName);
132 } catch (NoSuchMethodException e) {
133 throw new IllegalArgumentException(e);
134 } catch (SecurityException e) {
135 throw new IllegalStateException(e);
139 @SuppressWarnings("rawtypes")
140 private static abstract class DataObjectReadingStrategy {
142 private final Class<? extends DataContainer> parentType;
143 private final Class<? extends DataContainer> childType;
144 private final Method getterMethod;
146 @SuppressWarnings("unchecked")
147 public DataObjectReadingStrategy(Class parentType, Class childType) {
149 checkArgument(DataContainer.class.isAssignableFrom(parentType));
150 checkArgument(DataContainer.class.isAssignableFrom(childType));
151 this.parentType = parentType;
152 this.childType = childType;
153 this.getterMethod = resolveGetterMethod(parentType, childType);
156 @SuppressWarnings("unchecked")
157 public DataObjectReadingStrategy(Class parentType, Class childType, Method getter) {
158 this.parentType = parentType;
159 this.childType = childType;
160 this.getterMethod = getter;
163 @SuppressWarnings("unused")
164 protected Class<? extends DataContainer> getParentType() {
168 protected Class<? extends DataContainer> getChildType() {
172 protected Method getGetterMethod() {
176 abstract public Map<InstanceIdentifier, DataContainer> readUsingPathArgument(DataContainer parent,
177 PathArgument childArgument, InstanceIdentifier targetBuilder);
179 abstract public DataContainer read(DataContainer parent, Class<?> childType);
183 @SuppressWarnings("rawtypes")
184 private static class ContainerReadingStrategy extends DataObjectReadingStrategy {
186 public ContainerReadingStrategy(Class<? extends DataContainer> parent, Class<? extends DataContainer> child) {
187 super(parent, child);
188 checkArgument(child.isAssignableFrom(getGetterMethod().getReturnType()));
192 public Map<InstanceIdentifier, DataContainer> readUsingPathArgument(DataContainer parent,
193 PathArgument childArgument, InstanceIdentifier parentPath) {
194 final DataContainer result = read(parent, childArgument.getType());
195 if (result != null) {
196 @SuppressWarnings("unchecked")
197 InstanceIdentifier childPath = parentPath.child(childArgument.getType());
198 return Collections.singletonMap(childPath, result);
200 return Collections.emptyMap();
204 public DataContainer read(DataContainer parent, Class<?> childType) {
206 Object potentialData = getGetterMethod().invoke(parent);
207 checkState(potentialData instanceof DataContainer);
208 return (DataContainer) potentialData;
210 } catch (IllegalAccessException | InvocationTargetException e) {
211 throw new IllegalArgumentException(e);
216 @SuppressWarnings("rawtypes")
217 private static class ListItemReadingStrategy extends DataObjectReadingStrategy {
219 public ListItemReadingStrategy(Class<? extends DataContainer> parent, Class child) {
220 super(parent, child);
221 checkArgument(Iterable.class.isAssignableFrom(getGetterMethod().getReturnType()));
225 public DataContainer read(DataContainer parent, Class<?> childType) {
226 // This will always fail since we do not have key.
230 @SuppressWarnings("unchecked")
232 public Map<InstanceIdentifier, DataContainer> readUsingPathArgument(DataContainer parent,
233 PathArgument childArgument, InstanceIdentifier builder) {
235 Object potentialList = getGetterMethod().invoke(parent);
236 if (potentialList instanceof Iterable) {
238 final Iterable<Identifiable> dataList = (Iterable<Identifiable>) potentialList;
239 if (childArgument instanceof IdentifiableItem<?, ?>) {
240 return readUsingIdentifiableItem(dataList, (IdentifiableItem) childArgument, builder);
242 return readAll(dataList, builder);
245 } catch (InvocationTargetException e) {
246 throw new IllegalStateException(e);
247 } catch (IllegalAccessException e) {
248 throw new IllegalStateException(e);
249 } catch (IllegalArgumentException e) {
250 throw new IllegalStateException(e);
252 return Collections.emptyMap();
255 private Map<InstanceIdentifier, DataContainer> readAll(Iterable<Identifiable> dataList,
256 InstanceIdentifier parentPath) {
257 Builder<InstanceIdentifier, DataContainer> result = ImmutableMap
258 .<InstanceIdentifier, DataContainer> builder();
259 for (Identifiable item : dataList) {
260 @SuppressWarnings("unchecked")
261 InstanceIdentifier childPath = parentPath.child(getChildType(), item.getKey());
262 result.put(childPath, (DataContainer) item);
264 return result.build();
267 @SuppressWarnings("unchecked")
268 private Map<InstanceIdentifier, DataContainer> readUsingIdentifiableItem(Iterable<Identifiable> dataList,
269 IdentifiableItem childArgument, InstanceIdentifier parentPath) {
270 final Identifier<?> key = childArgument.getKey();
271 for (Identifiable item : dataList) {
272 if (key.equals(item.getKey()) && item instanceof DataContainer) {
273 checkState(childArgument.getType().isInstance(item),
274 "Found child is not instance of requested type");
275 InstanceIdentifier childPath = parentPath
276 .child(childArgument.getType(), item.getKey());
277 return Collections.singletonMap(childPath, (DataContainer) item);
280 return Collections.emptyMap();
285 private static final DataObjectReadingStrategy REAUSABLE_AUGMENTATION_READING_STRATEGY = new AugmentationReadingStrategy();
287 private static final class AugmentationReadingStrategy extends DataObjectReadingStrategy {
289 public AugmentationReadingStrategy() {
290 super(Augmentable.class, Augmentation.class, null);
293 @SuppressWarnings("rawtypes")
295 public Map<InstanceIdentifier, DataContainer> readUsingPathArgument(DataContainer parent,
296 PathArgument childArgument, InstanceIdentifier builder) {
297 checkArgument(childArgument instanceof Item<?>, "Path Argument must be Item without keys");
298 DataContainer aug = read(parent, childArgument.getType());
300 @SuppressWarnings("unchecked")
301 final InstanceIdentifier childPath = builder.child(childArgument.getType());
302 return Collections.singletonMap(childPath, aug);
304 return Collections.emptyMap();
309 public DataContainer read(DataContainer parent, Class<?> childType) {
310 checkArgument(Augmentation.class.isAssignableFrom(childType), "Parent must be Augmentable.");
311 checkArgument(parent instanceof Augmentable<?>, "Parent must be Augmentable.");
313 @SuppressWarnings({ "rawtypes", "unchecked" })
314 Augmentation potential = ((Augmentable) parent).getAugmentation(childType);
315 checkState(potential instanceof DataContainer, "Readed augmention must be data object");
316 return (DataContainer) potential;