001package jmri.implementation;
002
003import java.beans.PropertyChangeEvent;
004import java.beans.PropertyChangeListener;
005import java.beans.PropertyVetoException;
006import java.beans.VetoableChangeListener;
007import java.util.*;
008
009import javax.annotation.Nonnull;
010
011import jmri.*;
012import jmri.jmrit.display.EditorManager;
013import jmri.jmrit.display.layoutEditor.ConnectivityUtil;
014import jmri.jmrit.display.layoutEditor.LayoutBlock;
015import jmri.jmrit.display.layoutEditor.LayoutBlockConnectivityTools;
016import jmri.jmrit.display.layoutEditor.LayoutBlockManager;
017import jmri.jmrit.display.layoutEditor.LayoutEditor;
018import jmri.jmrit.display.layoutEditor.LayoutSlip;
019import jmri.jmrit.display.layoutEditor.LayoutTrackExpectedState;
020import jmri.jmrit.display.layoutEditor.LayoutTurnout;
021import jmri.jmrit.display.layoutEditor.LayoutTraverser;
022import jmri.jmrit.display.layoutEditor.LayoutTurntable;
023import jmri.jmrit.display.layoutEditor.LevelXing;
024import jmri.util.ThreadingUtil;
025
026/**
027 * Default implementation of {@link jmri.SignalMastLogic}.
028 *
029 * @author Kevin Dickerson Copyright (C) 2011
030 */
031public class DefaultSignalMastLogic extends AbstractNamedBean implements SignalMastLogic, VetoableChangeListener {
032
033    SignalMast source;
034    SignalMast destination;
035    String stopAspect;
036
037    Hashtable<SignalMast, DestinationMast> destList = new Hashtable<>();
038    LayoutEditor editor;
039
040    LayoutBlock facingBlock = null;
041    LayoutBlock remoteProtectingBlock = null;
042
043    boolean disposing = false;
044
045    /**
046     * Initialise a Signal Mast Logic for a given source Signal Mast.
047     *
048     * @param source  The Signal Mast we are configuring an SML for
049     */
050    public DefaultSignalMastLogic(@Nonnull SignalMast source) {
051        super(source.toString()); // default system name
052        this.source = source;
053        try {
054            this.stopAspect = source.getAppearanceMap().getSpecificAppearance(SignalAppearanceMap.DANGER);
055            this.source.addPropertyChangeListener(propertySourceMastListener);
056            if (source.getAspect() == null) {
057                source.setAspect(stopAspect);
058            }
059        } catch (Exception ex) {
060            log.error("Error while creating Signal Logic", ex);
061        }
062    }
063
064    // Most of the following methods will inherit Javadoc from SignalMastLogic.java
065    /**
066     * {@inheritDoc }
067     */
068    @Override
069    public void setFacingBlock(LayoutBlock facing) {
070        facingBlock = facing;
071    }
072
073    /**
074     * {@inheritDoc }
075     */
076    @Override
077    public LayoutBlock getFacingBlock() {
078        return facingBlock;
079    }
080
081    /**
082     * {@inheritDoc }
083     */
084    @Override
085    public LayoutBlock getProtectingBlock(@Nonnull SignalMast dest) {
086        if (!destList.containsKey(dest)) {
087            return null;
088        }
089        return destList.get(dest).getProtectingBlock();
090    }
091
092    /**
093     * {@inheritDoc }
094     */
095    @Override
096    public SignalMast getSourceMast() {
097        return source;
098    }
099
100    /**
101     * {@inheritDoc }
102     */
103    @Override
104    public void replaceSourceMast(SignalMast oldMast, SignalMast newMast) {
105        if (oldMast != source) {
106            // Old mast does not match new mast so will exit replace
107            return;
108        }
109        source.removePropertyChangeListener(propertySourceMastListener);
110        source = newMast;
111        stopAspect = source.getAppearanceMap().getSpecificAppearance(SignalAppearanceMap.DANGER);
112        source.addPropertyChangeListener(propertySourceMastListener);
113        if (source.getAspect() == null) {
114            source.setAspect(stopAspect);
115        }
116        getDestinationList().forEach(sm -> {
117            DestinationMast destMast = destList.get(sm);
118            if (destMast.getAssociatedSection() != null) {
119                String oldUserName = destMast.getAssociatedSection().getUserName();
120                String newUserName = source.getDisplayName() + ":" + sm.getDisplayName();
121                if (oldUserName != null) {
122                    InstanceManager.getDefault(NamedBeanHandleManager.class)
123                        .renameBean(oldUserName, newUserName, ((NamedBean) destMast.getAssociatedSection()));
124                } else {
125                    log.warn("AssociatedSection oldUserName null for destination mast {}, skipped",
126                        destMast.getDisplayName());
127                }
128            }
129        });
130        firePropertyChange(PROPERTY_UPDATED_SOURCE, oldMast, newMast);
131    }
132
133    /**
134     * {@inheritDoc }
135     */
136    @Override
137    public void replaceDestinationMast(SignalMast oldMast, SignalMast newMast) {
138        if (!destList.containsKey(oldMast)) {
139            return;
140        }
141        DestinationMast destMast = destList.get(oldMast);
142        destMast.updateDestinationMast(newMast);
143        if (destination == oldMast) {
144            oldMast.removePropertyChangeListener(propertyDestinationMastListener);
145            newMast.addPropertyChangeListener(propertyDestinationMastListener);
146            destination = newMast;
147            setSignalAppearance();
148        }
149        destList.remove(oldMast);
150        if (destMast.getAssociatedSection() != null) {
151            String oldUserName = destMast.getAssociatedSection().getUserName();
152            String newUserName = source.getDisplayName() + ":" + newMast.getDisplayName();
153            if (oldUserName != null) {
154                InstanceManager.getDefault(NamedBeanHandleManager.class)
155                    .renameBean(oldUserName, newUserName, destMast.getAssociatedSection());
156            } else {
157                log.warn("AssociatedSection oldUserName null for destination mast {}, skipped",
158                    destMast.getDisplayName());
159            }
160        }
161        destList.put(newMast, destMast);
162        firePropertyChange(PROPERTY_UPDATED_DESTINATION, oldMast, newMast);
163    }
164
165    /**
166     * {@inheritDoc }
167     */
168    @Override
169    public void setDestinationMast(SignalMast dest) {
170        if (destList.containsKey(dest)) {
171            // if already present, not a change
172            log.debug("Destination mast '{}' was already defined in SML with this source mast", dest.getDisplayName());
173            return;
174        }
175        int oldSize = destList.size();
176        destList.put(dest, new DestinationMast(dest));
177        //InstanceManager.getDefault(SignalMastLogicManager.class).addDestinationMastToLogic(this, dest);
178        firePropertyChange(PROPERTY_LENGTH, oldSize, destList.size());
179        // make new dest mast appear in (update of) SignallingSourcePanel Table by having that table listen to PropertyChange Events from SML TODO
180    }
181
182    /**
183     * {@inheritDoc }
184     */
185    @Override
186    public boolean isDestinationValid(SignalMast dest) {
187        if (dest == null) {
188            return false;
189        }
190        return destList.containsKey(dest);
191    }
192
193    /**
194     * {@inheritDoc }
195     */
196    @Override
197    public List<SignalMast> getDestinationList() {
198        List<SignalMast> out = new ArrayList<>();
199        Enumeration<SignalMast> en = destList.keys();
200        while (en.hasMoreElements()) {
201            out.add(en.nextElement());
202        }
203        return out;
204    }
205
206    /**
207     * {@inheritDoc }
208     */
209    @Override
210    public String getComment(SignalMast dest) {
211        if (!destList.containsKey(dest)) {
212            return "";
213        }
214        return destList.get(dest).getComment();
215    }
216
217    /**
218     * {@inheritDoc }
219     */
220    @Override
221    public void setComment(String comment, SignalMast dest) {
222        if (!destList.containsKey(dest)) {
223            return;
224        }
225        destList.get(dest).setComment(comment);
226    }
227
228    /**
229     * {@inheritDoc }
230     */
231    @Override
232    public void setStore(int store, SignalMast destination) {
233        if (!destList.containsKey(destination)) {
234            return;
235        }
236        destList.get(destination).setStore(store);
237    }
238
239    /**
240     * {@inheritDoc }
241     */
242    @Override
243    public int getStoreState(SignalMast destination) {
244        if (!destList.containsKey(destination)) {
245            return STORENONE;
246        }
247        return destList.get(destination).getStoreState();
248    }
249
250    /**
251     * {@inheritDoc }
252     */
253    @Override
254    public void setEnabled(SignalMast dest) {
255        if (!destList.containsKey(dest)) {
256            return;
257        }
258        destList.get(dest).setEnabled();
259    }
260
261    /**
262     * {@inheritDoc }
263     */
264    @Override
265    public void setDisabled(SignalMast dest) {
266        if (!destList.containsKey(dest)) {
267            return;
268        }
269        destList.get(dest).setDisabled();
270    }
271
272    /**
273     * {@inheritDoc }
274     */
275    @Override
276    public boolean isEnabled(SignalMast dest) {
277        if (!destList.containsKey(dest)) {
278            return false;
279        }
280        return destList.get(dest).isEnabled();
281    }
282
283    /**
284     * {@inheritDoc }
285     */
286    @Override
287    public boolean isActive(SignalMast dest) {
288        if (!destList.containsKey(dest)) {
289            return false;
290        }
291        return destList.get(dest).isActive();
292    }
293
294    /**
295     * {@inheritDoc }
296     */
297    @Override
298    public SignalMast getActiveDestination() {
299        for (SignalMast sm : getDestinationList()) {
300            if (destList.get(sm).isActive()) {
301                return sm;
302            }
303        }
304        return null;
305    }
306
307    /**
308     * {@inheritDoc }
309     */
310    @Override
311    public boolean removeDestination(SignalMast dest) {
312        int oldSize = destList.size();
313        if (destList.containsKey(dest)) {
314            //InstanceManager.getDefault(SignalMastLogicManager.class).removeDestinationMastToLogic(this, dest);
315            destList.get(dest).dispose();
316            destList.remove(dest);
317            firePropertyChange(PROPERTY_LENGTH, oldSize, destList.size());
318        }
319        return destList.isEmpty();
320    }
321
322    /**
323     * {@inheritDoc }
324     */
325    @Override
326    public void disableLayoutEditorUse() {
327        destList.values().forEach(dest -> {
328            try {
329                dest.useLayoutEditor(false);
330            } catch (JmriException e) {
331                log.error("Could not disable LayoutEditor ",  e);
332            }
333        });
334    }
335
336    /**
337     * {@inheritDoc }
338     */
339    @Override
340    public void useLayoutEditor(boolean boo, SignalMast destination) throws JmriException {
341        if (!destList.containsKey(destination)) {
342            return;
343        }
344        if (boo) {
345            log.debug("Set use layout editor");
346            Set<LayoutEditor> layout = InstanceManager.getDefault(EditorManager.class).getAll(LayoutEditor.class);
347            /*We don't care which layout editor panel the signalmast is on, just so long as
348             the routing is done via layout blocks*/
349            // TODO: what is this?
350            log.debug("userLayoutEditor finds layout list size is {}", layout.size());
351            for (LayoutEditor findeditor : layout) {
352                log.debug("layouteditor {}", findeditor.getLayoutName());
353                if (facingBlock == null) {
354                    facingBlock = InstanceManager.getDefault(LayoutBlockManager.class)
355                        .getFacingBlockByMast(getSourceMast(), findeditor);
356                }
357            }
358        }
359        destList.get(destination).useLayoutEditor(boo);
360    }
361
362    /**
363     * Add direction sensors to SML
364     *
365     * @return number of errors
366     */
367    @Override
368    public int setupDirectionSensors() {
369        // iterrate over the signal masts
370        int errorCount = 0;
371        for (SignalMast sm : getDestinationList()) {
372            String displayName = sm.getDisplayName();
373            Section sec = getAssociatedSection(sm);
374            if (sec != null) {
375                Block thisFacingBlock;
376                Sensor fwd = sec.getForwardBlockingSensor();
377                Sensor rev = sec.getReverseBlockingSensor();
378                LayoutBlock lBlock = getFacingBlock();
379                if (lBlock == null) {
380                    try {
381                        useLayoutEditor(true, sm); // force a refind
382                    } catch (JmriException ex) {
383                        continue;
384                    }
385                }
386                if (lBlock != null) {
387                    thisFacingBlock = lBlock.getBlock();
388                    EntryPoint fwdEntryPoint = sec.getEntryPointFromBlock(thisFacingBlock, Section.FORWARD);
389                    EntryPoint revEntryPoint = sec.getEntryPointFromBlock(thisFacingBlock, Section.REVERSE);
390                    log.debug("Mast[{}] Sec[{}] Fwd[{}] Rev [{}]",
391                            displayName, sec, fwd, rev);
392                    if (fwd != null && fwdEntryPoint != null) {
393                        addSensor(fwd.getUserName(), Sensor.INACTIVE, sm);
394                        log.debug("Mast[{}] Sec[{}] Fwd[{}] fwdEP[{}]",
395                                displayName, sec, fwd,
396                                fwdEntryPoint.getBlock().getUserName());
397
398                    } else if (rev != null && revEntryPoint != null) {
399                        addSensor(rev.getUserName(), Sensor.INACTIVE, sm);
400                        log.debug("Mast[{}] Sec[{}] Rev [{}] revEP[{}]",
401                                displayName, sec, rev,
402                                revEntryPoint.getBlock().getUserName());
403
404                    } else {
405                        log.error("Mast[{}] Cannot Establish entry point to protected section", displayName);
406                        errorCount += 1;
407                    }
408                } else {
409                    log.error("Mast[{}] No Facing Block", displayName);
410                    errorCount += 1;
411                }
412            } else {
413                log.error("Mast[{}] No Associated Section", displayName);
414                errorCount += 1;
415            }
416        }
417        return errorCount;
418    }
419
420    /**
421     * {@inheritDoc }
422     */
423    @Override
424    public void removeDirectionSensors() {
425        //TODO find aaway of easilty identifying the ones we added.
426    }
427
428    /**
429     * {@inheritDoc }
430     */
431    @Override
432    public boolean useLayoutEditor(SignalMast destination) {
433        if (!destList.containsKey(destination)) {
434            return false;
435        }
436        return destList.get(destination).useLayoutEditor();
437    }
438
439    /**
440     * {@inheritDoc }
441     */
442    @Override
443    public void useLayoutEditorDetails(boolean turnouts, boolean blocks, SignalMast destination) throws JmriException {
444        if (!destList.containsKey(destination)) {
445            return;
446        }
447        destList.get(destination).useLayoutEditorDetails(turnouts, blocks);
448    }
449
450    /**
451     * {@inheritDoc }
452     */
453    @Override
454    public boolean useLayoutEditorBlocks(SignalMast destination) {
455        if (!destList.containsKey(destination)) {
456            return false;
457        }
458        return destList.get(destination).useLayoutEditorBlocks();
459    }
460
461    /**
462     * {@inheritDoc }
463     */
464    @Override
465    public boolean useLayoutEditorTurnouts(SignalMast destination) {
466        if (!destList.containsKey(destination)) {
467            return false;
468        }
469        return destList.get(destination).useLayoutEditorTurnouts();
470    }
471
472    /**
473     * {@inheritDoc }
474     */
475    @Override
476    public Section getAssociatedSection(SignalMast destination) {
477        if (!destList.containsKey(destination)) {
478            return null;
479        }
480        return destList.get(destination).getAssociatedSection();
481    }
482
483    /**
484     * {@inheritDoc }
485     */
486    @Override
487    public void setAssociatedSection(Section sec, SignalMast destination) {
488        if (!destList.containsKey(destination)) {
489            return;
490        }
491        destList.get(destination).setAssociatedSection(sec);
492    }
493
494    /**
495     * {@inheritDoc }
496     */
497    @Override
498    public boolean allowAutoMaticSignalMastGeneration(SignalMast destination) {
499        if (!destList.containsKey(destination)) {
500            return false;
501        }
502        return destList.get(destination).allowAutoSignalMastGen();
503    }
504
505    /**
506     * {@inheritDoc }
507     */
508    @Override
509    public void allowAutoMaticSignalMastGeneration(boolean allow, SignalMast destination) {
510        if (!destList.containsKey(destination)) {
511            return;
512        }
513        destList.get(destination).allowAutoSignalMastGen(allow);
514    }
515
516    /**
517     * {@inheritDoc }
518     */
519    @Override
520    public void allowTurnoutLock(boolean lock, SignalMast destination) {
521        if (!destList.containsKey(destination)) {
522            return;
523        }
524        destList.get(destination).allowTurnoutLock(lock);
525    }
526
527    /**
528     * {@inheritDoc }
529     */
530    @Override
531    public boolean isTurnoutLockAllowed(SignalMast destination) {
532        if (!destList.containsKey(destination)) {
533            return false;
534        }
535        return destList.get(destination).isTurnoutLockAllowed();
536    }
537
538    /**
539     * {@inheritDoc }
540     */
541    @Override
542    public void setTurnouts(Hashtable<NamedBeanHandle<Turnout>, Integer> turnouts, SignalMast destination) {
543        if (!destList.containsKey(destination)) {
544            return;
545        }
546        destList.get(destination).setTurnouts(turnouts);
547    }
548
549    /**
550     * {@inheritDoc }
551     */
552    @Override
553    public void setAutoTurnouts(Hashtable<Turnout, Integer> turnouts, SignalMast destination) {
554        if (!destList.containsKey(destination)) {
555            return;
556        }
557        destList.get(destination).setAutoTurnouts(turnouts);
558    }
559
560    /**
561     * {@inheritDoc }
562     */
563    @Override
564    public void setBlocks(Hashtable<Block, Integer> blocks, SignalMast destination) {
565        if (!destList.containsKey(destination)) {
566            return;
567        }
568        destList.get(destination).setBlocks(blocks);
569    }
570
571    /**
572     * {@inheritDoc }
573     */
574    @Override
575    public void setAutoBlocks(LinkedHashMap<Block, Integer> blocks, SignalMast destination) {
576        if (!destList.containsKey(destination)) {
577            return;
578        }
579        destList.get(destination).setAutoBlocks(blocks);
580    }
581
582    /**
583     * {@inheritDoc }
584     */
585    @Override
586    public void setMasts(Hashtable<SignalMast, String> masts, SignalMast destination) {
587        if (!destList.containsKey(destination)) {
588            return;
589        }
590        destList.get(destination).setMasts(masts);
591    }
592
593    /**
594     * {@inheritDoc }
595     */
596    @Override
597    public void setAutoMasts(Hashtable<SignalMast, String> masts, SignalMast destination) {
598        if (!destList.containsKey(destination)) {
599            return;
600        }
601        destList.get(destination).setAutoMasts(masts, true);
602    }
603
604    /**
605     * {@inheritDoc }
606     */
607    @Override
608    public void setSensors(Hashtable<NamedBeanHandle<Sensor>, Integer> sensors, SignalMast destination) {
609        if (!destList.containsKey(destination)) {
610            return;
611        }
612        destList.get(destination).setSensors(sensors);
613    }
614
615    /**
616     * {@inheritDoc }
617     */
618    @Override
619    public void addSensor(String sensorName, int state, SignalMast destination) {
620        if (!destList.containsKey(destination)) {
621            return;
622        }
623        Sensor sen = InstanceManager.sensorManagerInstance().getSensor(sensorName);
624        if (sen != null) {
625            NamedBeanHandle<Sensor> namedSensor = InstanceManager.getDefault(NamedBeanHandleManager.class)
626                .getNamedBeanHandle(sensorName, sen);
627            destList.get(destination).addSensor(namedSensor, state);
628        }
629    }
630
631    /**
632     * {@inheritDoc }
633     */
634    @Override
635    public void removeSensor(String sensorName, SignalMast destination) {
636        Sensor sen = InstanceManager.sensorManagerInstance().getSensor(sensorName);
637        removeSensor(sen, destination);
638    }
639
640    public void removeSensor(Sensor sen, SignalMast destination) {
641        if (!destList.containsKey(destination)) {
642            return;
643        }
644        if (sen != null) {
645            destList.get(destination).removeSensor(sen);
646        }
647    }
648
649    /**
650     * {@inheritDoc }
651     */
652    @Override
653    public List<Block> getBlocks(SignalMast destination) {
654        if (!destList.containsKey(destination)) {
655            return new ArrayList<>();
656        }
657        return destList.get(destination).getBlocks();
658    }
659
660    /**
661     * {@inheritDoc }
662     */
663    @Override
664    public List<Block> getAutoBlocks(SignalMast destination) {
665        if (!destList.containsKey(destination)) {
666            return new ArrayList<>();
667        }
668        return destList.get(destination).getAutoBlocks();
669    }
670
671    /**
672     * {@inheritDoc }
673     */
674    @Override
675    public List<Block> getAutoBlocksBetweenMasts(SignalMast destination) {
676        if (!destList.containsKey(destination)) {
677            return new ArrayList<>();
678        }
679        return destList.get(destination).getAutoBlocksBetweenMasts();
680    }
681
682    /**
683     * {@inheritDoc }
684     */
685    @Override
686    public List<Turnout> getTurnouts(SignalMast destination) {
687        if (!destList.containsKey(destination)) {
688            return new ArrayList<>();
689        }
690        return destList.get(destination).getTurnouts();
691    }
692
693    /**
694     * {@inheritDoc }
695     */
696    @Override
697    public List<NamedBeanHandle<Turnout>> getNamedTurnouts(SignalMast destination) {
698        if (!destList.containsKey(destination)) {
699            return new ArrayList<>();
700        }
701        return destList.get(destination).getNamedTurnouts();
702    }
703
704    public void removeTurnout(Turnout turn, SignalMast destination) {
705        if (!destList.containsKey(destination)) {
706            return;
707        }
708
709        if (turn != null) {
710            destList.get(destination).removeTurnout(turn);
711        }
712    }
713
714    /**
715     * {@inheritDoc }
716     */
717    @Override
718    public List<Turnout> getAutoTurnouts(SignalMast destination) {
719        if (!destList.containsKey(destination)) {
720            return new ArrayList<>();
721        }
722        return destList.get(destination).getAutoTurnouts();
723    }
724
725    /**
726     * {@inheritDoc }
727     */
728    @Override
729    public List<Sensor> getSensors(SignalMast destination) {
730        if (!destList.containsKey(destination)) {
731            return new ArrayList<>();
732        }
733        return destList.get(destination).getSensors();
734    }
735
736    /**
737     * {@inheritDoc }
738     */
739    @Override
740    public List<NamedBeanHandle<Sensor>> getNamedSensors(SignalMast destination) {
741        if (!destList.containsKey(destination)) {
742            return new ArrayList<>();
743        }
744        return destList.get(destination).getNamedSensors();
745    }
746
747    /**
748     * {@inheritDoc }
749     */
750    @Override
751    public List<SignalMast> getSignalMasts(SignalMast destination) {
752        if (!destList.containsKey(destination)) {
753            return new ArrayList<>();
754        }
755        return destList.get(destination).getSignalMasts();
756    }
757
758    /**
759     * {@inheritDoc }
760     */
761    @Override
762    public List<SignalMast> getAutoMasts(SignalMast destination) {
763        if (!destList.containsKey(destination)) {
764            return new ArrayList<>();
765        }
766        return destList.get(destination).getAutoSignalMasts();
767    }
768
769    /**
770     * {@inheritDoc }
771     */
772    @Override
773    public void initialise() {
774        Enumeration<SignalMast> en = destList.keys();
775        while (en.hasMoreElements()) {
776            destList.get(en.nextElement()).initialise();
777        }
778    }
779
780    /**
781     * {@inheritDoc }
782     */
783    @Override
784    public void initialise(SignalMast destination) {
785        if (disposing) {
786            return;
787        }
788
789        if (!destList.containsKey(destination)) {
790            return;
791        }
792        destList.get(destination).initialise();
793    }
794
795    /**
796     * {@inheritDoc }
797     */
798    @Override
799    public LinkedHashMap<Block, Integer> setupLayoutEditorTurnoutDetails(List<LayoutBlock> blks, SignalMast destination) {
800        if (disposing) {
801            return new LinkedHashMap<>();
802        }
803
804        if (!destList.containsKey(destination)) {
805            return new LinkedHashMap<>();
806        }
807        return destList.get(destination).setupLayoutEditorTurnoutDetails(blks);
808    }
809
810    /**
811     * {@inheritDoc }
812     */
813    @Override
814    public void setupLayoutEditorDetails() {
815        if (disposing) {
816            return;
817        }
818        Enumeration<SignalMast> en = destList.keys();
819        while (en.hasMoreElements()) {
820            try {
821                destList.get(en.nextElement()).setupLayoutEditorDetails();
822            } catch (JmriException e) {
823                //Considered normal if no route is valid on a Layout Editor panel
824            }
825        }
826    }
827
828    /**
829     * Check if routes to the destination Signal Mast are clear.
830     *
831     * @return true if the path to the next signal is clear
832     */
833    boolean checkStates() {
834        SignalMast oldActiveMast = destination;
835        if (destination != null) {
836            firePropertyChange(PROPERTY_STATE, oldActiveMast, null);
837            log.debug("Remove listener from destination");
838            destination.removePropertyChangeListener(propertyDestinationMastListener);
839            if (destList.containsKey(destination)) {
840                destList.get(destination).clearTurnoutLock();
841            }
842        }
843
844        Enumeration<SignalMast> en = destList.keys();
845        log.debug("checkStates enumerates over {} mast(s)", destList.size());
846        while (en.hasMoreElements()) {
847            SignalMast key = en.nextElement();
848            log.debug("  Destination mast {}", key.getDisplayName());
849            log.debug("    isEnabled: {}", (destList.get(key)).isEnabled());
850            log.debug("    isActive: {}", destList.get(key).isActive());
851
852            if ((destList.get(key)).isEnabled() && (destList.get(key).isActive())) {
853                destination = key;
854                log.debug("      Add listener to destination");
855                destination.addPropertyChangeListener(propertyDestinationMastListener);
856                log.debug("      firePropertyChange: \"state\"");
857                firePropertyChange(PROPERTY_STATE, oldActiveMast, destination);
858                destList.get(key).lockTurnouts();
859                return true;
860            }
861        }
862        return false;
863    }
864
865    /**
866     * {@inheritDoc }
867     */
868    @Override
869    public boolean areBlocksIncluded(List<Block> blks) {
870        Enumeration<SignalMast> en = destList.keys();
871        while (en.hasMoreElements()) {
872            SignalMast dm = en.nextElement();
873            boolean included;
874            for (int i = 0; i < blks.size(); i++) {
875                included = destList.get(dm).isBlockIncluded(blks.get(i));
876                if (included) {
877                    return true;
878                }
879                included = destList.get(dm).isAutoBlockIncluded(blks.get(i));
880                if (included) {
881                    return true;
882                }
883            }
884        }
885        return false;
886    }
887
888    /**
889     * {@inheritDoc }
890     */
891    @Override
892    public int getBlockState(Block block, SignalMast destination) {
893        if (!destList.containsKey(destination)) {
894            return -1;
895        }
896        return destList.get(destination).getBlockState(block);
897    }
898
899    /**
900     * {@inheritDoc }
901     */
902    @Override
903    public boolean isBlockIncluded(Block block, SignalMast destination) {
904        if (!destList.containsKey(destination)) {
905            return false;
906        }
907        return destList.get(destination).isBlockIncluded(block);
908    }
909
910    /**
911     * {@inheritDoc }
912     */
913    @Override
914    public boolean isTurnoutIncluded(Turnout turnout, SignalMast destination) {
915        if (!destList.containsKey(destination)) {
916            return false;
917        }
918        return destList.get(destination).isTurnoutIncluded(turnout);
919    }
920
921    /**
922     * {@inheritDoc }
923     */
924    @Override
925    public boolean isSensorIncluded(Sensor sensor, SignalMast destination) {
926        if (!destList.containsKey(destination)) {
927            return false;
928        }
929        return destList.get(destination).isSensorIncluded(sensor);
930    }
931
932    /**
933     * {@inheritDoc }
934     */
935    @Override
936    public boolean isSignalMastIncluded(SignalMast signal, SignalMast destination) {
937        if (!destList.containsKey(destination)) {
938            return false;
939        }
940        return destList.get(destination).isSignalMastIncluded(signal);
941    }
942
943    /**
944     * {@inheritDoc }
945     */
946    @Override
947    public int getAutoBlockState(Block block, SignalMast destination) {
948        if (!destList.containsKey(destination)) {
949            return -1;
950        }
951        return destList.get(destination).getAutoBlockState(block);
952    }
953
954    /**
955     * {@inheritDoc }
956     */
957    @Override
958    public int getSensorState(Sensor sensor, SignalMast destination) {
959        if (!destList.containsKey(destination)) {
960            return -1;
961        }
962        return destList.get(destination).getSensorState(sensor);
963    }
964
965    /**
966     * {@inheritDoc }
967     */
968    @Override
969    public int getTurnoutState(Turnout turnout, SignalMast destination) {
970        if (!destList.containsKey(destination)) {
971            return -1;
972        }
973        return destList.get(destination).getTurnoutState(turnout);
974    }
975
976    /**
977     * {@inheritDoc }
978     */
979    @Override
980    public int getAutoTurnoutState(Turnout turnout, SignalMast destination) {
981        if (!destList.containsKey(destination)) {
982            return -1;
983        }
984        return destList.get(destination).getAutoTurnoutState(turnout);
985    }
986
987    /**
988     * {@inheritDoc }
989     */
990    @Override
991    public String getSignalMastState(SignalMast mast, SignalMast destination) {
992        if (!destList.containsKey(destination)) {
993            return null;
994        }
995        return destList.get(destination).getSignalMastState(mast);
996    }
997
998    /**
999     * {@inheritDoc }
1000     */
1001    @Override
1002    public String getAutoSignalMastState(SignalMast mast, SignalMast destination) {
1003        if (!destList.containsKey(destination)) {
1004            return null;
1005        }
1006        return destList.get(destination).getAutoSignalMastState(mast);
1007    }
1008
1009    /**
1010     * {@inheritDoc }
1011     */
1012    @Override
1013    public float getMaximumSpeed(SignalMast destination) {
1014        if (!destList.containsKey(destination)) {
1015            return -1;
1016        }
1017        return destList.get(destination).getMinimumSpeed();
1018    }
1019
1020    volatile boolean inWait = false;
1021
1022    /**
1023     * Before going active or checking that we can go active, wait 500ms
1024     * for things to settle down to help prevent a race condition.
1025     */
1026    synchronized void setSignalAppearance() {
1027        log.debug("setMastAppearance (Aspect) called for {}", source.getDisplayName());
1028        if (inWait) {
1029            log.debug("setMastAppearance (Aspect) called with inWait set, returning");
1030            return;
1031        }
1032        inWait = true;
1033
1034        // The next line forces a single initialization of InstanceManager.getDefault(SignalSpeedMap.class)
1035        // before launching parallel threads
1036        InstanceManager.getDefault(SignalSpeedMap.class);
1037
1038        // The next line forces a single initialization of InstanceManager.getDefault(SignalMastLogicManager.class)
1039        // before launching delay
1040        int tempDelay = InstanceManager.getDefault(SignalMastLogicManager.class).getSignalLogicDelay() / 2;
1041        log.debug("SignalMastLogicManager started (delay)");
1042        ThreadingUtil.runOnLayoutDelayed(
1043                () -> {
1044                    setMastAppearance();
1045                    inWait = false;
1046                },
1047                tempDelay
1048        );
1049
1050    }
1051
1052    /**
1053     * Evaluate the destination signal mast Aspect and set ours accordingly.
1054     */
1055    void setMastAppearance() {
1056        log.debug("Set source Signal Mast Aspect");
1057        if (getSourceMast().getHeld()) {
1058            log.debug("Signal is at a Held state so will set to the aspect defined for Held or Danger");
1059
1060            String heldAspect = getSourceMast().getAppearanceMap().getSpecificAppearance(SignalAppearanceMap.HELD);
1061            if (heldAspect != null) {
1062                log.debug("  Setting to HELD value of {}", heldAspect);
1063                ThreadingUtil.runOnLayout(() -> {
1064                    getSourceMast().setAspect(heldAspect);
1065                });
1066            } else {
1067                String dangerAspect = getSourceMast().getAppearanceMap().getSpecificAppearance(SignalAppearanceMap.DANGER);
1068                log.debug("  Setting to DANGER value of {}", dangerAspect);
1069                ThreadingUtil.runOnLayout(() -> {
1070                    getSourceMast().setAspect(dangerAspect);
1071                });
1072            }
1073            return;
1074        }
1075        if (!checkStates()) {
1076            log.debug("Advanced routes not clear, set Stop aspect");
1077            getSourceMast().setAspect(stopAspect);
1078            return;
1079        }
1080        String[] advancedAspect;
1081        if (destination.getHeld()) {
1082            if (destination.getAppearanceMap().getSpecificAppearance(SignalAppearanceMap.HELD) != null) {
1083                advancedAspect = getSourceMast().getAppearanceMap().getValidAspectsForAdvancedAspect(destination.getAppearanceMap().getSpecificAppearance(SignalAppearanceMap.HELD));
1084            } else {
1085                advancedAspect = getSourceMast().getAppearanceMap().getValidAspectsForAdvancedAspect(destination.getAppearanceMap().getSpecificAppearance(SignalAppearanceMap.DANGER));
1086            }
1087        } else {
1088            advancedAspect = getSourceMast().getAppearanceMap().getValidAspectsForAdvancedAspect(destination.getAspect());
1089        }
1090
1091        log.debug("distant aspect is {}", destination.getAspect());
1092        log.debug("advanced aspect is {}", advancedAspect != null ? advancedAspect : "<null>");
1093
1094        if (advancedAspect != null) {
1095            String aspect = stopAspect;
1096            if (destList.get(destination).permissiveBlock) {
1097                if (!getSourceMast().isPermissiveSmlDisabled()) {
1098                    //if a block is in a permissive state then we set the permissive appearance
1099                    aspect = getSourceMast().getAppearanceMap().getSpecificAppearance(SignalAppearanceMap.PERMISSIVE);
1100                }
1101            } else {
1102                for (String advancedAspect1 : advancedAspect) {
1103                    if (!getSourceMast().isAspectDisabled(advancedAspect1)) {
1104                        aspect = advancedAspect1;
1105                        break;
1106                    }
1107                }
1108                List<Integer> divergAspects = new ArrayList<>();
1109                List<Integer> nonDivergAspects = new ArrayList<>();
1110                List<Integer> eitherAspects = new ArrayList<>();
1111                if (advancedAspect.length > 1) {
1112                    float maxSigSpeed = -1;
1113                    float maxPathSpeed = destList.get(destination).getMinimumSpeed();
1114                    boolean divergRoute = destList.get(destination).turnoutThrown;
1115
1116                    log.debug("Diverging route? {}", divergRoute);
1117                    boolean divergFlagsAvailable = false;
1118                    //We split the aspects into two lists, one with diverging flag set, the other without.
1119                    for (int i = 0; i < advancedAspect.length; i++) {
1120                        String div = null;
1121                        if (!getSourceMast().isAspectDisabled(advancedAspect[i])) {
1122                            div = (String) getSourceMast().getSignalSystem().getProperty(advancedAspect[i], "route");
1123                        }
1124                        if (div != null) {
1125                            if (div.equals("Diverging")) {
1126                                log.debug("Aspect {} added as Diverging Route", advancedAspect[i]);
1127                                divergAspects.add(i);
1128                                divergFlagsAvailable = true;
1129                                log.debug("Using Diverging Flag");
1130                            } else if (div.equals("Either")) {
1131                                log.debug("Aspect {} added as both Diverging and Normal Route", advancedAspect[i]);
1132                                nonDivergAspects.add(i);
1133                                divergAspects.add(i);
1134                                divergFlagsAvailable = true;
1135                                eitherAspects.add(i);
1136                                log.debug("Using Diverging Flag");
1137                            } else {
1138                                log.debug("Aspect {} added as Normal Route", advancedAspect[i]);
1139                                nonDivergAspects.add(i);
1140                                log.debug("Aspect {} added as Normal Route", advancedAspect[i]);
1141                            }
1142                        } else {
1143                            nonDivergAspects.add(i);
1144                            log.debug("Aspect {} added as Normal Route", advancedAspect[i]);
1145                        }
1146                    }
1147                    if ((eitherAspects.equals(divergAspects)) && (divergAspects.size() < nonDivergAspects.size())) {
1148                        //There are no unique diverging aspects
1149                        log.debug("'Either' aspects equals divergAspects and is less than non-diverging aspects");
1150                        divergFlagsAvailable = false;
1151                    }
1152                    log.debug("path max speed : {}", maxPathSpeed);
1153                    for (int i = 0; i < advancedAspect.length; i++) {
1154                        if (!getSourceMast().isAspectDisabled(advancedAspect[i])) {
1155                            String strSpeed = (String) getSourceMast().getSignalSystem().getProperty(advancedAspect[i], "speed");
1156                            log.debug("Aspect Speed = {} for aspect {}", strSpeed, advancedAspect[i]);
1157                            /*  if the diverg flags available is set and the diverg aspect
1158                             array contains the entry then we will check this aspect.
1159
1160                             If the diverg flag has not been set then we will check.
1161                             */
1162                            log.debug("advanced aspect {}",advancedAspect[i]);
1163                            if ((divergRoute && (divergFlagsAvailable) && (divergAspects.contains(i))) || ((divergRoute && !divergFlagsAvailable) || (!divergRoute)) && (nonDivergAspects.contains(i))) {
1164                                log.debug("In list");
1165                                if ((strSpeed != null) && (!strSpeed.isEmpty())) {
1166                                    float speed = 0.0f;
1167                                    try {
1168                                        speed = Float.parseFloat(strSpeed);
1169                                    } catch (NumberFormatException nx) {
1170                                        // not a number, perhaps a name?
1171                                        try {
1172                                            speed = InstanceManager.getDefault(SignalSpeedMap.class).getSpeed(strSpeed);
1173                                        } catch (IllegalArgumentException ex) {
1174                                            // not a name either
1175                                            log.warn("Using speed = 0.0 because could not understand \"{}\"", strSpeed);
1176                                        }
1177                                    }
1178                                    //Integer state = Integer.parseInt(strSpeed);
1179                                    /* This pics out either the highest speed signal if there
1180                                     * is no block speed specified or the highest speed signal
1181                                     * that is under the minimum block speed.
1182                                     */
1183                                    log.debug("{} signal state speed {} maxSigSpeed {} maxPathSpeed {}", destination.getDisplayName(), speed, maxSigSpeed, maxPathSpeed);
1184                                    if (maxPathSpeed == 0) {
1185                                        if (maxSigSpeed == -1) {
1186                                            log.debug("min speed on this route is equal to 0 so will set this as our max speed");
1187                                            maxSigSpeed = speed;
1188                                            aspect = advancedAspect[i];
1189                                            log.debug("Aspect to set is {}", aspect);
1190                                        } else if (speed > maxSigSpeed) {
1191                                            log.debug("new speed is faster than old will use this");
1192                                            maxSigSpeed = speed;
1193                                            aspect = advancedAspect[i];
1194                                            log.debug("Aspect to set is {}", aspect);
1195                                        }
1196                                    } else if ((speed > maxSigSpeed) && (maxSigSpeed < maxPathSpeed) && (speed <= maxPathSpeed)) {
1197                                        //Only set the speed to the lowest if the max speed is greater than the path speed
1198                                        //and the new speed is less than the last max speed
1199                                        log.debug("our minimum speed on this route is less than our state speed, we will set this as our max speed");
1200                                        maxSigSpeed = speed;
1201                                        aspect = advancedAspect[i];
1202                                        log.debug("Aspect to set is {}", aspect);
1203                                    } else if ((maxSigSpeed > maxPathSpeed) && (speed < maxSigSpeed)) {
1204                                        log.debug("our max signal speed is greater than our path speed on this route, our speed is less that the maxSigSpeed");
1205                                        maxSigSpeed = speed;
1206                                        aspect = advancedAspect[i];
1207                                        log.debug("Aspect to set is {}", aspect);
1208
1209                                    } else if (maxSigSpeed == -1) {
1210                                        log.debug("maxSigSpeed returned as -1");
1211                                        maxSigSpeed = speed;
1212                                        aspect = advancedAspect[i];
1213                                        log.debug("Aspect to set is {}", aspect);
1214                                    }
1215                                }
1216                            }
1217                        } else {
1218                            log.debug("Aspect has been disabled {}", advancedAspect[i]);
1219                        }
1220                    }
1221                }
1222            }
1223            if ((aspect != null) && (!aspect.isEmpty())) {
1224                log.debug("setMastAppearance setting aspect \"{}\"", aspect);
1225                String aspectSet = aspect; // for lambda
1226                try {
1227                    ThreadingUtil.runOnLayout(() -> {
1228                        getSourceMast().setAspect(aspectSet);
1229                    });
1230                } catch (Exception ex) {
1231                    log.error("Exception while setting Signal Logic", ex);
1232                }
1233                return;
1234            }
1235        }
1236        log.debug("Aspect returned is not valid, setting stop");
1237        ThreadingUtil.runOnLayout(() -> {
1238            getSourceMast().setAspect(stopAspect);
1239        });
1240    }
1241
1242    /**
1243     * {@inheritDoc }
1244     */
1245    @Override
1246    public void setConflictingLogic(SignalMast sm, LevelXing lx) {
1247        if (sm == null) {
1248            return;
1249        }
1250        log.debug("setConflicting logic mast {}", sm.getDisplayName());
1251        if (sm == source) {
1252            log.debug("source is us so exit");
1253            return;
1254        }
1255        Enumeration<SignalMast> en = destList.keys();
1256        while (en.hasMoreElements()) {
1257            SignalMast dm = en.nextElement();
1258            if (destList.get(dm).isBlockIncluded(lx.getLayoutBlockAC())) {
1259                destList.get(dm).addAutoSignalMast(sm);
1260            } else if (destList.get(dm).isBlockIncluded(lx.getLayoutBlockBD())) {
1261                destList.get(dm).addAutoSignalMast(sm);
1262            } else if (destList.get(dm).isAutoBlockIncluded(lx.getLayoutBlockAC())) {
1263                destList.get(dm).addAutoSignalMast(sm);
1264            } else if (destList.get(dm).isAutoBlockIncluded(lx.getLayoutBlockBD())) {
1265                destList.get(dm).addAutoSignalMast(sm);
1266            } else {
1267                log.debug("Block not found");
1268            }
1269        }
1270    }
1271
1272    /**
1273     * {@inheritDoc }
1274     */
1275    @Override
1276    public void removeConflictingLogic(SignalMast sm, LevelXing lx) {
1277        if (sm == source) {
1278            return;
1279        }
1280        Enumeration<SignalMast> en = destList.keys();
1281        while (en.hasMoreElements()) {
1282            SignalMast dm = en.nextElement();
1283            if (destList.get(dm).isBlockIncluded(lx.getLayoutBlockAC())) {
1284                destList.get(dm).removeAutoSignalMast(sm);
1285            } else if (destList.get(dm).isBlockIncluded(lx.getLayoutBlockBD())) {
1286                destList.get(dm).removeAutoSignalMast(sm);
1287            }
1288        }
1289    }
1290
1291    /**
1292     * Class to store SML properties for a destination mast paired with this
1293     * source mast.
1294     */
1295    private class DestinationMast {
1296
1297        LayoutBlock destinationBlock = null;
1298        LayoutBlock protectingBlock = null; //this is the block that the source signal is protecting
1299
1300        List<NamedBeanSetting> userSetTurnouts = new ArrayList<>(0);
1301        Hashtable<Turnout, Integer> autoTurnouts = new Hashtable<>(0);
1302        //Hashtable<Turnout, Boolean> turnoutThroats = new Hashtable<Turnout, Boolean>(0);
1303        //Hashtable<Turnout, Boolean> autoTurnoutThroats = new Hashtable<Turnout, Boolean>(0);
1304
1305        List<NamedBeanSetting> userSetMasts = new ArrayList<>(0);
1306        Hashtable<SignalMast, String> autoMasts = new Hashtable<>(0);
1307        List<NamedBeanSetting> userSetSensors = new ArrayList<>(0);
1308        List<NamedBeanSetting> userSetBlocks = new ArrayList<>(0);
1309        boolean turnoutThrown = false;
1310        boolean permissiveBlock = false;
1311        boolean disposed = false;
1312
1313        List<LevelXing> blockInXings = new ArrayList<>();
1314
1315        //autoBlocks are for those automatically generated by the system.
1316        LinkedHashMap<Block, Integer> autoBlocks = new LinkedHashMap<>(0);
1317
1318        List<Block> xingAutoBlocks = new ArrayList<>(0);
1319        List<Block> dblCrossoverAutoBlocks = new ArrayList<>(0);
1320        SignalMast destinationSignalMast;
1321        boolean active = false;
1322        boolean destMastInit = false;
1323
1324        float minimumBlockSpeed = 0.0f;
1325
1326        boolean useLayoutEditor = false;
1327        boolean useLayoutEditorTurnouts = false;
1328        boolean useLayoutEditorBlocks = false;
1329        boolean lockTurnouts = false;
1330
1331        NamedBeanHandle<Section> associatedSection = null;
1332
1333        DestinationMast(SignalMast destination) {
1334            this.destinationSignalMast = destination;
1335            if (destination.getAspect() == null) {
1336                try {
1337                    destination.setAspect(destination.getAppearanceMap().getSpecificAppearance(SignalAppearanceMap.DANGER));
1338                } catch (Exception ex) {
1339                    log.error("Error while creating Signal Logic", ex);
1340                }
1341            }
1342        }
1343
1344        void updateDestinationMast(SignalMast newMast) {
1345            destinationSignalMast = newMast;
1346            if (destinationSignalMast.getAspect() == null) {
1347                try {
1348                    destinationSignalMast.setAspect(destinationSignalMast.getAppearanceMap().getSpecificAppearance(SignalAppearanceMap.DANGER));
1349                } catch (Exception ex) {
1350                    log.error("Error while creating Signal Logic", ex);
1351                }
1352            }
1353        }
1354
1355        LayoutBlock getProtectingBlock() {
1356            return protectingBlock;
1357        }
1358
1359        String getDisplayName() {
1360            return destinationSignalMast.getDisplayName();
1361        }
1362
1363        String comment;
1364
1365        String getComment() {
1366            return comment;
1367        }
1368
1369        void setComment(String comment) {
1370            String old = this.comment;
1371            this.comment = comment;
1372            firePropertyChange(PROPERTY_COMMENT, old, comment);
1373        }
1374
1375        boolean isActive() {
1376            if (disposed) {
1377                log.error("checkState called even though this has been disposed of");
1378                return false;
1379            }
1380            return active;
1381        }
1382
1383        float getMinimumSpeed() {
1384            return minimumBlockSpeed;
1385        }
1386
1387        boolean enable = true;
1388
1389        void setEnabled() {
1390            enable = true;
1391            firePropertyChange(PROPERTY_ENABLED, false, this.destinationSignalMast);
1392        }
1393
1394        void setDisabled() {
1395            enable = false;
1396            firePropertyChange(PROPERTY_ENABLED, true, this.destinationSignalMast);
1397        }
1398
1399        boolean isEnabled() {
1400            return enable;
1401        }
1402
1403        int store = STOREALL;
1404
1405        void setStore(int store) {
1406            this.store = store;
1407        }
1408
1409        int getStoreState() {
1410            return store;
1411        }
1412
1413        void setAssociatedSection(Section section) {
1414            if (section != null && (!useLayoutEditor || !useLayoutEditorBlocks)) {
1415                log.warn("This Logic {} to {} is not using the Layout Editor or its Blocks, the associated Section will not be populated correctly", source.getDisplayName(), destinationSignalMast.getDisplayName());
1416            }
1417            if (section == null) {
1418                associatedSection = null;
1419                return;
1420            }
1421            associatedSection = InstanceManager.getDefault(NamedBeanHandleManager.class).getNamedBeanHandle(section.getDisplayName(), section);
1422            if (!autoBlocks.isEmpty()) { // associatedSection is guaranteed to exist
1423                createSectionDetails();
1424            }
1425        }
1426
1427        Section getAssociatedSection() {
1428            if (associatedSection != null) {
1429                return associatedSection.getBean();
1430            }
1431            return null;
1432        }
1433
1434        void createSectionDetails() {
1435            getAssociatedSection().removeAllBlocksFromSection();
1436            getAutoBlocksBetweenMasts().forEach(key -> {
1437                getAssociatedSection().addBlock(key);
1438            });
1439            String dir = Path.decodeDirection(getFacingBlock().getNeighbourDirection(getProtectingBlock()));
1440            EntryPoint ep = new EntryPoint(getProtectingBlock().getBlock(), getFacingBlock().getBlock(), dir);
1441            ep.setTypeForward();
1442            getAssociatedSection().addToForwardList(ep);
1443
1444            LayoutBlock proDestLBlock = InstanceManager.getDefault(LayoutBlockManager.class).getProtectedBlockByNamedBean(destinationSignalMast, destinationBlock.getMaxConnectedPanel());
1445            if (proDestLBlock != null) {
1446                log.debug("Add protecting Block {}", proDestLBlock.getDisplayName());
1447                dir = Path.decodeDirection(proDestLBlock.getNeighbourDirection(destinationBlock));
1448                ep = new EntryPoint(destinationBlock.getBlock(), proDestLBlock.getBlock(), dir);
1449                ep.setTypeReverse();
1450                getAssociatedSection().addToReverseList(ep);
1451            } else {
1452                log.debug(" ### Protecting Block not found ### ");
1453            }
1454        }
1455
1456        boolean isTurnoutLockAllowed() {
1457            return lockTurnouts;
1458        }
1459
1460        void allowTurnoutLock(boolean lock) {
1461            if (lockTurnouts == lock) {
1462                return;
1463            }
1464            if (!lock) {
1465                clearTurnoutLock();
1466            }
1467            lockTurnouts = lock;
1468        }
1469
1470        void setTurnouts(Hashtable<NamedBeanHandle<Turnout>, Integer> turnouts) {
1471            if (this.userSetTurnouts != null) {
1472                userSetTurnouts.forEach(nbh ->
1473                    nbh.getBean().removePropertyChangeListener(propertyTurnoutListener));
1474            }
1475            destMastInit = false;
1476            if (turnouts == null) {
1477                userSetTurnouts = new ArrayList<>(0);
1478            } else {
1479                userSetTurnouts = new ArrayList<>();
1480                Enumeration<NamedBeanHandle<Turnout>> e = turnouts.keys();
1481                while (e.hasMoreElements()) {
1482                    NamedBeanHandle<Turnout> nbh = e.nextElement();
1483                    NamedBeanSetting nbs = new NamedBeanSetting(nbh, turnouts.get(nbh));
1484                    userSetTurnouts.add(nbs);
1485                }
1486            }
1487            firePropertyChange(PROPERTY_TURNOUTS, null, this.destinationSignalMast);
1488        }
1489
1490        void setAutoTurnouts(Hashtable<Turnout, Integer> turnouts) {
1491            log.debug("{} called setAutoTurnouts with {}", destinationSignalMast.getDisplayName(),
1492                (turnouts != null ? "" + turnouts.size() + " turnouts in hash table" : "null hash table reference"));
1493            if (this.autoTurnouts != null) {
1494                Enumeration<Turnout> keys = this.autoTurnouts.keys();
1495                while (keys.hasMoreElements()) {
1496                    Turnout key = keys.nextElement();
1497                    key.removePropertyChangeListener(propertyTurnoutListener);
1498                }
1499                //minimumBlockSpeed = 0;
1500            }
1501            destMastInit = false;
1502            if (turnouts == null) {
1503                this.autoTurnouts = new Hashtable<>(0);
1504            } else {
1505                this.autoTurnouts = new Hashtable<>(turnouts);
1506            }
1507            firePropertyChange(PROPERTY_AUTO_TURNOUTS, null, this.destinationSignalMast);
1508        }
1509
1510        void setBlocks(Hashtable<Block, Integer> blocks) {
1511            log.debug("{} Set blocks called", destinationSignalMast.getDisplayName());
1512            if (this.userSetBlocks != null) {
1513                userSetBlocks.forEach( nbh ->
1514                    nbh.getBean().removePropertyChangeListener(propertyBlockListener));
1515            }
1516            destMastInit = false;
1517
1518            userSetBlocks = new ArrayList<>(0);
1519            if (blocks != null) {
1520                userSetBlocks = new ArrayList<>();
1521                Enumeration<Block> e = blocks.keys();
1522                while (e.hasMoreElements()) {
1523                    Block blk = e.nextElement();
1524                    NamedBeanHandle<?> nbh = InstanceManager.getDefault(NamedBeanHandleManager.class).getNamedBeanHandle(blk.getDisplayName(), blk);
1525                    NamedBeanSetting nbs = new NamedBeanSetting(nbh, blocks.get(blk));
1526                    userSetBlocks.add(nbs);
1527                }
1528            }
1529            firePropertyChange(PROPERTY_BLOCKS, null, this.destinationSignalMast);
1530        }
1531
1532        public void setAutoBlocks(LinkedHashMap<Block, Integer> blocks) {
1533            if (log.isDebugEnabled()) {
1534                log.debug("{} called setAutoBlocks with {}", destinationSignalMast.getDisplayName(),
1535                    (blocks != null ? "" + blocks.size() + " blocks in hash table" : "null hash table reference"));
1536            }
1537            if (this.autoBlocks != null) {
1538                autoBlocks.keySet().forEach( key ->
1539                    key.removePropertyChangeListener(propertyBlockListener));
1540            }
1541            destMastInit = false;
1542            if (blocks == null) {
1543                this.autoBlocks = new LinkedHashMap<>(0);
1544
1545            } else {
1546                this.autoBlocks = new LinkedHashMap<>(blocks);
1547                //We shall remove the facing block in the list.
1548                if (facingBlock != null && autoBlocks.containsKey(facingBlock.getBlock())) {
1549                    autoBlocks.remove(facingBlock.getBlock());
1550                }
1551                if (getAssociatedSection() != null) {
1552                    createSectionDetails();
1553                }
1554            }
1555            firePropertyChange(PROPERTY_AUTO_BLOCKS, null, this.destinationSignalMast);
1556        }
1557
1558        void setMasts(Hashtable<SignalMast, String> masts) {
1559            if (this.userSetMasts != null) {
1560                userSetMasts.forEach( nbh ->
1561                    nbh.getBean().removePropertyChangeListener(propertySignalMastListener));
1562            }
1563
1564            destMastInit = false;
1565
1566            if (masts == null) {
1567                userSetMasts = new ArrayList<>(0);
1568            } else {
1569                userSetMasts = new ArrayList<>();
1570                Enumeration<SignalMast> e = masts.keys();
1571                while (e.hasMoreElements()) {
1572                    SignalMast mast = e.nextElement();
1573                    NamedBeanHandle<?> nbh = InstanceManager.getDefault(NamedBeanHandleManager.class)
1574                        .getNamedBeanHandle(mast.getDisplayName(), mast);
1575                    NamedBeanSetting nbs = new NamedBeanSetting(nbh, masts.get(mast));
1576                    userSetMasts.add(nbs);
1577                }
1578            }
1579            firePropertyChange(PROPERTY_MASTS, null, this.destinationSignalMast);
1580        }
1581
1582        /**
1583         *
1584         * @param newAutoMasts Hashtable of signal masts and set to Aspects
1585         * @param overwrite    When true, replace an existing autoMasts list in
1586         *                     the SML
1587         */
1588        void setAutoMasts(Hashtable<SignalMast, String> newAutoMasts, boolean overwrite) {
1589            log.debug("{} setAutoMast Called", destinationSignalMast.getDisplayName());
1590            if (this.autoMasts != null) {
1591                Enumeration<SignalMast> keys = this.autoMasts.keys();
1592                while (keys.hasMoreElements()) {
1593                    SignalMast key = keys.nextElement();
1594                    key.removePropertyChangeListener(propertySignalMastListener);
1595                }
1596                //minimumBlockSpeed = 0;
1597            }
1598            destMastInit = false;
1599            if (overwrite) {
1600                if (newAutoMasts == null) {
1601                    this.autoMasts = new Hashtable<>(0);
1602                } else {
1603                    this.autoMasts = new Hashtable<>(newAutoMasts);
1604                }
1605            } else {
1606                if (newAutoMasts == null) {
1607                    this.autoMasts = new Hashtable<>(0);
1608                } else {
1609                    Enumeration<SignalMast> keys = newAutoMasts.keys();
1610                    while (keys.hasMoreElements()) {
1611                        SignalMast key = keys.nextElement();
1612                        this.autoMasts.put(key, newAutoMasts.get(key));
1613                    }
1614                }
1615            }
1616            //kick off the process to add back in signal masts at crossings.
1617            for (int i = 0; i < blockInXings.size(); i++) {
1618                blockInXings.get(i).addSignalMastLogic(source);
1619            }
1620
1621            firePropertyChange(PROPERTY_AUTO_MASTS, null, this.destinationSignalMast);
1622        }
1623
1624        void setSensors(Hashtable<NamedBeanHandle<Sensor>, Integer> sensors) {
1625            if (this.userSetSensors != null) {
1626                userSetSensors.forEach(nbh ->
1627                    nbh.getBean().removePropertyChangeListener(propertySensorListener));
1628            }
1629            destMastInit = false;
1630
1631            if (sensors == null) {
1632                userSetSensors = new ArrayList<>(0);
1633            } else {
1634                userSetSensors = new ArrayList<>();
1635                Enumeration<NamedBeanHandle<Sensor>> e = sensors.keys();
1636                while (e.hasMoreElements()) {
1637                    NamedBeanHandle<Sensor> nbh = e.nextElement();
1638                    NamedBeanSetting nbs = new NamedBeanSetting(nbh, sensors.get(nbh));
1639                    userSetSensors.add(nbs);
1640                }
1641            }
1642            firePropertyChange(PROPERTY_SENSORS, null, this.destinationSignalMast);
1643        }
1644
1645        void addSensor(NamedBeanHandle<Sensor> sen, int state) {
1646            for (NamedBeanSetting nbh : userSetSensors) {
1647                if (nbh.getBean().equals(sen.getBean())) {
1648                    return;
1649                }
1650            }
1651            sen.getBean().addPropertyChangeListener(propertySensorListener);
1652            NamedBeanSetting nbs = new NamedBeanSetting(sen, state);
1653            userSetSensors.add(nbs);
1654            firePropertyChange(PROPERTY_SENSORS, null, this.destinationSignalMast);
1655        }
1656
1657// not used now, preserved for later use
1658//         void removeSensor(NamedBeanHandle<Sensor> sen) {
1659//             for (NamedBeanSetting nbh : userSetSensors) {
1660//                 if (nbh.getBean().equals(sen.getBean())) {
1661//                     sen.getBean().removePropertyChangeListener(propertySensorListener);
1662//                     userSetSensors.remove(nbh);
1663//                     firePropertyChange("sensors", null, this.destination);
1664//                     return;
1665//                 }
1666//             }
1667//         }
1668
1669        void removeSensor(Sensor sen) {
1670            for (NamedBeanSetting nbh : userSetSensors) {
1671                if (nbh.getBean().equals(sen)) {
1672                    sen.removePropertyChangeListener(propertySensorListener);
1673                    userSetSensors.remove(nbh);
1674                    firePropertyChange(PROPERTY_SENSORS, null, this.destinationSignalMast);
1675                    return;
1676                }
1677            }
1678        }
1679
1680        List<Block> getBlocks() {
1681            List<Block> out = new ArrayList<>();
1682            userSetBlocks.forEach( nbh -> out.add((Block) nbh.getBean()));
1683            return out;
1684        }
1685
1686        List<Block> getAutoBlocks() {
1687            List<Block> out = new ArrayList<>();
1688            Set<Block> blockKeys = autoBlocks.keySet();
1689            blockKeys.forEach(key -> out.add(key));
1690            return out;
1691        }
1692
1693        List<Block> getAutoBlocksBetweenMasts() {
1694            if (destList.get(destinationSignalMast).xingAutoBlocks.isEmpty()
1695                    && destList.get(destinationSignalMast).dblCrossoverAutoBlocks.isEmpty()) {
1696                return getAutoBlocks();
1697            }
1698            List<Block> returnList = getAutoBlocks();
1699            for (Block blk : getAutoBlocks()) {
1700                if (xingAutoBlocks.contains(blk)) {
1701                    returnList.remove(blk);
1702                }
1703                if (dblCrossoverAutoBlocks.contains(blk)) {
1704                    returnList.remove(blk);
1705                }
1706            }
1707
1708            return returnList;
1709        }
1710
1711        List<Turnout> getTurnouts() {
1712            List<Turnout> out = new ArrayList<>();
1713            userSetTurnouts.forEach( nbh -> out.add((Turnout) nbh.getBean()));
1714            return out;
1715        }
1716
1717        void removeTurnout(Turnout turn) {
1718            Iterator<NamedBeanSetting> nbh = userSetTurnouts.iterator();
1719            while (nbh.hasNext()) {
1720                NamedBeanSetting i = nbh.next();
1721                if (i.getBean().equals(turn)) {
1722                    turn.removePropertyChangeListener(propertyTurnoutListener);
1723                    nbh.remove();
1724                    firePropertyChange(PROPERTY_TURNOUTS, null, this.destinationSignalMast);
1725                }
1726            }
1727        }
1728
1729        @SuppressWarnings("unchecked") // (NamedBeanHandle<Turnout>) nbh.getNamedBean() is unchecked cast
1730        List<NamedBeanHandle<Turnout>> getNamedTurnouts() {
1731            List<NamedBeanHandle<Turnout>> out = new ArrayList<>();
1732            userSetTurnouts.forEach(nbh ->
1733                out.add((NamedBeanHandle<Turnout>) nbh.getNamedBean()));
1734            return out;
1735        }
1736
1737        List<Turnout> getAutoTurnouts() {
1738            List<Turnout> out = new ArrayList<>();
1739            Enumeration<Turnout> en = autoTurnouts.keys();
1740            while (en.hasMoreElements()) {
1741                out.add(en.nextElement());
1742            }
1743            return out;
1744        }
1745
1746        List<SignalMast> getSignalMasts() {
1747            List<SignalMast> out = new ArrayList<>();
1748            userSetMasts.forEach( nbh -> out.add((SignalMast) nbh.getBean()));
1749            return out;
1750        }
1751
1752        List<SignalMast> getAutoSignalMasts() {
1753            List<SignalMast> out = new ArrayList<>();
1754            Enumeration<SignalMast> en = autoMasts.keys();
1755            while (en.hasMoreElements()) {
1756                out.add(en.nextElement());
1757            }
1758            return out;
1759        }
1760
1761        List<Sensor> getSensors() {
1762            List<Sensor> out = new ArrayList<>();
1763            userSetSensors.forEach( nbh -> out.add((Sensor) nbh.getBean()));
1764            return out;
1765        }
1766
1767        @SuppressWarnings("unchecked") // (NamedBeanHandle<Sensor>) nbh.getNamedBean() is unchecked cast
1768        List<NamedBeanHandle<Sensor>> getNamedSensors() {
1769            List<NamedBeanHandle<Sensor>> out = new ArrayList<>();
1770            userSetSensors.forEach( nbh -> out.add((NamedBeanHandle<Sensor>) nbh.getNamedBean()));
1771            return out;
1772        }
1773
1774        boolean isBlockIncluded(Block block) {
1775            return userSetBlocks.stream().anyMatch(nbh -> (nbh.getBean().equals(block)));
1776        }
1777
1778        boolean isAutoBlockIncluded(LayoutBlock block) {
1779            if (block != null) {
1780                return autoBlocks.containsKey(block.getBlock());
1781            }
1782            return false;
1783        }
1784
1785        boolean isAutoBlockIncluded(Block block) {
1786            return autoBlocks.containsKey(block);
1787        }
1788
1789        boolean isBlockIncluded(LayoutBlock block) {
1790            return userSetBlocks.stream().anyMatch(nbh -> (nbh.getBean().equals(block.getBlock())));
1791        }
1792
1793        boolean isTurnoutIncluded(Turnout turnout) {
1794            return userSetTurnouts.stream().anyMatch(nbh -> (nbh.getBean().equals(turnout)));
1795        }
1796
1797        boolean isSensorIncluded(Sensor sensor) {
1798            return userSetSensors.stream().anyMatch(nbh -> (nbh.getBean().equals(sensor)));
1799        }
1800
1801        boolean isSignalMastIncluded(SignalMast signal) {
1802            return userSetMasts.stream().anyMatch(nbh -> (nbh.getBean().equals(signal)));
1803        }
1804
1805        int getAutoBlockState(Block block) {
1806            if (autoBlocks == null) {
1807                return -1;
1808            }
1809            return autoBlocks.get(block);
1810        }
1811
1812        int getBlockState(Block block) {
1813            if (userSetBlocks == null) {
1814                return -1;
1815            }
1816            for (NamedBeanSetting nbh : userSetBlocks) {
1817                if (nbh.getBean().equals(block)) {
1818                    return nbh.getSetting();
1819                }
1820            }
1821            return -1;
1822        }
1823
1824        int getSensorState(Sensor sensor) {
1825            if (userSetSensors == null) {
1826                return -1;
1827            }
1828            for (NamedBeanSetting nbh : userSetSensors) {
1829                if (nbh.getBean().equals(sensor)) {
1830                    return nbh.getSetting();
1831                }
1832            }
1833            return -1;
1834        }
1835
1836        int getTurnoutState(Turnout turnout) {
1837            if (userSetTurnouts == null) {
1838                return -1;
1839            }
1840            for (NamedBeanSetting nbh : userSetTurnouts) {
1841                if (nbh.getBean().equals(turnout)) {
1842                    return nbh.getSetting();
1843                }
1844            }
1845            return -1;
1846        }
1847
1848        int getAutoTurnoutState(Turnout turnout) {
1849            if (autoTurnouts == null) {
1850                return -1;
1851            }
1852            if (autoTurnouts.containsKey(turnout)) {
1853                return autoTurnouts.get(turnout);
1854            }
1855            return -1;
1856        }
1857
1858        String getSignalMastState(SignalMast mast) {
1859            if (userSetMasts == null) {
1860                return null;
1861            }
1862            for (NamedBeanSetting nbh : userSetMasts) {
1863                if (nbh.getBean().equals(mast)) {
1864                    return nbh.getStringSetting();
1865                }
1866            }
1867            return null;
1868        }
1869
1870        String getAutoSignalMastState(SignalMast mast) {
1871            if (autoMasts == null) {
1872                return null;
1873            }
1874            return autoMasts.get(mast);
1875        }
1876
1877        // the following 2 methods are not supplied in the implementation
1878        boolean inWait = false;
1879
1880        /*
1881         * Before going active or checking that we can go active, wait
1882         * for things to settle down to help prevent a race condition.
1883         */
1884        void checkState() {
1885            if (disposed) {
1886                log.error("checkState called even though this has been disposed of {}",
1887                    getSourceMast().getDisplayName());
1888                return;
1889            }
1890
1891            if (!enable) {
1892                return;
1893            }
1894            if (inWait) {
1895                return;
1896            }
1897
1898            log.debug("check Signal Dest State called");
1899            inWait = true;
1900
1901            // The next line forces a single initialization of InstanceManager.getDefault(SignalMastLogicManager.class)
1902            // before launching parallel threads
1903            int tempDelay = InstanceManager.getDefault(SignalMastLogicManager.class).getSignalLogicDelay();
1904
1905            ThreadingUtil.runOnLayoutDelayed(
1906                    () -> {
1907                        checkStateDetails();
1908                        inWait = false;
1909                    }, tempDelay
1910            );
1911        }
1912
1913        /**
1914         * Check the details of this source-destination signal mast logic pair.
1915         * Steps through every sensor, turnout etc. before setting the SML
1916         * Aspect on the source mast via {
1917         *
1918         * @see #setSignalAppearance } and {
1919         * @see #setMastAppearance }
1920         */
1921        private void checkStateDetails() {
1922            turnoutThrown = false;
1923            permissiveBlock = false;
1924            if (disposed) {
1925                log.error("checkStateDetails called even though this has been disposed of {} {}",
1926                    getSourceMast().getDisplayName(), destinationSignalMast.getDisplayName());
1927                return;
1928            }
1929            if (!enable) {
1930                return;
1931            }
1932            log.debug("From {} to {} internal check state", getSourceMast().getDisplayName(),
1933                destinationSignalMast.getDisplayName());
1934            active = false;
1935            if ((useLayoutEditor) && (autoTurnouts.isEmpty()) && (autoBlocks.isEmpty())) {
1936                return;
1937            }
1938            boolean state = true;
1939            Enumeration<Turnout> keys = autoTurnouts.keys();
1940            while (keys.hasMoreElements()) {
1941                Turnout key = keys.nextElement();
1942                if (key.getKnownState() != autoTurnouts.get(key)) {
1943                    if (key.getState() != autoTurnouts.get(key)) {
1944                        if (isTurnoutIncluded(key)) {
1945                            if (key.getState() != getTurnoutState(key)) {
1946                                state = false;
1947                            } else if (key.getState() == Turnout.THROWN) {
1948                                turnoutThrown = true;
1949                            }
1950                        } else {
1951                            state = false;
1952                        }
1953                    }
1954                } else if (key.getState() == Turnout.THROWN) {
1955                    turnoutThrown = true;
1956                }
1957            }
1958
1959            for (NamedBeanSetting nbh : userSetTurnouts) {
1960                Turnout key = (Turnout) nbh.getBean();
1961                if (key.getKnownState() != nbh.getSetting()) {
1962                    state = false;
1963                } else if (key.getState() == Turnout.THROWN) {
1964                    turnoutThrown = true;
1965                }
1966            }
1967
1968            Enumeration<SignalMast> mastKeys = autoMasts.keys();
1969            while (mastKeys.hasMoreElements()) {
1970                SignalMast key = mastKeys.nextElement();
1971                String aspect = key.getAspect();
1972                log.debug("key {} {} {}", key.getDisplayName(), aspect, autoMasts.get(key));
1973                if ((aspect != null) && (!aspect.equals(autoMasts.get(key)))) {
1974                    if (isSignalMastIncluded(key)) {
1975                        //Basically if we have a blank aspect, we don't care about the state of the signalmast
1976                        if (!getSignalMastState(key).isEmpty()) {
1977                            if (!aspect.equals(getSignalMastState(key))) {
1978                                state = false;
1979                            }
1980                        }
1981                    } else {
1982                        state = false;
1983                    }
1984                }
1985            }
1986            for (NamedBeanSetting nbh : userSetMasts) {
1987                SignalMast key = (SignalMast) nbh.getBean();
1988                String aspect = key.getAspect();
1989                if ((aspect == null) || (!aspect.equals(nbh.getStringSetting()))) {
1990                    state = false;
1991                }
1992            }
1993
1994            for (NamedBeanSetting nbh : userSetSensors) {
1995                Sensor key = (Sensor) nbh.getBean();
1996                if (key.getKnownState() != nbh.getSetting()) {
1997                    state = false;
1998                }
1999            }
2000
2001            for (Map.Entry<Block, Integer> entry : this.autoBlocks.entrySet()) {
2002                log.debug(" entry {} {} {}", entry.getKey().getDisplayName(),
2003                    entry.getKey().getState(), entry.getValue());
2004                if (entry.getKey().getState() != autoBlocks.get(entry.getKey())) {
2005                    if (isBlockIncluded(entry.getKey())) {
2006                        if (getBlockState(entry.getKey()) != 0x03) {
2007                            if (entry.getKey().getState() != getBlockState(entry.getKey())) {
2008                                if (entry.getKey().getState() == Block.OCCUPIED && entry.getKey().getPermissiveWorking()) {
2009                                    permissiveBlock = true;
2010                                } else {
2011                                    state = false;
2012                                }
2013                            }
2014                        }
2015                    } else {
2016                        if (entry.getKey().getState() == Block.OCCUPIED && entry.getKey().getPermissiveWorking()) {
2017                            permissiveBlock = true;
2018                        } else if (entry.getKey().getState() == Block.UNDETECTED) {
2019                            log.debug("Block {} is UNDETECTED so treat as unoccupied", entry.getKey().getDisplayName());
2020                        } else {
2021                            state = false;
2022                        }
2023                    }
2024                }
2025            }
2026
2027            for (NamedBeanSetting nbh : userSetBlocks) {
2028                Block key = (Block) nbh.getBean();
2029                if (nbh.getSetting() != 0x03) {
2030                    if (key.getState() != nbh.getSetting()) {
2031                        if (key.getState() == Block.OCCUPIED && key.getPermissiveWorking()) {
2032                            permissiveBlock = true;
2033                        } else {
2034                            state = false;
2035                        }
2036                    }
2037                }
2038            }
2039            if (permissiveBlock
2040                /*If a block has been found to be permissive, but the source signalmast
2041                 does not support a call-on/permissive aspect then the route can not be set*/
2042                && getSourceMast().getAppearanceMap().getSpecificAppearance(SignalAppearanceMap.PERMISSIVE) == null) {
2043                    state = false;
2044            }
2045
2046            /*This check is purely for use with the dispatcher, it will check to see if any of the blocks are set to "useExtraColor"
2047             which is a means to determine if the block is in a section that is occupied and it not ours thus we can set the signal to danger.*/
2048            if (state && getAssociatedSection() != null
2049                    && InstanceManager.isInitialized(jmri.jmrit.dispatcher.DispatcherFrame.class)
2050                    && InstanceManager.getNullableDefault(LayoutBlockManager.class) != null
2051                    && getAssociatedSection().getState() != Section.FORWARD) {
2052
2053                LayoutBlockManager lbm = InstanceManager.getDefault(LayoutBlockManager.class);
2054                for (Block key : autoBlocks.keySet()) {
2055                    LayoutBlock lb = lbm.getLayoutBlock(key);
2056                    if (lb != null && lb.getUseExtraColor()) {
2057                        state = false;
2058                        break;
2059                    }
2060                }
2061                if (!state) {
2062                    for (NamedBeanSetting nbh : userSetBlocks) {
2063                        Block key = (Block) nbh.getBean();
2064                        LayoutBlock lb = lbm.getLayoutBlock(key);
2065                        if (lb != null && lb.getUseExtraColor()) {
2066                            state = false;
2067                            break;
2068                        }
2069                    }
2070                }
2071            }
2072
2073            if (!state) {
2074                turnoutThrown = false;
2075                permissiveBlock = false;
2076            }
2077
2078            active = state;
2079            ThreadingUtil.runOnLayout(() -> {
2080                setSignalAppearance();
2081            });
2082        }
2083
2084        /**
2085         * Set up this source-destination signal mast logic pair. Steps through
2086         * every list defined on the source mast.
2087         */
2088        void initialise() {
2089            if ((destMastInit) || (disposed)) {
2090                return;
2091            }
2092
2093            active = false;
2094            turnoutThrown = false;
2095            permissiveBlock = false;
2096            boolean routeclear = true;
2097            if ((useLayoutEditor) && (autoTurnouts.isEmpty()) && (autoBlocks.isEmpty()) && (autoMasts.isEmpty())) {
2098                return;
2099            }
2100
2101            calculateSpeed();
2102
2103            Enumeration<Turnout> keys = autoTurnouts.keys();
2104            while (keys.hasMoreElements()) {
2105                Turnout key = keys.nextElement();
2106                key.addPropertyChangeListener(propertyTurnoutListener);
2107
2108                if (key.getKnownState() != autoTurnouts.get(key)) {
2109                    if (key.getState() != autoTurnouts.get(key)) {
2110                        if (isTurnoutIncluded(key)) {
2111                            if (key.getState() != getTurnoutState(key)) {
2112                                routeclear = false;
2113                            } else if (key.getState() == Turnout.THROWN) {
2114                                turnoutThrown = true;
2115                            }
2116                        } else {
2117                            routeclear = false;
2118                        }
2119                    }
2120                } else if (key.getState() == Turnout.THROWN) {
2121                    turnoutThrown = true;
2122                }
2123            }
2124
2125            for (NamedBeanSetting nbh : userSetTurnouts) {
2126                Turnout key = (Turnout) nbh.getBean();
2127                key.addPropertyChangeListener(propertyTurnoutListener, nbh.getBeanName(),
2128                    "Signal Mast Logic:" + source.getDisplayName() + " to " + destinationSignalMast.getDisplayName());
2129                if (key.getKnownState() != nbh.getSetting()) {
2130                    routeclear = false;
2131                } else if (key.getState() == Turnout.THROWN) {
2132                    turnoutThrown = true;
2133                }
2134            }
2135
2136            Enumeration<SignalMast> mastKeys = autoMasts.keys();
2137            while (mastKeys.hasMoreElements()) {
2138                SignalMast key = mastKeys.nextElement();
2139                log.debug("{} auto mast add list {}", destinationSignalMast.getDisplayName(), key.getDisplayName());
2140                key.addPropertyChangeListener(propertySignalMastListener);
2141                String aspect = key.getAspect();
2142                if ( aspect != null && !aspect.equals(autoMasts.get(key))) {
2143                    if (isSignalMastIncluded(key)) {
2144                        if (aspect.equals(getSignalMastState(key))) {
2145                            routeclear = false;
2146                        }
2147                    } else {
2148                        routeclear = false;
2149                    }
2150                }
2151            }
2152
2153            for (NamedBeanSetting nbh : userSetMasts) {
2154                SignalMast key = (SignalMast) nbh.getBean();
2155                key.addPropertyChangeListener(propertySignalMastListener);
2156                String aspect = key.getAspect();
2157                log.debug("mast '{}' key aspect '{}'", destinationSignalMast.getDisplayName(), aspect);
2158                if ((aspect == null) || (!aspect.equals(nbh.getStringSetting()))) {
2159                    routeclear = false;
2160                }
2161            }
2162            for (NamedBeanSetting nbh : userSetSensors) {
2163                Sensor sensor = (Sensor) nbh.getBean();
2164                sensor.addPropertyChangeListener(propertySensorListener, nbh.getBeanName(),
2165                    "Signal Mast Logic:" + source.getDisplayName() + " to " + destinationSignalMast.getDisplayName());
2166                if (sensor.getKnownState() != nbh.getSetting()) {
2167                    routeclear = false;
2168                }
2169            }
2170
2171            for (Map.Entry<Block, Integer> entry : this.autoBlocks.entrySet()) {
2172                log.debug("{} auto block add list {}", destinationSignalMast.getDisplayName(),
2173                    entry.getKey().getDisplayName());
2174                entry.getKey().addPropertyChangeListener(propertyBlockListener);
2175                if (entry.getKey().getState() != entry.getValue()) {
2176                    if (isBlockIncluded(entry.getKey())) {
2177                        if (entry.getKey().getState() != getBlockState(entry.getKey())) {
2178                            if (entry.getKey().getState() == Block.OCCUPIED && entry.getKey().getPermissiveWorking()) {
2179                                permissiveBlock = true;
2180                            } else {
2181                                routeclear = false;
2182                            }
2183                        }
2184                    } else {
2185                        if (entry.getKey().getState() == Block.OCCUPIED && entry.getKey().getPermissiveWorking()) {
2186                            permissiveBlock = true;
2187                        } else if (entry.getKey().getState() == Block.UNDETECTED) {
2188                            log.debug("Block {} is UNDETECTED so treat as unoccupied", entry.getKey().getDisplayName());
2189                        } else {
2190                            routeclear = false;
2191                        }
2192                    }
2193                }
2194            }
2195
2196            for (NamedBeanSetting nbh : userSetBlocks) {
2197                Block key = (Block) nbh.getBean();
2198                key.addPropertyChangeListener(propertyBlockListener);
2199                if (key.getState() != getBlockState(key)) {
2200                    if (key.getState() == Block.OCCUPIED && key.getPermissiveWorking()) {
2201                        permissiveBlock = true;
2202                    } else {
2203                        routeclear = false;
2204                    }
2205                }
2206            }
2207            if ( permissiveBlock
2208                /* If a block has been found to be permissive, but the source signalmast
2209                 does not support a call-on/permissive aspect then the route can not be set */
2210                && getSourceMast().getAppearanceMap().getSpecificAppearance(SignalAppearanceMap.PERMISSIVE) == null) {
2211                    routeclear = false;
2212            }
2213            if (routeclear) {
2214                active = true;
2215                setSignalAppearance();
2216            } else {
2217                permissiveBlock = false;
2218                turnoutThrown = false;
2219            }
2220            destMastInit = true;
2221        }
2222
2223        void useLayoutEditor(boolean boo) throws JmriException {
2224            log.debug("{} called useLayoutEditor({}), is {}",
2225                destinationSignalMast.getDisplayName(), boo, useLayoutEditor);
2226            if (useLayoutEditor == boo) {
2227                return;
2228            }
2229            useLayoutEditor = boo;
2230            if ((boo) && (InstanceManager.getDefault(LayoutBlockManager.class).routingStablised())) {
2231                // JmriException considered normal if there is no valid path using the layout editor.
2232                setupLayoutEditorDetails();
2233            } else {
2234                destinationBlock = null;
2235                facingBlock = null;
2236                protectingBlock = null;
2237                setAutoBlocks(null);
2238                setAutoTurnouts(null);
2239            }
2240        }
2241
2242        void useLayoutEditorDetails(boolean turnouts, boolean blocks) throws JmriException {
2243            log.debug("{} use layout editor details called {}",
2244                destinationSignalMast.getDisplayName(), useLayoutEditor);
2245            useLayoutEditorTurnouts = turnouts;
2246            useLayoutEditorBlocks = blocks;
2247            if ((useLayoutEditor) && (InstanceManager.getDefault(LayoutBlockManager.class).routingStablised())) {
2248                // JmriException considered normal if there is no valid path using the Layout Editor.
2249                setupLayoutEditorDetails();
2250            }
2251        }
2252
2253        void setupLayoutEditorDetails() throws JmriException {
2254            log.debug("setupLayoutEditorDetails: useLayoutEditor={} disposed={}", useLayoutEditor, disposed);
2255            if ((!useLayoutEditor) || (disposed)) {
2256                return;
2257            }
2258            LayoutBlockManager lbm = InstanceManager.getDefault(LayoutBlockManager.class);
2259            if ( destinationBlock != null) {
2260                log.debug("{} Set use layout editor", destinationSignalMast.getDisplayName());
2261            }
2262            Set<LayoutEditor> layout = InstanceManager.getDefault(EditorManager.class).getAll(LayoutEditor.class);
2263            List<LayoutBlock> protectingBlocks = new ArrayList<>();
2264            // We don't care which Layout Editor panel the signal mast is on, just so long as
2265            // the routing is done via layout blocks.
2266            remoteProtectingBlock = null;
2267            for (int i = 0; i < layout.size(); i++) {
2268                log.debug("{} Layout name {}", destinationSignalMast.getDisplayName(), editor );
2269                if (facingBlock == null) {
2270                    facingBlock = lbm.getFacingBlockByNamedBean(getSourceMast(), editor);
2271                }
2272                if (protectingBlock == null && protectingBlocks.isEmpty()) {
2273                    //This is wrong
2274                    protectingBlocks = lbm.getProtectingBlocksByNamedBean(getSourceMast(), editor);
2275                }
2276                if (destinationBlock == null) {
2277                    destinationBlock = lbm.getFacingBlockByNamedBean(destinationSignalMast, editor);
2278                }
2279                if (remoteProtectingBlock == null) {
2280                    remoteProtectingBlock = lbm.getProtectedBlockByNamedBean(destinationSignalMast, editor);
2281                }
2282            }
2283            // At this point, if we are not using the Layout Editor turnout or block
2284            // details then there is no point in trying to gather them.
2285            if ((!useLayoutEditorTurnouts) && (!useLayoutEditorBlocks)) {
2286                return;
2287            }
2288            if (facingBlock == null) {
2289                log.error("No facing block found for source mast {}", getSourceMast().getDisplayName());
2290                throw new JmriException("No facing block found for source mast " + getSourceMast().getDisplayName());
2291            }
2292            if (destinationBlock == null) {
2293                log.error("No facing block found for destination mast {}", destinationSignalMast.getDisplayName());
2294                throw new JmriException("No facing block found for destination mast " + destinationSignalMast.getDisplayName());
2295            }
2296            List<LayoutBlock> lblks = new ArrayList<>();
2297            if (protectingBlock == null) {
2298                log.debug("protecting block is null");
2299                String pBlkNames = "";
2300                StringBuffer lBlksNamesBuf = new StringBuffer();
2301                for (LayoutBlock pBlk : protectingBlocks) {
2302                    log.debug("checking layoutBlock {}", pBlk.getDisplayName());
2303                    pBlkNames = pBlkNames + pBlk.getDisplayName() + " (" + lbm.getLayoutBlockConnectivityTools().checkValidDest(facingBlock, pBlk, destinationBlock, remoteProtectingBlock, LayoutBlockConnectivityTools.Routing.MASTTOMAST) + "), ";
2304                    if (lbm.getLayoutBlockConnectivityTools().checkValidDest(facingBlock, pBlk, destinationBlock, remoteProtectingBlock, LayoutBlockConnectivityTools.Routing.MASTTOMAST)) {
2305                        try {
2306                            lblks = lbm.getLayoutBlockConnectivityTools().getLayoutBlocks(facingBlock, destinationBlock, pBlk, true, LayoutBlockConnectivityTools.Routing.MASTTOMAST);
2307                            protectingBlock = pBlk;
2308                            log.debug("building path names...");
2309                            for (LayoutBlock lBlk : lblks) {
2310                                lBlksNamesBuf.append(" ");
2311                                lBlksNamesBuf.append(lBlk.getDisplayName());
2312                            }
2313                            break;
2314                        } catch (JmriException ee) {
2315                            log.debug("path not found this time");
2316                        }
2317                    }
2318                }
2319                String lBlksNames = new String(lBlksNamesBuf);
2320
2321                if (protectingBlock == null) {
2322                    throw new JmriException("Path not valid, protecting block is null. Protecting block: " + pBlkNames
2323                        + " not connected to " + facingBlock.getDisplayName() + ". Layout block names: " + lBlksNames);
2324                }
2325            }
2326            if (!lbm.getLayoutBlockConnectivityTools().checkValidDest(facingBlock,protectingBlock,
2327                destinationBlock, remoteProtectingBlock, LayoutBlockConnectivityTools.Routing.MASTTOMAST)) {
2328                throw new JmriException("Path not valid, destination check failed.");
2329            }
2330            if (log.isDebugEnabled()) {
2331                log.debug("{} face {}", destinationSignalMast.getDisplayName(), facingBlock);
2332                log.debug("{} prot {}", destinationSignalMast.getDisplayName(), protectingBlock);
2333                log.debug("{} dest {}", destinationSignalMast.getDisplayName(), destinationBlock);
2334            }
2335
2336            if (destinationBlock != null && protectingBlock != null && facingBlock != null) {
2337                setAutoMasts(null, true);
2338                if (log.isDebugEnabled()) {
2339                    log.debug("{} face {}", destinationSignalMast.getDisplayName(), facingBlock.getDisplayName());
2340                    log.debug("{} prot {}", destinationSignalMast.getDisplayName(), protectingBlock.getDisplayName());
2341                    log.debug("{} dest {}", destinationSignalMast.getDisplayName(), destinationBlock.getDisplayName());
2342                }
2343
2344                try {
2345                    lblks = lbm.getLayoutBlockConnectivityTools().getLayoutBlocks(
2346                        facingBlock, destinationBlock, protectingBlock,
2347                            true, LayoutBlockConnectivityTools.Routing.MASTTOMAST);
2348                } catch (JmriException ee) {
2349                    log.error("No blocks found by the layout editor for pair {}-{}",
2350                        source.getDisplayName(), destinationSignalMast.getDisplayName());
2351                }
2352                LinkedHashMap<Block, Integer> block = setupLayoutEditorTurnoutDetails(lblks);
2353
2354                for (int i = 0; i < blockInXings.size(); i++) {
2355                    blockInXings.get(i).removeSignalMastLogic(source);
2356                }
2357                blockInXings = new ArrayList<>(0);
2358                xingAutoBlocks = new ArrayList<>(0);
2359                for (LayoutEditor lay : layout) {
2360                    for (LevelXing levelXing : lay.getLevelXings()) {
2361                        //Looking for a crossing that both layout blocks defined and they are individual.
2362                        if ((levelXing.getLayoutBlockAC() != null)
2363                                && (levelXing.getLayoutBlockBD() != null)
2364                                && (levelXing.getLayoutBlockAC() != levelXing.getLayoutBlockBD())) {
2365                            if (lblks.contains(levelXing.getLayoutBlockAC()) &&
2366                                    levelXing.getLayoutBlockAC() != facingBlock) {  // Don't include the facing xing blocks
2367                                block.put(levelXing.getLayoutBlockBD().getBlock(), Block.UNOCCUPIED);
2368                                xingAutoBlocks.add(levelXing.getLayoutBlockBD().getBlock());
2369                                blockInXings.add(levelXing);
2370                            } else if (lblks.contains(levelXing.getLayoutBlockBD()) &&
2371                                    levelXing.getLayoutBlockBD() != facingBlock) {  // Don't include the facing xing blocks
2372                                block.put(levelXing.getLayoutBlockAC().getBlock(), Block.UNOCCUPIED);
2373                                xingAutoBlocks.add(levelXing.getLayoutBlockAC().getBlock());
2374                                blockInXings.add(levelXing);
2375                            }
2376                        }
2377                    }
2378                }
2379                if (useLayoutEditorBlocks) {
2380                    setAutoBlocks(block);
2381                } else {
2382                    setAutoBlocks(null);
2383                }
2384                if (!useLayoutEditorTurnouts) {
2385                    setAutoTurnouts(null);
2386                }
2387
2388                setupAutoSignalMast(null, false);
2389            }
2390            initialise();
2391        }
2392
2393        /**
2394         * From a list of Layout Blocks, search for included Turnouts and their
2395         * Set To state.
2396         *
2397         * @param lblks List of Layout Blocks
2398         * @return a list of block - turnout state pairs
2399         */
2400        LinkedHashMap<Block, Integer> setupLayoutEditorTurnoutDetails(List<LayoutBlock> lblks) {
2401            ConnectivityUtil connection;
2402            List<LayoutTrackExpectedState<LayoutTurnout>> turnoutList;
2403            Hashtable<Turnout, Integer> turnoutSettings = new Hashtable<>();
2404            LinkedHashMap<Block, Integer> block = new LinkedHashMap<>();
2405            for (int i = 0; i < lblks.size(); i++) {
2406                log.debug("layoutblock {}",lblks.get(i).getDisplayName());
2407                block.put(lblks.get(i).getBlock(), Block.UNOCCUPIED);
2408                if ((i > 0)) {
2409                    int nxtBlk = i + 1;
2410                    int preBlk = i - 1;
2411                    if (i == lblks.size() - 1) {
2412                        nxtBlk = i;
2413                    }
2414                    //We use the best connectivity for the current block.
2415                    connection = new ConnectivityUtil(lblks.get(i).getMaxConnectedPanel());
2416                    if (i == lblks.size() - 1 && remoteProtectingBlock != null) {
2417                        turnoutList = connection.getTurnoutList(lblks.get(i)
2418                            .getBlock(), lblks.get(preBlk).getBlock(), remoteProtectingBlock.getBlock());
2419                    }else{
2420                        turnoutList = connection.getTurnoutList(lblks.get(i)
2421                            .getBlock(), lblks.get(preBlk).getBlock(), lblks.get(nxtBlk).getBlock());
2422                    }
2423                    for (int x = 0; x < turnoutList.size(); x++) {
2424                        LayoutTurnout lt = turnoutList.get(x).getObject();
2425                        if (lt instanceof LayoutSlip) {
2426                            LayoutSlip ls = (LayoutSlip) lt;
2427                            int slipState = turnoutList.get(x).getExpectedState();
2428                            int taState = ls.getTurnoutState(slipState);
2429                            Turnout tTemp = ls.getTurnout();
2430                            if (tTemp == null ) {
2431                                log.error("Unexpected null Turnout in {}, skipped", ls);
2432                                continue; // skip this one in loop, what else can you do?
2433                            }
2434                            turnoutSettings.put(ls.getTurnout(), taState);
2435                            int tbState = ls.getTurnoutBState(slipState);
2436                            turnoutSettings.put(ls.getTurnoutB(), tbState);
2437                        } else if ( lt != null ) {
2438                            String t = lt.getTurnoutName();
2439                            // temporary = why is this looking up the Turnout instead of using getTurnout()?
2440                            Turnout turnout = InstanceManager.turnoutManagerInstance().getTurnout(t);
2441                            if (log.isDebugEnabled()) {
2442                                if (    (lt.getTurnoutType() == LayoutTurnout.TurnoutType.RH_TURNOUT ||
2443                                         lt.getTurnoutType() == LayoutTurnout.TurnoutType.LH_TURNOUT ||
2444                                         lt.getTurnoutType() == LayoutTurnout.TurnoutType.WYE_TURNOUT)
2445                                        && (!lt.getBlockName().isEmpty())) {
2446                                    log.debug("turnout in list is straight left/right wye");
2447                                    log.debug("turnout block Name {}", lt.getBlockName());
2448                                    log.debug("current {} - pre {}", lblks.get(i).getBlock().getDisplayName(), lblks.get(preBlk).getBlock().getDisplayName());
2449                                    log.debug("A {}", lt.getConnectA());
2450                                    log.debug("B {}", lt.getConnectB());
2451                                    log.debug("C {}", lt.getConnectC());
2452                                    log.debug("D {}", lt.getConnectD());
2453                                }
2454                            }
2455                            if (turnout != null ) {
2456                                turnoutSettings.put(turnout, turnoutList.get(x).getExpectedState());
2457                            }
2458                            Turnout tempT;
2459                            if ((tempT = lt.getSecondTurnout()) != null) {
2460                                turnoutSettings.put(tempT, turnoutList.get(x).getExpectedState());
2461                            }
2462                            /* TODO: We could do with a more intelligent way to deal with double crossovers, other than
2463                                just looking at the state of the other conflicting blocks, such as looking at Signalmasts
2464                                that protect the other blocks and the settings of any other turnouts along the way.
2465                             */
2466                            if (lt.getTurnoutType() == LayoutTurnout.TurnoutType.DOUBLE_XOVER) {
2467                                LayoutBlock tempLB;
2468                                if (turnoutList.get(x).getExpectedState() == Turnout.THROWN) {
2469                                    if (lt.getLayoutBlock() == lblks.get(i) || lt.getLayoutBlockC() == lblks.get(i)) {
2470                                        // A or C, add B and D to remove list unless A=B or C=D
2471                                        if ((tempLB = lt.getLayoutBlockB()) != null) {
2472                                            if (!tempLB.equals(lt.getLayoutBlock())) {
2473                                                dblCrossoverAutoBlocks.add(tempLB.getBlock());
2474                                            }
2475                                            block.put(tempLB.getBlock(), Block.UNOCCUPIED);
2476                                        }
2477                                        if ((tempLB = lt.getLayoutBlockD()) != null) {
2478                                            if (!tempLB.equals(lt.getLayoutBlockC())) {
2479                                                dblCrossoverAutoBlocks.add(tempLB.getBlock());
2480                                            }
2481                                            block.put(tempLB.getBlock(), Block.UNOCCUPIED);
2482                                        }
2483                                    } else if (lt.getLayoutBlockB() == lblks.get(i) || lt.getLayoutBlockD() == lblks.get(i)) {
2484                                        // B or D, add A and C to remove list unless A=B or C=D
2485                                        if ((tempLB = lt.getLayoutBlock()) != null) {
2486                                            if (!tempLB.equals(lt.getLayoutBlockB())) {
2487                                                dblCrossoverAutoBlocks.add(tempLB.getBlock());
2488                                            }
2489                                            block.put(tempLB.getBlock(), Block.UNOCCUPIED);
2490                                        }
2491                                        if ((tempLB = lt.getLayoutBlockC()) != null) {
2492                                            if (!tempLB.equals(lt.getLayoutBlockD())) {
2493                                                dblCrossoverAutoBlocks.add(tempLB.getBlock());
2494                                            }
2495                                            block.put(tempLB.getBlock(), Block.UNOCCUPIED);
2496                                        }
2497                                    }
2498                                }
2499                            }
2500                        }
2501                    }
2502                }
2503            }
2504            // ----- Begin Turntable Alignment Check -----
2505            // For paths onto a turntable, we must add a condition for the turntable's alignment. The
2506            // turnout that controls the turntable position is associated with the specific ray track.
2507            // Each ray track can have a control turnout assigned. Setting this turnout to a specific
2508            // state (usually THROWN) is the command to align the turntable to that ray.
2509            // The following logic adds a CONDITION to the Signal Mast Logic, requiring that the
2510            // correct ray's turnout is in its required state before the signal will clear.
2511            // This does NOT command the turnout to move; it only checks its state for the interlocking.
2512            Set<LayoutEditor> layout = InstanceManager.getDefault(EditorManager.class).getAll(LayoutEditor.class);
2513            for (LayoutEditor lay : layout) {
2514                for (LayoutTurntable turntable : lay.getLayoutTurntables()) {
2515                    // Check for a path from an Approach Mast to the Buffer Mast (Path 3).
2516                    // The destination block is the turntable block, and the destination mast is the buffer mast.
2517                    if (turntable.getLayoutBlock() == destinationBlock && turntable.getBufferMast() == destinationSignalMast) {
2518                        // The source mast's facing block is the ray block for this path.
2519                        for (LayoutTurntable.RayTrack ray : turntable.getRayTrackList()) {
2520                            if (ray.getConnect() != null && ray.getConnect().getLayoutBlock() == DefaultSignalMastLogic.this.facingBlock) {
2521                                // This is the correct ray. Get its control turnout and required state.
2522                                Turnout rayTurnout = ray.getTurnout();
2523                                int requiredState = ray.getTurnoutState();
2524                                if (rayTurnout != null) {
2525                                    turnoutSettings.put(rayTurnout, requiredState);
2526                                }
2527                                break; // Found the ray, no need to check others.
2528                            }
2529                        }
2530                    }
2531                    // Check for a path from the Exit Mast to a remote mast (Path 1).
2532                    // The source mast is the turntable's exit mast.
2533                    if (turntable.getExitSignalMast() == getSourceMast()) {
2534                        // The protecting block is the ray block for this path.
2535                        for (LayoutTurntable.RayTrack ray : turntable.getRayTrackList()) {
2536                            if (ray.getConnect() != null && ray.getConnect().getLayoutBlock() == protectingBlock) {
2537                                // This is the correct ray. Get its control turnout and required state.
2538                                Turnout rayTurnout = ray.getTurnout();
2539                                int requiredState = ray.getTurnoutState();
2540                                if (rayTurnout != null) {
2541                                    turnoutSettings.put(rayTurnout, requiredState);
2542                                }
2543                                break; // Found the ray, no need to check others.
2544                            }
2545                        }
2546                    }
2547                }
2548            }
2549            // ----- End Turntable Alignment Check -----
2550            // ----- Begin Traverser Alignment Check -----
2551            for (LayoutEditor lay : layout) {
2552                for (LayoutTraverser traverser : lay.getLayoutTraversers()) {
2553                    // Check for a path from an Approach Mast to the Buffer Mast (Path 3).
2554                    // The destination block is the traverser block, and the destination mast is the buffer mast.
2555                    if (traverser.getLayoutBlock() == destinationBlock && traverser.getBufferMast() == destinationSignalMast) {
2556                        // The source mast's facing block is the slot block for this path.
2557                        for (LayoutTraverser.SlotTrack slot : traverser.getSlotList()) {
2558                            if (slot.getConnect() != null && slot.getConnect().getLayoutBlock() == DefaultSignalMastLogic.this.facingBlock) {
2559                                // This is the correct slot. Get its control turnout and required state.
2560                                Turnout slotTurnout = slot.getTurnout();
2561                                int requiredState = slot.getTurnoutState();
2562                                if (slotTurnout != null) {
2563                                    turnoutSettings.put(slotTurnout, requiredState);
2564                                }
2565                                break; // Found the slot, no need to check others.
2566                            }
2567                        }
2568                    }
2569                    // Check for a path from the Exit Mast to a remote mast (Path 1).
2570                    // The source mast is the traverser's exit mast.
2571                    if (traverser.getExitSignalMast() == getSourceMast()) {
2572                        // The protecting block is the slot block for this path.
2573                        for (LayoutTraverser.SlotTrack slot : traverser.getSlotList()) {
2574                            if (slot.getConnect() != null && slot.getConnect().getLayoutBlock() == protectingBlock) {
2575                                // This is the correct slot. Get its control turnout and required state.
2576                                Turnout slotTurnout = slot.getTurnout();
2577                                int requiredState = slot.getTurnoutState();
2578                                if (slotTurnout != null) {
2579                                    turnoutSettings.put(slotTurnout, requiredState);
2580                                }
2581                                break; // Found the slot, no need to check others.
2582                            }
2583                        }
2584                    }
2585                }
2586            }
2587            // ----- End Traverser Alignment Check -----
2588            if (useLayoutEditorTurnouts) {
2589                setAutoTurnouts(turnoutSettings);
2590            }
2591            return block;
2592        }
2593
2594        /**
2595         * Generate auto signalmast for a given SML. Looks through all the other
2596         * logics to see if there are any blocks that are in common and thus
2597         * will add the other signal mast protecting that block.
2598         *
2599         * @param sml       The Signal Mast Logic for which to set up
2600         *                  autoSignalMasts
2601         * @param overwrite When true, replace an existing autoMasts list in the
2602         *                  SML
2603         */
2604        void setupAutoSignalMast(SignalMastLogic sml, boolean overwrite) {
2605            if (!allowAutoSignalMastGeneration) {
2606                return;
2607            }
2608            List<SignalMastLogic> smlList = InstanceManager.getDefault(SignalMastLogicManager.class)
2609                .getLogicsByDestination(destinationSignalMast);
2610            List<Block> allBlock = new ArrayList<>();
2611
2612            userSetBlocks.forEach(nbh -> allBlock.add((Block) nbh.getBean()));
2613
2614            Set<Block> blockKeys = autoBlocks.keySet();
2615            blockKeys.stream().filter(key -> (!allBlock.contains(key))).forEachOrdered(key ->
2616                allBlock.add(key));
2617            Hashtable<SignalMast, String> masts;
2618            if (sml != null) {
2619                masts = autoMasts;
2620                if (sml.areBlocksIncluded(allBlock)) {
2621                    SignalMast mast = sml.getSourceMast();
2622                    String danger = mast.getAppearanceMap().getSpecificAppearance(SignalAppearanceMap.DANGER);
2623                    masts.put(mast, danger);
2624                } else {
2625                    //No change so will leave.
2626                    return;
2627                }
2628            } else {
2629                masts = new Hashtable<>();
2630                for (int i = 0; i < smlList.size(); i++) {
2631                    if (smlList.get(i).areBlocksIncluded(allBlock)) {
2632                        SignalMast mast = smlList.get(i).getSourceMast();
2633                        String danger = mast.getAppearanceMap().getSpecificAppearance(SignalAppearanceMap.DANGER);
2634                        masts.put(mast, danger);
2635                    }
2636                }
2637            }
2638            setAutoMasts(masts, overwrite);
2639        }
2640
2641        /**
2642         * Add a certain Signal Mast to the list of AutoSignalMasts for this
2643         * SML.
2644         *
2645         * @param mast The Signal Mast to be added
2646         */
2647        void addAutoSignalMast(SignalMast mast) {
2648            log.debug("{} add mast to auto list {}", destinationSignalMast.getDisplayName(), mast);
2649            String danger = mast.getAppearanceMap().getSpecificAppearance(SignalAppearanceMap.DANGER);
2650            if (danger == null) {
2651                log.error("Can not add SignalMast {} to logic for {} to {} "
2652                    + "as it does not have a Danger appearance configured",
2653                        mast.getDisplayName(), source.getDisplayName(), destinationSignalMast.getDisplayName());
2654                return;
2655            }
2656            this.autoMasts.put(mast, danger);
2657            if (destMastInit) {
2658                mast.addPropertyChangeListener(propertySignalMastListener);
2659            }
2660            firePropertyChange(PROPERTY_AUTO_MASTS, null, this.destinationSignalMast);
2661        }
2662
2663        /**
2664         * Remove a certain Signal Mast from the list of AutoSignalMasts for
2665         * this SML.
2666         *
2667         * @param mast The Signal Mast to be removed
2668         */
2669        void removeAutoSignalMast(SignalMast mast) {
2670            this.autoMasts.remove(mast);
2671            if (destMastInit) {
2672                mast.removePropertyChangeListener(propertySignalMastListener);
2673            }
2674            firePropertyChange(PROPERTY_AUTO_MASTS, this.destinationSignalMast, null);
2675        }
2676
2677        boolean useLayoutEditor() {
2678            return useLayoutEditor;
2679        }
2680
2681        boolean useLayoutEditorBlocks() {
2682            return useLayoutEditorBlocks;
2683        }
2684
2685        boolean useLayoutEditorTurnouts() {
2686            return useLayoutEditorTurnouts;
2687        }
2688
2689        boolean allowAutoSignalMastGeneration = false;
2690
2691        boolean allowAutoSignalMastGen() {
2692            return allowAutoSignalMastGeneration;
2693        }
2694
2695        void allowAutoSignalMastGen(boolean gen) {
2696            if (allowAutoSignalMastGeneration == gen) {
2697                return;
2698            }
2699            allowAutoSignalMastGeneration = gen;
2700        }
2701
2702        /**
2703         * Remove references from this Destination Mast and clear lists, so that
2704         * it can eventually be garbage-collected.
2705         * <p>
2706         * Note: This does not stop any delayed operations that might be queued.
2707         */
2708        void dispose() {
2709            disposed = true;
2710            clearTurnoutLock();
2711            destinationSignalMast.removePropertyChangeListener(propertyDestinationMastListener);
2712            setBlocks(null);
2713            setAutoBlocks(null);
2714            setTurnouts(null);
2715            setAutoTurnouts(null);
2716            setSensors(null);
2717            setMasts(null);
2718            setAutoMasts(null, true);
2719        }
2720
2721        void lockTurnouts() {
2722            // We do not allow the turnouts to be locked if we are disposing the logic,
2723            // if the logic is not active, or if we do not allow the turnouts to be locked.
2724            if ((disposed) || (!lockTurnouts) || (!active)) {
2725                return;
2726            }
2727
2728            userSetTurnouts.stream().map(nbh -> (Turnout) nbh.getBean()).forEachOrdered(key -> {
2729                key.setLocked(Turnout.CABLOCKOUT + Turnout.PUSHBUTTONLOCKOUT, true);
2730            });
2731            Enumeration<Turnout> keys = autoTurnouts.keys();
2732            while (keys.hasMoreElements()) {
2733                Turnout key = keys.nextElement();
2734                key.setLocked(Turnout.CABLOCKOUT + Turnout.PUSHBUTTONLOCKOUT, true);
2735            }
2736        }
2737
2738        void clearTurnoutLock() {
2739            // We do not allow the turnout lock to be cleared if we are not active,
2740            // and the lock flag has not been set.
2741            if ((!lockTurnouts) && (!active)) {
2742                return;
2743            }
2744
2745            Enumeration<Turnout> keys = autoTurnouts.keys();
2746            while (keys.hasMoreElements()) {
2747                Turnout key = keys.nextElement();
2748                key.setLocked(Turnout.CABLOCKOUT + Turnout.PUSHBUTTONLOCKOUT, false);
2749            }
2750
2751            userSetTurnouts.stream().map(nbh -> (Turnout) nbh.getBean()).forEachOrdered(key ->
2752                key.setLocked(Turnout.CABLOCKOUT + Turnout.PUSHBUTTONLOCKOUT, false));
2753        }
2754
2755        protected void calculateSpeed() {
2756            log.debug("{} calculate the speed setting for this logic ie what the signalmast will display", destinationSignalMast.getDisplayName());
2757            minimumBlockSpeed = 0.0f;
2758            Enumeration<Turnout> keys = autoTurnouts.keys();
2759            while (keys.hasMoreElements()) {
2760                Turnout key = keys.nextElement();
2761                log.debug("{} turnout {}", destinationSignalMast.getDisplayName(), key.getDisplayName());
2762                if (!isTurnoutIncluded(key)) {
2763                    if (autoTurnouts.get(key) == Turnout.CLOSED) {
2764                        if (((key.getStraightLimit() < minimumBlockSpeed) || (minimumBlockSpeed == 0)) && (key.getStraightLimit() != -1)) {
2765                            minimumBlockSpeed = key.getStraightLimit();
2766                            log.debug("{} turnout {} set speed to {}", destinationSignalMast.getDisplayName(), key.getDisplayName(), minimumBlockSpeed);
2767                        }
2768                    } else {
2769                        if (((key.getDivergingLimit() < minimumBlockSpeed) || (minimumBlockSpeed == 0)) && (key.getDivergingLimit() != -1)) {
2770                            minimumBlockSpeed = key.getDivergingLimit();
2771                            log.debug("{} turnout {} set speed to {}", destinationSignalMast.getDisplayName(), key.getDisplayName(), minimumBlockSpeed);
2772                        }
2773                    }
2774                }
2775            }
2776
2777            userSetTurnouts.forEach(nbh -> {
2778                Turnout key = (Turnout) nbh.getBean();
2779                if (nbh.getSetting() == Turnout.CLOSED) {
2780                    if (((key.getStraightLimit() < minimumBlockSpeed) || (minimumBlockSpeed == 0)) && (key.getStraightLimit() != -1)) {
2781                        minimumBlockSpeed = key.getStraightLimit();
2782                        log.debug("{} turnout {} set speed to {}", destinationSignalMast.getDisplayName(), key.getDisplayName(), minimumBlockSpeed);
2783                    }
2784                } else if (nbh.getSetting() == Turnout.THROWN) {
2785                    if (((key.getDivergingLimit() < minimumBlockSpeed) || (minimumBlockSpeed == 0)) && (key.getDivergingLimit() != -1)) {
2786                        minimumBlockSpeed = key.getDivergingLimit();
2787                        log.debug("{} turnout {} set speed to {}", destinationSignalMast.getDisplayName(), key.getDisplayName(), minimumBlockSpeed);
2788                    }
2789                }
2790            });
2791
2792            Set<Block> autoBlockKeys = autoBlocks.keySet();
2793            for (Block key : autoBlockKeys) {
2794                log.debug("{} auto block add list {}", destinationSignalMast.getDisplayName(), key.getDisplayName());
2795                if (!isBlockIncluded(key)) {
2796                    if (((key.getSpeedLimit() < minimumBlockSpeed) || (minimumBlockSpeed == 0)) && (key.getSpeedLimit() != -1)) {
2797                        minimumBlockSpeed = key.getSpeedLimit();
2798                        log.debug("{} block {} set speed to {}", destinationSignalMast.getDisplayName(), key.getDisplayName(), minimumBlockSpeed);
2799                    }
2800                }
2801            }
2802            for (NamedBeanSetting nbh : userSetBlocks) {
2803                Block key = (Block) nbh.getBean();
2804                if (((key.getSpeedLimit() < minimumBlockSpeed) || (minimumBlockSpeed == 0)) && (key.getSpeedLimit() != -1)) {
2805                    log.debug("{} block {} set speed to {}", destinationSignalMast.getDisplayName(), key.getDisplayName(), minimumBlockSpeed);
2806                    minimumBlockSpeed = key.getSpeedLimit();
2807                }
2808            }
2809            /*if(minimumBlockSpeed==-0.1f)
2810             minimumBlockSpeed==0.0f;*/
2811        }
2812
2813        protected PropertyChangeListener propertySensorListener = new PropertyChangeListener() {
2814            @Override
2815            public void propertyChange(PropertyChangeEvent e) {
2816                Sensor sen = (Sensor) e.getSource();
2817                log.debug("{} to {} destination sensor {} trigger {}",source.getDisplayName(), destinationSignalMast.getDisplayName(), sen.getDisplayName(), e.getPropertyName());
2818                if ( Sensor.PROPERTY_KNOWN_STATE.equals(e.getPropertyName())) {
2819                    int now = ((Integer) e.getNewValue());
2820                    log.debug("current value {} value we want {}", now, getSensorState(sen));
2821                    if (isSensorIncluded(sen) && getSensorState(sen) != now) {
2822                        log.debug("Sensor {} caused the signalmast to be set to danger", sen.getDisplayName());
2823                        //getSourceMast().setAspect(stopAspect);
2824                        if (active == true) {
2825                            active = false;
2826                            setSignalAppearance();
2827                        }
2828                    } else if (getSensorState(sen) == now) {
2829                        log.debug("{} sensor {} triggers a calculation of change", destinationSignalMast.getDisplayName(), sen.getDisplayName());
2830                        checkState();
2831                    }
2832                }
2833            }
2834        };
2835
2836        protected PropertyChangeListener propertyTurnoutListener = new PropertyChangeListener() {
2837            @Override
2838            public void propertyChange(PropertyChangeEvent e) {
2839                Turnout turn = (Turnout) e.getSource();
2840                //   log.debug(destination.getDisplayName() + " destination sensor "+ sen.getDisplayName() + "trigger");
2841                if ( Turnout.PROPERTY_KNOWN_STATE.equals(e.getPropertyName())) {
2842                    //Need to check this against the manual list vs auto list
2843                    //The manual list should over-ride the auto list
2844                    int now = ((Integer) e.getNewValue());
2845                    if (isTurnoutIncluded(turn)) {
2846                        if (getTurnoutState(turn) != now) {
2847                            log.debug("Turnout {} caused the signalmast to be set", turn.getDisplayName());
2848                            log.debug("From {} to {} Turnout {} caused the signalmast to be set to danger", getSourceMast().getDisplayName(), destinationSignalMast.getDisplayName(), turn.getDisplayName());
2849                            if (active == true) {
2850                                active = false;
2851                                setSignalAppearance();
2852                            }
2853                        } else {
2854                            log.debug("{} turnout {} triggers a calculation of change", destinationSignalMast.getDisplayName(), turn.getDisplayName());
2855                            checkState();
2856                        }
2857                    } else if (autoTurnouts.containsKey(turn)) {
2858                        if (getAutoTurnoutState(turn) != now) {
2859                            log.debug("Turnout {} auto caused the signalmast to be set", turn.getDisplayName());
2860                            log.debug("From {} to {} Auto Turnout {} auto caused the signalmast to be set to danger", getSourceMast().getDisplayName(), destinationSignalMast.getDisplayName(), turn.getDisplayName());
2861                            if (active == true) {
2862                                active = false;
2863                                setSignalAppearance();
2864                            }
2865                        } else {
2866                            log.debug("From {} to {} turnout {} triggers a calculation of change", getSourceMast().getDisplayName(), destinationSignalMast.getDisplayName(), turn.getDisplayName());
2867                            checkState();
2868                        }
2869                    }
2870
2871                } else if ( Turnout.PROPERTY_TURNOUT_STRAIGHT_SPEED.equals(e.getPropertyName())
2872                        || Turnout.PROPERTY_TURNOUT_DIVERGING_SPEED.equals(e.getPropertyName())) {
2873                    calculateSpeed();
2874                }
2875            }
2876        };
2877
2878        protected PropertyChangeListener propertyBlockListener = new PropertyChangeListener() {
2879            @Override
2880            public void propertyChange(PropertyChangeEvent e) {
2881                Block block = (Block) e.getSource();
2882                log.debug("{} destination block {} trigger {} {}", destinationSignalMast.getDisplayName(), block.getDisplayName(), e.getPropertyName(), e.getNewValue());
2883                if ( Block.PROPERTY_STATE.equals(e.getPropertyName()) || Block.PROPERTY_ALLOCATED.equals(e.getPropertyName())) {
2884                    // TODO: what is this?
2885                    log.debug("Included in user entered block {}", Boolean.toString(isBlockIncluded(block)));
2886                    log.debug("Included in AutoGenerated Block {}", Boolean.toString(autoBlocks.containsKey(block)));
2887                    if (isBlockIncluded(block)) {
2888                        log.debug("{} in manual block", destinationSignalMast.getDisplayName());
2889                        log.debug("  state: {}  {}", getBlockState(block), block.getState());
2890                        checkState();
2891                    } else if (autoBlocks.containsKey(block)) {
2892                        log.debug("{} in auto block", destinationSignalMast.getDisplayName());
2893                        log.debug("  states: {}  {}", getAutoBlockState(block), block.getState());
2894                        checkState();
2895                    } else {
2896                        log.debug("{} Not found", destinationSignalMast.getDisplayName());
2897                    }
2898                } else if ( e.getPropertyName().equals("BlockSpeedChange")) {
2899                    calculateSpeed();
2900                }
2901            }
2902        };
2903
2904        protected PropertyChangeListener propertySignalMastListener = new PropertyChangeListener() {
2905            @Override
2906            public void propertyChange(PropertyChangeEvent e) {
2907
2908                SignalMast mast = (SignalMast) e.getSource();
2909                log.debug("{} signalmast change {} {}", destinationSignalMast.getDisplayName(), mast.getDisplayName(), e.getPropertyName());
2910                //   log.debug(destination.getDisplayName() + " destination sensor "+ sen.getDisplayName() + "trigger");
2911                if ( SignalMast.PROPERTY_ASPECT.equals(e.getPropertyName())) {
2912
2913                    String now = ((String) e.getNewValue());
2914                    log.debug("{} match property {}", destinationSignalMast.getDisplayName(), now);
2915                    if (isSignalMastIncluded(mast)) {
2916                        if (!now.equals(getSignalMastState(mast))) {
2917                            log.debug("{} in mast list SignalMast {} caused the signalmast to be set", destinationSignalMast.getDisplayName(), mast.getDisplayName());
2918                            log.debug("SignalMast {} caused the signalmast to be set", mast.getDisplayName());
2919                            if (active) {
2920                                active = false;
2921                                setSignalAppearance();
2922                            }
2923                        } else {
2924                            log.debug("{} in mast list signalmast change", destinationSignalMast.getDisplayName());
2925                            checkState();
2926                        }
2927                    } else if (autoMasts.containsKey(mast)) {
2928                        if (!now.equals(getAutoSignalMastState(mast))) {
2929                            log.debug("SignalMast {} caused the signalmast to be set", mast.getDisplayName());
2930                            log.debug("{} in auto mast list SignalMast {} caused the signalmast to be set", destinationSignalMast.getDisplayName(), mast.getDisplayName());
2931                            if (active) {
2932                                active = false;
2933                                setSignalAppearance();
2934                            }
2935                        } else {
2936                            log.debug("{} in auto mast list signalmast change", destinationSignalMast.getDisplayName());
2937                            checkState();
2938                        }
2939                    }
2940                }
2941            }
2942        };
2943
2944        private class NamedBeanSetting {
2945
2946            NamedBeanHandle<?> namedBean;
2947            int setting = 0;
2948            String strSetting = null;
2949
2950            NamedBeanSetting(NamedBeanHandle<?> namedBean, int setting) {
2951                this.namedBean = namedBean;
2952                this.setting = setting;
2953            }
2954
2955            NamedBeanSetting(NamedBeanHandle<?> namedBean, String setting) {
2956                this.namedBean = namedBean;
2957                strSetting = setting;
2958            }
2959
2960            NamedBean getBean() {
2961                return namedBean.getBean();
2962            }
2963
2964            NamedBeanHandle<?> getNamedBean() {
2965                return namedBean;
2966            }
2967
2968            int getSetting() {
2969                return setting;
2970            }
2971
2972            String getStringSetting() {
2973                return strSetting;
2974            }
2975
2976            String getBeanName() {
2977                return namedBean.getName();
2978            }
2979        }
2980    }
2981
2982    /**
2983     * The listener on the destination Signal Mast.
2984     */
2985    private PropertyChangeListener propertyDestinationMastListener = new PropertyChangeListener() {
2986        @Override
2987        public void propertyChange(PropertyChangeEvent e) {
2988            SignalMast mast = (SignalMast) e.getSource();
2989            if (mast == destination) {
2990                log.debug("destination mast change {}", mast.getDisplayName());
2991                setSignalAppearance();
2992            }
2993        }
2994    };
2995
2996    /**
2997     * The listener on the source Signal Mast.
2998     */
2999    private PropertyChangeListener propertySourceMastListener = new PropertyChangeListener() {
3000        @Override
3001        public void propertyChange(PropertyChangeEvent e) {
3002            SignalMast mast = (SignalMast) e.getSource();
3003            if ((mast == source) && ( SignalMast.PROPERTY_HELD.equals(e.getPropertyName()))) {
3004                log.debug("source mast change {} {}", mast.getDisplayName(), e.getPropertyName());
3005                setSignalAppearance();
3006            }
3007        }
3008    };
3009
3010    //@todo need to think how we deal with auto generated lists based upon the layout editor.
3011    @Override
3012    public void vetoableChange(PropertyChangeEvent evt) throws PropertyVetoException {
3013        NamedBean nb = (NamedBean) evt.getOldValue();
3014        if (Manager.PROPERTY_CAN_DELETE.equals(evt.getPropertyName())) {
3015            boolean found = false;
3016            StringBuilder message = new StringBuilder();
3017            if (nb instanceof SignalMast) {
3018                if (nb.equals(source)) {
3019                    message.append("Has SignalMast Logic attached which will be <b>Deleted</b> to <ul>");
3020                    for (SignalMast sm : getDestinationList()) {
3021                        message.append("<li>");
3022                        message.append(sm.getDisplayName());
3023                        message.append("</li>");
3024                    }
3025                    message.append("</ul>");
3026                    throw new PropertyVetoException(message.toString(), evt);
3027
3028                } else if (isDestinationValid((SignalMast) nb)) {
3029                    throw new PropertyVetoException("Is the end point mast for logic attached to signal mast " + source.getDisplayName() + " which will be <b>Deleted</b> ", evt);
3030                }
3031                for (SignalMast sm : getDestinationList()) {
3032                    if (isSignalMastIncluded((SignalMast) nb, sm)) {
3033                        message.append("<li>");
3034                        message.append("Used in conflicting logic of ").append(source.getDisplayName())
3035                            .append(" & ").append(sm.getDisplayName()).append("</li>");
3036                    }
3037                }
3038            }
3039            if (nb instanceof Turnout) {
3040                for (SignalMast sm : getDestinationList()) {
3041                    if (isTurnoutIncluded((Turnout) nb, sm)) {
3042                        message.append("<li>Is in logic between Signal Masts ").append(source.getDisplayName())
3043                            .append(" ").append(sm.getDisplayName()).append("</li>");
3044                        found = true;
3045                    }
3046                }
3047            }
3048            if (nb instanceof Sensor) {
3049                for (SignalMast sm : getDestinationList()) {
3050                    if (isSensorIncluded((Sensor) nb, sm)) {
3051                        message.append("<li>");
3052                        message.append("Is in logic between Signal Masts ").append(source.getDisplayName())
3053                            .append(" ").append(sm.getDisplayName()).append("</li>");
3054                        found = true;
3055                    }
3056                }
3057            }
3058            if (found) {
3059                throw new PropertyVetoException(message.toString(), evt);
3060            }
3061        } else if (Manager.PROPERTY_DO_DELETE.equals(evt.getPropertyName())) {
3062            if (nb instanceof SignalMast) {
3063                if (nb.equals(source)) {
3064                    dispose();
3065                }
3066                if (isDestinationValid((SignalMast) nb)) {
3067                    removeDestination((SignalMast) nb);
3068                }
3069                for (SignalMast sm : getDestinationList()) {
3070                    if (isSignalMastIncluded((SignalMast) nb, sm)) {
3071                        log.warn("Unhandled condition: signal mast included during DoDelete");
3072                        // @todo need to deal with this situation
3073                    }
3074                }
3075            }
3076            if (nb instanceof Turnout) {
3077                Turnout t = (Turnout) nb;
3078                getDestinationList().stream().filter(sm -> (isTurnoutIncluded(t, sm))).forEachOrdered(sm -> {
3079                    removeTurnout(t, sm);
3080                });
3081            }
3082            if (nb instanceof Sensor) {
3083                Sensor s = (Sensor) nb;
3084                getDestinationList().stream().filter(sm -> (isSensorIncluded(s, sm))).forEachOrdered(sm -> {
3085                    removeSensor(s, sm);
3086                });
3087            }
3088        }
3089    }
3090
3091    /**
3092     * Note: This does not stop any delayed operations that might be queued.
3093     */
3094    @Override
3095    public void dispose() {
3096        disposing = true;
3097        getSourceMast().removePropertyChangeListener(propertySourceMastListener);
3098        Enumeration<SignalMast> en = destList.keys();
3099        while (en.hasMoreElements()) {
3100            SignalMast dm = en.nextElement();
3101            destList.get(dm).dispose();
3102        }
3103        super.dispose(); // release any prop change listeners
3104    }
3105
3106    /**
3107     * {@inheritDoc }
3108     */
3109    @Override
3110    public String getBeanType() {
3111        return Bundle.getMessage("BeanNameSignalMastLogic");
3112    }
3113
3114    /**
3115     * No valid integer state, always return a constant.
3116     *
3117     * @return Always zero
3118     */
3119    @Override
3120    public int getState() {
3121        return 0;
3122    }
3123
3124    @Override
3125    public void setState(int i) {
3126    }
3127
3128    /**
3129     * {@inheritDoc }
3130     */
3131    @Override
3132    public List<NamedBeanUsageReport> getUsageReport(NamedBean bean) {
3133        List<NamedBeanUsageReport> report = new ArrayList<>();
3134        if (bean != null) {
3135            if (bean.equals(getSourceMast())) {
3136                report.add(new NamedBeanUsageReport("SMLSourceMast"));  // NOI18N
3137            }
3138            getDestinationList().forEach((dest) -> {
3139                if (bean.equals(dest)) {
3140                    report.add(new NamedBeanUsageReport("SMLDestinationMast"));  // NOI18N
3141                }
3142                getAutoBlocks(dest).forEach((block) -> {
3143                    if (bean.equals(block)) {
3144                        report.add(new NamedBeanUsageReport("SMLBlockAuto", dest));  // NOI18N
3145                    }
3146                });
3147                getBlocks(dest).forEach((block) -> {
3148                    if (bean.equals(block)) {
3149                        report.add(new NamedBeanUsageReport("SMLBlockUser", dest));  // NOI18N
3150                    }
3151                });
3152                getAutoTurnouts(dest).forEach((turnout) -> {
3153                    if (bean.equals(turnout)) {
3154                        report.add(new NamedBeanUsageReport("SMLTurnoutAuto", dest));  // NOI18N
3155                    }
3156                });
3157                getTurnouts(dest).forEach((turnout) -> {
3158                    if (bean.equals(turnout)) {
3159                        report.add(new NamedBeanUsageReport("SMLTurnoutUser", dest));  // NOI18N
3160                    }
3161                });
3162                getSensors(dest).forEach((sensor) -> {
3163                    if (bean.equals(sensor)) {
3164                        report.add(new NamedBeanUsageReport("SMLSensor", dest));  // NOI18N
3165                    }
3166                });
3167                getAutoMasts(dest).forEach((mast) -> {
3168                    if (bean.equals(mast)) {
3169                        report.add(new NamedBeanUsageReport("SMLMastAuto", dest));  // NOI18N
3170                    }
3171                });
3172                getSignalMasts(dest).forEach((mast) -> {
3173                    if (bean.equals(mast)) {
3174                        report.add(new NamedBeanUsageReport("SMLMastUser", dest));  // NOI18N
3175                    }
3176                });
3177            });
3178        }
3179        return report;
3180    }
3181
3182    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(DefaultSignalMastLogic.class);
3183
3184}