6eb6293fea4431993bac686c54196eedae20a064
[mdsal.git] / binding2 / prototype / yang-binding2 / src / main / java / org / opendaylight / yangtools / yang / binding / util / DataObjectReadingUtil.java
1 /*
2  * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.yangtools.yang.binding.util;
9
10 import static com.google.common.base.Preconditions.checkArgument;
11 import static com.google.common.base.Preconditions.checkState;
12 import com.google.common.base.Optional;
13 import com.google.common.collect.ImmutableMap;
14 import com.google.common.collect.ImmutableMap.Builder;
15 import java.lang.reflect.InvocationTargetException;
16 import java.lang.reflect.Method;
17 import java.util.ArrayList;
18 import java.util.Collections;
19 import java.util.Iterator;
20 import java.util.List;
21 import java.util.Map;
22 import java.util.Map.Entry;
23 import org.opendaylight.yangtools.yang.binding.Augmentable;
24 import org.opendaylight.yangtools.yang.binding.Augmentation;
25 import org.opendaylight.yangtools.yang.binding.InterfaceTyped;
26 import org.opendaylight.yangtools.yang.binding.TreeNode;
27 import org.opendaylight.yangtools.yang.binding.IdentifiableListItem;
28 import org.opendaylight.yangtools.yang.binding.Identifier;
29 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
30 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.IdentifiableItem;
31 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.Item;
32 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument;
33
34 public class DataObjectReadingUtil {
35
36     private DataObjectReadingUtil() {
37         throw new UnsupportedOperationException("Utility class. Instantion is not allowed.");
38     }
39
40     /**
41      *
42      * @param parent
43      *            Parent object on which read operation will be performed
44      * @param parentPath
45      *            Path, to parent object.
46      * @param childPath
47      *            Path, which is nested to parent, and should be readed.
48      * @return Value of object.
49      */
50     public static final <T extends TreeNode, P extends TreeNode> Map<InstanceIdentifier<T>, T> readData(final P parent,
51                                                                                                         final InstanceIdentifier<P> parentPath, final InstanceIdentifier<T> childPath) {
52         checkArgument(parent != null, "Parent must not be null.");
53         checkArgument(parentPath != null, "Parent path must not be null");
54         checkArgument(childPath != null, "Child path must not be null");
55         checkArgument(parentPath.containsWildcarded(childPath), "Parent object must be parent of child.");
56
57         List<PathArgument> pathArgs = subList(parentPath.getPathArguments(), childPath.getPathArguments());
58         @SuppressWarnings("rawtypes")
59         Map<InstanceIdentifier, InterfaceTyped> lastFound = Collections
60                 .<InstanceIdentifier, InterfaceTyped> singletonMap(parentPath, parent);
61         for (PathArgument pathArgument : pathArgs) {
62             @SuppressWarnings("rawtypes")
63             final ImmutableMap.Builder<InstanceIdentifier, InterfaceTyped> potentialBuilder = ImmutableMap.builder();
64             for (@SuppressWarnings("rawtypes")
65             Entry<InstanceIdentifier, InterfaceTyped> entry : lastFound.entrySet()) {
66                 potentialBuilder.putAll(readData(entry, pathArgument));
67             }
68             lastFound = potentialBuilder.build();
69             if (lastFound.isEmpty()) {
70                 return Collections.emptyMap();
71             }
72         }
73         @SuppressWarnings({ "unchecked", "rawtypes" })
74         final Map<InstanceIdentifier<T>, T> result = (Map) lastFound;
75         return result;
76     }
77
78     @SuppressWarnings("rawtypes")
79     private static Map<InstanceIdentifier, InterfaceTyped> readData(final Entry<InstanceIdentifier, InterfaceTyped> entry,
80                                                                     final PathArgument pathArgument) {
81         return readData(entry.getValue(), entry.getKey(), pathArgument);
82     }
83
84     public static final <T extends TreeNode> Optional<T> readData(final TreeNode source, final Class<T> child) {
85         checkArgument(source != null, "Object should not be null.");
86         checkArgument(child != null, "Child type should not be null");
87         Class<? extends InterfaceTyped> parentClass = source.implementedInterface();
88
89         @SuppressWarnings("unchecked")
90         T potential = (T) resolveReadStrategy(parentClass, child).read(source, child);
91         return Optional.fromNullable(potential);
92     }
93
94     @SuppressWarnings("rawtypes")
95     private static final Map<InstanceIdentifier, InterfaceTyped> readData(final InterfaceTyped parent,
96                                                                           final InstanceIdentifier parentPath, final PathArgument child) {
97         checkArgument(parent != null, "Object should not be null.");
98         checkArgument(child != null, "Child argument should not be null");
99         Class<? extends InterfaceTyped> parentClass = parent.implementedInterface();
100         return resolveReadStrategy(parentClass, child.getType()).readUsingPathArgument(parent, child, parentPath);
101     }
102
103     private static DataObjectReadingStrategy resolveReadStrategy(final Class<? extends InterfaceTyped> parentClass,
104             final Class<? extends InterfaceTyped> type) {
105
106         DataObjectReadingStrategy strategy = createReadStrategy(parentClass, type);
107         // FIXME: Add caching of strategies
108         return strategy;
109     }
110
111     private static DataObjectReadingStrategy createReadStrategy(final Class<? extends InterfaceTyped> parent,
112             final Class<? extends InterfaceTyped> child) {
113
114         if (Augmentable.class.isAssignableFrom(parent) && Augmentation.class.isAssignableFrom(child)) {
115             return REAUSABLE_AUGMENTATION_READING_STRATEGY;
116         }
117
118         /*
119          * FIXME Ensure that this strategies also works for children of cases.
120          * Possible edge-case is : Parent container uses grouping foo case is
121          * added by augmentation also uses foo.
122          */
123         if (IdentifiableListItem.class.isAssignableFrom(child)) {
124             @SuppressWarnings("unchecked")
125             final Class<? extends IdentifiableListItem<?>> identifiableClass = (Class<? extends IdentifiableListItem<?>>) child;
126             return new ListItemReadingStrategy(parent, identifiableClass);
127         }
128         return new ContainerReadingStrategy(parent, child);
129     }
130
131     private static Method resolveGetterMethod(final Class<? extends InterfaceTyped> parent, final Class<?> child) {
132         String methodName = "get" + child.getSimpleName();
133         try {
134             return parent.getMethod(methodName);
135         } catch (NoSuchMethodException e) {
136             throw new IllegalArgumentException(e);
137         } catch (SecurityException e) {
138             throw new IllegalStateException(e);
139         }
140     }
141
142     @SuppressWarnings("rawtypes")
143     private static abstract class DataObjectReadingStrategy {
144
145         private final Class<? extends InterfaceTyped> parentType;
146         private final Class<? extends InterfaceTyped> childType;
147         private final Method getterMethod;
148
149         @SuppressWarnings("unchecked")
150         public DataObjectReadingStrategy(final Class parentType, final Class childType) {
151             checkArgument(InterfaceTyped.class.isAssignableFrom(parentType));
152             checkArgument(InterfaceTyped.class.isAssignableFrom(childType));
153             this.parentType = parentType;
154             this.childType = childType;
155             this.getterMethod = resolveGetterMethod(parentType, childType);
156         }
157
158         @SuppressWarnings("unchecked")
159         public DataObjectReadingStrategy(final Class parentType, final Class childType, final Method getter) {
160             this.parentType = parentType;
161             this.childType = childType;
162             this.getterMethod = getter;
163         }
164
165         @SuppressWarnings("unused")
166         protected Class<? extends InterfaceTyped> getParentType() {
167             return parentType;
168         }
169
170         protected Class<? extends InterfaceTyped> getChildType() {
171             return childType;
172         }
173
174         protected Method getGetterMethod() {
175             return getterMethod;
176         }
177
178         public abstract Map<InstanceIdentifier, InterfaceTyped> readUsingPathArgument(InterfaceTyped parent,
179                                                                                       PathArgument childArgument, InstanceIdentifier targetBuilder);
180
181         public abstract InterfaceTyped read(InterfaceTyped parent, Class<?> childType);
182
183     }
184
185     @SuppressWarnings("rawtypes")
186     private static class ContainerReadingStrategy extends DataObjectReadingStrategy {
187
188         public ContainerReadingStrategy(final Class<? extends InterfaceTyped> parent, final Class<? extends InterfaceTyped> child) {
189             super(parent, child);
190             checkArgument(child.isAssignableFrom(getGetterMethod().getReturnType()));
191         }
192
193         @Override
194         public Map<InstanceIdentifier, InterfaceTyped> readUsingPathArgument(final InterfaceTyped parent,
195                                                                              final PathArgument childArgument, final InstanceIdentifier parentPath) {
196             final InterfaceTyped result = read(parent, childArgument.getType());
197             if (result != null) {
198                 @SuppressWarnings("unchecked")
199                 InstanceIdentifier childPath = parentPath.child(childArgument.getType());
200                 return Collections.singletonMap(childPath, result);
201             }
202             return Collections.emptyMap();
203         }
204
205         @Override
206         public InterfaceTyped read(final InterfaceTyped parent, final Class<?> childType) {
207             try {
208                 Object potentialData = getGetterMethod().invoke(parent);
209                 checkState(potentialData instanceof InterfaceTyped);
210                 return (InterfaceTyped) potentialData;
211
212             } catch (IllegalAccessException | InvocationTargetException e) {
213                 throw new IllegalArgumentException(e);
214             }
215         }
216     }
217
218     @SuppressWarnings("rawtypes")
219     private static class ListItemReadingStrategy extends DataObjectReadingStrategy {
220
221         public ListItemReadingStrategy(final Class<? extends InterfaceTyped> parent, final Class child) {
222             super(parent, child);
223             checkArgument(Iterable.class.isAssignableFrom(getGetterMethod().getReturnType()));
224         }
225
226         @Override
227         public InterfaceTyped read(final InterfaceTyped parent, final Class<?> childType) {
228             // This will always fail since we do not have key.
229             return null;
230         }
231
232         @SuppressWarnings("unchecked")
233         @Override
234         public Map<InstanceIdentifier, InterfaceTyped> readUsingPathArgument(final InterfaceTyped parent,
235                                                                              final PathArgument childArgument, final InstanceIdentifier builder) {
236             try {
237                 Object potentialList = getGetterMethod().invoke(parent);
238                 if (potentialList instanceof Iterable) {
239
240                     final Iterable<IdentifiableListItem> dataList = (Iterable<IdentifiableListItem>) potentialList;
241                     if (childArgument instanceof IdentifiableItem<?, ?>) {
242                         return readUsingIdentifiableItem(dataList, (IdentifiableItem) childArgument, builder);
243                     } else {
244                         return readAll(dataList, builder);
245                     }
246                 }
247             } catch (InvocationTargetException e) {
248                 throw new IllegalStateException(e);
249             } catch (IllegalAccessException e) {
250                 throw new IllegalStateException(e);
251             } catch (IllegalArgumentException e) {
252                 throw new IllegalStateException(e);
253             }
254             return Collections.emptyMap();
255         }
256
257         private Map<InstanceIdentifier, InterfaceTyped> readAll(final Iterable<IdentifiableListItem> dataList,
258                                                                 final InstanceIdentifier parentPath) {
259             Builder<InstanceIdentifier, InterfaceTyped> result = ImmutableMap
260                     .<InstanceIdentifier, InterfaceTyped> builder();
261             for (IdentifiableListItem item : dataList) {
262                 @SuppressWarnings("unchecked")
263                 InstanceIdentifier childPath = parentPath.child(getChildType(), item.identifier());
264                 result.put(childPath, (InterfaceTyped) item);
265             }
266             return result.build();
267         }
268
269         @SuppressWarnings("unchecked")
270         private static Map<InstanceIdentifier, InterfaceTyped> readUsingIdentifiableItem(final Iterable<IdentifiableListItem> dataList,
271                                                                                          final IdentifiableItem childArgument, final InstanceIdentifier parentPath) {
272             final Identifier<?> key = childArgument.getKey();
273             for (IdentifiableListItem item : dataList) {
274                 if (key.equals(item.identifier()) && item instanceof InterfaceTyped) {
275                     checkState(childArgument.getType().isInstance(item),
276                             "Found child is not instance of requested type");
277                     InstanceIdentifier childPath = parentPath
278                             .child(childArgument.getType(), item.identifier());
279                     return Collections.singletonMap(childPath, (InterfaceTyped) item);
280                 }
281             }
282             return Collections.emptyMap();
283         }
284
285     }
286
287     private static final DataObjectReadingStrategy REAUSABLE_AUGMENTATION_READING_STRATEGY = new AugmentationReadingStrategy();
288
289     private static final class AugmentationReadingStrategy extends DataObjectReadingStrategy {
290
291         public AugmentationReadingStrategy() {
292             super(Augmentable.class, Augmentation.class, null);
293         }
294
295         @SuppressWarnings("rawtypes")
296         @Override
297         public Map<InstanceIdentifier, InterfaceTyped> readUsingPathArgument(final InterfaceTyped parent,
298                                                                              final PathArgument childArgument, final InstanceIdentifier builder) {
299             checkArgument(childArgument instanceof Item<?>, "Path Argument must be Item without keys");
300             InterfaceTyped aug = read(parent, childArgument.getType());
301             if (aug != null) {
302                 @SuppressWarnings("unchecked")
303                 final InstanceIdentifier childPath = builder.child(childArgument.getType());
304                 return Collections.singletonMap(childPath, aug);
305             } else {
306                 return Collections.emptyMap();
307             }
308         }
309
310         @Override
311         public InterfaceTyped read(final InterfaceTyped parent, final Class<?> childType) {
312             checkArgument(Augmentation.class.isAssignableFrom(childType), "Parent must be Augmentable.");
313             checkArgument(parent instanceof Augmentable<?>, "Parent must be Augmentable.");
314
315             @SuppressWarnings({ "rawtypes", "unchecked" })
316             Augmentation potential = ((Augmentable) parent).getAugmentation(childType);
317             checkState(potential instanceof InterfaceTyped, "Readed augmention must be data object");
318             return (InterfaceTyped) potential;
319         }
320     }
321
322     /**
323      * Create sublist view of child from element on [size-of-parent] position to
324      * last element.
325      *
326      * @param parent
327      * @param child
328      * @return sublist view of child argument
329      * @throws IllegalArgumentException
330      *             if parent argument is bigger than child
331      */
332     private static <P, C> List<C> subList(final Iterable<P> parent, final Iterable<C> child) {
333         Iterator<P> iParent = parent.iterator();
334         List<C> result = new ArrayList<>();
335         for (C arg : child) {
336             if (iParent.hasNext()) {
337                 iParent.next();
338             } else {
339                 result.add(arg);
340             }
341         }
342         if (iParent.hasNext()) {
343             throw new IllegalArgumentException("Parent argument is bigger than child.");
344         }
345         return result;
346     }
347
348 }