Fix various eclipse warnings
[mdsal.git] / binding / yang-binding / src / main / java / org / opendaylight / yangtools / yang / binding / util / DataObjectReadingUtil.java
1 /*
2  * Copyright (c) 2014 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.DataContainer;
26 import org.opendaylight.yangtools.yang.binding.DataObject;
27 import org.opendaylight.yangtools.yang.binding.Identifiable;
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 DataObject, P extends DataObject> 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, DataContainer> lastFound = Collections
60                 .<InstanceIdentifier, DataContainer> singletonMap(parentPath, parent);
61         for (PathArgument pathArgument : pathArgs) {
62             @SuppressWarnings("rawtypes")
63             final ImmutableMap.Builder<InstanceIdentifier, DataContainer> potentialBuilder = ImmutableMap.builder();
64             for (@SuppressWarnings("rawtypes")
65             Entry<InstanceIdentifier, DataContainer> 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, DataContainer> readData(final Entry<InstanceIdentifier, DataContainer> entry,
80             final PathArgument pathArgument) {
81         return readData(entry.getValue(), entry.getKey(), pathArgument);
82     }
83
84     public static final <T extends DataObject> Optional<T> readData(final DataObject 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 DataContainer> parentClass = source.getImplementedInterface();
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, DataContainer> readData(final DataContainer 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 DataContainer> parentClass = parent.getImplementedInterface();
100         return resolveReadStrategy(parentClass, child.getType()).readUsingPathArgument(parent, child, parentPath);
101     }
102
103     private static DataObjectReadingStrategy resolveReadStrategy(final Class<? extends DataContainer> parentClass,
104             final Class<? extends DataContainer> 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 DataContainer> parent,
112             final Class<? extends DataContainer> 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 (Identifiable.class.isAssignableFrom(child)) {
124             @SuppressWarnings("unchecked")
125             final Class<? extends Identifiable<?>> identifiableClass = (Class<? extends Identifiable<?>>) child;
126             return new ListItemReadingStrategy(parent, identifiableClass);
127         }
128         return new ContainerReadingStrategy(parent, child);
129     }
130
131     private static Method resolveGetterMethod(final Class<? extends DataContainer> 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 DataContainer> parentType;
146         private final Class<? extends DataContainer> childType;
147         private final Method getterMethod;
148
149         @SuppressWarnings("unchecked")
150         public DataObjectReadingStrategy(final Class parentType, final Class childType) {
151             super();
152             checkArgument(DataContainer.class.isAssignableFrom(parentType));
153             checkArgument(DataContainer.class.isAssignableFrom(childType));
154             this.parentType = parentType;
155             this.childType = childType;
156             this.getterMethod = resolveGetterMethod(parentType, childType);
157         }
158
159         @SuppressWarnings("unchecked")
160         public DataObjectReadingStrategy(final Class parentType, final Class childType, final Method getter) {
161             this.parentType = parentType;
162             this.childType = childType;
163             this.getterMethod = getter;
164         }
165
166         @SuppressWarnings("unused")
167         protected Class<? extends DataContainer> getParentType() {
168             return parentType;
169         }
170
171         protected Class<? extends DataContainer> getChildType() {
172             return childType;
173         }
174
175         protected Method getGetterMethod() {
176             return getterMethod;
177         }
178
179         public abstract Map<InstanceIdentifier, DataContainer> readUsingPathArgument(DataContainer parent,
180                 PathArgument childArgument, InstanceIdentifier targetBuilder);
181
182         public abstract DataContainer read(DataContainer parent, Class<?> childType);
183
184     }
185
186     @SuppressWarnings("rawtypes")
187     private static class ContainerReadingStrategy extends DataObjectReadingStrategy {
188
189         public ContainerReadingStrategy(final Class<? extends DataContainer> parent, final Class<? extends DataContainer> child) {
190             super(parent, child);
191             checkArgument(child.isAssignableFrom(getGetterMethod().getReturnType()));
192         }
193
194         @Override
195         public Map<InstanceIdentifier, DataContainer> readUsingPathArgument(final DataContainer parent,
196                 final PathArgument childArgument, final InstanceIdentifier parentPath) {
197             final DataContainer result = read(parent, childArgument.getType());
198             if (result != null) {
199                 @SuppressWarnings("unchecked")
200                 InstanceIdentifier childPath = parentPath.child(childArgument.getType());
201                 return Collections.singletonMap(childPath, result);
202             }
203             return Collections.emptyMap();
204         }
205
206         @Override
207         public DataContainer read(final DataContainer parent, final Class<?> childType) {
208             try {
209                 Object potentialData = getGetterMethod().invoke(parent);
210                 checkState(potentialData instanceof DataContainer);
211                 return (DataContainer) potentialData;
212
213             } catch (IllegalAccessException | InvocationTargetException e) {
214                 throw new IllegalArgumentException(e);
215             }
216         }
217     }
218
219     @SuppressWarnings("rawtypes")
220     private static class ListItemReadingStrategy extends DataObjectReadingStrategy {
221
222         public ListItemReadingStrategy(final Class<? extends DataContainer> parent, final Class child) {
223             super(parent, child);
224             checkArgument(Iterable.class.isAssignableFrom(getGetterMethod().getReturnType()));
225         }
226
227         @Override
228         public DataContainer read(final DataContainer parent, final Class<?> childType) {
229             // This will always fail since we do not have key.
230             return null;
231         }
232
233         @SuppressWarnings("unchecked")
234         @Override
235         public Map<InstanceIdentifier, DataContainer> readUsingPathArgument(final DataContainer parent,
236                 final PathArgument childArgument, final InstanceIdentifier builder) {
237             try {
238                 Object potentialList = getGetterMethod().invoke(parent);
239                 if (potentialList instanceof Iterable) {
240
241                     final Iterable<Identifiable> dataList = (Iterable<Identifiable>) potentialList;
242                     if (childArgument instanceof IdentifiableItem<?, ?>) {
243                         return readUsingIdentifiableItem(dataList, (IdentifiableItem) childArgument, builder);
244                     } else {
245                         return readAll(dataList, builder);
246                     }
247                 }
248             } catch (InvocationTargetException e) {
249                 throw new IllegalStateException(e);
250             } catch (IllegalAccessException e) {
251                 throw new IllegalStateException(e);
252             } catch (IllegalArgumentException e) {
253                 throw new IllegalStateException(e);
254             }
255             return Collections.emptyMap();
256         }
257
258         private Map<InstanceIdentifier, DataContainer> readAll(final Iterable<Identifiable> dataList,
259                 final InstanceIdentifier parentPath) {
260             Builder<InstanceIdentifier, DataContainer> result = ImmutableMap
261                     .<InstanceIdentifier, DataContainer> builder();
262             for (Identifiable item : dataList) {
263                 @SuppressWarnings("unchecked")
264                 InstanceIdentifier childPath = parentPath.child(getChildType(), item.getKey());
265                 result.put(childPath, (DataContainer) item);
266             }
267             return result.build();
268         }
269
270         @SuppressWarnings("unchecked")
271         private static Map<InstanceIdentifier, DataContainer> readUsingIdentifiableItem(final Iterable<Identifiable> dataList,
272                 final IdentifiableItem childArgument, final InstanceIdentifier parentPath) {
273             final Identifier<?> key = childArgument.getKey();
274             for (Identifiable item : dataList) {
275                 if (key.equals(item.getKey()) && item instanceof DataContainer) {
276                     checkState(childArgument.getType().isInstance(item),
277                             "Found child is not instance of requested type");
278                     InstanceIdentifier childPath = parentPath
279                             .child(childArgument.getType(), item.getKey());
280                     return Collections.singletonMap(childPath, (DataContainer) item);
281                 }
282             }
283             return Collections.emptyMap();
284         }
285
286     }
287
288     private static final DataObjectReadingStrategy REAUSABLE_AUGMENTATION_READING_STRATEGY = new AugmentationReadingStrategy();
289
290     private static final class AugmentationReadingStrategy extends DataObjectReadingStrategy {
291
292         public AugmentationReadingStrategy() {
293             super(Augmentable.class, Augmentation.class, null);
294         }
295
296         @SuppressWarnings("rawtypes")
297         @Override
298         public Map<InstanceIdentifier, DataContainer> readUsingPathArgument(final DataContainer parent,
299                 final PathArgument childArgument, final InstanceIdentifier builder) {
300             checkArgument(childArgument instanceof Item<?>, "Path Argument must be Item without keys");
301             DataContainer aug = read(parent, childArgument.getType());
302             if (aug != null) {
303                 @SuppressWarnings("unchecked")
304                 final InstanceIdentifier childPath = builder.child(childArgument.getType());
305                 return Collections.singletonMap(childPath, aug);
306             } else {
307                 return Collections.emptyMap();
308             }
309         }
310
311         @Override
312         public DataContainer read(final DataContainer parent, final Class<?> childType) {
313             checkArgument(Augmentation.class.isAssignableFrom(childType), "Parent must be Augmentable.");
314             checkArgument(parent instanceof Augmentable<?>, "Parent must be Augmentable.");
315
316             @SuppressWarnings({ "rawtypes", "unchecked" })
317             Augmentation potential = ((Augmentable) parent).getAugmentation(childType);
318             checkState(potential instanceof DataContainer, "Readed augmention must be data object");
319             return (DataContainer) potential;
320         }
321     }
322
323     /**
324      * Create sublist view of child from element on [size-of-parent] position to
325      * last element.
326      *
327      * @param parent
328      * @param child
329      * @return sublist view of child argument
330      * @throws IllegalArgumentException
331      *             if parent argument is bigger than child
332      */
333     private static <P, C> List<C> subList(final Iterable<P> parent, final Iterable<C> child) {
334         Iterator<P> iParent = parent.iterator();
335         List<C> result = new ArrayList<>();
336         for (C arg : child) {
337             if (iParent.hasNext()) {
338                 iParent.next();
339             } else {
340                 result.add(arg);
341             }
342         }
343         if (iParent.hasNext()) {
344             throw new IllegalArgumentException("Parent argument is bigger than child.");
345         }
346         return result;
347     }
348
349 }