/* * Copyright 2000-2021 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.lang.reflect.Method; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Locale; 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.util.SharedUtil; 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.BufferedValidatable; 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; import com.vaadin.v7.shared.AbstractFieldState; /** *
* 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 Property} interface. AbstractField
* implements that interface itself, too, so accessing the Property value
* represented by it is straightforward.
*
* AbstractField also provides the {@link 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. *
* ** The class also supports {@link Validator validators} to make sure the value * contained in the field is valid. *
* * @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 AbstractFieldgetValue
and
* setValue
must be compatible with this type: one must be able
* to safely cast the value returned from getValue
to the given
* type and pass any variable assignable to this type as an argument to
* setValue
.
*
* @return the type of the Field
*/
@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 BufferedValidatable#isInvalidCommitted()
*/
@Override
public boolean isInvalidCommitted() {
return invalidCommitted;
}
/**
* Sets if the invalid data should be committed to datasource.
*
* @see 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 Field.
* * When the field is in buffered mode, changes will not be committed to the * property data source until {@link #commit()} is called. *
** Setting buffered mode from true to false will commit any pending changes. *
** *
* * @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 Field. * * @return true if buffered mode is on, false otherwise */ @Override public boolean isBuffered() { return buffered; } // LegacyPropertyHelper has been removed in Vaadin 8 /* Property interface implementation */ /** * Gets the current value of the field. * ** This is the visible, modified and possible invalid value the user have * entered to the field. *
* ** 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. *
* ** Since Vaadin 7.0, no implicit conversions between other data types and * String are performed, but a converter is used if set. *
* * @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 if 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 if caller is sure that repaint is not needed. * @param ignoreReadOnly * True 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, ornull
if
* none defined.
*/
@Override
public Property getPropertyDataSource() {
return dataSource;
}
/**
* * Sets the specified Property as the data source for the field. All * uncommitted changes are replaced with a value from the new data source. *
* ** 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. *
* ** If the data source implements {@link Property.ValueChangeNotifier} and/or * {@link 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}. *
* ** 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(). *
* * @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