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

org.bithill.selenium.resolving.Reflection Maven / Gradle / Ivy

There is a newer version: 1.0
Show newest version
package org.bithill.selenium.resolving;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.function.Predicate;

/**
 * Collection of utility functions for reflection.
 */
class Reflection
{
    /** Provides list of all fields this object has, including fields in ancestors.
     *  These fields are candidates for resolve.
     *
     * @param instance object for which we want to get the fields
     * @param rootClass the root class of inheritance hierarchy, ancestors of this class are not searched for fields
     * @return list of fields
     */
    public static Field[] getClassFields(@Nonnull Object instance, @Nullable Class rootClass)
    {
        List fields = new ArrayList<>();

        // 1. add all fields the class declares
        fields.addAll(Arrays.asList(instance.getClass().getDeclaredFields()));

        // 2. add all fields of ancestor classes, up to the Resolvable (including)
        Class currentlyProcessedClass = instance.getClass();
        Class superClass;
        while ( (superClass = currentlyProcessedClass.getSuperclass()) != null )
        {
            if (superClass != rootClass)
            {
                fields.addAll(Arrays.asList(superClass.getDeclaredFields()));
                currentlyProcessedClass = superClass;
            }
            else
            {
                break;
            }
        }

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

    /** Provides list of all fields this object has, including fields in ancestors.
     *  These fields are candidates for resolve.
     *
     * @param instance object for which we want to get the fields
     * @return list of fields
     */
    public static Field[] getClassFields(@Nonnull Object instance)
    {
        return getClassFields(instance, Object.class);
    }

    /** Gets value of the field, regardless of its access modifiers.
     *
     * @param instance object from which we want to get the field
     * @param field the field we want got get value of
     *
     * @return value of the field
     */
    public static Object getFieldValue(@Nonnull Object instance, @Nonnull Field field)
    {
        Object result;

        field.setAccessible(true);
        try
        {
            result = field.get(instance);
        }
        catch (IllegalAccessException ex)
        {
            throw new RuntimeException(ex);
        }
        field.setAccessible(false);

        return result;
    }

    /** Sets given field if it matches provided predicate.
     *
     * @param instance object for which we want to set the field
     * @param field the field to check and set
     * @param value the value to which we want the field to be set
     * @param fieldPredicate predicate the field must match (fieldPredicate.test() returns true) to be set
     */
    public static void setFieldValue(@Nonnull Object instance, @Nonnull Field field, Object value, Predicate fieldPredicate)
    {
        try
        {
            field.setAccessible(true);
            if ( fieldPredicate.test(field)) { field.set(instance, value); }
            field.setAccessible(false);
        }
        catch (IllegalAccessException ex)
        {
            throw new RuntimeException(String.format("Field %s cannot be set.", field.getName()), ex);
        }
    }

    /** Sets given field.
     *
     * @param instance object for which we want to set the field
     * @param field the field to check and set
     * @param value the value to which we want the field to be set
     */
    public static void setFieldValue(@Nonnull Object instance, @Nonnull Field field, Object value)
    {
        setFieldValue(instance, field, value, aField -> true);
    }

    /** Check if the field is of desired type and has null value, if so, creates a new instance using default
     * constructor ans sets it to the field. Already set fields are not modified.
     *
     * @param instance object for which we want to set the field
     * @param field the field to check and set
     *
     * @return new instance of the field class for null-value field, existing value for non-null field
     *
     * @throws IllegalAccessException
     */
    public static Object createAndSetField(@Nonnull Object instance, @Nonnull Field field, Set allowedClasses)
    throws IllegalAccessException
    {
        Object fieldValue = getFieldValue(instance, field);
        Class fieldType = field.getType();


        // flag if the field type is allowed class or inherits from an allowed class
        @SuppressWarnings("unchecked")
        boolean isAllowedClass = allowedClasses.stream().anyMatch(aClass -> aClass.isAssignableFrom(fieldType));
        if (isAllowedClass && fieldValue == null)
        {
            try
            {
                setFieldValue(instance, field, fieldType.newInstance());
                fieldValue = getFieldValue(instance, field);
            }
            catch (InstantiationException ex)
            {
                throw new RuntimeException(ex);
            }
        }

        return fieldValue;
    }


    /** Retrieves type arguments of a given field.
     *
     * @param field of {@link ParameterizedType}
     * @return type arguments for a field of parametrized type, empty array for a filed of non-parametrized type
     */
    public static Type[] getFieldTypeArguments(@Nonnull Field field)
    {
        Type[] result = new Type[0];

        Type genericFieldType = field.getGenericType();

        if (genericFieldType instanceof ParameterizedType)
        {
            ParameterizedType aType = (ParameterizedType) genericFieldType;
            return aType.getActualTypeArguments();
        }

        return result;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy