*/
private transient Object[] setArgs, getArgs;
- /**
- * Is the MethodProperty read-only?
- */
- private boolean readOnly;
-
/**
* The getter and setter methods.
*/
out.writeObject(getArgs);
if (setMethod != null) {
out.writeObject(setMethod.getName());
- SerializerHelper.writeClass(out, setMethod.getDeclaringClass());
SerializerHelper
.writeClassArray(out, setMethod.getParameterTypes());
} else {
out.writeObject(null);
out.writeObject(null);
- out.writeObject(null);
}
if (getMethod != null) {
out.writeObject(getMethod.getName());
- SerializerHelper.writeClass(out, getMethod.getDeclaringClass());
SerializerHelper
.writeClassArray(out, getMethod.getParameterTypes());
} else {
out.writeObject(null);
out.writeObject(null);
- out.writeObject(null);
}
};
setArgs = (Object[]) in.readObject();
getArgs = (Object[]) in.readObject();
String name = (String) in.readObject();
- Class<T> setMethodClass = (Class<T>) SerializerHelper.readClass(in);
Class<?>[] paramTypes = SerializerHelper.readClassArray(in);
if (name != null) {
- setMethod = setMethodClass.getMethod(name, paramTypes);
+ setMethod = instance.getClass().getMethod(name, paramTypes);
} else {
setMethod = null;
}
name = (String) in.readObject();
- Class<T> getMethodClass = (Class<T>) SerializerHelper.readClass(in);
paramTypes = SerializerHelper.readClassArray(in);
if (name != null) {
- getMethod = getMethodClass.getMethod(name, paramTypes);
+ getMethod = instance.getClass().getMethod(name, paramTypes);
} else {
getMethod = null;
}
}
setArguments(new Object[] {}, new Object[] { null }, 0);
- readOnly = (setMethod == null);
this.instance = instance;
}
this.type = (Class<T>) convertPrimitiveType(type);
setArguments(getArgs, setArgs, setArgumentIndex);
- readOnly = (setMethod == null);
this.instance = instance;
}
this.getMethod = getMethod;
this.setMethod = setMethod;
setArguments(getArgs, setArgs, setArgumentIndex);
- readOnly = (setMethod == null);
this.instance = instance;
this.type = type;
}
* @return <code>true</code> if the object is in read-only mode,
* <code>false</code> if it's not
*/
+ @Override
public boolean isReadOnly() {
- return readOnly;
+ return super.isReadOnly() || (setMethod == null);
}
/**
throw new Property.ReadOnlyException();
}
- // Try to assign the compatible value directly
- if (newValue == null || type.isAssignableFrom(newValue.getClass())) {
- invokeSetMethod(newValue);
- } else {
+ Object value = convertValue(newValue, type);
- Object value;
- try {
+ invokeSetMethod(value);
+ fireValueChange();
+ }
- // Gets the string constructor
- final Constructor constr = getType().getConstructor(
- new Class[] { String.class });
+ /**
+ * Convert a value to the given type, using a constructor of the type that
+ * takes a single String parameter (toString() for the value) if necessary.
+ *
+ * @param value
+ * to convert
+ * @param type
+ * type into which the value should be converted
+ * @return converted value
+ */
+ protected static Object convertValue(Object value, Class<?> type) {
+ if (null == value || type.isAssignableFrom(value.getClass())) {
+ return value;
+ }
- value = constr
- .newInstance(new Object[] { newValue.toString() });
+ // convert using a string constructor
+ try {
+ // Gets the string constructor
+ final Constructor constr = type
+ .getConstructor(new Class[] { String.class });
- } catch (final java.lang.Exception e) {
- throw new Property.ConversionException(e);
- }
+ // Create a new object from the string
+ return constr.newInstance(new Object[] { value.toString() });
- // Creates new object from the string
- invokeSetMethod(value);
+ } catch (final java.lang.Exception e) {
+ throw new Property.ConversionException(e);
}
- fireValueChange();
}
/**
}
}
- /**
- * Returns the bean instance which to which the property applies. For
- * internal use.
- *
- * @return bean instance
- */
- protected Object getInstance() {
- return instance;
- }
-
- /**
- * Returns the setter method to use. For internal use.
- *
- * @return setter {@link Method}
- */
- protected Method getSetMethod() {
- return setMethod;
- }
-
- /**
- * Sets the Property's read-only mode to the specified status.
- *
- * @param newStatus
- * the new read-only status of the Property.
- */
- public void setReadOnly(boolean newStatus) {
- final boolean prevStatus = readOnly;
- if (newStatus) {
- readOnly = true;
- } else {
- readOnly = (setMethod == null);
- }
- if (prevStatus != readOnly) {
- fireReadOnlyStatusChange();
- }
- }
-
/**
* <code>Exception</code> object that signals that there were problems
* calling or finding the specified getter or setter methods of the
/**
* The method property from which the exception originates from
*/
- private final MethodProperty methodProperty;
+ private final Property property;
/**
* Cause of the method exception
* Constructs a new <code>MethodException</code> with the specified
* detail message.
*
- * @param methodProperty
- * the method property.
+ * @param property
+ * the property.
* @param msg
* the detail message.
*/
- public MethodException(MethodProperty methodProperty, String msg) {
+ public MethodException(Property property, String msg) {
super(msg);
- this.methodProperty = methodProperty;
+ this.property = property;
}
/**
* Constructs a new <code>MethodException</code> from another exception.
*
- * @param methodProperty
- * the method property.
+ * @param property
+ * the property.
* @param cause
* the cause of the exception.
*/
- public MethodException(MethodProperty methodProperty, Throwable cause) {
- this.methodProperty = methodProperty;
+ public MethodException(Property property, Throwable cause) {
+ this.property = property;
this.cause = cause;
}
/**
* Gets the method property this exception originates from.
+ *
+ * @return MethodProperty or null if not a valid MethodProperty
*/
public MethodProperty getMethodProperty() {
- return methodProperty;
+ return (property instanceof MethodProperty) ? (MethodProperty) property
+ : null;
+ }
+
+ /**
+ * Gets the method property this exception originates from.
+ *
+ * @return Property from which the exception originates
+ */
+ public Property getProperty() {
+ return property;
}
}
package com.vaadin.data.util;
import java.io.IOException;
-import java.io.InvalidObjectException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import com.vaadin.data.Property;
+import com.vaadin.data.util.MethodProperty.MethodException;
+
/**
* Nested accessor based property for a bean.
*
*
* @see MethodProperty
*
- * @param <T>
- * property type
- *
* @since 6.6
*/
-public class NestedMethodProperty<T> extends MethodProperty<T> {
+public class NestedMethodProperty extends AbstractProperty {
// needed for de-serialization
private String propertyName;
- // chain of getter methods up to but not including the last method handled
- // by the superclass
+ // chain of getter methods
private transient List<Method> getMethods;
+ /**
+ * The setter method.
+ */
+ private transient Method setMethod;
+
+ /**
+ * Bean instance used as a starting point for accessing the property value.
+ */
+ private Object instance;
+
+ private Class<?> type;
/* Special serialization to handle method references */
private void writeObject(java.io.ObjectOutputStream out) throws IOException {
out.defaultWriteObject();
- // getMethods is reconstructed on read based on propertyName
+ // getMethods and setMethod are reconstructed on read based on
+ // propertyName
}
/* Special serialization to handle method references */
ClassNotFoundException {
in.defaultReadObject();
- // re-build getMethods: some duplicated code with builder method
- getMethods = new ArrayList<Method>();
- Class<?> propertyClass = getInstance().getClass();
- String[] simplePropertyNames = propertyName.split("\\.");
- for (int i = 0; i < simplePropertyNames.length; i++) {
- String simplePropertyName = simplePropertyNames[i].trim();
- try {
- Method getter = initGetterMethod(simplePropertyName,
- propertyClass);
- propertyClass = getter.getReturnType();
- getMethods.add(getter);
- } catch (final java.lang.NoSuchMethodException e) {
- throw new InvalidObjectException("Bean property '"
- + simplePropertyName + "' not found");
- }
- }
+ initialize(instance, propertyName);
}
/**
* property name is a dot separated string pointing to a nested property,
* e.g. "manager.address.street".
*
- * @param <T>
- * property type (deepest nested property)
* @param instance
* top-level bean to which the property applies
* @param propertyName
* dot separated nested property name
- * @return new NestedMethodProperty instance
+ * @throws IllegalArgumentException
+ * if the property name is invalid
*/
- public static <T> NestedMethodProperty<T> buildNestedMethodProperty(
- Object instance, String propertyName) {
+ public NestedMethodProperty(Object instance, String propertyName) {
+ this.instance = instance;
+ initialize(instance, propertyName);
+ }
+
+ /**
+ * Initializes most of the internal fields based on the top-level bean
+ * instance and property name (dot-separated string).
+ *
+ * @param instance
+ * top-level bean to which the property applies
+ * @param propertyName
+ * dot separated nested property name
+ * @throws IllegalArgumentException
+ * if the property name is invalid
+ */
+ private void initialize(Object instance, String propertyName)
+ throws IllegalArgumentException {
+
List<Method> getMethods = new ArrayList<Method>();
String lastSimplePropertyName = propertyName;
Class<?> propertyClass = instance.getClass();
String[] simplePropertyNames = propertyName.split("\\.");
if (propertyName.endsWith(".") || 0 == simplePropertyNames.length) {
- throw new MethodException(null, "Invalid property name '"
+ throw new IllegalArgumentException("Invalid property name '"
+ propertyName + "'");
}
for (int i = 0; i < simplePropertyNames.length; i++) {
lastSimplePropertyName = simplePropertyName;
lastClass = propertyClass;
try {
- Method getter = initGetterMethod(simplePropertyName,
- propertyClass);
+ Method getter = MethodProperty.initGetterMethod(
+ simplePropertyName, propertyClass);
propertyClass = getter.getReturnType();
getMethods.add(getter);
} catch (final java.lang.NoSuchMethodException e) {
- throw new MethodException(null, "Bean property '"
- + simplePropertyName + "' not found");
+ throw new IllegalArgumentException("Bean property '"
+ + simplePropertyName + "' not found", e);
}
} else {
- throw new MethodException(null,
+ throw new IllegalArgumentException(
"Empty or invalid bean property identifier in '"
+ propertyName + "'");
}
} catch (final NoSuchMethodException skipped) {
}
- NestedMethodProperty<T> property = new NestedMethodProperty<T>(
- (Class<T>) convertPrimitiveType(type), instance, propertyName,
- lastGetMethod, setMethod);
- property.getMethods = getMethods;
+ this.type = MethodProperty.convertPrimitiveType(type);
+ this.propertyName = propertyName;
+ this.getMethods = getMethods;
+ this.setMethod = setMethod;
+ }
- return property;
+ public Class<?> getType() {
+ return type;
}
- protected NestedMethodProperty(Class<T> type, Object instance,
- String propertyName, Method lastGetMethod, Method setMethod) {
- super(type, instance, lastGetMethod, setMethod);
- this.propertyName = propertyName;
+ @Override
+ public boolean isReadOnly() {
+ return super.isReadOnly() || (null == setMethod);
}
/**
*
* @return the value of the Property
*/
- @Override
public Object getValue() {
try {
- Object instance = getInstance();
+ Object object = instance;
for (Method m : getMethods) {
- instance = m.invoke(instance);
+ object = m.invoke(object);
}
- return instance;
+ return object;
} catch (final Throwable e) {
throw new MethodException(this, e);
}
}
+ /**
+ * Sets the value of the property. This method supports setting from
+ * <code>String</code>s if either <code>String</code> is directly assignable
+ * to property type, or the type class contains a string constructor.
+ *
+ * @param newValue
+ * the New value of the property.
+ * @throws <code>Property.ReadOnlyException</code> if the object is in
+ * read-only mode.
+ * @throws <code>Property.ConversionException</code> if
+ * <code>newValue</code> can't be converted into the Property's
+ * native type directly or through <code>String</code>.
+ * @see #invokeSetMethod(Object)
+ */
+ public void setValue(Object newValue) throws ReadOnlyException,
+ ConversionException {
+ // Checks the mode
+ if (isReadOnly()) {
+ throw new Property.ReadOnlyException();
+ }
+
+ Object value = MethodProperty.convertValue(newValue, type);
+
+ invokeSetMethod(value);
+ fireValueChange();
+ }
+
/**
* Internal method to actually call the setter method of the wrapped
* property.
*
* @param value
*/
- @Override
protected void invokeSetMethod(Object value) {
try {
- Object instance = getInstance();
+ Object object = instance;
for (int i = 0; i < getMethods.size() - 1; i++) {
- instance = getMethods.get(i).invoke(instance);
+ object = getMethods.get(i).invoke(object);
}
- getSetMethod().invoke(instance, new Object[] { value });
+ setMethod.invoke(object, new Object[] { value });
} catch (final InvocationTargetException e) {
throw new MethodException(this, e.getTargetException());
} catch (final Exception e) {
}
public Property createProperty(BT bean) {
- return NestedMethodProperty.buildNestedMethodProperty(bean, name);
+ return new NestedMethodProperty(bean, name);
}
}
import junit.framework.Assert;
import junit.framework.TestCase;
-import com.vaadin.data.util.MethodProperty.MethodException;
import com.vaadin.data.util.NestedMethodProperty;
public class NestedMethodPropertyTest extends TestCase {
}
public void testSingleLevelNestedSimpleProperty() {
- NestedMethodProperty<String> nameProperty = NestedMethodProperty
- .buildNestedMethodProperty(vaadin, "name");
+ NestedMethodProperty nameProperty = new NestedMethodProperty(vaadin,
+ "name");
Assert.assertEquals(String.class, nameProperty.getType());
Assert.assertEquals("Vaadin", nameProperty.getValue());
}
public void testSingleLevelNestedObjectProperty() {
- NestedMethodProperty<Person> managerProperty = NestedMethodProperty
- .buildNestedMethodProperty(vaadin, "manager");
+ NestedMethodProperty managerProperty = new NestedMethodProperty(vaadin,
+ "manager");
Assert.assertEquals(Person.class, managerProperty.getType());
Assert.assertEquals(joonas, managerProperty.getValue());
}
public void testMultiLevelNestedProperty() {
- NestedMethodProperty<String> managerNameProperty = NestedMethodProperty
- .buildNestedMethodProperty(vaadin, "manager.name");
- NestedMethodProperty<Address> addressProperty = NestedMethodProperty
- .buildNestedMethodProperty(vaadin, "manager.address");
- NestedMethodProperty<String> streetProperty = NestedMethodProperty
- .buildNestedMethodProperty(vaadin, "manager.address.street");
- NestedMethodProperty<String> postalCodePrimitiveProperty = NestedMethodProperty
- .buildNestedMethodProperty(vaadin,
- "manager.address.postalCodePrimitive");
- NestedMethodProperty<String> postalCodeObjectProperty = NestedMethodProperty
- .buildNestedMethodProperty(vaadin,
- "manager.address.postalCodeObject");
- NestedMethodProperty<String> booleanProperty = NestedMethodProperty
- .buildNestedMethodProperty(vaadin, "manager.address.boolean");
+ NestedMethodProperty managerNameProperty = new NestedMethodProperty(
+ vaadin, "manager.name");
+ NestedMethodProperty addressProperty = new NestedMethodProperty(vaadin,
+ "manager.address");
+ NestedMethodProperty streetProperty = new NestedMethodProperty(vaadin,
+ "manager.address.street");
+ NestedMethodProperty postalCodePrimitiveProperty = new NestedMethodProperty(
+ vaadin, "manager.address.postalCodePrimitive");
+ NestedMethodProperty postalCodeObjectProperty = new NestedMethodProperty(
+ vaadin, "manager.address.postalCodeObject");
+ NestedMethodProperty booleanProperty = new NestedMethodProperty(vaadin,
+ "manager.address.boolean");
Assert.assertEquals(String.class, managerNameProperty.getType());
Assert.assertEquals("Joonas", managerNameProperty.getValue());
public void testEmptyPropertyName() {
try {
- NestedMethodProperty.buildNestedMethodProperty(vaadin, "");
+ new NestedMethodProperty(vaadin, "");
fail();
- } catch (MethodException e) {
+ } catch (IllegalArgumentException e) {
// should get exception
}
try {
- NestedMethodProperty.buildNestedMethodProperty(vaadin, " ");
+ new NestedMethodProperty(vaadin, " ");
fail();
- } catch (MethodException e) {
+ } catch (IllegalArgumentException e) {
// should get exception
}
}
public void testInvalidPropertyName() {
try {
- NestedMethodProperty.buildNestedMethodProperty(vaadin, ".");
+ new NestedMethodProperty(vaadin, ".");
fail();
- } catch (MethodException e) {
+ } catch (IllegalArgumentException e) {
// should get exception
}
try {
- NestedMethodProperty.buildNestedMethodProperty(vaadin, ".manager");
+ new NestedMethodProperty(vaadin, ".manager");
fail();
- } catch (MethodException e) {
+ } catch (IllegalArgumentException e) {
// should get exception
}
try {
- NestedMethodProperty.buildNestedMethodProperty(vaadin, "manager.");
+ new NestedMethodProperty(vaadin, "manager.");
fail();
- } catch (MethodException e) {
+ } catch (IllegalArgumentException e) {
// should get exception
}
try {
- NestedMethodProperty.buildNestedMethodProperty(vaadin,
- "manager..name");
+ new NestedMethodProperty(vaadin, "manager..name");
fail();
- } catch (MethodException e) {
+ } catch (IllegalArgumentException e) {
// should get exception
}
}
public void testInvalidNestedPropertyName() {
try {
- NestedMethodProperty.buildNestedMethodProperty(vaadin, "member");
+ new NestedMethodProperty(vaadin, "member");
fail();
- } catch (MethodException e) {
+ } catch (IllegalArgumentException e) {
// should get exception
}
try {
- NestedMethodProperty.buildNestedMethodProperty(vaadin,
- "manager.pet");
+ new NestedMethodProperty(vaadin, "manager.pet");
fail();
- } catch (MethodException e) {
+ } catch (IllegalArgumentException e) {
// should get exception
}
try {
- NestedMethodProperty.buildNestedMethodProperty(vaadin,
- "manager.address.city");
+ new NestedMethodProperty(vaadin, "manager.address.city");
fail();
- } catch (MethodException e) {
+ } catch (IllegalArgumentException e) {
// should get exception
}
}
public void testNullNestedProperty() {
- NestedMethodProperty<String> managerNameProperty = NestedMethodProperty
- .buildNestedMethodProperty(vaadin, "manager.name");
- NestedMethodProperty<String> streetProperty = NestedMethodProperty
- .buildNestedMethodProperty(vaadin, "manager.address.street");
+ NestedMethodProperty managerNameProperty = new NestedMethodProperty(
+ vaadin, "manager.name");
+ NestedMethodProperty streetProperty = new NestedMethodProperty(vaadin,
+ "manager.address.street");
joonas.setAddress(null);
try {
streetProperty.getValue();
fail();
- } catch (MethodException e) {
+ } catch (Exception e) {
// should get exception
}
try {
managerNameProperty.getValue();
fail();
- } catch (MethodException e) {
+ } catch (Exception e) {
// should get exception
}
try {
streetProperty.getValue();
fail();
- } catch (MethodException e) {
+ } catch (Exception e) {
// should get exception
}
}
public void testMultiLevelNestedPropertySetValue() {
- NestedMethodProperty<String> managerNameProperty = NestedMethodProperty
- .buildNestedMethodProperty(vaadin, "manager.name");
- NestedMethodProperty<Address> addressProperty = NestedMethodProperty
- .buildNestedMethodProperty(vaadin, "manager.address");
- NestedMethodProperty<String> streetProperty = NestedMethodProperty
- .buildNestedMethodProperty(vaadin, "manager.address.street");
- NestedMethodProperty<String> postalCodePrimitiveProperty = NestedMethodProperty
- .buildNestedMethodProperty(vaadin,
- "manager.address.postalCodePrimitive");
- NestedMethodProperty<String> postalCodeObjectProperty = NestedMethodProperty
- .buildNestedMethodProperty(vaadin,
- "manager.address.postalCodeObject");
+ NestedMethodProperty managerNameProperty = new NestedMethodProperty(
+ vaadin, "manager.name");
+ NestedMethodProperty addressProperty = new NestedMethodProperty(vaadin,
+ "manager.address");
+ NestedMethodProperty streetProperty = new NestedMethodProperty(vaadin,
+ "manager.address.street");
+ NestedMethodProperty postalCodePrimitiveProperty = new NestedMethodProperty(
+ vaadin, "manager.address.postalCodePrimitive");
+ NestedMethodProperty postalCodeObjectProperty = new NestedMethodProperty(
+ vaadin, "manager.address.postalCodeObject");
managerNameProperty.setValue("Joonas L");
Assert.assertEquals("Joonas L", joonas.getName());
}
public void testSerialization() throws IOException, ClassNotFoundException {
- NestedMethodProperty<String> streetProperty = NestedMethodProperty
- .buildNestedMethodProperty(vaadin, "manager.address.street");
+ NestedMethodProperty streetProperty = new NestedMethodProperty(vaadin,
+ "manager.address.street");
ByteArrayOutputStream baos = new ByteArrayOutputStream();
new ObjectOutputStream(baos).writeObject(streetProperty);
- NestedMethodProperty<String> property2 = (NestedMethodProperty<String>) new ObjectInputStream(
+ NestedMethodProperty property2 = (NestedMethodProperty) new ObjectInputStream(
new ByteArrayInputStream(baos.toByteArray())).readObject();
Assert.assertEquals("Ruukinkatu 2-4", property2.getValue());
}
public void testIsReadOnly() {
- NestedMethodProperty<String> streetProperty = NestedMethodProperty
- .buildNestedMethodProperty(vaadin, "manager.address.street");
- NestedMethodProperty<String> booleanProperty = NestedMethodProperty
- .buildNestedMethodProperty(vaadin, "manager.address.boolean");
+ NestedMethodProperty streetProperty = new NestedMethodProperty(vaadin,
+ "manager.address.street");
+ NestedMethodProperty booleanProperty = new NestedMethodProperty(vaadin,
+ "manager.address.boolean");
Assert.assertFalse(streetProperty.isReadOnly());
Assert.assertTrue(booleanProperty.isReadOnly());