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

org.cp.elements.lang.reflect.ReflectionUtils Maven / Gradle / Ivy

Go to download

Java Simplified. Extensions and Useful Constructs for the Java Platform. Codeprimate Elements (a.k.a. cp-elements) is a Java library and micro-framework used to simplify the development of software applications written in Java. Elements packages several APIs into one library in order to address various application concerns and aspects of software design and development collectively and conveniently. Elements is a highly simple, yet robust and proven library built on solid OO principles, software design patterns and best practices to effectively solve common and reoccurring problems in software development.

There is a newer version: 2.0.0-M1
Show newest version
/*
 * Copyright 2016 Author or Authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.cp.elements.lang.reflect;

import static org.cp.elements.util.stream.StreamUtils.stream;

import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.TreeSet;

import org.cp.elements.lang.Assert;
import org.cp.elements.lang.BooleanUtils;
import org.cp.elements.lang.ClassUtils;
import org.cp.elements.lang.Filter;
import org.cp.elements.lang.FluentApiExtension;
import org.cp.elements.lang.annotation.FluentApi;
import org.cp.elements.lang.annotation.NullSafe;
import org.cp.elements.lang.support.ComposableFilter;
import org.cp.elements.util.ComparatorUtils;

/**
 * The {@link ReflectionUtils} class is an abstract, utility base class encapsulating commons operations
 * used in Java Introspection and Reflection.
 *
 * @author John J. Blum
 * @see java.lang.Class
 * @see java.lang.Object
 * @see java.lang.reflect.Field
 * @see java.lang.reflect.Member
 * @see java.lang.reflect.Method
 * @see java.lang.reflect.Modifier
 * @see org.cp.elements.lang.ClassUtils
 * @see org.cp.elements.lang.Filter
 * @see org.cp.elements.lang.FluentApiExtension
 * @see org.cp.elements.lang.annotation.FluentApi
 * @see org.cp.elements.util.stream.StreamUtils
 * @since 1.0.0
 */
@SuppressWarnings("unused")
public abstract class ReflectionUtils extends ClassUtils {

  /**
   * Determines the class types for all the given arguments.
   *
   * @param arguments the array of (object) arguments from which to determine their class types.
   * @return an array of class types for each object in the array of arguments, or null if the given array is null.
   * This method is careful to guard against null elements given a non-null array of arguments.
   * @see org.cp.elements.lang.ClassUtils#getClass(Object)
   */
  @NullSafe
  public static Class[] getArgumentTypes(Object... arguments) {
    Class[] argumentTypes = null;

    if (arguments != null) {
      argumentTypes = new Class[arguments.length];

      for (int index = 0; index < arguments.length; index++) {
        argumentTypes[index] = getClass(arguments[index]);
      }
    }

    return argumentTypes;
  }

  /**
   * Gets the value of the field with the specified name on the given class type cast to the desired field type.
   * This method assumes the field is a static (class) member field.
   *
   * @param  the desired return type in which the field's value will be cast; should be compatible with
   * the field's declared type.
   * @param type the Class type on which the field is declared and defined.
   * @param fieldName a String indicating the name of the field from which to get the value.
   * @param fieldType the declared type of the class field.
   * @return the value of the specified field on the given class type cast to the desired type.
   * @throws IllegalArgumentException if the given class type does not declare a static member field
   * with the specified name.
   * @throws FieldAccessException if the value for the specified field could not be retrieved.
   * @see #getField(Class, String)
   * @see #getValue(Object, java.lang.reflect.Field, Class)
   */
  @SuppressWarnings("unchecked")
  public static  T getValue(Class type, String fieldName, Class fieldType) {
    try {
      return getValue(null, getField(type, fieldName), fieldType);
    }
    catch (FieldNotFoundException e) {
      throw new IllegalArgumentException(String.format("Field with name (%1$s) does not exist on class type (%2$s)!",
        fieldName, type.getName()), e);
    }
  }

  /**
   * Gets the value of the field with the specified name on the given object cast to the desired field type.
   * This method assumes the field is a instance (object) member field.
   *
   * @param  the desired return type in which the field's value will be cast; should be compatible with
   * the field's declared type.
   * @param obj the Object on which the field is defined.
   * @param fieldName a String indicating the name of the field from which to get the value.
   * @param fieldType the declared type of the object's field.
   * @return the value of the specified field on the given object cast to the desired type.
   * @throws IllegalArgumentException if the given object's class type does not declare a instance member field
   * with the specified name.
   * @throws FieldAccessException if the value for the specified field could not be retrieved.
   * @see #getField(Class, String)
   * @see #getValue(Object, java.lang.reflect.Field, Class)
   */
  public static  T getValue(Object obj, String fieldName, Class fieldType) {
    try {
      return getValue(obj, getField(obj.getClass(), fieldName), fieldType);
    }
    catch (FieldNotFoundException e) {
      throw new IllegalArgumentException(String.format("Field with name (%1$s) does not exist on object of type (%2$s)!",
        fieldName, obj.getClass().getName()), e);
    }
  }

  /**
   * Gets the value of the field on the given object cast to the desired class type.  If the "target" object is null,
   * then this method assumes the field is a static (class) member field; otherwise the field is considered
   * an instance (object) member field.  This method is not null-safe!
   *
   * @param  the desired return type in which the field's value will be cast; should be compatible with
   * the field's declared type.
   * @param target the Object on which the field is defined.
   * @param field the specified Field from which to get the value.
   * @param type the desired return type of the field's value; should be compatible with the field's declared type.
   * @return the value of the given field on the given object cast to the desired type.
   * @throws FieldAccessException if the value for the specified field could not be retrieved.
   * @throws NullPointerException if the field or type parameter arguments are null.
   */
  public static  T getValue(Object target, Field field, Class type) {
    try {
      boolean currentAccessible = field.isAccessible();
      field.setAccessible(true);
      Object value = field.get(target);
      field.setAccessible(currentAccessible);
      return type.cast(value);
    }
    catch (NullPointerException e) {
      throw e;
    }
    catch (Exception e) {
      throw new FieldAccessException(String.format("Failed to get value of field (%1$s) from %2$s type (%3$s)!",
        field.getName(), BooleanUtils.toString(target == null, "class", "object of"), getClassName(target)), e);
    }
  }

  /**
   * Sets the field with the specified name on the given class type to the given value. This method assumes the field
   * is a static (class) member field.
   *
   * @param type the Class type on which the field is declared and defined.
   * @param fieldName a String indicating the name of the field on which to set the value.
   * @param value the Object value to set the specified field to.
   * @throws IllegalArgumentException if the given class type does not declare a static member field
   * with the specified name.
   * @throws FieldAccessException if the value for the specified field could not be set.
   * @see #getField(Class, String)
   * @see #setField(Object, java.lang.reflect.Field, Object)
   */
  public static void setField(Class type, String fieldName, Object value) {
    try {
      setField(null, getField(type, fieldName), value);
    }
    catch (FieldNotFoundException e) {
      throw new IllegalArgumentException(String.format("Field with name (%1$s) does not exist on class type (%2$s)!",
        fieldName, type.getName()), e);
    }
  }

  /**
   * Sets the field with the specified name on the given object to the given value. This method assumes the field
   * is an instance (object) member field.
   *
   * @param obj the Object on which the field is defined.
   * @param fieldName a String indicating the name of the field on which to set the value.
   * @param value the Object value to set the specified field to.
   * @throws IllegalArgumentException if the given object's class type does not declare an instance member field
   * with the specified name.
   * @throws FieldAccessException if the value for the specified field could not be set.
   * @see #getField(Class, String)
   * @see #setField(Object, java.lang.reflect.Field, Object)
   */
  public static void setField(Object obj, String fieldName, Object value) {
    try {
      setField(obj, getField(obj.getClass(), fieldName), value);
    }
    catch (FieldNotFoundException e) {
      throw new IllegalArgumentException(String.format("Field with name (%1$s) does not exist on object of type (%2$s)!",
        fieldName, obj.getClass().getName()), e);
    }
  }

  /**
   * Sets the field on the given object to the given value. If the "target" object is null, then this method assumes
   * the field is a static (class) member field; otherwise the field is considered an instance (object) member field.
   * This method is not null-safe!
   *
   * @param target the Object on which the field is defined.
   * @param field the specified Field on which to set the value.
   * @param value the Object value to set the specified field to.
   * @throws FieldAccessException if the field is final, or the value for the specified field could not be set.
   * @throws NullPointerException if the field parameter argument is null.
   */
  @SuppressWarnings("all")
  public static void setField(Object target, Field field, Object value) {
    try {
      Assert.isFalse(Modifier.isFinal(field.getModifiers()), new FieldAccessException(String.format(
        "Cannot set the value of a final field (%1$s) on %2$s type (%3$s)!", field.getName(),
          BooleanUtils.toString(target == null, "class", "object of"), field.getDeclaringClass().getName())));

      boolean currentAccessible = field.isAccessible();

      field.setAccessible(true);
      field.set(target, value);
      field.setAccessible(currentAccessible);
    }
    catch (FieldAccessException e) {
      throw e;
    }
    catch (NullPointerException e) {
      throw e;
    }
    catch (Exception e) {
      throw new FieldAccessException(String.format("Failed to set field (%1$s) to value (%2$s) on %3$s type (%4$s)!",
        field.getName(), value, BooleanUtils.toString(target == null, "class", "object of"),
          field.getDeclaringClass().getName()), e);
    }
  }

  /**
   * Calls the no argument method with the specified name on the given class type.  This method assumes
   * the "method" to invoke is a static (class) member method with not return value.
   *
   * @param type the Class type on which the method to invoke is declared and defined.
   * @param methodName a String indicating the name of the method to invoke.
   * @throws IllegalArgumentException if the method with the specified name is not declared on the given class type.
   * @throws MethodInvocationException if the method invocation (call) fails to be executed successfully.
   * @see #getMethod(Class, String, Class[])
   * @see #invoke(Class, String, Class[], Object[], Class)
   */
  public static void invoke(Class type, String methodName) {
    invoke(type, methodName, null, null, Void.class);
  }

  /**
   * Calls the method with the specified name on the given class type, passing the given arguments.
   * This method assumes the "method" to invoke is a static (class) member method with no return value.
   *
   * @param type the Class type on which the method to invoke is declared and defined.
   * @param methodName a String indicating the name of the method to invoke.
   * @param arguments an array of objects constituting the method's signature as well as the arguments
   * to the method's parameters.
   * @throws IllegalArgumentException if the method with the specified name is not declared on the given class type.
   * @throws MethodInvocationException if the method invocation (call) fails to be executed successfully.
   * @see #getArgumentTypes(Object...)
   * @see #getMethod(Class, String, Class[])
   * @see #invoke(Class, String, Class[], Object[], Class)
   */
  public static void invoke(Class type, String methodName, Object... arguments) {
    invoke(type, methodName, getArgumentTypes(arguments), arguments, Void.class);
  }

  /**
   * Calls the no argument method with the specified name on the given class type, casting the method's return value
   * to the desired return type.  This method assumes the "method" to invoke is a static (class) member method.
   *
   * @param  the desired return type in which the method's return value will be cast; should be compatible with
   * the method's return type.
   * @param type the Class type on which the method to invoke is declared and defined.
   * @param methodName a String indicating the name of the method to invoke.
   * @param returnType the desired Class type in which to cast the method's return value.
   * @return the specified method's return value cast to the desired return type.
   * @throws IllegalArgumentException if the method with the specified name is not declared on the given class type.
   * @throws MethodInvocationException if the method invocation (call) fails to be executed successfully.
   * @see #getMethod(Class, String, Class[])
   * @see #invoke(Class, String, Class[], Object[], Class)
   */
  public static  T invoke(Class type, String methodName, Class returnType) {
    return invoke(type, methodName, null, null, returnType);
  }

  /**
   * Calls the method with the specified name on the given class type, passing the given arguments and casting
   * the method's return value to the desired class type.  This method assumes the "method" to invoke
   * is a static (class) member method.
   *
   * @param  the desired return type in which the method's return value will be cast; should be compatible with
   * the method's return type.
   * @param type the Class type on which the method to invoke is declared and defined.
   * @param methodName a String indicating the name of the method to invoke.
   * @param arguments an array of objects constituting the method's signature as well as the arguments
   * to the method's parameters.
   * @param returnType the desired Class type in which to cast the method's return value.
   * @return the specified method's return value cast to the desired return type.
   * @throws IllegalArgumentException if the method with the specified name is not declared on the given class type.
   * @throws MethodInvocationException if the method invocation (call) fails to be executed successfully.
   * @see #getArgumentTypes(Object...)
   * @see #invoke(Class, String, Class[], Object[], Class)
   */
  public static  T invoke(Class type, String methodName, Object[] arguments, Class returnType) {
    return invoke(type, methodName, getArgumentTypes(arguments), arguments, returnType);
  }

  /**
   * Calls the method with the specified name and signature on the given class type, passing the given arguments.
   * This method assumes the "method" to invoke is a static (class) member method with no return value.
   *
   * @param type the Class type on which the method to invoke is declared and defined.
   * @param methodName a String indicating the name of the method to invoke.
   * @param parameterTypes an array of Class types corresponding to the method's parameter types that specifies
   * the exact signature (order, type and number of parameters) of the method to invoke.  This is necessary
   * in cases where the method maybe overloaded.
   * @param arguments an array of objects constituting the method's signature as well as the arguments
   * to the method's parameters.
   * @throws IllegalArgumentException if the method with the specified name is not declared on the given class type.
   * @throws MethodInvocationException if the method invocation (call) fails to be executed successfully.
   * @see #getMethod(Class, String, Class[])
   * @see #getMethodSignature(String, Class[], Class)
   * @see #invoke(Object, java.lang.reflect.Method, Object[], Class)
   */
  public static void invoke(Class type, String methodName, Class[] parameterTypes, Object... arguments) {
    invoke(null, resolveMethod(type, methodName, parameterTypes, arguments, Void.class), arguments, Void.class);
  }

  /**
   * Calls the method with the specified name and signature on the given class type, passing the given arguments
   * and then the method's return value to the desired class type.  This method assumes the "method" to invoke
   * is a static (class) member method.
   *
   * @param  the desired return type in which the method's return value will be cast; should be compatible with
   * the method's return type.
   * @param type the Class type on which the method to invoke is declared and defined.
   * @param methodName a String indicating the name of the method to invoke.
   * @param parameterTypes an array of Class types corresponding to the method's parameter types that specifies
   * the exact signature (order, type and number of parameters) of the method to invoke.  This is necessary
   * in cases where the method maybe overloaded.
   * @param arguments an array of objects constituting the method's signature as well as the arguments
   * to the method's parameters.
   * @param returnType the desired Class type in which to cast the method's return value.
   * @return the specified method's return value cast to the desired return type.
   * @throws IllegalArgumentException if the method with the specified name is not declared on the given class type.
   * @throws MethodInvocationException if the method invocation (call) fails to be executed successfully.
   * @see #getMethod(Class, String, Class[])
   * @see #getMethodSignature(String, Class[], Class)
   * @see #invoke(Object, java.lang.reflect.Method, Object[], Class)
   */
  public static  T invoke(Class type, String methodName, Class[] parameterTypes, Object[] arguments,
      Class returnType) {

    try {
      return invoke(null, resolveMethod(type, methodName, parameterTypes, arguments, returnType),
        arguments, returnType);
    }
    catch (MethodNotFoundException e) {
      throw new IllegalArgumentException(String.format("No method with signature (%1$s) exists on class type (%2$s)!",
        getMethodSignature(methodName, parameterTypes, returnType), type.getName()), e);
    }
  }

  /**
   * Calls the method with the specified name on the given object.  This method assumes the "method" to invoke
   * is an instance (object) member method.
   *
   * @param obj the Object on which the method to invoke is defined.
   * @param methodName a String indicating the name of the method to invoke.
   * @throws IllegalArgumentException if the method with the specified name is not declared and defined
   * on the given object's class type.
   * @throws MethodInvocationException if the method invocation (call) fails to be executed successfully.
   * @see #invoke(Object, String, Class[], Object[], Class)
   */
  public static void invoke(Object obj, String methodName) {
    invoke(obj, methodName, null, null, Void.class);
  }

  /**
   * Calls the method with the specified name on the given object, passing the given arguments.
   * This method assumes the "method" to invoke is an instance (object) member method.
   *
   * @param obj the Object on which the method to invoke is defined.
   * @param methodName a String indicating the name of the method to invoke.
   * @param arguments an array of objects constituting the method's signature as well as the arguments
   * to the method's parameters.
   * @throws IllegalArgumentException if the method with the specified name is not declared and defined
   * on the given object's class type.
   * @throws MethodInvocationException if the method invocation (call) fails to be executed successfully.
   * @see #getArgumentTypes(Object...)
   * @see #invoke(Object, String, Class[], Object[], Class)
   */
  public static void invoke(Object obj, String methodName, Object... arguments) {
    invoke(obj, methodName, getArgumentTypes(arguments), arguments, Void.class);
  }

  /**
   * Calls the method with the specified name on the given object, casting the method's return value
   * to the desired class type.  This method assumes the "method" to invoke is an instance (object) member method.
   *
   * @param  the desired return type in which the method's return value will be cast; should be compatible with
   * the method's return type.
   * @param obj the Object on which the method to invoke is defined.
   * @param methodName a String indicating the name of the method to invoke.
   * @param returnType the desired Class type in which to cast the method's return value.
   * @return the specified method's return value cast to the desired return type.
   * @throws IllegalArgumentException if the method with the specified name is not declared and defined
   * on the given object's class type.
   * @throws MethodInvocationException if the method invocation (call) fails to be executed successfully.
   * @see #invoke(Object, String, Class[], Object[], Class)
   */
  public static  T invoke(Object obj, String methodName, Class returnType) {
    return invoke(obj, methodName, null, null, returnType);
  }

  /**
   * Calls the method with the specified name on the given object, passing the given arguments and casting
   * the method's return value to the desired class type.  This method assumes the "method" to invoke
   * is an instance (object) member method.
   *
   * @param  the desired return type in which the method's return value will be cast; should be compatible with
   * the method's return type.
   * @param obj the Object on which the method to invoke is defined.
   * @param methodName a String indicating the name of the method to invoke.
   * @param arguments an array of objects constituting the method's signature as well as the arguments
   * to the method's parameters.
   * @param returnType the desired Class type in which to cast the method's return value.
   * @return the specified method's return value cast to the desired return type.
   * @throws IllegalArgumentException if the method with the specified name is not declared and defined
   * on the given object's class type.
   * @throws MethodInvocationException if the method invocation (call) fails to be executed successfully.
   * @see #getArgumentTypes(Object...)
   * @see #invoke(Object, String, Class[], Object[], Class)
   */
  public static  T invoke(Object obj, String methodName, Object[] arguments, Class returnType) {
    return invoke(obj, methodName, getArgumentTypes(arguments), arguments, returnType);
  }

  /**
   * Calls the method with the specified name and signature on the given object, passing the given arguments.
   * This method assumes the "method" to invoke is an instance (object) member method.
   *
   * @param obj the Object on which the method to be invoked is defined.
   * @param methodName a String indicating the name of the method to invoke.
   * @param parameterTypes an array of Class types corresponding to the method's parameter types in order to specify
   * the exact signature (order, type and number) of the method to invoke.  This is necessary in cases where the
   * method maybe overloaded.
   * @param arguments an array of objects constituting the method's signature as well as the arguments
   * to the method's parameters.
   * @throws IllegalArgumentException if the method with the specified name is not declared and defined
   * on the given object's class type.
   * @throws MethodInvocationException if the method invocation (call) fails to be executed successfully.
   * @see #getMethod(Class, String, Class[])
   * @see #getMethodSignature(String, Class[], Class)
   * @see #invoke(Object, java.lang.reflect.Method, Object[], Class)
   */
  public static void invoke(Object obj, String methodName, Class[] parameterTypes, Object... arguments) {
    invoke(obj, resolveMethod(obj.getClass(), methodName, parameterTypes, arguments, Void.class),
      arguments, Void.class);
  }

  /**
   * Calls the method with the specified name and signature on the given object, passing the given arguments
   * and casting the method's return value to the desired class type.  This method assumes the "method" to invoke
   * is an instance (object) member method.
   *
   * @param  the desired return type in which the method's return value will be cast; should be compatible with
   * the method's return type.
   * @param obj the Object on which the method to be invoked is defined.
   * @param methodName a String indicating the name of the method to invoke.
   * @param parameterTypes an array of Class types corresponding to the method's parameter types in order to specify
   * the exact signature (order, type and number) of the method to invoke.  This is necessary in cases where the
   * method maybe overloaded.
   * @param arguments an array of objects constituting the method's signature as well as the arguments
   * to the method's parameters.
   * @param returnType the desired Class type in which to cast the method's return value.
   * @return the specified method's return value cast to the desired return type.
   * @throws IllegalArgumentException if the method with the specified name is not declared and defined
   * on the given object's class type.
   * @throws MethodInvocationException if the method invocation (call) fails to be executed successfully.
   * @see #getMethod(Class, String, Class[])
   * @see #getMethodSignature(String, Class[], Class)
   * @see #invoke(Object, java.lang.reflect.Method, Object[], Class)
   */
  public static  T invoke(Object obj, String methodName, Class[] parameterTypes, Object[] arguments,
      Class returnType) {

    try {
      return invoke(obj, resolveMethod(obj.getClass(), methodName, parameterTypes, arguments, returnType),
        arguments, returnType);
    }
    catch (MethodNotFoundException e) {
      throw new IllegalArgumentException(String.format("No method with signature (%1$s) exists on object of type (%2$s)!",
        getMethodSignature(methodName, parameterTypes, returnType), obj.getClass().getName()), e);
    }
  }

  /**
   * Calls the method with the specified name on the given object, passing the given arguments and casting
   * the method's return value to the desired class type.  If the target object is null, then this method assumes
   * the "method" to invoke is a static (class) member method, otherwise the "method" to invoke is considered
   * an instance (object) member method.
   *
   * @param  the desired return type in which the method's return value will be cast; should be compatible with
   * the method's return type.
   * @param target the Object on which the method is defined and will be invoked.
   * @param method the Method to invoke.
   * @param arguments an array of objects constituting the method's signature as well as the arguments
   * to the method's parameters.
   * @param returnType the desired Class type in which to cast the method's return value.
   * @return the specified method's return value cast to the desired return type.
   * @throws MethodInvocationException if the method invocation (call) fails to be executed successfully.
   * @throws NullPointerException if the method or returnType parameter arguments are null.
   * @see #getMethodSignature(String, Class[], Class)
   * @see java.lang.reflect.Method#invoke(Object, Object...)
   * @see java.lang.reflect.Method#setAccessible(boolean)
   */
  public static  T invoke(Object target, Method method, Object[] arguments, Class returnType) {
    try {
      boolean currentAccessible = method.isAccessible();
      method.setAccessible(true);
      Object returnValue = method.invoke(target, arguments);
      method.setAccessible(currentAccessible);
      return returnType.cast(returnValue);
    }
    catch (NullPointerException e) {
      throw e;
    }
    catch (Exception e) {
      throw new MethodInvocationException(String.format("Failed to invoke method (%1$s) on %2$s type (%3$s)!",
        getMethodSignature(method), BooleanUtils.toString(target == null, "class", "object of"),
          method.getDeclaringClass().getName()), e);
    }
  }

  /**
   * {@link FluentApi} object interface for performing callbacks on the given {@link Field fields}.
   *
   * @param fields collection of {@link Field Fields} on which to operate; maybe {@literal null} or empty.
   * @return a new instance of the {@link WithFields} {@link FluentApi} object interface.
   * @see org.cp.elements.lang.reflect.ReflectionUtils.WithFields
   * @see org.cp.elements.lang.annotation.FluentApi
   * @see NullSafe
   */
  @FluentApi @NullSafe
  public static WithFields withFields(Field... fields) {
    return new WithFields(fields);
  }

  /**
   * {@link FluentApi} object interface for performing callbacks on the given {@link Method methods}.
   *
   * @param methods collection of {@link Method Methods} on which to operate; maybe {@literal null} or empty.
   * @return a new instance of the {@link WithMethods} {@link FluentApi} object interface.
   * @see org.cp.elements.lang.reflect.ReflectionUtils.WithMethods
   * @see org.cp.elements.lang.annotation.FluentApi
   * @see NullSafe
   */
  @FluentApi @NullSafe
  public static WithMethods withMethods(Method... methods) {
    return new WithMethods(methods);
  }

  /**
   * Callback interface for members declared and defined on a given class/object type.
   *
   * @param  the specific member class type on which the callback is performed.
   * @see org.cp.elements.lang.reflect.ReflectionUtils.FieldCallback
   * @see org.cp.elements.lang.reflect.ReflectionUtils.MethodCallback
   */
  public interface MemberCallback {
    void with(T member);
  }

  /**
   * Callback interface for Fields declared and defined on a given class/object type.
   *
   * @see java.lang.reflect.Field
   * @see org.cp.elements.lang.reflect.ReflectionUtils.MemberCallback
   */
  public interface FieldCallback extends MemberCallback {
  }

  /**
   * Callback interface for Methods declared and defined on a given class/object type.
   *
   * @see java.lang.reflect.Method
   * @see org.cp.elements.lang.reflect.ReflectionUtils.MemberCallback
   */
  public interface MethodCallback extends MemberCallback {
  }

  /**
   * The {@link WithExpression} class defines the {@link FluentApi} object interface for {@link Class}
   * and {@link Object} type member callbacks.
   *
   * @param  member {@link Class} type on which the callback is performed.
   * @see org.cp.elements.lang.reflect.ReflectionUtils.WithFields
   * @see org.cp.elements.lang.reflect.ReflectionUtils.WithMethods
   * @see org.cp.elements.lang.FluentApiExtension
   */
  public static abstract class WithExpression implements FluentApiExtension {

    private volatile boolean accepted = false;

    private final Filter defaultFilter = (obj) -> (obj != null);

    private Filter filter;

    private final Set members = newMemberSet();

    /* (non-Javadoc) */
    @SuppressWarnings({ "unchecked", "varargs" })
    protected WithExpression(T... members) {
      if (members != null) {
        Collections.addAll(this.members, members);
      }
    }

    /* (non-Javadoc) */
    protected Filter getFilter() {
      return ComposableFilter.and(defaultFilter, filter);
    }

    /* (non-Javadoc) */
    protected Iterable getMembers() {
      return members;
    }

    /* (non-Javadoc) */
    protected boolean accepts(T member) {
      boolean localAccepted = getFilter().accept(member);
      this.accepted |= localAccepted;
      return localAccepted;
    }

    /* (non-Javadoc) */
    public WithExpression call(MemberCallback callback) {
      stream(getMembers()).filter(this::accepts).forEach(callback::with);
      return this;
    }

    /* (non-Javadoc) */
    public WithExpression matching(Filter filter) {
      this.filter = filter;
      return this;
    }

    /* (non-Javadoc) */
    protected abstract T[] members(Class type);

    /* (non-Javadoc) */
    protected Set newMemberSet() {
      return new HashSet<>();
    }

    /* (non-Javadoc) */
    public WithExpression on(Object obj) {
      return on(ClassUtils.getClass(obj));
    }

    /* (non-Javadoc) */
    public WithExpression on(Class type) {
      Assert.notNull(type, "The class type must not be null!");

      while (type != null) {
        Collections.addAll(this.members, members(type));
        type = type.getSuperclass();
      }

      return this;
    }

    /* (non-Javadoc) */
    @SuppressWarnings("all")
    public WithExpression throwing(RuntimeException e) {
      if (!accepted) {
        throw e;
      }

      return this;
    }
  }

  /**
   * The {@link WithFields} class defines the {@link FluentApi} object interface for {@link Class} and {@link Object}
   * type {@link Field} callbacks.
   *
   * @see org.cp.elements.lang.reflect.ReflectionUtils.WithExpression
   */
  public static class WithFields extends WithExpression {

    /* (non-Javadoc) */
    public WithFields(Field... fields) {
      super(fields);
    }

    /**
     * Returns all {@link Field} members declared on the given {@link Class} type.
     *
     * @param type {@link Class} type declaring the {@link Field Fields} to return.
     * @return an array of {@link Field Fields} declared on the given {@link Class} type.
     * @see java.lang.Class#getDeclaredFields()
     * @see java.lang.reflect.Field
     */
    @Override
    protected Field[] members(Class type) {
      return type.getDeclaredFields();
    }

    /* (non-Javadoc) */
    @Override
    protected Set newMemberSet() {
      return new TreeSet<>(ComparatorUtils.nullSafeArgumentsComparator((Field field1, Field field2) -> {
        String fullyQualifiedFieldOneName = field1.getDeclaringClass().getName().concat(field1.getName());
        String fullyQualifiedFieldTwoName = field2.getDeclaringClass().getName().concat(field2.getName());

        return fullyQualifiedFieldOneName.compareTo(fullyQualifiedFieldTwoName);
      }));
    }
  }

  /**
   * The {@link WithMethods} class defines the {@link FluentApi} object interface for {@link Class} and {@link Object}
   * type {@link Method} callbacks.
   *
   * @see org.cp.elements.lang.reflect.ReflectionUtils.WithMethods
   */
  public static class WithMethods extends WithExpression {

    /* (non-Javadoc) */
    public WithMethods(Method... methods) {
      super(methods);
    }

    /**
     * Returns all {@link Method} members declared on the given {@link Class} type.
     *
     * @param type {@link Class} type declaring the {@link Method Methods} to return.
     * @return an array of {@link Method Methods} declared on the given {@link Class} type.
     * @see java.lang.Class#getDeclaredFields()
     * @see java.lang.reflect.Field
     */
    @Override
    protected Method[] members(Class type) {
      return type.getDeclaredMethods();
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy