Package com.vaadin.ui

Source Code of com.vaadin.ui.AbstractField$FocusShortcut

/*
* Copyright 2011 Vaadin Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/

package com.vaadin.ui;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;

import com.vaadin.data.Buffered;
import com.vaadin.data.Property;
import com.vaadin.data.Validatable;
import com.vaadin.data.Validator;
import com.vaadin.data.Validator.InvalidValueException;
import com.vaadin.event.Action;
import com.vaadin.event.ActionManager;
import com.vaadin.event.ShortcutAction;
import com.vaadin.event.ShortcutListener;
import com.vaadin.terminal.CompositeErrorMessage;
import com.vaadin.terminal.ErrorMessage;
import com.vaadin.terminal.PaintException;
import com.vaadin.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.vaadin.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.vaadin.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.vaadin.data.Validator validators} to make
* sure the value contained in the field is valid.
* </p>
*
* @author IT Mill Ltd.
* @version
* 6.7.1
* @since 3.0
*/
@SuppressWarnings("serial")
public abstract class AbstractField extends AbstractComponent implements Field,
        Property.ReadOnlyStatusChangeListener,
        Property.ReadOnlyStatusChangeNotifier, Action.ShortcutNotifier {

    /* Private members */

    /**
     * Value of the abstract field.
     */
    private Object value;

    /**
     * Connected data-source.
     */
    private Property dataSource = null;

    /**
     * The list of validators.
     */
    private LinkedList<Validator> validators = null;

    /**
     * Auto commit mode.
     */
    private boolean writeThroughMode = true;

    /**
     * Reads the value from data-source, when it is not modified.
     */
    private boolean readThroughMode = true;

    /**
     * Is the field modified but not committed.
     */
    private boolean modified = false;

    /**
     * Flag to indicate that the field is currently committing its value to the
     * datasource.
     */
    private boolean committingValueToDataSource = false;

    /**
     * Current source exception.
     */
    private Buffered.SourceException currentBufferedSourceException = null;

    /**
     * Are the invalid values allowed 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;

    /**
     * The error message for the exception that is thrown when the field is
     * required but empty.
     */
    private String requiredError = "";

    /**
     * Is automatic validation enabled.
     */
    private boolean validationVisible = true;

    /**
     * Keeps track of the Actions added to this component; the actual
     * handling/notifying is delegated, usually to the containing window.
     */
    private ActionManager actionManager;

    private boolean valueWasModifiedByDataSourceDuringCommit;

    /* Component basics */

    /*
     * Paints the field. Don't add a JavaDoc comment here, we use the default
     * documentation from the implemented interface.
     */
    @Override
    public void paintContent(PaintTarget target) throws PaintException {

        // The tab ordering number
        if (getTabIndex() != 0) {
            target.addAttribute("tabindex", getTabIndex());
        }

        // If the field is modified, but not committed, set modified attribute
        if (isModified()) {
            target.addAttribute("modified", true);
        }

        // Adds the required attribute
        if (!isReadOnly() && isRequired()) {
            target.addAttribute("required", true);
        }

        // Hide the error indicator if needed
        if (shouldHideErrors()) {
            target.addAttribute("hideErrors", true);
        }
    }

    /**
     * Returns true if the error indicator be hidden when painting the component
     * even when there are errors.
     *
     * This is a mostly internal method, but can be overridden in subclasses
     * e.g. if the error indicator should also be shown for empty fields in some
     * cases.
     *
     * @return true to hide the error indicator, false to use the normal logic
     *         to show it when there are errors
     */
    protected boolean shouldHideErrors() {
        return isRequired() && isEmpty() && getComponentError() == null
                && getErrorMessage() != null;
    }

    /*
     * 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 read only
     * mode.
     */
    @Override
    public boolean isReadOnly() {
        return super.isReadOnly()
                || (dataSource != null && dataSource.isReadOnly());
    }

    /**
     * Changes the readonly state and throw read-only status change events.
     *
     * @see com.vaadin.ui.Component#setReadOnly(boolean)
     */
    @Override
    public void setReadOnly(boolean readOnly) {
        super.setReadOnly(readOnly);
        fireReadOnlyStatusChange();
    }

    /**
     * Tests if the invalid data is committed to datasource.
     *
     * @see com.vaadin.data.BufferedValidatable#isInvalidCommitted()
     */
    public boolean isInvalidCommitted() {
        return invalidCommitted;
    }

    /**
     * Sets if the invalid data should be committed to datasource.
     *
     * @see com.vaadin.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, InvalidValueException {
        if (dataSource != null && !dataSource.isReadOnly()) {
            if ((isInvalidCommitted() || isValid())) {
                final Object newValue = getValue();
                try {

                    // Commits the value to datasource.
                    valueWasModifiedByDataSourceDuringCommit = false;
                    committingValueToDataSource = true;
                    dataSource.setValue(newValue);

                } catch (final Throwable e) {

                    // Sets the buffering state.
                    currentBufferedSourceException = new Buffered.SourceException(
                            this, e);
                    requestRepaint();

                    // Throws the source exception.
                    throw currentBufferedSourceException;
                } finally {
                    committingValueToDataSource = false;
                }
            } else {
                /* An invalid value and we don't allow them, throw the exception */
                validate();
            }
        }

        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 (valueWasModifiedByDataSourceDuringCommit) {
            valueWasModifiedByDataSourceDuringCommit = false;
            fireValueChange(false);
        } else 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 = String.class == getType() ? dataSource.toString()
                        : 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 writeThroughMode;
    }

    /*
     * 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 writeThrough)
            throws Buffered.SourceException, InvalidValueException {
        if (writeThroughMode == writeThrough) {
            return;
        }
        writeThroughMode = writeThrough;
        if (writeThroughMode) {
            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 readThroughMode;
    }

    /*
     * 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 readThrough)
            throws Buffered.SourceException {
        if (readThroughMode == readThrough) {
            return;
        }
        readThroughMode = readThrough;
        if (!isModified() && readThroughMode && dataSource != null) {
            setInternalValue(String.class == getType() ? dataSource.toString()
                    : dataSource.getValue());
            fireValueChange(false);
        }
    }

    /* Property interface implementation */

    /**
     * Returns the value of the Property in human readable textual format.
     *
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        final Object value = getValue();
        if (value == null) {
            return null;
        }
        return getValue().toString();
    }

    /**
     * Gets the current value of the field.
     *
     * <p>
     * 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.
     * </p>
     *
     * <p>
     * Note that the object returned is compatible with getType(). For example,
     * if the type is String, this returns Strings even when the underlying
     * datasource is of some other type. In order to access the datasources
     * native type, use getPropertyDatasource().getValue() instead.
     * </p>
     *
     * <p>
     * Note that when you extend AbstractField, you must reimplement this method
     * if datasource.getValue() is not assignable to class returned by getType()
     * AND getType() is not String. In case of Strings, getValue() calls
     * datasource.toString() instead of datasource.getValue().
     * </p>
     *
     * @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 = String.class == getType() ? dataSource.toString()
                : dataSource.getValue();

        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();
            }

            // Repaint is needed even when the client thinks that it knows the
            // new state if validity of the component may change
            if (repaintIsNotNeeded && (isRequired() || getValidators() != null)) {
                repaintIsNotNeeded = false;
            }

            // If invalid values are not allowed, the value must be checked
            if (!isInvalidAllowed()) {
                final Collection<Validator> v = getValidators();
                if (v != null) {
                    for (final Iterator<Validator> i = v.iterator(); i
                            .hasNext();) {
                        (i.next()).validate(newValue);
                    }
                }
            }

            // Changes the value
            setInternalValue(newValue);
            modified = dataSource != null;

            valueWasModifiedByDataSourceDuringCommit = false;
            // In write through mode , try to commit
            if (isWriteThrough() && dataSource != null
                    && (isInvalidCommitted() || isValid())) {
                try {

                    // Commits the value to datasource
                    committingValueToDataSource = true;
                    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;
                } finally {
                    committingValueToDataSource = false;
                }
            }

            // If successful, remove set the buffering state to be ok
            if (currentBufferedSourceException != null) {
                currentBufferedSourceException = null;
                requestRepaint();
            }

            if (valueWasModifiedByDataSourceDuringCommit) {
                /*
                 * Value was modified by datasource. Force repaint even if
                 * repaint was not requested.
                 */
                valueWasModifiedByDataSourceDuringCommit = repaintIsNotNeeded = false;
            }

            // 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 are replaced with a value 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>
     *
     * <p>
     * Note: before 6.5 we actually called discard() method in the beginning of
     * the method. This was removed to simplify implementation, avoid excess
     * calls to backing property and to avoid odd value change events that were
     * previously fired (developer expects 0-1 value change events if this
     * method is called). Some complex field implementations might now need to
     * override this method to do housekeeping similar to discard().
     * </p>
     *
     * @param newDataSource
     *            the new data source Property.
     */
    public void setPropertyDataSource(Property newDataSource) {

        // Saves the old value
        final Object oldValue = value;

        // Stops listening the old data source changes
        if (dataSource != null
                && Property.ValueChangeNotifier.class
                        .isAssignableFrom(dataSource.getClass())) {
            ((Property.ValueChangeNotifier) dataSource).removeListener(this);
        }
        if (dataSource != null
                && Property.ReadOnlyStatusChangeNotifier.class
                        .isAssignableFrom(dataSource.getClass())) {
            ((Property.ReadOnlyStatusChangeNotifier) dataSource)
                    .removeListener(this);
        }

        // Sets the new data source
        dataSource = newDataSource;

        // Gets the value from source
        try {
            if (dataSource != null) {
                setInternalValue(String.class == getType() ? dataSource
                        .toString() : 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);
        }
        if (dataSource instanceof Property.ReadOnlyStatusChangeNotifier) {
            ((Property.ReadOnlyStatusChangeNotifier) dataSource)
                    .addListener(this);
        }

        // Copy the validators from the data source
        if (dataSource instanceof Validatable) {
            final Collection<Validator> validators = ((Validatable) dataSource)
                    .getValidators();
            if (validators != null) {
                for (final Iterator<Validator> i = validators.iterator(); i
                        .hasNext();) {
                    addValidator(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<Validator>();
        }
        validators.add(validator);
        requestRepaint();
    }

    /**
     * Gets the validators of the field.
     *
     * @return the Unmodifiable collection that holds all validators for the
     *         field.
     */
    public Collection<Validator> 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);
        }
        requestRepaint();
    }

    /**
     * Tests the current value against registered validators if the field is not
     * empty. If the field is empty it is considered valid if it is not required
     * and invalid otherwise. Validators are never checked for empty fields.
     *
     * @return <code>true</code> if all registered validators claim that the
     *         current value is valid or if the field is empty and not required,
     *         <code>false</code> otherwise.
     */
    public boolean isValid() {

        if (isEmpty()) {
            if (isRequired()) {
                return false;
            } else {
                return true;
            }
        }

        if (validators == null) {
            return true;
        }

        final Object value = getValue();
        for (final Iterator<Validator> i = validators.iterator(); i.hasNext();) {
            if (!(i.next()).isValid(value)) {
                return false;
            }
        }

        return true;
    }

    /**
     * Checks the validity of the Validatable by validating the field with all
     * attached validators except when the field is empty. An empty field is
     * invalid if it is required and valid otherwise.
     *
     * The "required" validation is a built-in validation feature. If the field
     * is required, but empty, validation will throw an EmptyValueException with
     * the error message set with setRequiredError().
     *
     * @see com.vaadin.data.Validatable#validate()
     */
    public void validate() throws Validator.InvalidValueException {

        if (isEmpty()) {
            if (isRequired()) {
                throw new Validator.EmptyValueException(requiredError);
            } else {
                return;
            }
        }

        // If there is no validator, there can not be any errors
        if (validators == null) {
            return;
        }

        // Initialize temps
        Validator.InvalidValueException firstError = null;
        LinkedList<InvalidValueException> errors = null;
        final Object value = getValue();

        // Gets all the validation errors
        for (final Iterator<Validator> i = validators.iterator(); i.hasNext();) {
            try {
                (i.next()).validate(value);
            } catch (final Validator.InvalidValueException e) {
                if (firstError == null) {
                    firstError = e;
                } else {
                    if (errors == null) {
                        errors = new LinkedList<InvalidValueException>();
                        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<InvalidValueException> i = errors.iterator(); i
                .hasNext();) {
            exceptions[index++] = 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.vaadin.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.vaadin.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.vaadin.ui.AbstractComponent#getErrorMessage()
     */
    @Override
    public ErrorMessage getErrorMessage() {

        /*
         * Check validation errors only if automatic validation is enabled.
         * Empty, required fields will generate a validation error containing
         * the requiredError string. For these fields the exclamation mark will
         * be hidden but the error must still be sent to the client.
         */
        ErrorMessage validationError = null;
        if (isValidationVisible()) {
            try {
                validate();
            } catch (Validator.InvalidValueException e) {
                if (!e.isInvisible()) {
                    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(
                    "Internal error finding methods in AbstractField");
        }
    }

    /*
     * 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(
                    "Internal error finding methods in AbstractField");
        }
    }

    /**
     * React to read only status changes of the property by requesting a
     * repaint.
     *
     * @see Property.ReadOnlyStatusChangeListener
     */
    public void readOnlyStatusChange(Property.ReadOnlyStatusChangeEvent event) {
        requestRepaint();
    }

    /**
     * An <code>Event</code> object specifying the Property whose read-only
     * status has changed.
     *
     * @author IT Mill Ltd.
     * @version
     * 6.7.1
     * @since 3.0
     */
    public class ReadOnlyStatusChangeEvent extends Component.Event implements
            Property.ReadOnlyStatusChangeEvent, Serializable {

        /**
         * 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.
     *
     * Changes are not forwarded to the listeners of the field during internal
     * operations of the field to avoid duplicate notifications.
     *
     * @param event
     *            the value change event telling the data source contents have
     *            changed.
     */
    public void valueChange(Property.ValueChangeEvent event) {
        if (isReadThrough()) {
            if (committingValueToDataSource) {
                boolean propertyNotifiesOfTheBufferedValue = event
                        .getProperty().getValue() == value
                        || (value != null && value.equals(event.getProperty()
                                .getValue()));
                if (!propertyNotifiesOfTheBufferedValue) {
                    /*
                     * Property (or chained property like PropertyFormatter) now
                     * reports different value than the one the field has just
                     * committed to it. In this case we respect the property
                     * value.
                     *
                     * Still, we don't fire value change yet, but instead
                     * postpone it until "commit" is done. See setValue(Object,
                     * boolean) and commit().
                     */
                    readValueFromProperty(event);
                    valueWasModifiedByDataSourceDuringCommit = true;
                }
            } else if (!isModified()) {
                readValueFromProperty(event);
                fireValueChange(false);
            }
        }
    }

    private void readValueFromProperty(Property.ValueChangeEvent event) {
        setInternalValue(event.getProperty().getValue());
    }

    @Override
    public void changeVariables(Object source, Map<String, Object> variables) {
        super.changeVariables(source, variables);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void focus() {
        super.focus();
    }

    /**
     * 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.
     * @deprecated use e.g.
     *             {@link DefaultFieldFactory#createFieldByPropertyType(Class)}
     *             instead
     */
    @Deprecated
    public static AbstractField constructField(Class<?> propertyType) {
        return (AbstractField) DefaultFieldFactory
                .createFieldByPropertyType(propertyType);
    }

    /*
     * (non-Javadoc)
     *
     * @see com.vaadin.ui.Component.Focusable#getTabIndex()
     */
    public int getTabIndex() {
        return tabIndex;
    }

    /*
     * (non-Javadoc)
     *
     * @see com.vaadin.ui.Component.Focusable#setTabIndex(int)
     */
    public void setTabIndex(int tabIndex) {
        this.tabIndex = tabIndex;
        requestRepaint();
    }

    /**
     * Sets the internal field value. This is purely used by AbstractField to
     * change the internal Field value. It does not trigger valuechange events.
     * It can be overridden by the inheriting classes to update all dependent
     * variables.
     *
     * @param newValue
     *            the new value to be set.
     */
    protected void setInternalValue(Object newValue) {
        value = newValue;
        if (validators != null && !validators.isEmpty()) {
            requestRepaint();
        }
    }

    /**
     * Notifies the component that it is connected to an application.
     *
     * @see com.vaadin.ui.Component#attach()
     */
    @Override
    public void attach() {
        super.attach();
        if (actionManager != null) {
            actionManager.setViewer(getWindow());
        }
    }

    @Override
    public void detach() {
        super.detach();
        if (actionManager != null) {
            actionManager.setViewer((Window) null);
        }
    }

    /**
     * Is this field required. Required fields must filled by the user.
     *
     * If the field is required, it is visually indicated in the user interface.
     * Furthermore, setting field to be required implicitly adds "non-empty"
     * validator and thus isValid() == false or any isEmpty() fields. In those
     * cases validation errors are not painted as it is obvious that the user
     * must fill in the required fields.
     *
     * On the other hand, for the non-required fields isValid() == true if the
     * field isEmpty() regardless of any attached validators.
     *
     *
     * @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.
     *
     * If the field is required, it is visually indicated in the user interface.
     * Furthermore, setting field to be required implicitly adds "non-empty"
     * validator and thus isValid() == false or any isEmpty() fields. In those
     * cases validation errors are not painted as it is obvious that the user
     * must fill in the required fields.
     *
     * On the other hand, for the non-required fields isValid() == true if the
     * field isEmpty() regardless of any attached validators.
     *
     * @param required
     *            Is the field required.
     */
    public void setRequired(boolean required) {
        this.required = required;
        requestRepaint();
    }

    /**
     * Set the error that is show if this field is required, but empty. When
     * setting requiredMessage to be "" or null, no error pop-up or exclamation
     * mark is shown for a empty required field. This faults to "". Even in
     * those cases isValid() returns false for empty required fields.
     *
     * @param requiredMessage
     *            Message to be shown when this field is required, but empty.
     */
    public void setRequiredError(String requiredMessage) {
        requiredError = requiredMessage;
        requestRepaint();
    }

    public String getRequiredError() {
        return requiredError;
    }

    /**
     * Is the field empty?
     *
     * In general, "empty" state is same as null. As an exception, TextField
     * also treats empty string as "empty".
     */
    protected boolean isEmpty() {
        return (getValue() == null);
    }

    /**
     * Is automatic, visible validation enabled?
     *
     * If automatic validation is enabled, any validators connected to this
     * component are evaluated while painting the component and potential error
     * messages are sent to client. If the automatic validation is turned off,
     * isValid() and validate() methods still work, but one must show the
     * validation in their own code.
     *
     * @return True, if automatic validation is enabled.
     */
    public boolean isValidationVisible() {
        return validationVisible;
    }

    /**
     * Enable or disable automatic, visible validation.
     *
     * If automatic validation is enabled, any validators connected to this
     * component are evaluated while painting the component and potential error
     * messages are sent to client. If the automatic validation is turned off,
     * isValid() and validate() methods still work, but one must show the
     * validation in their own code.
     *
     * @param validateAutomatically
     *            True, if automatic validation is enabled.
     */
    public void setValidationVisible(boolean validateAutomatically) {
        if (validationVisible != validateAutomatically) {
            requestRepaint();
            validationVisible = validateAutomatically;
        }
    }

    /**
     * Sets the current buffered source exception.
     *
     * @param currentBufferedSourceException
     */
    public void setCurrentBufferedSourceException(
            Buffered.SourceException currentBufferedSourceException) {
        this.currentBufferedSourceException = currentBufferedSourceException;
        requestRepaint();
    }

    /*
     * Actions
     */

    /**
     * Gets the {@link ActionManager} used to manage the
     * {@link ShortcutListener}s added to this {@link Field}.
     *
     * @return the ActionManager in use
     */
    protected ActionManager getActionManager() {
        if (actionManager == null) {
            actionManager = new ActionManager();
            if (getWindow() != null) {
                actionManager.setViewer(getWindow());
            }
        }
        return actionManager;
    }

    public void addShortcutListener(ShortcutListener shortcut) {
        getActionManager().addAction(shortcut);
    }

    public void removeShortcutListener(ShortcutListener shortcut) {
        if (actionManager != null) {
            actionManager.removeAction(shortcut);
        }
    }

    /**
     * A ready-made {@link ShortcutListener} that focuses the given
     * {@link Focusable} (usually a {@link Field}) when the keyboard shortcut is
     * invoked.
     *
     */
    public static class FocusShortcut extends ShortcutListener {
        protected Focusable focusable;

        /**
         * Creates a keyboard shortcut for focusing the given {@link Focusable}
         * using the shorthand notation defined in {@link ShortcutAction}.
         *
         * @param focusable
         *            to focused when the shortcut is invoked
         * @param shorthandCaption
         *            caption with keycode and modifiers indicated
         */
        public FocusShortcut(Focusable focusable, String shorthandCaption) {
            super(shorthandCaption);
            this.focusable = focusable;
        }

        /**
         * Creates a keyboard shortcut for focusing the given {@link Focusable}.
         *
         * @param focusable
         *            to focused when the shortcut is invoked
         * @param keyCode
         *            keycode that invokes the shortcut
         * @param modifiers
         *            modifiers required to invoke the shortcut
         */
        public FocusShortcut(Focusable focusable, int keyCode, int... modifiers) {
            super(null, keyCode, modifiers);
            this.focusable = focusable;
        }

        /**
         * Creates a keyboard shortcut for focusing the given {@link Focusable}.
         *
         * @param focusable
         *            to focused when the shortcut is invoked
         * @param keyCode
         *            keycode that invokes the shortcut
         */
        public FocusShortcut(Focusable focusable, int keyCode) {
            this(focusable, keyCode, null);
        }

        @Override
        public void handleAction(Object sender, Object target) {
            focusable.focus();
        }
    }
}
TOP

Related Classes of com.vaadin.ui.AbstractField$FocusShortcut

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.
m/analytics.js','ga'); ga('create', 'UA-20639858-1', 'auto'); ga('send', 'pageview');