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.
*
* In most cases, {@link #validate()} should be used instead of
* {@link #isValid()} to also get the error message.
*
* @return true
if all registered validators claim that the
* current value is valid or if the field is empty and not required,
* false
otherwise.
*/
public boolean isValid() {
try {
validate();
return true;
} catch (InvalidValueException e) {
return false;
}
}
/**
* Checks the validity of the Field.
*
* 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 value 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.data.Validatable#validate()
*/
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.
*
* 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 (getValueConverter() != null) {
try {
valueToValidate = getValueConverter()
.convertFromTargetToSource(fieldValue, getLocale());
} catch (Exception e) {
throw new InvalidValueException(e.getMessage());
}
}
List 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.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.
*
* In common setting where the user wants to assure the correctness of the
* datasource, but allow temporarily invalid contents in the field, the user
* should add the validators to datasource, that should not allow invalid
* values. The validators are automatically copied to the field when the
* datasource is set.
*
*
* @see com.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
&& getCurrentBufferedSourceException() == null) {
return null;
}
// Throw combination of the error types
return new CompositeErrorMessage(new ErrorMessage[] { superError,
validationError, 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 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 Event
object specifying the Property whose read-only
* status has changed.
*
* @author Vaadin Ltd.
* @version
* @VERSION@
* @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.
*/
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 = 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(convertFromDataSource(event.getProperty().getValue()));
}
@Override
public void changeVariables(Object source, Map variables) {
super.changeVariables(source, variables);
}
/**
* {@inheritDoc}
*/
@Override
public void focus() {
super.focus();
}
/*
* (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();
}
/**
*
* @return
*/
protected T getInternalValue() {
return value;
}
/**
* 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(T newValue) {
value = newValue;
if (validators != null && !validators.isEmpty()) {
requestRepaint();
}
}
/**
* 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 true
if the field is required .otherwise
* false
.
*/
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 (getFieldValue() == 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();
}
/**
* 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();
}
}
/**
* 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 getValueConverter() {
return valueConverter;
}
/**
* Sets the converter used to convert the property data source value to the
* field value. The converter must have a target type that matches the field
* type.
*
* The source for the converter is the data model and the target is the
* field.
*
* @param valueConverter
* The new value converter to use.
*/
public void setValueConverter(Converter, T> valueConverter) {
//
this.valueConverter = (Converter) valueConverter;
requestRepaint();
}
}