Rework AugmentRuntimeType and Choice/Case linkage
[mdsal.git] / binding / mdsal-binding-spec-util / src / main / java / org / opendaylight / mdsal / binding / spec / reflect / BindingReflections.java
index 2c1a2d1871be926cc51406a7fdbcac74fb8881ec..0f1dc3e9b90fc2b18ee2ec508e928fc9d4a6c2bc 100644 (file)
@@ -9,7 +9,6 @@ package org.opendaylight.mdsal.binding.spec.reflect;
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkState;
-import static com.google.common.base.Verify.verifyNotNull;
 import static java.util.Objects.requireNonNull;
 
 import com.google.common.annotations.Beta;
@@ -20,6 +19,7 @@ import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.ImmutableSet.Builder;
 import com.google.common.util.concurrent.ListenableFuture;
 import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
 import java.lang.reflect.ParameterizedType;
@@ -247,35 +247,40 @@ public final class BindingReflections {
         return match.group(0);
     }
 
-    @SuppressWarnings("checkstyle:illegalCatch")
     public static QNameModule getQNameModule(final Class<?> clz) {
         if (DataContainer.class.isAssignableFrom(clz) || BaseIdentity.class.isAssignableFrom(clz)
                 || Action.class.isAssignableFrom(clz)) {
             return findQName(clz).getModule();
         }
-        try {
-            return getModuleInfo(clz).getName().getModule();
-        } catch (Exception e) {
-            throw new IllegalStateException("Unable to get QName of defining model.", e);
-        }
+
+        return getModuleInfo(clz).getName().getModule();
     }
 
     /**
      * Returns instance of {@link YangModuleInfo} of declaring model for specific class.
      *
      * @param cls data object class
-     * @return Instance of {@link YangModuleInfo} associated with model, from
-     *         which this class was derived.
+     * @return Instance of {@link YangModuleInfo} associated with model, from which this class was derived.
      */
-    // FIXME: 8.0.0: do not throw Exception here
-    public static @NonNull YangModuleInfo getModuleInfo(final Class<?> cls) throws Exception {
-        checkArgument(cls != null);
-        String packageName = getModelRootPackageName(cls.getPackage());
+    public static @NonNull YangModuleInfo getModuleInfo(final Class<?> cls) {
+        final String packageName = getModelRootPackageName(cls.getPackage());
         final String potentialClassName = getModuleInfoClassName(packageName);
-        return ClassLoaderUtils.callWithClassLoader(cls.getClassLoader(), () -> {
-            Class<?> moduleInfoClass = Thread.currentThread().getContextClassLoader().loadClass(potentialClassName);
-            return (YangModuleInfo) verifyNotNull(moduleInfoClass.getMethod("getInstance").invoke(null));
-        });
+        final Class<?> moduleInfoClass;
+        try {
+            moduleInfoClass = cls.getClassLoader().loadClass(potentialClassName);
+        } catch (ClassNotFoundException e) {
+            throw new IllegalStateException("Failed to load " + potentialClassName, e);
+        }
+
+        final Object infoInstance;
+        try {
+            infoInstance = moduleInfoClass.getMethod("getInstance").invoke(null);
+        } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
+            throw new IllegalStateException("Failed to get instance of " + moduleInfoClass, e);
+        }
+
+        checkState(infoInstance instanceof YangModuleInfo, "Unexpected instance %s", infoInstance);
+        return (YangModuleInfo) infoInstance;
     }
 
     public static @NonNull String getModuleInfoClassName(final String packageName) {
@@ -433,19 +438,24 @@ public final class BindingReflections {
     /**
      * Scans supplied class and returns an iterable of all data children classes.
      *
-     * @param type
-     *            YANG Modeled Entity derived from DataContainer
+     * @param type YANG Modeled Entity derived from DataContainer
      * @return Iterable of all data children, which have YANG modeled entity
      */
-    public static Map<Class<?>, Method> getChildrenClassToMethod(final Class<?> type) {
-        return getChildrenClassToMethod(type, BindingMapping.GETTER_PREFIX);
+    public static Map<Class<? extends DataContainer>, Method> getChildrenClassToMethod(final Class<?> type) {
+        return getChildClassToMethod(type, BindingMapping.GETTER_PREFIX);
     }
 
-    private static Map<Class<?>, Method> getChildrenClassToMethod(final Class<?> type, final String prefix) {
+    @Beta
+    public static Map<Class<? extends DataContainer>, Method> getChildrenClassToNonnullMethod(final Class<?> type) {
+        return getChildClassToMethod(type, BindingMapping.NONNULL_PREFIX);
+    }
+
+    private static Map<Class<? extends DataContainer>, Method> getChildClassToMethod(final Class<?> type,
+            final String prefix) {
         checkArgument(type != null, "Target type must not be null");
         checkArgument(DataContainer.class.isAssignableFrom(type), "Supplied type %s must be derived from DataContainer",
             type);
-        Map<Class<?>, Method> ret = new HashMap<>();
+        Map<Class<? extends DataContainer>, Method> ret = new HashMap<>();
         for (Method method : type.getMethods()) {
             Optional<Class<? extends DataContainer>> entity = getYangModeledReturnType(method, prefix);
             if (entity.isPresent()) {
@@ -455,11 +465,6 @@ public final class BindingReflections {
         return ret;
     }
 
-    @Beta
-    public static Map<Class<?>, Method> getChildrenClassToNonnullMethod(final Class<?> type) {
-        return getChildrenClassToMethod(type, BindingMapping.NONNULL_PREFIX);
-    }
-
     private static Optional<Class<? extends DataContainer>> getYangModeledReturnType(final Method method,
             final String prefix) {
         final String methodName = method.getName();
@@ -482,10 +487,9 @@ public final class BindingReflections {
     private static Optional<Class<? extends DataContainer>> getYangModeledReturnType(final Method method,
             final int parameterOffset) {
         try {
-            return ClassLoaderUtils.callWithClassLoader(method.getDeclaringClass().getClassLoader(), () -> {
-                return genericParameter(method.getGenericReturnType(), parameterOffset).flatMap(
-                    result -> result instanceof Class ? optionalCast((Class<?>) result) : Optional.empty());
-            });
+            return ClassLoaderUtils.callWithClassLoader(method.getDeclaringClass().getClassLoader(),
+                () -> genericParameter(method.getGenericReturnType(), parameterOffset)
+                    .flatMap(result -> result instanceof Class ? optionalCast((Class<?>) result) : Optional.empty()));
         } catch (Exception e) {
             /*
              * It is safe to log this this exception on debug, since this
@@ -572,18 +576,11 @@ public final class BindingReflections {
          * @throws IllegalArgumentException If supplied class was not derived from YANG model.
          */
         // FIXME: Extend this algorithm to also provide QName for YANG modeled simple types.
-        @SuppressWarnings({ "rawtypes", "unchecked", "checkstyle:illegalCatch" })
+        @SuppressWarnings({ "rawtypes", "unchecked" })
         private static QName computeQName(final Class key) {
             checkArgument(isBindingClass(key), "Supplied class %s is not derived from YANG.", key);
 
-            YangModuleInfo moduleInfo;
-            try {
-                moduleInfo = getModuleInfo(key);
-            } catch (Exception e) {
-                throw new IllegalStateException("Unable to get QName for " + key + ". YangModuleInfo was not found.",
-                    e);
-            }
-            final QName module = moduleInfo.getName();
+            final QName module = getModuleInfo(key).getName();
             if (Augmentation.class.isAssignableFrom(key)) {
                 return module;
             } else if (isRpcType(key)) {
@@ -602,19 +599,6 @@ public final class BindingReflections {
         }
     }
 
-    /**
-     * Extracts augmentation from Binding DTO field using reflection.
-     *
-     * @param input Instance of DataObject which is augmentable and may contain augmentation
-     * @return Map of augmentations if read was successful, otherwise empty map.
-     * @deprecated Use {@link Augmentable#augmentations()} instead.
-     */
-    @SuppressWarnings("unchecked")
-    @Deprecated(forRemoval = true)
-    public static Map<Class<? extends Augmentation<?>>, Augmentation<?>> getAugmentations(final Augmentable<?> input) {
-        return (Map) input.augmentations();
-    }
-
     /**
      * Determines if two augmentation classes or case classes represents same
      * data.
@@ -643,6 +627,11 @@ public final class BindingReflections {
      *            Class which should be used at particular subtree
      * @return true if and only if classes represents same data.
      */
+    // FIXME: this really should live in BindingRuntimeTypes and should not be based on reflection. The only user is
+    //        binding-dom-codec and the logic could easily be performed on GeneratedType instead. For a particular
+    //        world this boils down to a matrix, which can be calculated either on-demand or when we create
+    //        BindingRuntimeTypes. Achieving that will bring us one step closer to being able to have a pre-compiled
+    //        Binding Runtime.
     @SuppressWarnings({ "rawtypes", "unchecked" })
     public static boolean isSubstitutionFor(final Class potential, final Class target) {
         Set<Class> subImplemented = new HashSet<>(Arrays.asList(potential.getInterfaces()));