001package jmri.implementation; 002 003import java.util.Arrays; 004import jmri.SignalHead; 005import jmri.Turnout; 006 007import javax.annotation.Nonnull; 008 009/** 010 * Abstract class providing the basic logic of the SignalHead interface. 011 * 012 * @author Bob Jacobsen Copyright (C) 2001 013 */ 014public abstract class AbstractSignalHead extends AbstractNamedBean 015 implements SignalHead, java.beans.VetoableChangeListener { 016 017 public AbstractSignalHead(String systemName, String userName) { 018 super(systemName, userName); 019 } 020 021 public AbstractSignalHead(String systemName) { 022 super(systemName); 023 } 024 025 @Override 026 public String getAppearanceName(int appearance) { 027 String ret = jmri.util.StringUtil.getNameFromState( 028 appearance, getValidStates(), getValidStateNames()); 029 if (ret != null) { 030 return ret; 031 } 032 return (""); 033 } 034 035 @Override 036 public String getAppearanceName() { 037 return getAppearanceName(getAppearance()); 038 } 039 040 @Override 041 public String getAppearanceKey(int appearance) { 042 String ret = jmri.util.StringUtil.getNameFromState( 043 appearance, getValidStates(), getValidStateKeys()); 044 if (ret != null) { 045 return ret; 046 } 047 return (""); 048 } 049 050 @Override 051 public String getAppearanceKey() { 052 return getAppearanceKey(getAppearance()); 053 } 054 055 protected int mAppearance = DARK; 056 057 @Override 058 public int getAppearance() { 059 return mAppearance; 060 } 061 062 /** 063 * Determine whether this signal shows an aspect or appearance 064 * that allows travel past it, e.g. it's "been cleared". 065 * This might be a yellow or green appearance, or an Approach or Clear 066 * aspect 067 */ 068 @Override 069 public boolean isCleared() { return !isAtStop() && !isShowingRestricting() && getAppearance()!=DARK; } 070 071 /** 072 * Determine whether this signal shows an aspect or appearance 073 * that allows travel past it only at restricted speed. 074 * This might be a flashing red appearance, or a 075 * Restricting aspect. 076 */ 077 @Override 078 public boolean isShowingRestricting() { return getAppearance() == FLASHRED || getAppearance() == LUNAR || getAppearance() == FLASHLUNAR; } 079 080 /** 081 * Determine whether this signal shows an aspect or appearance 082 * that forbid travel past it. 083 * This might be a red appearance, or a 084 * Stop aspect. Stop-and-Proceed or Restricting would return false here. 085 */ 086 @Override 087 public boolean isAtStop() { return getAppearance() == RED; } 088 089 090 // implementing classes will typically have a function/listener to get 091 // updates from the layout, which will then call 092 // public void firePropertyChange(String propertyName, 093 // Object oldValue, 094 // Object newValue) 095 // _once_ if anything has changed state 096 /** 097 * By default, signals are lit. 098 */ 099 protected boolean mLit = true; 100 101 /** 102 * Default behavior for "lit" parameter is to track value and return it. 103 * 104 * @return is lit 105 */ 106 @Override 107 public boolean getLit() { 108 return mLit; 109 } 110 111 /** 112 * By default, signals are not held. 113 */ 114 protected boolean mHeld = false; 115 116 /** 117 * "Held" parameter is just tracked and notified. 118 * @return is held 119 */ 120 @Override 121 public boolean getHeld() { 122 return mHeld; 123 } 124 125 /** 126 * Implement a shorter name for setAppearance. 127 * <p> 128 * This generally shouldn't be used by Java code; use setAppearance instead. 129 * The is provided to make Jython script access easier to read. 130 * @param s new state 131 */ 132 @Override 133 public void setState(int s) { 134 setAppearance(s); 135 } 136 137 /** 138 * Implement a shorter name for getAppearance. 139 * <p> 140 * This generally shouldn't be used by Java code; use getAppearance instead. 141 * The is provided to make Jython script access easier to read. 142 * @return current state 143 */ 144 @Override 145 public int getState() { 146 return getAppearance(); 147 } 148 149 public static int[] getDefaultValidStates() { 150 return Arrays.copyOf(validStates, validStates.length); 151 } 152 153 public static String[] getDefaultValidStateNames() { 154 String[] stateNames = new String[validStateKeys.length]; 155 int i = 0; 156 for (String stateKey : validStateKeys) { 157 stateNames[i++] = Bundle.getMessage(stateKey); 158 } 159 return stateNames; 160 } 161 162 /** 163 * Get a localized text describing appearance from the corresponding state index. 164 * 165 * @param appearance the index of the appearance 166 * @return translated name for appearance 167 */ 168 public static String getDefaultStateName(int appearance) { 169 String ret = jmri.util.StringUtil.getNameFromState( 170 appearance, getDefaultValidStates(), getDefaultValidStateNames()); 171 if (ret != null) { 172 return ret; 173 } else { 174 return (""); 175 } 176 } 177 178 private static final int[] validStates = new int[]{ 179 DARK, 180 RED, 181 YELLOW, 182 GREEN, 183 LUNAR, 184 FLASHRED, 185 FLASHYELLOW, 186 FLASHGREEN, 187 FLASHLUNAR 188 }; 189 private static final String[] validStateKeys = new String[]{ 190 "SignalHeadStateDark", 191 "SignalHeadStateRed", 192 "SignalHeadStateYellow", 193 "SignalHeadStateGreen", 194 "SignalHeadStateLunar", 195 "SignalHeadStateFlashingRed", 196 "SignalHeadStateFlashingYellow", 197 "SignalHeadStateFlashingGreen", 198 "SignalHeadStateFlashingLunar" 199 }; 200 201 /** 202 * {@inheritDoc} 203 */ 204 @Override 205 public int[] getValidStates() { 206 return Arrays.copyOf(validStates, validStates.length); // includes int for Lunar 207 } 208 209 /** 210 * {@inheritDoc} 211 */ 212 @Override 213 public String[] getValidStateKeys() { 214 return Arrays.copyOf(validStateKeys, validStateKeys.length); // includes int for Lunar 215 } 216 217 /** 218 * {@inheritDoc} 219 */ 220 @Override 221 public String[] getValidStateNames() { 222 return getDefaultValidStateNames(); 223 } 224 225 /** 226 * Check if a given turnout is used on this head. 227 * 228 * @param t Turnout object to check 229 * @return true if turnout is configured as output or driver of head 230 */ 231 abstract boolean isTurnoutUsed(Turnout t); 232 233 @Override 234 public void vetoableChange(java.beans.PropertyChangeEvent evt) throws java.beans.PropertyVetoException { 235 if ("CanDelete".equals(evt.getPropertyName())) { // NOI18N 236 if (isTurnoutUsed((Turnout) evt.getOldValue())) { 237 java.beans.PropertyChangeEvent e = new java.beans.PropertyChangeEvent(this, "DoNotDelete", null, null); 238 throw new java.beans.PropertyVetoException(Bundle.getMessage("InUseTurnoutSignalHeadVeto", getDisplayName()), e); // NOI18N 239 } 240 } 241 } 242 243 @Override 244 public @Nonnull String getBeanType() { 245 return Bundle.getMessage("BeanNameSignalHead"); 246 } 247 248// private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(AbstractSignalHead.class); 249 250}