aboutsummaryrefslogtreecommitdiffstats
path: root/src/com/itmill/toolkit/ui/AbstractField.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/itmill/toolkit/ui/AbstractField.java')
-rw-r--r--src/com/itmill/toolkit/ui/AbstractField.java922
1 files changed, 922 insertions, 0 deletions
diff --git a/src/com/itmill/toolkit/ui/AbstractField.java b/src/com/itmill/toolkit/ui/AbstractField.java
new file mode 100644
index 0000000000..13a20ad23d
--- /dev/null
+++ b/src/com/itmill/toolkit/ui/AbstractField.java
@@ -0,0 +1,922 @@
+/* *************************************************************************
+
+ IT Mill Toolkit
+
+ Development of Browser User Intarfaces 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/license.txt. Use of this product might
+ require purchasing a commercial license from IT Mill Ltd. For guidelines
+ on usage, see license/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.lang.reflect.Method;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.LinkedList;
+
+import com.itmill.toolkit.data.Buffered;
+import com.itmill.toolkit.data.Property;
+import com.itmill.toolkit.data.Validatable;
+import com.itmill.toolkit.data.Validator;
+import com.itmill.toolkit.terminal.ErrorMessage;
+import com.itmill.toolkit.terminal.PaintException;
+import com.itmill.toolkit.terminal.PaintTarget;
+
+/**
+ * <p>
+ * Abstract field component for implementing buffered property editors. The
+ * field may hold an internal value, or it may be connected to any data source
+ * that implements the {@link com.itmill.toolkit.data.Property}interface.
+ * <code>AbstractField</code> implements that interface itself, too, so
+ * accessing the Property value represented by it is straightforward.
+ * </p>
+ *
+ * <p>
+ * AbstractField also provides the {@link com.itmill.toolkit.data.Buffered}
+ * interface for buffering the data source value. By default the Field is in
+ * write through-mode and {@link #setWriteThrough(boolean)}should be called to
+ * enable buffering.
+ * </p>
+ *
+ * <p>
+ * The class also supports {@link com.itmill.toolkit.data.Validator validators}
+ * to make sure the value contained in the field is valid.
+ * </p>
+ *
+ * @author IT Mill Ltd.
+ * @version
+ * @VERSION@
+ * @since 3.0
+ */
+public abstract class AbstractField extends AbstractComponent implements Field,
+ Property.ReadOnlyStatusChangeNotifier {
+
+ /* Private members ************************************************* */
+
+ private boolean delayedFocus;
+
+ /** Value of the datafield */
+ private Object value;
+
+ /** Connected data-source. */
+ private Property dataSource = null;
+
+ /** The list of validators. */
+ private LinkedList validators = null;
+
+ /** Auto commit mode. */
+ private boolean writeTroughMode = true;
+
+ /** Read the value from data-source, when it is not modified. */
+ private boolean readTroughMode = true;
+
+ /** Is the field modified but not committed. */
+ private boolean modified = false;
+
+ /** Current source exception */
+ private Buffered.SourceException currentBufferedSourceException = null;
+
+ /** Are the invalid values alloved in fields ? */
+ private boolean invalidAllowed = true;
+
+ /** Are the invalid values committed */
+ private boolean invalidCommitted = false;
+
+ /** The tab order number of this field */
+ private int tabIndex = 0;
+
+ /** Unique focusable id */
+ private long focusableId = -1;
+
+ /** Required field */
+ private boolean required = false;
+
+ /* Component basics ************************************************ */
+
+ public AbstractField() {
+ this.focusableId = Window.getNewFocusableId(this);
+ }
+
+ /*
+ * Paint the field. Don't add a JavaDoc comment here, we use the default
+ * documentation from the implemented interface.
+ */
+ public void paintContent(PaintTarget target) throws PaintException {
+
+ // Focus control id
+ if (this.focusableId > 0) {
+ target.addAttribute("focusid", this.focusableId);
+ }
+
+ // The tab ordering number
+ if (this.tabIndex > 0)
+ target.addAttribute("tabindex", this.tabIndex);
+
+ // If the field is modified, but not committed, set modified attribute
+ if (isModified())
+ target.addAttribute("modified", true);
+
+ // Add required attribute
+ if (isRequired())
+ target.addAttribute("required", true);
+
+ }
+
+ /*
+ * Gets the field type Don't add a JavaDoc comment here, we use the default
+ * documentation from the implemented interface.
+ */
+ public abstract Class getType();
+
+ /**
+ * The abstract field is read only also if the data source is in readonly
+ * mode.
+ */
+ public boolean isReadOnly() {
+ return super.isReadOnly()
+ || (dataSource != null && dataSource.isReadOnly());
+ }
+
+ /**
+ * Change the readonly state and throw read-only status change events.
+ *
+ * @see com.itmill.toolkit.ui.Component#setReadOnly(boolean)
+ */
+ public void setReadOnly(boolean readOnly) {
+ super.setReadOnly(readOnly);
+ fireReadOnlyStatusChange();
+ }
+
+ /* Buffering ******************************************************* */
+
+ public boolean isInvalidCommitted() {
+ return invalidCommitted;
+ }
+
+ public void setInvalidCommitted(boolean isCommitted) {
+ this.invalidCommitted = isCommitted;
+ }
+
+ /*
+ * Save the current value to the data source Don't add a JavaDoc comment
+ * here, we use the default documentation from the implemented interface.
+ */
+ public void commit() throws Buffered.SourceException {
+ if (dataSource != null && (isInvalidCommitted() || isValid())
+ && !dataSource.isReadOnly()) {
+ Object newValue = getValue();
+ try {
+
+ // Commit the value to datasource
+ dataSource.setValue(newValue);
+
+ } catch (Throwable e) {
+
+ // Set the buffering state
+ currentBufferedSourceException = new Buffered.SourceException(
+ this, e);
+ requestRepaint();
+
+ // Throw the source exception
+ throw currentBufferedSourceException;
+ }
+ }
+
+ boolean repaintNeeded = false;
+
+ // The abstract field is not modified anymore
+ if (modified) {
+ modified = false;
+ repaintNeeded = true;
+ }
+
+ // If successful, remove set the buffering state to be ok
+ if (currentBufferedSourceException != null) {
+ currentBufferedSourceException = null;
+ repaintNeeded = true;
+ }
+
+ if (repaintNeeded)
+ requestRepaint();
+ }
+
+ /*
+ * Update the value from the data source. Don't add a JavaDoc comment here,
+ * we use the default documentation from the implemented interface.
+ */
+ public void discard() throws Buffered.SourceException {
+ if (dataSource != null) {
+
+ // Get the correct value from datasource
+ Object newValue;
+ try {
+
+ // Discard buffer by overwriting from datasource
+ newValue = dataSource.getValue();
+
+ // If successful, remove set the buffering state to be ok
+ if (currentBufferedSourceException != null) {
+ currentBufferedSourceException = null;
+ requestRepaint();
+ }
+ } catch (Throwable e) {
+
+ // Set the buffering state
+ currentBufferedSourceException = new Buffered.SourceException(
+ this, e);
+ requestRepaint();
+
+ // Throw the source exception
+ throw currentBufferedSourceException;
+ }
+
+ boolean wasModified = isModified();
+ modified = false;
+
+ // If the new value differs from the previous one
+ if ((newValue == null && value != null)
+ || (newValue != null && !newValue.equals(value))) {
+ setInternalValue(newValue);
+ fireValueChange();
+ }
+
+ // If the value did not change, but the modification status did
+ else if (wasModified) {
+ requestRepaint();
+ }
+ }
+ }
+
+ /*
+ * Has the field been modified since the last commit()? Don't add a JavaDoc
+ * comment here, we use the default documentation from the implemented
+ * interface.
+ */
+ public boolean isModified() {
+ return modified;
+ }
+
+ /*
+ * Test if the field is in write-through mode. Don't add a JavaDoc comment
+ * here, we use the default documentation from the implemented interface.
+ */
+ public boolean isWriteThrough() {
+ return writeTroughMode;
+ }
+
+ /*
+ * Set the field's write-through mode to the specified status Don't add a
+ * JavaDoc comment here, we use the default documentation from the
+ * implemented interface.
+ */
+ public void setWriteThrough(boolean writeTrough)
+ throws Buffered.SourceException {
+ if (writeTroughMode == writeTrough)
+ return;
+ writeTroughMode = writeTrough;
+ if (writeTroughMode)
+ commit();
+ }
+
+ /*
+ * Test if the field is in read-through mode. Don't add a JavaDoc comment
+ * here, we use the default documentation from the implemented interface.
+ */
+ public boolean isReadThrough() {
+ return readTroughMode;
+ }
+
+ /*
+ * Set the field's read-through mode to the specified status Don't add a
+ * JavaDoc comment here, we use the default documentation from the
+ * implemented interface.
+ */
+ public void setReadThrough(boolean readTrough)
+ throws Buffered.SourceException {
+ if (readTroughMode == readTrough)
+ return;
+ readTroughMode = readTrough;
+ if (!isModified() && readTroughMode && dataSource != null) {
+ setInternalValue(dataSource.getValue());
+ fireValueChange();
+ }
+ }
+
+ /* Property interface implementation ******************************* */
+
+ /**
+ * Returns the value of the Property in human readable textual format.
+ *
+ * @return <code>String</code> representation of the value stored in the
+ * Property
+ */
+ public String toString() {
+ Object value = getValue();
+ if (value == null)
+ return null;
+ return getValue().toString();
+ }
+
+ /**
+ * Gets the current value of the field. This is the visible, modified and
+ * possible invalid value the user have entered to the field. In the
+ * read-through mode, the abstract buffer is also updated and validation is
+ * performed.
+ *
+ * @return the current value of the field
+ */
+ public Object getValue() {
+
+ // Give the value from abstract buffers if the field if possible
+ if (dataSource == null || !isReadThrough() || isModified())
+ return value;
+
+ Object newValue = dataSource.getValue();
+ if ((newValue == null && value != null)
+ || (newValue != null && !newValue.equals(value))) {
+ setInternalValue(newValue);
+ fireValueChange();
+ }
+
+ return newValue;
+ }
+
+ /**
+ * Set the value of the field.
+ *
+ * @param newValue
+ * New value of the field.
+ */
+ public void setValue(Object newValue) throws Property.ReadOnlyException,
+ Property.ConversionException {
+
+ if ((newValue == null && value != null)
+ || (newValue != null && !newValue.equals(value))) {
+
+ // Read only fields can not be changed
+ if (isReadOnly())
+ throw new Property.ReadOnlyException();
+
+ // If invalid values are not allowed, the value must be checked
+ if (!isInvalidAllowed()) {
+ Collection v = getValidators();
+ if (v != null)
+ for (Iterator i = v.iterator(); i.hasNext();)
+ ((Validator) i.next()).validate(newValue);
+ }
+
+ // Change the value
+ setInternalValue(newValue);
+ modified = dataSource != null;
+
+ // In write trough mode , try to commit
+ if (isWriteThrough() && dataSource != null
+ && (isInvalidCommitted() || isValid())) {
+ try {
+
+ // Commit the value to datasource
+ dataSource.setValue(newValue);
+
+ // The buffer is now unmodified
+ modified = false;
+
+ } catch (Throwable e) {
+
+ // Set the buffering state
+ currentBufferedSourceException = new Buffered.SourceException(
+ this, e);
+ requestRepaint();
+
+ // Throw the source exception
+ throw currentBufferedSourceException;
+ }
+ }
+
+ // If successful, remove set the buffering state to be ok
+ if (currentBufferedSourceException != null) {
+ currentBufferedSourceException = null;
+ requestRepaint();
+ }
+
+ // Fire value change
+ fireValueChange();
+ }
+ }
+
+ /* External data source ******************************************** */
+
+ /**
+ * Gets the current data source of the field, if any.
+ *
+ * @return The current data source as a Property, or <code>null</code> if
+ * none defined.
+ */
+ public Property getPropertyDataSource() {
+ return dataSource;
+ }
+
+ /**
+ * <p>
+ * Sets the specified Property as the data source for the field. All
+ * uncommitted changes to the field are discarded and the value is refreshed
+ * from the new data source.
+ * </p>
+ *
+ * <p>
+ * If the datasource has any validators, the same validators are added to
+ * the field. Because the default behavior of the field is to allow invalid
+ * values, but not to allow committing them, this only adds visual error
+ * messages to fields and do not allow committing them as long as the value
+ * is invalid. After the value is valid, the error message is not shown and
+ * the commit can be done normally.
+ * </p>
+ *
+ * @param newDataSource
+ * the new data source Property
+ */
+ public void setPropertyDataSource(Property newDataSource) {
+
+ // Save the old value
+ Object oldValue = value;
+
+ // Discard all changes to old datasource
+ try {
+ discard();
+ } catch (Buffered.SourceException ignored) {
+ }
+
+ // Stop listening the old data source changes
+ if (dataSource != null
+ && Property.ValueChangeNotifier.class
+ .isAssignableFrom(dataSource.getClass()))
+ ((Property.ValueChangeNotifier) dataSource).removeListener(this);
+
+ // Set the new data source
+ dataSource = newDataSource;
+
+ // Get the value from source
+ try {
+ if (dataSource != null)
+ setInternalValue(dataSource.getValue());
+ modified = false;
+ } catch (Throwable e) {
+ currentBufferedSourceException = new Buffered.SourceException(this,
+ e);
+ modified = true;
+ }
+
+ // Listen the new data source if possible
+ if (dataSource instanceof Property.ValueChangeNotifier)
+ ((Property.ValueChangeNotifier) dataSource).addListener(this);
+
+ // Copy the validators from the data source
+ if (dataSource instanceof Validatable) {
+ Collection validators = ((Validatable) dataSource).getValidators();
+ if (validators != null)
+ for (Iterator i = validators.iterator(); i.hasNext();)
+ addValidator((Validator) i.next());
+ }
+
+ // Fire value change if the value has changed
+ if ((value != oldValue)
+ && ((value != null && !value.equals(oldValue)) || value == null))
+ fireValueChange();
+ }
+
+ /* Validation ****************************************************** */
+
+ /**
+ * Adds a new validator for the field's value. All validators added to a
+ * field are checked each time the its value changes.
+ *
+ * @param validator
+ * the new validator to be added
+ */
+ public void addValidator(Validator validator) {
+ if (validators == null)
+ validators = new LinkedList();
+ validators.add(validator);
+ }
+
+ /**
+ * Gets the validators of the field.
+ *
+ * @return Unmodifiable collection that holds all validators for the field.
+ */
+ public Collection getValidators() {
+ if (validators == null || validators.isEmpty())
+ return null;
+ return Collections.unmodifiableCollection(validators);
+ }
+
+ /**
+ * Removes a validator from the field.
+ *
+ * @param validator
+ * the validator to remove
+ */
+ public void removeValidator(Validator validator) {
+ if (validators != null)
+ validators.remove(validator);
+ }
+
+ /**
+ * Tests the current value against all registered validators.
+ *
+ * @return <code>true</code> if all registered validators claim that the
+ * current value is valid, <code>false</code> otherwise
+ */
+ public boolean isValid() {
+
+ if (validators == null)
+ return true;
+
+ Object value = getValue();
+ for (Iterator i = validators.iterator(); i.hasNext();)
+ if (!((Validator) i.next()).isValid(value))
+ return false;
+
+ return true;
+ }
+
+ public void validate() throws Validator.InvalidValueException {
+
+ // If there is no validator, there can not be any errors
+ if (validators == null)
+ return;
+
+ // Initialize temps
+ Validator.InvalidValueException firstError = null;
+ LinkedList errors = null;
+ Object value = getValue();
+
+ // Get all the validation errors
+ for (Iterator i = validators.iterator(); i.hasNext();)
+ try {
+ ((Validator) i.next()).validate(value);
+ } catch (Validator.InvalidValueException e) {
+ if (firstError == null)
+ firstError = e;
+ else {
+ if (errors == null) {
+ errors = new LinkedList();
+ errors.add(firstError);
+ }
+ errors.add(e);
+ }
+ }
+
+ // If there were no error
+ if (firstError == null)
+ return;
+
+ // If only one error occurred, throw it forwards
+ if (errors == null)
+ throw firstError;
+
+ // Create composite validator
+ Validator.InvalidValueException[] exceptions = new Validator.InvalidValueException[errors
+ .size()];
+ int index = 0;
+ for (Iterator i = errors.iterator(); i.hasNext();)
+ exceptions[index++] = (Validator.InvalidValueException) i.next();
+
+ throw new Validator.InvalidValueException(null, exceptions);
+ }
+
+ /**
+ * Fields allow invalid values by default. In most cases this is wanted,
+ * because the field otherwise visually forget the user input immediately.
+ *
+ * @see com.itmill.toolkit.data.Validatable#isInvalidAllowed()
+ *
+ * @return true iff the invalid values are allowed.
+ */
+ public boolean isInvalidAllowed() {
+ return invalidAllowed;
+ }
+
+ /**
+ * Fields allow invalid values by default. In most cases this is wanted,
+ * because the field otherwise visually forget the user input immediately.
+ * In common setting where the user wants to assure the correctness of the
+ * datasource, but allow temporarily invalid contents in the field, the user
+ * should add the validators to datasource, that should not allow invalid
+ * values. The validators are automatically copied to the field when the
+ * datasource is set.
+ *
+ * @see com.itmill.toolkit.data.Validatable#setInvalidAllowed(boolean)
+ */
+ public void setInvalidAllowed(boolean invalidAllowed)
+ throws UnsupportedOperationException {
+ this.invalidAllowed = invalidAllowed;
+ }
+
+ /**
+ * Error messages shown by the fields are composites of the error message
+ * thrown by the superclasses (that is the component error message),
+ * validation errors and buffered source errors.
+ *
+ * @see com.itmill.toolkit.ui.AbstractComponent#getErrorMessage()
+ */
+ public ErrorMessage getErrorMessage() {
+ ErrorMessage superError = super.getErrorMessage();
+ return superError;
+ /*
+ * TODO: Check the logic of this ErrorMessage validationError = null;
+ * try { validate(); } catch (Validator.InvalidValueException e) {
+ * validationError = e; }
+ *
+ * if (superError == null && validationError == null &&
+ * currentBufferedSourceException == null) return null; // Throw
+ * combination of the error types return new CompositeErrorMessage( new
+ * ErrorMessage[] { superError, validationError,
+ * currentBufferedSourceException });
+ */
+
+ }
+
+ /* Value change events ****************************************** */
+
+ private static final Method VALUE_CHANGE_METHOD;
+
+ static {
+ try {
+ VALUE_CHANGE_METHOD = Property.ValueChangeListener.class
+ .getDeclaredMethod("valueChange",
+ new Class[] { Property.ValueChangeEvent.class });
+ } catch (java.lang.NoSuchMethodException e) {
+ // This should never happen
+ throw new java.lang.RuntimeException();
+ }
+ }
+
+ /*
+ * Add a value change listener for the field. Don't add a JavaDoc comment
+ * here, we use the default documentation from the implemented interface.
+ */
+ public void addListener(Property.ValueChangeListener listener) {
+ addListener(AbstractField.ValueChangeEvent.class, listener,
+ VALUE_CHANGE_METHOD);
+ }
+
+ /*
+ * Remove a value change listener from the field. Don't add a JavaDoc
+ * comment here, we use the default documentation from the implemented
+ * interface.
+ */
+ public void removeListener(Property.ValueChangeListener listener) {
+ removeListener(AbstractField.ValueChangeEvent.class, listener,
+ VALUE_CHANGE_METHOD);
+ }
+
+ /**
+ * Emit a value change event. The value contained in the field is validated
+ * before the event is created.
+ */
+ protected void fireValueChange() {
+ fireEvent(new AbstractField.ValueChangeEvent(this));
+ requestRepaint();
+ }
+
+ /* Read-only status change events *************************************** */
+
+ private static final Method READ_ONLY_STATUS_CHANGE_METHOD;
+
+ static {
+ try {
+ READ_ONLY_STATUS_CHANGE_METHOD = Property.ReadOnlyStatusChangeListener.class
+ .getDeclaredMethod(
+ "readOnlyStatusChange",
+ new Class[] { Property.ReadOnlyStatusChangeEvent.class });
+ } catch (java.lang.NoSuchMethodException e) {
+ // This should never happen
+ throw new java.lang.RuntimeException();
+ }
+ }
+
+ /**
+ * An <code>Event</code> object specifying the Property whose read-only
+ * status has changed.
+ *
+ * @author IT Mill Ltd.
+ * @version
+ * @VERSION@
+ * @since 3.0
+ */
+ public class ReadOnlyStatusChangeEvent extends Component.Event implements
+ Property.ReadOnlyStatusChangeEvent {
+
+ /**
+ * Serial generated by eclipse.
+ */
+ private static final long serialVersionUID = 3258688823264161846L;
+
+ /**
+ * New instance of text change event
+ *
+ * @param source
+ * Source of the event.
+ */
+ public ReadOnlyStatusChangeEvent(AbstractField source) {
+ super(source);
+ }
+
+ /**
+ * Property where the event occurred
+ *
+ * @return Source of the event.
+ */
+ public Property getProperty() {
+ return (Property) getSource();
+ }
+ }
+
+ /*
+ * Add a read-only status change listener for the field. Don't add a JavaDoc
+ * comment here, we use the default documentation from the implemented
+ * interface.
+ */
+ public void addListener(Property.ReadOnlyStatusChangeListener listener) {
+ addListener(Property.ReadOnlyStatusChangeEvent.class, listener,
+ READ_ONLY_STATUS_CHANGE_METHOD);
+ }
+
+ /*
+ * Removes a read-only status change listener from the field. Don't add a
+ * JavaDoc comment here, we use the default documentation from the
+ * implemented interface.
+ */
+ public void removeListener(Property.ReadOnlyStatusChangeListener listener) {
+ removeListener(Property.ReadOnlyStatusChangeEvent.class, listener,
+ READ_ONLY_STATUS_CHANGE_METHOD);
+ }
+
+ /**
+ * Emit a read-only status change event. The value contained in the field is
+ * validated before the event is created.
+ */
+ protected void fireReadOnlyStatusChange() {
+ fireEvent(new AbstractField.ReadOnlyStatusChangeEvent(this));
+ }
+
+ /**
+ * This method listens to data source value changes and passes the changes
+ * forwards.
+ *
+ * @param event
+ * the value change event telling the data source contents have
+ * changed
+ */
+ public void valueChange(Property.ValueChangeEvent event) {
+ if (isReadThrough() || !isModified())
+ fireValueChange();
+ }
+
+ /** Ask the terminal to place the cursor to this field. */
+ public void focus() {
+ Window w = getWindow();
+ if (w != null) {
+ w.setFocusedComponent(this);
+ } else {
+ this.delayedFocus = true;
+ }
+ }
+
+ /**
+ * Create abstract field by the type of the property.
+ *
+ * <p>
+ * This returns most suitable field type for editing property of given type
+ * </p>
+ *
+ * @param propertyType
+ * Type of the property, that needs to be edited.
+ */
+ public static AbstractField constructField(Class propertyType) {
+
+ // Null typed properties can not be edited
+ if (propertyType == null)
+ return null;
+
+ // Date field
+ if (Date.class.isAssignableFrom(propertyType)) {
+ return new DateField();
+ }
+
+ // Boolean field
+ if (Boolean.class.isAssignableFrom(propertyType)) {
+ Button button = new Button("");
+ button.setSwitchMode(true);
+ button.setImmediate(false);
+ return button;
+ }
+
+ // Text field is used by default
+ return new TextField();
+ }
+
+ /**
+ * Get the tab index of this field. The tab index property is used to
+ * specify the natural tab ordering of fields.
+ *
+ * @return Tab index of this field. Negative value means unspecified.
+ */
+ public int getTabIndex() {
+ return tabIndex;
+ }
+
+ /**
+ * Get the tab index of this field. The tab index property is used to
+ * specify the natural tab ordering of fields.
+ *
+ * @param tabIndex
+ * The tab order of this component. Negative value means
+ * unspecified.
+ */
+ public void setTabIndex(int tabIndex) {
+ this.tabIndex = tabIndex;
+ }
+
+ /**
+ * Set the internal field value. This is purely used by AbstractField to
+ * change the internal Field value. It does not trigger any events. It can
+ * be overriden by the inheriting classes to update all dependent variables.
+ *
+ * @param newValue
+ * The new value to be set.
+ */
+ protected void setInternalValue(Object newValue) {
+ this.value = newValue;
+ }
+
+ /**
+ * @see com.itmill.toolkit.ui.Component.Focusable#getFocusableId()
+ */
+ public long getFocusableId() {
+ return this.focusableId;
+ }
+
+ /**
+ * @see com.itmill.toolkit.ui.Component#attach()
+ */
+ public void attach() {
+ super.attach();
+ if (this.delayedFocus) {
+ this.delayedFocus = false;
+ this.focus();
+ }
+ }
+
+ /**
+ * Is this field required.
+ *
+ * Required fields must filled by the user.
+ *
+ * @return true if the
+ */
+ public boolean isRequired() {
+ return required;
+ }
+
+ /**
+ * Set the field required. Required fields must filled by the user.
+ *
+ * @param required
+ * Is the field required
+ */
+ public void setRequired(boolean required) {
+ this.required = required;
+ }
+
+ /**
+ * Free used resources.
+ */
+ public void finalize() throws Throwable {
+ if (focusableId > -1) {
+ Window.removeFocusableId(focusableId);
+ }
+ super.finalize();
+ }
+} \ No newline at end of file