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

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

/*
 * 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.DslExtension;
import org.cp.elements.lang.Filter;
import org.cp.elements.lang.NullSafe;
import org.cp.elements.lang.annotation.DSL;
import org.cp.elements.lang.support.ComposableFilter;
import org.cp.elements.util.ComparatorUtils;

/**
 * The 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.annotation.DSL
 * @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);
    }
  }

  /**
   * Domain-specific language (DSL) expression for performing callbacks on the given fields.
   *
   * @param fields the specific collection of Fields on which to operate; maybe null or empty.
   * @return an instance of the WithFields DSL expression.
   * @see org.cp.elements.lang.reflect.ReflectionUtils.WithFields
   * @see org.cp.elements.lang.NullSafe
   * @see org.cp.elements.lang.annotation.DSL
   */
  @DSL @NullSafe
  public static WithFields withFields(Field... fields) {
    return new WithFields(fields);
  }

  /**
   * Domain-specific language (DSL) expression for performing callbacks on the given methods.
   *
   * @param methods the specific collection of Methods on which to operate; maybe null or empty.
   * @return an instance of the WithMethods DSL expression.
   * @see org.cp.elements.lang.reflect.ReflectionUtils.WithMethods
   * @see org.cp.elements.lang.NullSafe
   * @see org.cp.elements.lang.annotation.DSL
   */
  @DSL @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 WithExpression class defines the domain-specific language (DSL) expression for class/object type
   * member callbacks.
   *
   * @param  the specific member class type on which the callback is performed.
   * @see org.cp.elements.lang.DslExtension
   * @see org.cp.elements.lang.reflect.ReflectionUtils.WithFields
   * @see org.cp.elements.lang.reflect.ReflectionUtils.WithMethods
   */
  public static abstract class WithExpression implements DslExtension {

    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 WithFields class defines the domain-specific language (DSL) expression for class/object type
   * 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 WithMethods class defines the domain-specific language (DSL) expression for class/object type
   * 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 - 2025 Weber Informatics LLC | Privacy Policy