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