package com.insightfullogic.honest_profiler.ports.javafx.util.validation; import static com.insightfullogic.honest_profiler.ports.javafx.util.StyleUtil.STYLE_ERROR; import static com.insightfullogic.honest_profiler.ports.javafx.util.StyleUtil.STYLE_NORMAL; import static java.util.Arrays.asList; import java.util.ArrayList; import java.util.List; import java.util.function.Predicate; import com.insightfullogic.honest_profiler.ports.javafx.util.handle.ChangeListenerHandle; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.scene.Node; import javafx.scene.control.Button; /** * Listener which validates a String in a {@link Node}, and changes the styling of the {@link Node} and the associated * {@link Button} state based on the validity of the input. Typically this is the "OK" {@link Button} is associated. */ public class StringValidationListener implements ChangeListener<String> { // Instance Properties private ChangeListenerHandle<String> handle; private Node inputNode; private List<Button> buttons; private Predicate<String> test; // Instance Constructors /** * Constructor specifying the source {@link Node}, the predicate for testing the validity, and any {@link Button}s * which should be disabled if the validity test fails. * <p> * @param inputNode the source {@link Node} * @param test the predicate for testing the validity of the String value * @param buttons the {@link Button}s which should be disabled if the validity test fails */ public StringValidationListener(Node inputNode, Predicate<String> test, Button... buttons) { this.inputNode = inputNode; this.test = test; this.buttons = buttons == null ? new ArrayList<>() : asList(buttons); } // Management Methods /** * Create a {@link ChangeListenerHandle} for the input {@link Node} and the specified text {@link ObservableValue}, * and attaches it. * <p> * @param value the {@link ObservableValue} this Listener is added to * @param inputNode the new input {@link Node} being validated by the StringValidationListener * @return a new {@link ChangeListenerHandle} for this StringValidationListener and the specified * {@link ObservableValue} */ public ChangeListenerHandle<String> attach(ObservableValue<String> value, Node inputNode) { this.inputNode = inputNode; handle = new ChangeListenerHandle<String>(value, this); handle.attach(); return handle; } // ChangeListener Implementation @Override public void changed(ObservableValue<? extends String> value, String oldValue, String newValue) { // An empty value is styled as valid, but the associated Button is disabled. if ((newValue == null) || newValue.isEmpty()) { inputNode.setStyle(STYLE_NORMAL); setDisabled(true); return; } try { // Input valid => enable Button and style as valid. if (test.test(newValue)) { inputNode.setStyle(STYLE_NORMAL); setDisabled(false); return; } } catch (Throwable t) { // Do nothing, treat as failed test. } // Input invalid => disable Button and style as error. inputNode.setStyle(STYLE_ERROR); setDisabled(true); } /** * Disable or enable the {@link Button}s. * <p> * @param disable a boolean indicating whether the {@link Button}s should be disabled */ private void setDisabled(boolean disable) { buttons.forEach(button -> button.setDisable(disable)); } }