001package jmri.swing;
002
003import com.alexandriasoftware.swing.JInputValidator;
004import com.alexandriasoftware.swing.JInputValidatorPreferences;
005import com.alexandriasoftware.swing.Validation;
006import java.util.Objects;
007import javax.annotation.Nonnull;
008import javax.swing.JComponent;
009import javax.swing.text.JTextComponent;
010import jmri.Manager;
011import jmri.Manager.NameValidity;
012import jmri.NamedBean;
013import jmri.ProxyManager;
014
015/**
016 * A {@link com.alexandriasoftware.swing.JInputValidator} that validates a
017 * {@link jmri.NamedBean} system name.
018 * <p>
019 * Until the component gets focus, no validation icon is shown. Once the
020 * component has focus the following icons are shown:
021 * <ul>
022 * <li>If the component is blank and required was false when the validator was
023 * created, no validation is shown.</li>
024 * <li>If the component is blank and required was true when the validator was
025 * created, a warning icon is shown.</li>
026 * <li>If the component has an invalid system name, an error icon is shown.</li>
027 * <li>If the component has a potentially valid system name, a waring icon is
028 * shown.</li>
029 * <li>If the component has a valid system name, a success icon is shown.</li>
030 * </ul>
031 *
032 * @author Randall Wood Copyright 2019
033 */
034public class SystemNameValidator extends JInputValidator {
035
036    private Manager<?> manager;
037    private boolean required = false;
038
039    /**
040     * Create a SystemNameValidator. Same as calling
041     * {@link #SystemNameValidator(javax.swing.JComponent, jmri.Manager, boolean)}
042     * with {@code required == false}.
043     *
044     * @param component the component to validate has a valid system name
045     * @param manager   the manager that will be used for validation
046     */
047    public SystemNameValidator(@Nonnull JComponent component, @Nonnull Manager<?> manager) {
048        this(component, manager, false);
049    }
050
051    /**
052     * Create a SystemNameValidator.
053     *
054     * @param component the component to validate has a valid system name
055     * @param manager   the manager that will be used for validation
056     * @param required  true if input must be valid and
057     *                  {@link javax.swing.InputVerifier#verify(javax.swing.JComponent)}
058     *                  must return true to allow focus change; false otherwise
059     */
060    public SystemNameValidator(@Nonnull JComponent component, @Nonnull Manager<?> manager, boolean required) {
061        super(component, true, required);
062        setManager(manager);
063        this.required = required;
064    }
065
066    @Override
067    protected Validation getValidation(JComponent component, JInputValidatorPreferences preferences) {
068        if (component instanceof JTextComponent) {
069            JTextComponent jtc = (JTextComponent) component;
070            String text = jtc.getText();
071            if (text != null && !text.isEmpty()) {
072                try {
073                    if (manager instanceof ProxyManager) {
074                        ProxyManager<?> proxyManager = (ProxyManager<?>) manager;
075                        proxyManager.validateSystemNameFormat(text);
076                    } else {
077                        manager.makeSystemName(text, false);
078                    }
079                } catch (NamedBean.BadSystemNameException ex) {
080                    if (manager.validSystemNameFormat(text) == NameValidity.VALID_AS_PREFIX_ONLY) {
081                        return new Validation(Validation.Type.WARNING, Bundle.getMessage("SystemNameValidatorValidPrefix", text, manager.getBeanTypeHandled(),
082                                trimHtmlTags(getToolTipText())), preferences);
083                    }
084                    return new Validation(Validation.Type.DANGER, Bundle.getMessage("SystemNameValidatorInvalid", ex.getMessage(), trimHtmlTags(getToolTipText())), preferences);
085                }
086                return new Validation(Validation.Type.SUCCESS, getToolTipText(), preferences);
087            }
088        }
089        if (required) {
090            return new Validation(Validation.Type.WARNING, Bundle.getMessage("SystemNameValidatorRequired", trimHtmlTags(getToolTipText())), preferences);
091        }
092        return getNoneValidation();
093    }
094
095    public boolean isRequired() {
096        return required;
097    }
098
099    public void setRequired(boolean required) {
100        this.required = required;
101    }
102
103    /**
104     * Set the Manager used to validate system names.
105     * <p>
106     * If the manager changes, fires the a property change for the property
107     * {@code manager} and calls {@link #verify(javax.swing.JComponent)} to
108     * verify any text against the new manager.
109     *
110     * @param manager the new manager
111     */
112    public void setManager(@Nonnull Manager<?> manager) {
113        Objects.requireNonNull(manager, "Cannot validate against a null manager");
114        Manager<?> old = this.manager;
115        if (old == null || !old.equals(manager)) {
116            this.manager = manager;
117            getPropertyChangeSupport().firePropertyChange("manager", old, this.manager);
118            verify(getComponent());
119        }
120    }
121}