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}