package ilarkesto.base; import java.beans.BeanInfo; import java.beans.IntrospectionException; import java.beans.Introspector; import java.beans.PropertyDescriptor; import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Map; /** * Utilities for reflection and meta programming. */ public abstract class Reflect { /** * Call the <code>initialize()</code> method when it exists. */ public static void invokeInitializeIfThere(Object o) { Method m = getDeclaredMethod(o.getClass(), "initialize"); if (m == null) return; try { m.invoke(o); } catch (IllegalArgumentException ex) { throw new RuntimeException(ex); } catch (IllegalAccessException ex) { throw new RuntimeException(ex); } catch (InvocationTargetException ex) { throw new RuntimeException(ex); } } public static Object getProperty(Object o, String name) { String methodSuffix = Str.uppercaseFirstLetter(name); Method method = getDeclaredMethod(o.getClass(), "get" + methodSuffix); if (method == null) { method = getDeclaredMethod(o.getClass(), "is" + methodSuffix); Class<?> returnType = method.getReturnType(); if (returnType != boolean.class && returnType != Boolean.class) method = null; } if (method == null) throw new RuntimeException("No getter method for property: " + o.getClass().getSimpleName() + "." + name); try { return method.invoke(o); } catch (Exception ex) { throw new RuntimeException("Failed to invoke getter method: " + o.getClass().getSimpleName() + "." + method.getName() + "()", ex); } } public static Class getPropertyType(Object o, String name) { String methodName = "get" + Str.uppercaseFirstLetter(name); Method m = getDeclaredMethod(o.getClass(), methodName); if (m == null) return null; return m.getReturnType(); } public static Object getFieldValue(Object object, String fieldName) { return getFieldValue(object.getClass(), object, fieldName); } public static Object getFieldValue(Class<?> c, String fieldName) { return getFieldValue(c, null, fieldName); } public static Object getFieldValue(Class<?> c, Object object, String fieldName) { Field field = getDeclaredField(c, fieldName); if (field == null) return null; try { return field.get(object); } catch (IllegalArgumentException ex) { throw new RuntimeException(ex); } catch (IllegalAccessException ex) { throw new RuntimeException(ex); } } public static void setFieldValue(Object object, String fieldName, Object value) { setFieldValue(object.getClass(), object, fieldName, value); } public static void setFieldValue(Class<?> c, String fieldName, Object value) { setFieldValue(c, null, fieldName, value); } public static void setFieldValue(Class<?> c, Object object, String fieldName, Object value) { Field field = getDeclaredField(c, fieldName); if (field == null) throw new RuntimeException("Field does not exist: " + c.getName() + "." + fieldName); if (!field.isAccessible()) field.setAccessible(true); try { field.set(object, value); } catch (IllegalArgumentException ex) { throw new RuntimeException(ex); } catch (IllegalAccessException ex) { throw new RuntimeException(ex); } } public static void setProperties(Object o, Map<String, Object> properties) { if (properties == null) return; for (Map.Entry<String, ?> entry : properties.entrySet()) { setProperty(o, entry.getKey(), entry.getValue()); } } public static void setProperty(Object o, String name, Object value) { Method setter = getSetterMethod(o.getClass(), name); if (setter == null) throw new RuntimeException("Property setter not found: " + o.getClass() + "." + name); Class[] types = setter.getParameterTypes(); if (types.length != 1) throw new RuntimeException("Setter has illegar arguments: " + o.getClass() + "." + setter.getName()); if (value != null) { Class type = types[0]; if (!type.isAssignableFrom(value.getClass())) { if (type.equals(Boolean.class) || type.equals(boolean.class)) { value = Boolean.valueOf(value.toString()); } else if (type.equals(Integer.class) || type.equals(int.class)) { value = Integer.valueOf(value.toString()); } else if (type.equals(Long.class) || type.equals(long.class)) { value = Long.valueOf(value.toString()); } else if (type.equals(Float.class) || type.equals(float.class)) { value = Float.valueOf(value.toString()); } else if (type.equals(Double.class) || type.equals(double.class)) { value = Double.valueOf(value.toString()); } else { value = newInstance(type, value); } } } invoke(o, setter, value); } public static void setPropertyByStringValue(Object o, String name, String valueAsString) { Method setterMethod = getSetterMethod(o.getClass(), name); if (setterMethod == null) throw new RuntimeException("Setter " + o.getClass().getSimpleName() + ".set" + Str.uppercaseFirstLetter(name) + "(?) does not exist."); Class type = setterMethod.getParameterTypes()[0]; Object value = toType(valueAsString, type); invoke(o, setterMethod, value); } public static Object toType(String s, Class type) { if (type == String.class) return s; if (type == Boolean.class || type == boolean.class) return toBoolean(s); if (type == Integer.class || type == int.class) return toInteger(s); if (type == Long.class || type == long.class) return toLong(s); if (type == Character.class || type == char.class) return toCharacter(s); throw new RuntimeException("Unsupported type: " + type.getName()); } public static Character toCharacter(String s) { if (s == null) return null; if (s.length() < 1) return null; return s.charAt(0); } public static Integer toInteger(String s) { if (s == null) return null; return Integer.parseInt(s); } public static Long toLong(String s) { if (s == null) return null; return Long.parseLong(s); } public static Boolean toBoolean(String s) { if (s == null) return null; return s.equals(Boolean.TRUE.toString()); } public static Object newInstance(String className) { try { return newInstance(Class.forName(className)); } catch (ClassNotFoundException ex) { throw new RuntimeException(ex); } } public static <T extends Object> T newInstance(Class<T> type) { try { return type.newInstance(); } catch (InstantiationException ex) { throw new RuntimeException(ex); } catch (IllegalAccessException ex) { throw new RuntimeException(ex); } } public static <T extends Object> T newInstance(Class<T> type, Object... constructorParameters) { try { Constructor<T> constructor = type.getConstructor(getClasses(constructorParameters)); return constructor.newInstance(constructorParameters); } catch (Exception ex) { throw new RuntimeException(ex); } } public static Object invoke(Object object, String method, Object... parameters) { Method m = getDeclaredMethodUsingAutoboxing(object.getClass(), method, getClasses(parameters)); if (m == null) throw new NullPointerException("Method does not exist: " + object.getClass() + "." + method + "(" + Str.concat(getClassSimpleNames(parameters), ", ") + ")"); return invoke(object, m, parameters); } public static Object invoke(Object object, Method method, Object... parameters) { method.setAccessible(true); try { return method.invoke(object, parameters); } catch (Exception ex) { throw new RuntimeException(ex); } } public static boolean isTypesCompatible(Class[] methodTypes, Class[] parameterTypes, boolean autoboxing) { if (methodTypes.length != parameterTypes.length) return false; for (int i = 0; i < methodTypes.length; i++) { if (!isTypeCompatible(methodTypes[i], parameterTypes[i], autoboxing)) return false; } return true; } public static boolean isTypeCompatible(Class methodType, Class parameterType, boolean autoboxing) { if (parameterType == null) return true; if (methodType.equals(parameterType)) return true; if (!autoboxing) return false; // check autoboxing if (methodType.equals(Float.class) && parameterType.equals(float.class)) return true; if (methodType.equals(float.class) && parameterType.equals(Float.class)) return true; if (methodType.equals(Integer.class) && parameterType.equals(int.class)) return true; if (methodType.equals(int.class) && parameterType.equals(Integer.class)) return true; if (methodType.equals(Double.class) && parameterType.equals(double.class)) return true; if (methodType.equals(double.class) && parameterType.equals(Double.class)) return true; if (methodType.equals(Long.class) && parameterType.equals(long.class)) return true; if (methodType.equals(long.class) && parameterType.equals(Long.class)) return true; return false; } public static Method getDeclaredMethodUsingAutoboxing(Class<?> clazz, String name, Class<?>... parameterTypes) { for (Method m : clazz.getDeclaredMethods()) { if (!name.equals(m.getName())) continue; if (isTypesCompatible(m.getParameterTypes(), parameterTypes, true)) return m; } if (clazz != Object.class) return getDeclaredMethodUsingAutoboxing(clazz.getSuperclass(), name, parameterTypes); return null; } public static Method getDeclaredMethod(Class<?> clazz, String name, Class<?>... parameterTypes) { Method m = null; try { m = clazz.getDeclaredMethod(name, parameterTypes); } catch (SecurityException ex) { throw new RuntimeException(ex); } catch (NoSuchMethodException ex) { if (clazz != Object.class) { m = getDeclaredMethod(clazz.getSuperclass(), name, parameterTypes); } } return m; } public static Method getSetterMethod(Class<?> clazz, String property) { Method m = null; String methodName = "set" + Str.uppercaseFirstLetter(property); try { for (Method method : clazz.getDeclaredMethods()) { if (method.getName().equals(methodName) && method.getParameterTypes().length == 1) { m = method; break; } } } catch (SecurityException ex) { throw new RuntimeException(ex); } if (m == null) { if (clazz != Object.class) { m = getSetterMethod(clazz.getSuperclass(), property); } } return m; } public static Field getDeclaredField(Class<?> clazz, String name) { Field f = null; try { f = clazz.getDeclaredField(name); } catch (SecurityException ex) { throw new RuntimeException(ex); } catch (NoSuchFieldException ex) { if (clazz != Object.class) { f = getDeclaredField(clazz.getSuperclass(), name); } } return f; } public static Class<?>[] getClasses(Object... objects) { Class<?>[] result = new Class[objects.length]; for (int i = 0; i < objects.length; i++) { result[i] = objects[i] == null ? null : objects[i].getClass(); } return result; } public static String[] getClassSimpleNames(Class... classes) { String[] names = new String[classes.length]; for (int i = 0; i < classes.length; i++) { names[i] = classes[i] == null ? null : classes[i].getSimpleName(); } return names; } public static String[] getClassSimpleNames(Object... objects) { return getClassSimpleNames(getClasses(objects)); } public static Class<?> findClass(String classNameWithoutPackage, String... possiblePackageNames) { for (int i = 0; i < possiblePackageNames.length; i++) { String fullClassName = possiblePackageNames[i] + "." + classNameWithoutPackage; try { return Class.forName(fullClassName); } catch (ClassNotFoundException ex) { // nop, try next } } return null; } // ------------------- // --- annotations --- // ------------------- public static void processAnnotations(Object object, FieldAnnotationHandler handler) { processAnnotations(object, object.getClass(), handler); } public static void processAnnotations(Object object, Class<?> clazz, FieldAnnotationHandler handler) { Field[] fields = clazz.getDeclaredFields(); for (int i = fields.length - 1; i >= 0; i--) { Annotation[] annotations = fields[i].getAnnotations(); for (int j = 0; j < annotations.length; j++) { handler.handle(annotations[j], fields[i], object); } } Class<?> supa = clazz.getSuperclass(); Class<?>[] interfaces = clazz.getInterfaces(); for (int i = 0; i < interfaces.length; i++) { processAnnotations(object, interfaces[i], handler); } if (supa != null && !supa.equals(Object.class)) processAnnotations(object, supa, handler); } public static void processAnnotations(Object object, MethodAnnotationHandler handler) { processAnnotations(object, object.getClass(), handler); } public static void processAnnotations(Object object, Class<?> clazz, MethodAnnotationHandler handler) { Method[] methods = clazz.getDeclaredMethods(); for (int i = methods.length - 1; i >= 0; i--) { Annotation[] annotations = methods[i].getAnnotations(); for (int j = 0; j < annotations.length; j++) { handler.handle(annotations[j], methods[i], object); } } Class<?> supa = clazz.getSuperclass(); Class<?>[] interfaces = clazz.getInterfaces(); for (int i = 0; i < interfaces.length; i++) { processAnnotations(object, interfaces[i], handler); } if (supa != null && !supa.equals(Object.class)) processAnnotations(object, supa, handler); } public static void processAnnotations(Object object, PropertyMethodAnnotationHandler handler, boolean getter, boolean setter) { processAnnotations(object, object.getClass(), handler, getter, setter); } public static void processAnnotations(Object object, Class<?> clazz, PropertyMethodAnnotationHandler handler, boolean getter, boolean setter) { BeanInfo beanInfo; try { beanInfo = Introspector.getBeanInfo(clazz); } catch (IntrospectionException ex) { throw new RuntimeException(ex); } PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors(); for (int i = 0; i < propertyDescriptors.length; i++) { PropertyDescriptor propertyDescriptor = propertyDescriptors[i]; if (getter) { Method method = propertyDescriptor.getReadMethod(); if (method != null) { Annotation[] annotations = method.getAnnotations(); for (int j = 0; j < annotations.length; j++) { handler.handle(annotations[j], propertyDescriptor, object); } } } if (setter) { Method method = propertyDescriptor.getWriteMethod(); if (method != null) { Annotation[] annotations = method.getAnnotations(); for (int j = 0; j < annotations.length; j++) { handler.handle(annotations[j], propertyDescriptor, object); } } } } Class<?> supa = clazz.getSuperclass(); Class<?>[] interfaces = clazz.getInterfaces(); for (int i = 0; i < interfaces.length; i++) { processAnnotations(object, interfaces[i], handler, getter, setter); } if (supa != null && !supa.equals(Object.class)) processAnnotations(object, supa, handler, getter, setter); } public static interface MethodAnnotationHandler { public void handle(Annotation annotation, Method method, Object object); } public static interface PropertyMethodAnnotationHandler { public void handle(Annotation annotation, PropertyDescriptor property, Object object); } public static interface FieldAnnotationHandler { public void handle(Annotation annotation, Field field, Object object); } }