/*
* Copyright (c) 2002-2015, JIDE Software Inc. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package jidefx.scene.control.validation;
import com.jidefx.utils.function.TriFunction;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.ObservableMap;
import javafx.css.PseudoClass;
import javafx.css.Styleable;
import javafx.event.EventHandler;
import javafx.geometry.Point2D;
import javafx.scene.Node;
import javafx.scene.control.Cell;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.ComboBox;
import javafx.scene.control.DatePicker;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.control.RadioButton;
import javafx.scene.control.TableView;
import javafx.scene.control.TextInputControl;
import javafx.scene.control.Tooltip;
import javafx.scene.control.TreeView;
import javafx.scene.layout.Region;
import jidefx.scene.control.decoration.DecorationUtils;
import jidefx.scene.control.decoration.Decorator;
import jidefx.utils.CommonUtils;
import jidefx.utils.FXUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@SuppressWarnings({"UnusedDeclaration", "Convert2Lambda"})
public class ValidationUtils {
public static class TooltipFix extends Tooltip {
private Node parentNode;
public TooltipFix(Node parentNode) {
this.parentNode = parentNode;
}
@Override
public Styleable getStyleableParent() {
Styleable styleableParent = super.getStyleableParent();
if (styleableParent != null) {
return styleableParent;
} else {
return parentNode;
}
}
}
private static final String PROPERTY_ON_DEMAND_VALIDATOR = "Validation.On.Demand.Validator"; //NON-NLS
private static final String PROPERTY_ON_DEMAND_OBSERVABLE_VALUE = "Validation.On.Demand.ObservableValue"; //NON-NLS
private static final String PROPERTY_ON_DEMAND_EVENT_FILTER = "Validation.On.Demand.EventFilter"; //NON-NLS
private static final String PROPERTY_ON_DEMAND_OBJECT = "Validation.On.Demand.Object"; //NON-NLS
private static final String PROPERTY_ON_FLY_VALIDATOR = "Validation.On.Fly.Validator"; //NON-NLS
private static final String PROPERTY_ON_FLY_OBSERVABLE_VALUE = "Validation.On.Fly.ObservableValue"; //NON-NLS
private static final String PROPERTY_ON_FLY_LISTENER = "Validation.On.Fly.Listener"; //NON-NLS
private static final String PROPERTY_ON_FLY_EVENT_FILTER = "Validation.On.Fly.EventFilter"; //NON-NLS
private static final String PROPERTY_ON_FOCUS_LOST_VALIDATOR = "Validation.On.FocusLost.Validator"; //NON-NLS
private static final String PROPERTY_ON_FOCUS_LOST_LISTENER = "Validation.On.FocusLost.Listener"; //NON-NLS
private static final String PROPERTY_ON_FOCUS_LOST_EVENT_FILTER = "Validation.On.FocusLost.EventFilter"; //NON-NLS
private static final String PROPERTY_ON_FOCUS_LOST_OBJECT = "Validation.On.FocusLost.Object"; //NON-NLS
private static final String PROPERTY_VALIDATION_RESULT = "Validation.Result";
private static final String PROPERTY_VALIDATION_RESULT_MESSAGE = "Validation.Result.Message";
public static final String PSEUDO_CLASS_VALIDATION_ERROR = "validation-error"; //NON-NLS
public static final String PSEUDO_CLASS_VALIDATION_WARNING = "validation-warning"; //NON-NLS
public static final String PSEUDO_CLASS_VALIDATION_INFO = "validation-info"; //NON-NLS
public static final String PSEUDO_CLASS_VALIDATION_OK = "validation-ok"; //NON-NLS
/**
* Installs a validator to a target node using the ON_FLY validation mode. If there is an old validator, it will be
* uninstalled first. A default observable value will be used for the give node. For example, textProperty of a
* TextInputControl or its subclasses, selectedItemProperty of the SelectionModel for ListView, TableView, TreeView,
* ChoiceBox, and ComboBox, selectedProperty for CheckBox and RadioButton. A default event handler will be installed
* on the node to display the validation result.
*
* @param targetNode the target node where the validator will be installed
* @param validator the validator
* @param <T> the date type of the observable value.
* @return true or false. True if the validator is installed correctly. It actually always return true as long as
* the observable value and the validator are not null.
*/
public static <T> boolean install(Node targetNode, Validator validator) {
return install(targetNode, getDefaultObservableValue(targetNode), validator, getDefaultMode(), createDefaultValidationEventHandler(targetNode, ValidationDecorators::graphicDecoratorCreator));
}
/**
* Installs a validator to a target node. If there is an old validator, it will be uninstalled first. A default
* observable value will be used for the give node. For example, textProperty of a TextInputControl or its
* subclasses, selectedItemProperty of the SelectionModel for ListView, TableView, TreeView, ChoiceBox, and
* ComboBox, selectedProperty for CheckBox and RadioButton. A default event handler will be installed on the node to
* display the validation result.
*
* @param targetNode the target node where the validator will be installed
* @param validator the validator
* @param mode the validation mode
* @param <T> the date type of the observable value.
* @return true or false. True if the validator is installed correctly. It actually always return true as long as
* the observable value and the validator are not null.
*/
public static <T> boolean install(Node targetNode, Validator validator, ValidationMode mode) {
return install(targetNode, getDefaultObservableValue(targetNode), validator, mode, createDefaultValidationEventHandler(targetNode, ValidationDecorators::graphicDecoratorCreator));
}
/**
* Installs a validator to a target node using the ON_FLY validation mode. If there is an old validator, it will be
* uninstalled first. A default event handler will be installed on the node to display the validation result.
*
* @param targetNode the target node where the validator will be installed
* @param observableValue the observable value to listen to if the validation mode is ON_FLY. In the other two
* modes, there is where the value to be validated is retrieved
* @param validator the validator
* @param <T> the date type of the observable value.
* @return true or false. True if the validator is installed correctly. It actually always return true as long as
* the observable value and the validator are not null.
*/
public static <T> boolean install(Node targetNode, ObservableValue<T> observableValue, Validator validator) {
return install(targetNode, observableValue, validator, ValidationMode.ON_FLY, createDefaultValidationEventHandler(targetNode, ValidationDecorators::graphicDecoratorCreator));
}
/**
* Installs a validator to a target node. If there is an old validator, it will be uninstalled first. A default
* event handler will be installed on the node to display the validation result.
*
* @param targetNode the target node where the validator will be installed
* @param observableValue the observable value to listen to if the validation mode is ON_FLY. In the other two
* modes, there is where the value to be validated is retrieved
* @param validator the validator
* @param mode the validation mode
* @param <T> the date type of the observable value.
* @return true or false. True if the validator is installed correctly. It actually always return true as long as
* the observable value and the validator are not null.
*/
public static <T> boolean install(Node targetNode, ObservableValue<T> observableValue, Validator validator, ValidationMode mode) {
return install(targetNode, observableValue, validator, mode, createDefaultValidationEventHandler(targetNode, ValidationDecorators::graphicDecoratorCreator));
}
/**
* Installs a validator to a target node using the ON_FLY validation mode. If there is an old validator, it will be
* uninstalled first.
*
* @param targetNode the target node where the validator will be installed
* @param observableValue the observable value to listen to if the validation mode is ON_FLY. In the other two
* modes, there is where the value to be validated is retrieved
* @param validator the validator
* @param eventFilter the event handler. It will be added to the target node. When there is a validation event,
* this handler will response to it and display the validation result.
* @param <T> the date type of the observable value.
* @return true or false. True if the validator is installed correctly. It actually always return true as long as
* the observable value and the validator are not null.
*/
public static <T> boolean install(Node targetNode, ObservableValue<T> observableValue, Validator validator, EventHandler<ValidationEvent> eventFilter) {
return install(targetNode, observableValue, validator, getDefaultMode(), eventFilter);
}
/**
* Installs a validator to a target node. If there is an old validator, it will be uninstalled first.
*
* @param targetNode the target node where the validator will be installed
* @param observableValue the observable value to listen to if the validation mode is ON_FLY. In the other two
* modes, there is where the value to be validated is retrieved
* @param validator the validator
* @param mode the validation mode
* @param eventFilter the event handler. It will be added to the target node. When there is a validation event,
* this handler will response to it and display the validation result.
* @param <T> the date type of the observable value.
* @return true or false. True if the validator is installed correctly. It actually always return true as long as
* the observable value and the validator are not null.
*/
public static <T> boolean install(Node targetNode, ObservableValue<T> observableValue, Validator validator, ValidationMode mode, EventHandler<ValidationEvent> eventFilter) {
if (observableValue == null || validator == null) {
return false;
}
switch (mode) {
case ON_FOCUS_LOST:
return setOnFocusLostValidation(targetNode, validator, observableValue, eventFilter);
case ON_DEMAND:
return setOnDemandValidation(targetNode, validator, observableValue, eventFilter);
case ON_FLY:
return setOnFlyValidation(targetNode, validator, observableValue, eventFilter);
}
return false;
}
public static <T> boolean install(Node targetNode, ObservableValue<T> observableValue, Validator validator, ValidationMode mode, TriFunction<Node, Decorator, ValidationEvent, Decorator<Label>> decorationCreator) {
return install(targetNode, observableValue, validator, mode, createDefaultValidationEventHandler(targetNode, decorationCreator));
}
// TODO: what if the targetNode is a TableView, ListView or a TreeView. Can the validator work as well when the validation is for an editing cell??
private static ObservableValue getDefaultObservableValue(Node targetNode) {
ObservableValue observableValue = null;
Class clazz = targetNode.getClass();
if (TextInputControl.class.isAssignableFrom(clazz)) {
return ((TextInputControl) targetNode).textProperty();
} else if (ListView.class.isAssignableFrom(clazz)) {
return ((ListView) targetNode).getSelectionModel().selectedItemProperty();
} else if (TreeView.class.isAssignableFrom(clazz)) {
return ((TreeView) targetNode).getSelectionModel().selectedItemProperty();
} else if (TableView.class.isAssignableFrom(clazz)) {
return ((TableView) targetNode).getSelectionModel().selectedItemProperty();
} else if (ComboBox.class.isAssignableFrom(clazz)) {
return ((ComboBox) targetNode).getSelectionModel().selectedItemProperty();
} else if (ChoiceBox.class.isAssignableFrom(clazz)) {
return ((ChoiceBox) targetNode).getSelectionModel().selectedItemProperty();
} else if (CheckBox.class.isAssignableFrom(clazz)) {
return ((CheckBox) targetNode).selectedProperty();
} else if (RadioButton.class.isAssignableFrom(clazz)) {
return ((RadioButton) targetNode).selectedProperty();
} else if (DatePicker.class.isAssignableFrom(clazz)) {
return ((DatePicker) targetNode).valueProperty();
}
return observableValue;
}
private static ValidationMode getDefaultMode() {
return ValidationMode.ON_FLY;
}
private static <T> boolean setOnFlyValidation(Node targetNode, Validator validator, ObservableValue<T> targetProperty, EventHandler<ValidationEvent> eventFilter) {
uninstall(targetNode, ValidationMode.ON_FLY);
ChangeListener<T> listener = new ChangeListener<T>() {
@Override
public void changed(ObservableValue<? extends T> observable, T oldValue, T newValue) {
ValidationObject validationObject = new ValidationObject(targetNode, oldValue, newValue);
ValidationEvent event = validator.call(validationObject);
targetNode.fireEvent(event);
}
};
targetProperty.addListener(listener);
targetNode.addEventFilter(ValidationEvent.ANY, eventFilter);
targetNode.getProperties().put(PROPERTY_ON_FLY_VALIDATOR, validator);
targetNode.getProperties().put(PROPERTY_ON_FLY_OBSERVABLE_VALUE, targetProperty);
targetNode.getProperties().put(PROPERTY_ON_FLY_EVENT_FILTER, eventFilter);
targetNode.getProperties().put(PROPERTY_ON_FLY_LISTENER, listener);
return true;
}
private static <T> boolean setOnFocusLostValidation(Node targetNode, Validator validator, ObservableValue<T> targetProperty, EventHandler<ValidationEvent> eventFilter) {
uninstall(targetNode, ValidationMode.ON_FOCUS_LOST);
ChangeListener<Boolean> listener = new ChangeListener<Boolean>() {
@Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
if (newValue) { // focus gained
targetNode.getProperties().put(PROPERTY_ON_FOCUS_LOST_OBJECT, targetProperty.getValue()); // save old value
} else { // focus lost
Object oldValidationValue = targetNode.getProperties().get(PROPERTY_ON_FOCUS_LOST_OBJECT);
T newValidationValue = targetProperty.getValue();
if (!CommonUtils.equals(oldValidationValue, newValidationValue)) {
ValidationObject validationObject = new ValidationObject(targetNode, oldValidationValue, newValidationValue);
ValidationEvent event = validator.call(validationObject);
targetNode.fireEvent(event);
}
}
}
};
targetNode.focusedProperty().addListener(listener);
targetNode.addEventFilter(ValidationEvent.ANY, eventFilter);
targetNode.getProperties().put(PROPERTY_ON_FOCUS_LOST_VALIDATOR, validator);
targetNode.getProperties().put(PROPERTY_ON_FOCUS_LOST_EVENT_FILTER, eventFilter);
targetNode.getProperties().put(PROPERTY_ON_FOCUS_LOST_LISTENER, listener);
targetNode.getProperties().put(PROPERTY_ON_FOCUS_LOST_OBJECT, targetProperty.getValue());
return true;
}
private static <T> boolean setOnDemandValidation(Node targetNode, Validator validator, ObservableValue<T> targetProperty, EventHandler<ValidationEvent> eventFilter) {
uninstall(targetNode, ValidationMode.ON_DEMAND);
targetNode.addEventFilter(ValidationEvent.ANY, eventFilter);
targetNode.getProperties().put(PROPERTY_ON_DEMAND_VALIDATOR, validator);
targetNode.getProperties().put(PROPERTY_ON_DEMAND_OBSERVABLE_VALUE, targetProperty);
targetNode.getProperties().put(PROPERTY_ON_DEMAND_EVENT_FILTER, eventFilter);
targetNode.getProperties().put(PROPERTY_ON_DEMAND_OBJECT, targetProperty.getValue()); // save old value
return true;
}
/**
* Uninstalls the validators from the target node for all validation modes.
*
* @param targetNode the node to be validated.
* @return true if uninstalled successfully. Otherwise false.
*/
public static boolean uninstall(Node targetNode) {
return uninstall(targetNode, ValidationMode.ON_FLY) && uninstall(targetNode, ValidationMode.ON_FOCUS_LOST) && uninstall(targetNode, ValidationMode.ON_DEMAND);
}
/**
* Uninstalls the validator from the target node for the given validation mode.
*
* @param targetNode the node to be validated.
* @param mode the validation mode.
* @return true if uninstalled successfully. If the target node is null or there is no validators installed, it will
* return false. Please be noted that all validators will be cleared no matter what is returned.
*/
@SuppressWarnings("unchecked")
public static boolean uninstall(Node targetNode, ValidationMode mode) {
if (targetNode == null) {
return false;
}
ValidationMode removeMode = mode;
if (removeMode == null) {
removeMode = getDefaultMode();
}
switch (removeMode) {
case ON_DEMAND: {
Object eventFilter = targetNode.getProperties().get(PROPERTY_ON_DEMAND_EVENT_FILTER);
if (eventFilter instanceof EventHandler) {
targetNode.removeEventFilter(ValidationEvent.ANY, (EventHandler<ValidationEvent>) eventFilter);
}
Object remove = targetNode.getProperties().remove(PROPERTY_ON_DEMAND_VALIDATOR);
if (remove != null) {
targetNode.getProperties().remove(PROPERTY_ON_DEMAND_OBSERVABLE_VALUE);
targetNode.getProperties().remove(PROPERTY_ON_DEMAND_EVENT_FILTER);
} else return false;
}
break;
case ON_FOCUS_LOST: {
Object eventFilter = targetNode.getProperties().get(PROPERTY_ON_FOCUS_LOST_EVENT_FILTER);
if (eventFilter instanceof EventHandler) {
targetNode.removeEventFilter(ValidationEvent.ANY, (EventHandler<ValidationEvent>) eventFilter);
}
Object remove = targetNode.getProperties().remove(PROPERTY_ON_FOCUS_LOST_VALIDATOR);
if (remove != null) {
targetNode.getProperties().remove(PROPERTY_ON_FOCUS_LOST_LISTENER);
targetNode.getProperties().remove(PROPERTY_ON_FOCUS_LOST_EVENT_FILTER);
targetNode.getProperties().remove(PROPERTY_ON_FOCUS_LOST_OBJECT);
Object listener = targetNode.getProperties().get(PROPERTY_ON_FOCUS_LOST_LISTENER);
if (listener instanceof ChangeListener) {
targetNode.focusedProperty().removeListener((ChangeListener) listener);
} else return false;
} else return false;
}
break;
case ON_FLY:
default: {
Object eventFilter = targetNode.getProperties().get(PROPERTY_ON_FLY_EVENT_FILTER);
if (eventFilter instanceof EventHandler) {
targetNode.removeEventFilter(ValidationEvent.ANY, (EventHandler<ValidationEvent>) eventFilter);
}
DecorationUtils.uninstall(targetNode);
Object remove = targetNode.getProperties().remove(PROPERTY_ON_FLY_VALIDATOR);
if (remove != null) {
targetNode.getProperties().remove(PROPERTY_ON_FLY_OBSERVABLE_VALUE);
targetNode.getProperties().remove(PROPERTY_ON_FLY_LISTENER);
targetNode.getProperties().remove(PROPERTY_ON_FLY_EVENT_FILTER);
targetNode.getProperties().remove(PROPERTY_VALIDATION_RESULT);
targetNode.getProperties().remove(PROPERTY_VALIDATION_RESULT_MESSAGE);
Object onFlyValue = targetNode.getProperties().get(PROPERTY_ON_FLY_OBSERVABLE_VALUE);
Object onFlyListener = targetNode.getProperties().get(PROPERTY_ON_FLY_LISTENER);
if (onFlyValue instanceof ObservableValue && onFlyListener instanceof ChangeListener) {
((ObservableValue) onFlyValue).removeListener((ChangeListener) onFlyListener);
} else return false;
} else return false;
}
break;
}
return true;
}
/**
* Force the validator to execute validation immediately when there is no correspond event. Should be used ON_FLY
* and ON_FOCUS_LOST validation modes.
*
* @param validateNode the validate node
* @param mode the validation mode, ON_FLY or ON_FOCUS_LOST.
*/
public static void forceValidate(Node validateNode, ValidationMode mode) {
Object validator;
ObservableValue observableValue;
switch (mode) {
case ON_FLY:
validator = validateNode.getProperties().get(PROPERTY_ON_FLY_VALIDATOR);
observableValue = (ObservableValue) validateNode.getProperties().get(PROPERTY_ON_FLY_OBSERVABLE_VALUE);
if (validator instanceof Validator) {
ValidationObject object = new ValidationObject(validateNode, null, observableValue.getValue());
ValidationEvent event = ((Validator) validator).call(object);
validateNode.fireEvent(event);
}
break;
case ON_FOCUS_LOST:
validator = validateNode.getProperties().get(PROPERTY_ON_FLY_VALIDATOR);
observableValue = (ObservableValue) validateNode.getProperties().get(PROPERTY_ON_FOCUS_LOST_OBJECT);
if (validator instanceof Validator) {
ValidationObject object = new ValidationObject(validateNode, null, observableValue);
ValidationEvent event = ((Validator) validator).call(object);
validateNode.fireEvent(event);
}
break;
}
}
/**
* Validates the Region. It will call the validators that were installed on any children using ON_DEMAND validation
* mode.
*
* @param targetRegionOrNode the node or the region to be validated.
* @return the true or false. True if there is no validation errors. False is there is an error.
*/
public static boolean validateOnDemand(Node targetRegionOrNode) {
if (targetRegionOrNode == null) {
return false;
}
final List<Node> nodes = new ArrayList<>();
FXUtils.setRecursively(targetRegionOrNode, new FXUtils.Handler<Node>() {
@Override
public boolean condition(Node c) {
return c.getProperties().get(PROPERTY_ON_DEMAND_VALIDATOR) instanceof Validator;
}
@Override
public void action(Node c) {
nodes.add(c);
}
});
boolean valid = true;
for (Node validateNode : nodes) {
ObservableMap<Object, Object> properties = validateNode.getProperties();
if (properties.get(PROPERTY_ON_DEMAND_VALIDATOR) instanceof Validator && properties.get(PROPERTY_ON_DEMAND_OBSERVABLE_VALUE) instanceof ObservableValue) {
Validator validator = (Validator) properties.get(PROPERTY_ON_DEMAND_VALIDATOR);
ObservableValue observableValue = (ObservableValue) properties.get(PROPERTY_ON_DEMAND_OBSERVABLE_VALUE);
Object oldValue = properties.get(PROPERTY_ON_DEMAND_OBJECT);
ValidationObject object = new ValidationObject(validateNode, oldValue, observableValue.getValue());
ValidationEvent event = validator.call(object);
validateNode.fireEvent(event);
if (ValidationEvent.VALIDATION_ERROR.equals(event.getEventType())) {
valid = false;
} else { // if valid, we will store the current value as the old value so that it can be used next time
properties.put(PROPERTY_ON_DEMAND_OBJECT, object);
}
}
}
if (targetRegionOrNode instanceof Region) {
((Region) targetRegionOrNode).requestLayout();
}
return valid;
}
public static boolean isValidationDecorator(Decorator decorator) {
if (Optional.of(decorator.getNode()).isPresent()) {
Node _node = Optional.of((decorator).getNode()).get();
if (_node instanceof Label) {
Label validationLabel = (Label) _node;
// Search for validation label
long count = validationLabel.getStyleClass()
.stream()
.filter(styleClass -> styleClass.equals(ValidationDecorators.PROPERTY_VALIDATION_DECORATOR)).count();
if (count > 0) {
return true;
}
}
}
return false;
}
public static Optional<List<Decorator>> getValidationDecorators(Node node) {
if (Optional.ofNullable(DecorationUtils.getDecorators(node)).isPresent()) {
Object o = Optional.of(DecorationUtils.getDecorators(node)).get();
if (o instanceof Decorator) {
if (isValidationDecorator((Decorator) o)) {
return Optional.of(new ArrayList<Decorator>() {{
add((Decorator) o);
}});
}
} else if (o instanceof Decorator[]) {
Decorator[] decorators = (Decorator[]) o;
List<Decorator> validationLabels = new ArrayList<>();
for (int i = 0; i < decorators.length; i++) {
Decorator d = decorators[i];
if (isValidationDecorator(d)) {
validationLabels.add(d);
}
}
return Optional.of(validationLabels);
}
}
return Optional.empty();
}
public static void showTooltip(Label label) {
if (label != null && label.getTooltip() != null) {
Point2D point = label.localToScene(15.0, 15.0);
label.getTooltip().setAutoHide(true);
label.getTooltip().show(label.getScene().getWindow(), point.getX()
+ label.getScene().getX() + label.getScene().getWindow().getX(), point.getY()
+ label.getScene().getY() + label.getScene().getWindow().getY());
}
}
/**
* Creates a validation event handler. This handler will install an icon decorator to the node to be validated, and
* also set the pseudo-class as "validation-ok", "validation-info", "validation-warning", "warning-error" so that
* you can use css file to display the validation result.
*
* @param targetNode the node to be validated
* @return the event handler
*/
public static EventHandler<ValidationEvent> createDefaultValidationEventHandler(Node targetNode, TriFunction<Node, Decorator, ValidationEvent, Decorator<Label>> decorationCreator) {
return new EventHandler<ValidationEvent>() {
Decorator resultDecorator = null;
@Override
public void handle(ValidationEvent event) {
if (event != null) {
adjustValidationPseudoClasses(event, targetNode);
if (event.getEventType().equals(ValidationEvent.VALIDATION_UNKNOWN)) {
DecorationUtils.uninstall(targetNode);
resultDecorator = null;
targetNode.getParent().requestLayout();
return;
}
if (targetNode instanceof Cell && event.getEventType().equals(ValidationEvent.VALIDATION_OK)) {
return;
}
// check to make sure it is the same event type and even message
Object validationResultProperty = targetNode.getProperties().get(PROPERTY_VALIDATION_RESULT);
Object validationResultMessageProperty = targetNode.getProperties().get(PROPERTY_VALIDATION_RESULT_MESSAGE);
if (validationResultProperty != null && validationResultProperty.equals(event.getEventType().getName())
&& validationResultMessageProperty != null && validationResultMessageProperty.equals(event.getMessage())) {
return;
}
// Create decorator using passed function (look into ValidationDecorators)
resultDecorator = decorationCreator.apply(targetNode, resultDecorator, event);
adjustValidationPseudoClasses(event, resultDecorator.getNode());
targetNode.getProperties().put(PROPERTY_VALIDATION_RESULT, event.getEventType().getName());
targetNode.getProperties().put(PROPERTY_VALIDATION_RESULT_MESSAGE, event.getMessage());
}
}
};
}
private static void adjustValidationPseudoClasses(ValidationEvent event, Node targetNode) {
if (event.getEventType() == ValidationEvent.VALIDATION_OK) {
targetNode.pseudoClassStateChanged(PseudoClass.getPseudoClass(PSEUDO_CLASS_VALIDATION_OK), true);
targetNode.pseudoClassStateChanged(PseudoClass.getPseudoClass(PSEUDO_CLASS_VALIDATION_INFO), false);
targetNode.pseudoClassStateChanged(PseudoClass.getPseudoClass(PSEUDO_CLASS_VALIDATION_WARNING), false);
targetNode.pseudoClassStateChanged(PseudoClass.getPseudoClass(PSEUDO_CLASS_VALIDATION_ERROR), false);
} else if (event.getEventType() == ValidationEvent.VALIDATION_ERROR) {
targetNode.pseudoClassStateChanged(PseudoClass.getPseudoClass(PSEUDO_CLASS_VALIDATION_OK), false);
targetNode.pseudoClassStateChanged(PseudoClass.getPseudoClass(PSEUDO_CLASS_VALIDATION_INFO), false);
targetNode.pseudoClassStateChanged(PseudoClass.getPseudoClass(PSEUDO_CLASS_VALIDATION_WARNING), false);
targetNode.pseudoClassStateChanged(PseudoClass.getPseudoClass(PSEUDO_CLASS_VALIDATION_ERROR), true);
} else if (event.getEventType() == ValidationEvent.VALIDATION_WARNING) {
targetNode.pseudoClassStateChanged(PseudoClass.getPseudoClass(PSEUDO_CLASS_VALIDATION_OK), false);
targetNode.pseudoClassStateChanged(PseudoClass.getPseudoClass(PSEUDO_CLASS_VALIDATION_INFO), false);
targetNode.pseudoClassStateChanged(PseudoClass.getPseudoClass(PSEUDO_CLASS_VALIDATION_WARNING), true);
targetNode.pseudoClassStateChanged(PseudoClass.getPseudoClass(PSEUDO_CLASS_VALIDATION_ERROR), false);
} else if (event.getEventType() == ValidationEvent.VALIDATION_INFO) {
targetNode.pseudoClassStateChanged(PseudoClass.getPseudoClass(PSEUDO_CLASS_VALIDATION_OK), false);
targetNode.pseudoClassStateChanged(PseudoClass.getPseudoClass(PSEUDO_CLASS_VALIDATION_INFO), true);
targetNode.pseudoClassStateChanged(PseudoClass.getPseudoClass(PSEUDO_CLASS_VALIDATION_WARNING), false);
targetNode.pseudoClassStateChanged(PseudoClass.getPseudoClass(PSEUDO_CLASS_VALIDATION_ERROR), false);
} else { // UNKNOWN
targetNode.pseudoClassStateChanged(PseudoClass.getPseudoClass(PSEUDO_CLASS_VALIDATION_OK), false);
targetNode.pseudoClassStateChanged(PseudoClass.getPseudoClass(PSEUDO_CLASS_VALIDATION_INFO), false);
targetNode.pseudoClassStateChanged(PseudoClass.getPseudoClass(PSEUDO_CLASS_VALIDATION_WARNING), false);
targetNode.pseudoClassStateChanged(PseudoClass.getPseudoClass(PSEUDO_CLASS_VALIDATION_ERROR), false);
}
}
public static ValidationStatus getValidationStatus(Node targetNode) {
ValidationStatus status = ValidationStatus.VALIDATION_UNKNOWN;
Object validationResult = targetNode.getProperties().get(PROPERTY_VALIDATION_RESULT);
if (validationResult != null) {
String validationResultName = validationResult.toString();
if (validationResultName.equals(ValidationEvent.VALIDATION_ERROR.getName())) {
status = ValidationStatus.VALIDATION_ERROR;
} else if (validationResultName.equals(ValidationEvent.VALIDATION_INFO.getName())) {
status = ValidationStatus.VALIDATION_INFO;
} else if (validationResultName.equals(ValidationEvent.VALIDATION_OK.getName())) {
status = ValidationStatus.VALIDATION_OK;
} else if (validationResultName.equals(ValidationEvent.VALIDATION_UNKNOWN.getName())) {
status = ValidationStatus.VALIDATION_UNKNOWN;
} else if (validationResultName.equals(ValidationEvent.VALIDATION_WARNING.getName())) {
status = ValidationStatus.VALIDATION_WARNING;
}
}
return status;
}
}