ReflectivePropertypublic class ReflectiveProperty extends Property Internal class to automatically generate a Property for a given class/name pair, given the
specification of {@link Property#of(java.lang.Class, java.lang.Class, java.lang.String)} |
Fields Summary |
---|
private static final String | PREFIX_GET | private static final String | PREFIX_IS | private static final String | PREFIX_SET | private Method | mSetter | private Method | mGetter | private Field | mField |
Constructors Summary |
---|
public ReflectiveProperty(Class propertyHolder, Class valueType, String name)For given property name 'name', look for getName/isName method or 'name' field.
Also look for setName method (optional - could be readonly). Failing method getters and
field results in throwing NoSuchPropertyException.
// TODO: cache reflection info for each new class/name pair
super(valueType, name);
char firstLetter = Character.toUpperCase(name.charAt(0));
String theRest = name.substring(1);
String capitalizedName = firstLetter + theRest;
String getterName = PREFIX_GET + capitalizedName;
try {
mGetter = propertyHolder.getMethod(getterName, (Class<?>[])null);
} catch (NoSuchMethodException e) {
// getName() not available - try isName() instead
getterName = PREFIX_IS + capitalizedName;
try {
mGetter = propertyHolder.getMethod(getterName, (Class<?>[])null);
} catch (NoSuchMethodException e1) {
// Try public field instead
try {
mField = propertyHolder.getField(name);
Class fieldType = mField.getType();
if (!typesMatch(valueType, fieldType)) {
throw new NoSuchPropertyException("Underlying type (" + fieldType + ") " +
"does not match Property type (" + valueType + ")");
}
return;
} catch (NoSuchFieldException e2) {
// no way to access property - throw appropriate exception
throw new NoSuchPropertyException("No accessor method or field found for"
+ " property with name " + name);
}
}
}
Class getterType = mGetter.getReturnType();
// Check to make sure our getter type matches our valueType
if (!typesMatch(valueType, getterType)) {
throw new NoSuchPropertyException("Underlying type (" + getterType + ") " +
"does not match Property type (" + valueType + ")");
}
String setterName = PREFIX_SET + capitalizedName;
try {
mSetter = propertyHolder.getMethod(setterName, getterType);
} catch (NoSuchMethodException ignored) {
// Okay to not have a setter - just a readonly property
}
|
Methods Summary |
---|
public V | get(T object)
if (mGetter != null) {
try {
return (V) mGetter.invoke(object, (Object[])null);
} catch (IllegalAccessException e) {
throw new AssertionError();
} catch (InvocationTargetException e) {
throw new RuntimeException(e.getCause());
}
} else if (mField != null) {
try {
return (V) mField.get(object);
} catch (IllegalAccessException e) {
throw new AssertionError();
}
}
// Should not get here: there should always be a non-null getter or field
throw new AssertionError();
| public boolean | isReadOnly()Returns false if there is no setter or public field underlying this Property.
return (mSetter == null && mField == null);
| public void | set(T object, V value)
if (mSetter != null) {
try {
mSetter.invoke(object, value);
} catch (IllegalAccessException e) {
throw new AssertionError();
} catch (InvocationTargetException e) {
throw new RuntimeException(e.getCause());
}
} else if (mField != null) {
try {
mField.set(object, value);
} catch (IllegalAccessException e) {
throw new AssertionError();
}
} else {
throw new UnsupportedOperationException("Property " + getName() +" is read-only");
}
| private boolean | typesMatch(java.lang.Class valueType, java.lang.Class getterType)Utility method to check whether the type of the underlying field/method on the target
object matches the type of the Property. The extra checks for primitive types are because
generics will force the Property type to be a class, whereas the type of the underlying
method/field will probably be a primitive type instead. Accept float as matching Float,
etc.
if (getterType != valueType) {
if (getterType.isPrimitive()) {
return (getterType == float.class && valueType == Float.class) ||
(getterType == int.class && valueType == Integer.class) ||
(getterType == boolean.class && valueType == Boolean.class) ||
(getterType == long.class && valueType == Long.class) ||
(getterType == double.class && valueType == Double.class) ||
(getterType == short.class && valueType == Short.class) ||
(getterType == byte.class && valueType == Byte.class) ||
(getterType == char.class && valueType == Character.class);
}
return false;
}
return true;
|
|