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