package org.javabuilders.util; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.TreeSet; import org.javabuilders.BuildException; /** * A replacement for the Apache Commons PropertyUtils. * I did not like the API (throws exceptions on every method) + had lots of dependencies that bloated the * JavaBuilders size * @author Jacek Furmankiewicz * */ public class PropertyUtils { //cache them for faster lookups private static Map<String,Method> getters = new HashMap<String, Method>(); private static Map<String,Method> setters = new HashMap<String, Method>(); private static StringBuilder bld = new StringBuilder(); /** * Checks if a property name is valid * @param instance * @param propertyName * @return */ public static boolean isValid(Object instance, String propertyName) { boolean valid = false; if (instance != null && propertyName != null && propertyName.length() > 0) { String name = getSetterName(propertyName); if (getSetter(instance, name) != null) { valid = true; } } return valid; } /** * @param instance Object instance * @param propertyName Property name * @return Type of the property */ public static Class<?> getPropertyType(Object instance, String propertyName) { String name = getSetterName(propertyName); Method setter = getSetter(instance, name); return setter.getParameterTypes()[0]; } /** * @param instance Object instance * @param propertyName Property name * @return Type of the property */ public static Set<String> getPropertyNames(Class<?> clazz) { Set<String> names = new TreeSet<String>(); StringBuilder bld = new StringBuilder(); if (clazz != null) { Method[] methods = clazz.getMethods(); for(Method method : methods) { if (method.getName().startsWith("set") && method.getParameterTypes().length == 1 ) { bld.setLength(0); bld.append(method.getName().substring(3,4).toLowerCase()); if (method.getName().length() > 4) { bld.append(method.getName().substring(4)); } names.add(bld.toString()); } } } return names; } /** * @param instance Object instance * @param propertyName Property name * @return Type of the property */ public static Set<String> getPropertyNames(Object instance) { Set<String> names = new TreeSet<String>(); if (instance != null) { names = getPropertyNames(instance.getClass()); } return names; } /** * Sets the value via reflection on an object * @param instance Instance * @param propertyName Property name * @param value Value */ public static void setProperty(Object instance, String propertyName, Object value) { if (instance != null && propertyName != null) { String name = getSetterName(propertyName); Method setter = getSetter(instance, name); if (setter != null) { try { setter.invoke(instance, value); } catch (Exception e) { throw new BuildException(e,"Failed to set {0}.{1} = {2}",instance.getClass().getName(), propertyName,value); } } else { throw new BuildException("Unable to find setter method: {0}.{1}",instance.getClass().getName(),name); } } else { throw new BuildException("instance and propertyName cannot be null."); } } /** * @param instance Object instance * @param propertyName Property Name * @return */ public static Object getProperty(Object instance, String propertyName) { Object value = null; if (instance != null) { String name = getGetterName(propertyName); Method getter = getGetter(instance, name); if (getter != null) { try { value = getter.invoke(instance); } catch (Exception e) { throw new BuildException(e,"Failed to get {0}.{1}",instance.getClass().getSimpleName(), propertyName); } } else { throw new BuildException("Unable to find getter method: {0}.{1}",instance.getClass().getName(),name); } } return value; } /** * @param instance Instance * @param propertyExpression Nested property expression * @return Value */ public static Object getNestedProperty(Object instance, String propertyExpression) { String[] parts = propertyExpression.split("\\."); if (parts.length == 0) { parts = new String[]{propertyExpression}; } Object value = null; for(int i = 0; i < parts.length; i++) { String part = parts[i]; value = getProperty(instance, part); //stop on first null if (value == null || i == (parts.length - 1)) { break; } else { //keep going down instance = value; } } return value; } //gets list of setter methods private static Method getSetter(Object instance, String methodName) { bld.setLength(0); String key = bld.append(instance.getClass().getName()).append(".").append(methodName).toString(); Method setter = setters.get(key); if (setter == null) { Method[] ms = instance.getClass().getMethods(); for(Method m : ms) { if (m.getName().equals(methodName) && m.getParameterTypes().length == 1) { setter = m; setter.setAccessible(true); setters.put(key, setter); break; } } } return setter; } //gets list of setter methods private static Method getGetter(Object instance, String methodName) { bld.setLength(0); String key = bld.append(instance.getClass().getName()).append(".").append(methodName).toString(); Method getter = getters.get(key); if (getter == null) { Method[] ms = instance.getClass().getMethods(); for(Method m : ms) { if (m.getName().equals(methodName) && m.getParameterTypes().length == 0 && m.getReturnType() != null) { getter = m; getter.setAccessible(true); getters.put(key, getter); break; } } } return getter; } //gets the setter name public static String getSetterName(String propertyName) { StringBuilder bld = new StringBuilder(propertyName.length() + 3); bld.setLength(0); bld.append("set").append(propertyName.substring(0,1).toUpperCase()).append(propertyName.substring(1)); return bld.toString(); } //gets the getter name public static String getGetterName(String propertyName) { StringBuilder bld = new StringBuilder(propertyName.length() + 3); bld.setLength(0); bld.append("get").append(propertyName.substring(0,1).toUpperCase()).append(propertyName.substring(1)); return bld.toString(); } /** * Verifies a getter is valid for a type * @param type Type * @param getterName Getter name * @return Getter return type */ public static Class<?> verifyGetter(Class<?> type, String getterName, Class<?>... allowedReturnTypes) { try { Method m = type.getMethod(getterName); Class<?> returnType = m.getReturnType(); boolean validReturn = false; for(Class<?> allowed : allowedReturnTypes) { if (allowed.isAssignableFrom(returnType)) { validReturn = true; break; } } if (!validReturn) { StringBuilder bld = new StringBuilder(); for(Class<?> allowed : allowedReturnTypes) { if (bld.length() > 0) bld.append(", "); bld.append(allowed.getName()); } throw new BuildException("{0}.{1} return type is not in the list of allowed types:\n{2}",type.getName(),getterName,bld); } else { return returnType; } } catch (Exception e) { throw new BuildException("Unable to find {0}.{1} getter. {2}",type.getName(),getterName, e.getMessage()); } } }