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

com.alee.utils.ReflectUtils Maven / Gradle / Ivy

The newest version!
/*
 * This file is part of WebLookAndFeel library.
 *
 * WebLookAndFeel 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 3 of the License, or
 * (at your option) any later version.
 *
 * WebLookAndFeel 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 WebLookAndFeel library.  If not, see .
 */

package com.alee.utils;

import com.alee.api.annotations.NotNull;
import com.alee.api.annotations.Nullable;
import com.alee.utils.collection.ImmutableList;
import com.alee.utils.reflection.FieldHelper;
import com.alee.utils.reflection.ModifierType;
import com.alee.utils.reflection.ReflectionException;
import org.slf4j.LoggerFactory;

import java.lang.reflect.*;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.*;

/**
 * This class provides a set of utilities to simplify work with Reflection API.
 *
 * @author Mikle Garin
 */
public final class ReflectUtils
{
    /**
     * todo 1. Remove all "safe" methods
     * todo 2. Rework this utility class into an object that is only instantiated when needed
     * todo 3. Add implemenetation for vararg search
     */

    /**
     * Whether should allow safe methods to log errors or not.
     * By default it is disabled to hide some WebLaF exceptions which occur due to various method checks.
     * You can enable it in case you need a deeper look into whats happening here.
     */
    private static boolean safeMethodsLoggingEnabled = false;

    /**
     * Fields lookup cache.
     */
    private static final Map> fieldsLookupCache = new HashMap> ();

    /**
     * Methods lookup cache.
     */
    private static final Map> methodsLookupCache = new HashMap> ();

    /**
     * {@code jdk.internal.loader.BuiltinClassLoader} class available starting from Java 9.
     * It's {@code jdk.internal.loader.ClassLoaders.AppClassLoader} extension is used as default application {@link ClassLoader}.
     */
    private static final String JAVA9_BUILT_IN_CLASS_LOADER = "jdk.internal.loader.BuiltinClassLoader";

    /**
     * Private constructor to avoid instantiation.
     */
    private ReflectUtils ()
    {
        throw new UtilityException ( "Utility classes are not meant to be instantiated" );
    }

    /**
     * Returns whether should allow safe methods to log errors or not.
     *
     * @return {@code true} if should allow safe methods to log errors, {@code false} otherwise
     */
    public static boolean isSafeMethodsLoggingEnabled ()
    {
        return safeMethodsLoggingEnabled;
    }

    /**
     * Sets whether should allow safe methods to log errors or not.
     *
     * @param enabled whether should allow safe methods to log errors or not
     */
    public static void setSafeMethodsLoggingEnabled ( final boolean enabled )
    {
        ReflectUtils.safeMethodsLoggingEnabled = enabled;
    }

    /**
     * Returns class for the specified canonical name.
     *
     * @param canonicalName class canonical name
     * @param            class type
     * @return class for the specified canonical name
     */
    @Nullable
    public static  Class getClassSafely ( @NotNull final String canonicalName )
    {
        try
        {
            return getClass ( canonicalName );
        }
        catch ( final Exception e )
        {
            if ( safeMethodsLoggingEnabled )
            {
                final String msg = "ReflectionUtils method failed: getClassSafely ( %s )";
                LoggerFactory.getLogger ( ReflectUtils.class ).error ( String.format ( msg, canonicalName ), e );
            }
            return null;
        }
    }

    /**
     * Returns class for the specified canonical name.
     *
     * @param canonicalName class canonical name
     * @param            class type
     * @return class for the specified canonical name
     * @throws ClassNotFoundException if class was not found
     */
    @NotNull
    public static  Class getClass ( @NotNull final String canonicalName ) throws ClassNotFoundException
    {
        return ( Class ) Class.forName ( canonicalName );
    }

    /**
     * Returns inner class with the specified name.
     *
     * @param fromClass      class to look for the inner class
     * @param innerClassName inner class name
     * @param             inner class type
     * @return inner class with the specified name
     */
    public static  Class getInnerClassSafely ( final Class fromClass, final String innerClassName )
    {
        return getInnerClassSafely ( fromClass.getCanonicalName (), innerClassName );
    }

    /**
     * Returns inner class with the specified name.
     *
     * @param fromClassName  name of the class to look for the inner class
     * @param innerClassName inner class name
     * @param             inner class type
     * @return inner class with the specified name
     */
    public static  Class getInnerClassSafely ( final String fromClassName, final String innerClassName )
    {
        try
        {
            return getInnerClass ( fromClassName, innerClassName );
        }
        catch ( final Exception e )
        {
            if ( safeMethodsLoggingEnabled )
            {
                final String msg = "ReflectionUtils method failed: getInnerClassSafely ( %s, %s )";
                LoggerFactory.getLogger ( ReflectUtils.class ).error ( String.format ( msg, fromClassName, innerClassName ), e );
            }
            return null;
        }
    }

    /**
     * Returns inner class with the specified name.
     *
     * @param fromClass      class to look for the inner class
     * @param innerClassName inner class name
     * @param             inner class type
     * @return inner class with the specified name
     * @throws ClassNotFoundException if inner class was not found
     */
    public static  Class getInnerClass ( final Class fromClass, final String innerClassName ) throws ClassNotFoundException
    {
        return getInnerClass ( fromClass.getCanonicalName (), innerClassName );
    }

    /**
     * Returns inner class with the specified name.
     *
     * @param fromClassName  name of the class to look for the inner class
     * @param innerClassName inner class name
     * @param             inner class type
     * @return inner class with the specified name
     * @throws ClassNotFoundException if inner class was not found
     */
    public static  Class getInnerClass ( final String fromClassName, final String innerClassName ) throws ClassNotFoundException
    {
        return getClass ( fromClassName + "$" + innerClassName );
    }

    /**
     * Returns method caller class.
     * It is not recommended to use this method anywhere but in debugging.
     *
     * @return method caller class
     */
    public static Class getCallerClass ()
    {
        // We have to add one to depth since this method call is increasing it
        return getCallerClass ( 1 );
    }

    /**
     * Returns method caller class.
     * It is not recommended to use this method anywhere but in debugging.
     *
     * @param additionalDepth additional methods depth
     * @return method caller class
     */
    public static Class getCallerClass ( final int additionalDepth )
    {
        // Depth explanation:
        // 0 - this method class
        // 1 - this method caller class
        // 2 - caller's class caller
        // additionalDepth - in case call goes through additional methods this is required
        final int depth = 2 + additionalDepth;

        try
        {
            // We add additional 3 levels of depth due to reflection calls here
            return callStaticMethod ( "sun.reflect.Reflection", "getCallerClass", depth + 3 );
        }
        catch ( final Exception e )
        {
            try
            {
                // Simply use determined depth
                return getClass ( new Throwable ().getStackTrace ()[ depth ].getClassName () );
            }
            catch ( final ClassNotFoundException ex )
            {
                return null;
            }
        }
    }

    /**
     * Returns all fields in the specified object class and all of its superclasses.
     *
     * @param object object to find fields for
     * @return all fields in the specified object class and all of its superclasses
     */
    public static List getFields ( final Object object )
    {
        return getFields ( object.getClass () );
    }

    /**
     * Returns all fields in the specified class and all of its superclasses.
     *
     * @param clazz class to find fields for
     * @return all fields in the specified class and all of its superclasses
     */
    public static List getFields ( final Class clazz )
    {
        return getFields ( clazz, ModifierType.STATIC );
    }

    /**
     * Returns all fields in the specified object class and all of its superclasses.
     *
     * @param object           object to find fields for
     * @param ignoredModifiers modifiers of fields to ignore
     * @return all fields in the specified object class and all of its superclasses
     */
    public static List getFields ( final Object object, final ModifierType... ignoredModifiers )
    {
        return getFields ( object.getClass (), ignoredModifiers );
    }

    /**
     * Returns all fields in the specified class and all of its superclasses.
     *
     * @param clazz            class to find fields for
     * @param ignoredModifiers modifiers of fields to ignore
     * @return all fields in the specified class and all of its superclasses
     */
    public static List getFields ( final Class clazz, final ModifierType... ignoredModifiers )
    {
        return getFields ( clazz, new HashSet (), ignoredModifiers );
    }

    /**
     * Returns all fields in the specified class and all of its superclasses.
     *
     * @param clazz            class to find fields for
     * @param found            found field names
     * @param ignoredModifiers modifiers of fields to ignore
     * @return all fields in the specified class and all of its superclasses
     */
    private static List getFields ( final Class clazz, final Set found, final ModifierType... ignoredModifiers )
    {
        // Find all current-level fields
        final Field[] declared = clazz.getDeclaredFields ();
        final List fields = new ArrayList ( declared.length );
        for ( final Field field : declared )
        {
            // Adding fields with unique name that haven't been found yet (on higher hierarchy levels)
            // and that do not contain any modifiers from the ignore list passed into this methos
            if ( !found.contains ( field.getName () ) && ReflectUtils.hasNoneOfModifiers ( field, ignoredModifiers ) )
            {
                // Making field accessible for usage convenience
                field.setAccessible ( true );

                // Collecting field
                fields.add ( field );

                // Marking unique field name as used
                // This is important to avoid overwriting fields with ones from parent classes (with the same name)
                found.add ( field.getName () );
            }
        }

        // Find all superclass fields
        final Class superclass = clazz.getSuperclass ();
        if ( superclass != null )
        {
            fields.addAll ( getFields ( superclass, found, ignoredModifiers ) );
        }

        return fields;
    }

    /**
     * Returns whether or not {@link Class} has any of the specified modifiers.
     *
     * @param clazz     {@link Class} to check modifiers for
     * @param modifiers modifiers to look for
     * @return {@code true} if {@link Class} has any of the specified modifiers, {@code false} otherwise
     */
    public static boolean hasAnyOfModifiers ( final Class clazz, final ModifierType... modifiers )
    {
        return hasAnyOfModifiers ( clazz, new ImmutableList ( modifiers ) );
    }

    /**
     * Returns whether or not {@link Class} has any of the specified modifiers.
     *
     * @param clazz     {@link Class} to check modifiers for
     * @param modifiers modifiers to look for
     * @return {@code true} if {@link Class} has any of the specified modifiers, {@code false} otherwise
     */
    public static boolean hasAnyOfModifiers ( final Class clazz, final Collection modifiers )
    {
        boolean contains = false;
        if ( CollectionUtils.notEmpty ( modifiers ) )
        {
            for ( final ModifierType modifier : modifiers )
            {
                if ( modifier.is ( clazz ) )
                {
                    contains = true;
                    break;
                }
            }
        }
        return contains;
    }

    /**
     * Returns whether or not {@link Class} has all of the specified modifiers.
     *
     * @param clazz     {@link Class} to check modifiers for
     * @param modifiers modifiers to look for
     * @return {@code true} if {@link Class} has all of the specified modifiers, {@code false} otherwise
     */
    public static boolean hasAllOfModifiers ( final Class clazz, final ModifierType... modifiers )
    {
        return hasAllOfModifiers ( clazz, new ImmutableList ( modifiers ) );
    }

    /**
     * Returns whether or not {@link Class} has all of the specified modifiers.
     *
     * @param clazz     {@link Class} to check modifiers for
     * @param modifiers modifiers to look for
     * @return {@code true} if {@link Class} has all of the specified modifiers, {@code false} otherwise
     */
    public static boolean hasAllOfModifiers ( final Class clazz, final Collection modifiers )
    {
        boolean contains = true;
        if ( ArrayUtils.notEmpty ( modifiers ) )
        {
            for ( final ModifierType modifier : modifiers )
            {
                if ( modifier.not ( clazz ) )
                {
                    contains = false;
                    break;
                }
            }
        }
        return contains;
    }

    /**
     * Returns whether or not {@link Class} has none of the specified modifiers.
     *
     * @param clazz     {@link Class} to check modifiers for
     * @param modifiers modifiers to look for
     * @return {@code true} if {@link Class} has none of the specified modifiers, {@code false} otherwise
     */
    public static boolean hasNoneOfModifiers ( final Class clazz, final ModifierType... modifiers )
    {
        return hasNoneOfModifiers ( clazz, new ImmutableList ( modifiers ) );
    }

    /**
     * Returns whether or not {@link Class} has none of the specified modifiers.
     *
     * @param clazz     {@link Class} to check modifiers for
     * @param modifiers modifiers to look for
     * @return {@code true} if {@link Class} has none of the specified modifiers, {@code false} otherwise
     */
    public static boolean hasNoneOfModifiers ( final Class clazz, final Collection modifiers )
    {
        return !hasAnyOfModifiers ( clazz, modifiers );
    }

    /**
     * Returns whether or not {@link Method} has any of the specified modifiers.
     *
     * @param method    {@link Method} to check modifiers for
     * @param modifiers modifiers to look for
     * @return {@code true} if {@link Method} has any of the specified modifiers, {@code false} otherwise
     */
    public static boolean hasAnyOfModifiers ( final Method method, final ModifierType... modifiers )
    {
        return hasAnyOfModifiers ( method, new ImmutableList ( modifiers ) );
    }

    /**
     * Returns whether or not {@link Method} has any of the specified modifiers.
     *
     * @param method    {@link Method} to check modifiers for
     * @param modifiers modifiers to look for
     * @return {@code true} if {@link Method} has any of the specified modifiers, {@code false} otherwise
     */
    public static boolean hasAnyOfModifiers ( final Method method, final Collection modifiers )
    {
        boolean contains = false;
        if ( CollectionUtils.notEmpty ( modifiers ) )
        {
            for ( final ModifierType modifier : modifiers )
            {
                if ( modifier.is ( method ) )
                {
                    contains = true;
                    break;
                }
            }
        }
        return contains;
    }

    /**
     * Returns whether or not {@link Method} has all of the specified modifiers.
     *
     * @param method    {@link Method} to check modifiers for
     * @param modifiers modifiers to look for
     * @return {@code true} if {@link Method} has all of the specified modifiers, {@code false} otherwise
     */
    public static boolean hasAllOfModifiers ( final Method method, final ModifierType... modifiers )
    {
        return hasAllOfModifiers ( method, new ImmutableList ( modifiers ) );
    }

    /**
     * Returns whether or not {@link Method} has all of the specified modifiers.
     *
     * @param method    {@link Method} to check modifiers for
     * @param modifiers modifiers to look for
     * @return {@code true} if {@link Method} has all of the specified modifiers, {@code false} otherwise
     */
    public static boolean hasAllOfModifiers ( final Method method, final Collection modifiers )
    {
        boolean contains = true;
        if ( ArrayUtils.notEmpty ( modifiers ) )
        {
            for ( final ModifierType modifier : modifiers )
            {
                if ( modifier.not ( method ) )
                {
                    contains = false;
                    break;
                }
            }
        }
        return contains;
    }

    /**
     * Returns whether or not {@link Method} has none of the specified modifiers.
     *
     * @param method    {@link Method} to check modifiers for
     * @param modifiers modifiers to look for
     * @return {@code true} if {@link Method} has none of the specified modifiers, {@code false} otherwise
     */
    public static boolean hasNoneOfModifiers ( final Method method, final ModifierType... modifiers )
    {
        return hasNoneOfModifiers ( method, new ImmutableList ( modifiers ) );
    }

    /**
     * Returns whether or not {@link Method} has none of the specified modifiers.
     *
     * @param method    {@link Method} to check modifiers for
     * @param modifiers modifiers to look for
     * @return {@code true} if {@link Method} has none of the specified modifiers, {@code false} otherwise
     */
    public static boolean hasNoneOfModifiers ( final Method method, final Collection modifiers )
    {
        return !hasAnyOfModifiers ( method, modifiers );
    }

    /**
     * Returns whether or not {@link Field} has any of the specified modifiers.
     *
     * @param field     {@link Field} to check modifiers for
     * @param modifiers modifiers to look for
     * @return {@code true} if {@link Field} has any of the specified modifiers, {@code false} otherwise
     */
    public static boolean hasAnyOfModifiers ( final Field field, final ModifierType... modifiers )
    {
        return hasAnyOfModifiers ( field, new ImmutableList ( modifiers ) );
    }

    /**
     * Returns whether or not {@link Field} has any of the specified modifiers.
     *
     * @param field     {@link Field} to check modifiers for
     * @param modifiers modifiers to look for
     * @return {@code true} if {@link Field} has any of the specified modifiers, {@code false} otherwise
     */
    public static boolean hasAnyOfModifiers ( final Field field, final Collection modifiers )
    {
        boolean contains = false;
        if ( CollectionUtils.notEmpty ( modifiers ) )
        {
            for ( final ModifierType modifier : modifiers )
            {
                if ( modifier.is ( field ) )
                {
                    contains = true;
                    break;
                }
            }
        }
        return contains;
    }

    /**
     * Returns whether or not {@link Field} has all of the specified modifiers.
     *
     * @param field     {@link Field} to check modifiers for
     * @param modifiers modifiers to look for
     * @return {@code true} if {@link Field} has all of the specified modifiers, {@code false} otherwise
     */
    public static boolean hasAllOfModifiers ( final Field field, final ModifierType... modifiers )
    {
        return hasAllOfModifiers ( field, new ImmutableList ( modifiers ) );
    }

    /**
     * Returns whether or not {@link Field} has all of the specified modifiers.
     *
     * @param field     {@link Field} to check modifiers for
     * @param modifiers modifiers to look for
     * @return {@code true} if {@link Field} has all of the specified modifiers, {@code false} otherwise
     */
    public static boolean hasAllOfModifiers ( final Field field, final Collection modifiers )
    {
        boolean contains = true;
        if ( ArrayUtils.notEmpty ( modifiers ) )
        {
            for ( final ModifierType modifier : modifiers )
            {
                if ( modifier.not ( field ) )
                {
                    contains = false;
                    break;
                }
            }
        }
        return contains;
    }

    /**
     * Returns whether or not {@link Field} has none of the specified modifiers.
     *
     * @param field     {@link Field} to check modifiers for
     * @param modifiers modifiers to look for
     * @return {@code true} if {@link Field} has none of the specified modifiers, {@code false} otherwise
     */
    public static boolean hasNoneOfModifiers ( final Field field, final ModifierType... modifiers )
    {
        return hasNoneOfModifiers ( field, new ImmutableList ( modifiers ) );
    }

    /**
     * Returns whether or not {@link Field} has none of the specified modifiers.
     *
     * @param field     {@link Field} to check modifiers for
     * @param modifiers modifiers to look for
     * @return {@code true} if {@link Field} has none of the specified modifiers, {@code false} otherwise
     */
    public static boolean hasNoneOfModifiers ( final Field field, final Collection modifiers )
    {
        return !hasAnyOfModifiers ( field, modifiers );
    }

    /**
     * Returns specified class field.
     * This method will also look for the field in super-classes if any exist.
     *
     * @param classType type of the class where field can be located
     * @param fieldName field name
     * @return specified class field
     */
    @Nullable
    public static Field getFieldSafely ( @NotNull final Class classType, @NotNull final String fieldName )
    {
        Field field = null;
        try
        {
            field = getField ( classType, fieldName );
        }
        catch ( final NoSuchFieldException e )
        {
            if ( safeMethodsLoggingEnabled )
            {
                final String msg = "ReflectionUtils method failed: getFieldSafely ( %s, %s )";
                LoggerFactory.getLogger ( ReflectUtils.class ).error ( String.format ( msg, classType, fieldName ), e );
            }
        }
        return field;
    }

    /**
     * Returns specified class field.
     * If field is not found in the object class all superclasses will be searched for that field.
     * This method will also find {@code protected}, {@code private} and package local fields.
     *
     * @param classType type of the class where field can be located
     * @param fieldName field name
     * @return specified class field
     * @throws NoSuchFieldException if field was not found
     */
    @NotNull
    public static Field getField ( @NotNull final Class classType, @NotNull final String fieldName ) throws NoSuchFieldException
    {
        // Field key
        final String canonicalName = classType.getCanonicalName ();
        final String key = canonicalName + "." + fieldName;

        // Checking cache existence
        Field field = null;
        Map classFieldsCache = fieldsLookupCache.get ( classType );
        if ( classFieldsCache != null )
        {
            field = classFieldsCache.get ( key );
        }
        else
        {
            classFieldsCache = new HashMap ( 1 );
            fieldsLookupCache.put ( classType, classFieldsCache );
        }

        // Updating cache
        if ( field == null )
        {
            // Trying to retrieve field from class or one of its superclasses
            field = getFieldImpl ( classType, fieldName );

            // Trying to retrieve static field from interface
            if ( field == null )
            {
                field = getInterfaceFieldImpl ( classType, fieldName );
            }

            // Checking field existence
            if ( field != null )
            {
                field.setAccessible ( true );
            }
            else
            {
                final String msg = "Field '%s' not found in class: %s";
                throw new NoSuchFieldException ( String.format ( msg, fieldName, canonicalName ) );
            }

            // Caching field
            classFieldsCache.put ( key, field );
        }

        return field;
    }

    /**
     * Returns specified class field.
     * This method will also look for the field in super-classes if any exist.
     *
     * @param classType type of the class where field can be located
     * @param fieldName field name
     * @return specified class field
     */
    private static Field getFieldImpl ( final Class classType, final String fieldName )
    {
        Field field;
        try
        {
            field = classType.getDeclaredField ( fieldName );
        }
        catch ( final NoSuchFieldException e )
        {
            final Class superclass = classType.getSuperclass ();
            field = superclass != null ? getFieldImpl ( superclass, fieldName ) : null;
        }
        return field;
    }

    /**
     * Returns specified class interface static field.
     * This method will also look for the field in super-class interfaces if any exist.
     *
     * @param classType type of the interface where field can be located
     * @param fieldName field name
     * @return specified class interface static field
     */
    private static Field getInterfaceFieldImpl ( final Class classType, final String fieldName )
    {
        Field field = null;
        if ( classType.isInterface () )
        {
            final Field[] fields = classType.getDeclaredFields ();
            for ( final Field f : fields )
            {
                if ( f.getName ().equals ( fieldName ) )
                {
                    field = f;
                    break;
                }
            }
        }
        if ( field == null )
        {
            final Class[] interfaces = classType.getInterfaces ();
            for ( final Class iface : interfaces )
            {
                field = getInterfaceFieldImpl ( iface, fieldName );
                if ( field != null )
                {
                    break;
                }
            }
        }
        if ( field == null )
        {
            final Class superclass = classType.getSuperclass ();
            field = superclass != null ? getInterfaceFieldImpl ( superclass, fieldName ) : null;
        }
        return field;
    }

    /**
     * Returns specified class field's type.
     * This method will also look for the field in super-classes if any exist.
     *
     * @param classType type of the class where field can be located
     * @param fieldName field name
     * @return specified class field's type
     */
    public static Class getFieldTypeSafely ( final Class classType, final String fieldName )
    {
        try
        {
            return getFieldType ( classType, fieldName );
        }
        catch ( final NoSuchFieldException e )
        {
            if ( safeMethodsLoggingEnabled )
            {
                final String msg = "ReflectionUtils method failed: getFieldTypeSafely ( %s, %s )";
                LoggerFactory.getLogger ( ReflectUtils.class ).error ( String.format ( msg, classType, fieldName ), e );
            }
            return null;
        }
    }

    /**
     * Returns specified class field's type.
     * This method will also look for the field in super-classes if any exist.
     *
     * @param classType type of the class where field can be located
     * @param fieldName field name
     * @return specified class field's type
     * @throws NoSuchFieldException if field was not found
     */
    public static Class getFieldType ( final Class classType, final String fieldName ) throws NoSuchFieldException
    {
        return getField ( classType, fieldName ).getType ();
    }

    /**
     * Applies specified value to object field.
     * This method allows to access and modify even private fields.
     *
     * @param object object instance
     * @param field  object field
     * @param value  field value
     * @return {@code true} if value was applied successfully, {@code false} otherwise
     */
    public static boolean setFieldValueSafely ( final Object object, final String field, final Object value )
    {
        try
        {
            setFieldValue ( object, field, value );
            return true;
        }
        catch ( final Exception e )
        {
            if ( safeMethodsLoggingEnabled )
            {
                final String msg = "ReflectionUtils method failed: setFieldValueSafely";
                LoggerFactory.getLogger ( ReflectUtils.class ).error ( msg, e );
            }
            return false;
        }
    }

    /**
     * Applies specified value to object field.
     * This method allows to access and modify even private fields.
     *
     * @param object    object instance
     * @param fieldName object field name
     * @param value     field value
     * @throws NoSuchFieldException   if field was not found
     * @throws IllegalAccessException if field is inaccessible
     */
    public static void setFieldValue ( final Object object, final String fieldName, final Object value )
            throws NoSuchFieldException, IllegalAccessException
    {
        // Retrieving actual field
        final Field actualField = getField ( object.getClass (), fieldName );

        // Applying field value
        setFieldValue ( object, actualField, value );
    }

    /**
     * Applies specified value to static class field.
     * This method allows to access and modify even private fields.
     *
     * @param classType type of the class where static field can be located
     * @param field     object field
     * @param value     field value
     * @return {@code true} if value was applied successfully, {@code false} otherwise
     */
    public static boolean setStaticFieldValueSafely ( final Class classType, final String field, final Object value )
    {
        try
        {
            setStaticFieldValue ( classType, field, value );
            return true;
        }
        catch ( final Exception e )
        {
            if ( safeMethodsLoggingEnabled )
            {
                final String msg = "ReflectionUtils method failed: setFieldValueSafely";
                LoggerFactory.getLogger ( ReflectUtils.class ).error ( msg, e );
            }
            return false;
        }
    }

    /**
     * Applies specified value to static class field.
     * This method allows to access and modify even private fields.
     *
     * @param classType type of the class where static field can be located
     * @param fieldName object field name
     * @param value     field value
     * @throws NoSuchFieldException   if field was not found
     * @throws IllegalAccessException if field is inaccessible
     */
    public static void setStaticFieldValue ( final Class classType, final String fieldName, final Object value )
            throws NoSuchFieldException, IllegalAccessException
    {
        // Retrieving actual field
        final Field actualField = getField ( classType, fieldName );

        // Applying field value
        setFieldValue ( null, actualField, value );
    }

    /**
     * Applies specified value to object field.
     * This method allows to access and modify even private object fields.
     *
     * @param object object instance
     * @param field  object field
     * @param value  field value
     * @return {@code true} if value was applied successfully, {@code false} otherwise
     */
    public static boolean setFieldValueSafely ( final Object object, final Field field, final Object value )
    {
        try
        {
            setFieldValue ( object, field, value );
            return true;
        }
        catch ( final Exception e )
        {
            if ( safeMethodsLoggingEnabled )
            {
                final String msg = "ReflectionUtils method failed: setFieldValueSafely";
                LoggerFactory.getLogger ( ReflectUtils.class ).error ( msg, e );
            }
            return false;
        }
    }

    /**
     * Applies specified value to object field.
     * This method allows to access and modify even private object fields.
     *
     * @param object object instance
     * @param field  object field
     * @param value  field value
     * @throws IllegalAccessException if field is inaccessible
     */
    public static void setFieldValue ( final Object object, final Field field, final Object value )
            throws IllegalAccessException
    {
        // Making field accessible
        if ( !field.isAccessible () )
        {
            field.setAccessible ( true );
        }

        // Removing final modifier if needed
        final int oldModifiers = field.getModifiers ();
        if ( ModifierType.FINAL.is ( oldModifiers ) )
        {
            // todo This shouldn't really be called ever by WebLaF itself under normal circumstances
            FieldHelper.setFieldModifiers ( field, oldModifiers & ~Modifier.FINAL );
        }

        // Updating field value
        field.set ( object, value );

        // Restoring final modifier if it was removed
        if ( ModifierType.FINAL.is ( oldModifiers ) )
        {
            FieldHelper.setFieldModifiers ( field, oldModifiers );
        }
    }

    /**
     * Returns object field value.
     * This method allows to access even private object fields.
     *
     * @param object    object instance
     * @param fieldName object field name
     * @param        field value type
     * @return object field value
     */
    public static  T getFieldValueSafely ( final Object object, final String fieldName )
    {
        try
        {
            return getFieldValue ( object, fieldName );
        }
        catch ( final Exception e )
        {
            if ( safeMethodsLoggingEnabled )
            {
                final String msg = "ReflectionUtils method failed: getFieldValueSafely";
                LoggerFactory.getLogger ( ReflectUtils.class ).error ( msg, e );
            }
            return null;
        }
    }

    /**
     * Returns object field value.
     * This method allows to access even private object fields.
     *
     * @param object    object instance
     * @param fieldName object field name
     * @param        field value type
     * @return object field value
     * @throws NoSuchFieldException   if field was not found
     * @throws IllegalAccessException if field is inaccessible
     */
    public static  T getFieldValue ( final Object object, final String fieldName ) throws NoSuchFieldException, IllegalAccessException
    {
        final Field actualField = getField ( object.getClass (), fieldName );
        ModifierType.STATIC.checkNot ( actualField );
        return ( T ) actualField.get ( object );
    }

    /**
     * Returns static field value from the specified class.
     *
     * @param classType class type
     * @param fieldName class field name
     * @param        returned value type
     * @return static field value from the specified class
     */
    public static  T getStaticFieldValueSafely ( final Class classType, final String fieldName )
    {
        try
        {
            return getStaticFieldValue ( classType, fieldName );
        }
        catch ( final Exception e )
        {
            if ( safeMethodsLoggingEnabled )
            {
                final String msg = "ReflectionUtils method failed: getStaticFieldValueSafely";
                LoggerFactory.getLogger ( ReflectUtils.class ).error ( msg, e );
            }
            return null;
        }
    }

    /**
     * Returns static field value from the specified class.
     *
     * @param classType class type
     * @param fieldName class field name
     * @param        returned value type
     * @return static field value from the specified class
     * @throws NoSuchFieldException   if field was not found
     * @throws IllegalAccessException if field is inaccessible
     */
    public static  T getStaticFieldValue ( final Class classType, final String fieldName )
            throws NoSuchFieldException, IllegalAccessException
    {
        final Field actualField = getField ( classType, fieldName );
        ModifierType.STATIC.check ( actualField );
        return ( T ) actualField.get ( null );
    }

    /**
     * Returns class name with ".java" extension in the end.
     *
     * @param classObject object of class type
     * @return class name with ".java" extension in the end
     */
    @NotNull
    public static String getJavaClassName ( @NotNull final Object classObject )
    {
        return getJavaClassName ( classObject.getClass () );
    }

    /**
     * Returns class name with ".java" extension in the end.
     *
     * @param classType class type
     * @return class name with ".java" extension in the end
     */
    @NotNull
    public static String getJavaClassName ( @NotNull final Class classType )
    {
        return getClassName ( classType ) + ".java";
    }

    /**
     * Returns class name with ".class" extension in the end.
     *
     * @param classObject object of class type
     * @return class name with ".class" extension in the end
     */
    @NotNull
    public static String getClassFileName ( @NotNull final Object classObject )
    {
        return getClassFileName ( classObject.getClass () );
    }

    /**
     * Returns class name with ".class" extension in the end.
     *
     * @param classType class type
     * @return class name with ".class" extension in the end
     */
    @NotNull
    public static String getClassFileName ( @NotNull final Class classType )
    {
        return ReflectUtils.getClassName ( classType ) + ".class";
    }

    /**
     * Returns class name.
     *
     * @param classObject object of class type
     * @return class name
     */
    @NotNull
    public static String getClassName ( @NotNull final Object classObject )
    {
        return getClassName ( classObject.getClass () );
    }

    /**
     * Returns class name.
     *
     * @param classType class type
     * @return class name
     */
    @NotNull
    public static String getClassName ( @NotNull final Class classType )
    {
        final String canonicalName = classType.getCanonicalName ();
        final String fullName = canonicalName != null ? canonicalName : classType.toString ();
        final int dot = fullName.lastIndexOf ( "." );
        return dot != -1 ? fullName.substring ( dot + 1 ) : fullName;
    }

    /**
     * Returns complete class name that includes enclosing class name if one exists.
     *
     * @param classObject object of class type
     * @return complete class name that includes enclosing class name if one exists
     */
    @NotNull
    public static String getCompleteClassName ( @NotNull final Object classObject )
    {
        return getCompleteClassName ( classObject.getClass () );
    }

    /**
     * Returns complete class name that includes enclosing class name if one exists.
     *
     * @param classType class type
     * @return complete class name that includes enclosing class name if one exists
     */
    @NotNull
    public static String getCompleteClassName ( @NotNull final Class classType )
    {
        final String name = getClassName ( classType );
        final Class enclosingClass = classType.getEnclosingClass ();
        return enclosingClass != null ? getCompleteClassName ( enclosingClass ) + "$" + name : name;
    }

    /**
     * Returns class packages.
     *
     * @param classObject object of class type
     * @return class packages
     */
    public static String[] getClassPackages ( final Object classObject )
    {
        return getClassPackages ( classObject.getClass () );
    }

    /**
     * Returns class packages.
     *
     * @param classType class type
     * @return class packages
     */
    public static String[] getClassPackages ( final Class classType )
    {
        return getPackages ( classType.getPackage ().getName () );
    }

    /**
     * Returns packages names.
     *
     * @param packageName package name
     * @return packages names
     */
    public static String[] getPackages ( final String packageName )
    {
        return packageName.split ( "\\." );
    }

    /**
     * Returns newly created class instance.
     *
     * @param canonicalClassName canonical class name
     * @param arguments          class constructor arguments
     * @param                 class instance type
     * @return newly created class instance
     */
    public static  T createInstanceSafely ( final String canonicalClassName, final Object... arguments )
    {
        try
        {
            return createInstance ( canonicalClassName, arguments );
        }
        catch ( final Exception e )
        {
            if ( safeMethodsLoggingEnabled )
            {
                final String msg = "ReflectionUtils method failed: createInstanceSafely";
                LoggerFactory.getLogger ( ReflectUtils.class ).error ( msg, e );
            }
            return null;
        }
    }

    /**
     * Returns newly created class instance.
     *
     * @param canonicalClassName canonical class name
     * @param arguments          class constructor arguments
     * @param                 class instance type
     * @return newly created class instance
     * @throws ClassNotFoundException    if class was not found
     * @throws InvocationTargetException if method throws an exception
     * @throws IllegalAccessException    if method is inaccessible
     * @throws InstantiationException    if the class is abstract
     * @throws NoSuchMethodException     if method was not found
     */
    public static  T createInstance ( final String canonicalClassName, final Object... arguments )
            throws ClassNotFoundException, InvocationTargetException, IllegalAccessException, InstantiationException, NoSuchMethodException
    {
        return createInstance ( loadClass ( canonicalClassName ), arguments );
    }

    /**
     * Returns newly created class instance.
     *
     * @param theClass  class to process
     * @param arguments class constructor arguments
     * @param        class instance type
     * @return newly created class instance
     */
    public static  T createInstanceSafely ( final Class theClass, final Object... arguments )
    {
        try
        {
            return createInstance ( theClass, arguments );
        }
        catch ( final Exception e )
        {
            if ( safeMethodsLoggingEnabled )
            {
                final String msg = "ReflectionUtils method failed: createInstanceSafely";
                LoggerFactory.getLogger ( ReflectUtils.class ).error ( msg, e );
            }
            return null;
        }
    }

    /**
     * Returns newly created class instance.
     *
     * @param theClass  class to process
     * @param arguments class constructor arguments
     * @param        class instance type
     * @return newly created class instance
     * @throws InstantiationException    if the class is abstract
     * @throws NoSuchMethodException     if method was not found
     * @throws InvocationTargetException if method throws an exception
     * @throws IllegalAccessException    if method is inaccessible
     */
    public static  T createInstance ( final Class theClass, final Object... arguments )
            throws InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException
    {
        // Retrieving argument types
        final Class[] parameterTypes = getClassTypes ( arguments );

        // Retrieving constructor
        final Constructor constructor = getConstructor ( theClass, parameterTypes );

        // Creating new instance
        return ( T ) constructor.newInstance ( arguments );
    }

    /**
     * Returns class constructor for the specified argument types.
     * This method will also find {@code protected}, {@code private} and package local constructors.
     *
     * todo 1. Constructors priority check (by super types)
     * todo    Right now some constructor with [Object] arg might be used instead of constructor with [String]
     * todo    To avoid issues don't call constructors with same amount of arguments and which are cast-able to each other
     * todo 2. Vararg constructors might not be found in many cases
     * todo    Additional checks/workarounds for such constructors should be added to avoid issues
     *
     * @param theClass       class to process
     * @param parameterTypes constructor argument types
     * @return class constructor for the specified argument types
     * @throws NoSuchMethodException if constructor was not found
     */
    public static Constructor getConstructor ( final Class theClass, final Class... parameterTypes ) throws NoSuchMethodException
    {
        // This enhancement is a bad idea since protected/private constructor it won't be found
        /*// Simplified constructor search for empty parameters
        if ( parameterTypes.length == 0 )
        {
            return theClass.getConstructor ();
        }*/

        // This enhancement is a bad idea as it will return appropriate inner class constructor
        // but you will surely be disoriented outside of this call why you have an extra parement
        // and generally you won't be able to properly instantiate it without additional workarounds
        /*// Workaround for simplifying inner classes constructor retrieval
        final Class[] actualParameterTypes;
        if ( theClass.isMemberClass () && ModifierType.STATIC.not ( theClass ) )
        {
            actualParameterTypes = new Class[ parameterTypes.length + 1 ];
            actualParameterTypes[ 0 ] = theClass.getEnclosingClass ();
            System.arraycopy ( parameterTypes, 0, actualParameterTypes, 1, parameterTypes.length );
        }
        else
        {
            actualParameterTypes = parameterTypes;
        }*/

        // Special check for inner classes
        if ( theClass.isMemberClass () && ModifierType.STATIC.not ( theClass ) )
        {
            // Ensure first parameter is a type compatible with class enclosing specified inner class
            if ( parameterTypes.length == 0 )
            {
                // No parameters at all, it seems caller is not aware it is asking to find inner class constructor
                throw new ReflectionException ( "Enclosing class paramter for inner class constructor is missing" );
            }
            else if ( !isAssignable ( theClass.getEnclosingClass (), parameterTypes[ 0 ] ) )
            {
                // Inner's class enclosing class is not assignable from first parameter type
                throw new ReflectionException ( "Incorrect first parameter for inner class constructor" );
            }
        }

        // Constructors can be used only from the topmost class so we don't need to look for them in superclasses
        for ( final Constructor constructor : theClass.getDeclaredConstructors () )
        {
            // Retrieving constructor parameter types
            final Class[] types = constructor.getParameterTypes ();

            // Checking some simple cases first
            if ( types.length != parameterTypes.length )
            {
                // Inappropriate constructor
                continue;
            }
            else if ( types.length == 0 )
            {
                // Constructor with no parameters
                constructor.setAccessible ( true );
                return constructor;
            }

            // Checking parameter types
            boolean fits = true;
            for ( int i = 0; i < types.length; i++ )
            {
                if ( !isAssignable ( types[ i ], parameterTypes[ i ] ) )
                {
                    fits = false;
                    break;
                }
            }
            if ( fits )
            {
                constructor.setAccessible ( true );
                return constructor;
            }
        }

        // Throwing proper exception that constructor was not found
        throw new NoSuchMethodException ( "Constructor was not found: " +
                theClass.getCanonicalName () + argumentTypesToString ( parameterTypes ) );
    }

    /**
     * Returns result of called static method.
     * Will return null in case method is void-type.
     *
     * @param canonicalClassName canonical class name
     * @param methodName         static method name
     * @param arguments          method arguments
     * @param                 method result type
     * @return result of called static method
     */
    public static  T callStaticMethodSafely ( final String canonicalClassName, final String methodName, final Object... arguments )
    {
        try
        {
            return callStaticMethod ( canonicalClassName, methodName, arguments );
        }
        catch ( final Exception e )
        {
            if ( safeMethodsLoggingEnabled )
            {
                final String msg = "ReflectionUtils method failed: callStaticMethodSafely";
                LoggerFactory.getLogger ( ReflectUtils.class ).error ( msg, e );
            }
            return null;
        }
    }

    /**
     * Returns result of called static method.
     * Will return null in case method is void-type.
     *
     * @param canonicalClassName canonical class name
     * @param methodName         static method name
     * @param arguments          method arguments
     * @param                 method result type
     * @return result of called static method
     * @throws ClassNotFoundException    if class was not found
     * @throws NoSuchMethodException     if method was not found
     * @throws InvocationTargetException if method throws an exception
     * @throws IllegalAccessException    if method is inaccessible
     */
    public static  T callStaticMethod ( final String canonicalClassName, final String methodName, final Object... arguments )
            throws ClassNotFoundException, InvocationTargetException, NoSuchMethodException, IllegalAccessException
    {
        return callStaticMethod ( getClass ( canonicalClassName ), methodName, arguments );
    }

    /**
     * Returns result of called static method.
     * Will return null in case method is void-type.
     *
     * @param theClass   class to process
     * @param methodName static method name
     * @param arguments  method arguments
     * @param         method result type
     * @return result of called static method
     */
    public static  T callStaticMethodSafely ( final Class theClass, final String methodName, final Object... arguments )
    {
        try
        {
            return callStaticMethod ( theClass, methodName, arguments );
        }
        catch ( final Exception e )
        {
            if ( safeMethodsLoggingEnabled )
            {
                final String msg = "ReflectionUtils method failed: callStaticMethodSafely";
                LoggerFactory.getLogger ( ReflectUtils.class ).error ( msg, e );
            }
            return null;
        }
    }

    /**
     * Returns result of called static method.
     * Will return null in case method is void-type.
     *
     * @param theClass   class to process
     * @param methodName static method name
     * @param arguments  static method arguments
     * @param         method result type
     * @return result given by called static method
     * @throws NoSuchMethodException     if method was not found
     * @throws InvocationTargetException if method throws an exception
     * @throws IllegalAccessException    if method is inaccessible
     */
    public static  T callStaticMethod ( final Class theClass, final String methodName, final Object... arguments )
            throws NoSuchMethodException, InvocationTargetException, IllegalAccessException
    {
        final Method method = getMethod ( theClass, methodName, arguments );
        return ( T ) method.invoke ( null, arguments );
    }

    /**
     * Returns list of results returned by called methods.
     *
     * @param objects    objects to call methods on
     * @param methodName method name
     * @param arguments  method arguments
     * @param         method result type
     * @return list of results returned by called methods
     */
    @NotNull
    public static  List callMethodsSafely ( @NotNull final List objects, @NotNull final String methodName,
                                                  @NotNull final Object... arguments )
    {
        final List results = new ArrayList ();
        for ( final Object object : objects )
        {
            try
            {
                results.add ( ( T ) callMethod ( object, methodName, arguments ) );
            }
            catch ( final Exception e )
            {
                if ( safeMethodsLoggingEnabled )
                {
                    final String msg = "ReflectionUtils method failed: callMethodsSafely:callMethod";
                    LoggerFactory.getLogger ( ReflectUtils.class ).error ( msg, e );
                }
                results.add ( null );
            }
        }
        return results;
    }

    /**
     * Returns list of results returned by called methods.
     *
     * @param objects    objects to call methods on
     * @param methodName method name
     * @param arguments  method arguments
     * @param         method result type
     * @return list of results returned by called methods
     * @throws NoSuchMethodException     if method was not found
     * @throws InvocationTargetException if method throws an exception
     * @throws IllegalAccessException    if method is inaccessible
     */
    @NotNull
    public static  List callMethods ( @NotNull final List objects, @NotNull final String methodName,
                                            @NotNull final Object... arguments )
            throws NoSuchMethodException, InvocationTargetException, IllegalAccessException
    {
        final List results = new ArrayList ();
        for ( final Object object : objects )
        {
            results.add ( ( T ) callMethod ( object, methodName, arguments ) );
        }
        return results;
    }

    /**
     * Returns an array of results returned by called methods.
     *
     * @param objects    objects to call methods on
     * @param methodName method name
     * @param arguments  method arguments
     * @return an array of results returned by called methods
     */
    @NotNull
    public static Object[] callMethodsSafely ( @NotNull final Object[] objects, @NotNull final String methodName,
                                               @NotNull final Object... arguments )
    {
        final Object[] results = new Object[ objects.length ];
        for ( int i = 0; i < objects.length; i++ )
        {
            try
            {
                results[ i ] = callMethod ( objects[ i ], methodName, arguments );
            }
            catch ( final Exception e )
            {
                if ( safeMethodsLoggingEnabled )
                {
                    final String msg = "ReflectionUtils method failed: callMethodsSafely:callMethod";
                    LoggerFactory.getLogger ( ReflectUtils.class ).error ( msg, e );
                }
                results[ i ] = null;
            }
        }
        return results;
    }

    /**
     * Returns an array of results returned by called methods.
     *
     * @param objects    objects to call methods on
     * @param methodName method name
     * @param arguments  method arguments
     * @return an array of results returned by called methods
     * @throws NoSuchMethodException     if method was not found
     * @throws InvocationTargetException if method throws an exception
     * @throws IllegalAccessException    if method is inaccessible
     */
    @NotNull
    public static Object[] callMethods ( @NotNull final Object[] objects, @NotNull final String methodName,
                                         @NotNull final Object... arguments )
            throws NoSuchMethodException, InvocationTargetException, IllegalAccessException
    {
        final Object[] results = new Object[ objects.length ];
        for ( int i = 0; i < objects.length; i++ )
        {
            results[ i ] = callMethod ( objects[ i ], methodName, arguments );
        }
        return results;
    }

    /**
     * Returns result given by called method.
     *
     * @param object     object instance
     * @param methodName method name
     * @param arguments  method arguments
     * @param         method result type
     * @return result given by called method
     */
    @Nullable
    public static  T callMethodSafely ( @NotNull final Object object, @NotNull final String methodName,
                                           @NotNull final Object... arguments )
    {
        T result;
        try
        {
            result = callMethod ( object, methodName, arguments );
        }
        catch ( final Exception e )
        {
            if ( safeMethodsLoggingEnabled )
            {
                final String msg = "ReflectionUtils method failed: callMethodSafely";
                LoggerFactory.getLogger ( ReflectUtils.class ).error ( msg, e );
            }
            result = null;
        }
        return result;
    }

    /**
     * Calls object's method with the specified name and arguments.
     * If method is not found in the object class all superclasses will be searched for that method.
     * Returns result given by called method.
     *
     * @param object     object instance
     * @param methodName method name
     * @param arguments  method arguments
     * @param         method result type
     * @return result given by called method
     * @throws NoSuchMethodException       if method was not found
     * @throws InvocationTargetException   if method throws an exception
     * @throws IllegalAccessException      if method is inaccessible
     * @throws ExceptionInInitializerError if the initialization provoked by this method fails
     */
    @Nullable
    public static  T callMethod ( @NotNull final Object object, @NotNull final String methodName, @NotNull final Object... arguments )
            throws NoSuchMethodException, InvocationTargetException, IllegalAccessException
    {
        final Method method = getMethod ( object.getClass (), methodName, arguments );
        return ( T ) method.invoke ( object, arguments );
    }

    /**
     * Returns field getter method by popular method naming pattern.
     * Basically those are "getFieldName"-like and "isFieldName"-like method names.
     *
     * @param object object
     * @param field  field name
     * @return field getter method by popular method naming pattern
     */
    public static Method getFieldGetter ( final Object object, final String field )
    {
        return getFieldGetter ( object.getClass (), field );
    }

    /**
     * Returns field getter method by popular method naming pattern.
     * Basically those are "getFieldName"-like and "isFieldName"-like method names.
     *
     * @param aClass object class
     * @param field  field name
     * @return field getter method by popular method naming pattern
     */
    public static Method getFieldGetter ( final Class aClass, final String field )
    {
        // Look for "get" method
        final Method get = getMethodSafely ( aClass, getGetterMethodName ( field ) );
        if ( get != null )
        {
            // Return "get" method
            return get;
        }
        else
        {
            // Return "is" method
            return getMethodSafely ( aClass, getIsGetterMethodName ( field ) );
        }
    }

    /**
     * Returns setter method name for the specified field.
     *
     * @param field field name
     * @return setter method name for the specified field
     */
    public static String getSetterMethodName ( final String field )
    {
        return "set" + field.substring ( 0, 1 ).toUpperCase ( Locale.ROOT ) + field.substring ( 1 );
    }

    /**
     * Returns getter method name for the specified field.
     *
     * @param field field name
     * @return getter method name for the specified field
     */
    public static String getGetterMethodName ( final String field )
    {
        return "get" + field.substring ( 0, 1 ).toUpperCase ( Locale.ROOT ) + field.substring ( 1 );
    }

    /**
     * Returns "is" getter method name for the specified field.
     *
     * @param field field name
     * @return "is" getter method name for the specified field
     */
    public static String getIsGetterMethodName ( final String field )
    {
        return "is" + field.substring ( 0, 1 ).toUpperCase ( Locale.ROOT ) + field.substring ( 1 );
    }

    /**
     * Returns whether method with the specified name and arguments exists in the specified object.
     * If method is not found in the object class all superclasses will be searched for that method.
     *
     * @param object     object
     * @param methodName method name
     * @param arguments  method arguments
     * @return {@code true} if method with the specified name and arguments exists in the specified object, {@code false} otherwise
     */
    public static boolean hasMethod ( @NotNull final Object object, @NotNull final String methodName, @NotNull final Object... arguments )
    {
        return hasMethod ( object.getClass (), methodName, arguments );
    }

    /**
     * Returns whether method with the specified name and arguments exists in the specified class.
     * If method is not found in the object class all superclasses will be searched for that method.
     *
     * @param aClass     object class
     * @param methodName method name
     * @param arguments  method arguments
     * @return {@code true} if method with the specified name and arguments exists in the specified class, {@code false} otherwise
     */
    public static boolean hasMethod ( @NotNull final Class aClass, @NotNull final String methodName, @NotNull final Object... arguments )
    {
        boolean result;
        try
        {
            result = getMethod ( aClass, methodName, arguments ) != null;
        }
        catch ( final Exception e )
        {
            result = false;
        }
        return result;
    }

    /**
     * Calls object's method with the specified name and arguments.
     * If method is not found in the object class all superclasses will be searched for that method.
     * Returns result given by called method.
     *
     * @param object     object
     * @param methodName method name
     * @param arguments  method arguments
     * @return result given by called method
     */
    public static Method getMethodSafely ( final Object object, final String methodName, final Object... arguments )
    {
        return getMethodSafely ( object.getClass (), methodName, arguments );
    }

    /**
     * Returns object's method with the specified name and arguments.
     * If method is not found in the object class all superclasses will be searched for that method.
     *
     * @param aClass     object class
     * @param methodName method name
     * @param arguments  method arguments
     * @return object's method with the specified name and arguments
     */
    public static Method getMethodSafely ( final Class aClass, final String methodName, final Object... arguments )
    {
        try
        {
            return getMethod ( aClass, methodName, arguments );
        }
        catch ( final Exception e )
        {
            if ( safeMethodsLoggingEnabled )
            {
                final String msg = "ReflectionUtils method failed: getMethodSafely";
                LoggerFactory.getLogger ( ReflectUtils.class ).error ( msg, e );
            }
            return null;
        }
    }

    /**
     * Returns object's method with the specified name and arguments.
     * If method is not found in the object class all superclasses will be searched for that method.
     *
     * @param object     object
     * @param methodName method name
     * @param arguments  method arguments
     * @return object's method with the specified name and arguments
     * @throws NoSuchMethodException if method was not found
     */
    public static Method getMethod ( final Object object, final String methodName, final Object... arguments )
            throws NoSuchMethodException
    {
        return getMethod ( object.getClass (), methodName, arguments );
    }

    /**
     * Returns object's method with the specified name and arguments.
     * If method is not found in the object class all superclasses will be searched for that method.
     * This method will also find {@code protected}, {@code private} and package local methods.
     *
     * todo 1. Methods priority check (by super types)
     * todo    Right now some method with [Object] arg might be used instead of method with [String]
     * todo    To avoid issues don't call methods with same amount of arguments and which are cast-able to each other
     * todo 2. Vararg methods might not be found in many cases
     * todo    Additional checks/workarounds for such methods should be added to avoid issues
     *
     * @param aClass     object class
     * @param methodName method name
     * @param arguments  method arguments
     * @return object's method with the specified name and arguments
     * @throws NoSuchMethodException if method was not found
     */
    public static Method getMethod ( @NotNull final Class aClass, @NotNull final String methodName, @NotNull final Object... arguments )
            throws NoSuchMethodException
    {
        // Method key
        final Class[] classTypes = getClassTypes ( arguments );
        final String key = aClass.getCanonicalName () + "." + methodName + argumentTypesToString ( classTypes );

        // Checking cache
        Method method = null;
        Map classMethodsCache = methodsLookupCache.get ( aClass );
        if ( classMethodsCache != null )
        {
            method = classMethodsCache.get ( key );
        }
        else
        {
            classMethodsCache = new HashMap ( 1 );
            methodsLookupCache.put ( aClass, classMethodsCache );
        }

        // Updating cache
        if ( method == null )
        {
            method = getMethodImpl ( aClass, methodName, arguments );
            classMethodsCache.put ( key, method );
        }

        return method;
    }

    /**
     * Returns object's method with the specified name and arguments.
     * If method is not found in the object class all superclasses will be searched for that method.
     *
     * @param aClass     object class
     * @param methodName method name
     * @param arguments  method arguments
     * @return object's method with the specified name and arguments
     * @throws NoSuchMethodException if method was not found
     */
    private static Method getMethodImpl ( final Class aClass, final String methodName, final Object[] arguments )
            throws NoSuchMethodException
    {
        // This enhancement was a bad idea and was disabled
        // In case method is protected/private or located in one of superclasses it won't be found
        //        if ( arguments.length == 0 )
        //        {
        //            // Searching simple method w/o arguments
        //            method = aClass.getMethod ( methodName );
        //            method.setAccessible ( true );
        //        }

        // Searching for more complex method
        final Class[] types = getClassTypes ( arguments );
        return getMethodImpl ( aClass, aClass, methodName, types );
    }

    /**
     * Returns object's method with the specified name and arguments.
     * If method is not found in the object class all superclasses will be searched for that method.
     *
     * @param topClass     initial object class
     * @param currentClass object class we are looking in for the method
     * @param methodName   method name
     * @param types        method argument types
     * @return object's method with the specified name and arguments
     * @throws NoSuchMethodException if method was not found
     */
    private static Method getMethodImpl ( final Class topClass, final Class currentClass, final String methodName, final Class[] types )
            throws NoSuchMethodException
    {
        // Searching for the specified method in object's class or one of its superclasses
        for ( final Method method : currentClass.getDeclaredMethods () )
        {
            // Checking method name
            if ( method.getName ().equals ( methodName ) )
            {
                // Checking method arguments count
                final Class[] mt = method.getParameterTypes ();
                if ( mt.length == types.length )
                {
                    // Checking that arguments fit
                    boolean fits = true;
                    for ( int i = 0; i < mt.length; i++ )
                    {
                        if ( !isAssignable ( mt[ i ], types[ i ] ) )
                        {
                            fits = false;
                            break;
                        }
                    }
                    if ( fits )
                    {
                        // Returning found method
                        method.setAccessible ( true );
                        return method;
                    }
                }
            }
        }

        // Search object superclass for this method
        final Class superclass = currentClass.getSuperclass ();
        if ( superclass != null )
        {
            return getMethodImpl ( topClass, superclass, methodName, types );
        }

        // Throwing proper method not found exception
        throw new NoSuchMethodException ( "Method was not found: " +
                topClass.getCanonicalName () + "." + methodName + argumentTypesToString ( types ) );
    }

    /**
     * Returns text representation for array of argument types.
     *
     * @param types argument types array
     * @return text representation for array of argument types
     */
    @NotNull
    private static String argumentTypesToString ( @Nullable final Class[] types )
    {
        final StringBuilder buf = new StringBuilder ( "(" );
        if ( types != null )
        {
            for ( int i = 0; i < types.length; i++ )
            {
                if ( i > 0 )
                {
                    buf.append ( ", " );
                }
                final Class c = types[ i ];
                buf.append ( c == null ? "null" : c.getCanonicalName () );
            }
        }
        return buf.append ( ")" ).toString ();
    }

    /**
     * Returns cloned object.
     *
     * @param object object to clone
     * @param     cloned object type
     * @return cloned object
     * @throws NoSuchMethodException     if method was not found
     * @throws InvocationTargetException if method throws an exception
     * @throws IllegalAccessException    if method is inaccessible
     */
    @Nullable
    public static  T clone ( @Nullable final T object )
            throws NoSuchMethodException, IllegalAccessException, InvocationTargetException
    {
        return object != null ? ( T ) ReflectUtils.callMethod ( object, "clone" ) : null;
    }

    /**
     * Returns cloned object.
     *
     * @param object object to clone
     * @param     cloned object type
     * @return cloned object
     */
    @Nullable
    public static  T cloneSafely ( @Nullable final T object )
    {
        return object != null ? ( T ) ReflectUtils.callMethodSafely ( object, "clone" ) : null;
    }

    /**
     * Returns class loaded for the specified canonical class name.
     *
     * @param canonicalClassName canonical class name
     * @return class loaded for the specified canonical class name
     * @throws ClassNotFoundException if class was not found
     */
    @NotNull
    public static Class loadClass ( @NotNull final String canonicalClassName ) throws ClassNotFoundException
    {
        return ReflectUtils.class.getClassLoader ().loadClass ( canonicalClassName );
    }

    /**
     * Returns an array of argument class types.
     *
     * @param arguments arguments to process
     * @return an array of argument class types
     */
    @NotNull
    public static Class[] getClassTypes ( @NotNull final Object[] arguments )
    {
        final Class[] parameterTypes = new Class[ arguments.length ];
        for ( int i = 0; i < arguments.length; i++ )
        {
            parameterTypes[ i ] = arguments[ i ] != null ? arguments[ i ].getClass () : null;
        }
        return parameterTypes;
    }

    /**
     * Returns whether specified {@link Class} type is assignable from another one or not.
     *
     * @param type    {@link Class} type to check against
     * @param another {@link Class} to check check
     * @return {@code true} if specified {@link Class} type is assignable from another one, {@code false} otherwise
     */
    @SuppressWarnings ( "ConstantConditions" )
    public static boolean isAssignable ( @NotNull final Class type, @Nullable final Class another )
    {
        boolean assignable = false;
        if ( another == null )
        {
            assignable = !type.isPrimitive ();
        }
        else if ( type.isAssignableFrom ( another ) )
        {
            assignable = true;
        }
        else if ( type.isPrimitive () )
        {
            if ( type == boolean.class )
            {
                assignable = Boolean.class.isAssignableFrom ( another );
            }
            else if ( type == int.class )
            {
                assignable = Integer.class.isAssignableFrom ( another );
            }
            else if ( type == char.class )
            {
                assignable = Character.class.isAssignableFrom ( another );
            }
            else if ( type == byte.class )
            {
                assignable = Byte.class.isAssignableFrom ( another );
            }
            else if ( type == short.class )
            {
                assignable = Short.class.isAssignableFrom ( another );
            }
            else if ( type == long.class )
            {
                assignable = Long.class.isAssignableFrom ( another );
            }
            else if ( type == float.class )
            {
                assignable = Float.class.isAssignableFrom ( another );
            }
            else if ( type == double.class )
            {
                assignable = Double.class.isAssignableFrom ( another );
            }
            else if ( type == void.class )
            {
                assignable = Void.class.isAssignableFrom ( another );
            }
        }
        return assignable;
    }

    /**
     * Returns whether or not specified object has primitive type.
     * Specified {@code object} must never be {@code null}.
     *
     * @param object object to check
     * @return {@code true} if specified object has primitive type, {@code false} otherwise
     */
    public static boolean isPrimitive ( @NotNull final Object object )
    {
        return isPrimitive ( object.getClass () );
    }

    /**
     * Returns whether or not specified class type is primitive.
     * Specified {@code clazz} must never be {@code null}.
     *
     * @param type class type to check
     * @return {@code true} if specified class type is primitive, {@code false} otherwise
     */
    @SuppressWarnings ( "ConstantConditions" )
    public static boolean isPrimitive ( @NotNull final Class type )
    {
        return type.isPrimitive () ||
                Boolean.class.isAssignableFrom ( type ) ||
                Character.class.isAssignableFrom ( type ) ||
                Byte.class.isAssignableFrom ( type ) ||
                Short.class.isAssignableFrom ( type ) ||
                Integer.class.isAssignableFrom ( type ) ||
                Long.class.isAssignableFrom ( type ) ||
                Float.class.isAssignableFrom ( type ) ||
                Double.class.isAssignableFrom ( type ) ||
                Void.class.isAssignableFrom ( type );
    }

    /**
     * Returns default primitive type value.
     *
     * @param type primitive class type
     * @return default primitive type value
     */
    @NotNull
    public static Object getDefaultPrimitiveValue ( @NotNull final Class type )
    {
        final Object value;
        if ( type.isPrimitive () )
        {
            if ( type == boolean.class )
            {
                value = false;
            }
            else if ( type == int.class )
            {
                value = 0;
            }
            else if ( type == char.class )
            {
                value = '\u0000';
            }
            else if ( type == byte.class )
            {
                value = ( byte ) 0;
            }
            else if ( type == short.class )
            {
                value = ( short ) 0;
            }
            else if ( type == long.class )
            {
                value = 0L;
            }
            else if ( type == float.class )
            {
                value = 0.0f;
            }
            else if ( type == double.class )
            {
                value = 0.0d;
            }
            else
            {
                throw new IllegalArgumentException ( "Unknown primitive type: " + type );
            }
        }
        else
        {
            throw new IllegalArgumentException ( "Type is not primitive: " + type );
        }
        return value;
    }

    /**
     * Returns whether {@link Class} or one of it's superclasses contains specified text in their name or not.
     *
     * @param theClass {@link Class} to check
     * @param text     text to look for
     * @return {@code true} if {@link Class} or one of it's superclasses contains specified text in their name, {@code false} otherwise
     */
    public static boolean containsInClassOrSuperclassName ( @Nullable final Class theClass, @NotNull final String text )
    {
        boolean contains = false;
        if ( theClass != null )
        {
            final String name = theClass.getCanonicalName ();
            if ( name != null )
            {
                contains = name.contains ( text ) || containsInClassOrSuperclassName ( theClass.getSuperclass (), text );
            }
            else
            {
                contains = containsInClassOrSuperclassName ( theClass.getSuperclass (), text );
            }
        }
        return contains;
    }

    /**
     * Returns closest superclass for both of the specified classes.
     *
     * @param object1 first object to retrieve {@link Class} of
     * @param object2 second object to retrieve {@link Class} of
     * @return closest superclass for both of the specified classes
     */
    @NotNull
    public static Class getClosestSuperclass ( @NotNull final Object object1, @NotNull final Object object2 )
    {
        return getClosestSuperclass ( object1.getClass (), object2.getClass () );
    }

    /**
     * Returns closest super {@link Class} for both of the specified {@link Class}es.
     *
     * @param class1 first {@link Class}
     * @param class2 second {@link Class}
     * @return closest super {@link Class} for both of the specified {@link Class}es
     */
    @NotNull
    public static Class getClosestSuperclass ( @NotNull final Class class1, @NotNull final Class class2 )
    {
        final Class closestSuperClass;
        if ( class1.isAssignableFrom ( class2 ) )
        {
            closestSuperClass = class1;
        }
        else if ( class2.isAssignableFrom ( class1 ) )
        {
            closestSuperClass = class2;
        }
        else
        {
            final Class super1 = class1.getSuperclass ();
            final Class super2 = class2.getSuperclass ();
            closestSuperClass = getClosestSuperclass ( super1, super2 );
        }
        return closestSuperClass;
    }

    /**
     * Adds {@link List} of specified {@link URL}s into {@link ClassLoader}'s class path.
     *
     * @param classLoader {@link ClassLoader} to modify class path for
     * @param classPath   {@link List} of {@link URL}s to add into {@link ClassLoader}'s class path
     */
    public static void addToClasspath ( @NotNull final ClassLoader classLoader, @NotNull final List classPath )
    {
        if ( classLoader instanceof URLClassLoader )
        {
            try
            {
                // Workaround for class path modification via URLClassLoader
                for ( final URL url : classPath )
                {
                    ReflectUtils.callMethod ( classLoader, "addURL", url );
                }
            }
            catch ( final Exception e )
            {
                throw new ReflectionException ( String.format (
                        "Unable to add classpath URLs into class loader %s",
                        classLoader.getClass ().getCanonicalName ()
                ), e );
            }
        }
        else if ( SystemUtils.isJava9orAbove () )
        {
            try
            {
                // Workaround for class path modification via BuiltinClassLoader
                final Class builtInClassLoaderClass = Class.forName ( JAVA9_BUILT_IN_CLASS_LOADER );
                if ( builtInClassLoaderClass.isAssignableFrom ( classLoader.getClass () ) )
                {
                    // jdk.internal.loader.URLClassPath
                    final Object urlClassPath = ReflectUtils.getFieldValue ( classLoader, "ucp" );
                    for ( final URL url : classPath )
                    {
                        ReflectUtils.callMethod ( urlClassPath, "addURL", url );
                    }
                }
                else
                {
                    throw new ReflectionException ( String.format (
                            "Plugin loading is not supported for class loader: %s",
                            classLoader.getClass ().getCanonicalName ()
                    ) );
                }
            }
            catch ( final ClassNotFoundException e )
            {
                throw new ReflectionException ( String.format (
                        "Unable to find %s class",
                        JAVA9_BUILT_IN_CLASS_LOADER
                ), e );
            }
            catch ( final Exception e )
            {
                throw new ReflectionException ( String.format (
                        "Unable to add classpath URLs into class loader %s",
                        classLoader.getClass ().getCanonicalName ()
                ), e );
            }
        }
        else
        {
            throw new ReflectionException ( String.format (
                    "Plugin loading is not supported for class loader: %s",
                    classLoader.getClass ().getCanonicalName ()
            ) );
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy