/* * JEF - Copyright 2009-2010 Jiyi (mr.jiyi@gmail.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package jef.tools.reflect; import java.lang.annotation.Annotation; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.NoSuchElementException; import javax.management.ReflectionException; import jef.common.log.LogUtil; import jef.tools.ArrayUtils; import jef.tools.Assert; import jef.tools.StringUtils; import jef.tools.collection.CollectionUtils; public final class BeanWrapperImpl extends BeanWrapper { private static final int MAX_ENTRIES = 200; private static Map<Class<?>, Map<String, PropertyHolder>> cache = new LinkedHashMap<Class<?>, Map<String, PropertyHolder>>() { private static final long serialVersionUID = 1L; protected boolean removeEldestEntry(Entry<Class<?>, Map<String, PropertyHolder>> eldest) { return size() > MAX_ENTRIES; } }; @Override public String toString() { return obj.toString(); } public <T extends Annotation> T getAnnotationOnField(String name, Class<T> clz) { PropertyHolder pp = properties.get(name); Assert.notNull(pp, name + " is not a property of class " + this.getClassName()); return pp.getFieldAnnotation(clz); } public <T extends Annotation> T getAnnotationOnGetter(String name, Class<T> clz) { PropertyHolder pp = properties.get(name); Assert.notNull(pp, name + " is not a property of class " + this.getClassName()); return pp.getGetterAnnotation(clz); } public <T extends Annotation> T getAnnotationOnSetter(String name, Class<T> clz) { PropertyHolder pp = properties.get(name); Assert.notNull(pp, name + " is not a property of class " + this.getClassName()); return pp.getSetterAnnotation(clz); } public Object getWrapped() { return obj; } protected Object obj; private ClassEx clz; private Map<String, PropertyHolder> properties = null; public BeanWrapperImpl(Object obj) { this(obj, false); } /** * 构造对象 * <p> * Title: * </p> * <p> * Description: * </p> * * @param obj * @param fieldBase * 是否基于字段来分析属性。如果为是,则不论是否有setter和getter,返回全部field */ public BeanWrapperImpl(Object obj, boolean fieldBase) { super(obj); this.obj = obj; Class<?> c = obj.getClass(); this.clz=new ClassEx(c); if (cache.containsKey(c)) { properties = cache.get(c); } else { properties = init(clz, fieldBase); cache.put(c, properties); } } private static Map<String, PropertyHolder> init(ClassEx cls, boolean fieldBase) { List<Class<?>> supers = new ArrayList<Class<?>>(); Class<?> c=cls.getWrappered(); while (c != Object.class){ supers.add(c); c = c.getSuperclass(); }; Map<String, PropertyHolder> result = new HashMap<String, PropertyHolder>(); for (int i = supers.size(); i > 0; i--) { result.putAll(initByClass(cls,supers.get(i - 1), fieldBase)); } return result; } private static Map<String, PropertyHolder> initByClass(ClassEx cw,Class<?> c, boolean fieldBase) { Map<String, PropertyHolder> myMap = new HashMap<String, PropertyHolder>(); for (Field field : c.getDeclaredFields()) { int mod = field.getModifiers(); if (Modifier.isStatic(mod) || Modifier.isNative(mod)) { continue; } FieldEx fex=new FieldEx(field,cw); // 由于这里是根据field来查找getter 和 setter 的,对于非标准bean可能会出问题。 MethodEx getter = BeanUtils.getGetter(fex); MethodEx setter = BeanUtils.getSetter(fex); if (setter != null || getter != null || fieldBase) { if(!Modifier.isPublic(mod)){ field.setAccessible(true); } PropertyHolder holder=new PropertyHolder(getter, setter, fex, field.getName()); myMap.put(field.getName(), holder); } } return myMap; } /** * 是否属性 */ public boolean isProperty(String fieldName) { PropertyHolder pp = properties.get(fieldName); return pp != null; } /** * 判断属性可读? */ public boolean isReadableProperty(String fieldName) { PropertyHolder pp = properties.get(fieldName); if (pp == null) return false; return pp.getReadMethod() != null; } /** * 判断属性可否写 */ public boolean isWritableProperty(String fieldName) { if (fieldName == null) return false; PropertyHolder pp = properties.get(fieldName); if (pp == null) return false; return pp.getWriteMethod() != null; } public Type getPropertyType(String fieldName) { PropertyHolder pp = properties.get(fieldName); if (pp == null) throw new NoSuchElementException("Cound not find field ["+fieldName+"] in "+this.obj.getClass().getName()); return pp.getGenericType(); } @Override public Class<?> getPropertyRawType(String fieldName) { PropertyHolder pp = properties.get(fieldName); if (pp == null) throw new NoSuchElementException("Cound not find field ["+fieldName+"] in "+this.obj.getClass().getName()); return pp.getType(); } /** * 获得属性值 */ public Object getPropertyValue(String name) { PropertyHolder pp = properties.get(name); if (pp == null) throw new NullPointerException(name + " is not a property of class " + this.getClassName()); Method m = pp.getReadMethod(); if (m == null) throw new NullPointerException(); try { return m.invoke(obj, new Object[] {}); } catch (IllegalArgumentException e) { LogUtil.exception(e); return null; } catch (IllegalAccessException e) { LogUtil.exception(e); return null; } catch (InvocationTargetException e) { LogUtil.exception(e); return null; } } /** * 设置属性 */ public void setPropertyValue(String fieldName, Object newValue) { PropertyHolder pp = properties.get(fieldName); if (pp == null) throw new NullPointerException("Can not find property '" + fieldName + "' in bean " + obj.getClass().getName()); Method m = pp.getWriteMethod(); if (m == null) throw new NullPointerException("Can not find set method '" + fieldName + "' in bean " + obj.getClass().getName()); try { m.invoke(obj, new Object[] { newValue }); } catch (IllegalArgumentException e) { String detail=StringUtils.exceptionStack(e, "jef","com"); String message=StringUtils.concat("IllegalArgumentException while setting property", fieldName," in bean ",obj.getClass().getName()," value:", (newValue == null ? "null" : newValue.getClass().getName()),"\ndetail:",detail); LogUtil.error(message); } catch (IllegalAccessException e) { String detail=StringUtils.exceptionStack(e, "jef","com"); String message=StringUtils.concat("IllegalAccessException while setting property", fieldName," in bean ",obj.getClass().getName(),"\ndetail:",detail); LogUtil.error(message); } catch (InvocationTargetException e) { String detail=StringUtils.exceptionStack(e.getTargetException(), "jef","com"); String message=StringUtils.concat("InvocationTargetException while setting property", fieldName," in bean ",obj.getClass().getName(),"\ndetail:",detail); LogUtil.error(message); } } /** * 查找全部可读写的属性 */ public PropertyHolder[] getRwPropertyDescriptors() { ArrayList<PropertyHolder> list = new ArrayList<PropertyHolder>(); for (PropertyHolder pp : properties.values()) { if (pp.getReadMethod() != null && pp.getWriteMethod() != null) { list.add(pp); } } return list.toArray(new PropertyHolder[list.size()]); } /** * 查找属性名称(忽略大小写) */ public String findPropertyIgnoreCase(String string) { if (properties.containsKey(string)) return string; for (String field : properties.keySet()) { if (field.equalsIgnoreCase(string)) return field; } return null; } /** * 通过反射执行一个方法,不论该方法是否私有或受保护 * * @param methodName * @param param * @return * @throws ReflectionException */ public Object invokeMethod(String methodName, Object... param) throws ReflectionException { try { Class<?>[] cs = new Class[param.length]; for (int i = 0; i < param.length; i++) { if (param[i] == null) { cs[i] = Object.class; } else { cs[i] = param[i].getClass(); } } MethodEx me = BeanUtils.getCompatibleMethod(obj.getClass(), methodName, cs); return me.invoke(obj, param); } catch (IllegalArgumentException e) { throw new ReflectionException(e); } catch (IllegalAccessException e) { throw new ReflectionException(e); } catch (InvocationTargetException e) { throw new ReflectionException(e); } } // /** // * 获取某个field的类型 // */ // public Class<?> getFieldType(String fieldName) { // PropertyHolder pp = properties.get(fieldName); // if (pp == null) // throw new NullPointerException(StringUtils.concat("Can not find PropertyDescriptor in ",getClassName()," for field: ", fieldName)); // return GenericUtils.getRawClass(pp.getPropertyType()); // } public String getClassName() { return obj.getClass().getName(); } /** * 更为复杂的获取属性方法 * * @param name * : 支持复杂属性递归获取。 例如: friend.name.first * 取bean的friend属性,再取其name属性,再取其first属性 例如: friends[2].name * 取bean的friends属性,然后取这个集合的第3个元素,再取其name属性 例如: children[-2] * 取bean的children属性,然后取这个集合的倒数第2个元素 * @throws ReflectionException */ public Object getNestedProperty(String name) { int n = name.indexOf('.'); if (n > -1) { String field = name.substring(0, n); name = name.substring(n + 1); Object tmp = null; try { tmp = getIndexedObject(field, false); } catch (ReflectionException e) { LogUtil.exception(e); } if (tmp == null) return null; return new BeanWrapperImpl(tmp).getNestedProperty(name); } else { try { return getIndexedObject(name, false); } catch (ReflectionException e) { throw new RuntimeException(e); } } } /** * @throws ReflectionException * */ public void setNestedProperty(String name, Object value) throws ReflectionException { int n = name.indexOf('.'); if (n > -1) { String field = name.substring(0, n); name = name.substring(n + 1); Object tmp = getIndexedObject(field, true); new BeanWrapperImpl(tmp).setNestedProperty(name, value); } else { setIndexedPropertyValue(name, value); } } /** * 设置字段属性,允许使用[index]表示第几个元素 */ @SuppressWarnings("rawtypes") public void setIndexedPropertyValue(String field, Object value) { int x = field.indexOf('['); int y = field.indexOf(']'); if (x > -1 && y > -1 && y > x) { Entry<String, Integer> info = toKeyAndIndex(field, x, y); int index = info.getValue(); ClassEx c = new ClassEx(getPropertyType(info.getKey())); Object o = getPropertyValue(info.getKey()); if (o == null) { if (c.isArray()) { ClassEx cmpType=new ClassEx(c.getComponentType()); Object array = Array.newInstance(cmpType.getWrappered(), 0); // 修正数据类型 value = ConvertUtils.toProperType(value, cmpType, null); array = ArrayUtils.setValueAndExpandArray(array, index, value); this.setPropertyValue(info.getKey(), array); } else if (List.class.isAssignableFrom(c.getWrappered())) { List list = new ArrayList(); listSetAndExpand(list, index, value); this.setPropertyValue(info.getKey(), list); } } else { if (c.isArray()) { ClassEx cmpType=new ClassEx(c.getComponentType()); value = ConvertUtils.toProperType(value, cmpType, null); Object array = ArrayUtils.setValueAndExpandArray(o, index, value); if (array != value) this.setPropertyValue(info.getKey(), array); } else if (List.class.isAssignableFrom(c.getWrappered())) { ClassEx cmpType=new ClassEx(c.getComponentType()); Object old = ConvertUtils.findElementInstance(o); value = ConvertUtils.toProperType(value, cmpType, old); listSetAndExpand((List) value, index, value); } } } else { Type c = this.getPropertyType(field); Object old = this.isReadableProperty(field) ? this.getPropertyValue(field) : null; value = ConvertUtils.toProperType(value, new ClassEx(c), old); this.setPropertyValue(field, value); } } public Object getIndexedObject(String field) { try { return getIndexedObject(field, false); } catch (ReflectionException e) { LogUtil.exception(e); return null; } } /** * 获取字段属性,允许使用[index]表示第几个元素 * * @throws IllegalAccessException * @throws InstantiationException */ @SuppressWarnings("rawtypes") private Object getIndexedObject(String field, boolean create) throws ReflectionException { int x = field.indexOf('['); int y = field.indexOf(']'); if (x > -1 && y > -1 && y > x) { Entry<String, Integer> info = toKeyAndIndex(field, x, y); int index = info.getValue(); Object o = getPropertyValue(info.getKey()); if (o == null) { throw new NullPointerException("value of " + info.getKey() + " is null."); } if (o.getClass().isArray()) { if (create && ArrayUtils.isIndexValid(o, index) == false) { Object r = ArrayUtils.toFixLength(o, (index < 0 ? -index : index + 1)); if (r != o) setPropertyValue(info.getKey(), r);// 扩展数组 Object instance = ConvertUtils.createElementByElement(r); if (instance != null) ArrayUtils.set(r, index, instance); } return ArrayUtils.get(o, index); } else if (o instanceof List) { if (create && isIndexValid(o, index) == false) { toFixedSize((List) o, (index < 0 ? -index : index + 1)); Object instance = ConvertUtils.createElementByElement(o); if (instance != null) listSet((List) o, index, instance); } List<?> l = (List<?>) o; return index >= 0 ? l.get(index) : l.get(l.size() + index); } else { throw new IllegalArgumentException("Not a Indexed Object(" + field + "):" + o.getClass().getName()); } } else { Object obj = getPropertyValue(field); if (obj == null && create) { ClassEx c = new ClassEx(getPropertyType(field)); try { obj = c.newInstance(); } catch (Exception e) { throw new ReflectionException(e); } setPropertyValue(field, obj); } return obj; } } /** * 获取List当中的值 * * @param obj * @param index * @return */ @SuppressWarnings("rawtypes") private static Object listGet(List obj, int index) { int length = obj.size(); if (index < 0) index += length; return obj.get(index); } /** * 设置List当中的值 * * @param obj * @param index * @param value */ @SuppressWarnings({ "rawtypes", "unchecked" }) private static void listSet(List obj, int index, Object value) { int length = obj.size(); if (index < 0) index += length; obj.set(index, value); } /** * 检测索引是否有效 当序号为负数时,-1表示最后一个元素,-2表示倒数第二个,以此类推 * @param obj * @param index * @return 如果下标有效则true */ private static boolean isIndexValid(Object obj, int index) { int length = CollectionUtils.length(obj); if (index < 0) index += length; return index >= 0 && index < length; } /** * 设置List当中的值。如果超出下标,自动扩展List以适应 * @param obj * @param index * @param value */ @SuppressWarnings("rawtypes") private static void listSetAndExpand(List obj, int index, Object value) { int length = obj.size(); if (index < 0 && index + length >= 0) { index += length; } else if (index < 0) {// 需要扩张 toFixedSize(obj, -index); } else if (index >= length) {// 扩张 toFixedSize(obj, index + 1); } listSet(obj, index, value); } /** * 将list的大小调节为指定的大小 如果List长度大于制定的大小,后面的元素将被丢弃, 如果list小于指定大小,将会由null代替 */ @SuppressWarnings({ "rawtypes", "unchecked" }) private static void toFixedSize(List obj, int newsize) { int len = obj.size(); if (newsize == len) return; if (newsize > len) { for (int i = len; i < newsize; i++) { obj.add(null); } } else { for (int i = len; i > newsize; i--) { obj.remove(i - 1); } } } private static jef.common.Entry<String, Integer> toKeyAndIndex(String name, int x, int y) { try { return new jef.common.Entry<String, Integer>(name.substring(0, x), Integer.parseInt(name.substring(x + 1, y))); } catch (NumberFormatException e) { throw new RuntimeException(e); } } @Override public Collection<String> getPropertyNames() { return this.properties.keySet(); } @Override public Collection<String> getRwPropertyNames() { ArrayList<String> list = new ArrayList<String>(); for (PropertyHolder pp : properties.values()) { if (pp.getReadMethod() != null && pp.getWriteMethod() != null) { list.add(pp.getName()); } } return list; } @Override public Collection<? extends Property> getProperties() { return properties.values(); } @Override public Property getProperty(String name) { return properties.get(name); } }