001package jmri.jmrit.entryexit;
002
003import java.beans.PropertyChangeEvent;
004import java.beans.PropertyChangeListener;
005import java.util.Hashtable;
006import java.util.List;
007import java.util.Map.Entry;
008import jmri.InstanceManager;
009import jmri.NamedBean;
010import jmri.Sensor;
011import jmri.SignalHead;
012import jmri.SignalMast;
013import jmri.jmrit.display.EditorManager;
014import jmri.jmrit.display.SensorIcon;
015import jmri.jmrit.display.layoutEditor.LayoutBlock;
016import jmri.jmrit.display.layoutEditor.LayoutEditor;
017import jmri.jmrit.display.layoutEditor.LayoutSlip;
018import jmri.jmrit.display.layoutEditor.LayoutTurnout;
019import jmri.jmrit.display.layoutEditor.LevelXing;
020import jmri.jmrit.display.layoutEditor.PositionablePoint;
021import org.slf4j.Logger;
022import org.slf4j.LoggerFactory;
023
024public class PointDetails {
025
026    //May want to look at putting a listener on the refLoc to listen to updates to blocks, signals and sensors attached to it
027    LayoutEditor panel = null;
028    LayoutBlock facing;
029    List<LayoutBlock> protectingBlocks;
030    private NamedBean refObj;
031    private Object refLoc;
032    private Sensor sensor;
033    private SignalMast signalmast;
034    private SignalHead signalhead;
035
036    static int nxButtonTimeout = 10;
037
038    Source sourceRoute;
039    transient Hashtable<DestinationPoints, Source> destinations = new Hashtable<>(5);
040
041    public PointDetails(LayoutBlock facing, List<LayoutBlock> protecting) {
042        this.facing = facing;
043        this.protectingBlocks = protecting;
044    }
045
046    public LayoutBlock getFacing() {
047        return facing;
048    }
049
050    public List<LayoutBlock> getProtecting() {
051        return protectingBlocks;
052    }
053
054    //This might be better off a ref to the source pointdetail.
055    boolean routeToSet = false;
056
057    void setRouteTo(boolean boo) {
058        routeToSet = boo;
059    }
060
061    boolean routeFromSet = false;
062
063    void setRouteFrom(boolean boo) {
064        routeFromSet = boo;
065    }
066
067    public void setPanel(LayoutEditor panel) {
068        this.panel = panel;
069        // find the panel that actually contains this sensor, default to the supplied panel
070        for (LayoutEditor layout : InstanceManager.getDefault(EditorManager.class).getAll(LayoutEditor.class)) {
071            for (SensorIcon si : layout.getSensorList()) {
072                if (sensor == si.getNamedBean()) {
073                    this.panel = layout;
074                    return;
075                }
076            }
077        }
078    }
079
080    void setSensor(Sensor sen) {
081        if (sensor == sen) {
082            return;
083        }
084        if (sensor != null) {
085            sensor.removePropertyChangeListener(nxButtonListener);
086        }
087        sensor = sen;
088        if (sensor != null) {
089            sensor.addPropertyChangeListener(nxButtonListener);
090        }
091    }
092
093    void addSensorList() {
094        sensor.addPropertyChangeListener(nxButtonListener);
095    }
096
097    void removeSensorList() {
098        sensor.removePropertyChangeListener(nxButtonListener);
099    }
100
101    //Sensor getSensor() { return sensor; }
102    protected PropertyChangeListener nxButtonListener = new PropertyChangeListener() {
103        //First off if we were inactive, and now active
104        @Override
105        public void propertyChange(PropertyChangeEvent e) {
106            nxButtonStateChange(e);
107        }
108    };
109
110    private void nxButtonStateChange(PropertyChangeEvent e) {
111        if (!e.getPropertyName().equals("KnownState")) {  // NOI18N
112            return;
113        }
114        int now = ((Integer) e.getNewValue());
115        int old = ((Integer) e.getOldValue());
116
117        if ((old == Sensor.UNKNOWN) || (old == Sensor.INCONSISTENT)) {
118            setButtonState(EntryExitPairs.NXBUTTONINACTIVE);
119            return;
120        }
121
122        DestinationPoints destPoint = null;
123
124        for (Entry<DestinationPoints, Source> dp : destinations.entrySet()) {
125            destPoint = dp.getKey();
126            if (destPoint.isEnabled() && dp.getValue().getPoint().getNXState() == EntryExitPairs.NXBUTTONSELECTED) {
127                setButtonState(EntryExitPairs.NXBUTTONSELECTED);
128                destPoint.activeBean(false);
129                return;
130            }
131        }
132
133        if (sourceRoute != null) {
134            if (now == Sensor.ACTIVE && getNXState() == EntryExitPairs.NXBUTTONINACTIVE) {
135                setButtonState(EntryExitPairs.NXBUTTONSELECTED);
136                for (Entry<PointDetails, DestinationPoints> en : sourceRoute.pointToDest.entrySet()) {
137                    //Sensor sen = getSensorFromPoint(en.getKey().getPoint());
138                    //Set a time out on the source sensor, so that if its state hasn't been changed, then we will clear it out.
139                    if (en.getValue().isEnabled() && !en.getValue().getUniDirection()) {
140                        if (en.getKey().getNXState() == EntryExitPairs.NXBUTTONSELECTED) {
141                            sourceRoute.activeBean(en.getValue(), true);
142                        }
143                    }
144                }
145            } else if (now == Sensor.INACTIVE && getNXState() == EntryExitPairs.NXBUTTONSELECTED) {
146                //sensor inactive, nxbutton state was selected, going to set back to inactive - ie user cancelled button
147                setButtonState(EntryExitPairs.NXBUTTONINACTIVE);
148            } else if (now == Sensor.INACTIVE && getNXState() == EntryExitPairs.NXBUTTONACTIVE) {
149                //Sensor gone inactive, while nxbutton was selected - potential start of user either clear route or setting another
150                setButtonState(EntryExitPairs.NXBUTTONSELECTED);
151                for (Entry<PointDetails, DestinationPoints> en : sourceRoute.pointToDest.entrySet()) {
152                    //Sensor sen = getSensorFromPoint(en.getKey().getPoint());
153                    //Set a time out on the source sensor, so that if its state hasn't been changed, then we will clear it out.
154                    if (en.getValue().isEnabled() && !en.getValue().getUniDirection()) {
155                        if (en.getKey().getNXState() == EntryExitPairs.NXBUTTONSELECTED) {
156                            sourceRoute.activeBean(en.getValue(), false);
157                        }
158                    }
159                }
160            }
161        } else if (destPoint != null) {
162            //Button set as a destination but has no source, it has had a change in state
163            if (now == Sensor.ACTIVE) {
164                //State now is Active will set flashing
165                setButtonState(EntryExitPairs.NXBUTTONSELECTED);
166            } else if (getNXState() == EntryExitPairs.NXBUTTONACTIVE) {
167                //Sensor gone inactive while it was previosly active
168                setButtonState(EntryExitPairs.NXBUTTONSELECTED);
169            } else if (getNXState() == EntryExitPairs.NXBUTTONSELECTED) {
170                //Sensor gone inactive while it was previously selected therefore will cancel
171                setButtonState(EntryExitPairs.NXBUTTONINACTIVE);
172            }
173        }
174        jmri.InstanceManager.getDefault(jmri.jmrit.entryexit.EntryExitPairs.class).setMultiPointRoute(this, panel);
175    }
176
177    void setSignalMast(SignalMast mast) {
178        signalmast = mast;
179    }
180
181    void setSource(Source src) {
182        if (sourceRoute == src) {
183            return;
184        }
185        sourceRoute = src;
186    }
187
188    void setDestination(DestinationPoints srcdp, Source src) {
189        if (!destinations.containsKey(srcdp)) {
190            destinations.put(srcdp, src);
191        }
192    }
193
194    void removeDestination(DestinationPoints srcdp) {
195        destinations.remove(srcdp);
196        if (sourceRoute == null && destinations.isEmpty()) {
197            stopFlashSensor();
198            sensor.removePropertyChangeListener(nxButtonListener);
199            setSensor(null);
200        }
201    }
202
203    void removeSource(Source src) {
204        sourceRoute = null;
205        if (destinations.isEmpty()) {
206            stopFlashSensor();
207            setSensor(null);
208        }
209    }
210
211    private int nxButtonState = EntryExitPairs.NXBUTTONINACTIVE;
212
213    void setButtonState(int state) {
214        setNXButtonState(state);
215    }
216
217    void setNXState(int state) {
218        if (state == nxButtonState) {
219            return;
220        }
221        if (state == EntryExitPairs.NXBUTTONSELECTED) {
222            nxButtonTimeOut();
223            flashSensor();
224        } else {
225            cancelNXButtonTimeOut();
226            stopFlashSensor();
227        }
228        nxButtonState = state;
229    }
230
231    public int getNXState() {
232        return nxButtonState;
233    }
234
235    SignalMast getSignalMast() {
236        return signalmast;
237    }
238
239    void setSignalHead(SignalHead head) {
240        signalhead = head;
241    }
242
243    SignalHead getSignalHead() {
244        return signalhead;
245    }
246
247    public LayoutEditor getPanel() {
248        return panel;
249    }
250
251    public void setRefObject(NamedBean refObs) {
252        for (LayoutEditor pnl : InstanceManager.getDefault(EditorManager.class).getAll(LayoutEditor.class)) {
253            if (refLoc == null) {
254                setRefObjectByPanel(refObs, pnl);
255            }
256        }
257    }
258
259    public void setRefObjectByPanel(NamedBean refObs, LayoutEditor pnl) {
260        refObj = refObs;
261        if (pnl != null && refObj != null) {
262            if (refObj instanceof SignalMast || refObj instanceof Sensor) {
263                //String mast = ((SignalMast)refObj).getUserName();
264                refLoc = pnl.getFinder().findPositionablePointByEastBoundBean(refObj);
265                if (refLoc == null) {
266                    refLoc = pnl.getFinder().findPositionablePointByWestBoundBean(refObj);
267                }
268                if (refLoc == null) {
269                    refLoc = pnl.getFinder().findLayoutTurnoutByBean(refObj);
270                }
271                if (refLoc == null) {
272                    refLoc = pnl.getFinder().findLevelXingByBean(refObj);
273                }
274                if (refLoc == null) {
275                    refLoc = pnl.getFinder().findLayoutSlipByBean(refObj);
276                }
277                if (refObj instanceof Sensor) {
278                    setSensor((Sensor) refObj);
279                }
280            } else if (refObj instanceof SignalHead) {
281                String signal = ((SignalHead) refObj).getDisplayName();
282                refLoc = pnl.getFinder().findPositionablePointByEastBoundSignal(signal);
283                if (refLoc == null) {
284                    refLoc = pnl.getFinder().findPositionablePointByWestBoundSignal(signal);
285                }
286            }
287        }
288//        if (refLoc != null) {
289//            if (refLoc instanceof PositionablePoint) {
290//                //((PositionablePoint)refLoc).addPropertyChangeListener(this);
291//            } else if (refLoc instanceof LayoutTurnout) {  //<== this includes LayoutSlips
292//                //((LayoutTurnout)refLoc).addPropertyChangeListener(this);
293//            } else if (refLoc instanceof LevelXing) {
294//                //((LevelXing)refLoc).addPropertyChangeListener(this);
295//            }
296//        }
297        //With this set ref we can probably add a listener to it, so that we can detect when a change to the point details takes place
298    }
299
300    public NamedBean getRefObject() {
301        return refObj;
302    }
303
304    public Object getRefLocation() {
305        return refLoc;
306    }
307
308    //LayoutEditor getLayoutEditor() { return panel; }
309    public boolean isRouteToPointSet() {
310        return routeToSet;
311    }
312
313    public boolean isRouteFromPointSet() {
314        return routeFromSet;
315    }
316
317    public String getDisplayName() {
318        if (sensor != null) {
319            String description = sensor.getDisplayName();
320            if (signalmast != null) {
321                description = description + " (" + signalmast.getDisplayName() + ")";
322            }
323            return description;
324        }
325
326        if (refObj instanceof SignalMast) {
327            return ((SignalMast) refObj).getDisplayName();
328        } else if (refObj instanceof Sensor) {
329            return ((Sensor) refObj).getDisplayName();
330        } else if (refObj instanceof SignalHead) {
331            return ((SignalHead) refObj).getDisplayName();
332        }
333        return "no display name";  // NOI18N
334    }
335
336    transient Thread nxButtonTimeOutThr;
337
338    void nxButtonTimeOut() {
339        if ((nxButtonTimeOutThr != null) && (nxButtonTimeOutThr.isAlive())) {
340            return;
341        }
342        extendedtime = true;
343        class ButtonTimeOut implements Runnable {
344
345            ButtonTimeOut() {
346            }
347
348            @Override
349            public void run() {
350                try {
351                    //Stage one default timer for the button if no other button has been pressed
352                    Thread.sleep(nxButtonTimeout * 1000L);
353                    //Stage two if an extended time out has been requested
354                    if (extendedtime) {
355                        Thread.sleep(60000);  //timeout after a minute waiting for the sml to set.
356                    }
357                } catch (InterruptedException ex) {
358                    log.debug("Flash timer cancelled");  // NOI18N
359                }
360                setNXButtonState(EntryExitPairs.NXBUTTONINACTIVE);
361            }
362        }
363        ButtonTimeOut t = new ButtonTimeOut();
364        nxButtonTimeOutThr = jmri.util.ThreadingUtil.newThread(t, "NX Button Timeout " + getSensor().getDisplayName());  // NOI18N
365
366        nxButtonTimeOutThr.start();
367    }
368
369    void cancelNXButtonTimeOut() {
370        if (nxButtonTimeOutThr != null) {
371            nxButtonTimeOutThr.interrupt();
372        }
373
374    }
375
376    boolean extendedtime = false;
377
378    public void flashSensor() {
379        for (SensorIcon si : getPanel().getSensorList()) {
380            if (si.getSensor() == getSensor()) {
381                si.flashSensor(2, Sensor.ACTIVE, Sensor.INACTIVE);
382            }
383        }
384    }
385
386    public void stopFlashSensor() {
387        for (SensorIcon si : getPanel().getSensorList()) {
388            if (si.getSensor() == getSensor()) {
389                si.stopFlash();
390            }
391        }
392    }
393
394    synchronized public void setNXButtonState(int state) {
395        if (getSensor() == null) {
396            return;
397        }
398        if (state == EntryExitPairs.NXBUTTONINACTIVE) {
399            //If a route is set to or from out point then we need to leave/set the sensor to ACTIVE
400            if (isRouteToPointSet()) {
401                state = EntryExitPairs.NXBUTTONACTIVE;
402            } else if (isRouteFromPointSet()) {
403                state = EntryExitPairs.NXBUTTONACTIVE;
404            }
405        }
406        setNXState(state);
407        int sensorState;
408        switch (state) {
409            case EntryExitPairs.NXBUTTONINACTIVE:
410                sensorState = Sensor.INACTIVE;
411                break;
412            case EntryExitPairs.NXBUTTONACTIVE:
413                sensorState = Sensor.ACTIVE;
414                break;
415            case EntryExitPairs.NXBUTTONSELECTED:
416                sensorState = Sensor.ACTIVE;
417                break;
418            default:
419                sensorState = Sensor.UNKNOWN;
420                break;
421        }
422
423        //Might need to clear listeners at the stage and then reapply them after.
424        if (getSensor().getKnownState() != sensorState) {
425            removeSensorList();
426            try {
427                getSensor().setKnownState(sensorState);
428            } catch (jmri.JmriException ex) {
429                log.error("Could not set Sensor known state.", ex);
430            }
431            addSensorList();
432        }
433    }
434
435    /**
436     * @since 4.17.6
437     * Making the source object available for scripting in Jython.
438     * @return the sensor.
439     */
440    public Sensor getSensor() {
441        if (getRefObject() == null) {
442            return null;
443        }
444        if ((getPanel() != null) && (!getPanel().isEditable()) && (sensor != null)) {
445            return sensor;
446        }
447
448        if (getRefObject() instanceof Sensor) {
449            setSensor((Sensor) getRefObject());
450            return (Sensor) getRefObject();
451        }
452        Object objLoc = getRefLocation();
453        Object objRef = getRefObject();
454        SignalHead head = null;
455        SignalMast mast = null;
456        String username = "";
457        String systemname = "";
458        Sensor foundSensor = null;
459        if (objRef instanceof SignalMast) {
460            mast = (SignalMast) objRef;
461        }
462        if (objRef instanceof SignalHead) {
463            head = (SignalHead) objRef;
464            username = head.getUserName();
465            systemname = head.getSystemName();
466        }
467        jmri.SensorManager sm = InstanceManager.sensorManagerInstance();
468        if (objLoc instanceof PositionablePoint) {
469            PositionablePoint p = (PositionablePoint) objLoc;
470            if (mast != null) {
471                if (p.getEastBoundSignalMast() == objRef) {
472                    foundSensor = p.getEastBoundSensor();
473                } else if (p.getWestBoundSignalMast() == objRef) {
474                    foundSensor = p.getWestBoundSensor();
475                }
476            } else if (head != null) {
477                if ((p.getEastBoundSignal().equals(username))
478                        || p.getEastBoundSignal().equals(systemname)) {
479                    foundSensor = p.getEastBoundSensor();
480                } else if ((p.getWestBoundSignal().equals(username))
481                        || p.getWestBoundSignal().equals(systemname)) {
482                    foundSensor = p.getWestBoundSensor();
483                }
484            }
485        } else if (objLoc instanceof LayoutSlip) {
486            LayoutSlip sl = (LayoutSlip) objLoc;
487            if (mast != null) {
488                if (sl.getSignalAMast() == objRef) {
489                    foundSensor = sl.getSensorA();
490                } else if (sl.getSignalBMast() == objRef) {
491                    foundSensor = sl.getSensorB();
492                } else if (sl.getSignalCMast() == objRef) {
493                    foundSensor = sl.getSensorC();
494                } else if (sl.getSignalDMast() == objRef) {
495                    foundSensor = sl.getSensorD();
496                }
497            }
498            if (head != null) {
499                if ((sl.getSignalA1Name().equals(username)) || (sl.getSignalA1Name().equals(systemname))) {
500                    foundSensor = sm.getSensor(sl.getSensorAName());
501                } else if ((sl.getSignalB1Name().equals(username)) || (sl.getSignalB1Name().equals(systemname))) {
502                    foundSensor = sm.getSensor(sl.getSensorBName());
503                } else if ((sl.getSignalC1Name().equals(username)) || (sl.getSignalC1Name().equals(systemname))) {
504                    foundSensor = sm.getSensor(sl.getSensorCName());
505                } else if ((sl.getSignalD1Name().equals(username)) || (sl.getSignalD1Name().equals(systemname))) {
506                    foundSensor = sm.getSensor(sl.getSensorDName());
507                }
508            }
509        } else //note: you have to do this after LayoutSlip
510        // because LayoutSlip extends LayoutTurnout
511        // (So a LayoutSlip would be an instance of LayoutTurnout.)
512        if (objLoc instanceof LayoutTurnout) {  //<== this includes LayoutSlips
513            LayoutTurnout t = (LayoutTurnout) objLoc;
514            if (mast != null) {
515                if (t.getSignalAMast() == objRef) {
516                    foundSensor = t.getSensorA();
517                } else if (t.getSignalBMast() == objRef) {
518                    foundSensor = t.getSensorB();
519                } else if (t.getSignalCMast() == objRef) {
520                    foundSensor = t.getSensorC();
521                } else if (t.getSignalDMast() == objRef) {
522                    foundSensor = t.getSensorD();
523                }
524            }
525            if (head != null) {
526                if ((t.getSignalA1Name().equals(username)) || (t.getSignalA1Name().equals(systemname))) {
527                    foundSensor = t.getSensorA();
528                } else if ((t.getSignalA2Name().equals(username)) || (t.getSignalA2Name().equals(systemname))) {
529                    foundSensor = t.getSensorA();
530                } else if ((t.getSignalA3Name().equals(username)) || (t.getSignalA3Name().equals(systemname))) {
531                    foundSensor = t.getSensorA();
532                } else if ((t.getSignalB1Name().equals(username)) || (t.getSignalB1Name().equals(systemname))) {
533                    foundSensor = t.getSensorB();
534                } else if ((t.getSignalB2Name().equals(username)) || (t.getSignalB2Name().equals(systemname))) {
535                    foundSensor = t.getSensorB();
536                } else if ((t.getSignalC1Name().equals(username)) || (t.getSignalC1Name().equals(systemname))) {
537                    foundSensor = t.getSensorC();
538                } else if ((t.getSignalC2Name().equals(username)) || (t.getSignalC2Name().equals(systemname))) {
539                    foundSensor = t.getSensorC();
540                } else if ((t.getSignalD1Name().equals(username)) || (t.getSignalD1Name().equals(systemname))) {
541                    foundSensor = t.getSensorD();
542                } else if ((t.getSignalD2Name().equals(username)) || (t.getSignalD2Name().equals(systemname))) {
543                    foundSensor = t.getSensorD();
544                }
545            }
546        } else if (objLoc instanceof LevelXing) {
547            LevelXing x = (LevelXing) objLoc;
548            if (mast != null) {
549                if (x.getSignalAMast() == objRef) {
550                    foundSensor = x.getSensorA();
551                } else if (x.getSignalBMast() == objRef) {
552                    foundSensor = x.getSensorB();
553                } else if (x.getSignalCMast() == objRef) {
554                    foundSensor = x.getSensorC();
555                } else if (x.getSignalDMast() == objRef) {
556                    foundSensor = x.getSensorD();
557                }
558            }
559            if (head != null) {
560                if ((x.getSignalAName().equals(username)) || (x.getSignalAName().equals(systemname))) {
561                    foundSensor = x.getSensorA();
562                } else if ((x.getSignalBName().equals(username)) || (x.getSignalBName().equals(systemname))) {
563                    foundSensor = x.getSensorB();
564                } else if ((x.getSignalCName().equals(username)) || (x.getSignalCName().equals(systemname))) {
565                    foundSensor = x.getSensorC();
566                } else if ((x.getSignalDName().equals(username)) || (x.getSignalDName().equals(systemname))) {
567                    foundSensor = x.getSensorD();
568                }
569            }
570        }
571        setSensor(foundSensor);
572        return foundSensor;
573    }
574
575    NamedBean getSignal() {
576        if ((getPanel() != null) && (!getPanel().isEditable()) && (getSignalMast() != null)) {
577            return getSignalMast();
578        }
579        if ((getPanel() != null) && (!getPanel().isEditable()) && (getSignalHead() != null)) {
580            return getSignalHead();
581        }
582        jmri.SignalHeadManager sh = InstanceManager.getDefault(jmri.SignalHeadManager.class);
583        NamedBean signal = null;
584
585        if (getRefObject() == null) {
586            log.error("Signal not found at point");  // NOI18N
587            return null;
588        } else if (getRefObject() instanceof SignalMast) {
589            signal = getRefObject();
590            setSignalMast(((SignalMast) getRefObject()));
591            return signal;
592        } else if (getRefObject() instanceof SignalHead) {
593            signal = getRefObject();
594            setSignalHead(((SignalHead) getRefObject()));
595            return signal;
596        }
597
598        Sensor sen = (Sensor) getRefObject();
599        log.debug("  Looking at sensor '{}' on panel '{}' at '{}'",
600                sen.getDisplayName(), getPanel().getLayoutName(), getRefLocation());
601        if (getRefLocation() instanceof PositionablePoint) {
602            PositionablePoint p = (PositionablePoint) getRefLocation();
603            if (p.getEastBoundSensor() == sen) {
604                if (p.getEastBoundSignalMast() != null) {
605                    signal = p.getEastBoundSignalMast();
606                } else if (!p.getEastBoundSignal().isEmpty()) {
607                    signal = sh.getSignalHead(p.getEastBoundSignal());
608                }
609            } else if (p.getWestBoundSensor() == sen) {
610                if (p.getWestBoundSignalMast() != null) {
611                    signal = p.getWestBoundSignalMast();
612                } else if (!p.getWestBoundSignal().isEmpty()) {
613                    signal = sh.getSignalHead(p.getWestBoundSignal());
614                }
615            }
616        } else if (getRefLocation() instanceof LayoutSlip) {
617            LayoutSlip t = (LayoutSlip) getRefLocation();
618            if (t.getSensorA() == sen) {
619                if (t.getSignalAMast() != null) {
620                    signal = t.getSignalAMast();
621                } else if (!t.getSignalA1Name().isEmpty()) {
622                    signal = sh.getSignalHead(t.getSignalA1Name());
623                }
624            } else if (t.getSensorB() == sen) {
625                if (t.getSignalBMast() != null) {
626                    signal = t.getSignalBMast();
627                } else if (!t.getSignalB1Name().isEmpty()) {
628                    signal = sh.getSignalHead(t.getSignalB1Name());
629                }
630            } else if (t.getSensorC() == sen) {
631                if (t.getSignalCMast() != null) {
632                    signal = t.getSignalCMast();
633                } else if (!t.getSignalC1Name().isEmpty()) {
634                    signal = sh.getSignalHead(t.getSignalC1Name());
635                }
636            } else if (t.getSensorD() == sen) {
637                if (t.getSignalDMast() != null) {
638                    signal = t.getSignalDMast();
639                } else if (!t.getSignalD1Name().isEmpty()) {
640                    signal = sh.getSignalHead(t.getSignalD1Name());
641                }
642            }
643        } else //note: you have to do this after LayoutSlip
644        // because LayoutSlip extends LayoutTurnout
645        // (So a LayoutSlip would be an instance of LayoutTurnout.)
646        if (getRefLocation() instanceof LayoutTurnout) {  //<== this includes LayoutSlips
647            LayoutTurnout t = (LayoutTurnout) getRefLocation();
648            if (t.getSensorA() == sen) {
649                if (t.getSignalAMast() != null) {
650                    signal = t.getSignalAMast();
651                } else if (!t.getSignalA1Name().isEmpty()) {
652                    signal = sh.getSignalHead(t.getSignalA1Name());
653                }
654            } else if (t.getSensorB() == sen) {
655                if (t.getSignalBMast() != null) {
656                    signal = t.getSignalBMast();
657                } else if (!t.getSignalB1Name().isEmpty()) {
658                    signal = sh.getSignalHead(t.getSignalB1Name());
659                }
660            } else if (t.getSensorC() == sen) {
661                if (t.getSignalCMast() != null) {
662                    signal = t.getSignalCMast();
663                } else if (!t.getSignalC1Name().isEmpty()) {
664                    signal = sh.getSignalHead(t.getSignalC1Name());
665                }
666            } else if (t.getSensorD() == sen) {
667                if (t.getSignalDMast() != null) {
668                    signal = t.getSignalDMast();
669                } else if (!t.getSignalD1Name().isEmpty()) {
670                    signal = sh.getSignalHead(t.getSignalD1Name());
671                }
672            }
673        } else if (getRefLocation() instanceof LevelXing) {
674            LevelXing x = (LevelXing) getRefLocation();
675            if (x.getSensorA() == sen) {
676                if (x.getSignalAMast() != null) {
677                    signal = x.getSignalAMast();
678                } else if (!x.getSignalAName().isEmpty()) {
679                    signal = sh.getSignalHead(x.getSignalAName());
680                }
681            } else if (x.getSensorB() == sen) {
682                if (x.getSignalBMast() != null) {
683                    signal = x.getSignalBMast();
684                } else if (!x.getSignalBName().isEmpty()) {
685                    signal = sh.getSignalHead(x.getSignalBName());
686                }
687            } else if (x.getSensorC() == sen) {
688                if (x.getSignalCMast() != null) {
689                    signal = x.getSignalCMast();
690                } else if (!x.getSignalCName().isEmpty()) {
691                    signal = sh.getSignalHead(x.getSignalCName());
692                }
693            } else if (x.getSensorD() == sen) {
694                if (x.getSignalDMast() != null) {
695                    signal = x.getSignalDMast();
696                } else if (!x.getSignalDName().isEmpty()) {
697                    signal = sh.getSignalHead(x.getSignalDName());
698                }
699            }
700        }
701
702        if (signal instanceof SignalMast) {
703            setSignalMast(((SignalMast) signal));
704        } else if (signal instanceof SignalHead) {
705            setSignalHead(((SignalHead) signal));
706        }
707        return signal;
708    }
709
710    @Override
711    public boolean equals(Object obj) {
712        if (obj == this) {
713            return true;
714        }
715        if (obj == null) {
716            return false;
717        }
718
719        if (!(getClass() == obj.getClass())) {
720            return false;
721        } else {
722            PointDetails tmp = (PointDetails) obj;
723            if (tmp.getFacing() != this.facing) {
724                return false;
725            }
726            if (!tmp.getProtecting().equals(this.protectingBlocks)) {
727                return false;
728            }
729            if (tmp.getPanel() != this.panel) {
730                return false;
731            }
732        }
733        return true;
734    }
735
736    @Override
737    public int hashCode() {
738        int hash = 7;
739        hash = 37 * hash + (this.panel != null ? this.panel.hashCode() : 0);
740        hash = 37 * hash + (this.facing != null ? this.facing.hashCode() : 0);
741        hash = 37 * hash + (this.protectingBlocks != null ? this.protectingBlocks.hashCode() : 0);
742        return hash;
743    }
744
745    java.beans.PropertyChangeSupport pcs = new java.beans.PropertyChangeSupport(this);
746
747    public synchronized void addPropertyChangeListener(java.beans.PropertyChangeListener l) {
748        pcs.addPropertyChangeListener(l);
749    }
750
751    public synchronized void removePropertyChangeListener(java.beans.PropertyChangeListener l) {
752        pcs.removePropertyChangeListener(l);
753    }
754
755    protected void firePropertyChange(String p, Object old, Object n) {
756        pcs.firePropertyChange(p, old, n);
757    }
758
759    private final static Logger log = LoggerFactory.getLogger(PointDetails.class);
760}