Author: shane.bryzak(a)jboss.com
Date: 2010-05-30 22:50:10 -0400 (Sun, 30 May 2010)
New Revision: 6361
Added:
extensions/trunk/src/main/java/org/jboss/weld/extensions/util/AbstractBeanProperty.java
extensions/trunk/src/main/java/org/jboss/weld/extensions/util/TypedBeanProperty.java
Modified:
extensions/trunk/src/main/java/org/jboss/weld/extensions/util/AnnotatedBeanProperty.java
Log:
WELDX-106
Added:
extensions/trunk/src/main/java/org/jboss/weld/extensions/util/AbstractBeanProperty.java
===================================================================
---
extensions/trunk/src/main/java/org/jboss/weld/extensions/util/AbstractBeanProperty.java
(rev 0)
+++
extensions/trunk/src/main/java/org/jboss/weld/extensions/util/AbstractBeanProperty.java 2010-05-31
02:50:10 UTC (rev 6361)
@@ -0,0 +1,356 @@
+package org.jboss.weld.extensions.util;
+
+import java.beans.Introspector;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+
+/**
+ * Base class for bean property wrappers
+ *
+ * @author Shane Bryzak
+ */
+public abstract class AbstractBeanProperty
+{
+ /**
+ * Property field
+ */
+ private Field propertyField;
+
+ /**
+ * Property getter method
+ */
+ private Method propertyGetter;
+
+ /**
+ * Property setter method
+ */
+ private Method propertySetter;
+
+ /**
+ * Property name
+ */
+ private String name;
+
+ /**
+ * The class containing the property
+ */
+ private Class<?> targetClass;
+
+ /**
+ * Flag indicating whether the target class has been scanned
+ */
+ private boolean scanned = false;
+
+ private boolean isFieldProperty;
+
+ /**
+ * Flag indicating whether a valid property has been found
+ */
+ private boolean valid = false;
+
+ private Type propertyType;
+
+ /**
+ *
+ * @param targetClass
+ */
+ public AbstractBeanProperty(Class<?> targetClass)
+ {
+ this.targetClass = targetClass;
+ }
+
+ protected void scan()
+ {
+ if (scanned) return;
+
+ scanned = true;
+
+ // First check declared fields
+ for (Field f : targetClass.getDeclaredFields())
+ {
+ if (fieldMatches(f))
+ {
+ setupFieldProperty(f);
+ valid = true;
+ return;
+ }
+ }
+
+ // Then check public fields, in case it's inherited
+ for (Field f : targetClass.getFields())
+ {
+ if (fieldMatches(f))
+ {
+ setupFieldProperty(f);
+ valid = true;
+ return;
+ }
+ }
+
+ // Then check public methods (we ignore private methods)
+ for (Method m : targetClass.getMethods())
+ {
+ if (methodMatches(m))
+ {
+ String methodName = m.getName();
+
+ if ( m.getName().startsWith("get") )
+ {
+ this.name = Introspector.decapitalize( m.getName().substring(3) );
+ }
+ else if ( methodName.startsWith("is") )
+ {
+ this.name = Introspector.decapitalize( m.getName().substring(2) );
+ }
+
+ if (this.name != null)
+ {
+ this.propertyGetter = getGetterMethod(targetClass, this.name);
+ this.propertySetter = getSetterMethod(targetClass, this.name);
+ this.propertyType = this.propertyGetter.getGenericReturnType();
+ isFieldProperty = false;
+ valid = true;
+ }
+ else
+ {
+ throw new IllegalStateException("Invalid accessor method, must start
with 'get' or 'is'. " +
+ "Method: " + m + " in class: " + targetClass);
+ }
+ }
+ }
+ }
+
+ protected abstract boolean fieldMatches(Field f);
+
+ protected abstract boolean methodMatches(Method m);
+
+ private void setupFieldProperty(Field propertyField)
+ {
+ this.propertyField = propertyField;
+ isFieldProperty = true;
+ this.name = propertyField.getName();
+ this.propertyType = propertyField.getGenericType();
+ }
+
+ /**
+ * This method sets the property value for a specified bean to the specified
+ * value. The property to be set is either a field or setter method that
+ * matches the specified annotation class and returns true for the isMatch()
+ * method.
+ *
+ * @param bean The bean containing the property to set
+ * @param value The new property value
+ * @throws Exception
+ */
+ public void setValue(Object bean, Object value) throws Exception
+ {
+ scan();
+
+ if (isFieldProperty)
+ {
+ setFieldValue(propertyField, bean, value);
+ }
+ else
+ {
+ invokeMethod(propertySetter, bean, value);
+ }
+ }
+
+ /**
+ * Returns the property value for the specified bean. The property to be
+ * returned is either a field or getter method that matches the specified
+ * annotation class and returns true for the isMatch() method.
+ *
+ * @param bean The bean to read the property from
+ * @return The property value
+ * @throws Exception
+ */
+ public Object getValue(Object bean) throws Exception
+ {
+ scan();
+
+ if (isFieldProperty)
+ {
+ return getFieldValue(propertyField, bean);
+ }
+ else
+ {
+ return invokeMethod(propertyGetter, bean);
+ }
+ }
+
+ /**
+ * Returns the property type
+ *
+ * @return The property type
+ */
+ public Type getPropertyType()
+ {
+ scan();
+ return propertyType;
+ }
+
+ /**
+ * Returns true if the property has been successfully located, otherwise
+ * returns false.
+ *
+ * @return
+ */
+ public boolean isValid()
+ {
+ scan();
+ return valid;
+ }
+
+ /**
+ * Returns the name of the property. If the property is a field, then the
+ * field name is returned. Otherwise, if the property is a method, then the
+ * name that is returned is the getter method name without the "get" or
"is"
+ * prefix, and a lower case first letter.
+ *
+ * @return The name of the property
+ */
+ public String getName()
+ {
+ scan();
+ return name;
+ }
+
+ private Object getFieldValue(Field field, Object obj)
+ {
+ field.setAccessible(true);
+ try
+ {
+ return field.get(obj);
+ }
+ catch (IllegalAccessException e)
+ {
+ throw new RuntimeException(buildGetFieldValueErrorMessage(field, obj), e);
+ }
+ catch (NullPointerException ex)
+ {
+ NullPointerException ex2 = new NullPointerException(
+ buildGetFieldValueErrorMessage(field, obj));
+ ex2.initCause(ex.getCause());
+ throw ex2;
+ }
+ }
+
+ private String buildGetFieldValueErrorMessage(Field field, Object obj)
+ {
+ return String.format("Exception reading [%s] field from object [%s].",
+ field.getName(), obj);
+ }
+
+ private void setFieldValue(Field field, Object obj, Object value)
+ {
+ field.setAccessible(true);
+ try
+ {
+ field.set(obj, value);
+ }
+ catch (IllegalAccessException e)
+ {
+ throw new RuntimeException(buildSetFieldValueErrorMessage(field, obj, value),
e);
+ }
+ catch (NullPointerException ex)
+ {
+ NullPointerException ex2 = new NullPointerException(
+ buildSetFieldValueErrorMessage(field, obj, value));
+ ex2.initCause(ex.getCause());
+ throw ex2;
+ }
+ }
+
+ private String buildSetFieldValueErrorMessage(Field field, Object obj, Object value)
+ {
+ return String.format("Exception setting [%s] field on object [%s] to value
[%s]",
+ field.getName(), obj, value);
+ }
+
+ private Object invokeMethod(Method method, Object obj, Object... args)
+ {
+ try
+ {
+ return method.invoke(obj, args);
+ }
+ catch (IllegalAccessException ex)
+ {
+ throw new RuntimeException(buildInvokeMethodErrorMessage(method, obj, args),
ex);
+ }
+ catch (IllegalArgumentException ex)
+ {
+ throw new IllegalArgumentException(buildInvokeMethodErrorMessage(method, obj,
args), ex.getCause());
+ }
+ catch (InvocationTargetException ex)
+ {
+ throw new RuntimeException(buildInvokeMethodErrorMessage(method, obj, args),
ex);
+ }
+ catch (NullPointerException ex)
+ {
+ NullPointerException ex2 = new
NullPointerException(buildInvokeMethodErrorMessage(method, obj, args));
+ ex2.initCause(ex.getCause());
+ throw ex2;
+ }
+ catch (ExceptionInInitializerError e)
+ {
+ throw new RuntimeException(buildInvokeMethodErrorMessage(method, obj, args),
e);
+ }
+ }
+
+ private String buildInvokeMethodErrorMessage(Method method, Object obj, Object...
args)
+ {
+ StringBuilder message = new StringBuilder(String.format(
+ "Exception invoking method [%s] on object [%s], using arguments
[",
+ method.getName(), obj));
+ if (args != null) for (int i = 0; i < args.length; i++) message.append((i > 0
? "," : "") + args[i]);
+ message.append("]");
+ return message.toString();
+ }
+
+ private Method getSetterMethod(Class<?> clazz, String name)
+ {
+ Method[] methods = clazz.getMethods();
+ for (Method method: methods)
+ {
+ String methodName = method.getName();
+ if ( methodName.startsWith("set") &&
method.getParameterTypes().length==1 )
+ {
+ if ( Introspector.decapitalize( methodName.substring(3) ).equals(name) )
+ {
+ return method;
+ }
+ }
+ }
+ throw new IllegalArgumentException("no such setter method: " +
clazz.getName() + '.' + name);
+ }
+
+ private Method getGetterMethod(Class<?> clazz, String name)
+ {
+ Method[] methods = clazz.getMethods();
+ for (Method method: methods)
+ {
+ String methodName = method.getName();
+ if ( method.getParameterTypes().length==0 )
+ {
+ if ( methodName.startsWith("get") )
+ {
+ if ( Introspector.decapitalize( methodName.substring(3) ).equals(name) )
+ {
+ return method;
+ }
+ }
+ else if ( methodName.startsWith("is") )
+ {
+ if ( Introspector.decapitalize( methodName.substring(2) ).equals(name) )
+ {
+ return method;
+ }
+ }
+ }
+ }
+ throw new IllegalArgumentException("no such getter method: " +
clazz.getName() + '.' + name);
+ }
+}
Modified:
extensions/trunk/src/main/java/org/jboss/weld/extensions/util/AnnotatedBeanProperty.java
===================================================================
---
extensions/trunk/src/main/java/org/jboss/weld/extensions/util/AnnotatedBeanProperty.java 2010-05-30
11:54:06 UTC (rev 6360)
+++
extensions/trunk/src/main/java/org/jboss/weld/extensions/util/AnnotatedBeanProperty.java 2010-05-31
02:50:10 UTC (rev 6361)
@@ -1,36 +1,22 @@
package org.jboss.weld.extensions.util;
-import java.beans.Introspector;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
-import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
-import java.lang.reflect.Type;
-import java.lang.ExceptionInInitializerError;
/**
- * A convenience class for working with an annotated property (either a field or method)
of
- * a JavaBean class. By providing an isMatch() method in a concrete implementation
- * of this class, annotations may be matched on their attribute values or other
- * conditions.
+ * A convenience class for working with an annotated property (either a field or
+ * method) of a JavaBean class. By providing an overridden annotationMatches()
+ * method in a subclass, annotations may be matched on their attribute values or
+ * other conditions.
*
* @author Shane Bryzak
*/
-public class AnnotatedBeanProperty<T extends Annotation>
-{
- private Field propertyField;
- private Method propertyGetter;
- private Method propertySetter;
- private String name;
- private Type propertyType;
+public class AnnotatedBeanProperty<T extends Annotation> extends
AbstractBeanProperty
+{
private T annotation;
- private boolean isFieldProperty;
- private boolean set = false;
-
- private Class<?> targetClass;
private Class<T> annotationClass;
- private boolean scanned = false;
/**
* Default constructor
@@ -41,150 +27,49 @@
*/
public AnnotatedBeanProperty(Class<?> cls, Class<T> annotationClass)
{
- this.targetClass = cls;
+ super(cls);
this.annotationClass = annotationClass;
}
- /**
- * Scans the target class to locate the annotated property
- */
- private void scan()
+ protected boolean fieldMatches(Field f)
{
- // First check declared fields
- for (Field f : targetClass.getDeclaredFields())
- {
- if (f.isAnnotationPresent(annotationClass) &&
- isMatch(f.getAnnotation(annotationClass)))
- {
- setupFieldProperty(f);
- this.annotation = f.getAnnotation(annotationClass);
- set = true;
- return;
- }
- }
-
- // Then check public fields, in case it's inherited
- for (Field f : targetClass.getFields())
- {
- if (f.isAnnotationPresent(annotationClass) &&
- isMatch(f.getAnnotation(annotationClass)))
- {
- this.annotation = f.getAnnotation(annotationClass);
- setupFieldProperty(f);
- set = true;
- return;
- }
+ if (f.isAnnotationPresent(annotationClass) &&
+ annotationMatches(f.getAnnotation(annotationClass)))
+ {
+ this.annotation = f.getAnnotation(annotationClass);
+ return true;
}
-
- // Then check public methods (we ignore private methods)
- for (Method m : targetClass.getMethods())
- {
- if (m.isAnnotationPresent(annotationClass) &&
- isMatch(m.getAnnotation(annotationClass)))
- {
- this.annotation = m.getAnnotation(annotationClass);
- String methodName = m.getName();
-
- if ( m.getName().startsWith("get") )
- {
- this.name = Introspector.decapitalize( m.getName().substring(3) );
- }
- else if ( methodName.startsWith("is") )
- {
- this.name = Introspector.decapitalize( m.getName().substring(2) );
- }
-
- if (this.name != null)
- {
- this.propertyGetter = getGetterMethod(targetClass, this.name);
- this.propertySetter = getSetterMethod(targetClass, this.name);
- this.propertyType = this.propertyGetter.getGenericReturnType();
- isFieldProperty = false;
- set = true;
- }
- else
- {
- throw new IllegalStateException("Invalid accessor method, must start
with 'get' or 'is'. " +
- "Method: " + m + " in class: " + targetClass);
- }
- }
- }
-
- scanned = true;
- }
-
- /**
- * This method may be overridden by a subclass. It can be used to scan
- * for an annotation with particular attribute values, or to allow a match
- * based on more complex logic.
- *
- * @param annotation The potential match
- * @return true if the specified annotation is a match
- */
- protected boolean isMatch(T annotation)
- {
- return true;
- }
-
- /**
- * This method sets the property value for a specified bean to the specified
- * value. The property to be set is either a field or setter method that
- * matches the specified annotation class and returns true for the isMatch()
- * method.
- *
- * @param bean The bean containing the property to set
- * @param value The new property value
- * @throws Exception
- */
- public void setValue(Object bean, Object value) throws Exception
- {
- if (!scanned) scan();
-
- if (isFieldProperty)
- {
- setFieldValue(propertyField, bean, value);
- }
else
{
- invokeMethod(propertySetter, bean, value);
+ return false;
}
}
-
- /**
- * Returns the property value for the specified bean. The property to be
- * returned is either a field or getter method that matches the specified
- * annotation class and returns true for the isMatch() method.
- *
- * @param bean The bean to read the property from
- * @return The property value
- * @throws Exception
- */
- public Object getValue(Object bean) throws Exception
+
+ protected boolean methodMatches(Method m)
{
- if (!scanned) scan();
-
- if (isFieldProperty)
+ if (m.isAnnotationPresent(annotationClass) &&
+ annotationMatches(m.getAnnotation(annotationClass)))
{
- return getFieldValue(propertyField, bean);
+ this.annotation = m.getAnnotation(annotationClass);
+ return true;
}
else
{
- return invokeMethod(propertyGetter, bean);
+ return false;
}
}
/**
- * Returns the name of the property. If the property is a field, then the
- * field name is returned. Otherwise, if the property is a method, then the
- * name that is returned is the getter method name without the "get" or
"is"
- * prefix, and a lower case first letter.
+ * This method may be overridden by a subclass. It can be used to scan
+ * for an annotation with particular attribute values, or to allow a match
+ * based on more complex logic.
*
- * @return The name of the property
+ * @param annotation The potential match
+ * @return true if the specified annotation is a match
*/
- public String getName()
+ protected boolean annotationMatches(T annotation)
{
- if (!scanned) scan();
- return name;
+ return true;
}
/**
@@ -194,174 +79,11 @@
*/
public T getAnnotation()
{
- if (!scanned) scan();
+ scan();
return annotation;
}
+
+
- /**
- * Returns the property type
- *
- * @return The property type
- */
- public Type getPropertyType()
- {
- if (!scanned) scan();
- return propertyType;
- }
-
- /**
- * Returns true if the property has been successfully located, otherwise
- * returns false.
- *
- * @return
- */
- public boolean isSet()
- {
- if (!scanned) scan();
- return set;
- }
-
- private void setupFieldProperty(Field propertyField)
- {
- this.propertyField = propertyField;
- isFieldProperty = true;
- this.name = propertyField.getName();
- this.propertyType = propertyField.getGenericType();
- }
-
- private Object getFieldValue(Field field, Object obj)
- {
- field.setAccessible(true);
- try
- {
- return field.get(obj);
- }
- catch (IllegalAccessException e)
- {
- throw new RuntimeException(buildGetFieldValueErrorMessage(field, obj), e);
- }
- catch (NullPointerException ex)
- {
- NullPointerException ex2 = new NullPointerException(
- buildGetFieldValueErrorMessage(field, obj));
- ex2.initCause(ex.getCause());
- throw ex2;
- }
- }
-
- private String buildGetFieldValueErrorMessage(Field field, Object obj)
- {
- return String.format("Exception reading [%s] field from object [%s].",
- field.getName(), obj);
- }
-
- private void setFieldValue(Field field, Object obj, Object value)
- {
- field.setAccessible(true);
- try
- {
- field.set(obj, value);
- }
- catch (IllegalAccessException e)
- {
- throw new RuntimeException(buildSetFieldValueErrorMessage(field, obj, value),
e);
- }
- catch (NullPointerException ex)
- {
- NullPointerException ex2 = new NullPointerException(
- buildSetFieldValueErrorMessage(field, obj, value));
- ex2.initCause(ex.getCause());
- throw ex2;
- }
- }
-
- private String buildSetFieldValueErrorMessage(Field field, Object obj, Object value)
- {
- return String.format("Exception setting [%s] field on object [%s] to value
[%s]",
- field.getName(), obj, value);
- }
-
- private Object invokeMethod(Method method, Object obj, Object... args)
- {
- try
- {
- return method.invoke(obj, args);
- }
- catch (IllegalAccessException ex)
- {
- throw new RuntimeException(buildInvokeMethodErrorMessage(method, obj, args),
ex);
- }
- catch (IllegalArgumentException ex)
- {
- throw new IllegalArgumentException(buildInvokeMethodErrorMessage(method, obj,
args), ex.getCause());
- }
- catch (InvocationTargetException ex)
- {
- throw new RuntimeException(buildInvokeMethodErrorMessage(method, obj, args),
ex);
- }
- catch (NullPointerException ex)
- {
- NullPointerException ex2 = new
NullPointerException(buildInvokeMethodErrorMessage(method, obj, args));
- ex2.initCause(ex.getCause());
- throw ex2;
- }
- catch (ExceptionInInitializerError e)
- {
- throw new RuntimeException(buildInvokeMethodErrorMessage(method, obj, args),
e);
- }
- }
-
- private String buildInvokeMethodErrorMessage(Method method, Object obj, Object...
args)
- {
- StringBuilder message = new StringBuilder(String.format(
- "Exception invoking method [%s] on object [%s], using arguments
[",
- method.getName(), obj));
- if (args != null) for (int i = 0; i < args.length; i++) message.append((i > 0
? "," : "") + args[i]);
- message.append("]");
- return message.toString();
- }
-
- private Method getSetterMethod(Class<?> clazz, String name)
- {
- Method[] methods = clazz.getMethods();
- for (Method method: methods)
- {
- String methodName = method.getName();
- if ( methodName.startsWith("set") &&
method.getParameterTypes().length==1 )
- {
- if ( Introspector.decapitalize( methodName.substring(3) ).equals(name) )
- {
- return method;
- }
- }
- }
- throw new IllegalArgumentException("no such setter method: " +
clazz.getName() + '.' + name);
- }
-
- private Method getGetterMethod(Class<?> clazz, String name)
- {
- Method[] methods = clazz.getMethods();
- for (Method method: methods)
- {
- String methodName = method.getName();
- if ( method.getParameterTypes().length==0 )
- {
- if ( methodName.startsWith("get") )
- {
- if ( Introspector.decapitalize( methodName.substring(3) ).equals(name) )
- {
- return method;
- }
- }
- else if ( methodName.startsWith("is") )
- {
- if ( Introspector.decapitalize( methodName.substring(2) ).equals(name) )
- {
- return method;
- }
- }
- }
- }
- throw new IllegalArgumentException("no such getter method: " +
clazz.getName() + '.' + name);
- }
+
}
\ No newline at end of file
Added:
extensions/trunk/src/main/java/org/jboss/weld/extensions/util/TypedBeanProperty.java
===================================================================
--- extensions/trunk/src/main/java/org/jboss/weld/extensions/util/TypedBeanProperty.java
(rev 0)
+++
extensions/trunk/src/main/java/org/jboss/weld/extensions/util/TypedBeanProperty.java 2010-05-31
02:50:10 UTC (rev 6361)
@@ -0,0 +1,39 @@
+package org.jboss.weld.extensions.util;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+/**
+ * A convenience class for working with a typed property (either a field or
+ * method) of a JavaBean class.
+ *
+ * @author Shane Bryzak
+ */
+public class TypedBeanProperty<T> extends AbstractBeanProperty
+{
+ private Class<?> propertyClass;
+
+ public TypedBeanProperty(Class<?> cls, Class<?> propertyClass)
+ {
+ super(cls);
+
+ if (propertyClass == null)
+ {
+ throw new IllegalArgumentException("propertyClass can not be null.");
+ }
+
+ this.propertyClass = propertyClass;
+ }
+
+ @Override
+ protected boolean fieldMatches(Field f)
+ {
+ return propertyClass.equals(f.getType());
+ }
+
+ @Override
+ protected boolean methodMatches(Method m)
+ {
+ return propertyClass.equals(m.getReturnType());
+ }
+}