Java tutorial
/** * This file is part of PaxmlCore. * * PaxmlCore is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * PaxmlCore is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with PaxmlCore. If not, see <http://www.gnu.org/licenses/>. */ package org.paxml.util; import java.beans.PropertyDescriptor; import java.lang.annotation.Annotation; import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.beanutils.ConvertUtils; import org.apache.commons.collections.iterators.ArrayIterator; import org.apache.commons.lang3.StringUtils; import org.codehaus.jackson.map.ObjectMapper; import org.paxml.core.PaxmlRuntimeException; import org.springframework.beans.BeanUtils; /** * Utility for reflection. * * @author Xuetao Niu * */ public final class ReflectUtils { /** * Class visitor. * * @author Xuetao Niu * * @param <T> * the type of return value. */ public static interface IClassVisitor<T> { /** * Visit a class and return a value. * * @param clazz * the class * @return the return value */ T onVisit(Class<?> clazz); } /** * Traverse the inheritance tree of a class, including the class itself. * * @param <T> * the type to return as traversal result. * @param clazz * the class to traverse * @param upTillClass * the top level class to stop at * @param includeInterfaces * true to traverse interfaces, false not to * @param visitor * the visitor to be called on visiting each class on the * inheritance tree. If the onVisit() method returns null, the * traversal will continue, otherwise it will stop and return the * value as overall result. * @return the value returned by the visitor's. */ public static <T> T traverseInheritance(Class<?> clazz, Class<?> upTillClass, boolean includeInterfaces, IClassVisitor<T> visitor) { if (!includeInterfaces && clazz.isInterface()) { return null; } if (upTillClass != null && upTillClass.equals(clazz)) { return visitor.onVisit(clazz); } if (Object.class.equals(clazz)) { return null; } T result = visitor.onVisit(clazz); if (result != null) { return result; } for (Class<?> intf : clazz.getInterfaces()) { result = traverseInheritance(intf, upTillClass, includeInterfaces, visitor); if (result != null) { return result; } } Class<?> parentClass = clazz.getSuperclass(); if (parentClass != null) { result = traverseInheritance(parentClass, upTillClass, includeInterfaces, visitor); } return result; } public static interface TraverseObjectCallback { boolean onElement(Object ele); } public static void traverseObject(Object obj, TraverseObjectCallback callback) { if (obj == null) { return; } Iterator it = null; if (obj instanceof Iterable) { it = ((Iterable) obj).iterator(); } else if (obj instanceof Iterator) { it = (Iterator) obj; } else if (obj instanceof Enumeration) { it = Collections.list((Enumeration) obj).iterator(); } else if (obj.getClass().isArray()) { it = new ArrayIterator(obj); } else if (obj instanceof String) { it = new ArrayIterator(StringUtils.split((String) obj)); } else if (obj instanceof Map) { it = ((Map) obj).keySet().iterator(); } else { it = Arrays.asList(obj).iterator(); } while (it.hasNext()) { Object ele = it.next(); if (!callback.onElement(ele)) { return; } } } /** * Find annotation from a class and all super classes and interfaces, stop * at the first encounter. * * @param <A> * the annotation type * @param clazz * the class * @param annotationClass * the annotation class * @return the annotation, or null if not from inhheritance tree. */ public static <A extends Annotation> A getAnnotation(Class<?> clazz, final Class<A> annotationClass) { return traverseInheritance(clazz, null, true, new IClassVisitor<A>() { public A onVisit(Class<?> clazz) { return clazz.getAnnotation(annotationClass); } }); } /** * Check if a class implements an interface class. * * @param implementingClass * the implementing class * @param interfaceClass * the interface class * @param matchNameOnly * true to only do name string comparison, false also compares * the class loader. * @return true if yes, false not */ public static boolean isImplementingClass(Class<?> implementingClass, final Class<?> interfaceClass, final boolean matchNameOnly) { if (!interfaceClass.isInterface() || implementingClass.isInterface() || implementingClass.equals(interfaceClass)) { return false; } Object result = traverseInheritance(implementingClass, interfaceClass, true, new IClassVisitor<Object>() { public Object onVisit(Class<?> clazz) { if (matchNameOnly) { return clazz.getName().equals(interfaceClass.getName()) ? new Object() : null; } else { return clazz.equals(interfaceClass) ? new Object() : null; } } }); return result != null; } /** * Check if a class is subclassing another class. * * @param subClass * the sub class * @param superClass * the super class * @param matchNameOnly * true to only do name string comparison, false also compares * the class loader. * @return true if yes, false not */ public static boolean isSubClass(Class<?> subClass, final Class<?> superClass, final boolean matchNameOnly) { if (subClass.equals(superClass)) { return false; } if (superClass.equals(Object.class)) { return true; } Object result = traverseInheritance(subClass, superClass, subClass.isInterface(), new IClassVisitor<Object>() { public Object onVisit(Class<?> clazz) { if (matchNameOnly) { return clazz.getName().equals(superClass.getName()) ? new Object() : null; } else { return clazz.equals(superClass) ? new Object() : null; } } }); return result != null; } /** * Load class from class name, swallowing possible ClassNotFoundException. * * @param clazz * the class name * @param cl * the classloader, set to null to use the current thread context * class loader. * @return the loaded class, or null if class not found. */ public static Class loadClass(String clazz, ClassLoader cl) { cl = cl == null ? Thread.currentThread().getContextClassLoader() : cl; try { return cl.loadClass(clazz); } catch (ClassNotFoundException e) { return null; } } /** * Load a class, converting possible ClassNotFoundException into * paxmlRuntimeException. * * @param className * the class name * @param cl * the classloader, set to null to use the current thread context * class loader. * @return the loaded class. * @throws PaxmlRuntimeException * if the load fails. */ public static Class<?> loadClassStrict(String className, ClassLoader cl) { cl = cl == null ? Thread.currentThread().getContextClassLoader() : cl; try { return cl.loadClass(className); } catch (ClassNotFoundException e) { throw new PaxmlRuntimeException("Class not found: " + className, e); } } /** * Check if a class is abstract class or interface. * * @param clazz * the class * @return true if yes, false not */ public static boolean isAbstract(Class<?> clazz) { return Modifier.isAbstract(clazz.getModifiers()); } /** * Create an object with the default constructor. * * @param className * the name of the class to create object from * @param cl * the class loader, set to null to use the current thread class * loader * @param constructParams * the parameters to call constructor with * @return the constructed object. * @throws RuntimeException * if the construction fails. */ public static Object createObject(String className, ClassLoader cl, Object... constructParams) { return createObject(loadClassStrict(className, cl), constructParams); } /** * Construct object from class using the default constructor. * * @param <T> * the class type * @param clazz * the class * @param constructParams * the parameters to call constructor with * @return the object * @throws RuntimeException * if the construction fails. */ public static <T> T createObject(Class<? extends T> clazz, Object... constructParams) { Constructor<T> con = null; Class[] argTypes = null; for (Constructor c : clazz.getDeclaredConstructors()) { argTypes = c.getParameterTypes(); if (argTypes.length == constructParams.length) { con = c; break; } } if (con == null) { throw new PaxmlRuntimeException("No constructor found with " + constructParams.length + " parameters!"); } try { Object[] args = new Object[constructParams.length]; for (int i = args.length - 1; i >= 0; i--) { args[i] = coerceType(constructParams[i], argTypes[i]); } con.setAccessible(true); return con.newInstance(args); } catch (Exception e) { throw new PaxmlRuntimeException("Cannot create instance from class: " + clazz.getName(), e); } } /** * Coerce object type. * * @param <T> * the expected type * @param from * from object * @param expectedType * to object type * @return the to object */ public static <T> T coerceType(Object from, Class<? extends T> expectedType) { if (from == null) { return null; } Object targetValue = null; if (expectedType.isInstance(from)) { targetValue = from; } else if (expectedType.isEnum()) { for (Object e : expectedType.getEnumConstants()) { if (from.toString().equalsIgnoreCase(e.toString())) { targetValue = e; break; } } if (targetValue == null) { throw new PaxmlRuntimeException( "No enum named '" + from + "' is defined in class: " + expectedType); } } else if (List.class.equals(expectedType)) { targetValue = new ArrayList(); collect(from, (Collection) targetValue, true); } else if (Set.class.equals(expectedType)) { targetValue = new LinkedHashSet(); collect(from, (Collection) targetValue, true); } else if (isImplementingClass(expectedType, Collection.class, false)) { try { targetValue = expectedType.newInstance(); } catch (Exception e) { throw new PaxmlRuntimeException(e); } collect(from, (Collection) targetValue, true); } else if (Iterator.class.equals(expectedType)) { List list = new ArrayList(); collect(from, list, true); targetValue = list.iterator(); } else if (isImplementingClass(expectedType, Coerceable.class, false)) { try { Constructor c = expectedType.getConstructor(Object.class); targetValue = c.newInstance(from); } catch (Exception e) { throw new PaxmlRuntimeException(e); } } else if (from instanceof Map) { return mapToBean((Map) from, expectedType); } else { targetValue = ConvertUtils.convert(from.toString(), expectedType); } return (T) targetValue; } public static int collect(Iterator it, Collection col) { int i = 0; while (it.hasNext()) { i++; col.add(it.next()); } return i; } public static int collect(Iterable it, Collection col) { return collect(it.iterator(), col); } public static int collect(Map map, Collection col) { for (Map.Entry entry : ((Map<?, ?>) map).entrySet()) { Map e = new HashMap(); e.put(entry.getKey(), entry.getValue()); col.add(e); } return map.size(); } /** * Collect elements from source to target. * * @param obj * the collectable source * @param col * the target collection * @param collectSingle * true to put single not collectable on the target collection, * false not * @return the number of elements collected */ public static int collect(Object obj, Collection col, boolean collectSingle) { if (obj == null) { return 0; } else if (obj instanceof Iterable) { return collect((Iterable) obj, col); } else if (obj instanceof Iterator) { return collect((Iterator) obj, col); } else if (obj instanceof Map) { return collect((Map) obj, col); } else if (obj.getClass().isArray()) { return collectArray(obj, col); } else if (obj instanceof Enumeration) { return collect((Enumeration) obj, col); } else if (collectSingle) { col.add(obj); return 1; } return 0; } public static int collectArray(Object array, Collection col) { int len = Array.getLength(array); for (int i = 0; i < len; i++) { col.add(Array.get(array, i)); } return len; } public static int collect(Enumeration e, Collection col) { int i = 0; while (e.hasMoreElements()) { i++; col.add(e.nextElement()); } return i; } /** * Put a value into a list if it is not a listable object. * * @param obj * the value * @return list, never null. */ public static List getList(Object obj) { List list = new ArrayList(0); for (Object v : new IterableObject(obj)) { list.add(v); } return list; } /** * Set a property for a bean. * * @param bean * the bean * @param pd * the property descriptor * @param value * the property value */ public static void callSetter(Object bean, PropertyDescriptor pd, Object value) { Method setter = pd.getWriteMethod(); if (setter == null) { throw new PaxmlRuntimeException( "Property '" + pd.getName() + "' is not settable on class: " + bean.getClass().getName()); } value = coerceType(value, setter.getParameterTypes()[0]); try { setter.invoke(bean, value); } catch (Exception e) { throw new PaxmlRuntimeException("Cannot call setter on property: " + pd.getName(), e); } } /** * Call a static method. If not found, exception will be thrown. * * @param className * the class name * @param method * the method name * @param args * the args name * @return the method return value. * */ public static Object callStaticMethod(String className, String method, Object[] args) { if (args == null) { args = new Object[0]; } Class clazz = ReflectUtils.loadClassStrict(className, null); for (Method m : clazz.getMethods()) { if (!m.getName().equals(method)) { continue; } Class[] argTypes = m.getParameterTypes(); if (argTypes.length == args.length) { Object[] actualArgs = new Object[args.length]; for (int i = args.length - 1; i >= 0; i--) { actualArgs[i] = ReflectUtils.coerceType(args[i], argTypes[i]); } try { return m.invoke(null, actualArgs); } catch (Exception e) { throw new PaxmlRuntimeException(e); } } } throw new PaxmlRuntimeException( "No method named '" + method + "' has " + args.length + " parameters from class: " + className); } /** * Call a method on an object. * * @param obj * the object * @param method * the method name * @param args * the arguments * @return the return value */ public static Object callMethod(Object obj, String method, Object[] args) { Class clazz = obj.getClass(); for (Method m : clazz.getMethods()) { if (!m.getName().equals(method)) { continue; } Class[] argTypes = m.getParameterTypes(); if (argTypes.length == args.length) { Object[] actualArgs = new Object[args.length]; for (int i = args.length - 1; i >= 0; i--) { actualArgs[i] = ReflectUtils.coerceType(args[i], argTypes[i]); } try { return m.invoke(obj, actualArgs); } catch (Exception e) { throw new PaxmlRuntimeException(e); } } } throw new PaxmlRuntimeException("No method named '" + method + "' has " + args.length + " parameters from class: " + clazz.getName()); } /** * Property descriptor type enum. * * @author Xuetao Niu * */ public static enum PropertyDescriptorType { GETTER, SETTER } /** * Get a specific type of property descriptors, except the "class" property. * * @param clazz * the class * @param type * the type filter, null means no filtering * @return the list of property descriptors. */ public static List<PropertyDescriptor> getPropertyDescriptors(Class clazz, PropertyDescriptorType type) { List<PropertyDescriptor> list = new ArrayList<PropertyDescriptor>(); for (PropertyDescriptor pd : BeanUtils.getPropertyDescriptors(clazz)) { if ("class".equals(pd.getName())) { continue; } switch (type) { case GETTER: if (pd.getReadMethod() != null) { list.add(pd); } break; case SETTER: if (pd.getWriteMethod() != null) { list.add(pd); } break; default: list.add(pd); } } return list; } public static Map beanToMap(Object bean) { ObjectMapper m = new ObjectMapper(); Map<String, Object> mappedObject = m.convertValue(bean, Map.class); return mappedObject; } public static <T> T mapToBean(Map map, Class<T> clazz) { ObjectMapper m = new ObjectMapper(); T bean = m.convertValue(map, clazz); return bean; } }