/* ************************************************************************* IT Mill Toolkit Development of Browser User Interfaces Made Easy Copyright (C) 2000-2006 IT Mill Ltd ************************************************************************* This product is distributed under commercial license that can be found from the product package on license.pdf. Use of this product might require purchasing a commercial license from IT Mill Ltd. For guidelines on usage, see licensing-guidelines.html ************************************************************************* For more information, contact: IT Mill Ltd phone: +358 2 4802 7180 Ruukinkatu 2-4 fax: +358 2 4802 7181 20540, Turku email: info@itmill.com Finland company www: www.itmill.com Primary source for information and releases: www.itmill.com ********************************************************************** */ package com.itmill.toolkit.ui; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import com.itmill.toolkit.data.Buffered; import com.itmill.toolkit.data.Item; import com.itmill.toolkit.data.Property; import com.itmill.toolkit.data.Validatable; import com.itmill.toolkit.data.Validator; import com.itmill.toolkit.data.Validator.InvalidValueException; import com.itmill.toolkit.data.util.BeanItem; import com.itmill.toolkit.terminal.PaintException; import com.itmill.toolkit.terminal.PaintTarget; /** * Form component provides easy way of creating and managing sets fields. * *
* Form
is a container for fields implementing {@link Field}
* interface. It provides support for any layouts and provides buffering
* interface for easy connection of commit and discard buttons. All the form
* fields can be customized by adding validators, setting captions and icons,
* setting immediateness, etc. Also direct mechanism for replacing existing
* fields with selections is given.
*
* Form
provides customizable editor for classes implementing
* {@link com.itmill.toolkit.data.Item} interface. Also the form itself
* implements this interface for easier connectivity to other items. To use the
* form as editor for an item, just connect the item to form with
* {@link Form#setItemDataSource(Item)}. If only a part of the item needs to be
* edited, {@link Form#setItemDataSource(Item,Collection)} can be used instead.
* After the item has been connected to the form, the automatically created
* fields can be customized and new fields can be added. If you need to connect
* a class that does not implement {@link com.itmill.toolkit.data.Item}
* interface, most properties of any class following bean pattern, can be
* accessed trough {@link com.itmill.toolkit.data.util.BeanItem}.
*
* By default the form uses OrderedLayout
with
* form
-style.
*
* The property id must not be already used in the form. *
* ** This field is added to the form layout in the default position (the * position used by {@link Layout#addComponent(Component)} method. In the * special case that the underlying layout is a custom layout, string * representation of the property id is used instead of the default * location. *
* * @param propertyId * the Property id the the field. * @param field * the New field added to the form. */ public void addField(Object propertyId, Field field) { if (propertyId != null && field != null) { dependsOn(field); field.dependsOn(this); fields.put(propertyId, field); propertyIds.addLast(propertyId); field.setReadThrough(readThrough); field.setWriteThrough(writeThrough); if (layout instanceof CustomLayout) { ((CustomLayout) layout).addComponent(field, propertyId .toString()); } else { layout.addComponent(field); } requestRepaint(); } } /** * The property identified by the property id. * ** The property data source of the field specified with property id is * returned. If there is a (with specified property id) having no data * source, the field is returned instead of the data source. *
* * @see com.itmill.toolkit.data.Item#getItemProperty(Object) */ public Property getItemProperty(Object id) { Field field = (Field) fields.get(id); if (field == null) { return null; } Property property = field.getPropertyDataSource(); if (property != null) { return property; } else { return field; } } /** * Gets the field identified by the propertyid. * * @param propertyId * the id of the property. */ public Field getField(Object propertyId) { return (Field) fields.get(propertyId); } /* Documented in interface */ public Collection getItemPropertyIds() { return Collections.unmodifiableCollection(propertyIds); } /** * Removes the property and corresponding field from the form. * * @see com.itmill.toolkit.data.Item#removeItemProperty(Object) */ public boolean removeItemProperty(Object id) { Field field = (Field) fields.get(id); if (field != null) { propertyIds.remove(id); fields.remove(id); removeDirectDependency(field); field.removeDirectDependency(this); layout.removeComponent(field); return true; } return false; } /** * Removes all properties and fields from the form. * * @return the Success of the operation. Removal of all fields succeeded if * (and only if) the return value istrue
.
*/
public boolean removeAllProperties() {
Object[] properties = propertyIds.toArray();
boolean success = true;
for (int i = 0; i < properties.length; i++) {
if (!removeItemProperty(properties[i])) {
success = false;
}
}
return success;
}
/* Documented in the interface */
public Item getItemDataSource() {
return itemDatasource;
}
/**
* Sets the item datasource for the form.
*
* * Setting item datasource clears any fields, the form might contain and * adds all the properties as fields to the form. *
* * @see com.itmill.toolkit.data.Item.Viewer#setItemDataSource(Item) */ public void setItemDataSource(Item newDataSource) { setItemDataSource(newDataSource, newDataSource != null ? newDataSource .getItemPropertyIds() : null); } /** * Set the item datasource for the form, but limit the form contents to * specified properties of the item. * ** Setting item datasource clears any fields, the form might contain and * adds the specified the properties as fields to the form, in the specified * order. *
* * @see com.itmill.toolkit.data.Item.Viewer#setItemDataSource(Item) */ public void setItemDataSource(Item newDataSource, Collection propertyIds) { // Removes all fields first from the form removeAllProperties(); // Sets the datasource itemDatasource = newDataSource; // If the new datasource is null, just set null datasource if (itemDatasource == null) { return; } // Adds all the properties to this form for (Iterator i = propertyIds.iterator(); i.hasNext();) { Object id = i.next(); Property property = itemDatasource.getItemProperty(id); if (id != null && property != null) { Field f = fieldFactory.createField(itemDatasource, id, this); if (f != null) { f.setPropertyDataSource(property); addField(id, f); } } } } /** * Gets the layout of the form. * *
* By default form uses OrderedLayout
with form
-style.
*
* By default form uses OrderedLayout
with form
-style.
*
* The list values and descriptions are given as array. The value-array must * contain the current value of the field and the lengths of the arrays must * match. Null values are not supported. *
* * @param propertyId * the id of the property. * @param values * @param descriptions * @return the select property generated */ public Select replaceWithSelect(Object propertyId, Object[] values, Object[] descriptions) { // Checks the parameters if (propertyId == null || values == null || descriptions == null) { throw new NullPointerException("All parameters must be non-null"); } if (values.length != descriptions.length) { throw new IllegalArgumentException( "Value and description list are of different size"); } // Gets the old field Field oldField = (Field) fields.get(propertyId); if (oldField == null) { throw new IllegalArgumentException("Field with given propertyid '" + propertyId.toString() + "' can not be found."); } Object value = oldField.getValue(); // Checks that the value exists and check if the select should // be forced in multiselect mode boolean found = false; boolean isMultiselect = false; for (int i = 0; i < values.length && !found; i++) { if (values[i] == value || (value != null && value.equals(values[i]))) { found = true; } } if (value != null && !found) { if (value instanceof Collection) { for (Iterator it = ((Collection) value).iterator(); it .hasNext();) { Object val = it.next(); found = false; for (int i = 0; i < values.length && !found; i++) { if (values[i] == val || (val != null && val.equals(values[i]))) { found = true; } } if (!found) { throw new IllegalArgumentException( "Currently selected value '" + val + "' of property '" + propertyId.toString() + "' was not found"); } } isMultiselect = true; } else { throw new IllegalArgumentException("Current value '" + value + "' of property '" + propertyId.toString() + "' was not found"); } } // Creates the new field matching to old field parameters Select newField = new Select(); if (isMultiselect) { newField.setMultiSelect(true); } newField.setCaption(oldField.getCaption()); newField.setReadOnly(oldField.isReadOnly()); newField.setReadThrough(oldField.isReadThrough()); newField.setWriteThrough(oldField.isWriteThrough()); // Creates the options list newField.addContainerProperty("desc", String.class, ""); newField.setItemCaptionPropertyId("desc"); for (int i = 0; i < values.length; i++) { Object id = values[i]; if (id == null) { id = new Object(); newField.setNullSelectionItemId(id); } Item item = newField.addItem(id); if (item != null) { item.getItemProperty("desc").setValue( descriptions[i].toString()); } } // Sets the property data source Property property = oldField.getPropertyDataSource(); oldField.setPropertyDataSource(null); newField.setPropertyDataSource(property); // Replaces the old field with new one layout.replaceComponent(oldField, newField); fields.put(propertyId, newField); removeDirectDependency(oldField); oldField.removeDirectDependency(this); dependsOn(newField); newField.dependsOn(this); return newField; } /** * Notifies the component that it is connected to an application * * @see com.itmill.toolkit.ui.Component#attach() */ public void attach() { super.attach(); layout.attach(); } /** * Notifies the component that it is detached from the application. * * @see com.itmill.toolkit.ui.Component#detach() */ public void detach() { super.detach(); layout.detach(); } /** * Adds a new validator for this object. * * @see com.itmill.toolkit.data.Validatable#addValidator(com.itmill.toolkit.data.Validator) */ public void addValidator(Validator validator) { if (validators == null) { validators = new LinkedList(); } validators.add(validator); } /** * Removes a previously registered validator from the object. * * @see com.itmill.toolkit.data.Validatable#removeValidator(com.itmill.toolkit.data.Validator) */ public void removeValidator(Validator validator) { if (validators != null) { validators.remove(validator); } } /** * Gets the Lists all validators currently registered for the object. * * @see com.itmill.toolkit.data.Validatable#getValidators() */ public Collection getValidators() { if (validators == null) { validators = new LinkedList(); } return validators; } /** * Tests the current value of the object against all registered validators * * @see com.itmill.toolkit.data.Validatable#isValid() */ public boolean isValid() { boolean valid = true; for (Iterator i = propertyIds.iterator(); i.hasNext();) { valid &= ((Field) fields.get(i.next())).isValid(); } return valid; } /** * Checks the validity of the validatable. * * @see com.itmill.toolkit.data.Validatable#validate() */ public void validate() throws InvalidValueException { for (Iterator i = propertyIds.iterator(); i.hasNext();) { ((Field) fields.get(i.next())).validate(); } } /** * Checks the validabtable object accept invalid values. * * @see com.itmill.toolkit.data.Validatable#isInvalidAllowed() */ public boolean isInvalidAllowed() { return true; } /** * Should the validabtable object accept invalid values. * * @see com.itmill.toolkit.data.Validatable#setInvalidAllowed(boolean) */ public void setInvalidAllowed(boolean invalidValueAllowed) throws UnsupportedOperationException { throw new UnsupportedOperationException(); } /** * Sets the component's to read-only mode to the specified state. * * @see com.itmill.toolkit.ui.Component#setReadOnly(boolean) */ public void setReadOnly(boolean readOnly) { super.setReadOnly(readOnly); for (Iterator i = propertyIds.iterator(); i.hasNext();) { ((Field) fields.get(i.next())).setReadOnly(readOnly); } } /** * Sets the field factory of Form. * *FieldFactory
is used to create fields for form properties.
* By default the form uses BaseFieldFactory to create Field instances.
*
* @param fieldFactory
* the New factory used to create the fields.
* @see Field
* @see FieldFactory
*/
public void setFieldFactory(FieldFactory fieldFactory) {
this.fieldFactory = fieldFactory;
}
/**
* Get the field factory of the form.
*
* @return the FieldFactory Factory used to create the fields.
*/
public FieldFactory getFieldFactory() {
return fieldFactory;
}
/**
* Gets the field type.
*
* @see com.itmill.toolkit.ui.AbstractField#getType()
*/
public Class getType() {
if (getPropertyDataSource() != null) {
return getPropertyDataSource().getType();
}
return Object.class;
}
/**
* Sets the internal value.
*
* This is relevant when the Form is used as Field.
*
* @see com.itmill.toolkit.ui.AbstractField#setInternalValue(java.lang.Object)
*/
protected void setInternalValue(Object newValue) {
// Stores the old value
Object oldValue = propertyValue;
// Sets the current Value
super.setInternalValue(newValue);
propertyValue = newValue;
// Ignores form updating if data object has not changed.
if (oldValue != newValue) {
setFormDataSource(newValue, getVisibleItemProperties());
}
}
/**
* Gets the first field in form.
*
* @return the Field.
*/
private Field getFirstField() {
Object id = null;
if (getItemPropertyIds() != null) {
id = getItemPropertyIds().iterator().next();
}
if (id != null) {
return getField(id);
}
return null;
}
/**
* Updates the internal form datasource.
*
* Method setFormDataSource.
*
* @param data
* @param properties
*/
protected void setFormDataSource(Object data, Collection properties) {
// If data is an item use it.
Item item = null;
if (data instanceof Item) {
item = (Item) data;
} else if (data != null) {
item = new BeanItem(data);
}
// Sets the datasource to form
if (item != null && properties != null) {
// Shows only given properties
this.setItemDataSource(item, properties);
} else {
// Shows all properties
this.setItemDataSource(item);
}
}
/**
* Returns the visibleProperties.
*
* @return the Collection of visible Item properites.
*/
public Collection getVisibleItemProperties() {
return visibleItemProperties;
}
/**
* Sets the visibleProperties.
*
* @param visibleProperties
* the visibleProperties to set.
*/
public void setVisibleItemProperties(Collection visibleProperties) {
visibleItemProperties = visibleProperties;
Object value = getValue();
setFormDataSource(value, getVisibleItemProperties());
}
/**
* Focuses the first field in the form.
*
* @see com.itmill.toolkit.ui.Component.Focusable#focus()
*/
public void focus() {
Field f = getFirstField();
if (f != null) {
f.focus();
}
}
/**
* Sets the Tabulator index of this Focusable component.
*
* @see com.itmill.toolkit.ui.Component.Focusable#setTabIndex(int)
*/
public void setTabIndex(int tabIndex) {
super.setTabIndex(tabIndex);
for (Iterator i = getItemPropertyIds().iterator(); i.hasNext();) {
(getField(i.next())).setTabIndex(tabIndex);
}
}
}