summaryrefslogtreecommitdiffstats
path: root/compatibility-server/src
diff options
context:
space:
mode:
authorArtur Signell <artur@vaadin.com>2016-08-25 14:46:30 +0300
committerArtur Signell <artur@vaadin.com>2016-08-26 11:40:13 +0300
commitc11121e2b7e685d42eb129c8a97e5ed4690af2f2 (patch)
treee1ed542be05eda64d12d18f8310db9900f835cb5 /compatibility-server/src
parent58853fe47bb01a0c0c2b5c380056d22ccccd6f08 (diff)
downloadvaadin-framework-c11121e2b7e685d42eb129c8a97e5ed4690af2f2.tar.gz
vaadin-framework-c11121e2b7e685d42eb129c8a97e5ed4690af2f2.zip
Move old Field and AbstractField to compatibility package
Change-Id: Ia9b6f77763abac87ec61d1ee198cb8d41419a934
Diffstat (limited to 'compatibility-server/src')
-rw-r--r--compatibility-server/src/main/java/com/vaadin/v7/ui/AbstractField.java1810
-rw-r--r--compatibility-server/src/main/java/com/vaadin/v7/ui/Field.java150
-rw-r--r--compatibility-server/src/test/java/com/vaadin/v7/tests/VaadinClasses.java21
-rw-r--r--compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/abstractfield/AbstractFieldValueChangeTestBase.java130
-rw-r--r--compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/abstractfield/FieldDefaultValuesTest.java76
-rw-r--r--compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/textfield/TextFieldValueChangeTest.java2
-rw-r--r--compatibility-server/src/test/java/com/vaadin/v7/tests/server/components/ComboBoxValueChangeTest.java2
7 files changed, 2189 insertions, 2 deletions
diff --git a/compatibility-server/src/main/java/com/vaadin/v7/ui/AbstractField.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/AbstractField.java
new file mode 100644
index 0000000000..33f262d581
--- /dev/null
+++ b/compatibility-server/src/main/java/com/vaadin/v7/ui/AbstractField.java
@@ -0,0 +1,1810 @@
+/*
+ * Copyright 2000-2016 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.v7.ui;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Locale;
+import java.util.logging.Logger;
+
+import org.jsoup.nodes.Attributes;
+import org.jsoup.nodes.Element;
+
+import com.vaadin.event.Action;
+import com.vaadin.event.ShortcutAction;
+import com.vaadin.event.ShortcutListener;
+import com.vaadin.server.AbstractErrorMessage;
+import com.vaadin.server.CompositeErrorMessage;
+import com.vaadin.server.ErrorMessage;
+import com.vaadin.shared.AbstractFieldState;
+import com.vaadin.shared.util.SharedUtil;
+import com.vaadin.ui.AbstractComponent;
+import com.vaadin.ui.Component;
+import com.vaadin.ui.declarative.DesignAttributeHandler;
+import com.vaadin.ui.declarative.DesignContext;
+import com.vaadin.v7.data.Buffered;
+import com.vaadin.v7.data.Property;
+import com.vaadin.v7.data.Validatable;
+import com.vaadin.v7.data.Validator;
+import com.vaadin.v7.data.Validator.InvalidValueException;
+import com.vaadin.v7.data.util.converter.Converter;
+import com.vaadin.v7.data.util.converter.Converter.ConversionException;
+import com.vaadin.v7.data.util.converter.ConverterUtil;
+
+/**
+ * <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.v7.data.Property}interface.
+ * <code>LegacyAbstractField</code> implements that interface itself, too, so
+ * accessing the Property value represented by it is straightforward.
+ * </p>
+ *
+ * <p>
+ * LegacyAbstractField also provides the {@link com.vaadin.v7.data.Buffered}
+ * interface for buffering the data source value. By default the LegacyField is
+ * in write through-mode and {@link #setWriteThrough(boolean)}should be called
+ * to enable buffering.
+ * </p>
+ *
+ * <p>
+ * The class also supports {@link com.vaadin.v7.data.Validator validators}
+ * to make sure the value contained in the field is valid.
+ * </p>
+ *
+ * @author Vaadin Ltd.
+ * @since 3.0
+ *
+ * @deprecated This class is, apart from the rename, identical to the Vaadin 7
+ * {@code com.vaadin.ui.AbstractField}. It is provided for
+ * compatibility and migration purposes. As of 8.0, new field
+ * implementations should extend the new
+ * {@link com.vaadin.ui.AbstractField} instead.
+ */
+@SuppressWarnings("serial")
+@Deprecated
+public abstract class AbstractField<T> extends AbstractComponent
+ implements Field<T>, Property.ReadOnlyStatusChangeListener,
+ Property.ReadOnlyStatusChangeNotifier, Action.ShortcutNotifier {
+
+ /* Private members */
+
+ /**
+ * Value of the abstract field.
+ */
+ private T value;
+
+ /**
+ * A converter used to convert from the data model type to the field type
+ * and vice versa.
+ */
+ private Converter<T, Object> converter = null;
+ /**
+ * Connected data-source.
+ */
+ private Property<?> dataSource = null;
+
+ /**
+ * The list of validators.
+ */
+ private LinkedList<Validator> validators = null;
+
+ /**
+ * True if field is in buffered mode, false otherwise
+ */
+ private boolean buffered;
+
+ /**
+ * 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 error message for the exception that is thrown when the field is
+ * required but empty.
+ */
+ private String requiredError = "";
+
+ /**
+ * The error message that is shown when the field value cannot be converted.
+ */
+ private String conversionError = "Could not convert value to {0}";
+
+ /**
+ * Is automatic validation enabled.
+ */
+ private boolean validationVisible = true;
+
+ private boolean valueWasModifiedByDataSourceDuringCommit;
+
+ /**
+ * Whether this field is currently registered as listening to events from
+ * its data source.
+ *
+ * @see #setPropertyDataSource(Property)
+ * @see #addPropertyListeners()
+ * @see #removePropertyListeners()
+ */
+ private boolean isListeningToPropertyEvents = false;
+
+ /**
+ * The locale used when setting the value.
+ */
+ private Locale valueLocale = null;
+
+ /* Component basics */
+
+ /*
+ * Paints the field. Don't add a JavaDoc comment here, we use the default
+ * documentation from the implemented interface.
+ */
+
+ /**
+ * 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() {
+ // getErrorMessage() can still return something else than null based on
+ // validation etc.
+ return isRequired() && isEmpty() && getComponentError() == null;
+ }
+
+ /**
+ * Returns the type of the LegacyField. The methods <code>getValue</code>
+ * and <code>setValue</code> must be compatible with this type: one must be
+ * able to safely cast the value returned from <code>getValue</code> to the
+ * given type and pass any variable assignable to this type as an argument
+ * to <code>setValue</code>.
+ *
+ * @return the type of the LegacyField
+ */
+ @Override
+ public abstract Class<? extends T> 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.v7.data.BufferedValidatable#isInvalidCommitted()
+ */
+ @Override
+ public boolean isInvalidCommitted() {
+ return invalidCommitted;
+ }
+
+ /**
+ * Sets if the invalid data should be committed to datasource.
+ *
+ * @see com.vaadin.v7.data.BufferedValidatable#setInvalidCommitted(boolean)
+ */
+ @Override
+ 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.
+ */
+ @Override
+ public void commit()
+ throws Buffered.SourceException, InvalidValueException {
+ if (dataSource != null && !dataSource.isReadOnly()) {
+ if ((isInvalidCommitted() || isValid())) {
+ try {
+
+ // Commits the value to datasource.
+ valueWasModifiedByDataSourceDuringCommit = false;
+ committingValueToDataSource = true;
+ getPropertyDataSource().setValue(getConvertedValue());
+ } catch (final Throwable e) {
+
+ // Sets the buffering state.
+ SourceException sourceException = new Buffered.SourceException(
+ this, e);
+ setCurrentBufferedSourceException(sourceException);
+
+ // Throws the source exception.
+ throw sourceException;
+ } finally {
+ committingValueToDataSource = false;
+ }
+ } else {
+ /*
+ * An invalid value and we don't allow them, throw the exception
+ */
+ validate();
+ }
+ }
+
+ // The abstract field is not modified anymore
+ if (isModified()) {
+ setModified(false);
+ }
+
+ // If successful, remove set the buffering state to be ok
+ if (getCurrentBufferedSourceException() != null) {
+ setCurrentBufferedSourceException(null);
+ }
+
+ if (valueWasModifiedByDataSourceDuringCommit) {
+ valueWasModifiedByDataSourceDuringCommit = false;
+ fireValueChange(false);
+ }
+
+ }
+
+ /*
+ * Updates the value from the data source. Don't add a JavaDoc comment here,
+ * we use the default documentation from the implemented interface.
+ */
+ @Override
+ public void discard() throws Buffered.SourceException {
+ updateValueFromDataSource();
+ }
+
+ /**
+ * Gets the value from the data source. This is only here because of clarity
+ * in the code that handles both the data model value and the field value.
+ *
+ * @return The value of the property data source
+ */
+ private Object getDataSourceValue() {
+ return dataSource.getValue();
+ }
+
+ /**
+ * Returns the field value. This is always identical to {@link #getValue()}
+ * and only here because of clarity in the code that handles both the data
+ * model value and the field value.
+ *
+ * @return The value of the field
+ */
+ private T getFieldValue() {
+ // Give the value from abstract buffers if the field if possible
+ if (dataSource == null || isBuffered() || isModified()) {
+ return getInternalValue();
+ }
+
+ // There is no buffered value so use whatever the data model provides
+ return convertFromModel(getDataSourceValue());
+ }
+
+ /*
+ * 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.
+ */
+ @Override
+ public boolean isModified() {
+ return getState(false).modified;
+ }
+
+ private void setModified(boolean modified) {
+ getState().modified = modified;
+ }
+
+ /**
+ * Sets the buffered mode of this LegacyField.
+ * <p>
+ * When the field is in buffered mode, changes will not be committed to the
+ * property data source until {@link #commit()} is called.
+ * </p>
+ * <p>
+ * Setting buffered mode from true to false will commit any pending changes.
+ * </p>
+ * <p>
+ *
+ * </p>
+ *
+ * @since 7.0.0
+ * @param buffered
+ * true if buffered mode should be turned on, false otherwise
+ */
+ @Override
+ public void setBuffered(boolean buffered) {
+ if (this.buffered == buffered) {
+ return;
+ }
+ this.buffered = buffered;
+ if (!buffered) {
+ commit();
+ }
+ }
+
+ /**
+ * Checks the buffered mode of this LegacyField.
+ *
+ * @return true if buffered mode is on, false otherwise
+ */
+ @Override
+ public boolean isBuffered() {
+ return buffered;
+ }
+
+ /* Property interface implementation */
+
+ /**
+ * Gets the current value of the field.
+ *
+ * <p>
+ * This is the visible, modified and possible invalid value the user have
+ * entered to the field.
+ * </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 converted value,
+ * use {@link #getConvertedValue()} and to access the value of the property
+ * data source, use {@link Property#getValue()} for the property data
+ * source.
+ * </p>
+ *
+ * <p>
+ * Since Vaadin 7.0, no implicit conversions between other data types and
+ * String are performed, but a converter is used if set.
+ * </p>
+ *
+ * @return the current value of the field.
+ */
+ @Override
+ public T getValue() {
+ return getFieldValue();
+ }
+
+ /**
+ * Sets the value of the field.
+ *
+ * @param newFieldValue
+ * the New value of the field.
+ * @throws Property.ReadOnlyException
+ */
+ @Override
+ public void setValue(T newFieldValue) throws Property.ReadOnlyException,
+ Converter.ConversionException {
+ setValue(newFieldValue, false);
+ }
+
+ /**
+ * Sets the value of the field.
+ *
+ * @param newFieldValue
+ * the New value of the field.
+ * @param repaintIsNotNeeded
+ * True iff caller is sure that repaint is not needed.
+ * @throws Property.ReadOnlyException
+ * @throws Converter.ConversionException
+ * @throws InvalidValueException
+ */
+ protected void setValue(T newFieldValue, boolean repaintIsNotNeeded) {
+ setValue(newFieldValue, repaintIsNotNeeded, false);
+ }
+
+ /**
+ * Sets the value of the field.
+ *
+ * @since 7.5.7
+ * @param newFieldValue
+ * the New value of the field.
+ * @param repaintIsNotNeeded
+ * True iff caller is sure that repaint is not needed.
+ * @param ignoreReadOnly
+ * True iff if the read-only check should be ignored
+ * @throws Property.ReadOnlyException
+ * @throws Converter.ConversionException
+ * @throws InvalidValueException
+ */
+ protected void setValue(T newFieldValue, boolean repaintIsNotNeeded,
+ boolean ignoreReadOnly) throws Property.ReadOnlyException,
+ Converter.ConversionException, InvalidValueException {
+
+ if (!SharedUtil.equals(newFieldValue, getInternalValue())) {
+
+ // Read only fields can not be changed
+ if (!ignoreReadOnly && isReadOnly()) {
+ throw new Property.ReadOnlyException();
+ }
+ try {
+ T doubleConvertedFieldValue = convertFromModel(
+ convertToModel(newFieldValue));
+ if (!SharedUtil.equals(newFieldValue,
+ doubleConvertedFieldValue)) {
+ newFieldValue = doubleConvertedFieldValue;
+ repaintIsNotNeeded = false;
+ }
+ } catch (Throwable t) {
+ // Ignore exceptions in the conversion at this stage. Any
+ // conversion error will be handled later by validate().
+ }
+
+ // Repaint is needed even when the client thinks that it knows the
+ // new state if validity of the component may change
+ if (repaintIsNotNeeded && (isRequired() || hasValidators()
+ || getConverter() != null)) {
+ repaintIsNotNeeded = false;
+ }
+
+ if (!isInvalidAllowed()) {
+ /*
+ * If invalid values are not allowed the value must be validated
+ * before it is set. If validation fails, the
+ * InvalidValueException is thrown and the internal value is not
+ * updated.
+ */
+ validate(newFieldValue);
+ }
+
+ // Changes the value
+ setInternalValue(newFieldValue);
+ setModified(dataSource != null);
+
+ valueWasModifiedByDataSourceDuringCommit = false;
+ // In not buffering, try to commit
+ if (!isBuffered() && dataSource != null
+ && (isInvalidCommitted() || isValid())) {
+ try {
+
+ // Commits the value to datasource
+ committingValueToDataSource = true;
+ getPropertyDataSource()
+ .setValue(convertToModel(newFieldValue));
+
+ // The buffer is now unmodified
+ setModified(false);
+
+ } catch (final Throwable e) {
+
+ // Sets the buffering state
+ currentBufferedSourceException = new Buffered.SourceException(
+ this, e);
+ markAsDirty();
+
+ // Throws the source exception
+ throw currentBufferedSourceException;
+ } finally {
+ committingValueToDataSource = false;
+ }
+ }
+
+ // If successful, remove set the buffering state to be ok
+ if (getCurrentBufferedSourceException() != null) {
+ setCurrentBufferedSourceException(null);
+ }
+
+ if (valueWasModifiedByDataSourceDuringCommit) {
+ /*
+ * Value was modified by datasource. Force repaint even if
+ * repaint was not requested.
+ */
+ valueWasModifiedByDataSourceDuringCommit = repaintIsNotNeeded = false;
+ }
+
+ // Fires the value change
+ fireValueChange(repaintIsNotNeeded);
+
+ }
+ }
+
+ @Deprecated
+ static boolean equals(Object value1, Object value2) {
+ return SharedUtil.equals(value1, value2);
+ }
+
+ /* 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.
+ */
+ @Override
+ 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>
+ * If the data source implements
+ * {@link com.vaadin.v7.data.Property.ValueChangeNotifier} and/or
+ * {@link com.vaadin.v7.data.Property.ReadOnlyStatusChangeNotifier}, the field
+ * registers itself as a listener and updates itself according to the events
+ * it receives. To avoid memory leaks caused by references to a field no
+ * longer in use, the listener registrations are removed on
+ * {@link AbstractField#detach() detach} and re-added on
+ * {@link AbstractField#attach() attach}.
+ * </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.
+ */
+ @Override
+ public void setPropertyDataSource(Property newDataSource) {
+
+ // Saves the old value
+ final Object oldValue = getInternalValue();
+
+ // Stop listening to the old data source
+ removePropertyListeners();
+
+ // Sets the new data source
+ dataSource = newDataSource;
+ getState().propertyReadOnly = dataSource == null ? false
+ : dataSource.isReadOnly();
+
+ // Check if the current converter is compatible.
+ if (newDataSource != null
+ && !ConverterUtil.canConverterPossiblyHandle(
+ getConverter(), getType(), newDataSource.getType())) {
+ // There is no converter set or there is no way the current
+ // converter can be compatible.
+ setConverter(newDataSource.getType());
+ }
+ // Gets the value from source. This requires that a valid converter has
+ // been set.
+ try {
+ if (dataSource != null) {
+ T fieldValue = convertFromModel(getDataSourceValue());
+ setInternalValue(fieldValue);
+ }
+ setModified(false);
+ if (getCurrentBufferedSourceException() != null) {
+ setCurrentBufferedSourceException(null);
+ }
+ } catch (final Throwable e) {
+ setCurrentBufferedSourceException(
+ new Buffered.SourceException(this, e));
+ setModified(true);
+ throw getCurrentBufferedSourceException();
+ }
+
+ // Listen to new data source if possible
+ addPropertyListeners();
+
+ // 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
+ T value = getInternalValue();
+ if ((value != oldValue) && ((value != null && !value.equals(oldValue))
+ || value == null)) {
+ fireValueChange(false);
+ }
+ }
+
+ /**
+ * Retrieves a converter for the field from the converter factory defined
+ * for the application. Clears the converter if no application reference is
+ * available or if the factory returns null.
+ *
+ * @param datamodelType
+ * The type of the data model that we want to be able to convert
+ * from
+ */
+ public void setConverter(Class<?> datamodelType) {
+ Converter<T, ?> c = (Converter<T, ?>) ConverterUtil
+ .getConverter(getType(), datamodelType, getSession());
+ setConverter(c);
+ }
+
+ /**
+ * Convert the given value from the data source type to the UI type.
+ *
+ * @param newValue
+ * The data source value to convert.
+ * @return The converted value that is compatible with the UI type or the
+ * original value if its type is compatible and no converter is set.
+ * @throws Converter.ConversionException
+ * if there is no converter and the type is not compatible with
+ * the data source type.
+ */
+ private T convertFromModel(Object newValue) {
+ return convertFromModel(newValue, getLocale());
+ }
+
+ /**
+ * Convert the given value from the data source type to the UI type.
+ *
+ * @param newValue
+ * The data source value to convert.
+ * @return The converted value that is compatible with the UI type or the
+ * original value if its type is compatible and no converter is set.
+ * @throws Converter.ConversionException
+ * if there is no converter and the type is not compatible with
+ * the data source type.
+ */
+ private T convertFromModel(Object newValue, Locale locale) {
+ return ConverterUtil.convertFromModel(newValue, getType(),
+ getConverter(), locale);
+ }
+
+ /**
+ * Convert the given value from the UI type to the data source type.
+ *
+ * @param fieldValue
+ * The value to convert. Typically returned by
+ * {@link #getFieldValue()}
+ * @return The converted value that is compatible with the data source type.
+ * @throws Converter.ConversionException
+ * if there is no converter and the type is not compatible with
+ * the data source type.
+ */
+ private Object convertToModel(T fieldValue)
+ throws Converter.ConversionException {
+ return convertToModel(fieldValue, getLocale());
+ }
+
+ /**
+ * Convert the given value from the UI type to the data source type.
+ *
+ * @param fieldValue
+ * The value to convert. Typically returned by
+ * {@link #getFieldValue()}
+ * @param locale
+ * The locale to use for the conversion
+ * @return The converted value that is compatible with the data source type.
+ * @throws Converter.ConversionException
+ * if there is no converter and the type is not compatible with
+ * the data source type.
+ */
+ private Object convertToModel(T fieldValue, Locale locale)
+ throws Converter.ConversionException {
+ Class<?> modelType = getModelType();
+ try {
+ return ConverterUtil.convertToModel(fieldValue,
+ (Class<Object>) modelType, getConverter(), locale);
+ } catch (ConversionException e) {
+ throw new ConversionException(getConversionError(modelType, e), e);
+ }
+ }
+
+ /**
+ * Retrieves the type of the currently used data model. If the field has no
+ * data source then the model type of the converter is used.
+ *
+ * @since 7.1
+ * @return The type of the currently used data model or null if no data
+ * source or converter is set.
+ */
+ protected Class<?> getModelType() {
+ Property<?> pd = getPropertyDataSource();
+ if (pd != null) {
+ return pd.getType();
+ } else if (getConverter() != null) {
+ return getConverter().getModelType();
+ }
+ return null;
+ }
+
+ /**
+ * Returns the conversion error with {0} replaced by the data source type
+ * and {1} replaced by the exception (localized) message.
+ *
+ * @since 7.1
+ * @param dataSourceType
+ * the type of the data source
+ * @param e
+ * a conversion exception which can provide additional
+ * information
+ * @return The value conversion error string with parameters replaced.
+ */
+ protected String getConversionError(Class<?> dataSourceType,
+ ConversionException e) {
+ String conversionError = getConversionError();
+
+ if (conversionError != null) {
+ if (dataSourceType != null) {
+ conversionError = conversionError.replace("{0}",
+ dataSourceType.getSimpleName());
+ }
+ if (e != null) {
+ conversionError = conversionError.replace("{1}",
+ e.getLocalizedMessage());
+ }
+ }
+ return conversionError;
+ }
+
+ /**
+ * Returns the current value (as returned by {@link #getValue()}) converted
+ * to the data source type.
+ * <p>
+ * This returns the same as {@link AbstractField#getValue()} if no
+ * converter has been set. The value is not necessarily the same as the data
+ * source value e.g. if the field is in buffered mode and has been modified.
+ * </p>
+ *
+ * @return The converted value that is compatible with the data source type
+ */
+ public Object getConvertedValue() {
+ return convertToModel(getFieldValue());
+ }
+
+ /**
+ * Sets the value of the field using a value of the data source type. The
+ * value given is converted to the field type and then assigned to the
+ * field. This will update the property data source in the same way as when
+ * {@link #setValue(Object)} is called.
+ *
+ * @param value
+ * The value to set. Must be the same type as the data source.
+ */
+ public void setConvertedValue(Object value) {
+ setValue(convertFromModel(value));
+ }
+
+ /* 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.
+ */
+ @Override
+ public void addValidator(Validator validator) {
+ if (validators == null) {
+ validators = new LinkedList<>();
+ }
+ validators.add(validator);
+ markAsDirty();
+ }
+
+ /**
+ * Gets the validators of the field.
+ *
+ * @return An unmodifiable collection that holds all validators for the
+ * field.
+ */
+ @Override
+ public Collection<Validator> getValidators() {
+ if (validators == null) {
+ return Collections.emptyList();
+ } else {
+ return Collections.unmodifiableCollection(validators);
+ }
+ }
+
+ private boolean hasValidators() {
+ return validators != null && !validators.isEmpty();
+ }
+
+ /**
+ * Removes the validator from the field.
+ *
+ * @param validator
+ * the validator to remove.
+ */
+ @Override
+ public void removeValidator(Validator validator) {
+ if (validators != null) {
+ validators.remove(validator);
+ }
+ markAsDirty();
+ }
+
+ /**
+ * Removes all validators from the field.
+ */
+ @Override
+ public void removeAllValidators() {
+ if (validators != null) {
+ validators.clear();
+ }
+ markAsDirty();
+ }
+
+ /**
+ * 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.
+ *
+ * In most cases, {@link #validate()} should be used instead of
+ * {@link #isValid()} to also get the error message.
+ *
+ * @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.
+ */
+ @Override
+ public boolean isValid() {
+
+ try {
+ validate();
+ return true;
+ } catch (InvalidValueException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Checks the validity of the LegacyField.
+ *
+ * A field is invalid if it is set as required (using
+ * {@link #setRequired(boolean)} and is empty, if one or several of the
+ * validators added to the field indicate it is invalid or if the value
+ * cannot be converted provided a converter has been set.
+ *
+ * The "required" validation is a built-in validation feature. If the field
+ * is required and empty this method throws an EmptyValueException with the
+ * error message set using {@link #setRequiredError(String)}.
+ *
+ * @see com.vaadin.v7.data.Validatable#validate()
+ */
+ @Override
+ public void validate() throws Validator.InvalidValueException {
+
+ if (isRequired() && isEmpty()) {
+ throw new Validator.EmptyValueException(requiredError);
+ }
+ validate(getFieldValue());
+ }
+
+ /**
+ * Validates that the given value pass the validators for the field.
+ * <p>
+ * This method does not check the requiredness of the field.
+ *
+ * @param fieldValue
+ * The value to check
+ * @throws Validator.InvalidValueException
+ * if one or several validators fail
+ */
+ protected void validate(T fieldValue)
+ throws Validator.InvalidValueException {
+
+ Object valueToValidate = fieldValue;
+
+ // If there is a converter we start by converting the value as we want
+ // to validate the converted value
+ if (getConverter() != null) {
+ try {
+ valueToValidate = getConverter().convertToModel(fieldValue,
+ getModelType(), getLocale());
+ } catch (ConversionException e) {
+ throw new InvalidValueException(
+ getConversionError(getConverter().getModelType(), e));
+ }
+ }
+
+ List<InvalidValueException> validationExceptions = new ArrayList<>();
+ if (validators != null) {
+ // Gets all the validation errors
+ for (Validator v : validators) {
+ try {
+ v.validate(valueToValidate);
+ } catch (final Validator.InvalidValueException e) {
+ validationExceptions.add(e);
+ }
+ }
+ }
+
+ // If there were no errors
+ if (validationExceptions.isEmpty()) {
+ return;
+ }
+
+ // If only one error occurred, throw it forwards
+ if (validationExceptions.size() == 1) {
+ throw validationExceptions.get(0);
+ }
+
+ InvalidValueException[] exceptionArray = validationExceptions.toArray(
+ new InvalidValueException[validationExceptions.size()]);
+
+ // Create a composite validator and include all exceptions
+ throw new Validator.InvalidValueException(null, exceptionArray);
+ }
+
+ /**
+ * 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.v7.data.Validatable#isInvalidAllowed()
+ */
+ @Override
+ 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.v7.data.Validatable#setInvalidAllowed(boolean)
+ */
+ @Override
+ 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.
+ */
+ Validator.InvalidValueException 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
+ && getCurrentBufferedSourceException() == null) {
+ return null;
+ }
+
+ // Throw combination of the error types
+ return new CompositeErrorMessage(new ErrorMessage[] { superError,
+ AbstractErrorMessage
+ .getErrorMessageForException(validationError),
+ AbstractErrorMessage.getErrorMessageForException(
+ getCurrentBufferedSourceException()) });
+
+ }
+
+ /* 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 LegacyAbstractField");
+ }
+ }
+
+ /*
+ * Adds a value change listener for the field. Don't add a JavaDoc comment
+ * here, we use the default documentation from the implemented interface.
+ */
+ @Override
+ public void addValueChangeListener(Property.ValueChangeListener listener) {
+ addListener(AbstractField.ValueChangeEvent.class, listener,
+ VALUE_CHANGE_METHOD);
+ // ensure "automatic immediate handling" works
+ markAsDirty();
+ }
+
+ /**
+ * @deprecated As of 7.0, replaced by
+ * {@link #addValueChangeListener(com.vaadin.v7.data.Property.ValueChangeListener)}
+ **/
+ @Override
+ @Deprecated
+ public void addListener(Property.ValueChangeListener listener) {
+ addValueChangeListener(listener);
+ }
+
+ /*
+ * Removes a value change listener from the field. Don't add a JavaDoc
+ * comment here, we use the default documentation from the implemented
+ * interface.
+ */
+ @Override
+ public void removeValueChangeListener(
+ Property.ValueChangeListener listener) {
+ removeListener(AbstractField.ValueChangeEvent.class, listener,
+ VALUE_CHANGE_METHOD);
+ // ensure "automatic immediate handling" works
+ markAsDirty();
+ }
+
+ /**
+ * @deprecated As of 7.0, replaced by
+ * {@link #removeValueChangeListener(com.vaadin.v7.data.Property.ValueChangeListener)}
+ **/
+ @Override
+ @Deprecated
+ public void removeListener(Property.ValueChangeListener listener) {
+ removeValueChangeListener(listener);
+ }
+
+ /**
+ * 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) {
+ markAsDirty();
+ }
+ }
+
+ /* 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 LegacyAbstractField");
+ }
+ }
+
+ /**
+ * React to read only status changes of the property by requesting a
+ * repaint.
+ *
+ * @see Property.ReadOnlyStatusChangeListener
+ */
+ @Override
+ public void readOnlyStatusChange(Property.ReadOnlyStatusChangeEvent event) {
+ boolean readOnly = event.getProperty().isReadOnly();
+
+ boolean shouldFireChange = isReadOnly() != readOnly
+ || getState().propertyReadOnly != readOnly;
+
+ getState().propertyReadOnly = readOnly;
+
+ if (shouldFireChange) {
+ fireReadOnlyStatusChange();
+ }
+ }
+
+ /**
+ * An <code>Event</code> object specifying the Property whose read-only
+ * status has changed.
+ *
+ * @author Vaadin Ltd.
+ * @since 3.0
+ */
+ public static 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.
+ */
+ @Override
+ 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.
+ */
+ @Override
+ public void addReadOnlyStatusChangeListener(
+ Property.ReadOnlyStatusChangeListener listener) {
+ addListener(Property.ReadOnlyStatusChangeEvent.class, listener,
+ READ_ONLY_STATUS_CHANGE_METHOD);
+ }
+
+ /**
+ * @deprecated As of 7.0, replaced by
+ * {@link #addReadOnlyStatusChangeListener(com.vaadin.v7.data.Property.ReadOnlyStatusChangeListener)}
+ **/
+ @Override
+ @Deprecated
+ public void addListener(Property.ReadOnlyStatusChangeListener listener) {
+ addReadOnlyStatusChangeListener(listener);
+ }
+
+ /*
+ * 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.
+ */
+ @Override
+ public void removeReadOnlyStatusChangeListener(
+ Property.ReadOnlyStatusChangeListener listener) {
+ removeListener(Property.ReadOnlyStatusChangeEvent.class, listener,
+ READ_ONLY_STATUS_CHANGE_METHOD);
+ }
+
+ /**
+ * @deprecated As of 7.0, replaced by
+ * {@link #removeReadOnlyStatusChangeListener(com.vaadin.v7.data.Property.ReadOnlyStatusChangeListener)}
+ **/
+ @Override
+ @Deprecated
+ public void removeListener(Property.ReadOnlyStatusChangeListener listener) {
+ removeReadOnlyStatusChangeListener(listener);
+ }
+
+ /**
+ * 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.
+ */
+ @Override
+ public void valueChange(Property.ValueChangeEvent event) {
+ if (!isBuffered()) {
+ if (committingValueToDataSource) {
+ boolean propertyNotifiesOfTheBufferedValue = SharedUtil.equals(
+ event.getProperty().getValue(), getInternalValue());
+ 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(convertFromModel(event.getProperty().getValue()));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void focus() {
+ super.focus();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.ui.Component.Focusable#getTabIndex()
+ */
+ @Override
+ public int getTabIndex() {
+ return getState(false).tabIndex;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.ui.Component.Focusable#setTabIndex(int)
+ */
+ @Override
+ public void setTabIndex(int tabIndex) {
+ getState().tabIndex = tabIndex;
+ }
+
+ /**
+ * Returns the internal field value, which might not match the data source
+ * value e.g. if the field has been modified and is not in write-through
+ * mode.
+ *
+ * This method can be overridden by subclasses together with
+ * {@link #setInternalValue(Object)} to compute internal field value at
+ * runtime. When doing so, typically also {@link #isModified()} needs to be
+ * overridden and care should be taken in the management of the empty state
+ * and buffering support.
+ *
+ * @return internal field value
+ */
+ protected T getInternalValue() {
+ return value;
+ }
+
+ /**
+ * Sets the internal field value. This is purely used by LegacyAbstractField
+ * to change the internal LegacyField value. It does not trigger valuechange
+ * events. It can be overridden by the inheriting classes to update all
+ * dependent variables.
+ *
+ * Subclasses can also override {@link #getInternalValue()} if necessary.
+ *
+ * @param newValue
+ * the new value to be set.
+ */
+ protected void setInternalValue(T newValue) {
+ value = newValue;
+ valueLocale = getLocale();
+ if (validators != null && !validators.isEmpty()) {
+ markAsDirty();
+ }
+ }
+
+ /**
+ * Notifies the component that it is connected to an application.
+ *
+ * @see com.vaadin.ui.Component#attach()
+ */
+ @Override
+ public void attach() {
+ super.attach();
+
+ localeMightHaveChanged();
+ if (!isListeningToPropertyEvents) {
+ addPropertyListeners();
+ if (!isModified() && !isBuffered()) {
+ // Update value from data source
+ updateValueFromDataSource();
+ }
+ }
+ }
+
+ @Override
+ public void setLocale(Locale locale) {
+ super.setLocale(locale);
+ localeMightHaveChanged();
+ }
+
+ private void localeMightHaveChanged() {
+ if (!SharedUtil.equals(valueLocale, getLocale())) {
+ // The locale HAS actually changed
+
+ if (dataSource != null && !isModified()) {
+ // When we have a data source and the internal value is directly
+ // read from that we want to update the value
+ T newInternalValue = convertFromModel(
+ getPropertyDataSource().getValue());
+ if (!SharedUtil.equals(newInternalValue, getInternalValue())) {
+ setInternalValue(newInternalValue);
+ fireValueChange(false);
+ }
+ } else if (dataSource == null && converter != null) {
+ /*
+ * No data source but a converter has been set. The same issues
+ * as above but we cannot use propertyDataSource. Convert the
+ * current value back to a model value using the old locale and
+ * then convert back using the new locale. If this does not
+ * match the field value we need to set the converted value
+ * again.
+ */
+ Object convertedValue = convertToModel(getInternalValue(),
+ valueLocale);
+ T newinternalValue = convertFromModel(convertedValue);
+ if (!SharedUtil.equals(getInternalValue(), newinternalValue)) {
+ setInternalValue(newinternalValue);
+ fireValueChange(false);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void detach() {
+ super.detach();
+ // Stop listening to data source events on detach to avoid a potential
+ // memory leak. See #6155.
+ removePropertyListeners();
+ }
+
+ /**
+ * 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>.
+ */
+ @Override
+ public boolean isRequired() {
+ return getState(false).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.
+ */
+ @Override
+ public void setRequired(boolean required) {
+ getState().required = required;
+ }
+
+ /**
+ * 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.
+ */
+ @Override
+ public void setRequiredError(String requiredMessage) {
+ requiredError = requiredMessage;
+ markAsDirty();
+ }
+
+ @Override
+ public String getRequiredError() {
+ return requiredError;
+ }
+
+ /**
+ * Gets the error that is shown if the field value cannot be converted to
+ * the data source type.
+ *
+ * @return The error that is shown if conversion of the field value fails
+ */
+ public String getConversionError() {
+ return conversionError;
+ }
+
+ /**
+ * Sets the error that is shown if the field value cannot be converted to
+ * the data source type. If {0} is present in the message, it will be
+ * replaced by the simple name of the data source type. If {1} is present in
+ * the message, it will be replaced by the ConversionException message.
+ *
+ * @param valueConversionError
+ * Message to be shown when conversion of the value fails
+ */
+ public void setConversionError(String valueConversionError) {
+ this.conversionError = valueConversionError;
+ markAsDirty();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return (getFieldValue() == null);
+ }
+
+ @Override
+ public void clear() {
+ setValue(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) {
+ markAsDirty();
+ validationVisible = validateAutomatically;
+ }
+ }
+
+ /**
+ * Sets the current buffered source exception.
+ *
+ * @param currentBufferedSourceException
+ */
+ public void setCurrentBufferedSourceException(
+ Buffered.SourceException currentBufferedSourceException) {
+ this.currentBufferedSourceException = currentBufferedSourceException;
+ markAsDirty();
+ }
+
+ /**
+ * Gets the current buffered source exception.
+ *
+ * @return The current source exception
+ */
+ protected Buffered.SourceException getCurrentBufferedSourceException() {
+ return currentBufferedSourceException;
+ }
+
+ /**
+ * 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();
+ }
+ }
+
+ private void updateValueFromDataSource() {
+ if (dataSource != null) {
+
+ // Gets the correct value from datasource
+ T newFieldValue;
+ try {
+
+ // Discards buffer by overwriting from datasource
+ newFieldValue = convertFromModel(getDataSourceValue());
+
+ // If successful, remove set the buffering state to be ok
+ if (getCurrentBufferedSourceException() != null) {
+ setCurrentBufferedSourceException(null);
+ }
+ } catch (final Throwable e) {
+ // FIXME: What should really be done here if conversion fails?
+
+ // Sets the buffering state
+ currentBufferedSourceException = new Buffered.SourceException(
+ this, e);
+ markAsDirty();
+
+ // Throws the source exception
+ throw currentBufferedSourceException;
+ }
+
+ final boolean wasModified = isModified();
+ setModified(false);
+
+ // If the new value differs from the previous one
+ if (!SharedUtil.equals(newFieldValue, getInternalValue())) {
+ setInternalValue(newFieldValue);
+ fireValueChange(false);
+ } else if (wasModified) {
+ // If the value did not change, but the modification status did
+ markAsDirty();
+ }
+ }
+ }
+
+ /**
+ * Gets the converter used to convert the property data source value to the
+ * field value.
+ *
+ * @return The converter or null if none is set.
+ */
+ public Converter<T, Object> getConverter() {
+ return converter;
+ }
+
+ /**
+ * Sets the converter used to convert the field value to property data
+ * source type. The converter must have a presentation type that matches the
+ * field type.
+ *
+ * @param converter
+ * The new converter to use.
+ */
+ public void setConverter(Converter<T, ?> converter) {
+ this.converter = (Converter<T, Object>) converter;
+ markAsDirty();
+ }
+
+ @Override
+ protected AbstractFieldState getState() {
+ return (AbstractFieldState) super.getState();
+ }
+
+ @Override
+ protected AbstractFieldState getState(boolean markAsDirty) {
+ return (AbstractFieldState) super.getState(markAsDirty);
+ }
+
+ @Override
+ public void beforeClientResponse(boolean initial) {
+ super.beforeClientResponse(initial);
+
+ // Hide the error indicator if needed
+ getState().hideErrors = shouldHideErrors();
+ }
+
+ /**
+ * Registers this as an event listener for events sent by the data source
+ * (if any). Does nothing if
+ * <code>isListeningToPropertyEvents == true</code>.
+ */
+ private void addPropertyListeners() {
+ if (!isListeningToPropertyEvents) {
+ if (dataSource instanceof Property.ValueChangeNotifier) {
+ ((Property.ValueChangeNotifier) dataSource).addListener(this);
+ }
+ if (dataSource instanceof Property.ReadOnlyStatusChangeNotifier) {
+ ((Property.ReadOnlyStatusChangeNotifier) dataSource)
+ .addListener(this);
+ }
+ isListeningToPropertyEvents = true;
+ }
+ }
+
+ /**
+ * Stops listening to events sent by the data source (if any). Does nothing
+ * if <code>isListeningToPropertyEvents == false</code>.
+ */
+ private void removePropertyListeners() {
+ if (isListeningToPropertyEvents) {
+ if (dataSource instanceof Property.ValueChangeNotifier) {
+ ((Property.ValueChangeNotifier) dataSource)
+ .removeListener(this);
+ }
+ if (dataSource instanceof Property.ReadOnlyStatusChangeNotifier) {
+ ((Property.ReadOnlyStatusChangeNotifier) dataSource)
+ .removeListener(this);
+ }
+ isListeningToPropertyEvents = false;
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.ui.AbstractComponent#readDesign(org.jsoup.nodes .Element,
+ * com.vaadin.ui.declarative.DesignContext)
+ */
+ @Override
+ public void readDesign(Element design, DesignContext designContext) {
+ super.readDesign(design, designContext);
+ Attributes attr = design.attributes();
+ if (design.hasAttr("readonly")) {
+ setReadOnly(DesignAttributeHandler.readAttribute("readonly", attr,
+ Boolean.class));
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.ui.AbstractComponent#getCustomAttributes()
+ */
+ @Override
+ protected Collection<String> getCustomAttributes() {
+ Collection<String> attributes = super.getCustomAttributes();
+ attributes.add("readonly");
+ // must be handled by subclasses
+ attributes.add("value");
+ attributes.add("converted-value");
+ return attributes;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.ui.AbstractComponent#writeDesign(org.jsoup.nodes.Element
+ * , com.vaadin.ui.declarative.DesignContext)
+ */
+ @Override
+ public void writeDesign(Element design, DesignContext designContext) {
+ super.writeDesign(design, designContext);
+ AbstractField def = (AbstractField) designContext
+ .getDefaultInstance(this);
+ Attributes attr = design.attributes();
+ // handle readonly
+ DesignAttributeHandler.writeAttribute("readonly", attr,
+ super.isReadOnly(), def.isReadOnly(), Boolean.class);
+ }
+
+ private static final Logger getLogger() {
+ return Logger.getLogger(AbstractField.class.getName());
+ }
+}
diff --git a/compatibility-server/src/main/java/com/vaadin/v7/ui/Field.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/Field.java
new file mode 100644
index 0000000000..0756dfaf90
--- /dev/null
+++ b/compatibility-server/src/main/java/com/vaadin/v7/ui/Field.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2000-2016 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.v7.ui;
+
+import com.vaadin.data.HasRequired;
+import com.vaadin.data.HasValue.ValueChange;
+import com.vaadin.ui.Component;
+import com.vaadin.ui.Component.Focusable;
+import com.vaadin.v7.data.BufferedValidatable;
+import com.vaadin.v7.data.Property;
+
+/**
+ * LegacyField interface is implemented by all legacy field components that have
+ * a value that the user can change through the user interface.
+ *
+ * LegacyField components are built upon the framework defined in the
+ * LegacyField interface and the {@link com.vaadin.AbstractField} base
+ * class.
+ *
+ * The LegacyField interface inherits the {@link com.vaadin.ui.Component}
+ * superinterface and also the {@link com.vaadin.ui.Property} interface to have
+ * a value for the field.
+ *
+ * @author Vaadin Ltd.
+ *
+ * @param <T>
+ * the type of values in the field, which might not be the same type
+ * as that of the data source if converters are used
+ *
+ * @deprecated This interface is, apart from the rename, identical to the Vaadin
+ * 7 {@code com.vaadin.ui.Field}. It is provided for compatibility
+ * and migration purposes. As of 8.0, new field components should
+ * extend {@link com.vaadin.ui.AbstractField} instead.
+ */
+@Deprecated
+public interface Field<T> extends Component, BufferedValidatable,
+ Property<T>, Property.ValueChangeNotifier, Property.ValueChangeListener,
+ Property.Editor, Focusable, HasRequired {
+ /**
+ * Is this field required.
+ *
+ * Required fields must filled by the user.
+ *
+ * @return <code>true</code> if the field is required,otherwise
+ * <code>false</code>.
+ * @since 3.1
+ */
+ @Override
+ public boolean isRequired();
+
+ /**
+ * Sets the field required. Required fields must filled by the user.
+ *
+ * @param required
+ * Is the field required.
+ * @since 3.1
+ */
+ @Override
+ public void setRequired(boolean required);
+
+ /**
+ * Sets the error message to be displayed if a required field is empty.
+ *
+ * @param requiredMessage
+ * Error message.
+ * @since 5.2.6
+ */
+ public void setRequiredError(String requiredMessage);
+
+ /**
+ * Gets the error message that is to be displayed if a required field is
+ * empty.
+ *
+ * @return Error message.
+ * @since 5.2.6
+ */
+ public String getRequiredError();
+
+ /**
+ * An <code>Event</code> object specifying the LegacyField whose value has
+ * been changed.
+ *
+ * @author Vaadin Ltd.
+ * @since 3.0
+ *
+ * @deprecated As of 8.0, replaced by {@link ValueChange}.
+ */
+ @Deprecated
+ @SuppressWarnings("serial")
+ public static class ValueChangeEvent extends Component.Event
+ implements Property.ValueChangeEvent {
+
+ /**
+ * Constructs a new event object with the specified source field object.
+ *
+ * @param source
+ * the field that caused the event.
+ */
+ public ValueChangeEvent(Field source) {
+ super(source);
+ }
+
+ /**
+ * Gets the Property which triggered the event.
+ *
+ * @return the Source Property of the event.
+ */
+ @Override
+ public Property getProperty() {
+ return (Property) getSource();
+ }
+
+ }
+
+ /**
+ * Is the field empty?
+ *
+ * In general, "empty" state is same as null. As an exception, TextField
+ * also treats empty string as "empty".
+ *
+ * @since 7.4
+ * @return true if the field is empty, false otherwise
+ */
+ public boolean isEmpty();
+
+ /**
+ * Clears the value of the field.
+ * <p>
+ * The field value is typically reset to the initial value of the field.
+ * Calling {@link #isEmpty()} on a cleared field must always returns true.
+ *
+ * @since 7.4
+ */
+ public void clear();
+
+}
diff --git a/compatibility-server/src/test/java/com/vaadin/v7/tests/VaadinClasses.java b/compatibility-server/src/test/java/com/vaadin/v7/tests/VaadinClasses.java
new file mode 100644
index 0000000000..b37f1bbe86
--- /dev/null
+++ b/compatibility-server/src/test/java/com/vaadin/v7/tests/VaadinClasses.java
@@ -0,0 +1,21 @@
+package com.vaadin.v7.tests;
+
+import java.io.IOException;
+import java.util.List;
+
+import com.vaadin.v7.ui.Field;
+
+@SuppressWarnings("deprecation")
+public class VaadinClasses {
+
+ public static List<Class<? extends Field>> getFields() {
+ try {
+ return com.vaadin.tests.VaadinClasses.findClasses(Field.class,
+ "com.vaadin.ui");
+ } catch (IOException e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+}
diff --git a/compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/abstractfield/AbstractFieldValueChangeTestBase.java b/compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/abstractfield/AbstractFieldValueChangeTestBase.java
new file mode 100644
index 0000000000..e37e19aecb
--- /dev/null
+++ b/compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/abstractfield/AbstractFieldValueChangeTestBase.java
@@ -0,0 +1,130 @@
+package com.vaadin.v7.tests.server.component.abstractfield;
+
+import org.easymock.EasyMock;
+import org.junit.Test;
+
+import com.vaadin.v7.data.Property.ValueChangeEvent;
+import com.vaadin.v7.data.Property.ValueChangeListener;
+import com.vaadin.v7.data.Property.ValueChangeNotifier;
+import com.vaadin.v7.data.util.ObjectProperty;
+import com.vaadin.v7.ui.AbstractField;
+
+/**
+ * Base class for tests for checking that value change listeners for fields are
+ * not called exactly once when they should be, and not at other times.
+ *
+ * Does not check all cases (e.g. properties that do not implement
+ * {@link ValueChangeNotifier}).
+ *
+ * Subclasses should implement {@link #setValue()} and call
+ * <code>super.setValue(LegacyAbstractField)</code>. Also, subclasses should
+ * typically override {@link #setValue(AbstractField)} to set the field
+ * value via <code>changeVariables()</code>.
+ */
+public abstract class AbstractFieldValueChangeTestBase<T> {
+
+ private AbstractField<T> field;
+ private ValueChangeListener listener;
+
+ protected void setUp(AbstractField<T> field) {
+ this.field = field;
+ listener = EasyMock.createStrictMock(ValueChangeListener.class);
+
+ }
+
+ protected ValueChangeListener getListener() {
+ return listener;
+ }
+
+ /**
+ * Test that listeners are not called when they have been unregistered.
+ */
+ @Test
+ public void testRemoveListener() {
+ getField().setPropertyDataSource(new ObjectProperty<String>(""));
+ getField().setBuffered(false);
+
+ // Expectations and start test
+ listener.valueChange(EasyMock.isA(ValueChangeEvent.class));
+ EasyMock.replay(listener);
+
+ // Add listener and set the value -> should end up in listener once
+ getField().addListener(listener);
+ setValue(getField());
+
+ // Ensure listener was called once
+ EasyMock.verify(listener);
+
+ // Remove the listener and set the value -> should not end up in
+ // listener
+ getField().removeListener(listener);
+ setValue(getField());
+
+ // Ensure listener still has been called only once
+ EasyMock.verify(listener);
+ }
+
+ /**
+ * Common unbuffered case: both writeThrough (auto-commit) and readThrough
+ * are on. Calling commit() should not cause notifications.
+ *
+ * Using the readThrough mode allows changes made to the property value to
+ * be seen in some cases also when there is no notification of value change
+ * from the property.
+ *
+ * LegacyField value change notifications closely mirror value changes of
+ * the data source behind the field.
+ */
+ @Test
+ public void testNonBuffered() {
+ getField().setPropertyDataSource(new ObjectProperty<String>(""));
+ getField().setBuffered(false);
+
+ expectValueChangeFromSetValueNotCommit();
+ }
+
+ /**
+ * Fully buffered use where the data source is neither read nor modified
+ * during editing, and is updated at commit().
+ *
+ * LegacyField value change notifications reflect the buffered value in the
+ * field, not the original data source value changes.
+ */
+ public void testBuffered() {
+ getField().setPropertyDataSource(new ObjectProperty<String>(""));
+ getField().setBuffered(true);
+
+ expectValueChangeFromSetValueNotCommit();
+ }
+
+ protected void expectValueChangeFromSetValueNotCommit() {
+ // Expectations and start test
+ listener.valueChange(EasyMock.isA(ValueChangeEvent.class));
+ EasyMock.replay(listener);
+
+ // Add listener and set the value -> should end up in listener once
+ getField().addListener(listener);
+ setValue(getField());
+
+ // Ensure listener was called once
+ EasyMock.verify(listener);
+
+ // commit
+ getField().commit();
+
+ // Ensure listener was not called again
+ EasyMock.verify(listener);
+ }
+
+ protected AbstractField<T> getField() {
+ return field;
+ }
+
+ /**
+ * Override in subclasses to set value with changeVariables().
+ */
+ protected void setValue(AbstractField<T> field) {
+ field.setValue((T) "newValue");
+ }
+
+}
diff --git a/compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/abstractfield/FieldDefaultValuesTest.java b/compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/abstractfield/FieldDefaultValuesTest.java
new file mode 100644
index 0000000000..a53e3bbbd1
--- /dev/null
+++ b/compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/abstractfield/FieldDefaultValuesTest.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2000-2016 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.v7.tests.server.component.abstractfield;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import com.vaadin.ui.Slider;
+import com.vaadin.v7.tests.VaadinClasses;
+import com.vaadin.v7.ui.Field;
+
+public class FieldDefaultValuesTest {
+
+ @Test
+ public void testFieldsHaveDefaultValueAfterClear() throws Exception {
+ for (Field<?> field : createFields()) {
+ Object originalValue = field.getValue();
+
+ field.clear();
+
+ Object clearedValue = field.getValue();
+
+ Assert.assertEquals(
+ "Expected to get default value after clearing "
+ + field.getClass().getName(),
+ originalValue, clearedValue);
+ }
+ }
+
+ @Test
+ public void testFieldsAreEmptyAfterClear() throws Exception {
+ for (Field<?> field : createFields()) {
+ field.clear();
+
+ if (field instanceof Slider) {
+ Assert.assertFalse(
+ "Slider should not be empty even after being cleared",
+ field.isEmpty());
+
+ } else {
+ Assert.assertTrue(
+ field.getClass().getName()
+ + " should be empty after being cleared",
+ field.isEmpty());
+ }
+ }
+ }
+
+ @SuppressWarnings("rawtypes")
+ private static List<Field<?>> createFields()
+ throws InstantiationException, IllegalAccessException {
+ List<Field<?>> fieldInstances = new ArrayList<Field<?>>();
+
+ for (Class<? extends Field> fieldType : VaadinClasses.getFields()) {
+ fieldInstances.add(fieldType.newInstance());
+ }
+ return fieldInstances;
+ }
+
+}
diff --git a/compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/textfield/TextFieldValueChangeTest.java b/compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/textfield/TextFieldValueChangeTest.java
index bb286296b4..9a35dbeacd 100644
--- a/compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/textfield/TextFieldValueChangeTest.java
+++ b/compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/textfield/TextFieldValueChangeTest.java
@@ -8,9 +8,9 @@ import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
-import com.vaadin.tests.server.components.AbstractFieldValueChangeTestBase;
import com.vaadin.v7.data.Property.ValueChangeEvent;
import com.vaadin.v7.data.util.ObjectProperty;
+import com.vaadin.v7.tests.server.component.abstractfield.AbstractFieldValueChangeTestBase;
import com.vaadin.v7.ui.AbstractField;
import com.vaadin.v7.ui.TextField;
diff --git a/compatibility-server/src/test/java/com/vaadin/v7/tests/server/components/ComboBoxValueChangeTest.java b/compatibility-server/src/test/java/com/vaadin/v7/tests/server/components/ComboBoxValueChangeTest.java
index dbba1a0e9f..ffb46c519a 100644
--- a/compatibility-server/src/test/java/com/vaadin/v7/tests/server/components/ComboBoxValueChangeTest.java
+++ b/compatibility-server/src/test/java/com/vaadin/v7/tests/server/components/ComboBoxValueChangeTest.java
@@ -4,8 +4,8 @@ import org.junit.Before;
import com.vaadin.server.ServerRpcManager;
import com.vaadin.server.ServerRpcMethodInvocation;
-import com.vaadin.tests.server.components.AbstractFieldValueChangeTestBase;
import com.vaadin.v7.shared.ui.combobox.ComboBoxServerRpc;
+import com.vaadin.v7.tests.server.component.abstractfield.AbstractFieldValueChangeTestBase;
import com.vaadin.v7.ui.AbstractField;
import com.vaadin.v7.ui.ComboBox;