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