]> source.dussan.org Git - vaadin-framework.git/commitdiff
Fixes #846
authorJoonas Lehtinen <joonas.lehtinen@itmill.com>
Wed, 14 May 2008 16:03:25 +0000 (16:03 +0000)
committerJoonas Lehtinen <joonas.lehtinen@itmill.com>
Wed, 14 May 2008 16:03:25 +0000 (16:03 +0000)
svn changeset:4488/svn branch:trunk

src/com/itmill/toolkit/ui/AbstractField.java
src/com/itmill/toolkit/ui/AbstractSelect.java
src/com/itmill/toolkit/ui/Button.java
src/com/itmill/toolkit/ui/DateField.java
src/com/itmill/toolkit/ui/Select.java
src/com/itmill/toolkit/ui/Slider.java
src/com/itmill/toolkit/ui/TextField.java

index 0b309ab56281f83acd7a3150559b5668801831e5..a04402e6e7fb80b43f56fb029b2f83513b31b7c7 100644 (file)
@@ -10,12 +10,14 @@ import java.util.Collections;
 import java.util.Date;
 import java.util.Iterator;
 import java.util.LinkedList;
+import java.util.Map;
 
 import com.itmill.toolkit.Application;
 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.CompositeErrorMessage;
 import com.itmill.toolkit.terminal.ErrorMessage;
 import com.itmill.toolkit.terminal.PaintException;
 import com.itmill.toolkit.terminal.PaintTarget;
@@ -47,915 +49,934 @@ import com.itmill.toolkit.terminal.PaintTarget;
  * @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;
-
-    /**
-     * Reads 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;
-
-    /**
-     * Required field.
-     */
-    private boolean required = false;
-
-    /* Component basics ************************************************ */
-
-    /*
-     * Paints 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 {
-
-        // The tab ordering number
-        if (tabIndex > 0) {
-            target.addAttribute("tabindex", tabIndex);
-        }
-
-        // If the field is modified, but not committed, set modified attribute
-        if (isModified()) {
-            target.addAttribute("modified", true);
-        }
-
-        // Adds the 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());
-    }
-
-    /**
-     * Changes 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();
-    }
-
-    /**
-     * Tests if the invalid data is committed to datasource.
-     * 
-     * @see com.itmill.toolkit.data.BufferedValidatable#isInvalidCommitted()
-     */
-    public boolean isInvalidCommitted() {
-        return invalidCommitted;
-    }
-
-    /**
-     * Sets if the invalid data should be committed to datasource.
-     * 
-     * @see com.itmill.toolkit.data.BufferedValidatable#setInvalidCommitted(boolean)
-     */
-    public void setInvalidCommitted(boolean isCommitted) {
-        invalidCommitted = isCommitted;
-    }
-
-    /*
-     * Saves 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()) {
-            final Object newValue = getValue();
-            try {
-
-                // Commits the value to datasource.
-                dataSource.setValue(newValue);
-
-            } catch (final Throwable e) {
-
-                // Sets the buffering state.
-                currentBufferedSourceException = new Buffered.SourceException(
-                        this, e);
-                requestRepaint();
-
-                // Throws 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();
-        }
-    }
-
-    /*
-     * Updates 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) {
-
-            // Gets the correct value from datasource
-            Object newValue;
-            try {
-
-                // Discards 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 (final Throwable e) {
-
-                // Sets the buffering state
-                currentBufferedSourceException = new Buffered.SourceException(
-                        this, e);
-                requestRepaint();
-
-                // Throws the source exception
-                throw currentBufferedSourceException;
-            }
-
-            final 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(false);
-            }
-
-            // 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;
-    }
-
-    /*
-     * Tests 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;
-    }
-
-    /*
-     * Sets 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();
-        }
-    }
-
-    /*
-     * Tests 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;
-    }
-
-    /*
-     * Sets 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(false);
-        }
-    }
-
-    /* Property interface implementation ******************************* */
-
-    /**
-     * Returns the value of the Property in human readable textual format.
-     * 
-     * @see java.lang.Object#toString()
-     */
-    public String toString() {
-        final 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;
-        }
-
-        final Object newValue = dataSource.getValue();
-        if ((newValue == null && value != null)
-                || (newValue != null && !newValue.equals(value))) {
-            setInternalValue(newValue);
-            fireValueChange(false);
-        }
-
-        return newValue;
-    }
-
-    /**
-     * Sets the value of the field.
-     * 
-     * @param newValue
-     *                the New value of the field.
-     * @throws Property.ReadOnlyException
-     * @throws Property.ConversionException
-     */
-    public void setValue(Object newValue) throws Property.ReadOnlyException,
-            Property.ConversionException {
-        setValue(newValue, false);
-    }
-
-    /**
-     * Sets the value of the field.
-     * 
-     * @param newValue
-     *                the New value of the field.
-     * @param repaintIsNotNeeded
-     *                True iff caller is sure that repaint is not needed.
-     * @throws Property.ReadOnlyException
-     * @throws Property.ConversionException
-     */
-    protected void setValue(Object newValue, boolean repaintIsNotNeeded)
-            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()) {
-                final Collection v = getValidators();
-                if (v != null) {
-                    for (final Iterator i = v.iterator(); i.hasNext();) {
-                        ((Validator) i.next()).validate(newValue);
-                    }
-                }
-            }
-
-            // Changes the value
-            setInternalValue(newValue);
-            modified = dataSource != null;
-
-            // In write trough mode , try to commit
-            if (isWriteThrough() && dataSource != null
-                    && (isInvalidCommitted() || isValid())) {
-                try {
-
-                    // Commits the value to datasource
-                    dataSource.setValue(newValue);
-
-                    // The buffer is now unmodified
-                    modified = false;
-
-                } catch (final Throwable e) {
-
-                    // Sets the buffering state
-                    currentBufferedSourceException = new Buffered.SourceException(
-                            this, e);
-                    requestRepaint();
-
-                    // Throws the source exception
-                    throw currentBufferedSourceException;
-                }
-            }
-
-            // If successful, remove set the buffering state to be ok
-            if (currentBufferedSourceException != null) {
-                currentBufferedSourceException = null;
-                requestRepaint();
-            }
-
-            // Fires the value change
-            fireValueChange(repaintIsNotNeeded);
-        }
-    }
-
-    /* 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) {
-
-        // Saves the old value
-        final Object oldValue = value;
-
-        // Discards all changes to old datasource
-        try {
-            discard();
-        } catch (final Buffered.SourceException ignored) {
-        }
-
-        // Stops listening the old data source changes
-        if (dataSource != null
-                && Property.ValueChangeNotifier.class
-                        .isAssignableFrom(dataSource.getClass())) {
-            ((Property.ValueChangeNotifier) dataSource).removeListener(this);
-        }
-
-        // Sets the new data source
-        dataSource = newDataSource;
-
-        // Gets the value from source
-        try {
-            if (dataSource != null) {
-                setInternalValue(dataSource.getValue());
-            }
-            modified = false;
-        } catch (final Throwable e) {
-            currentBufferedSourceException = new Buffered.SourceException(this,
-                    e);
-            modified = true;
-        }
-
-        // Listens 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) {
-            final Collection validators = ((Validatable) dataSource)
-                    .getValidators();
-            if (validators != null) {
-                for (final Iterator i = validators.iterator(); i.hasNext();) {
-                    addValidator((Validator) i.next());
-                }
-            }
-        }
-
-        // Fires value change if the value has changed
-        if ((value != oldValue)
-                && ((value != null && !value.equals(oldValue)) || value == null)) {
-            fireValueChange(false);
-        }
-    }
-
-    /* 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 the Unmodifiable collection that holds all validators for the
-     *         field.
-     */
-    public Collection getValidators() {
-        if (validators == null || validators.isEmpty()) {
-            return null;
-        }
-        return Collections.unmodifiableCollection(validators);
-    }
-
-    /**
-     * Removes the 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;
-        }
-
-        final Object value = getValue();
-        for (final Iterator i = validators.iterator(); i.hasNext();) {
-            if (!((Validator) i.next()).isValid(value)) {
-                return false;
-            }
-        }
-
-        return true;
-    }
-
-    /**
-     * Checks the validity of the validatable
-     * 
-     * @see com.itmill.toolkit.data.Validatable#validate()
-     */
-    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;
-        final Object value = getValue();
-
-        // Gets all the validation errors
-        for (final Iterator i = validators.iterator(); i.hasNext();) {
-            try {
-                ((Validator) i.next()).validate(value);
-            } catch (final 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;
-        }
-
-        // Creates composite validator
-        final Validator.InvalidValueException[] exceptions = new Validator.InvalidValueException[errors
-                .size()];
-        int index = 0;
-        for (final 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.
-     * 
-     * @return true iff the invalid values are allowed.
-     * @see com.itmill.toolkit.data.Validatable#isInvalidAllowed()
-     */
-    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.
-     * <p>
-     * 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.
-     * </p>
-     * 
-     * @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() {
-        final 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 (final java.lang.NoSuchMethodException e) {
-            // This should never happen
-            throw new java.lang.RuntimeException();
-        }
-    }
-
-    /*
-     * Adds 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);
-    }
-
-    /*
-     * Removes 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);
-    }
-
-    /**
-     * Emits the value change event. The value contained in the field is
-     * validated before the event is created.
-     */
-    protected void fireValueChange(boolean repaintIsNotNeeded) {
-        fireEvent(new AbstractField.ValueChangeEvent(this));
-        if (!repaintIsNotNeeded) {
-            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 (final 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
-         *                the Source of the event.
-         */
-        public ReadOnlyStatusChangeEvent(AbstractField source) {
-            super(source);
-        }
-
-        /**
-         * Property where the event occurred.
-         * 
-         * @return the Source of the event.
-         */
-        public Property getProperty() {
-            return (Property) getSource();
-        }
-    }
-
-    /*
-     * Adds 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);
-    }
-
-    /**
-     * Emits the 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(false);
-        }
-    }
-
-    /**
-     * Asks the terminal to place the cursor to this field.
-     */
-    public void focus() {
-        final Application app = getApplication();
-        if (app != null) {
-            app.setFocusedComponent(this);
-        } else {
-            delayedFocus = true;
-        }
-    }
-
-    /**
-     * Creates abstract field by the type of the property.
-     * 
-     * <p>
-     * This returns most suitable field type for editing property of given type.
-     * </p>
-     * 
-     * @param propertyType
-     *                the 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)) {
-            final Button button = new Button("");
-            button.setSwitchMode(true);
-            button.setImmediate(false);
-            return button;
-        }
-
-        // Text field is used by default
-        return new TextField();
-    }
-
-    /**
-     * Gets the tab index of this field. The tab index property is used to
-     * specify the natural tab ordering of fields.
-     * 
-     * @return the Tab index of this field. Negative value means unspecified.
-     */
-    public int getTabIndex() {
-        return tabIndex;
-    }
-
-    /**
-     * Gets 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;
-    }
-
-    /**
-     * Sets 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) {
-        value = newValue;
-    }
-
-    /**
-     * Notifies the component that it is connected to an application.
-     * 
-     * @see com.itmill.toolkit.ui.Component#attach()
-     */
-    public void attach() {
-        super.attach();
-        if (delayedFocus) {
-            delayedFocus = false;
-            focus();
-        }
-    }
-
-    /**
-     * Is this field required. Required fields must filled by the user.
-     * 
-     * @return <code>true</code> if the field is required .otherwise
-     *         <code>false</code>.
-     */
-    public boolean isRequired() {
-        return required;
-    }
-
-    /**
-     * Sets the field required. Required fields must filled by the user.
-     * 
-     * @param required
-     *                Is the field required.
-     */
-    public void setRequired(boolean required) {
-        this.required = required;
-    }
+               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;
+
+       /**
+        * Reads 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;
+
+       /**
+        * Required field.
+        */
+       private boolean required = false;
+
+       /* Component basics ************************************************ */
+
+       /*
+        * Paints 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 {
+
+               // The tab ordering number
+               if (tabIndex > 0) {
+                       target.addAttribute("tabindex", tabIndex);
+               }
+
+               // If the field is modified, but not committed, set modified attribute
+               if (isModified()) {
+                       target.addAttribute("modified", true);
+               }
+
+               // Adds the 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());
+       }
+
+       /**
+        * Changes 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();
+       }
+
+       /**
+        * Tests if the invalid data is committed to datasource.
+        * 
+        * @see com.itmill.toolkit.data.BufferedValidatable#isInvalidCommitted()
+        */
+       public boolean isInvalidCommitted() {
+               return invalidCommitted;
+       }
+
+       /**
+        * Sets if the invalid data should be committed to datasource.
+        * 
+        * @see com.itmill.toolkit.data.BufferedValidatable#setInvalidCommitted(boolean)
+        */
+       public void setInvalidCommitted(boolean isCommitted) {
+               invalidCommitted = isCommitted;
+       }
+
+       /*
+        * Saves 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()) {
+                       final Object newValue = getValue();
+                       try {
+
+                               // Commits the value to datasource.
+                               dataSource.setValue(newValue);
+
+                       } catch (final Throwable e) {
+
+                               // Sets the buffering state.
+                               currentBufferedSourceException = new Buffered.SourceException(
+                                               this, e);
+                               requestRepaint();
+
+                               // Throws 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();
+               }
+       }
+
+       /*
+        * Updates 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) {
+
+                       // Gets the correct value from datasource
+                       Object newValue;
+                       try {
+
+                               // Discards 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 (final Throwable e) {
+
+                               // Sets the buffering state
+                               currentBufferedSourceException = new Buffered.SourceException(
+                                               this, e);
+                               requestRepaint();
+
+                               // Throws the source exception
+                               throw currentBufferedSourceException;
+                       }
+
+                       final 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(false);
+                       }
+
+                       // 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;
+       }
+
+       /*
+        * Tests 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;
+       }
+
+       /*
+        * Sets 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();
+               }
+       }
+
+       /*
+        * Tests 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;
+       }
+
+       /*
+        * Sets 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(false);
+               }
+       }
+
+       /* Property interface implementation ******************************* */
+
+       /**
+        * Returns the value of the Property in human readable textual format.
+        * 
+        * @see java.lang.Object#toString()
+        */
+       public String toString() {
+               final 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;
+               }
+
+               final Object newValue = dataSource.getValue();
+               if ((newValue == null && value != null)
+                               || (newValue != null && !newValue.equals(value))) {
+                       setInternalValue(newValue);
+                       fireValueChange(false);
+               }
+
+               return newValue;
+       }
+
+       /**
+        * Sets the value of the field.
+        * 
+        * @param newValue
+        *            the New value of the field.
+        * @throws Property.ReadOnlyException
+        * @throws Property.ConversionException
+        */
+       public void setValue(Object newValue) throws Property.ReadOnlyException,
+                       Property.ConversionException {
+               setValue(newValue, false);
+       }
+
+       /**
+        * Sets the value of the field.
+        * 
+        * @param newValue
+        *            the New value of the field.
+        * @param repaintIsNotNeeded
+        *            True iff caller is sure that repaint is not needed.
+        * @throws Property.ReadOnlyException
+        * @throws Property.ConversionException
+        */
+       protected void setValue(Object newValue, boolean repaintIsNotNeeded)
+                       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()) {
+                               final Collection v = getValidators();
+                               if (v != null) {
+                                       for (final Iterator i = v.iterator(); i.hasNext();) {
+                                               ((Validator) i.next()).validate(newValue);
+                                       }
+                               }
+                       }
+
+                       // Changes the value
+                       setInternalValue(newValue);
+                       modified = dataSource != null;
+
+                       // In write trough mode , try to commit
+                       if (isWriteThrough() && dataSource != null
+                                       && (isInvalidCommitted() || isValid())) {
+                               try {
+
+                                       // Commits the value to datasource
+                                       dataSource.setValue(newValue);
+
+                                       // The buffer is now unmodified
+                                       modified = false;
+
+                               } catch (final Throwable e) {
+
+                                       // Sets the buffering state
+                                       currentBufferedSourceException = new Buffered.SourceException(
+                                                       this, e);
+                                       requestRepaint();
+
+                                       // Throws the source exception
+                                       throw currentBufferedSourceException;
+                               }
+                       }
+
+                       // If successful, remove set the buffering state to be ok
+                       if (currentBufferedSourceException != null) {
+                               currentBufferedSourceException = null;
+                               requestRepaint();
+                       }
+
+                       // Fires the value change
+                       fireValueChange(repaintIsNotNeeded);
+               }
+       }
+
+       /* 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) {
+
+               // Saves the old value
+               final Object oldValue = value;
+
+               // Discards all changes to old datasource
+               try {
+                       discard();
+               } catch (final Buffered.SourceException ignored) {
+               }
+
+               // Stops listening the old data source changes
+               if (dataSource != null
+                               && Property.ValueChangeNotifier.class
+                                               .isAssignableFrom(dataSource.getClass())) {
+                       ((Property.ValueChangeNotifier) dataSource).removeListener(this);
+               }
+
+               // Sets the new data source
+               dataSource = newDataSource;
+
+               // Gets the value from source
+               try {
+                       if (dataSource != null) {
+                               setInternalValue(dataSource.getValue());
+                       }
+                       modified = false;
+               } catch (final Throwable e) {
+                       currentBufferedSourceException = new Buffered.SourceException(this,
+                                       e);
+                       modified = true;
+               }
+
+               // Listens 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) {
+                       final Collection validators = ((Validatable) dataSource)
+                                       .getValidators();
+                       if (validators != null) {
+                               for (final Iterator i = validators.iterator(); i.hasNext();) {
+                                       addValidator((Validator) i.next());
+                               }
+                       }
+               }
+
+               // Fires value change if the value has changed
+               if ((value != oldValue)
+                               && ((value != null && !value.equals(oldValue)) || value == null)) {
+                       fireValueChange(false);
+               }
+       }
+
+       /* 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 the Unmodifiable collection that holds all validators for the
+        *         field.
+        */
+       public Collection getValidators() {
+               if (validators == null || validators.isEmpty()) {
+                       return null;
+               }
+               return Collections.unmodifiableCollection(validators);
+       }
+
+       /**
+        * Removes the 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;
+               }
+
+               final Object value = getValue();
+               for (final Iterator i = validators.iterator(); i.hasNext();) {
+                       if (!((Validator) i.next()).isValid(value)) {
+                               return false;
+                       }
+               }
+
+               return true;
+       }
+
+       /**
+        * Checks the validity of the validatable
+        * 
+        * @see com.itmill.toolkit.data.Validatable#validate()
+        */
+       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;
+               final Object value = getValue();
+
+               // Gets all the validation errors
+               for (final Iterator i = validators.iterator(); i.hasNext();) {
+                       try {
+                               ((Validator) i.next()).validate(value);
+                       } catch (final 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;
+               }
+
+               // Creates composite validator
+               final Validator.InvalidValueException[] exceptions = new Validator.InvalidValueException[errors
+                               .size()];
+               int index = 0;
+               for (final 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.
+        * 
+        * @return true iff the invalid values are allowed.
+        * @see com.itmill.toolkit.data.Validatable#isInvalidAllowed()
+        */
+       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.
+        * <p>
+        * 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.
+        * </p>
+        * 
+        * @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() {
+
+               // Check validation errors
+               ErrorMessage validationError = null;
+               try {
+                       validate();
+               } catch (Validator.InvalidValueException e) {
+                       validationError = e;
+               }
+
+               // Check if there are any systems errors
+               final ErrorMessage superError = super.getErrorMessage();
+
+               // Return if there are no errors at all
+               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 (final java.lang.NoSuchMethodException e) {
+                       // This should never happen
+                       throw new java.lang.RuntimeException();
+               }
+       }
+
+       /*
+        * Adds 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);
+       }
+
+       /*
+        * Removes 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);
+       }
+
+       /**
+        * Emits the value change event. The value contained in the field is
+        * validated before the event is created.
+        */
+       protected void fireValueChange(boolean repaintIsNotNeeded) {
+               fireEvent(new AbstractField.ValueChangeEvent(this));
+               if (!repaintIsNotNeeded) {
+                       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 (final 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
+                *            the Source of the event.
+                */
+               public ReadOnlyStatusChangeEvent(AbstractField source) {
+                       super(source);
+               }
+
+               /**
+                * Property where the event occurred.
+                * 
+                * @return the Source of the event.
+                */
+               public Property getProperty() {
+                       return (Property) getSource();
+               }
+       }
+
+       /*
+        * Adds 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);
+       }
+
+       /**
+        * Emits the 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(false);
+               }
+       }
+
+       @Override
+       public void changeVariables(Object source, Map variables) {
+               super.changeVariables(source, variables);
+               if (validators != null && !validators.isEmpty()) requestRepaint();
+       }
+
+       @Override
+       public String getTag() {
+               // TODO Auto-generated method stub
+               return null;
+       }
+
+       /**
+        * Asks the terminal to place the cursor to this field.
+        */
+       public void focus() {
+               final Application app = getApplication();
+               if (app != null) {
+                       app.setFocusedComponent(this);
+               } else {
+                       delayedFocus = true;
+               }
+       }
+
+       /**
+        * Creates abstract field by the type of the property.
+        * 
+        * <p>
+        * This returns most suitable field type for editing property of given type.
+        * </p>
+        * 
+        * @param propertyType
+        *            the 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)) {
+                       final Button button = new Button("");
+                       button.setSwitchMode(true);
+                       button.setImmediate(false);
+                       return button;
+               }
+
+               // Text field is used by default
+               return new TextField();
+       }
+
+       /**
+        * Gets the tab index of this field. The tab index property is used to
+        * specify the natural tab ordering of fields.
+        * 
+        * @return the Tab index of this field. Negative value means unspecified.
+        */
+       public int getTabIndex() {
+               return tabIndex;
+       }
+
+       /**
+        * Gets 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;
+       }
+
+       /**
+        * Sets 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) {
+               value = newValue;
+       }
+
+       /**
+        * Notifies the component that it is connected to an application.
+        * 
+        * @see com.itmill.toolkit.ui.Component#attach()
+        */
+       public void attach() {
+               super.attach();
+               if (delayedFocus) {
+                       delayedFocus = false;
+                       focus();
+               }
+       }
+
+       /**
+        * Is this field required. Required fields must filled by the user.
+        * 
+        * @return <code>true</code> if the field is required .otherwise
+        *         <code>false</code>.
+        */
+       public boolean isRequired() {
+               return required;
+       }
+
+       /**
+        * Sets the field required. Required fields must filled by the user.
+        * 
+        * @param required
+        *            Is the field required.
+        */
+       public void setRequired(boolean required) {
+               this.required = required;
+               requestRepaint();
+       }
 
 }
\ No newline at end of file
index bda24b1ecf031157912df2394ddfcb3298635930..161204e0122f3a134d866a352d4554b04c4f5bfa 100644 (file)
@@ -356,7 +356,7 @@ public abstract class AbstractSelect extends AbstractField implements
      *      java.util.Map)
      */
     public void changeVariables(Object source, Map variables) {
-        // Try to set the property value
+       super.changeVariables(source, variables);
 
         // New option entered (and it is allowed)
         final String newitem = (String) variables.get("newitem");
index 06abecc826de294cccf6b13d0b692a88c52351df..0af982268f75d2087d1c7242a7ec1e6903a8bbb1 100644 (file)
@@ -152,6 +152,7 @@ public class Button extends AbstractField {
      * @param variables
      */
     public void changeVariables(Object source, Map variables) {
+       super.changeVariables(source, variables);
         if (isReadOnly()) {
             System.err.println("Button: ignoring variable change for"
                     + " read-only component, caption=" + getCaption());
index 942d95de9114ab8628423bfe0ae8a3c46c5cde3b..bd205557fd58f375fb6cd6908bfae7218082b2e4 100644 (file)
@@ -238,6 +238,7 @@ public class DateField extends AbstractField {
      * interface.
      */
     public void changeVariables(Object source, Map variables) {
+       super.changeVariables(source, variables);
 
         if (!isReadOnly()
                 && (variables.containsKey("year")
index 09e91104b860c0cbcde12ea56ddf28eb533f8f48..eaeffdc7460aadb1aa4920a4fac77deeed0bdd88 100644 (file)
@@ -321,6 +321,12 @@ public class Select extends AbstractSelect implements AbstractSelect.Filtering {
      *      java.util.Map)
      */
     public void changeVariables(Object source, Map variables) {
+
+       // TODO We really should call super instead, but this needs validating that AbstractSelect.changeVariables is correct. See ticket #
+               if (isReadThrough() || !isModified() || (getValidators() != null && !getValidators().isEmpty())) {
+                       fireValueChange(false);
+               }
+
         // Selection change
         if (variables.containsKey("selected")) {
             final String[] ka = (String[]) variables.get("selected");
index d0598e70fe1e92fa1cd4d729a801cb6dd82fa572..d459b3123e5ed1c1ea92984d05d2408a8f8dc118 100644 (file)
@@ -424,7 +424,8 @@ public class Slider extends AbstractField {
      * @param variables\r
      */\r
     public void changeVariables(Object source, Map variables) {\r
-        if (variables.containsKey("value")) {\r
+       super.changeVariables(source, variables);\r
+       if (variables.containsKey("value")) {\r
             final Object value = variables.get("value");\r
             final Double newValue = new Double(value.toString());\r
             if (newValue != null && newValue != getValue()\r
index 056b75a155a1b43ecc19555e57dd435626d33c23..49dbe20829767f80b1f6b5d79c67db95e7c46c3e 100644 (file)
@@ -213,6 +213,8 @@ public class TextField extends AbstractField {
      */
     public void changeVariables(Object source, Map variables) {
 
+       super.changeVariables(source, variables);
+       
         // Sets the text
         if (variables.containsKey("text") && !isReadOnly()) {