org.acmsl.commons.utils.ReflectionUtils.java Source code

Java tutorial

Introduction

Here is the source code for org.acmsl.commons.utils.ReflectionUtils.java

Source

//;-*- mode: java -*-
/*
                    ACM-SL Commons
    
Copyright (C) 2002-today  Jose San Leandro Armendariz
                          chous@acm-sl.org
    
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public
License as published by the Free Software Foundation; either
version 2 of the License, or any later version.
    
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.
    
You should have received a copy of the GNU General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    
Thanks to ACM S.L. for distributing this library under the GPL license.
Contact info: jose.sanleandro@acm-sl.com
    
 ******************************************************************************
 *
 * Filename: ReflectionUtils.java
 *
 * Author: Jose San Leandro Armendariz
 *
 * Description: Provides some useful methods when working with reflection.
 */
package org.acmsl.commons.utils;

/*
 * Importing project classes.
 */
import org.acmsl.commons.Literals;
import org.acmsl.commons.patterns.Singleton;
import org.acmsl.commons.patterns.Utils;

/*
 * Importing Commons-Logging classes.
 */
import org.apache.commons.logging.LogFactory;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

/*
 * Importing some JDK classes.
 */
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;

/**
 * Provides some useful methods when working with reflection.
 * @author <a href="mailto:chous@acm-sl.org">Jose San Leandro Armendariz</a>
 */
public class ReflectionUtils implements Utils, Singleton {
    /**
     * A cached empty field array.
     */
    public static final Field[] EMPTY_FIELD_ARRAY = new Field[0];

    /**
     * Singleton implemented to avoid the double-checked locking.
     */
    private static class ReflectionUtilsSingletonContainer {
        /**
         * The actual singleton.
         */
        @NotNull
        public static final ReflectionUtils SINGLETON = new ReflectionUtils();
    }

    /**
     * Default protected constructor to avoid accidental instantiation.
     */
    protected ReflectionUtils() {
    }

    /**
     * Retrieves a ReflectionUtils instance.
     * @return such instance.
     */
    @NotNull
    public static ReflectionUtils getInstance() {
        return ReflectionUtilsSingletonContainer.SINGLETON;
    }

    /**
     * Retrieves the parent classes of given object.
     * @param object the object to analyze.
     * @return the ordered collection of superclasses.
     */
    @NotNull
    public Class<?>[] retrieveSuperClasses(@NotNull final Object object) {
        return retrieveSuperClasses(object.getClass());
    }

    /**
     * Retrieves the parent classes of given class.
     * @param classInstance the class to analyze.
     * @param <T> the type.
     * @param <U> the parent class type.
     * @return the ordered collection of superclasses.
     */
    @SuppressWarnings("unchecked")
    @NotNull
    public <T extends U, U> Class<U>[] retrieveSuperClasses(@NotNull final Class<T> classInstance) {
        @NotNull
        final Class<U>[] result;

        @NotNull
        final Collection<Class<U>> t_cSuperClasses = new ArrayList<>();

        t_cSuperClasses.add((Class<U>) classInstance);

        t_cSuperClasses.addAll(this.<T, U>retrieveParentClasses(classInstance));

        result = t_cSuperClasses.toArray((Class<U>[]) new Class<?>[t_cSuperClasses.size()]);

        return result;
    }

    /**
     * Recursively retrieves the parent classes.
     * @param objectClass the object class.
     * @param <T> the type.
     * @param <U> the parent class type.
     * @return the parent classes.
     */
    @NotNull
    protected <T extends U, U> Collection<Class<U>> retrieveParentClasses(@NotNull final Class<T> objectClass) {
        @NotNull
        final Collection<Class<U>> result;

        @SuppressWarnings("unchecked")
        @Nullable
        final Class<U> parent = (Class<U>) objectClass.getSuperclass();

        if (parent != null) {
            result = new ArrayList<>();

            result.add(parent);

            result.addAll(this.<U, U>retrieveParentClasses(parent));
        } else {
            result = new ArrayList<>(0);
        }

        return result;
    }

    /**
     * Retrieves the members (not only public ones)
     * matching a concrete type, declared in given class.
     * This method will likely fail under a security manager
     * without explicit permissions.
     * @param classInstance the class instance.
     * @param type the type to match.
     * @return such member instance.
     * @param <C> the class type.
     * @param <T> the type.
     */
    @NotNull
    public <C, T> Field[] getMember(@NotNull final Class<C> classInstance, @NotNull final Class<T> type) {
        @NotNull
        final Collection<Field> t_cResult = new ArrayList<>();

        @NotNull
        final Class<?>[] t_aClasses = retrieveSuperClasses(classInstance);

        for (@NotNull
        final Class<?> t_Class : t_aClasses) {
            t_cResult.addAll(getClassMembersAsCollection(t_Class, type));
        }

        return t_cResult.toArray(new Field[t_cResult.size()]);
    }

    /**
     * Retrieves the members (not only public ones)
     * matching a concrete type, declared in given class.
     * This method will likely fail under a security manager
     * without explicit permissions.
     * @param classInstance the class instance.
     * @param type the type to match.
     * @param <C> the class type.
     * @param <T> the type.
     * @return such member instance.
     */
    @NotNull
    public <C, T> Field[] getClassMembers(@NotNull final Class<C> classInstance, @NotNull final Class<T> type) {
        @NotNull
        final Field[] result;

        @NotNull
        final Collection<Field> t_cMembers = getClassMembersAsCollection(classInstance, type);

        if (t_cMembers.size() == 0) {
            result = t_cMembers.toArray(new Field[t_cMembers.size()]);
        } else {
            result = EMPTY_FIELD_ARRAY;
        }

        return result;
    }

    /**
     * Retrieves the members (not only public ones)
     * matching a concrete type, declared in given class.
     * This method will likely fail under a security manager
     * without explicit permissions.
     * @param classInstance the class instance.
     * @param type the type to match.
     * @param <C> the class type.
     * @param <T> the type.
     * @return such fields.
     */
    @SuppressWarnings("unchecked")
    @NotNull
    public <C, T> Collection<Field> getClassMembersAsCollection(@NotNull final Class<C> classInstance,
            @NotNull final Class<T> type) {
        @NotNull
        final Collection<Field> result = new ArrayList<>();

        @Nullable
        Field[] t_Aux = null;

        try {
            t_Aux = classInstance.getDeclaredFields();
        } catch (@NotNull final Throwable throwable) {
            try {
                LogFactory.getLog(ReflectionUtils.class).warn("Cannot retrieve " + classInstance + " members",
                        throwable);
            } catch (final Throwable classLoadingProblem) {
                System.err.println(Literals.ERROR_USING_COMMONS_LOGGING_THIS_CAN_HAPPEN
                        + Literals.DUE_TO_LOG4_J_CLASS_LOADING_ISSUES);

                classLoadingProblem.printStackTrace(System.err);
            }
        }

        if (t_Aux != null) {
            for (@Nullable
            final Field t_CurrentField : t_Aux) {
                if (t_CurrentField != null) {
                    @Nullable
                    final Class<?> t_CurrentMemberClass = t_CurrentField.getType();

                    if ((t_CurrentMemberClass != null)
                            && ((t_CurrentMemberClass.equals(type) || (type.isAssignableFrom(t_CurrentMemberClass))
                                    || (t_CurrentMemberClass.isAssignableFrom(type))))) {
                        result.add(t_CurrentField);
                    }
                }
            }
        }

        return result;
    }

    /**
     * Retrieves the value of given field, for a concrete instance.
     * @param instance the instance.
     * @param field the field.
     * @return the field value, on given instance, or <code>null</code>
     * if such field doesn't exist or is not accessible.
     */
    @Nullable
    public Object getFieldValue(@NotNull final Object instance, @NotNull final Field field) {
        @Nullable
        Object result = null;

        try {
            result = field.get(instance);
        } catch (final Throwable throwable) {
            try {
                LogFactory.getLog(ReflectionUtils.class).info(
                        "Cannot retrieve the value of " + "the field " + field + " on instance " + instance,
                        throwable);
            } catch (final Throwable classLoadingProblem) {
                System.err.println(Literals.ERROR_USING_COMMONS_LOGGING_THIS_CAN_HAPPEN
                        + Literals.DUE_TO_LOG4_J_CLASS_LOADING_ISSUES);

                classLoadingProblem.printStackTrace(System.err);
            }
        }

        return result;
    }

    /**
     * Returns the thread context class loader if available.
     * The thread context class loader is available for JDK 1.2
     * or later, if certain security conditions are met.
     * Note: This logic is adapted from Commons-Logging.
     * @return the class loader.
     * @throws IllegalAccessException when trying to access
     * <code>Thread.getContextClassLoader()</code> via reflection.
     * @throws InvocationTargetException when trying to access
     * <code>Thread.getContextClassLoader()</code> via reflection, and
     * the target exception is not a <code>SecurityException</code>..
     */
    @NotNull
    public ClassLoader getContextClassLoader() throws IllegalAccessException, InvocationTargetException {
        @Nullable
        ClassLoader result = null;

        try {
            // Are we running on a JDK 1.2 or later system?
            @NotNull
            final Method t_Method = Thread.class.getMethod("getContextClassLoader", (Class<?>) null);

            // Get the thread context class loader (if there is one)
            try {
                result = (ClassLoader) t_Method.invoke(Thread.currentThread(), (Class<?>) null);
            } catch (final InvocationTargetException invocationTargetException) {
                /**
                 * InvocationTargetException is thrown by 'invoke' when
                 * the method being invoked (getContextClassLoader) throws
                 * an exception.
                 *
                 * getContextClassLoader() throws SecurityException when
                 * the context class loader isn't an ancestor of the
                 * calling class's class loader, or if security
                 * permissions are restricted.
                 *
                 * In the first case (not related), we want to ignore and
                 * keep going.  We cannot help but also ignore the second
                 * with the logic below, but other calls elsewhere (to
                 * obtain a class loader) will trigger this exception where
                 * we can make a distinction.
                 */
                if (invocationTargetException.getTargetException() instanceof SecurityException) {
                    LogFactory.getLog(ReflectionUtils.class).info("Could not retrieve context class loader.",
                            invocationTargetException);
                } else {
                    throw invocationTargetException;
                }
            }
        } catch (final NoSuchMethodException noSuchMethodException) {
            // Assume we are running on JDK 1.1
            result = ReflectionUtils.class.getClassLoader();
        }
        if (result == null) {
            result = ReflectionUtils.class.getClassLoader();
        }

        // Return the selected class loader
        return result;
    }
}