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

org.apache.commons.beanutils.MorePropertyUtilsBean Maven / Gradle / Ivy

There is a newer version: 5.3.2
Show newest version
package org.apache.commons.beanutils;

import static gu.sql2java.utils.CaseSupport.*;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.DynaBean;
import org.apache.commons.beanutils.PropertyUtilsBean;
import org.apache.commons.beanutils.expression.DefaultResolver;
import org.apache.commons.beanutils.expression.Resolver;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONException;
import com.google.common.base.Function;
import com.google.common.base.Throwables;

import gu.sql2java.BaseRow;
import gu.sql2java.RowMetaData;
import gu.sql2java.excel.config.PropertyConfig;
import gu.sql2java.excel.utils.FieldUtils;
/**
 * 基于{@link PropertyUtilsBean}实现对类的私有字段的读写{@link FieldPropertyDescriptor}的支持,
 * 以及对{@link gu.sql2java.BaseRow}的读写支持
 * 
 * @author guyadong
 *
 */
public class MorePropertyUtilsBean extends PropertyUtilsBean {
    /** Log instance */
    private final Log log = LogFactory.getLog(MorePropertyUtilsBean.class);
    private final Resolver resolver = new DefaultResolver();
	private final Function configSupplier;
    /** An empty object array */
    private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
    private static final ThreadLocal curentNestedName=new ThreadLocal<>();
    
    public MorePropertyUtilsBean(){
    	this(null);
    }
	public MorePropertyUtilsBean(FunctionconfigSupplier) {
		super();
		this.configSupplier = configSupplier;
	}

	/**
	 * 当调用父类方法返回为{@code null}时根据bean的类型尝试返回对应的{@link PropertyDescriptor}实例
* 如果bean为{@link BaseRow}则返回{@link BaseRowPropertyDescriptor}实例, * 如果能在bean的类中找到同名的成员({@link Field}),返回{@link FieldPropertyDescriptor}, * 这个过程中如果没有找到指定的成员,会自动尝试对字段名进行驼峰命名(camel-case)和蛇形命名(snake-case)转换来查找成员. * @see org.apache.commons.beanutils.PropertyUtilsBean#getPropertyDescriptor(java.lang.Object, java.lang.String) */ @Override public PropertyDescriptor getPropertyDescriptor(Object bean, String name) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException { PropertyDescriptor descriptor = super.getPropertyDescriptor(bean, name); if(null == descriptor){ try { if(bean instanceof BaseRow ){ RowMetaData metaData = ((BaseRow)bean).fetchMetaData(); if(metaData.columnIDOf(name) >= 0){ return new BaseRowPropertyDescriptor(metaData, name); } } Field field = FieldUtils.getField(bean.getClass(), name,true); if(null == field){ if(isSnakelcase(name)){ field = FieldUtils.getField(bean.getClass(), toCamelcase(name),true); }else if(isCamelcase(name)){ field = FieldUtils.getField(bean.getClass(), toSnakecase(name),true); } } if(null != field){ return new FieldPropertyDescriptor(field); } } catch (IntrospectionException e) { throw new RuntimeException(e); } } return descriptor; } @Override public Method getWriteMethod(Class clazz, PropertyDescriptor descriptor) { if(descriptor instanceof MorePropertyDescriptor){ return descriptor.getWriteMethod(); } if(null != configSupplier){ PropertyConfig config = configSupplier.apply(curentNestedName.get()); String methodName = config.getColumnConfig().getWriteMethod(); if(!methodName.isEmpty()){ try { Field field = FieldUtils.getField(clazz, descriptor.getName(),true); return clazz.getMethod(methodName, null != field ? field.getType() : descriptor.getPropertyType()); } catch (Exception e) { Throwables.throwIfUnchecked(e); throw new RuntimeException(e); } } } return super.getWriteMethod(clazz, descriptor); } @Override Method getReadMethod(Class clazz, PropertyDescriptor descriptor) { if(descriptor instanceof MorePropertyDescriptor){ return descriptor.getReadMethod(); } if(null != configSupplier){ PropertyConfig config = configSupplier.apply(curentNestedName.get()); String methodName = config.getColumnConfig().getReadMethod(); if(!methodName.isEmpty()){ try { return clazz.getMethod(methodName); } catch (Exception e) { Throwables.throwIfUnchecked(e); throw new RuntimeException(e); } } } return super.getReadMethod(clazz, descriptor); } /** * 主要代码复制自{@link PropertyUtilsBean#getSimpleProperty(Object, String)}
* 不同只在于invokeMethod方法改为调用{@link #invokeMethod(Method, Object, String, Object[])} */ @Override public Object getSimpleProperty(Object bean, String name) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException { if (bean == null) { throw new IllegalArgumentException("No bean specified"); } if (name == null) { throw new IllegalArgumentException("No name specified for bean class '" + bean.getClass() + "'"); } // Validate the syntax of the property name if (resolver.hasNested(name)) { throw new IllegalArgumentException ("Nested property names are not allowed: Property '" + name + "' on bean class '" + bean.getClass() + "'"); } else if (resolver.isIndexed(name)) { throw new IllegalArgumentException ("Indexed property names are not allowed: Property '" + name + "' on bean class '" + bean.getClass() + "'"); } else if (resolver.isMapped(name)) { throw new IllegalArgumentException ("Mapped property names are not allowed: Property '" + name + "' on bean class '" + bean.getClass() + "'"); } // Handle DynaBean instances specially if (bean instanceof DynaBean) { final DynaProperty descriptor = ((DynaBean) bean).getDynaClass().getDynaProperty(name); if (descriptor == null) { throw new NoSuchMethodException("Unknown property '" + name + "' on dynaclass '" + ((DynaBean) bean).getDynaClass() + "'" ); } return (((DynaBean) bean).get(name)); } // Retrieve the property getter method for the specified property final PropertyDescriptor descriptor = getPropertyDescriptor(bean, name); if (descriptor == null) { throw new NoSuchMethodException("Unknown property '" + name + "' on class '" + bean.getClass() + "'" ); } final Method readMethod = getReadMethod(bean.getClass(), descriptor); if (readMethod == null) { throw new NoSuchMethodException("Property '" + name + "' has no getter method in class '" + bean.getClass() + "'"); } // Call the property getter and return the value final Object value = invokeMethod(readMethod, bean,name, EMPTY_OBJECT_ARRAY); return (value); } /** * 主要代码复制自{@link PropertyUtilsBean#setSimpleProperty(Object, String, Object)}
* 不同只在于invokeMethod方法改为调用{@link #invokeMethod(Method, Object, String, Object[])} */ @Override public void setSimpleProperty(Object bean, String name, Object value) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException { if (bean == null) { throw new IllegalArgumentException("No bean specified"); } if (name == null) { throw new IllegalArgumentException("No name specified for bean class '" + bean.getClass() + "'"); } // Validate the syntax of the property name if (resolver.hasNested(name)) { throw new IllegalArgumentException ("Nested property names are not allowed: Property '" + name + "' on bean class '" + bean.getClass() + "'"); } else if (resolver.isIndexed(name)) { throw new IllegalArgumentException ("Indexed property names are not allowed: Property '" + name + "' on bean class '" + bean.getClass() + "'"); } else if (resolver.isMapped(name)) { throw new IllegalArgumentException ("Mapped property names are not allowed: Property '" + name + "' on bean class '" + bean.getClass() + "'"); } // Handle DynaBean instances specially if (bean instanceof DynaBean) { final DynaProperty descriptor = ((DynaBean) bean).getDynaClass().getDynaProperty(name); if (descriptor == null) { throw new NoSuchMethodException("Unknown property '" + name + "' on dynaclass '" + ((DynaBean) bean).getDynaClass() + "'" ); } ((DynaBean) bean).set(name, value); return; } // Retrieve the property setter method for the specified property final PropertyDescriptor descriptor = getPropertyDescriptor(bean, name); if (descriptor == null) { throw new NoSuchMethodException("Unknown property '" + name + "' on class '" + bean.getClass() + "'" ); } final Method writeMethod = getWriteMethod(bean.getClass(), descriptor); if (writeMethod == null) { throw new NoSuchMethodException("Property '" + name + "' has no setter method in class '" + bean.getClass() + "'"); } // Call the property setter method final Object[] values = new Object[1]; values[0] = value; if (log.isTraceEnabled()) { final String valueClassName = value == null ? "" : value.getClass().getName(); log.trace("setSimpleProperty: Invoking method " + writeMethod + " with value " + value + " (class " + valueClassName + ")"); } invokeMethod(writeMethod, bean, name,values); } @Override public Object getNestedProperty(Object bean, String name) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException { if (bean == null) { throw new IllegalArgumentException("No bean specified"); } if (name == null) { throw new IllegalArgumentException("No name specified for bean class '" + bean.getClass() + "'"); } curentNestedName.set(""); try { // Resolve nested references while (resolver.hasNested(name)) { String next = resolver.next(name); Object nestedBean = null; bean = tryParseJsonIfString(bean); if (bean instanceof Map) { AtomicReference ref = new AtomicReference<>(next); nestedBean = getPropertyOfMapBean((Map) bean, ref); if(!next.equals(ref.get())){ next = ref.get(); } } else if (resolver.isMapped(next)) { nestedBean = getMappedProperty(bean, next); } else if (resolver.isIndexed(next)) { nestedBean = getIndexedProperty(bean, next); } else { concat(next); nestedBean = getSimpleProperty(bean, next); } if (nestedBean == null) { return null; } bean = nestedBean; name = resolver.remove(name); } concat(name); bean = tryParseJsonIfString(bean); if (bean instanceof Map) { bean = getPropertyOfMapBean((Map) bean, new AtomicReference<>(name)); } else if (resolver.isMapped(name)) { bean = getMappedProperty(bean, name); } else if (resolver.isIndexed(name)) { bean = getIndexedProperty(bean, name); } else { bean = getSimpleProperty(bean, name); } return bean; } finally { curentNestedName.remove(); } } private void concat(String next){ String n = curentNestedName.get(); if(!n.isEmpty()){ n = n.concat("."); } n = n.concat(next); curentNestedName.set(n); } /** * 读取{@link Map}中{@code propertyName}定义的值
* 如果不存在{@code propertyName}定义的key, * 尝试将{@code propertyName}转为驼峰命名(camel-case)或蛇形命名(snake-case)的key再试
* 如果找到了则将转换后的字段名保存在{@code propertyName} * @param bean Map Object * @param propertyName [inout] property name * @return value or null */ private Object getPropertyOfMapBean(Map bean, AtomicReferencepropertyName) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { if(bean.containsKey(propertyName.get())){ return super.getPropertyOfMapBean(bean, propertyName.get()); } Object value = null; if(isCamelcase(propertyName.get())){ String p = toSnakecase(propertyName.get()); if(bean.containsKey(p)){ value = bean.get(p); propertyName.set(p); } }else if(isSnakelcase(propertyName.get())){ String p = toSnakecase(propertyName.get()); if(bean.containsKey(p)){ value = bean.get(p); propertyName.set(p); } } return value; } /** * 对{@link #invokeMethod(Method, Object, Object[])}进一步封装,支持{@link FieldPropertyDescriptor}和{@link BaseRowPropertyDescriptor}
* 当输入的方法对象自来{@link FieldPropertyDescriptor}或{@link BaseRowPropertyDescriptor}时 * 使用 {@link FieldPropertyDescriptor}或{@link BaseRowPropertyDescriptor}提供读写字段方法要求的参数调用 * @see FieldPropertyDescriptor#readMethod(Object, String) * @see FieldPropertyDescriptor#writeMethod(Object, String, Object) */ private Object invokeMethod( final Method method, final Object bean, final String name, final Object[] values)throws IllegalAccessException, InvocationTargetException{ if(MorePropertyDescriptor.class.isAssignableFrom(method.getDeclaringClass())){ return method.invoke(null, net.gdface.utils.ArrayUtils.addAll(new Object[]{bean,name}, values)); } return invokeMethod(method,bean,values); } /** * This just catches and wraps IllegalArgumentException. * copy from {@link PropertyUtilsBean} */ private Object invokeMethod( final Method method, final Object bean, final Object[] values) throws IllegalAccessException, InvocationTargetException { if(bean == null) { throw new IllegalArgumentException("No bean specified " + "- this should have been checked before reaching this method"); } try { return method.invoke(bean, values); } catch (final NullPointerException cause) { // JDK 1.3 and JDK 1.4 throw NullPointerException if an argument is // null for a primitive value (JDK 1.5+ throw IllegalArgumentException) String valueString = ""; if (values != null) { for (int i = 0; i < values.length; i++) { if (i>0) { valueString += ", " ; } if (values[i] == null) { valueString += ""; } else { valueString += (values[i]).getClass().getName(); } } } String expectedString = ""; final Class[] parTypes = method.getParameterTypes(); if (parTypes != null) { for (int i = 0; i < parTypes.length; i++) { if (i > 0) { expectedString += ", "; } expectedString += parTypes[i].getName(); } } final IllegalArgumentException e = new IllegalArgumentException( "Cannot invoke " + method.getDeclaringClass().getName() + "." + method.getName() + " on bean class '" + bean.getClass() + "' - " + cause.getMessage() // as per https://issues.apache.org/jira/browse/BEANUTILS-224 + " - had objects of type \"" + valueString + "\" but expected signature \"" + expectedString + "\"" ); if (!BeanUtils.initCause(e, cause)) { log.error("Method invocation failed", cause); } throw e; } catch (final IllegalArgumentException cause) { String valueString = ""; if (values != null) { for (int i = 0; i < values.length; i++) { if (i>0) { valueString += ", " ; } if (values[i] == null) { valueString += ""; } else { valueString += (values[i]).getClass().getName(); } } } String expectedString = ""; final Class[] parTypes = method.getParameterTypes(); if (parTypes != null) { for (int i = 0; i < parTypes.length; i++) { if (i > 0) { expectedString += ", "; } expectedString += parTypes[i].getName(); } } final IllegalArgumentException e = new IllegalArgumentException( "Cannot invoke " + method.getDeclaringClass().getName() + "." + method.getName() + " on bean class '" + bean.getClass() + "' - " + cause.getMessage() // as per https://issues.apache.org/jira/browse/BEANUTILS-224 + " - had objects of type \"" + valueString + "\" but expected signature \"" + expectedString + "\"" ); if (!BeanUtils.initCause(e, cause)) { log.error("Method invocation failed", cause); } throw e; } } private Object tryParseJsonIfString(Object bean) { if(bean instanceof String){ try { return JSON.parseObject((String)bean); } catch (JSONException e) { throw new IllegalArgumentException(String.format("INVALID String %s for bean",bean)); } } return bean; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy