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

com.github.ibole.infrastructure.common.utils.ReflectionUtil Maven / Gradle / Ivy

The newest version!
package com.github.ibole.infrastructure.common.utils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * This class can be used for reflection purpose. Both class should match the getter and setter.
 * supports collections list and set copy.
 */
public class ReflectionUtil {

  /**
   * This method is used to copy one bean values to another bean.
   * 
   * @param toClazz Class object of Destination bean, which will be used to create class instance to
   *        copy values from passed object
   * @param from Origin bean from where to copy values
   * @param  T Type parameter
   * @return object copied from
   * @throws IllegalArgumentException If passed bean is null.
   * @throws InvocationTargetException If method access is denied
   * @throws InstantiationException If not able to create an instance of an passed toClass
   * @throws IllegalAccessException If access denied to access given class
   */
  public static  T copy(Class toClazz, Object from) throws Exception {
    return copy(toClazz, from, false);
  }

  /**
   * This method is used to copy one bean values to another bean.
   * 
   * @param to : Destination bean where values need to be copy from origin
   * @param from : Origin bean from where to copy values
   * @throws IllegalArgumentException If passed bean is null.
   * @throws InvocationTargetException If method access is denied
   */
  public static void copy(final Object to, final Object from) throws Exception {
    copy(to, from, false);
  }

  /**
   * This method is used to copy one bean values to another bean.
   * 
   * @param to : Origin bean from where to copy values
   * @param from : Destination bean where values need to be copy from origin
   * @param strictCopy boolean value denoting that copy should be in strict manner or not. If this
   *        is true then null values will also copied to destination bean from origin bean
   * @throws IllegalArgumentException If passed bean is null.
   * @throws InvocationTargetException If method access is denied
   */
  synchronized public static void copy(final Object to, final Object from, boolean strictCopy)
      throws Exception {
    copy(to, from, strictCopy, null);
  }

  /**
   * This method is used to copy one bean values to another bean.
   * 
   * @param toClazz Class object of Destination bean, which will be used to create class instance to
   *        copy values from passed object
   * @param from Origin bean from where to copy values
   * @param strictCopy boolean value denoting that copy should be in strict manner or not. If this
   *        is true then null values will also copied to destination bean from origin bean
   * @param  T Type parameter
   * @return object copied from
   * @throws IllegalArgumentException If passed bean is null.
   * @throws InvocationTargetException If method access is denied
   * @throws InstantiationException If not able to create an instance of an passed toClass
   * @throws IllegalAccessException If access denied to access given class
   */
  @SuppressWarnings("unchecked")
  public static  T copy(Class toClazz, Object from, boolean strictCopy) throws Exception {
    if (toClazz == null)
      throw new IllegalArgumentException("No destination bean specified");
    Object to = toClazz.newInstance();
    copy(to, from, strictCopy);
    return (T) to;
  }

  /**
   * This method is used to copy one bean values to another bean. This method accepts the Map of
   * excluded classes which means if any of the class in Map key found then instead of throwing
   * exception it copies its value to its value class object.
   * 
   * @param toClazz Class object of Destination bean, which will be used to create class instance to
   *        copy values from passed object
   * @param from Origin bean from where to copy values
   * @param excludeClassesMap {@link Map} map of excluded classes key-value pair. eg. key will be
   *        class in "to" and value will be class in "from".
   * @param  T Type parameter
   * @return Object copied from
   * @throws IllegalArgumentException If passed bean is null.
   * @throws InvocationTargetException If method access is denied
   * @throws InstantiationException If not able to create an instance of an passed toClass
   * @throws IllegalAccessException If access denied to access given class
   * 
   */
  @SuppressWarnings("unchecked")
  public static  T copy(Class toClazz, Object from, Map, Class> excludeClassesMap)
      throws Exception {
    if (toClazz == null)
      throw new IllegalArgumentException("No destination bean specified");
    Object to = toClazz.newInstance();
    copy(to, from, false, excludeClassesMap);
    return (T) to;
  }

  /**
   * This method is used to copy one bean values to another bean. This method accepts the Map of
   * excluded classes which means if any of the class in Map key found then instead of throwing
   * exception it copies its value to its value class object.
   * 
   * @param to : Destination bean where values need to be copy from origin
   * @param from : Origin bean from where to copy values
   * @param excludeClassesMap {@link Map} map of excluded classes key-value pair. eg. key will be
   *        class in "to" and value will be class in "from".
   * @throws IllegalArgumentException If passed bean is null.
   * @throws InvocationTargetException If method access is denied
   */
  public static void copy(final Object to, final Object from,
      final Map, Class> excludeClassesMap) throws IllegalArgumentException,
      InvocationTargetException {
    copy(to, from, false, excludeClassesMap);
  }

  /**
   * This method is used to copy one bean values to another bean.
   * 
   * @param to : Origin bean from where to copy values
   * @param from : Destination bean where values need to be copy from origin
   * @param strictCopy boolean value denoting that copy should be in strict manner or not. If this
   *        is true then null values will also copied to destination bean from origin bean
   * @param excludeClassesMap {@link Map} map of excluded classes key-value pair. eg. key will be
   *        class in "to" and value will be class in "from".
   * @throws IllegalArgumentException If passed bean is null.
   * @throws InvocationTargetException If method access is denied
   */
  synchronized private static void copy(final Object to, final Object from, boolean strictCopy,
      final Map, Class> excludeClassesMap) throws IllegalArgumentException,
      InvocationTargetException {
    if (to == null) {
      throw new IllegalArgumentException("No destination bean specified");
    }
    if (from == null) {
      throw new IllegalArgumentException("No origin bean specified");
    }
    try {
      if (checkForCollection(to, from)) {
        copyCollection((Collection) to, (Collection) from, excludeClassesMap);
      } else {
        Class toClass = to.getClass();
        Method[] toMethods = toClass.getMethods();
        Set toMethodsList = new LinkedHashSet(Arrays.asList(toMethods));

        Class fromClass = from.getClass();
        Method[] fromMethods = fromClass.getMethods();
        Set fromMethodsList = new LinkedHashSet(Arrays.asList(fromMethods));

        Iterator fromMethodsIterator = fromMethodsList.iterator();
        while (fromMethodsIterator.hasNext()) {
          Method fromMethod = fromMethodsIterator.next();
          boolean isBoolean = false;
          Class returnType = fromMethod.getReturnType();
          if (fromMethod.getName().startsWith("is") && boolean.class.equals(returnType)) {
            isBoolean = true;
          }
          if (isGetter(fromMethod, isBoolean)) {
            Object value = null;
            try {
              value = fromMethod.invoke(from);
            } catch (Exception e) {
              throw new InvocationTargetException(e,
                  "Getter method cannot have any parameter. Error on method : "
                      + fromMethod.getName());
            }
            if (strictCopy) {
              setValues(returnType, value, fromMethod, to, toMethodsList, isBoolean,
                  excludeClassesMap);
            } else {
              if (value != null) {
                setValues(returnType, value, fromMethod, to, toMethodsList, isBoolean,
                    excludeClassesMap);
              }
            }
          }
        }
      }
    } catch (Exception e) {
      e.printStackTrace();
    } finally {

    }
  }

  /**
   * This method is used to find setter method.
   * 
   * @param clazz The class
   * @param fieldName the field name used to find the matching matching method.
   * @return setter Method if found, otherwise return null.
   */
  public static Method findSetterMethod(Class clazz, String fieldName) {
    String expectedName =
        "set" + new String(new char[] {fieldName.charAt(0)}).toUpperCase() + fieldName.substring(1);

    for (Method method : clazz.getDeclaredMethods()) {
      if (method.getName().equals(expectedName) && method.getParameterTypes().length == 1) {
        if (method.getParameterTypes()[0].equals(fieldName.getClass())) {
          return method;
        }
      }
    }
    return null;
  }

  /**
   * This method is used to check method is getter or not.
   * 
   * @param method Method which needs to be check
   * @param isBoolean If the given method is primitive boolean type because getter for it starts
   *        with "is".
   * @return true is method is getter
   */
  private static boolean isGetter(Method method, boolean isBoolean) {
    if (isBoolean && !method.getName().startsWith("is"))
      return false;
    if (!isBoolean && !method.getName().startsWith("get"))
      return false;
    if (method.getParameterTypes().length != 0)
      return false;
    if (void.class.equals(method.getReturnType()))
      return false;
    return true;
  }

  /**
   * This method is used to check method is setter or not.
   * 
   * @param method Method which needs to be check
   * @return true is method is setter
   */
  private static boolean isSetter(Method method) {
    if (!method.getName().startsWith("set"))
      return false;
    if (method.getParameterTypes().length != 1)
      return false;
    return true;
  }

  /**
   * This method set the values to destination bean
   * 
   * @param returnType Return type of from class method
   * @param value value of from class getter method to be copied to destination
   * @param fromMethod method of from class
   * @param to destination class object
   * @param toMethodsList set of to class all methods
   * @param isBoolean method return type is boolean or not
   * @throws IllegalArgumentException If passed bean is null.
   * @throws InvocationTargetException If method access is denied
   * @throws IllegalAccessException If access denied to access given class
   */
  private static void setValues(Class returnType, Object value, final Method fromMethod,
      final Object to, Set toMethodsList, boolean isBoolean,
      Map, Class> excludeClassesMap) throws IllegalArgumentException,
      IllegalAccessException, InvocationTargetException {
    if (!returnType.isPrimitive()) {
      fromMethod.getReturnType().cast(value);
    }
    Iterator toMethodsIterator = toMethodsList.iterator();
    while (toMethodsIterator.hasNext()) {
      Method toMethod = (Method) toMethodsIterator.next();
      if (isSetter(toMethod)) {
        int fromSubstringIndex = 3;
        if (isBoolean)
          fromSubstringIndex = 2;
        if (toMethod.getName().substring(3)
            .equals(fromMethod.getName().substring(fromSubstringIndex))) {
          try {
            if (excludeClassesMap != null && value instanceof Collection) {
              processInnerCollections(toMethod, fromMethod, value, to, excludeClassesMap);
            } else {
              toMethod.invoke(to, value);
            }
          } catch (Exception e) {
            if (value != null) {
              Class[] parameterTypes = toMethod.getParameterTypes();
              Class pTypeClazz = parameterTypes[0];
              if (pTypeClazz.isInstance(to)) {
                // Instance found for same class.
                try {
                  toMethod.invoke(
                      to,
                      toMethod.getDeclaringClass().cast(
                          copy(toMethod.getDeclaringClass(), value, excludeClassesMap)));
                } catch (Exception e1) {
                  System.err.println(e1.getMessage()
                      + ": Not able to copy the same instance of given object");
                  e1.printStackTrace();
                }
              } else {
                if (excludeClassesMap != null && excludeClassesMap.get(pTypeClazz) != null
                    && excludeClassesMap.get(pTypeClazz).equals(fromMethod.getReturnType())) {
                  try {
                    toMethod
                        .invoke(to, pTypeClazz.cast(copy(pTypeClazz, value, excludeClassesMap)));
                  } catch (Exception e2) {
                    e2.printStackTrace();
                  }
                }
              }
            } else {
              throw new InvocationTargetException(e,
                  "Data type mismatched or access specifier is wrong. Error on method: TO method = "
                      + toMethod.getName() + " FROM method = " + fromMethod.getName());
            }
          }
          toMethodsList.remove(toMethod);
          break;
        }
      }
    }
  }

  /**
   * This method is used to copy collections of User Defined classes.
   * 
   * @param toMethod method of to class
   * @param fromMethod method of from class
   * @param value - value to copy
   * @param to destination class object
   * @param excludeClassesMap {@link Map} map of excluded classes key-value pair. eg. key will be
   *        class in "to" and value will be class in "from".
   * @throws Exception
   */
  @SuppressWarnings("unchecked")
  private static void processInnerCollections(Method toMethod, Method fromMethod, Object value,
      Object to, Map, Class> excludeClassesMap) throws Exception {

    Type[] types = toMethod.getGenericParameterTypes();
    ParameterizedType pType = (ParameterizedType) types[0];
    Class clazz = (Class) pType.getActualTypeArguments()[0];

    Type type = fromMethod.getGenericReturnType();
    ParameterizedType fpType = (ParameterizedType) type;
    Class fromSubclazz = (Class) fpType.getActualTypeArguments()[0];
    if (excludeClassesMap.get(clazz) != null && excludeClassesMap.get(clazz).equals(fromSubclazz)) {
      if (value instanceof List) {
        @SuppressWarnings("rawtypes")
        List list = new ArrayList();
        for (@SuppressWarnings("rawtypes")
        Iterator it = ((List) value).iterator(); it.hasNext();) {
          list.add(copy(clazz, it.next(), excludeClassesMap));
        }
        toMethod.invoke(to, list);
      } else if (value instanceof Set) {
        @SuppressWarnings("rawtypes")
        Set set = new HashSet();
        for (@SuppressWarnings("rawtypes")
        Iterator it = ((Set) value).iterator(); it.hasNext();) {
          set.add(copy(clazz, it.next(), excludeClassesMap));
        }
        toMethod.invoke(to, set);
      }
    } else {
      toMethod.invoke(to, value);
    }
  }

  /**
   * This method put the given field value into given object
   * 
   * @param obj - object where value has to be put
   * @param clazz - class of passed object
   * @param fieldName - {@link String} field(variable) name
   * @param value - value which has to be set
   * @param  Type parameter
   */
  public static  void findPut(Object obj, Class clazz, String fieldName, Object value) {
    if (clazz.isInstance(obj)) {
      clazz.cast(obj);
    }
    try {
      Field field = clazz.getDeclaredField(fieldName);
      String methodName = "set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
      Method setter = clazz.getMethod(methodName, field.getType());
      setter.invoke(obj, value);
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  /**
   * This method put the given map string key field key with their respective value into given
   * object
   * 
   * @param obj - object where value has to be put
   * @param map - {@link Map} String key and Object value map, where key will be field name and
   *        object will be value for that field.
   */
  public static void findNPut(Object obj, Map map) {
    if (obj == null || map == null || map.size() == 0) {
      System.err.println("Invalid Input.");
      return;
    }
    Class clazz = obj.getClass();
    if (clazz.isInstance(obj)) {
      try {
        clazz.cast(obj);
        Set fields = map.keySet();
        for (Iterator iterator = fields.iterator(); iterator.hasNext();) {
          String variable = (String) iterator.next();
          Field field = null;
          try {
            field = clazz.getDeclaredField(variable);
          } catch (Exception e) {
            System.err.println(variable
                + " : field not found in this class hence finding in its super class.");
            try {
              field = clazz.getSuperclass().getDeclaredField(variable);
            } catch (Exception e1) {
              System.err.println(variable
                  + " : field also not found in super class, hence skipping it.");
              continue;
            }
          }
          if (field != null) {
            Method setter = null;
            String methodName =
                "set" + variable.substring(0, 1).toUpperCase() + variable.substring(1);
            try {
              setter = clazz.getDeclaredMethod(methodName, field.getType());
            } catch (Exception e) {
              System.err.println(methodName
                  + " : method not found in this class hence finding in its super class.");
              try {
                setter = clazz.getSuperclass().getDeclaredMethod(methodName, field.getType());
              } catch (Exception e1) {
                System.err.println(methodName
                    + " : method also not found in super class, hence skipping it.");
                continue;
              }
            }
            if (setter != null) {
              try {
                setter.invoke(obj, map.get(variable));
              } catch (Exception e) {
                e.printStackTrace();
              }
            }
          }
        }
      } catch (Exception e) {
        e.printStackTrace();
        System.err.println("Cannot cast passed object " + obj + " to given " + clazz + " Class.");
      }
    } else {
      System.err.println("Sent Object " + obj + " is not an instance of passed " + clazz
          + " Class.");
    }
  }

  /**
   * This method check that to and from object are collections object or not.
   * 
   * @param to - to object
   * @param from - from object
   * @exception {@link IllegalArgumentException} - if one is collection and other is not.
   * @return true if both are collections else if both are not collections then false otherwise
   *         throw {@link IllegalArgumentException}
   */
  private static boolean checkForCollection(Object to, Object from) throws IllegalArgumentException {
    if (to instanceof Collection) {
      if (from instanceof Collection) {
        return true;
      } else {
        throw new IllegalArgumentException(
            "From object found as collection but To object is not a collection.");
      }
    } else
      return false;
  }

  /**
   * This method copy the one collection object values into another collection.
   * 
   * @param to - to object
   * @param from - from object
   * @param excludeClassesMap {@link Map} map of excluded classes key-value pair. eg. key will be
   *        class in "to" and value will be class in "from".
   */
  @SuppressWarnings("rawtypes")
  private static void copyCollection(Collection to, Collection from,
      Map, Class> excludeClassesMap) {
    if (!from.isEmpty()) {
      if (to instanceof List && from instanceof List) {
        copyList((List) to, (List) from, excludeClassesMap);
      } else if (to instanceof Set && from instanceof Set) {
        copySet((Set) to, (Set) from, excludeClassesMap);
      }
    }
  }

  /**
   * This method copy the one list object values into another list.
   * 
   * @param to - to object
   * @param from - from object
   * @param excludeClassesMap {@link Map} map of excluded classes key-value pair. eg. key will be
   *        class in "to" and value will be class in "from".
   */
  @SuppressWarnings({"unchecked", "rawtypes"})
  private static void copyList(List to, List from, Map, Class> excludeClassesMap) {
    for (Object obj : from) {
      if (obj != null) {
        Class fromListParametrizedClass = obj.getClass();
        try {
          if (excludeClassesMap != null && excludeClassesMap.get(fromListParametrizedClass) != null) {
            Object convertedObj =
                ReflectionUtil.copy(excludeClassesMap.get(fromListParametrizedClass), obj);
            to.add(convertedObj);
          } else {
            to.add(obj);
          }
        } catch (Exception e) {
          e.printStackTrace();
          System.err.println("Exception while copying list data: + " + obj);
        }
      }
    }
  }

  /**
   * This method copy the one set object values into another set.
   * 
   * @param to - to object
   * @param from - from object
   * @param excludeClassesMap {@link Map} map of excluded classes key-value pair. eg. key will be
   *        class in "to" and value will be class in "from".
   */
  @SuppressWarnings({"unchecked", "rawtypes"})
  private static void copySet(Set to, Set from, Map, Class> excludeClassesMap) {
    for (Object obj : from) {
      if (obj != null) {
        Class fromListParametrizedClass = obj.getClass();
        if (fromListParametrizedClass != null)
          fromListParametrizedClass = obj.getClass();
        try {
          if (excludeClassesMap != null && excludeClassesMap.get(fromListParametrizedClass) != null) {
            Object convertedObj =
                ReflectionUtil.copy(excludeClassesMap.get(fromListParametrizedClass), obj);
            to.add(convertedObj);
          }
        } catch (Exception e) {
          e.printStackTrace();
          System.err.println("Exception while copying set data: + " + obj);
        }
      }
    }
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy