org.apache.commons.beanutils.MorePropertyUtilsBean Maven / Gradle / Ivy
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.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(isSnakecase(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(isSnakecase(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