All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jexx.bean.AbstractBeanPropertyAccessor Maven / Gradle / Ivy

The newest version!
package jexx.bean;

import jexx.convert.Convert;
import jexx.exception.UtilException;
import jexx.util.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.*;
import java.util.*;
import java.util.stream.Collectors;

public abstract class AbstractBeanPropertyAccessor implements PropertyAccessor {

    private static final Logger LOG = LoggerFactory.getLogger(AbstractBeanPropertyAccessor.class);

    /** 内嵌路径 */
    private String nestedPath = "";
    /** 包装对象 */
    private Object wrappedObject;
    /** 父对象 */
    private Object rootObject;

    /** 允许array,list,set自动增长 */
    protected boolean allowCollectionAutoGrow = true;
    /**
     * 当持有对象为null时, 是否允许创建对象
     * 
     *     a.b 持有对象为 a
     *     a[1][2] 持有对象为 a[1]
     *     a[1][2].b 持有对象为 a[1][2]
     * 
*/ protected boolean allowCreateHoldValueIfNull = true; /** * 设置包装对象 * @param object 待包装对象 * @param nestedPath 包装对象在父对象中路径 * @param rootObject 父对象 */ public void setWrappedInstance(Object object, String nestedPath, Object rootObject){ this.wrappedObject = ObjectUtil.unwrapOptional(object); Assert.notNull(this.wrappedObject, "Target object must not be null"); this.nestedPath = (nestedPath != null ? nestedPath : ""); this.rootObject = (!this.nestedPath.isEmpty() ? rootObject : this.wrappedObject); } /** * 设置包装对象 * @param object 待包装对象 */ public void setWrappedInstance(Object object){ setWrappedInstance(object, "", null); } /** * 返回包装对象 */ public final Object getWrappedInstance() { Assert.isTrue(this.wrappedObject != null, "No wrapped object"); return this.wrappedObject; } /** * 返回包装对象的Class */ public final Class getWrappedClass() { return getWrappedInstance().getClass(); } /** * 对象路径 */ public final String getNestedPath() { return this.nestedPath; } /** * 根对象 * @return 根对象 */ public final Object getRootInstance() { Assert.isTrue(this.rootObject != null, "No root object"); return this.rootObject; } /** * 根对象的class */ public final Class getRootClass() { return getRootInstance().getClass(); } public void setAllowCollectionAutoGrow(boolean allowCollectionAutoGrow) { this.allowCollectionAutoGrow = allowCollectionAutoGrow; } public void setAllowCreateHoldValueIfNull(boolean allowCreateHoldValueIfNull) { this.allowCreateHoldValueIfNull = allowCreateHoldValueIfNull; } /** * 根据属性名称获取值 * @param propertyName 属性名称 * @return 值 */ public Object getPropertyValue(String propertyName){ int index = propertyName.indexOf(NESTED_PROPERTY_SEPARATOR); if(index > -1){ String nestedProperty = propertyName.substring(0, index); AbstractBeanPropertyAccessor accessor = getNestedPropertyAccessor(nestedProperty); return accessor.getPropertyValue(propertyName.substring(index+1)); } PropertyToken token = getPropertyNameToken(propertyName); return getPropertyValue(token); } protected Object getPropertyValue(PropertyToken token){ String propertyName = token.canonicalName; String actual = token.actualName; PropertyHandle handle = getPropertyHandle(actual); if(handle == null || !handle.isRead()){ throw new BeanException(StringUtil.format("Class[{}] cannot read property {}", getRootClass(), actual)); } try { Object value = handle.getValue(); if(token.keys != null){ StringBuilder indexedPropertyName = new StringBuilder(token.actualName); // apply indexes and map keys for (int i = 0; i < token.keys.length; i++) { String key = token.keys[i]; if (value == null) { throw new BeanException("Cannot read index property \"{}\" because key \"{}\" is null ", propertyName, indexedPropertyName.toString()); } else if (value.getClass().isArray()) { int index = Integer.parseInt(key); value = growArrayIfNecessary(value, index, indexedPropertyName.toString()); value = Array.get(value, index); } else if (value instanceof List) { List list = (List)value; int index = Integer.parseInt(key); growCollectionIfNecessary(list, index, indexedPropertyName.toString(), handle, i+1); value = list.get(index); } else if (value instanceof Set) { // Apply index to Iterator in case of a Set. Set set = (Set) value; int index = Integer.parseInt(key); growCollectionIfNecessary(set, index, indexedPropertyName.toString(), handle, i+1); if (index < 0 || index >= set.size()) { throw new BeanException("Cannot get element {} from set because the set's size is {}, path={}", index, set.size(), indexedPropertyName); } Iterator it = set.iterator(); for (int j = 0; it.hasNext(); j++) { Object elem = it.next(); if (j == index) { value = elem; break; } } } else if (value instanceof Map) { Map map = (Map) value; Class paramType = handle.getMapKeyType(i+1); Object convertedMapKey = convertIfNecessary(key, paramType); value = map.get(convertedMapKey); } else { throw new IllegalArgumentException(StringUtil.format("key {} is not illegal")); } indexedPropertyName.append(PROPERTY_KEY_PREFIX).append(key).append(PROPERTY_KEY_SUFFIX); } } return value; } catch (Exception e){ throw new BeanException(e); } } /** * 获取索引属性名称的路径与对应的值; *

* 如获取a[].b[] : *

     *         a[0]b[0]=1
     *         a[0]b[1]=2
     *     
*

* @param propertyName 索引属性名称 * @return 路径与对应的值, map有序 */ public List getIndexPropertyValues(String propertyName){ List indexProperties = new ArrayList<>(); indexProperties.add(new IndexProperty(this.nestedPath, this.wrappedObject)); return getIndexPropertyValues(propertyName, indexProperties); } /** * 获取索引属性名称的路径与对应的值; null值会丢失 */ public Map getMapIndexPropertyValues(String propertyName){ List indexProperties = getIndexPropertyValues(propertyName); return indexProperties.stream().collect(Collectors.toMap(IndexProperty::getPath, IndexProperty::getValue)); } protected List getIndexPropertyValues(String propertyName, List indexProperties){ int index = propertyName.indexOf(NESTED_PROPERTY_SEPARATOR); if(index > -1){ String nestedProperty = propertyName.substring(0, index); try { List newList = new ArrayList<>(); for(IndexProperty property : indexProperties){ AbstractBeanPropertyAccessor accessor = newNestedPropertyAccessor(property.getValue(), property.getPath()); List m1 = accessor.getIndexPropertyValues(nestedProperty); newList.addAll(m1); } return getIndexPropertyValues(propertyName.substring(index+1), newList); } catch (Exception e) { throw new BeanException(e); } } PropertyToken token = getPropertyNameToken(propertyName); List newList = new ArrayList<>(); for(IndexProperty property : indexProperties){ scanPropertyMapValue(newList, property.getPath(), property.getValue(), token, 0); } return newList; } protected List scanPropertyMapValue(List all, String rootPath, Object holdingObject, PropertyToken token, int keyLocation){ if(holdingObject == null){ if(ArrayUtil.isEmpty(token.keys) || token.keys.length == keyLocation - 1){ String path = StringUtil.isEmpty(rootPath) ? "" : rootPath.concat("."); all.add(new IndexProperty(path.concat(token.getPath()), null)); return all; } return all; } if(keyLocation == 0){ AbstractBeanPropertyAccessor accessor = newNestedPropertyAccessor(holdingObject, rootPath); Object value = accessor.getPropertyValue(token.actualName); return scanPropertyMapValue(all, rootPath, value, token, keyLocation+1); } if(ArrayUtil.isEmpty(token.keys) || token.keys.length == keyLocation - 1){ String path = StringUtil.isEmpty(rootPath) ? "" : rootPath.concat("."); all.add(new IndexProperty(path.concat(token.getPath()), holdingObject)); return all; } int keyIndex = keyLocation - 1; String key = token.keys[keyIndex]; if (holdingObject.getClass().isArray()){ int length = Array.getLength(holdingObject); if("".equals(key)){ for(int i = 0; i < length; i++){ Array.get(holdingObject, i); PropertyToken newToken = token.clone(); newToken.keys[keyIndex] = Integer.toString(i); scanPropertyMapValue(all, rootPath, Array.get(holdingObject, i), newToken, keyLocation+1); } } else{ int index = Integer.parseInt(key); if(index < 0 || index >= length){ throw new BeanException("key {} is illegal", key); } PropertyToken newToken = token.clone(); scanPropertyMapValue(all, rootPath, Array.get(holdingObject, index), newToken, keyLocation+1); } } else if (holdingObject instanceof List) { int length = ((List) holdingObject).size(); if("".equals(key)){ for(int i = 0; i < length; i++){ PropertyToken newToken = token.clone(); newToken.keys[keyIndex] = Integer.toString(i); scanPropertyMapValue(all, rootPath, ((List) holdingObject).get(i), newToken, keyLocation+1); } } else{ int index = Integer.parseInt(key); if(index < 0 || index >= length){ throw new BeanException("key {} is illegal", key); } PropertyToken newToken = token.clone(); scanPropertyMapValue(all, rootPath, Array.get(holdingObject, index), newToken, keyLocation+1); } } else if (holdingObject instanceof Set) { Set set = (Set) holdingObject; int length = set.size(); if("".equals(key)){ Iterator it = set.iterator(); for (int i = 0; it.hasNext(); i++) { Object elem = it.next(); PropertyToken newToken = token.clone(); newToken.keys[keyIndex] = Integer.toString(i); scanPropertyMapValue(all, rootPath, elem, newToken, keyLocation+1); } } else{ int index = Integer.parseInt(key); if(index < 0 || index >= length){ throw new BeanException("key {} is illegal", key); } Iterator it = set.iterator(); for (int i = 0; it.hasNext(); i++) { if(i == index){ Object elem = it.next(); PropertyToken newToken = token.clone(); newToken.keys[keyIndex] = Integer.toString(i); scanPropertyMapValue(all, rootPath, elem, newToken, keyLocation+1); break; } } } } else if (holdingObject instanceof Map) { Map mValue = (Map) holdingObject; if("".equals(key)){ for(Map.Entry entry : mValue.entrySet()){ PropertyToken newToken = token.clone(); newToken.keys[keyIndex] = entry.getKey().toString(); scanPropertyMapValue(all, rootPath, entry.getValue(), newToken, keyLocation+1); } } else{ PropertyToken newToken = token.clone(); scanPropertyMapValue(all, rootPath, mValue.get(key), newToken, keyLocation+1); } } return all; } /** * 获取索引属性名称上一级的值; 如 属性名称为 a[a1][a2] 即 上一级为 a[a1] * @param token 索引属性token */ protected Object getPropertyHoldingValue(PropertyToken token){ PropertyToken holdingToken = getPropertyHoldingToken(token); return getPropertyValue(holdingToken); } /** * 获取token的上一级token * @param token token * @return 上一级属性的token */ protected PropertyToken getPropertyHoldingToken(PropertyToken token){ Assert.isTrue(token.keys != null, "No token keys"); String canonicalName = token.canonicalName; int lastIndex = canonicalName.lastIndexOf(PROPERTY_KEY_PREFIX); canonicalName = canonicalName.substring(0, lastIndex); String[] keys = new String[token.keys.length - 1]; System.arraycopy(token.keys, 0, keys, 0, token.keys.length - 1); PropertyToken holdingToken = new PropertyToken(token.actualName); holdingToken.canonicalName = canonicalName; holdingToken.keys = keys; return holdingToken; } /** * 确保属性持有者不为null */ private void ensurePropertyHoldingValueNotNull(PropertyToken token){ if(this.allowCreateHoldValueIfNull && token.keys != null){ try { PropertyHandle ph = getPropertyHandle(token.actualName); Object value = ph.getValue(); if(value == null){ value = newValue(ph.getPropertyType(), null, token.actualName); ph.setValue(value); } StringBuilder indexedPropertyName = new StringBuilder(token.actualName); for (int i = 0; i < token.keys.length-1; i++) { String key = token.keys[i]; if (value.getClass().isArray()) { int index = Integer.parseInt(key); value = growArrayIfNecessary(value, index, indexedPropertyName.toString()); Object v = Array.get(value, index); if(v == null){ v = newValue(ph.getNestedType(i+1), null, key); Array.set(value, index, v); } value = v; } else if (value instanceof List) { List list = (List)value; int index = Integer.parseInt(key); growCollectionIfNecessary(list, index, indexedPropertyName.toString(), ph, i+1); Object v = list.get(index); if(v == null){ v = newValue(ph.getNestedType(i+1), null, key); Array.set(value, index, v); } value = v; } else if (value instanceof Set) { Set set = (Set)value; int index = Integer.parseInt(key); if (index < 0 || index >= set.size()) { throw new BeanException(StringUtil.format("property name {} key[{}] is big!", indexedPropertyName, key)); } Object v = null; Iterator it = set.iterator(); for (int j = 0; it.hasNext(); j++) { Object elem = it.next(); if (j == index) { v = elem; break; } } if(v == null){ v = newValue(ph.getNestedType(i+1), null, key); Array.set(value, index, v); } value = v; } else if (value instanceof Map) { Map map = (Map) value; Class paramType = ph.getMapKeyType(i+1); Object convertedMapKey = convertIfNecessary(key, paramType); Object v = map.get(convertedMapKey); if(v == null){ v = newValue(ph.getMapValueType(i+1), null, key); map.put(convertedMapKey, v); } value = v; } else { throw new IllegalArgumentException(StringUtil.format("key {} is not illegal")); } indexedPropertyName.append(PROPERTY_KEY_PREFIX).append(key).append(PROPERTY_KEY_SUFFIX); } } catch (Exception e){ LOG.error("", e); } } } public void setPropertyValue(String propertyName, Object value){ int index = propertyName.indexOf(NESTED_PROPERTY_SEPARATOR); if(index > -1){ String nestedProperty = propertyName.substring(0, index); AbstractBeanPropertyAccessor accessor = getNestedPropertyAccessor(nestedProperty); accessor.setPropertyValue(propertyName.substring(index+1), value); } else{ PropertyToken token = getPropertyNameToken(propertyName); setPropertyValue(token, value); } } protected void setPropertyValue(PropertyToken token, Object newValue){ PropertyHandle ph = getPropertyHandle(token.actualName); if(ph == null || !ph.isWrite()){ throw new BeanException("Class[{}] cannot write property {}", getWrappedClass(), token.actualName); } try { ensurePropertyHoldingValueNotNull(token); if(token.keys != null){ String lastKey = token.keys[token.keys.length - 1]; PropertyToken holdingToken = getPropertyHoldingToken(token); Object holdingValue = getPropertyValue(holdingToken); if(holdingValue == null){ throw new BeanException("Class[{}] cannot write index property {} because holding key {} is null ", getWrappedClass(), token.canonicalName, holdingToken.canonicalName); } else if(holdingValue.getClass().isArray()){ Class requiredType = holdingValue.getClass().getComponentType(); int arrayIndex = Integer.parseInt(lastKey); int length = Array.getLength(holdingValue); if(this.allowCollectionAutoGrow && arrayIndex >= length){ holdingValue = growArrayIfNecessary(holdingValue, arrayIndex, holdingToken.canonicalName); } Object convertValue = convertIfNecessary(newValue, requiredType); Array.set(holdingValue, arrayIndex, convertValue); } else if(holdingValue instanceof List){ List list = (List)holdingValue; Class requiredType = ph.getNestedType(token.keys.length); int arrayIndex = Integer.parseInt(lastKey); if(this.allowCollectionAutoGrow && arrayIndex >= list.size()){ for (int i = list.size(); i <= arrayIndex; i++) { if(i == arrayIndex){ list.add(Convert.convert(requiredType, newValue)); } else{ list.add(newValue(requiredType, null, holdingToken.canonicalName)); } } } else{ list.set(arrayIndex, Convert.convert(requiredType, newValue)); } } else if(holdingValue instanceof Set){ Set set = (Set)holdingValue; int arrayIndex = Integer.parseInt(lastKey); //只能添加元素 if(arrayIndex != set.size()){ throw new BeanException("only add element for set of size {}, path={}", arrayIndex, set.size(), holdingToken.canonicalName); } Class requiredType = ph.getNestedType(token.keys.length); set.add(Convert.convert(requiredType, newValue)); } else if (holdingValue instanceof Map){ Map map = (Map)holdingValue; Class keyType = ph.getMapKeyType(token.keys.length); Object key = Convert.convert(keyType, lastKey); Class valueType = ph.getMapValueType(token.keys.length); Object value = Convert.convert(valueType, newValue); map.put(key, value); } else{ throw new IllegalArgumentException(StringUtil.format("Class[{}] property {} is neither an array or a list or a map" ,getWrappedClass(), holdingToken.canonicalName)); } } else{ ph.setValue(newValue); } } catch (Exception e){ throw new BeanException(e); } } protected PropertyToken getPropertyNameToken(String propertyName){ String actualName = null; List keys = new ArrayList<>(2); int searchIndex = 0; while (searchIndex != -1) { int keyStart = propertyName.indexOf(PROPERTY_KEY_PREFIX, searchIndex); searchIndex = -1; if (keyStart != -1) { int keyEnd = propertyName.indexOf(PROPERTY_KEY_SUFFIX, keyStart + PROPERTY_KEY_PREFIX.length()); if (keyEnd != -1) { if (actualName == null) { actualName = propertyName.substring(0, keyStart); } String key = propertyName.substring(keyStart + PROPERTY_KEY_PREFIX.length(), keyEnd); if (key.length() > 1 && (key.startsWith("'") && key.endsWith("'")) || (key.startsWith("\"") && key.endsWith("\""))) { key = key.substring(1, key.length() - 1); } keys.add(key); searchIndex = keyEnd + PROPERTY_KEY_SUFFIX.length(); } } } PropertyToken tokens = new PropertyToken(actualName != null ? actualName : propertyName); if (!keys.isEmpty()) { tokens.canonicalName += PROPERTY_KEY_PREFIX.concat(StringUtil.join(keys, PROPERTY_KEY_SUFFIX + PROPERTY_KEY_PREFIX)).concat(PROPERTY_KEY_SUFFIX); tokens.keys = ArrayUtil.toStringArray(keys); } return tokens; } /** * 获取属性处理类 */ protected abstract PropertyHandle getPropertyHandle(String propertyName); /** * 根据类型转换为对应的属性 * @param value 值 * @param type 值对应属性 * @return 转换后的值 */ private Object convertIfNecessary(Object value, Type type){ return Convert.convert(type, value); } /** * 自动增长数组 * @param array 数组对象 * @param index 索引 * @param name 属性名称 * @return 数组索引值 */ private Object growArrayIfNecessary(Object array, int index, String name){ int length = Array.getLength(array); if(this.allowCollectionAutoGrow && index >= length){ Class componentType = array.getClass().getComponentType(); Object newArray = Array.newInstance(componentType, index+1); System.arraycopy(array, 0, newArray, 0, length); setPropertyValue(name, newArray); return newArray; } return array; } private Collection growCollectionIfNecessary(Collection collection, int index, String name, PropertyHandle pd, int nestingLevel){ int size = collection.size(); if(this.allowCollectionAutoGrow && index >= size){ Class elementType = pd.getNestedType(nestingLevel); if(elementType != null){ for(int i = size; i <= index; i++){ collection.add(newValue(elementType, null, name)); } } } return collection; } /** * 根据类型创建value * @param type 对象类型 * @param elementType 对象为集合时的元素类型 * @param name 属性名称 * @return value */ private Object newValue(Class type, Class elementType, String name) { try { if (type.isArray()) { Class componentType = type.getComponentType(); // TODO - only handles 2-dimensional arrays if (componentType.isArray()) { Object array = Array.newInstance(componentType, 1); Array.set(array, 0, Array.newInstance(componentType.getComponentType(), 0)); return array; } else { return Array.newInstance(componentType, 0); } } else if (Collection.class.isAssignableFrom(type)) { return CollectionUtil.createCollection(type, elementType, 16); } else if (Map.class.isAssignableFrom(type)) { return MapUtil.createMap(type, elementType, 16); } else { Constructor ctor = type.getDeclaredConstructor(); if (Modifier.isPrivate(ctor.getModifiers())) { if(this.allowCollectionAutoGrow){ } throw new IllegalAccessException("Auto-growing not allowed with private constructor: " + ctor); } return ReflectUtil.newInstance(ctor); } } catch (Throwable ex) { throw new UtilException(ex, "Could not instantiate property type \"{}\" to auto-grow nested property path {}", type.getName(), name); } } /** * 构造属性寄存对象 * @param object 当前对象 * @param nestedPath 当前内嵌路径 * @return 属性寄存对象 */ protected abstract AbstractBeanPropertyAccessor newNestedPropertyAccessor(Object object, String nestedPath); /** * 根据内嵌属性名称获取属性寄存器对象 * @param nestedProperty 含有内嵌变量的属性,如a[1], a["b"] * @return 属性寄存对象器 */ private AbstractBeanPropertyAccessor getNestedPropertyAccessor(String nestedProperty) { PropertyToken tokens = getPropertyNameToken(nestedProperty); String canonicalName = tokens.canonicalName; //确保持有属性不为空 ensurePropertyHoldingValueNotNull(tokens); Object value = getPropertyValue(tokens); if(this.allowCreateHoldValueIfNull && value == null){ PropertyHandle ph = getPropertyHandle(tokens.actualName); Class clazz = tokens.keys != null ?ph.getNestedType(tokens.keys.length): ph.getPropertyType(); value = newValue(clazz, null, tokens.actualName); setPropertyValue(tokens, value); } if(value == null){ throw new BeanException("property \"{}\" is null", nestedProperty); } AbstractBeanPropertyAccessor propertyAccessor = newNestedPropertyAccessor(value, this.nestedPath+canonicalName+NESTED_PROPERTY_SEPARATOR); return propertyAccessor; } protected abstract static class PropertyHandle{ private String propertyName; private boolean read; private boolean write; public PropertyHandle(String propertyName, boolean read, boolean write) { this.propertyName = propertyName; this.read = read; this.write = write; } public boolean isRead(){ return this.read; } public boolean isWrite(){ return this.write; } public abstract Object getValue() throws Exception; public abstract void setValue(Object value) throws Exception; /** 获取属性类型 */ public abstract Class getPropertyType(); public abstract Class getCollectionType(int nestingLevel); public abstract Class getMapKeyType(int nestingLevel); public abstract Class getMapValueType(int nestingLevel); /** * 获取集合类内嵌内省 * @param nestingLevel 内嵌层次 * @param typeIndexesPerLevel 每个层次对应索引类型, 如Map有两个类型 * @return 指定层次的内嵌类型 */ protected abstract Class getNestedType(int nestingLevel, Map typeIndexesPerLevel); public Class getNestedType(int nestingLevel){ return getNestedType(nestingLevel, null); } /** * type解析成对应的class */ protected Class resolveClass(Type type){ if(type == null){ return null; } if(type instanceof Class){ return (Class)type; } if(type instanceof GenericArrayType){ return ((GenericArrayType) type).getGenericComponentType().getClass(); } if(type instanceof ParameterizedType){ Type newType = ((ParameterizedType) type).getRawType(); return resolveClass(newType); } return null; } } protected static class PropertyToken{ /** 属性名, 如 a[1]为a */ protected String actualName; /** 标准属性名(全路径), 如 a */ protected String canonicalName; /** 属性key, 1 */ protected String[] keys; public PropertyToken(String propertyName) { this.actualName = propertyName; this.canonicalName = propertyName; } public PropertyToken clone(){ PropertyToken token = new PropertyToken(this.actualName); token.canonicalName = canonicalName; token.keys = ObjectUtil.clone(keys); return token; } public String getPath(){ String canonicalName = actualName; if(ArrayUtil.isNotEmpty(keys)){ String str = PROPERTY_KEY_PREFIX.concat(StringUtil.join(keys, PROPERTY_KEY_SUFFIX + PROPERTY_KEY_PREFIX)).concat(PROPERTY_KEY_SUFFIX); canonicalName = actualName.concat(str); } return canonicalName; } } }