Java tutorial
/* * Copyright (c) 2005, Jeong-Ho Eun * All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package pico.commons.beans; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.UndeclaredThrowableException; import java.math.BigDecimal; import java.math.BigInteger; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.Hashtable; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; import java.util.Vector; import org.apache.commons.beanutils.ConvertUtils; /** * Bean public Field , set, get, field * . * @author Eun Jeong-Ho, succeun@gmail.com * @since 2005. 6. 16. */ public class BeanInfo { private static final String[] EMPTY_STRING_ARRAY = new String[0]; private static final Set<Class<?>> SIMPLE_TYPE_SET = new HashSet<Class<?>>(); private static final Map<Class<?>, BeanInfo> CLASS_INFO_MAP = Collections .synchronizedMap(new LinkedHashMap<Class<?>, BeanInfo>()); static { SIMPLE_TYPE_SET.add(byte.class); SIMPLE_TYPE_SET.add(short.class); SIMPLE_TYPE_SET.add(char.class); SIMPLE_TYPE_SET.add(int.class); SIMPLE_TYPE_SET.add(long.class); SIMPLE_TYPE_SET.add(float.class); SIMPLE_TYPE_SET.add(double.class); SIMPLE_TYPE_SET.add(boolean.class); SIMPLE_TYPE_SET.add(String.class); SIMPLE_TYPE_SET.add(Byte.class); SIMPLE_TYPE_SET.add(Short.class); SIMPLE_TYPE_SET.add(Character.class); SIMPLE_TYPE_SET.add(Integer.class); SIMPLE_TYPE_SET.add(Long.class); SIMPLE_TYPE_SET.add(Float.class); SIMPLE_TYPE_SET.add(Double.class); SIMPLE_TYPE_SET.add(Boolean.class); SIMPLE_TYPE_SET.add(Date.class); SIMPLE_TYPE_SET.add(Class.class); SIMPLE_TYPE_SET.add(BigInteger.class); SIMPLE_TYPE_SET.add(BigDecimal.class); SIMPLE_TYPE_SET.add(Collection.class); SIMPLE_TYPE_SET.add(Set.class); SIMPLE_TYPE_SET.add(Map.class); SIMPLE_TYPE_SET.add(List.class); SIMPLE_TYPE_SET.add(HashMap.class); SIMPLE_TYPE_SET.add(TreeMap.class); SIMPLE_TYPE_SET.add(ArrayList.class); SIMPLE_TYPE_SET.add(LinkedList.class); SIMPLE_TYPE_SET.add(HashSet.class); SIMPLE_TYPE_SET.add(TreeSet.class); SIMPLE_TYPE_SET.add(Vector.class); SIMPLE_TYPE_SET.add(Hashtable.class); SIMPLE_TYPE_SET.add(Enumeration.class); } /** * (JDK String, List) . * @param clazz * @return True true */ public static boolean isKnownType(Class<?> clazz) { if (SIMPLE_TYPE_SET.contains(clazz)) { return true; } else if (Collection.class.isAssignableFrom(clazz)) // Collection , { return true; } else if (Map.class.isAssignableFrom(clazz)) { return true; } else if (List.class.isAssignableFrom(clazz)) { return true; } else if (Set.class.isAssignableFrom(clazz)) { return true; } else return Iterator.class.isAssignableFrom(clazz); } /** * * ClassInfo . * @param clazz * @return ClassInfo */ public static BeanInfo getInstance(Class<?> clazz) { return getInstance(clazz, false); } /** * * ClassInfo . * @param clazz * @return ClassInfo */ public static BeanInfo getInstance(Class<?> clazz, boolean isPrivateAccess) { synchronized (clazz) { BeanInfo cache = (BeanInfo) CLASS_INFO_MAP.get(clazz); if (cache == null) { cache = new BeanInfo(clazz, isPrivateAccess); CLASS_INFO_MAP.put(clazz, cache); } return cache; } } /** * {@link Method#invoke Method.invoke()} * Throwable, Throwable . * @param t - Throwable * @return Throwable */ public static Throwable unwrapThrowable(Throwable t) { Throwable t2 = t; while (true) { if (t2 instanceof InvocationTargetException) { t2 = ((InvocationTargetException) t).getTargetException(); } else if (t instanceof UndeclaredThrowableException) { t2 = ((UndeclaredThrowableException) t).getUndeclaredThrowable(); } else { return t2; } } } /** * Bean Property . * Property set . * , property name setName() . * @param obj Bean set * @param name Property * @param value Property set . */ public static void setProperty(Object obj, String name, String value) { BeanInfo info = BeanInfo.getInstance(obj.getClass()); info.invokeSetter(obj, name, value); } /** * . * @return String */ public static String toString(Object obj) { StringBuffer buf = new StringBuffer(); BeanInfo info = BeanInfo.getInstance(obj.getClass()); String fullname = info.getClassName(); buf.append(fullname).append(":{"); Field[] fields = info.getFields(); for (int i = 0; i < fields.length; i++) { try { if (i != 0) buf.append(','); buf.append(fields[i].getName()).append('='); Object f = fields[i].get(obj); Class<?> fc = fields[i].getType(); if (fc.isArray()) { buf.append('['); int length = Array.getLength(f); for (int j = 0; j < length; j++) { if (j != 0) buf.append(','); Object element = Array.get(f, j); buf.append(element.toString()); } buf.append(']'); } else buf.append(f.toString()); } catch (Exception e) { } } if (fields.length > 0) buf.append(','); String[] methodNames = info.getGetterNames(); for (int i = 0; i < methodNames.length; i++) { try { if (i != 0) buf.append(','); buf.append(methodNames[i]).append('='); Object f = info.invokeGetter(obj, methodNames[i]); Class<?> fc = info.getGetter(methodNames[i]).getReturnType(); if (fc.isArray()) { buf.append('['); int length = Array.getLength(f); for (int j = 0; j < length; j++) { if (j != 0) buf.append(','); Object element = Array.get(f, j); buf.append(element.toString()); } buf.append(']'); } else buf.append(f.toString()); } catch (Exception e) { } } buf.append('}'); return buf.toString(); } /** * <code>toString</code> . * @param obj Object * @return String * @throws java.lang.IllegalAccessException */ public static String toSimpleString(Object obj) throws IllegalAccessException, InvocationTargetException { StringBuffer strBuf = new StringBuffer(); strBuf.append('{'); BeanInfo info = BeanInfo.getInstance(obj.getClass()); Field[] fields = info.getFields(); if (fields.length > 0) { for (int i = 0; i < fields.length - 1; i++) strBuf.append(fields[i].get(obj)).append(','); strBuf.append(fields[fields.length - 1].get(obj)); } Method[] methods = info.getGetters(); if (methods.length > 0) { if (fields.length > 0) strBuf.append(','); for (int i = 0; i < methods.length - 1; i++) strBuf.append(methods[i].invoke(obj)).append(','); strBuf.append(methods[methods.length - 1].invoke(obj)); } strBuf.append('}'); return strBuf.toString(); } private String className; private Class<?> targetClass; private String[] getterNames = EMPTY_STRING_ARRAY; private String[] setterNames = EMPTY_STRING_ARRAY; private Map<String, Method> setMethods = new LinkedHashMap<String, Method>(); private Map<String, Method> getMethods = new LinkedHashMap<String, Method>(); private Map<String, Class<?>> setTypes = new LinkedHashMap<String, Class<?>>(); private Map<String, Class<?>> getTypes = new LinkedHashMap<String, Class<?>>(); private Map<String, Field> fields = new LinkedHashMap<String, Field>(); private String[] fieldNames = EMPTY_STRING_ARRAY; private Map<String, Class<?>> fieldTypes = new LinkedHashMap<String, Class<?>>(); private boolean isPrivateAccess = false; // , /** * . * @param clazz * @param isPrivateAccess */ private BeanInfo(Class<?> clazz, boolean isPrivateAccess) { this.isPrivateAccess = isPrivateAccess; className = clazz.getName(); targetClass = clazz; addMethods(clazz); addFields(clazz); Class<?> superClass = clazz.getSuperclass(); while (superClass != null) { addMethods(superClass); addFields(superClass); superClass = superClass.getSuperclass(); } getterNames = getMethods.keySet().toArray(new String[getMethods.keySet().size()]); setterNames = setMethods.keySet().toArray(new String[setMethods.keySet().size()]); fieldNames = fields.keySet().toArray(new String[fields.keySet().size()]); } /** * . * @return */ public Class<?> getTargetClass() { return this.targetClass; } /** * public {@link Field Field} . * @param cls Class */ private void addFields(Class<?> cls) { Field[] fields = (isPrivateAccess) ? cls.getDeclaredFields() : cls.getFields(); fields = ReflectionUtil.normalize(fields); for (int i = 0; i < fields.length; i++) { if (isPrivateAccess) fields[i].setAccessible(true); String fieldName = fields[i].getName(); Class<?> fieldType = fields[i].getType(); this.fields.put(fieldName, fields[i]); fieldTypes.put(fieldName, fieldType); } } /** * get~, set~, is~ . * @param cls Class */ private void addMethods(Class<?> cls) { Method[] methods = (isPrivateAccess) ? cls.getDeclaredMethods() : cls.getMethods(); for (int i = 0; i < methods.length; i++) { if (isPrivateAccess) methods[i].setAccessible(true); String name = methods[i].getName(); if (name.equals("getClass")) // getClass() continue; if (name.startsWith("set") && name.length() > 3) { if (methods[i].getParameterTypes().length == 1 && methods[i].getReturnType() == void.class) { name = dropCase(name); setMethods.put(name, methods[i]); setTypes.put(name, methods[i].getParameterTypes()[0]); } } else if (name.startsWith("get") && name.length() > 3) { if (methods[i].getParameterTypes().length == 0) { name = dropCase(name); getMethods.put(name, methods[i]); getTypes.put(name, methods[i].getReturnType()); } } else if (name.startsWith("is") && name.length() > 2) { if (methods[i].getParameterTypes().length == 0) { name = dropCase(name); getMethods.put(name, methods[i]); getTypes.put(name, methods[i].getReturnType()); } } } /* try { BeanInfo bi = Introspector.getBeanInfo(cls); PropertyDescriptor[] propertyDescriptors = bi.getPropertyDescriptors(); for (int i = 0; i < propertyDescriptors.length;i++) { Method getter = propertyDescriptors[i].getReadMethod(); String name = dropCase(getter.getEngineName()); getMethods.put(name, getter); getTypes.put(name, getter.getReturnType()); Method setter = propertyDescriptors[i].getWriteMethod(); name = dropCase(setter.getEngineName()); setMethods.put(name, setter); setTypes.put(name, setter.getParameterTypes()[0]); } } catch (IntrospectionException e) { throw new BeanNotAccessException("failed Method analysis. ", e); } */ } /** * is, set, get . * @param name * @return */ private static String dropCase(String name) { if (name.startsWith("is")) name = name.substring(2); else if (name.startsWith("get") || name.startsWith("set")) name = name.substring(3); if (name.length() == 1 || (name.length() > 1 && !Character.isUpperCase(name.charAt(1)))) name = name.substring(0, 1).toLowerCase(Locale.US) + name.substring(1); return name; } /** * . * @return */ public String getClassName() { return className; } /** * Field . * @return */ public Field[] getFields() { return (Field[]) fields.values().toArray(new Field[0]); } /** * Setter . * @return Setter */ public Method[] getSetters() { return (Method[]) setMethods.values().toArray(new Method[0]); } /** * Getter . * @return Getter */ public Method[] getGetters() { return (Method[]) getMethods.values().toArray(new Method[0]); } /** * Setter . * @param propertyName Property * @return Method Setter */ public Method getSetter(String propertyName) { Method method = (Method) setMethods.get(propertyName); if (method == null) throw new BeanNotAccessException( "There is no WRITEABLE property named '" + propertyName + "' in class '" + className + "'"); return method; } /** * Getter . * @param propertyName - Property * @return Method Getter */ public Method getGetter(String propertyName) { Method method = (Method) getMethods.get(propertyName); if (method == null) throw new BeanNotAccessException( "There is no READABLE property named '" + propertyName + "' in class '" + className + "'"); return method; } /** * . * @param propertyName - Property * @return Field */ public Field getField(String propertyName) { Field field = (Field) fields.get(propertyName); if (field == null) throw new BeanNotAccessException( "There is no FIELD named '" + propertyName + "' in class '" + className + "'"); return field; } /** * Setter . * @param propertyName Property * @return Setter */ public Class<?> getSetterType(String propertyName) { Class<?> clazz = setTypes.get(propertyName); if (clazz == null) throw new BeanNotAccessException( "There is no WRITEABLE property named '" + propertyName + "' in class '" + className + "'"); return clazz; } /** * Getter . * @param propertyName Property * @return Getter */ public Class<?> getGetterType(String propertyName) { Class<?> clazz = getTypes.get(propertyName); if (clazz == null) throw new BeanNotAccessException( "There is no READABLE property named '" + propertyName + "' in class '" + className + "'"); return clazz; } /** * Field . * @param propertyName Property * @return Field */ public Class<?> getFieldType(String propertyName) { Class<?> clazz = fieldTypes.get(propertyName); if (clazz == null) throw new BeanNotAccessException( "There is no FIELD property named '" + propertyName + "' in class '" + className + "'"); return clazz; } /** * Setter . * @param bean Bean * @param propertyName Property * @param param Method Setter */ public void invokeSetter(Object bean, String propertyName, Object param) { Method method = (Method) setMethods.get(propertyName); if (method == null) throw new BeanNotAccessException( "There is no WRITEABLE property named '" + propertyName + "' in class '" + className + "'"); try { if (param != null) { if (targetClass.isAssignableFrom(param.getClass())) method.invoke(bean, new Object[] { param }); else { Class<?> clsParam = setTypes.get(propertyName); //Object tmp = Convertor.convert(clsParam, param); // Convertor Object tmp = ConvertUtils.convert(param, clsParam); method.invoke(bean, new Object[] { tmp }); } } } catch (IllegalAccessException e) { throw new BeanNotAccessException("failed Method invoke. ", e); } catch (IllegalArgumentException e) { throw new BeanNotAccessException("failed Method invoke. ", e); } catch (InvocationTargetException e) { throw new BeanNotAccessException("failed Method invoke. ", e); } } /** * Getter . * @param bean Bean * @param propertyName - Property * @return Method Getter */ public Object invokeGetter(Object bean, String propertyName) { Method method = (Method) getMethods.get(propertyName); if (method == null) throw new BeanNotAccessException( "There is no READABLE property named '" + propertyName + "' in class '" + className + "'"); try { return method.invoke(bean, new Object[] {}); } catch (IllegalAccessException e) { throw new BeanNotAccessException("failed Method invoke. ", e); } catch (IllegalArgumentException e) { throw new BeanNotAccessException("failed Method invoke. ", e); } catch (InvocationTargetException e) { throw new BeanNotAccessException("failed Method invoke. ", e); } } /** * Field . * @param bean Bean * @param propertyName Property * @param param Method Setter */ public void setField(Object bean, String propertyName, Object param) { Field field = (Field) fields.get(propertyName); if (field == null) throw new BeanNotAccessException( "There is no FIELD property named '" + propertyName + "' in class '" + className + "'"); try { if (param != null) { if (targetClass.isAssignableFrom(param.getClass())) field.set(bean, param); else { Class<?> clsParam = fieldTypes.get(propertyName); //Object tmp = Convertor.convert(clsParam, param); Object tmp = ConvertUtils.convert(param, clsParam); field.set(bean, tmp); } } } catch (IllegalAccessException e) { throw new BeanNotAccessException("failed Method invoke. ", e); } catch (IllegalArgumentException e) { throw new BeanNotAccessException("failed Method invoke. ", e); } } /** * Field . * @param bean Bean * @param propertyName - Property * @return Field */ public Object getField(Object bean, String propertyName) { Field field = (Field) fields.get(propertyName); if (field == null) throw new BeanNotAccessException( "There is no FIELD property named '" + propertyName + "' in class '" + className + "'"); try { return field.get(bean); } catch (IllegalAccessException e) { throw new BeanNotAccessException("failed Method invoke. ", e); } catch (IllegalArgumentException e) { throw new BeanNotAccessException("failed Method invoke. ", e); } } /** * Readable . * @return */ public String[] getGetterNames() { return getterNames; } /** * Writeable . * @return */ public String[] getSetterNames() { return setterNames; } /** * Field . * @return */ public String[] getFieldNames() { return fieldNames; } /** * Writeable . * @param propertyName * @return True Writeable */ public boolean hasSetter(String propertyName) { return setMethods.keySet().contains(propertyName); } /** * Readable . * @param propertyName * @return True Readable */ public boolean hasGetter(String propertyName) { return getMethods.keySet().contains(propertyName); } /** * Field . * @param propertyName * @return True Field */ public boolean hasField(String propertyName) { return fields.keySet().contains(propertyName); } /** * Getter, Setter Bean */ public final static int METHOD_TYPE = 0; /** * Public Field Bean */ public final static int FIELD_TYPE = 1; /** * Bean . * Getter . * @return {@link METHOD_TYPE} {@link FIELD_TYPE} */ public int getType() { String[] names = getGetterNames(); return (names.length > 0) ? METHOD_TYPE : FIELD_TYPE; } }