Exploring Vaadin (6) - summary of several classes
"Formatting proxy for a property. / This class can be used to implement formatting for any type of Property datasources. The idea is to connect this as proxy between UI component and the original datasource. "
It'a an abstract class, with methods format / parse left to implement by user. Methods format() / parse() converts String to and from Object. It requires a property datasource to initiate a new instance. The datasource can be replaced with setPropertyDataSource(), and the PropertyFormatter code will take care of discontinue listening to old datasource and begin listening on new datasource, setting readOnly, firing change events, etc.
Below is the sample code for usage of this class on document of the class.
public String format(Object value) {
return ((Double) value).toString() + " 000000000 " ;
}
public Object parse(String formattedValue) throws Exception {
return Double.parseDouble(formattedValue);
}
});
Conversion occur at toString(), which invokes "return format(value)", and setValue(), which invokes "dataSource.setValue(parse((String) newValue))".
As a listenter (to the data source), on change of data source, the handler of PropertyFormatter simply propogates the event to its own listeners, i.e. fires corresponding events.
Abstract Class AbstractSelect
"A class representing a selection of items the user has selected in a UI. The set of choices is presented as a set of Item(s) in a
Container"
"A Select component may be in single- or multiselect mode. Multiselect mode means that more than one item can be selected simultaneously."
First, item caption mode determines how to get caption from itemId: (summary from code)
case ITEM_CAPTION_MODE_ID: itemId.toString();
case ITEM_CAPTION_MODE_INDEX:
if (items instanceof Container.Indexed)
caption = String.valueOf(((Container.Indexed) items).indexOfId(itemId));
case ITEM_CAPTION_MODE_ITEM:
getItem(itemId).toString();
case ITEM_CAPTION_MODE_EXPLICIT:
(String) itemCaptions.get(itemId);
case ITEM_CAPTION_MODE_EXPLICIT_DEFAULTS_ID:
(String) itemCaptions.get(itemId);
or
itemId.toString();
case ITEM_CAPTION_MODE_PROPERTY (use with setItemCaptionPropertyId):
items.getContainerProperty(itemId, getItemCaptionPropertyId()).toString();
Then, see getValue()
// Give the value from abstract buffers if the field if possible
if (dataSource == null || ! isReadThrough() || isModified()) {
return value;
}
Object newValue = String. class == getType() ? dataSource.toString()
: dataSource.getValue();
if ((newValue == null && value != null )
|| (newValue != null && ! newValue.equals(value))) {
setInternalValue(newValue);
fireValueChange( false );
}
return newValue;
}
if no datasource, not readthrough (then read buffer), or has modified (dirty), get the current value that "is the visible, modified and possible invalid value".
otherwise, if type is String, return dataSource.toString()
then otherwise, return dataSource.getValue()
if things changed (newValue from datasource not equals value from UI), setInternalValue(newValue) and fireValueChange.
This is a little bit strange, getValue() may possibly fire valuechange event. But again this is understandable. Value from datasource may have changed.
Interface FormFieldFactory
"Factory interface for creating new Field-instances based on Item
, property id and uiContext (the component responsible for displaying fields). Currently this interface is used by Form
, but might later be used by some other components for Field
generation. "
Object propertyId,
Component uiContext)
public class DefaultFieldFactory implements FormFieldFactory, TableFieldFactory
This class is just the basic implementation and far from perfect. In most cases a more sophisticated one shall be build. In "createFieldByPropertyType", only Item->Form/Date->DateField/Boolean->CheckBox/[anything else]->TextField is provided. In "createCaptionByPropertyId", it simply converts "firstName" to "First Name". Therefore, as mentioned, this is indeed just a basic implementation, for reference.
It's worth mention how Form calls the field factory.
- In addItemProperty(Object id, Property property) : fieldFactory.createField(this, id, this);
- In setItemDataSource(Item newDataSource, Collection propertyIds) : fieldFactory.createField(itemDatasource, id, this);
public class AbstractField
This class is the base of most Field implementations.
My interest first lies in commit() method:
- if (dataSource != null && !dataSource.isReadOnly()), then value is simply not committed to data source.
- if do commit, first test isInvalidCommitted || isValid(). if test fails, call validate() to throw the exception
- try dataSource.setValue( getValue())
- if anything fails (a Throwable), encapsulate it with a Buffered.SourceException, request repaint, and throw the new exception.
The above answers two important questions (1) when and how validation is done (2) what is committed to data source (getValue())
Now see what value is get from getValue():
// Give the value from abstract buffers if the field if possible
if (dataSource == null || ! isReadThrough() || isModified()) {
return value;
}
Object newValue = String. class == getType() ? dataSource.toString()
: dataSource.getValue();
if ((newValue == null && value != null )
|| (newValue != null && ! newValue.equals(value))) {
setInternalValue(newValue);
fireValueChange( false );
}
return newValue;
}
That is, when there is no dataSource, or not read through (i.e. read internal value, not datasource value), or modified (i.e. internal value is most updated), return internal value; otherwise, check if getType() is String. if yes, newValue (will be returned) is dataSource().toString, otherwise newValue is dataSource.getValue(). set internal value and fire value change when necessary, return new value.
According to comment, getValue is probably override by sub classes.