001package jmri.implementation;
002
003import java.util.*;
004import javax.annotation.*;
005
006import jmri.NamedBean;
007import jmri.NamedBeanHandle;
008import jmri.NamedBeanUsageReport;
009
010import jmri.InstanceManager;
011import jmri.SignalAppearanceMap;
012import jmri.SignalMast;
013import jmri.SignalSystem;
014
015/**
016 * Abstract class providing the basic logic of the SignalMast interface.
017 *
018 * @author Bob Jacobsen Copyright (C) 2009
019 */
020public abstract class AbstractSignalMast extends AbstractNamedBean
021        implements SignalMast, java.beans.VetoableChangeListener {
022
023    public AbstractSignalMast(String systemName, String userName) {
024        super(systemName, userName);
025    }
026
027    public AbstractSignalMast(String systemName) {
028        super(systemName);
029    }
030
031    @Override
032    public void setAspect(@Nonnull String aspect) {
033        String oldAspect = this.aspect;
034        this.aspect = aspect;
035        this.speed = (String) getSignalSystem().getProperty(aspect, "speed");
036        firePropertyChange(PROPERTY_ASPECT, oldAspect, aspect);
037    }
038
039    @Override
040    public String getAspect() {
041        return aspect;
042    }
043    protected String aspect = null;
044
045    public String getSpeed() {
046        return speed;
047    }
048    protected String speed = null;
049
050    /**
051     * The state is the index of the current aspect in the list of possible
052     * aspects.
053     */
054    @Override
055    public int getState() {
056        return -1;
057    }
058
059    @Override
060    public void setState(int i) {
061    }
062
063    /**
064     * By default, signals are lit.
065     */
066    private boolean mLit = true;
067
068    /**
069     * Default behavior for "lit" property is to track value and return it.
070     */
071    @Override
072    public boolean getLit() {
073        return mLit;
074    }
075
076    /**
077     * By default, signals are not held.
078     */
079    private boolean mHeld = false;
080
081    /**
082     * "Held" property is just tracked and notified.
083     */
084    @Override
085    public boolean getHeld() {
086        return mHeld;
087    }
088
089    /**
090     * Set the lit property.
091     * <p>
092     * This acts on all the SignalHeads included in this SignalMast
093     *
094     * @param newLit the new value of lit
095     */
096    @Override
097    public void setLit(boolean newLit) {
098        boolean oldLit = mLit;
099        mLit = newLit;
100        if (oldLit != newLit) {
101            firePropertyChange(PROPERTY_LIT, oldLit, newLit);
102        }
103    }
104
105    /**
106     * Set the held property of the signal mast.
107     * <p>
108     * Note that this does not directly effect the output on the layout; the
109     * held property is a local variable which effects the aspect only via
110     * higher-level logic.
111     *
112     * @param newHeld the new value of the help property
113     */
114    @Override
115    public void setHeld(boolean newHeld) {
116        boolean oldHeld = mHeld;
117        mHeld = newHeld;
118        if (oldHeld != newHeld) {
119            firePropertyChange(PROPERTY_HELD, oldHeld, newHeld);
120        }
121    }
122
123    @Override
124    public boolean isAtStop() {
125        // should this also include DANGER?
126        return "0".equals(speed);
127    }
128
129    @Override
130    public boolean isShowingRestricting() {
131        String displayedAspect = getAspect();
132        if ( displayedAspect != null && displayedAspect.equals(getAppearanceMap().getSpecificAppearance(SignalAppearanceMap.PERMISSIVE))) return true;
133        return false;
134    }
135
136    @Override
137    public boolean isCleared() {
138        String displayedAspect = getAspect();
139        if ( displayedAspect == null ) {
140            return false;
141        }
142        if (displayedAspect.equals(getAppearanceMap().getSpecificAppearance(SignalAppearanceMap.PERMISSIVE))) return false;
143        if (displayedAspect.equals(getAppearanceMap().getSpecificAppearance(SignalAppearanceMap.HELD))) return false;
144        if (displayedAspect.equals(getAppearanceMap().getSpecificAppearance(SignalAppearanceMap.DANGER))) return false;
145        return true;
146    }
147
148    protected DefaultSignalAppearanceMap map;
149    SignalSystem systemDefn;
150
151    boolean disablePermissiveSignalMastLogic = false;
152
153    @Override
154    public void setPermissiveSmlDisabled(boolean disabled) {
155        disablePermissiveSignalMastLogic = disabled;
156        firePropertyChange(PROPERTY_PERMISSIVE_SML_DISABLED, null, disabled);
157    }
158
159    /**
160     * {@inheritDoc }
161     */
162    @Override
163    public boolean isPermissiveSmlDisabled() {
164        return disablePermissiveSignalMastLogic;
165    }
166
167    protected void configureSignalSystemDefinition(String name) {
168        systemDefn = InstanceManager.getDefault(jmri.SignalSystemManager.class).getSystem(name);
169        if (systemDefn == null) {
170            log.error("Did not find signal definition: {}", name);
171            throw new IllegalArgumentException("Signal definition not found: " + name);
172        }
173    }
174
175    protected void configureAspectTable(String signalSystemName, String aspectMapName) {
176        map = DefaultSignalAppearanceMap.getMap(signalSystemName, aspectMapName);
177    }
178
179    @Override
180    public SignalSystem getSignalSystem() {
181        return systemDefn;
182    }
183
184    @Override
185    public SignalAppearanceMap getAppearanceMap() {
186        return map;
187    }
188
189    protected ArrayList<String> disabledAspects = new ArrayList<>(1);
190
191    @Override
192    @Nonnull
193    public Vector<String> getValidAspects() {
194        java.util.Enumeration<String> e = map.getAspects();
195        // copy List to Vector
196        Vector<String> v = new Vector<>();
197        while (e.hasMoreElements()) {
198            String a = e.nextElement();
199            if (!disabledAspects.contains(a)) {
200                v.add(a);
201            }
202        }
203        return v;
204    }
205
206    /**
207     * {@inheritDoc }
208     */
209    @Override
210    public String getMastType() {
211        return mastType;
212    }
213
214    @Override
215    public void setMastType(@Nonnull String type) {
216        Objects.requireNonNull(type, "MastType cannot be null");
217        mastType = type;
218    }
219    String mastType;
220
221    /**
222     * Get a list of all the known aspects for this mast, including those that
223     * have been disabled.
224     *
225     * @return list of known aspects; may be empty
226     */
227    public Vector<String> getAllKnownAspects() {
228        java.util.Enumeration<String> e = map.getAspects();
229        Vector<String> v = new Vector<>();
230        while (e.hasMoreElements()) {
231            v.add(e.nextElement());
232        }
233        return v;
234    }
235
236    public void setAspectDisabled(String aspect) {
237        if (aspect == null || aspect.isEmpty()) {
238            return;
239        }
240        if (!map.checkAspect(aspect)) {
241            log.warn("attempting to disable an aspect: {} that is not on the mast {}", aspect, getDisplayName());
242            return;
243        }
244        if (!disabledAspects.contains(aspect)) {
245            disabledAspects.add(aspect);
246            firePropertyChange(PROPERTY_ASPECT_DISABLED, null, aspect);
247        }
248    }
249
250    public void setAspectEnabled(String aspect) {
251        if (aspect == null || aspect.isEmpty()) {
252            return;
253        }
254        if (!map.checkAspect(aspect)) {
255            log.warn("attempting to disable an aspect: {} that is not on the mast {}", aspect, getDisplayName());
256            return;
257        }
258        if (disabledAspects.contains(aspect)) {
259            disabledAspects.remove(aspect);
260            firePropertyChange(PROPERTY_ASPECT_ENABLED, null, aspect);
261        }
262    }
263
264    public List<String> getDisabledAspects() {
265        return disabledAspects;
266    }
267
268    @Override
269    public boolean isAspectDisabled(String aspect) {
270        return disabledAspects.contains(aspect);
271    }
272
273    private boolean allowUnLit = true;
274
275    @Override
276    public void setAllowUnLit(boolean boo) {
277        allowUnLit = boo;
278    }
279
280    @Override
281    public boolean allowUnLit() {
282        return allowUnLit;
283    }
284
285    @Override
286    public void vetoableChange(java.beans.PropertyChangeEvent evt) throws java.beans.PropertyVetoException {
287    }
288
289    @Override
290    public String getBeanType() {
291        return Bundle.getMessage("BeanNameSignalMast");
292    }
293
294    @Override
295    public List<NamedBeanUsageReport> getUsageReport(NamedBean bean) {
296        List<NamedBeanUsageReport> report = new ArrayList<>();
297        if (bean != null) {
298            if (bean instanceof jmri.Turnout) {
299                if (this instanceof jmri.implementation.TurnoutSignalMast) {
300                    var m = (jmri.implementation.TurnoutSignalMast) this;
301                    var t = (jmri.Turnout) bean;
302                    if (m.isTurnoutUsed(t)) {
303                        report.add(new NamedBeanUsageReport("SignalMastTurnout"));  // NOI18N
304                    }
305                } else if (this instanceof jmri.implementation.MatrixSignalMast) {
306                    var m = (jmri.implementation.MatrixSignalMast) this;
307                    var t = (jmri.Turnout) bean;
308                    if (m.isTurnoutUsed(t)) {
309                        report.add(new NamedBeanUsageReport("SignalMastTurnout"));  // NOI18N
310                    }
311                }
312            } else if (bean instanceof jmri.SignalHead) {
313                if (this instanceof jmri.implementation.SignalHeadSignalMast) {
314                    var m = (jmri.implementation.SignalHeadSignalMast) this;
315                    var h = (jmri.SignalHead) bean;
316                    for (NamedBeanHandle<jmri.SignalHead> handle : m.getHeadsUsed()) {
317                        if (h.equals(handle.getBean())) {
318                            report.add(new NamedBeanUsageReport("SignalMastSignalHead"));  // NOI18N
319                        }
320                    }
321                }
322            }
323        }
324        return report;
325    }
326
327    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(AbstractSignalMast.class);
328
329}