/* * Copyright 2005-2010 Ignis Software Tools Ltd. All rights reserved. */ package jsystem.utils.beans; import java.io.File; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.text.ParseException; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.Properties; import java.util.logging.Level; import java.util.logging.Logger; import jsystem.framework.IgnoreMethod; import jsystem.framework.TestBeanClass; import jsystem.framework.TestBeanMethod; import jsystem.framework.common.CommonResources; import jsystem.framework.scenario.Parameter.ParameterType; import jsystem.framework.scenario.ParameterProvider; import jsystem.framework.scenario.PropertyValidator; import jsystem.framework.scenario.UseProvider; import jsystem.runner.loader.LoadersManager; import jsystem.utils.DateUtils; import jsystem.utils.FileUtils; import jsystem.utils.StringUtils; /** * Utilities class for bean exploring and manipulations. * * @author guy.arieli */ public class BeanUtils { private static Logger log = Logger.getLogger(BeanUtils.class.getName()); /** * Get all the bean elements of a class * * @param reflectClass * the class to explore * @param checkForGet * if set to true will check for getter method as well otherwise * only the setter is mandatory. * @param fieldsClass * an array of all the type of classes that are required: * String.class, Integer.TYPE ... * @return An map with field name as key contain all the bean elements. */ public static HashMap<String, BeanElement> getBeanMap(Class<?> reflectClass, boolean checkForGet, boolean supportEnum, Class<?>... fieldsClass) { ArrayList<BeanElement> beans = getBeans(reflectClass, checkForGet, supportEnum, fieldsClass); HashMap<String, BeanElement> map = new HashMap<String, BeanElement>() { private static final long serialVersionUID = -5404691466641093312L; @Override public BeanElement put(String key, BeanElement value) { return super.put(key.toLowerCase(), value); } @Override public BeanElement get(Object key) { return super.get(key.toString().toLowerCase()); } }; for (BeanElement bean : beans) { // insert it were the field is start with lower case. map.put(bean.getName().toLowerCase(), bean); } return map; } /** * Get all the bean elements of a class * * @param reflectClass * the class to explore * @param checkForGet * if set to true will check for getter method as well otherwise * only the setter is mandatory. * @param fieldsClass * an array of all the type of classes that are required: * String.class, Integer.TYPE ... * @return An array list of contain all the bean elements. */ public static ArrayList<BeanElement> getBeans(Class<?> reflectClass, boolean checkForGet, boolean supportEnum, Class<?>... fieldsClass) { ArrayList<BeanElement> beans = new ArrayList<BeanElement>(); Method[] methods = reflectClass.getMethods(); for (Method currentMethod : methods) { // the method should start with set if (currentMethod.getName().toLowerCase().startsWith("set")) { if (!Modifier.isPublic(currentMethod.getModifiers())) { continue; } // should be ignored if (currentMethod.getAnnotation(IgnoreMethod.class) != null) { continue; } String fieldName = currentMethod.getName().substring("set".length(), currentMethod.getName().length()); // and should have single parameter Class<?>[] types = currentMethod.getParameterTypes(); if (types.length == 1) { UseProvider useProvider = currentMethod.getAnnotation(UseProvider.class); Class<?> paramClass = types[0]; if ((supportEnum && paramClass.isEnum()) || isClassOfTypes(paramClass, fieldsClass) || useProvider != null) { Method getter = findGetMethod(methods, paramClass, fieldName); if (checkForGet && getter == null) { continue; } TestBeanMethod testBeanMethod = currentMethod.getAnnotation(TestBeanMethod.class); BeanElement beanElement = new BeanElement(); if (testBeanMethod != null) { if (testBeanMethod.ignore()) { continue; } Class<? extends PropertyValidator>[] validators = testBeanMethod.validators(); beanElement.setValidators(validators); } if (useProvider != null) { try { String[] args = useProvider.config(); ParameterProvider provider = (ParameterProvider) LoadersManager.getInstance() .getLoader().loadClass(useProvider.provider().getName()).newInstance(); provider.setProviderConfig(args); beanElement.setParameterProvider(provider); } catch (Exception e) { log.log(Level.WARNING, "Fail to create new instance of provider", e); continue; } } if (paramClass.isEnum()) { beanElement.setHasOptions(true); Object[] array = paramClass.getEnumConstants(); if (array != null) { String[] options = new String[array.length]; for (int objectIndex = 0; objectIndex < array.length; objectIndex++) { options[objectIndex] = ((Enum<?>) array[objectIndex]).name(); } beanElement.setOptions(options); } } else { Method optionsMethod = findGetOptionsMethod(methods, paramClass, fieldName); if (optionsMethod != null) { beanElement.setHasOptions(true); try { Object[] array = (Object[]) optionsMethod.invoke(reflectClass.newInstance()); if (array != null) { String[] options = new String[array.length]; for (int optionIndex = 0; optionIndex < array.length; optionIndex++) { options[optionIndex] = array[optionIndex].toString(); } beanElement.setOptions(options); } } catch (Exception e) { e.printStackTrace(); continue; } } // reflectClass.getMethod("get", parameterTypes) } if (testBeanMethod != null) { String[] options = testBeanMethod.options(); if (options != null && options.length > 0) { beanElement.setHasOptions(true); beanElement.setOptions(options); } beanElement.setGroups(testBeanMethod.group()); beanElement.setEditable(testBeanMethod.editable()); } beanElement.setName(fieldName); beanElement.setSetMethod(currentMethod); beanElement.setGetMethod(getter); beanElement.setType(paramClass); beans.add(beanElement); } } } } TestBeanClass testBeanClass = reflectClass.getAnnotation(TestBeanClass.class); if (testBeanClass != null) { String[] includes = testBeanClass.include(); if (includes != null && includes.length > 0) { ArrayList<BeanElement> orderedBeans = new ArrayList<BeanElement>(); for (String currentInclude : includes) { if (currentInclude == null) { continue; } String includeToLower = currentInclude.toLowerCase(); for (BeanElement currentBeanElement : beans) { if (currentBeanElement.getName().toLowerCase().equals(includeToLower)) { orderedBeans.add(currentBeanElement); } } } return orderedBeans; } } return beans; } public static boolean isClassOfTypes(Class<?> clazz, Class<?>... fieldsClass) { // if not set if (fieldsClass == null || fieldsClass.length == 0) { return true; } for (Class<?> className : fieldsClass) { if (className.isAssignableFrom(clazz)) { return true; } } return false; } private static Method findGetMethod(Method[] methods, Class<?> returnType, String fieldName) { for (Method currentMethod : methods) { if (!Modifier.isPublic(currentMethod.getModifiers())) { continue; } if (returnType.equals(Boolean.TYPE)) { if (currentMethod.getName().toLowerCase().equals("is" + fieldName.toLowerCase())) { if (returnType.equals(currentMethod.getReturnType())) { return currentMethod; } } } if (currentMethod.getName().toLowerCase().equals("get" + fieldName.toLowerCase())) { if (returnType.equals(currentMethod.getReturnType())) { return currentMethod; } } } return null; } private static Method findGetOptionsMethod(Method[] methods, Class<?> returnType, String fieldName) { for (Method currentMethod : methods) { if (!Modifier.isPublic(currentMethod.getModifiers())) { continue; } if (currentMethod.getGenericParameterTypes() == null || currentMethod.getGenericParameterTypes().length != 0) { continue; } if (currentMethod.getName().toLowerCase().equals("get" + fieldName.toLowerCase() + "options")) { if (returnType.isArray()) { if (currentMethod.getReturnType().equals(returnType)) { return currentMethod; } } else { if (currentMethod.getReturnType().isArray() && currentMethod.getReturnType().getComponentType().equals(returnType)) { return currentMethod; } } } } return null; } /** * * @return A list of all the basic types: String, int, long, float, short * and double. */ public static Class<?>[] getBasicTypes() { return new Class[] { String.class, Integer.TYPE, Long.TYPE, Float.TYPE, Double.TYPE, Short.TYPE, Boolean.TYPE, Byte.TYPE, File.class, Date.class, Enum.class, String[].class }; } /** * Get an object bean elements value in Properties format * * @param object * The object to explore * @param beanElements * The array of bean element to explore. * @return <code>Properties</code> object contain the field name and values. * @throws Exception */ public static Properties objectToProperties(Object object, ArrayList<BeanElement> beanElements) throws Exception { Properties properties = new Properties(); for (BeanElement currentBeanElement : beanElements) { if (currentBeanElement.getGetMethod() == null) { continue; } Object value = currentBeanElement.getGetMethod().invoke(object, new Object[0]); if (value != null) { properties.put(currentBeanElement.getName(), StringUtils.advancedToString(value)); } } return properties; } /** * Create an object based on is <code>Class</code> and properties object. * * @param clazz * the class type of the object. * @param properties * properties object to init the object with. * @return the instantiated object * @throws Exception */ public static Object propertiesToObject(Class<?> clazz, HashMap<String, String> properties) throws Exception { Object objectInstance = clazz.newInstance(); ArrayList<BeanElement> beans = getBeans(clazz, false, true, getBasicTypes()); for (BeanElement currentBeanElement : beans) { invoke(objectInstance, currentBeanElement.getSetMethod(), properties.get(currentBeanElement.getName()), currentBeanElement.getType()); } return objectInstance; } /** * Invoke setter method of an object using a string value translated to the * expected value types. * * @param object * the object to operate on. * @param method * the method to invoke. * @param value * the string value to use. * @param type * the type of the input. * @throws Exception */ public static void invoke(Object object, Method method, String value, Class<?> type) throws Exception { if (value == null && !String.class.equals(type)) { return; } try { method.invoke(object, getObjects(type, value)); } catch (Throwable t) { log.warning("Unknown type: " + type.getName()); } } public static CellEditorType getBeanType(BeanElement element) { Class<?> elementType = element.getType(); if (element.getParameterProvider() != null) { return CellEditorType.USER_DEFINED; } else if (elementType.equals(String[].class) && element.isHasOptions()) { return CellEditorType.MULTI_SELECTION_LIST; } else if (element.isHasOptions()) { return CellEditorType.LIST; } else if (elementType.equals(Integer.TYPE)) { return CellEditorType.INT; } else if (elementType.equals(Long.TYPE)) { return CellEditorType.LONG; } else if (elementType.equals(Double.TYPE)) { return CellEditorType.DOUBLE; } else if (elementType.equals(Short.TYPE)) { return CellEditorType.SHORT; } else if (elementType.equals(Float.TYPE)) { return CellEditorType.FLOAT; } else if (elementType.equals(Boolean.TYPE)) { return CellEditorType.BOOLEAN; } else if (elementType.equals(String.class)) { return CellEditorType.STRING; } else if (elementType.equals(Byte.TYPE)) { return CellEditorType.BYTE; } else if (elementType.equals(File.class)) { return CellEditorType.FILE; } else if (elementType.equals(Date.class)) { return CellEditorType.DATE; } return CellEditorType.UNKNOWN; } /** * convert the given value to a matching object according to the parameter * type * * @param value * the value to convert * @param type * the Parameter Type * @return the Object matching value by type or null if the value specified * is null */ public static Object getMatchingTypeObject(Object value, ParameterType type) { if (null == value) { return null; } switch (type) { case FILE: return FileUtils.replaceSeparator(value.toString()); case STRING: case ENUM: case DATE: case REFERENCE: return value.toString(); case BOOLEAN: return Boolean.valueOf(value.toString()); case FLOAT: return Float.valueOf(value.toString()); case INT: return Integer.valueOf(value.toString()); case LONG: return Long.valueOf(value.toString()); case DOUBLE: return Double.valueOf(value.toString()); case SHORT: return Short.valueOf(value.toString()); case STRING_ARRAY: if (value instanceof String) { return value.toString().split(CommonResources.DELIMITER); } else if (value instanceof String[]) { StringBuffer toReturn = new StringBuffer(); for (int i = 0; i < ((String[]) value).length; i++) { if (toReturn.length() == 0) { toReturn = new StringBuffer(((String[]) value)[i]); } else { toReturn.append(CommonResources.DELIMITER + ((String[]) value)[i]); } } return toReturn.toString(); } case USER_DEFINED: return value; } return value; } public static Object getObjects(Class<?> clazz, String value) { if (clazz.equals(String.class)) { return value; } else if (clazz.equals(Integer.TYPE)) { if (value == null) { return -1; } return Integer.parseInt(value); } else if (clazz.equals(Long.TYPE)) { if (value == null) { return -1L; } return Long.parseLong(value); } else if (clazz.equals(Float.TYPE)) { if (value == null) { return (float) -1; } return Float.parseFloat(value); } else if (clazz.equals(Double.TYPE)) { if (value == null) { return (double) -1; } return Double.parseDouble(value); } else if (clazz.equals(Short.TYPE)) { if (value == null) { return (short) -1; } return Short.parseShort(value); } else if (clazz.equals(Boolean.TYPE)) { if (value == null) { return false; } return Boolean.parseBoolean(value); } else if (clazz.equals(Byte.TYPE)) { if (value == null) { return false; } return Byte.parseByte(value); } else if (clazz.equals(Character.TYPE)) { if (value == null) { return null; } if (value.length() > 0) { return value.charAt(0); } } else if (clazz.equals(File.class)) { if (value == null) { return null; } return new File(value); } else if (Enum.class.isAssignableFrom(clazz)) { if (value == null) { return null; } for (Object currentObject : clazz.getEnumConstants()) { if (currentObject.toString().equals(value)) { return currentObject; } } return null; } else if (clazz.equals(Date.class)) { if (value == null) { return null; } try { return DateUtils.parseDate(value); } catch (ParseException e) { e.printStackTrace(); } return new Date(System.currentTimeMillis()); } else if (String[].class.isAssignableFrom(clazz)) { if (value == null) { return null; } return value.split(CommonResources.DELIMITER); } throw new RuntimeException("Unknown class type: " + clazz.getName()); } /** * Initialize class using it name * * @param type * the class name * @return the class instance * @throws Exception */ public static Class<?> getClassType(String type) throws Exception { if (type.equals("int")) { return Integer.TYPE; } else if (type.equals("long")) { return Long.TYPE; } else if (type.equals("float")) { return Float.TYPE; } else if (type.equals("double")) { return Double.TYPE; } else if (type.equals("short")) { return Short.TYPE; } else if (type.equals("byte")) { return Byte.TYPE; } else if (type.equals("char")) { return Character.TYPE; } else if (type.equals("boolean")) { return Boolean.TYPE; } else { return LoadersManager.getInstance().getLoader().loadClass(type); } } /** * Create instance from the specified class name and cast it to the * specified class type. * * @param className * @param type * @return instance from the specified class or null if failed * @author Itai */ public static <T> T createInstanceFromClassName(final String className, final Class<T> type) { Class<?> clazz = null; try { clazz = LoadersManager.getInstance().getLoader().loadClass(className); } catch (ClassNotFoundException e) { return null; } if (clazz == null) { return null; } Object instance = null; try { instance = clazz.newInstance(); } catch (InstantiationException | IllegalAccessException e) { return null; } return type.cast(instance); } }