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    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}