Package easyjava

Source Code of easyjava.ReflectUtils

package easyjava;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import easyjava.annotations.ThrowRTE;
import easyjava.exceptions.InstanceCreationException;
import easyjava.exceptions.ReadOnlyFieldException;

/**
* Provides static utility methods to make reflection operations easy.
*
* @author Ovunc Cetin
*/
public class ReflectUtils {

  /**
   * Creates a new instance of the given class with the given constructor
   * arguments.
   *
   * @param cls
   *            class to instantiate
   * @param args
   *            constructor arguments.
   * @return new instance
   */
  @ThrowRTE(throwThis = InstanceCreationException.class)
  public static <T> T createInstance(Class<T> cls, Object... args) {
    // find the cons. that matches the given args.
    Constructor<T> cons = findConstructorForArgs(cls, args);

    return cons.newInstance(args);
  }

  /**
   * Returns <code>true</code> if the given type is neither abstract class nor
   * interface.
   *
   * @param type
   *            type to check.
   * @return <code>true</code> if the type is neither abstract class nor
   *         interface, <code>false</code> otherwise.
   */
  public static boolean isConcreteType(Class<?> type) {
    return !(isAbstractClass(type) | type.isInterface());
  }

  /**
   * Finds the annotation in given annotation type for the specified field.
   * <p>
   * To find the annotation, this method first looks for the field
   * declaration. If it is found, it is returned. Otherwise, the getter and
   * the setter methods are checked in order. In the case that the result is
   * unsuccessful, then <code>null</code> is returned.
   *
   * @param <T>
   *            annotation type
   * @param field
   *            field to look for its annotation.
   * @param annCls
   *            annotation to look for.
   * @return annotation if exists, <code>null</code> otherwise.
   */
  public static <T extends Annotation> T findAnnotation(Field field,
      Class<T> annCls) {
    // look at field...
    T annotation = field.getAnnotation(annCls);
    if (annotation == null) {
      // look at the getter...
      annotation = getGetterMethod(field).getAnnotation(annCls);
      if (annotation == null) {
        // look at the setter method...
        annotation = getSetterMethod(field).getAnnotation(annCls);
      }
    }
    return annotation;
  }

  /**
   * Returns the list of fields declared in the given class and annotated by
   * the given annotation.
   *
   * @param target
   *            target class holding the fields.
   * @param annotation
   *            annotation marking the fields.
   * @return field list.
   * @see #findPropertyFields(Class, Class)
   */
  public static List<Field> findFields(Class<?> target,
      Class<? extends Annotation> annotation) {
    final List<Field> result = new ArrayList<Field>();

    Field[] fields = target.getDeclaredFields();
    for (Field field : fields) {
      if (field.isAnnotationPresent(annotation)) {
        result.add(field);
      }
    }

    return result;
  }

  /**
   * Returns the list of methods declared in the given class and annotated by
   * the given annotation.
   *
   * @param target
   *            target class holding the methods.
   * @param annotation
   *            annotation marking the methods.
   * @return method list.
   */
  public static List<Method> findMethods(Class<?> target,
      Class<? extends Annotation> annotation) {
    final List<Method> result = new ArrayList<Method>();

    Method[] methods = target.getMethods();
    for (Method method : methods) {
      if (method.isAnnotationPresent(annotation)) {
        result.add(method);
      }
    }

    return result;
  }

  /**
   * Returns the list of methods declared in target's type and annotated by
   * the given annotation.
   *
   * @param target
   *            target object of which class has the result methods.
   * @param annotation
   *            annotation marking the methods.
   * @return method list.
   */
  public static List<Method> findMethods(Object target,
      Class<? extends Annotation> annotation) {
    return findMethods(target.getClass(), annotation);
  }

  /**
   * Returns the list of fields declared in the given class and annotated by
   * the given annotation. It also returns the fields of which accessor or
   * mutator is annotated by the annotation.
   *
   * @param target
   *            target class holding the fields.
   * @param annotation
   *            annotation marking the fields and accessor/mutator methods.
   * @return field list.
   * @see #findFields(Class, Class)
   */
  public static List<Field> findPropertyFields(Class<?> target,
      Class<? extends Annotation> annotation) {
    final List<Field> result = new ArrayList<Field>();

    Field[] fields = target.getDeclaredFields();
    for (Field field : fields) {
      if (isAnnotatedProperty(field, annotation)) {
        result.add(field);
      }
    }

    return result;
  }

  /**
   * If exists, it returns a constructor of the given class which takes
   * arguments in the given types. Otherwise, <code>null</code> is returned.
   *
   * @param cls
   *            class to find its constructor.
   * @param argTypes
   *            argument types of the desired constructor.
   * @return desired constructor if exists, <code>null</code> otherwise.
   */
  public static <T> Constructor<T> getConstructorIfExists(Class<T> cls,
      Class<?>... argTypes) {
    try {
      return cls.getConstructor(argTypes);
    } catch (SecurityException e) {
      return null;
    } catch (NoSuchMethodException e) {
      return null;
    }
  }

  /**
   * Returns the getter method for the given field.
   *
   * @param field
   *            field to return its getter.
   * @return getter method of the field if exists, <code>null</code> otherwise
   *         if the field is write-only.
   */
  public static Method getGetterMethod(Field field) {
    String capFieldName = capitalize(field.getName());
    String methodName = "get".concat(capFieldName);
    Class<?> cls = field.getDeclaringClass();

    try {
      return cls.getMethod(methodName, new Class<?>[0]);
    } catch (NoSuchMethodException e) {
      // check for "is*" method for booleans...
      if (field.getType().equals(boolean.class)
          || field.getType().equals(Boolean.class)) {
        try {
          methodName = "is".concat(capFieldName);
          return cls.getMethod(methodName, new Class<?>[0]);
        } catch (NoSuchMethodException e1) {
          return null; // no is* method for boolean...
        }
      } else {
        return null; // not boolean type...
      }
    }
  }

  /**
   * Returns all annotations for the specified field. To collect the
   * annotations, it looks for the field declaration and the corresponding
   * accessor methods.
   *
   * @param field
   *            field to look for its annotation.
   * @param annCls
   *            annotation to look for.
   * @return annotation if exists, <code>null</code> otherwise.
   */
  public static Map<Class<? extends Annotation>, Annotation> getPropertyAnnotations(
      Field field) {
    final Method getter = getGetterMethod(field);
    final Method setter = getSetterMethod(field);

    Map<Class<? extends Annotation>, Annotation> result = new HashMap<Class<? extends Annotation>, Annotation>();
    // look at field...
    Annotation[] annotations = field.getAnnotations();
    putAllAnnotations(annotations, result);

    if (getter != null) {
      // look at the getter...
      annotations = getter.getAnnotations();
      putAllAnnotations(annotations, result);
    }

    if (setter != null) {
      // look at the setter ...
      annotations = setter.getAnnotations();
      putAllAnnotations(annotations, result);
    }

    return result;
  }

  /**
   * Returns the setter method for the given field.
   *
   * @param field
   *            field to return its setter.
   * @return setter method of the field if exists, <code>null</code> otherwise
   *         if the field is read-only.
   */
  public static Method getSetterMethod(Field field) {
    String methodName = "set".concat(capitalize(field.getName()));
    Class<?> cls = field.getDeclaringClass();

    try {
      return cls
          .getMethod(methodName, new Class<?>[] { field.getType() });
    } catch (NoSuchMethodException e) {
      return null; // no such setter.
    }
  }

  /**
   * Returns if the given type is an abstract class or not.
   *
   * @param type
   *            type to check.
   * @return <code>true</code> if the type is an abstract class,
   *         <code>false</code> otherwise. It returns <code>false</code> if
   *         the type is an interface which has <code>abstract</code>
   *         modifier.
   */
  public static boolean isAbstractClass(Class<?> type) {
    return !type.isInterface()
        & (type.getModifiers() & Modifier.ABSTRACT) != 0;
  }

  /**
   * Returns whether the given method's parameter in the specified index is
   * annotated by the given annotation or not.
   *
   * @param method
   *            method taking the parameter.
   * @param paramIndex
   *            index of the parameter to check.
   * @param ann
   *            annotation expected to mark the parameter.
   * @return <code>true</code> if the specified method parameter is annotated
   *         by the given annotation, <code>false</code> otherwise.
   */
  public static boolean isAnnotatedBy(Method method, int paramIndex,
      Class<? extends Annotation> ann) {
    Annotation[][] allParamAnns = method.getParameterAnnotations();
    Annotation[] paramAnns = allParamAnns[paramIndex];

    for (Annotation paramAnn : paramAnns) {
      if (paramAnn.annotationType().equals(ann)) {
        return true;
      }
    }

    return false;
  }

  /**
   * Returns if the given field or its accessor/mutator method is annotated by
   * the given annotation.
   *
   * @param ann
   *            annotation.
   * @return <code>true</code> if the field or its get/set method is annotated
   *         by the annotation, <code>false</code> otherwise.
   */
  public static boolean isAnnotatedProperty(Field f,
      Class<? extends Annotation> ann) {
    final Method getter = getGetterMethod(f);
    final Method setter = getSetterMethod(f);

    boolean result = f.isAnnotationPresent(ann);
    result |= getter != null ? getter.isAnnotationPresent(ann) : false;
    result |= setter != null ? setter.isAnnotationPresent(ann) : false;

    return result;
  }

  /**
   * Returns if the given type is an array type or sub type of
   * {@link Collection} interface.
   *
   * @param type
   *            type to check.
   * @return <code>true</code> if the type is an array or a collection,
   *         <code>false</code> otherwise.
   */
  public static boolean isArrayOrCollection(Class<?> type) {
    if (type.isArray() || Collection.class.isAssignableFrom(type)) {
      return true;
    }

    return false;
  }

  /**
   * Sets the given value to the specified property field by invoking the
   * setter method of the field or by assigning the value if and only if the
   * field is accessible.
   *
   * @param target
   *            target object which owns the field.
   * @param f
   *            field to set its value.
   * @param value
   *            value to set.
   * @throws ReadOnlyFieldException
   *             if setter method for the field does not exist.
   */
  public static void setPropertyField(Object target, Field f, Object value) {
    if (f.isAccessible()) {
      try { // assign the value...
        f.set(target, value);
      } catch (IllegalAccessException e) {
        // never reaches here...
      }
    }

    Method setter = getSetterMethod(f);
    if (setter == null) {
      throw new ReadOnlyFieldException(toLongFieldName(f));
    }

    try {
      setter.invoke(target, value);
    } catch (Exception e) {
      throw new ReflectionException(e.getMessage(), e);
    }
  }

  /**
   * Sets the given value to the specified property field by invoking the
   * setter method of the field or by assigning the value if and only if the
   * field is accessible.
   *
   * @param target
   *            target object which owns the field.
   * @param f
   *            field to set its value.
   * @param value
   *            value to set.
   * @throws ReadOnlyFieldException
   *             if setter method for the field does not exist.
   */
  @ThrowRTE(throwThis = ReflectionException.class)
  public static void setPropertyField(Object target, String fieldName,
      Object value) {
    setPropertyField(target, target.getClass().getDeclaredField(fieldName),
        value);
  }

  /**
   * Capitalize the given field name.
   */
  private static String capitalize(String fieldName) {
    return ("" + fieldName.charAt(0)).toUpperCase(Locale.ENGLISH)
        + fieldName.substring(1);
  }

  /**
   * Finds the constructor which can be called by the given arguments. If no
   * constructor found, <code>null</code> is returned.
   *
   * @throws NoSuchMethodException
   *
   */
  @SuppressWarnings("unchecked")
  private static <T> Constructor<T> findConstructorForArgs(Class<T> cls,
      Object... args) throws NoSuchMethodException {
    Constructor<T>[] constructors = (Constructor<T>[]) cls
        .getConstructors();
    if (args.length == 0) { // default constructor...
      return cls.getConstructor(new Class<?>[0]);
    }
    for (Constructor<T> cons : constructors) {
      Class<?>[] argTypes = cons.getParameterTypes();
      if (argTypes.length == args.length) {
        // compare each argument type...
        for (int i = 0; i < argTypes.length; i++) {
          if (!argTypes[i].isInstance(args[i])) {
            break; // one of the arguments not matched...
          }
        }
        // all arguments are matched...
        return cons;
      }
    }

    return null;
  }

  /**
   * Puts each annotation in the given array into the given map if the
   * annotation does not already exist in the map.
   *
   * @param annotations
   *            annotations to put.
   * @param target
   *            target map.
   */
  private static void putAllAnnotations(Annotation[] annotations,
      Map<Class<? extends Annotation>, Annotation> target) {
    for (Annotation annotation : annotations) {
      if (!target.containsKey(annotation.annotationType())) {
        target.put(annotation.annotationType(), annotation);
      }
    }
  }

  private static String toLongFieldName(Field f) {
    return f.getDeclaringClass().getName() + "#" + f.getName();
  }
}
TOP

Related Classes of easyjava.ReflectUtils

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.