First of all congrats on Vaadin 7, I am loving the new features so far!
I am currently working on porting a rather large application from 6 to 7. There is quite a lot of code in the project that interacts with tables and other containers through properties by Table.getItemProperty().setValue(). Now as the setValue(Object) was changed to setValue(T) there are also a lot of “type safety” -warnings.
To deal with the warnings I have used a static helper method and also a wrapper-class that augments a Container with methods that help avoiding the warnings. My solutions are mostly inspired by Joshua Bloch’s great book “Effective Java (2nd Edition)”.
public class PropertyHelper {
// this method helps dealing with raw-property returned from
// Container.getContainerProperty()
@SuppressWarnings("rawtypes")
public static void setPropVal(Property prop, Object val) {
setValue((Property<?>) prop, val);
}
public static <T> void setValue(Property<T> prop, Object val) {
if (prop == null) {
throw new NullPointerException("Property 'prop' was null");
} else if (!prop.getType().isAssignableFrom(val.getClass())) {
throw new IllegalArgumentException(
"Tried to set value of property=" + prop + "; the "
+ "value (=" + val
+ ") was of incompatible type (required type: "
+ prop.getType() + "; type of value: "
+ val.getClass() + ").");
} else {
prop.setValue(prop.getType().cast(val));
}
}
public static class GenericsContainerAugmenter implements Container {
private final Container toAugment;
public GenericsContainerAugmenter(Container toAugment) {
this.toAugment = toAugment;
}
// these methods act somewhat as if property-id was
// actually a composite of
// property-id and required-type of the property (although having two
// properties differentiated only by value-type is not possible);
// there could also be errors instead of doing nothing and returning
// null
@SuppressWarnings("unchecked")
public <T> Property<T> getContainerProperty(Object itemId,
Object propertyId, Class<T> type) {
Property<?> prop = toAugment.getContainerProperty(itemId,
propertyId);
if (prop != null && type.isAssignableFrom(prop.getType())) {
// this cast should be safe here
return (Property<T>) prop;
} else {
return null;
}
}
// T is not needed here as the implementation is delegated to PropertyHelper
public void setPropertyValue(Object itemId, Object propertyId,
Object value) {
Property<?> prop = toAugment.getContainerProperty(itemId,
propertyId);
if (prop != null
&& prop.getType().isAssignableFrom(value.getClass())) {
PropertyHelper.setValue(prop, value);
}
}
public <T> T getPropertyValue(Object itemId, Object propertyId,
Class<T> valType) {
Property<?> prop = toAugment.getContainerProperty(itemId,
propertyId);
if (valType.isAssignableFrom(prop.getType())) {
return valType.cast(prop.getValue());
} else {
return null;
}
}
// rest of the interface-methods delegated to "toAugment"
}
}
Now you can write type-safe (or run-time checked with dynamic casts) code that mainly has the added complexity of requiring the extra Class<?> parameter in some method calls.
NativeSelect testSel = new NativeSelect();
GenericsContainerAugmenter cont = new GenericsContainerAugmenter(
testSel.getContainerDataSource());
cont.addContainerProperty("testProp", String.class, null);
cont.addItem("testItem");
cont.setPropertyValue("testItem", "testProp", "test-value");
Property<String> testProp = cont.getContainerProperty("testItem",
"testProp", String.class);
testProp.setValue("test-value-2");
String val = cont.getPropertyValue("testItem", "testProp", String.class);
I will probably make a feature request of adding something similar to the core Vaadin. The most convenient place to add needed methods in that case would probably be the Container-interface itself.
That is unless someone points to me that there already exists some similar methods in core Vaadin, or that I can write my code in such a way that I do not need to use the Container.getProperty().setValue() -sequence at all.
(I realize that this is closely related to at least tickets #8791 and #5158 but I think that they did not directly address/solve my problem).
EDIT:
Oops, that code does not handle setting the value of a Property to null.
Better test in the if would be “val == null || prop.getType().isAssignableFrom(val.getClass())”; that should work as Class.cast(null) succeeds and returns null.