I’ve added a List called dogs in the Person class and the accompanying getters & setters
In the field factory I’ve added a TwinColumnSelect for the dogs list which is populated using Dog objects (not simple Strings)
...
if ("dogs".equals(propertyId)) {
TwinColSelect tf = new TwinColSelect();
tf.setNullSelectionAllowed(true);
tf.setMultiSelect(true);
tf.setImmediate(true);
tf.setContainerDataSource(new BeanItemContainer<Dog>(ALL_DOGS));
// Rem: ALL_DOGS is a List of Dog object instances
f = tf;
}
...
The problem is when I select some dogs and click on apply (commit the form) I receive a:
I’ve investgated a bit and it seems that when replacing the List with a Set the code works.
Traving Vaadin’s code I’ve spotted why it throws an exception when using a List. Here is MethodProperty.setValue() method:
public void setValue(Object newValue) throws Property.ReadOnlyException,
Property.ConversionException {
// Checks the mode
if (isReadOnly()) {
throw new Property.ReadOnlyException();
}
// Try to assign the compatible value directly
if (newValue == null || [b]
type.isAssignableFrom(newValue.getClass())
[/b]) {
invokeSetMethod(newValue);
} else {
Object value;
try {
// Gets the string constructor
final Constructor constr = getType().getConstructor(
new Class[] { String.class });
value = constr
.newInstance(new Object[] { newValue.toString() });
} catch (final java.lang.Exception e) {
throw new Property.ConversionException(e);
}
// Creates new object from the string
invokeSetMethod(value);
}
fireValueChange();
}
The isAssignable return false and it defaults to finding a Constructor taking a String as argument which triggers an exception.
I am a bit disapointed by this behavior and hoped a smarter one when dealing with collections than replacing the existing collection by a completely new one (as done by MethodProperty.invokeSetMethod()). I am using collections coming from entities and Hiberante dirty-checking mechanism does not like collection replacement much.
I am ok to submit a path that would behave differently: if both types are collections move the missing items to the existing collection.
While waiting for the fix to be integrated in the next version, you can use the following solution. Basically, it will adjust the return type only when it is about to commit data. When creating the component, just pass in the type which can be easily obtain from your FieldFactory implementation.
Cheers,
Tien
/**
* @author ttran
*
*/
public class TwinColSelectEx extends TwinColSelect {
private Class<?> type;
private boolean isCommiting;
public TwinColSelectEx(Class<?> type) {
super();
this.type = type;
}
@Override
public Object getValue() {
final Object retValue = super.getValue();
// If the return value is not a set
if (isCommiting && type != null && List.class.isAssignableFrom(type)) {
if (retValue == null) {
return new ArrayList(0);
}
if (retValue instanceof Set) {
return Collections.unmodifiableList(new ArrayList((Set) retValue));
} else if (retValue instanceof Collection) {
return new ArrayList((Collection) retValue);
} else {
final List l = new ArrayList(1);
if (items.containsId(retValue)) {
l.add(retValue);
}
return l;
}
}
return retValue;
}
@Override
public void commit() throws SourceException, InvalidValueException {
isCommiting = true;
try {
super.commit();
} finally {
isCommiting = false;
}
}
}
I Overrided method but no effect still getting same exception com.vaadin.data.Property$ConversionException: java.lang.NoSuchMethodException: java.util.List.(java.lang.String)