All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.ardikars.common.util.Reflections Maven / Gradle / Ivy

The newest version!
package com.ardikars.common.util;

import com.ardikars.common.annotation.Helper;

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

@Helper
public class Reflections {

    private static final boolean ACCESS_CONTROL;

    /**
     * Try to call {@link AccessibleObject#setAccessible(boolean)} but will catch any {@link SecurityException}
     * and return it.
     * The caller must check if it returns {@code null} and if not handle the returned exception.
     * @param object accessible object.
     * @param checkAccessible accessible.
     * @return returns null on success, throwable otherwise.
     */
    public static Throwable trySetAccessible(final AccessibleObject object, final boolean checkAccessible) throws RuntimeException {
        if (checkAccessible && !ACCESS_CONTROL) {
            return new UnsupportedOperationException("Reflective setAccessible(true) disabled");
        }
        return setAccessible(object, checkAccessible);
    }

    /**
     * Try to force call {@link AccessibleObject#setAccessible(boolean)} but will catch any {@link SecurityException}
     * and return it.
     * The caller must check if it returns {@code null} and if not handle the returned exception.
     * @param object accessible object.
     * @param checkAccessible accessible.
     * @return returns null on success, throwable otherwise.
     * @deprecated please use {@link Reflections}.{@link #trySetAccessible(AccessibleObject, boolean)}
     */
    public static Throwable forceSetAccessible(final AccessibleObject object, final boolean checkAccessible) throws RuntimeException {
        return setAccessible(object, checkAccessible);
    }

    /**
     * Gets a {@code List} of superclasses for the given class.
     *
     * @param cls  the class to look up.
     * @return returns {@code List} of superclasses.
     */
    public static List> getAllSuperClasses(final Class cls) {
        Validate.notIllegalArgument(cls != null, new IllegalArgumentException("Class should be not null"));
        final List> classes = new ArrayList>();
        Class superclass = cls.getSuperclass();
        while (superclass != null) {
            classes.add(superclass);
            superclass = superclass.getSuperclass();
        }
        return Collections.unmodifiableList(classes);
    }

    /**
     * Gets a {@code List} of superclasses and class it self for the given class.
     *
     * @param cls  the class to look up.
     * @return returns {@code List} of superclasses and class it self.
     */
    public static List> getAllClasses(final Class cls) {
        Validate.notIllegalArgument(cls != null, new IllegalArgumentException("Class should be not null"));
        final List> classes = new ArrayList>();
        classes.add(cls);
        classes.addAll(getAllSuperClasses(cls));
        return Collections.unmodifiableList(classes);
    }

    /**
     * Gets a {@code List} of all interfaces implemented by the given
     * class and its superclasses.
     *
     * @param cls the class to look up.
     * @return returns {@code List} of interfaces.
     */
    public static List> getAllInterfaces(final Class cls) {
        Validate.notIllegalArgument(cls != null, new IllegalArgumentException("Class should be not null"));
        List> classes = getAllClasses(cls);
        Iterator> iterator = classes.iterator();
        List> interfaces = new ArrayList>();
        while (iterator.hasNext()) {
            Class clazz = iterator.next();
            for (Class i : clazz.getInterfaces()) {
                interfaces.add(i);
            }
        }
        return Collections.unmodifiableList(interfaces);
    }

    /**
     * Get a {@code List} of all classes and interfaces.
     * @param cls the class to look up.
     * @return returns {@code List} of all classes and interfaces.
     */
    public static List> getAllClassesAndInterfaces(final Class cls) {
        Validate.notIllegalArgument(cls != null, new IllegalArgumentException("Class should be not null"));
        List> classes = new ArrayList>();
        classes.addAll(getAllClasses(cls));
        classes.addAll(getAllInterfaces(cls));
        return Collections.unmodifiableList(classes);
    }

    /**
     * Get a {@code List} of all super classes and interfaces.
     * @param cls the class to look up.
     * @return returns {@code List} of all super classes and interfaces.
     */
    public static List> getAllSuperClassesAndInterfaces(final Class cls) {
        Validate.notIllegalArgument(cls != null, new IllegalArgumentException("Class should be not null"));
        final List> allSuperClassesAndInterfaces = new ArrayList>();
        final List> allSuperclasses = getAllSuperClasses(cls);
        int superClassIndex = 0;
        final List> allInterfaces = getAllInterfaces(cls);
        int interfaceIndex = 0;
        while (interfaceIndex < allInterfaces.size()
                || superClassIndex < allSuperclasses.size()) {
            Class acls;
            if (interfaceIndex >= allInterfaces.size()) {
                acls = allSuperclasses.get(superClassIndex++);
            } else if (superClassIndex >= allSuperclasses.size()) {
                acls = allInterfaces.get(interfaceIndex++);
            } else if (interfaceIndex < superClassIndex) {
                acls = allInterfaces.get(interfaceIndex++);
            } else if (superClassIndex < interfaceIndex) {
                acls = allSuperclasses.get(superClassIndex++);
            } else {
                acls = allInterfaces.get(interfaceIndex++);
            }
            allSuperClassesAndInterfaces.add(acls);
        }
        return Collections.unmodifiableList(allSuperClassesAndInterfaces);
    }

    /**
     * Get public field from current or super class/interface.
     * @param cls the class to look up.
     * @param fieldName field name.
     * @return returns {@link Field}.
     * @throws NoSuchFieldException field not found.
     */
    public static Field getPublicFiled(final  Class cls, String fieldName) throws NoSuchFieldException {
        final Field declaredField = cls.getField(fieldName);
        if (Modifier.isPublic(declaredField.getDeclaringClass().getModifiers())) {
            return declaredField;
        }
        final List> candidateClasses = getAllSuperClassesAndInterfaces(cls);
        for (final Class candidateClass : candidateClasses) {
            if (!Modifier.isPublic(candidateClass.getModifiers())) {
                continue;
            }
            Field candidateField;
            try {
                candidateField = candidateClass.getField(fieldName);
            } catch (final NoSuchFieldException ex) {
                continue;
            }
            if (Modifier.isPublic(candidateField.getDeclaringClass().getModifiers())) {
                return candidateField;
            }
        }
        throw new NoSuchFieldException("Can't find a public field for " + fieldName);
    }

    /**
     * Get public method from current or super class/interface.
     * @param cls the class to look up.
     * @param methodName method name.
     * @param parameterTypes parameter types.
     * @return returns {@link Method}.
     * @throws NoSuchMethodException method not found.
     */
    public static Method getPublicMethod(final Class cls, final String methodName, final Class... parameterTypes)
            throws NoSuchMethodException {
        final Method declaredMethod = cls.getMethod(methodName, parameterTypes);
        if (Modifier.isPublic(declaredMethod.getDeclaringClass().getModifiers())) {
            return declaredMethod;
        }
        final List> candidateClasses = getAllSuperClassesAndInterfaces(cls);
        for (final Class candidateClass : candidateClasses) {
            if (!Modifier.isPublic(candidateClass.getModifiers())) {
                continue;
            }
            Method candidateMethod;
            try {
                candidateMethod = candidateClass.getMethod(methodName, parameterTypes);
            } catch (final NoSuchMethodException ex) {
                continue;
            }
            if (Modifier.isPublic(candidateMethod.getDeclaringClass().getModifiers())) {
                return candidateMethod;
            }
        }
        throw new NoSuchMethodException("Can't find a public method for "
                + methodName + " " + Arrays.toString(parameterTypes));
    }

    private static Throwable setAccessible(final AccessibleObject object, final boolean checkAccessible) throws RuntimeException {
        Object obj = AccessController.doPrivileged(new PrivilegedAction() {
            @Override
            public Object run() {
                try {
                    object.setAccessible(checkAccessible);
                    return null;
                } catch (SecurityException e) {
                    return e;
                } catch (RuntimeException e) {
                    return e;
                }
            }
        });
        if (obj == null) {
            return null;
        } else {
            return (Throwable) obj;
        }
    }

    /**
     * Is the specified class an inner class or static nested class.
     *
     * @param cls  the class to check, may be null.
     * @return {@code true} if the class is an inner or static nested class,
     *  false if not or {@code null}
     */
    public static boolean isInnerClass(final Class cls) {
        return cls != null && cls.getEnclosingClass() != null;
    }

    static {
        ACCESS_CONTROL = Properties.getBoolean("common.util.tryReflectionSetAccessible", Platforms.getJavaMojorVersion() < 9);
    }

}