Move BindingReflections to mdsal-binding-spec-util
[mdsal.git] / binding / yang-binding / src / main / java / org / opendaylight / yangtools / yang / binding / util / BindingReflections.java
index d2456f52ba092479ff445be5395364e762252d3f..d1c3fbdd15c4021ae707d272bf35866650a681d6 100644 (file)
@@ -9,6 +9,7 @@ package org.opendaylight.yangtools.yang.binding.util;
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkState;
+
 import com.google.common.base.Optional;
 import com.google.common.cache.CacheBuilder;
 import com.google.common.cache.CacheLoader;
@@ -16,23 +17,22 @@ import com.google.common.cache.LoadingCache;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.ImmutableSet.Builder;
 import com.google.common.collect.Sets;
+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.Type;
-import java.net.URI;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.ServiceLoader;
-import java.util.concurrent.Callable;
-import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
+import javax.annotation.RegEx;
 import org.opendaylight.yangtools.util.ClassLoaderUtils;
+import org.opendaylight.yangtools.yang.binding.Action;
 import org.opendaylight.yangtools.yang.binding.Augmentable;
 import org.opendaylight.yangtools.yang.binding.Augmentation;
 import org.opendaylight.yangtools.yang.binding.BaseIdentity;
@@ -46,19 +46,24 @@ import org.opendaylight.yangtools.yang.binding.YangModelBindingProvider;
 import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.YangConstants;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public class BindingReflections {
+@Deprecated
+public final class BindingReflections {
 
     private static final long EXPIRATION_TIME = 60;
-    private static final String ROOT_PACKAGE_PATTERN_STRING = "(org.opendaylight.yang.gen.v1.[a-z0-9_\\.]*\\.rev[0-9][0-9][0-1][0-9][0-3][0-9])";
+
+    @RegEx
+    private static final String ROOT_PACKAGE_PATTERN_STRING =
+            "(org.opendaylight.yang.gen.v1.[a-z0-9_\\.]*\\.(?:rev[0-9][0-9][0-1][0-9][0-3][0-9]|norev))";
     private static final Pattern ROOT_PACKAGE_PATTERN = Pattern.compile(ROOT_PACKAGE_PATTERN_STRING);
     private static final Logger LOG = LoggerFactory.getLogger(BindingReflections.class);
 
-    private static final LoadingCache<Class<?>, Optional<QName>> CLASS_TO_QNAME = CacheBuilder.newBuilder() //
-            .weakKeys() //
-            .expireAfterAccess(EXPIRATION_TIME, TimeUnit.SECONDS) //
+    private static final LoadingCache<Class<?>, Optional<QName>> CLASS_TO_QNAME = CacheBuilder.newBuilder()
+            .weakKeys()
+            .expireAfterAccess(EXPIRATION_TIME, TimeUnit.SECONDS)
             .build(new ClassToQNameLoader());
 
     private BindingReflections() {
@@ -66,16 +71,13 @@ public class BindingReflections {
     }
 
     /**
-     * Find augmentation target class from concrete Augmentation class
-     *
-     * This method uses first generic argument of implemented
-     * {@link Augmentation} interface.
+     * Find augmentation target class from concrete Augmentation class. This method uses first generic argument of
+     * implemented {@link Augmentation} interface.
      *
      * @param augmentation
      *            {@link Augmentation} subclass for which we want to determine
      *            augmentation target.
-     * @return Augmentation target - class which augmentation provides
-     *         additional extensions.
+     * @return Augmentation target - class which augmentation provides additional extensions.
      */
     public static Class<? extends Augmentable<?>> findAugmentationTarget(
             final Class<? extends Augmentation<?>> augmentation) {
@@ -83,10 +85,8 @@ public class BindingReflections {
     }
 
     /**
-     * Find data hierarchy parent from concrete Data class
-     *
-     * This method uses first generic argument of implemented {@link ChildOf}
-     * interface.
+     * Find data hierarchy parent from concrete Data class. This method uses first generic argument of implemented
+     * {@link ChildOf} interface.
      *
      * @param childClass
      *            child class for which we want to find the parent class.
@@ -97,11 +97,8 @@ public class BindingReflections {
     }
 
     /**
-     * Find data hierarchy parent from concrete Data class
-     *
-     * This method is shorthand which gets DataObject class by invoking
-     * {@link DataObject#getImplementedInterface()} and uses
-     * {@link #findHierarchicalParent(Class)}.
+     * Find data hierarchy parent from concrete Data class. This method is shorthand which gets DataObject class by
+     * invoking {@link DataObject#getImplementedInterface()} and uses {@link #findHierarchicalParent(Class)}.
      *
      * @param child
      *            Child object for which the parent needs to be located.
@@ -115,24 +112,19 @@ public class BindingReflections {
     }
 
     /**
-     * Returns a QName associated to supplied type
+     * Returns a QName associated to supplied type.
      *
-     * @param dataType
-     * @return QName associated to supplied dataType. If dataType is
-     *         Augmentation method does not return canonical QName, but QName
-     *         with correct namespace revision, but virtual local name, since
-     *         augmentations do not have name.
-     *
-     *         May return null if QName is not present.
+     * @param dataType Data type class
+     * @return QName associated to supplied dataType. If dataType is Augmentation method does not return canonical
+     *         QName, but QName with correct namespace revision, but virtual local name, since augmentations do not
+     *         have name. May return null if QName is not present.
      */
-    public static final QName findQName(final Class<?> dataType) {
+    public static QName findQName(final Class<?> dataType) {
         return CLASS_TO_QNAME.getUnchecked(dataType).orNull();
     }
 
     /**
-     * Checks if method is RPC invocation
-     *
-     *
+     * Checks if method is RPC invocation.
      *
      * @param possibleMethod
      *            Method to check
@@ -140,50 +132,48 @@ public class BindingReflections {
      */
     public static boolean isRpcMethod(final Method possibleMethod) {
         return possibleMethod != null && RpcService.class.isAssignableFrom(possibleMethod.getDeclaringClass())
-                && Future.class.isAssignableFrom(possibleMethod.getReturnType())
-                && possibleMethod.getParameterTypes().length <= 1;
+                && ListenableFuture.class.isAssignableFrom(possibleMethod.getReturnType())
+                // length <= 2: it seemed to be impossible to get correct RpcMethodInvoker because of
+                // resolveRpcInputClass() check.While RpcMethodInvoker counts with one argument for
+                // non input type and two arguments for input type, resolveRpcInputClass() counting
+                // with zero for non input and one for input type
+                && possibleMethod.getParameterTypes().length <= 2;
     }
 
     /**
-     *
-     * Extracts Output class for RPC method
+     * Extracts Output class for RPC method.
      *
      * @param targetMethod
      *            method to scan
-     * @return Optional.absent() if result type could not be get, or return type
-     *         is Void.
+     * @return Optional.absent() if result type could not be get, or return type is Void.
      */
     @SuppressWarnings("rawtypes")
     public static Optional<Class<?>> resolveRpcOutputClass(final Method targetMethod) {
-        checkState(isRpcMethod(targetMethod), "Supplied method is not Rpc invocation method");
+        checkState(isRpcMethod(targetMethod), "Supplied method is not a RPC invocation method");
         Type futureType = targetMethod.getGenericReturnType();
         Type rpcResultType = ClassLoaderUtils.getFirstGenericParameter(futureType);
         Type rpcResultArgument = ClassLoaderUtils.getFirstGenericParameter(rpcResultType);
         if (rpcResultArgument instanceof Class && !Void.class.equals(rpcResultArgument)) {
-            return Optional.<Class<?>> of((Class) rpcResultArgument);
+            return Optional.of((Class) rpcResultArgument);
         }
         return Optional.absent();
     }
 
     /**
-     *
-     * Extracts input class for RPC method
+     * Extracts input class for RPC method.
      *
      * @param targetMethod
      *            method to scan
-     * @return Optional.absent() if rpc has no input, Rpc input type otherwise.
+     * @return Optional.absent() if RPC has no input, RPC input type otherwise.
      */
-    @SuppressWarnings("unchecked")
+    @SuppressWarnings("rawtypes")
     public static Optional<Class<? extends DataContainer>> resolveRpcInputClass(final Method targetMethod) {
-        @SuppressWarnings("rawtypes")
-        Class[] types = targetMethod.getParameterTypes();
-        if (types.length == 0) {
-            return Optional.absent();
-        }
-        if (types.length == 1) {
-            return Optional.<Class<? extends DataContainer>> of(types[0]);
+        for (Class clazz : targetMethod.getParameterTypes()) {
+            if (DataContainer.class.isAssignableFrom(clazz)) {
+                return Optional.of(clazz);
+            }
         }
-        throw new IllegalArgumentException("Method has 2 or more arguments.");
+        return Optional.absent();
     }
 
     public static QName getQName(final Class<? extends BaseIdentity> context) {
@@ -191,16 +181,10 @@ public class BindingReflections {
     }
 
     /**
-     *
      * Checks if class is child of augmentation.
-     *
-     *
-     * @param clazz
-     * @return
      */
     public static boolean isAugmentationChild(final Class<?> clazz) {
-        // FIXME: Current resolver could be still confused when
-        // child node was added by grouping
+        // FIXME: Current resolver could be still confused when child node was added by grouping
         checkArgument(clazz != null);
 
         @SuppressWarnings({ "rawtypes", "unchecked" })
@@ -244,43 +228,47 @@ public class BindingReflections {
         return match.group(0);
     }
 
-    public static final QNameModule getQNameModule(final Class<?> clz) {
-        if(DataContainer.class.isAssignableFrom(clz) || BaseIdentity.class.isAssignableFrom(clz)) {
+    @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 {
-            YangModuleInfo modInfo = BindingReflections.getModuleInfo(clz);
-            return getQNameModule(modInfo);
+            return BindingReflections.getModuleInfo(clz).getName().getModule();
         } catch (Exception e) {
             throw new IllegalStateException("Unable to get QName of defining model.", e);
         }
     }
 
-    public static final QNameModule getQNameModule(final YangModuleInfo modInfo) {
-        return QNameModule.create(URI.create(modInfo.getNamespace()), QName.parseRevision(modInfo.getRevision()));
+    /**
+     * Extract a QNameModule from YangModuleInfo.
+     *
+     * @param modInfo Module info
+     * @return QNameModule for the module
+     * @throws NullPointerException in modInfo is null
+     *
+     * @deprecated Use {@code YangModuleInfo.getName().getModule()} instead.
+     */
+    @Deprecated
+    public static QNameModule getQNameModule(final YangModuleInfo modInfo) {
+        return modInfo.getName().getModule();
     }
 
     /**
+     * Returns instance of {@link YangModuleInfo} of declaring model for specific class.
      *
-     * Returns instance of {@link YangModuleInfo} of declaring model for
-     * specific class.
-     *
-     * @param cls
+     * @param cls data object class
      * @return Instance of {@link YangModuleInfo} associated with model, from
      *         which this class was derived.
-     * @throws Exception
      */
     public static YangModuleInfo getModuleInfo(final Class<?> cls) throws Exception {
         checkArgument(cls != null);
         String packageName = getModelRootPackageName(cls.getPackage());
         final String potentialClassName = getModuleInfoClassName(packageName);
-        return ClassLoaderUtils.withClassLoader(cls.getClassLoader(), new Callable<YangModuleInfo>() {
-            @Override
-            public YangModuleInfo call() throws ClassNotFoundException, IllegalAccessException,
-                    IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
-                Class<?> moduleInfoClass = Thread.currentThread().getContextClassLoader().loadClass(potentialClassName);
-                return (YangModuleInfo) moduleInfoClass.getMethod("getInstance").invoke(null);
-            }
+        return ClassLoaderUtils.callWithClassLoader(cls.getClassLoader(), () -> {
+            Class<?> moduleInfoClass = Thread.currentThread().getContextClassLoader().loadClass(potentialClassName);
+            return (YangModuleInfo) moduleInfoClass.getMethod("getInstance").invoke(null);
         });
     }
 
@@ -289,7 +277,6 @@ public class BindingReflections {
     }
 
     /**
-     *
      * Check if supplied class is derived from YANG model.
      *
      * @param cls
@@ -300,14 +287,13 @@ public class BindingReflections {
         if (DataContainer.class.isAssignableFrom(cls) || Augmentation.class.isAssignableFrom(cls)) {
             return true;
         }
-        return (cls.getName().startsWith(BindingMapping.PACKAGE_PREFIX));
+        return cls.getName().startsWith(BindingMapping.PACKAGE_PREFIX);
     }
 
     /**
-     *
      * Checks if supplied method is callback for notifications.
      *
-     * @param method
+     * @param method method to check
      * @return true if method is notification callback.
      */
     public static boolean isNotificationCallback(final Method method) {
@@ -323,11 +309,10 @@ public class BindingReflections {
     }
 
     /**
+     * Checks is supplied class is a {@link Notification}.
      *
-     * Checks is supplied class is Notification.
-     *
-     * @param potentialNotification
-     * @return
+     * @param potentialNotification class to examine
+     * @return True if the class represents a Notification.
      */
     public static boolean isNotification(final Class<?> potentialNotification) {
         checkArgument(potentialNotification != null, "potentialNotification must not be null.");
@@ -335,11 +320,8 @@ public class BindingReflections {
     }
 
     /**
-     *
-     * Loads {@link YangModuleInfo} infos available on current classloader.
-     *
-     * This method is shorthand for {@link #loadModuleInfos(ClassLoader)} with
-     * {@link Thread#getContextClassLoader()} for current thread.
+     * Loads {@link YangModuleInfo} infos available on current classloader. This method is shorthand for
+     * {@link #loadModuleInfos(ClassLoader)} with {@link Thread#getContextClassLoader()} for current thread.
      *
      * @return Set of {@link YangModuleInfo} available for current classloader.
      */
@@ -348,18 +330,16 @@ public class BindingReflections {
     }
 
     /**
-     *
      * Loads {@link YangModuleInfo} infos available on supplied classloader.
      *
-     * {@link YangModuleInfo} are discovered using {@link ServiceLoader} for
-     * {@link YangModelBindingProvider}. {@link YangModelBindingProvider} are
-     * simple classes which holds only pointers to actual instance
+     * <p>
+     * {@link YangModuleInfo} are discovered using {@link ServiceLoader} for {@link YangModelBindingProvider}.
+     * {@link YangModelBindingProvider} are simple classes which holds only pointers to actual instance
      * {@link YangModuleInfo}.
      *
-     * When {@link YangModuleInfo} is available, all dependencies are
-     * recursivelly collected into returning set by collecting results of
-     * {@link YangModuleInfo#getImportedModules()}.
-     *
+     * <p>
+     * When {@link YangModuleInfo} is available, all dependencies are recursively collected into returning set by
+     * collecting results of {@link YangModuleInfo#getImportedModules()}.
      *
      * @param loader
      *            Classloader for which {@link YangModuleInfo} should be
@@ -367,7 +347,7 @@ public class BindingReflections {
      * @return Set of {@link YangModuleInfo} available for supplied classloader.
      */
     public static ImmutableSet<YangModuleInfo> loadModuleInfos(final ClassLoader loader) {
-        Builder<YangModuleInfo> moduleInfoSet = ImmutableSet.<YangModuleInfo> builder();
+        Builder<YangModuleInfo> moduleInfoSet = ImmutableSet.builder();
         ServiceLoader<YangModelBindingProvider> serviceLoader = ServiceLoader.load(YangModelBindingProvider.class,
                 loader);
         for (YangModelBindingProvider bindingProvider : serviceLoader) {
@@ -387,7 +367,6 @@ public class BindingReflections {
     }
 
     /**
-     *
      * Checks if supplied class represents RPC Input / RPC Output.
      *
      * @param targetType
@@ -395,16 +374,14 @@ public class BindingReflections {
      * @return true if class represents RPC Input or RPC Output class.
      */
     public static boolean isRpcType(final Class<? extends DataObject> targetType) {
-        return DataContainer.class.isAssignableFrom(targetType) //
-                && !ChildOf.class.isAssignableFrom(targetType) //
-                && !Notification.class.isAssignableFrom(targetType) //
+        return DataContainer.class.isAssignableFrom(targetType)
+                && !ChildOf.class.isAssignableFrom(targetType)
+                && !Notification.class.isAssignableFrom(targetType)
                 && (targetType.getName().endsWith("Input") || targetType.getName().endsWith("Output"));
     }
 
     /**
-     *
-     * Scans supplied class and returns an iterable of all data children
-     * classes.
+     * Scans supplied class and returns an iterable of all data children classes.
      *
      * @param type
      *            YANG Modeled Entity derived from DataContainer
@@ -425,9 +402,7 @@ public class BindingReflections {
     }
 
     /**
-     *
-     * Scans supplied class and returns an iterable of all data children
-     * classes.
+     * Scans supplied class and returns an iterable of all data children classes.
      *
      * @param type
      *            YANG Modeled Entity derived from DataContainer
@@ -435,7 +410,8 @@ public class BindingReflections {
      */
     public static Map<Class<?>, Method> getChildrenClassToMethod(final Class<?> type) {
         checkArgument(type != null, "Target type must not be null");
-        checkArgument(DataContainer.class.isAssignableFrom(type), "Supplied type must be derived from DataContainer");
+        checkArgument(DataContainer.class.isAssignableFrom(type), "Supplied type %s must be derived from DataContainer",
+            type);
         Map<Class<?>, Method> ret = new HashMap<>();
         for (Method method : type.getMethods()) {
             Optional<Class<? extends DataContainer>> entity = getYangModeledReturnType(method);
@@ -446,37 +422,28 @@ public class BindingReflections {
         return ret;
     }
 
-    @SuppressWarnings("unchecked")
+    @SuppressWarnings({ "unchecked", "rawtypes", "checkstyle:illegalCatch" })
     private static Optional<Class<? extends DataContainer>> getYangModeledReturnType(final Method method) {
-        if (method.getName().equals("getClass") || !method.getName().startsWith("get")
+        if ("getClass".equals(method.getName()) || !method.getName().startsWith("get")
                 || method.getParameterTypes().length > 0) {
             return Optional.absent();
         }
 
-        @SuppressWarnings("rawtypes")
         Class returnType = method.getReturnType();
         if (DataContainer.class.isAssignableFrom(returnType)) {
-            return Optional.<Class<? extends DataContainer>> of(returnType);
+            return Optional.of(returnType);
         } else if (List.class.isAssignableFrom(returnType)) {
             try {
-                return ClassLoaderUtils.withClassLoader(method.getDeclaringClass().getClassLoader(),
-                        new Callable<Optional<Class<? extends DataContainer>>>() {
-                            @SuppressWarnings("rawtypes")
-                            @Override
-                            public Optional<Class<? extends DataContainer>> call() {
-                                Type listResult = ClassLoaderUtils.getFirstGenericParameter(method
-                                        .getGenericReturnType());
-                                if (listResult instanceof Class
-                                        && DataContainer.class.isAssignableFrom((Class) listResult)) {
-                                    return Optional.<Class<? extends DataContainer>> of((Class) listResult);
-                                }
-                                return Optional.absent();
-                            }
-
-                        });
+                return ClassLoaderUtils.callWithClassLoader(method.getDeclaringClass().getClassLoader(), () -> {
+                    Type listResult = ClassLoaderUtils.getFirstGenericParameter(method.getGenericReturnType());
+                    if (listResult instanceof Class
+                            && DataContainer.class.isAssignableFrom((Class) listResult)) {
+                        return Optional.of((Class) listResult);
+                    }
+                    return Optional.absent();
+                });
             } catch (Exception e) {
                 /*
-                 *
                  * It is safe to log this this exception on debug, since this
                  * method should not fail. Only failures are possible if the
                  * runtime / backing.
@@ -490,118 +457,108 @@ public class BindingReflections {
     private static class ClassToQNameLoader extends CacheLoader<Class<?>, Optional<QName>> {
 
         @Override
-        public Optional<QName> load(final Class<?> key) throws Exception {
+        public Optional<QName> load(@SuppressWarnings("NullableProblems") final Class<?> key) throws Exception {
             return resolveQNameNoCache(key);
         }
-    }
-
-    /**
-     *
-     * Tries to resolve QName for supplied class.
-     *
-     * Looks up for static field with name from constant
-     * {@link BindingMapping#QNAME_STATIC_FIELD_NAME} and returns value if
-     * present.
-     *
-     * If field is not present uses {@link #computeQName(Class)} to compute
-     * QName for missing types.
-     *
-     * @param key
-     * @return
-     */
-    private static Optional<QName> resolveQNameNoCache(final Class<?> key) {
-        try {
-            Field field = key.getField(BindingMapping.QNAME_STATIC_FIELD_NAME);
-            Object obj = field.get(null);
-            if (obj instanceof QName) {
-                return Optional.of((QName) obj);
-            }
 
-        } catch (NoSuchFieldException e) {
-            return Optional.of(computeQName(key));
+        /**
+         * Tries to resolve QName for supplied class. Looks up for static field with name from constant
+         * {@link BindingMapping#QNAME_STATIC_FIELD_NAME} and returns value if present. If field is not present uses
+         * {@link #computeQName(Class)} to compute QName for missing types.
+         */
+        private static Optional<QName> resolveQNameNoCache(final Class<?> key) {
+            try {
+                final Field field;
+                try {
+                    field = key.getField(BindingMapping.QNAME_STATIC_FIELD_NAME);
+                } catch (NoSuchFieldException e) {
+                    LOG.debug("{} does not have a {} field, falling back to computation", key,
+                        BindingMapping.QNAME_STATIC_FIELD_NAME, e);
+                    return Optional.of(computeQName(key));
+                }
 
-        } catch (SecurityException | IllegalArgumentException | IllegalAccessException e) {
-            /*
-             *
-             * It is safe to log this this exception on debug, since this method
-             * should not fail. Only failures are possible if the runtime /
-             * backing.
-             */
-            LOG.debug("Unexpected exception during extracting QName for {}", key, e);
+                final Object obj = field.get(null);
+                if (obj instanceof QName) {
+                    return Optional.of((QName) obj);
+                }
+            } catch (SecurityException | IllegalArgumentException | IllegalAccessException e) {
+                /*
+                 * It is safe to log this this exception on debug, since this method should not fail. Only failures are
+                 * possible if the runtime / backing is inconsistent.
+                 */
+                LOG.debug("Unexpected exception during extracting QName for {}", key, e);
+            }
+            return Optional.absent();
         }
-        return Optional.absent();
-    }
 
-    /**
-     * Computes QName for supplied class
-     *
-     * Namespace and revision are same as {@link YangModuleInfo} associated with
-     * supplied class.
-     * <p>
-     * If class is
-     * <ul>
-     * <li>rpc input: local name is "input".
-     * <li>rpc output: local name is "output".
-     * <li>augmentation: local name is "module name".
-     * </ul>
-     *
-     * There is also fallback, if it is not possible to compute QName using
-     * following algorithm returns module QName.
-     *
-     * FIXME: Extend this algorithm to also provide QName for YANG modeled
-     * simple types.
-     *
-     * @throws IllegalStateException
-     *             If YangModuleInfo could not be resolved
-     * @throws IllegalArgumentException
-     *             If supplied class was not derived from YANG model.
-     *
-     */
-    @SuppressWarnings({ "rawtypes", "unchecked" })
-    private static QName computeQName(final Class key) {
-        if (isBindingClass(key)) {
+        /**
+         * Computes QName for supplied class. Namespace and revision are same as {@link YangModuleInfo} associated with
+         * supplied class.
+         *
+         * <p>
+         * If class is
+         * <ul>
+         * <li>rpc input: local name is "input".
+         * <li>rpc output: local name is "output".
+         * <li>augmentation: local name is "module name".
+         * </ul>
+         *
+         * <p>
+         * There is also fallback, if it is not possible to compute QName using following algorithm returns module
+         * QName.
+         *
+         * @throws IllegalStateException If YangModuleInfo could not be resolved
+         * @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" })
+        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);
+                throw new IllegalStateException("Unable to get QName for " + key + ". YangModuleInfo was not found.",
+                    e);
             }
-            final QName module = QName.cachedReference(getModuleQName(moduleInfo));
+            final QName module = moduleInfo.getName();
             if (Augmentation.class.isAssignableFrom(key)) {
                 return module;
             } else if (isRpcType(key)) {
                 final String className = key.getSimpleName();
                 if (className.endsWith(BindingMapping.RPC_OUTPUT_SUFFIX)) {
-                    return QName.cachedReference(QName.create(module, "output"));
-                } else {
-                    return QName.cachedReference(QName.create(module, "input"));
+                    return YangConstants.operationOutputQName(module.getModule()).intern();
                 }
+
+                return YangConstants.operationInputQName(module.getModule()).intern();
             }
+
             /*
              * Fallback for Binding types which do not have QNAME field
              */
             return module;
-        } else {
-            throw new IllegalArgumentException("Supplied class " + key + "is not derived from YANG.");
         }
     }
 
     /**
-     * Given a {@link YangModuleInfo}, create a QName representing it. The QName
-     * is formed by reusing the module's namespace and revision using the
-     * module's name as the QName's local name.
+     * Given a {@link YangModuleInfo}, create a QName representing it. The QName is formed by reusing the module's
+     * namespace and revision using the module's name as the QName's local name.
      *
      * @param moduleInfo
      *            module information
      * @return QName representing the module
+     *
+     * @deprecated Use {@link YangModuleInfo#getName()} instead.
      */
+    @Deprecated
     public static QName getModuleQName(final YangModuleInfo moduleInfo) {
         checkArgument(moduleInfo != null, "moduleInfo must not be null.");
-        return QName.create(moduleInfo.getNamespace(), moduleInfo.getRevision(), moduleInfo.getName());
+        return moduleInfo.getName();
     }
 
     /**
-     * Extracts augmentation from Binding DTO field using reflection
+     * Extracts augmentation from Binding DTO field using reflection.
      *
      * @param input
      *            Instance of DataObject which is augmentable and may contain
@@ -613,9 +570,9 @@ public class BindingReflections {
     }
 
     /**
-     *
      * Determines if two augmentation classes or case classes represents same
      * data.
+     *
      * <p>
      * Two augmentations or cases could be substituted only if and if:
      * <ul>
@@ -626,6 +583,7 @@ public class BindingReflections {
      * <li>If cases: Both are from same choice. Choice class was generated for
      * data node in grouping.</li>
      * </ul>
+     *
      * <p>
      * <b>Explanation:</b> Binding Specification reuses classes generated for
      * groupings as part of normal data tree, this classes from grouping could
@@ -634,7 +592,7 @@ public class BindingReflections {
      * constructors, etc).
      *
      * @param potential
-     *            Class which is potential substition
+     *            Class which is potential substitution
      * @param target
      *            Class which should be used at particular subtree
      * @return true if and only if classes represents same data.
@@ -658,8 +616,7 @@ public class BindingReflections {
                     return false;
                 }
             } catch (NoSuchMethodException e) {
-                // Counterpart method is missing, so classes could not be
-                // substituted.
+                // Counterpart method is missing, so classes could not be substituted.
                 return false;
             } catch (SecurityException e) {
                 throw new IllegalStateException("Could not compare methods", e);