blob: d79235e80e74eeddcaeb4755a8004f81dc1e3e88 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.bval.util;
import org.apache.commons.beanutils.PropertyUtils;
import java.beans.PropertyDescriptor;
import java.lang.annotation.ElementType;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Map;
/**
* Description: Undefined dynamic strategy (FIELD or METHOD access) Uses PropertyUtils or tries to determine field to
* access the value<br/>
*/
public class PropertyAccess extends AccessStrategy {
private final Class<?> beanClass;
private final String propertyName;
private Field rememberField;
/**
* Create a new PropertyAccess instance.
*
* @param clazz
* @param propertyName
*/
public PropertyAccess(Class<?> clazz, String propertyName) {
this.beanClass = clazz;
this.propertyName = propertyName;
}
/**
* {@inheritDoc}
*/
public ElementType getElementType() {
return rememberField != null ? ElementType.FIELD : ElementType.METHOD;
}
private static Object getPublicProperty(Object bean, String property) throws InvocationTargetException,
NoSuchMethodException, IllegalAccessException {
if (bean instanceof Map<?, ?>) {
return ((Map<?, ?>) bean).get(property);
} else { // supports DynaBean and standard Objects
return PropertyUtils.getSimpleProperty(bean, property);
}
}
/**
* Get a named property from <code>bean</code>.
*
* @param bean
* @param propertyName
* @return Object found
* @throws InvocationTargetException
* @throws NoSuchMethodException
* @throws IllegalAccessException
*/
public static Object getProperty(Object bean, String propertyName) throws InvocationTargetException,
NoSuchMethodException, IllegalAccessException {
return new PropertyAccess(bean.getClass(), propertyName).get(bean);
}
/**
* {@inheritDoc}
*/
public String toString() {
return "Property{" + beanClass.getName() + '.' + propertyName + '}';
}
/**
* {@inheritDoc}
*/
public Type getJavaType() {
Type result = getTypeInner();
return result == null ? Object.class : result;
}
/**
* Learn whether this {@link PropertyAccess} references a known property.
*
* @return boolean
*/
public boolean isKnown() {
return getTypeInner() != null;
}
/**
* Find out what, if any, type can be calculated.
*
* @return type found or <code>null</code>
*/
private Type getTypeInner() {
if (rememberField != null) {
return rememberField.getGenericType();
}
Method readMethod = getPropertyReadMethod(propertyName, beanClass);
if (readMethod != null) {
return readMethod.getGenericReturnType();
}
Field fld = getField(propertyName, beanClass);
if (fld != null) {
cacheField(fld);
return rememberField.getGenericType();
}
return null;
}
private static Method getPropertyReadMethod(String propertyName, Class<?> beanClass) {
for (PropertyDescriptor each : PropertyUtils.getPropertyDescriptors(beanClass)) {
if (each.getName().equals(propertyName)) {
return each.getReadMethod();
}
}
return null;
}
private static Field getField(String propertyName, Class<?> beanClass) {
try { // try public field
return beanClass.getField(propertyName);
} catch (NoSuchFieldException ex2) {
// search for private/protected field up the hierarchy
Class<?> theClass = beanClass;
while (theClass != null) {
try {
return theClass.getDeclaredField(propertyName);
} catch (NoSuchFieldException ex3) {
// do nothing
}
theClass = theClass.getSuperclass();
}
}
return null;
}
/**
* {@inheritDoc}
*/
public String getPropertyName() {
return propertyName;
}
/**
* {@inheritDoc}
*/
public Object get(Object bean) {
try {
if (rememberField != null) { // cache field of previous access
return rememberField.get(bean);
}
try { // try public method
return getPublicProperty(bean, propertyName);
} catch (NoSuchMethodException ex) {
return getFieldValue(bean);
}
} catch (IllegalArgumentException e) {
throw e;
} catch (Exception e) {
throw new IllegalArgumentException("cannot access " + propertyName, e);
}
}
private Object getFieldValue(Object bean) throws IllegalAccessException {
Field field = getField(propertyName, beanClass);
if (field != null) {
cacheField(field);
return rememberField.get(bean);
}
throw new IllegalArgumentException("cannot access field " + propertyName);
}
private void cacheField(Field field) {
if (!field.isAccessible()) {
field.setAccessible(true);
}
this.rememberField = field;
}
/**
* {@inheritDoc}
*/
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
PropertyAccess that = (PropertyAccess) o;
return beanClass.equals(that.beanClass) && propertyName.equals(that.propertyName);
}
/**
* {@inheritDoc}
*/
public int hashCode() {
int result;
result = beanClass.hashCode();
result = 31 * result + propertyName.hashCode();
return result;
}
}