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