Author: rwhitcomb
Date: Fri Mar 19 23:16:18 2021
New Revision: 1887831
URL: http://svn.apache.org/viewvc?rev=1887831&view=rev
Log:
PIVOT-1056: Some code cleanup and (maybe) optimization in BeanAdapter.
Modified:
pivot/trunk/core/src/org/apache/pivot/beans/BeanAdapter.java
Modified: pivot/trunk/core/src/org/apache/pivot/beans/BeanAdapter.java
URL:
http://svn.apache.org/viewvc/pivot/trunk/core/src/org/apache/pivot/beans/BeanAdapter.java?rev=1887831&r1=1887830&r2=1887831&view=diff
==============================================================================
--- pivot/trunk/core/src/org/apache/pivot/beans/BeanAdapter.java (original)
+++ pivot/trunk/core/src/org/apache/pivot/beans/BeanAdapter.java Fri Mar 19
23:16:18 2021
@@ -51,27 +51,29 @@ import org.apache.pivot.util.Utils;
*/
public class BeanAdapter implements Map<String, Object> {
/**
- * Property iterator. Returns a value for each getter method and public,
+ * Property iterator. Returns a property name for each getter method and
public,
* non-final field defined by the bean.
*/
private class PropertyIterator implements Iterator<String> {
private Method[] methods = null;
private Field[] fields = null;
- private int i = 0;
- private int j = 0;
- private String nextProperty = null;
-
- public PropertyIterator() {
- Class<?> type = bean.getClass();
- methods = type.getMethods();
- fields = type.getFields();
+ private int methodIndex = 0;
+ private int fieldIndex = 0;
+ private String nextPropertyName = null;
+
+ /**
+ * Construct the property iterator over our bean object.
+ */
+ PropertyIterator() {
+ methods = beanClass.getMethods();
+ fields = beanClass.getFields();
nextProperty();
}
@Override
public boolean hasNext() {
- return (nextProperty != null);
+ return (nextPropertyName != null);
}
@Override
@@ -80,17 +82,20 @@ public class BeanAdapter implements Map<
throw new NoSuchElementException();
}
- String nextPropertyLocal = this.nextProperty;
+ String nameToReturn = nextPropertyName;
nextProperty();
- return nextPropertyLocal;
+ return nameToReturn;
}
+ /**
+ * Iterate to the next acceptable property method/field in the bean
object.
+ */
private void nextProperty() {
- nextProperty = null;
+ nextPropertyName = null;
- while (i < methods.length && nextProperty == null) {
- Method method = methods[i++];
+ while (methodIndex < methods.length && nextPropertyName == null) {
+ Method method = methods[methodIndex++];
if (method.getParameterTypes().length == 0
&& (method.getModifiers() & Modifier.STATIC) == 0) {
@@ -107,33 +112,27 @@ public class BeanAdapter implements Map<
if (prefix != null) {
int propertyOffset = prefix.length();
- nextProperty =
Character.toLowerCase(methodName.charAt(propertyOffset))
+ String propertyName =
Character.toLowerCase(methodName.charAt(propertyOffset))
+ methodName.substring(propertyOffset + 1);
- if (nextProperty.equals("class")) {
- nextProperty = null;
+ if (!propertyName.equals("class")) {
+ if (!ignoreReadOnlyProperties ||
!isReadOnly(propertyName)) {
+ nextPropertyName = propertyName;
+ }
}
}
-
- if (nextProperty != null && ignoreReadOnlyProperties
- && isReadOnly(nextProperty)) {
- nextProperty = null;
- }
}
}
- if (nextProperty == null) {
- while (j < fields.length && nextProperty == null) {
- Field field = fields[j++];
+ if (nextPropertyName == null) {
+ while (fieldIndex < fields.length && nextPropertyName == null)
{
+ Field field = fields[fieldIndex++];
int modifiers = field.getModifiers();
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers &
Modifier.STATIC) == 0) {
- nextProperty = field.getName();
- }
-
- if (nextProperty != null && ignoreReadOnlyProperties
- && (modifiers & Modifier.FINAL) != 0) {
- nextProperty = null;
+ if (!ignoreReadOnlyProperties || (modifiers &
Modifier.FINAL) == 0) {
+ nextPropertyName = field.getName();
+ }
}
}
}
@@ -146,8 +145,10 @@ public class BeanAdapter implements Map<
}
}
- private Object bean;
- private boolean ignoreReadOnlyProperties;
+ private final Object bean;
+ private final Class<?> beanClass;
+ private final String beanClassName;
+ private final boolean ignoreReadOnlyProperties;
private MapListener.Listeners<String, Object> mapListeners = new
MapListener.Listeners<>();
@@ -165,10 +166,10 @@ public class BeanAdapter implements Map<
/**
* Creates a new bean dictionary.
*
- * @param bean The bean object to wrap.
+ * @param beanObject The bean object to wrap.
*/
- public BeanAdapter(final Object bean) {
- this(bean, false);
+ public BeanAdapter(final Object beanObject) {
+ this(beanObject, false);
}
/**
@@ -176,15 +177,17 @@ public class BeanAdapter implements Map<
* straight fields marked as <code>final</code> or bean properties where
* there is a "get" method but no corresponding "set" method).
*
- * @param bean The bean object to wrap.
- * @param ignoreReadOnlyProperties {@code true} if {@code final} or
non-settable
+ * @param beanObject The bean object to wrap.
+ * @param ignoreReadOnlyValue {@code true} if {@code final} or non-settable
* fields should be excluded from the dictionary, {@code false} to include
all fields.
*/
- public BeanAdapter(final Object bean, final boolean
ignoreReadOnlyProperties) {
- Utils.checkNull(bean, "bean object");
+ public BeanAdapter(final Object beanObject, final boolean
ignoreReadOnlyValue) {
+ Utils.checkNull(beanObject, "bean object");
- this.bean = bean;
- this.ignoreReadOnlyProperties = ignoreReadOnlyProperties;
+ bean = beanObject;
+ beanClass = bean.getClass();
+ beanClassName = beanClass.getName();
+ ignoreReadOnlyProperties = ignoreReadOnlyValue;
}
/**
@@ -209,17 +212,17 @@ public class BeanAdapter implements Map<
Object value = null;
- Method getterMethod = getGetterMethod(key);
+ Method getterMethod = getGetterMethod(beanClass, key);
if (getterMethod == null) {
- Field field = getField(key);
+ Field field = getField(beanClass, key);
if (field != null) {
try {
value = field.get(bean);
} catch (IllegalAccessException exception) {
throw new RuntimeException(String.format(
- ILLEGAL_ACCESS_EXCEPTION_MESSAGE_FORMAT, key,
bean.getClass().getName()),
+ ILLEGAL_ACCESS_EXCEPTION_MESSAGE_FORMAT, key,
beanClassName),
exception);
}
}
@@ -228,10 +231,10 @@ public class BeanAdapter implements Map<
value = getterMethod.invoke(bean, new Object[] {});
} catch (IllegalAccessException exception) {
throw new
RuntimeException(String.format(ILLEGAL_ACCESS_EXCEPTION_MESSAGE_FORMAT,
- key, bean.getClass().getName()), exception);
+ key, beanClassName), exception);
} catch (InvocationTargetException exception) {
throw new RuntimeException(String.format(
- "Error getting property \"%s\" for type %s.", key,
bean.getClass().getName()),
+ "Error getting property \"%s\" for type %s.", key,
beanClassName),
exception.getCause());
}
}
@@ -261,7 +264,7 @@ public class BeanAdapter implements Map<
if (valueUpdated != null) {
// Get the setter method for the value type
- setterMethod = getSetterMethod(key, valueUpdated.getClass());
+ setterMethod = getSetterMethod(beanClass, key,
valueUpdated.getClass());
}
if (setterMethod == null) {
@@ -269,18 +272,18 @@ public class BeanAdapter implements Map<
Class<?> propertyType = getType(key);
if (propertyType != null) {
- setterMethod = getSetterMethod(key, propertyType);
+ setterMethod = getSetterMethod(beanClass, key, propertyType);
valueUpdated = coerce(valueUpdated, propertyType, key);
}
}
if (setterMethod == null) {
- Field field = getField(key);
+ Field field = getField(beanClass, key);
if (field == null) {
throw new PropertyNotFoundException("Property \"" + key + "\""
+ " does not exist or is read-only for type "
- + bean.getClass().getName() + ".");
+ + beanClassName + ".");
}
Class<?> fieldType = field.getType();
@@ -295,18 +298,18 @@ public class BeanAdapter implements Map<
field.set(bean, valueUpdated);
} catch (IllegalAccessException exception) {
throw new
RuntimeException(String.format(ILLEGAL_ACCESS_EXCEPTION_MESSAGE_FORMAT,
- key, bean.getClass().getName()), exception);
+ key, beanClassName), exception);
}
} else {
try {
setterMethod.invoke(bean, new Object[] {valueUpdated});
} catch (IllegalAccessException exception) {
throw new
RuntimeException(String.format(ILLEGAL_ACCESS_EXCEPTION_MESSAGE_FORMAT,
- key, bean.getClass().getName()), exception);
+ key, beanClassName), exception);
} catch (InvocationTargetException exception) {
throw new RuntimeException(String.format(
"Error setting property \"%s\" for type %s to value
\"%s\"", key,
- bean.getClass().getName(), "" + valueUpdated),
exception.getCause());
+ beanClassName, "" + valueUpdated), exception.getCause());
}
}
@@ -376,10 +379,10 @@ public class BeanAdapter implements Map<
public boolean containsKey(final String key) {
Utils.checkNullOrEmpty(key, "key");
- boolean containsKey = (getGetterMethod(key) != null);
+ boolean containsKey = (getGetterMethod(beanClass, key) != null);
if (!containsKey) {
- containsKey = (getField(key) != null);
+ containsKey = (getField(beanClass, key) != null);
}
return containsKey;
@@ -425,7 +428,7 @@ public class BeanAdapter implements Map<
* otherwise.
*/
public boolean isReadOnly(final String key) {
- return isReadOnly(bean.getClass(), key);
+ return isReadOnly(beanClass, key);
}
/**
@@ -436,7 +439,7 @@ public class BeanAdapter implements Map<
* @see #getType(Class, String)
*/
public Class<?> getType(final String key) {
- return getType(bean.getClass(), key);
+ return getType(beanClass, key);
}
/**
@@ -447,7 +450,7 @@ public class BeanAdapter implements Map<
* @see #getGenericType(Class, String)
*/
public Type getGenericType(final String key) {
- return getGenericType(bean.getClass(), key);
+ return getGenericType(beanClass, key);
}
/**
@@ -466,39 +469,6 @@ public class BeanAdapter implements Map<
}
/**
- * Returns the getter method for a property.
- *
- * @param key The property name.
- * @return The getter method, or {@code null} if the method does not exist.
- */
- private Method getGetterMethod(final String key) {
- return getGetterMethod(bean.getClass(), key);
- }
-
- /**
- * Returns the setter method for a property.
- *
- * @param key The property name.
- * @param valueType The value type of the property in question.
- * @return The getter method, or {@code null} if the method does not exist.
- */
- private Method getSetterMethod(final String key, final Class<?> valueType)
{
- return getSetterMethod(bean.getClass(), key, valueType);
- }
-
- /**
- * Returns the public, non-static field for a property. Note that fields
- * will only be consulted for bean properties after bean methods.
- *
- * @param key The property name
- * @return The field, or {@code null} if the field does not exist, or is
- * non-public or static
- */
- private Field getField(final String key) {
- return getField(bean.getClass(), key);
- }
-
- /**
* Tests the read-only state of a property. Note that if no such property
* exists, this method will return {@code true} (it will <u>not</u> throw
* an exception).
@@ -652,12 +622,72 @@ public class BeanAdapter implements Map<
}
/**
+ * Simplified version of {@link #getSetterMethod(Class, String, Class)}
that
+ * doesn't do the null checks, or have to redo the method name calculation.
+ *
+ * @param beanClass The bean class.
+ * @param methodName The setter method name we are looking for.
+ * @param valueType The type of the property value.
+ * @return The setter method, or {@code null} if the method cannot be
found.
+ */
+ private static Method internalGetSetterMethod(final Class<?> beanClass,
final String methodName,
+ final Class<?> valueType) {
+ Method setterMethod = null;
+
+ try {
+ setterMethod = beanClass.getMethod(methodName, valueType);
+ } catch (NoSuchMethodException exception) {
+ // No-op
+ }
+
+ if (setterMethod == null) {
+ // Look for a match on the value's super type
+ Class<?> superType = valueType.getSuperclass();
+ setterMethod = internalGetSetterMethod(beanClass, methodName,
superType);
+ }
+
+ if (setterMethod == null) {
+ // If value type is a primitive wrapper, look for a method
+ // signature with the corresponding primitive type
+ try {
+ Field primitiveTypeField = valueType.getField("TYPE");
+ Class<?> primitiveValueType = (Class<?>)
primitiveTypeField.get(null);
+
+ try {
+ setterMethod = beanClass.getMethod(methodName,
primitiveValueType);
+ } catch (NoSuchMethodException exception) {
+ // No-op
+ }
+ } catch (NoSuchFieldException exception) {
+ // No-op
+ } catch (IllegalAccessException exception) {
+ throw new RuntimeException(String.format(
+ ILLEGAL_ACCESS_EXCEPTION_MESSAGE_FORMAT, methodName,
beanClass.getName()),
+ exception);
+ }
+ }
+
+ if (setterMethod == null) {
+ // Walk the interface graph to find a matching method
+ Class<?>[] interfaces = valueType.getInterfaces();
+
+ int i = 0, n = interfaces.length;
+ while (setterMethod == null && i < n) {
+ Class<?> interfaceType = interfaces[i++];
+ setterMethod = internalGetSetterMethod(beanClass, methodName,
interfaceType);
+ }
+ }
+
+ return setterMethod;
+ }
+
+ /**
* Returns the setter method for a property.
*
* @param beanClass The bean class.
* @param key The property name.
* @param valueType The type of the property.
- * @return The getter method, or {@code null} if the method does not exist.
+ * @return The setter method, or {@code null} if the method does not exist.
*/
public static Method getSetterMethod(final Class<?> beanClass, final
String key,
final Class<?> valueType) {
@@ -672,49 +702,7 @@ public class BeanAdapter implements Map<
String keyUpdated = Character.toUpperCase(key.charAt(0)) +
key.substring(1);
final String methodName = SET_PREFIX + keyUpdated;
- try {
- setterMethod = beanClass.getMethod(methodName, valueType);
- } catch (NoSuchMethodException exception) {
- // No-op
- }
-
- if (setterMethod == null) {
- // Look for a match on the value's super type
- Class<?> superType = valueType.getSuperclass();
- setterMethod = getSetterMethod(beanClass, key, superType);
- }
-
- if (setterMethod == null) {
- // If value type is a primitive wrapper, look for a method
- // signature with the corresponding primitive type
- try {
- Field primitiveTypeField = valueType.getField("TYPE");
- Class<?> primitiveValueType = (Class<?>)
primitiveTypeField.get(null);
-
- try {
- setterMethod = beanClass.getMethod(methodName,
primitiveValueType);
- } catch (NoSuchMethodException exception) {
- // No-op
- }
- } catch (NoSuchFieldException exception) {
- // No-op
- } catch (IllegalAccessException exception) {
- throw new RuntimeException(String.format(
- ILLEGAL_ACCESS_EXCEPTION_MESSAGE_FORMAT, keyUpdated,
beanClass.getName()),
- exception);
- }
- }
-
- if (setterMethod == null) {
- // Walk the interface graph to find a matching method
- Class<?>[] interfaces = valueType.getInterfaces();
-
- int i = 0, n = interfaces.length;
- while (setterMethod == null && i < n) {
- Class<?> interfaceType = interfaces[i++];
- setterMethod = getSetterMethod(beanClass, key,
interfaceType);
- }
- }
+ setterMethod = internalGetSetterMethod(beanClass, methodName,
valueType);
}
return setterMethod;
@@ -739,85 +727,72 @@ public class BeanAdapter implements Map<
if (value == null) {
// Null values can only be coerced to null
coercedValue = null;
- } else {
- if (type.isAssignableFrom(value.getClass())) {
- // Value doesn't need coercion
- coercedValue = value;
- } else if (type.isEnum()) {
- // Find and invoke the valueOf(String) method using an upper
- // case conversion of the supplied Object's toString() value
- try {
- String valueString =
value.toString().toUpperCase(Locale.ENGLISH);
- Method valueOfMethod =
type.getMethod(ENUM_VALUE_OF_METHOD_NAME, String.class);
- coercedValue = valueOfMethod.invoke(null, valueString);
- } catch (IllegalAccessException | InvocationTargetException
- | SecurityException | NoSuchMethodException e) {
- // Nothing to be gained by handling the getMethod() &
invoke() exceptions separately
- throw new IllegalArgumentException(String.format(
- ENUM_COERCION_EXCEPTION_MESSAGE,
value.getClass().getName(), value, type,
- Arrays.toString(type.getEnumConstants())), e);
- }
+ } else if (type == Object.class ||
type.isAssignableFrom(value.getClass())) {
+ // Value doesn't need coercion
+ coercedValue = value;
+ } else if (type.isEnum()) {
+ // Find and invoke the valueOf(String) method using an upper
+ // case conversion of the supplied Object's toString() value
+ try {
+ String valueString =
value.toString().toUpperCase(Locale.ENGLISH);
+ Method valueOfMethod =
type.getMethod(ENUM_VALUE_OF_METHOD_NAME, String.class);
+ coercedValue = valueOfMethod.invoke(null, valueString);
+ } catch (IllegalAccessException | InvocationTargetException
+ | SecurityException | NoSuchMethodException e) {
+ // Nothing to be gained by handling the getMethod() & invoke()
exceptions separately
+ throw new IllegalArgumentException(String.format(
+ ENUM_COERCION_EXCEPTION_MESSAGE,
value.getClass().getName(), value, type,
+ Arrays.toString(type.getEnumConstants())), e);
+ }
+ } else if (type == String.class) {
+ coercedValue = value.toString();
+ } else if (type == Boolean.class || type == Boolean.TYPE) {
+ coercedValue = Boolean.parseBoolean(value.toString());
+ } else if (type == Character.class || type == Character.TYPE) {
+ coercedValue = value.toString().charAt(0);
+ } else if (type == Byte.class || type == Byte.TYPE) {
+ if (value instanceof Number) {
+ coercedValue = ((Number) value).byteValue();
} else {
- // Coerce the value to the requested type
- if (type == String.class) {
- coercedValue = value.toString();
- } else if (type == Boolean.class || type == Boolean.TYPE) {
- coercedValue = Boolean.parseBoolean(value.toString());
- } else if (type == Character.class || type == Character.TYPE) {
- coercedValue = value.toString().charAt(0);
- } else if (type == Byte.class || type == Byte.TYPE) {
- if (value instanceof Number) {
- coercedValue = ((Number) value).byteValue();
- } else {
- coercedValue = Byte.parseByte(value.toString());
- }
- } else if (type == Short.class || type == Short.TYPE) {
- if (value instanceof Number) {
- coercedValue = ((Number) value).shortValue();
- } else {
- coercedValue = Short.parseShort(value.toString());
- }
- } else if (type == Integer.class || type == Integer.TYPE) {
- if (value instanceof Number) {
- coercedValue = ((Number) value).intValue();
- } else {
- coercedValue = Integer.parseInt(value.toString());
- }
- } else if (type == Long.class || type == Long.TYPE) {
- if (value instanceof Number) {
- coercedValue = ((Number) value).longValue();
- } else {
- coercedValue = Long.parseLong(value.toString());
- }
- } else if (type == Float.class || type == Float.TYPE) {
- if (value instanceof Number) {
- coercedValue = ((Number) value).floatValue();
- } else {
- coercedValue = Float.parseFloat(value.toString());
- }
- } else if (type == Double.class || type == Double.TYPE) {
- if (value instanceof Number) {
- coercedValue = ((Number) value).doubleValue();
- } else {
- coercedValue = Double.parseDouble(value.toString());
- }
- } else if (type == BigInteger.class) {
- if (value instanceof Number) {
- coercedValue = new BigInteger(((Number)
value).toString());
- } else {
- coercedValue = new BigInteger(value.toString());
- }
- } else if (type == BigDecimal.class) {
- if (value instanceof Number) {
- coercedValue = new BigDecimal(((Number)
value).toString());
- } else {
- coercedValue = new BigDecimal(value.toString());
- }
- } else {
- throw new IllegalArgumentException("Unable to coerce "
- + value.getClass().getName() + " to " + type + " for
\"" + key + "\" property.");
- }
+ coercedValue = Byte.parseByte(value.toString());
+ }
+ } else if (type == Short.class || type == Short.TYPE) {
+ if (value instanceof Number) {
+ coercedValue = ((Number) value).shortValue();
+ } else {
+ coercedValue = Short.parseShort(value.toString());
+ }
+ } else if (type == Integer.class || type == Integer.TYPE) {
+ if (value instanceof Number) {
+ coercedValue = ((Number) value).intValue();
+ } else {
+ coercedValue = Integer.parseInt(value.toString());
}
+ } else if (type == Long.class || type == Long.TYPE) {
+ if (value instanceof Number) {
+ coercedValue = ((Number) value).longValue();
+ } else {
+ coercedValue = Long.parseLong(value.toString());
+ }
+ } else if (type == Float.class || type == Float.TYPE) {
+ if (value instanceof Number) {
+ coercedValue = ((Number) value).floatValue();
+ } else {
+ coercedValue = Float.parseFloat(value.toString());
+ }
+ } else if (type == Double.class || type == Double.TYPE) {
+ if (value instanceof Number) {
+ coercedValue = ((Number) value).doubleValue();
+ } else {
+ coercedValue = Double.parseDouble(value.toString());
+ }
+ } else if (type == BigInteger.class) {
+ coercedValue = new BigInteger(value.toString());
+ } else if (type == BigDecimal.class) {
+ coercedValue = new BigDecimal(value.toString());
+ } else {
+ throw new IllegalArgumentException("Unable to coerce "
+ + value.getClass().getName() + " to " + type + " for \"" + key
+ "\" property.");
}
return (T) coercedValue;