java/src/jmri/jmrit/display/layoutEditor/LayoutEditorAuxTools.java

Summary

Maintainability
F
2 wks
Test Coverage
C
73%
package jmri.jmrit.display.layoutEditor;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import jmri.BeanSetting;
import jmri.Path;
import jmri.Turnout;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * LayoutEditorAuxTools provides tools making use of layout connectivity
 * available in Layout Editor panels. (More tools are in
 * LayoutEditorTools.java.)
 * <p>
 * This module manages block connectivity for its associated LayoutEditor.
 * <p>
 * A single object of this type, obtained via {@link LayoutEditor#getLEAuxTools()}
 * is shared across all instances of {@link ConnectivityUtil}.
 * <p>
 * The tools in this module are accessed via the Tools menu in Layout Editor, or
 * directly from LayoutEditor or LayoutEditor specific modules.
 *
 * @author Dave Duchamp Copyright (c) 2008
 * @author George Warner Copyright (c) 2017-2018
 */
final public class LayoutEditorAuxTools {
    // constants

    // operational instance variables
    final private LayoutModels models;
    final private List<LayoutConnectivity> cList = new ArrayList<>(); // LayoutConnectivity list
    private boolean blockConnectivityChanged = false;  // true if block connectivity may have changed
    private boolean initialized = false;

    // constructor method
    public LayoutEditorAuxTools(LayoutModels theModels) {
        models = theModels;
    }

    // register a change in block connectivity that may require an update of connectivity list
    public void setBlockConnectivityChanged() {
        blockConnectivityChanged = true;
    }

    /**
     * Get Connectivity involving a specific Layout Block.
     * <p>
     * This routine returns an ArrayList of BlockConnectivity objects involving
     * the specified LayoutBlock.
     * @param blk the layout block.
     * @return the layout connectivity list, not null.
     */
    public List<LayoutConnectivity> getConnectivityList(LayoutBlock blk) {
        if (!initialized) {
            initializeBlockConnectivity();
        }
        if (blockConnectivityChanged) {
            updateBlockConnectivity();
        }
        List<LayoutConnectivity> retList = new ArrayList<>();
        for (LayoutConnectivity lc : cList) {
            if ((lc.getBlock1() == blk) || (lc.getBlock2() == blk)) {
                retList.add(lc);
            }
        }
        return (retList);
    }

    /**
     * Initializes the block connectivity (block boundaries) for a Layout Editor
     * panel.
     * <p>
     * This routine sets up the LayoutConnectivity objects needed to show the
     * current connectivity. It gets its information from arrays contained in
     * LayoutEditor.
     * <p>
     * One LayoutConnectivity object is created for each block boundary --
     * connection points where two blocks join. Block boundaries can occur where
     * ever a track segment in one block joins with: 1) a track segment in
     * another block -OR- 2) a connection point in a layout turnout in another
     * block -OR- 3) a connection point in a level crossing in another block.
     * <p>
     * The first block is always a track segment. The direction set in the
     * LayoutConnectivity is the direction of the track segment alone for cases
     * 2) and 3) above. For case 1), two track segments, the direction reflects
     * an "average" over the two track segments. See LayoutConnectivity for the
     * allowed values of direction.
     * <p>
     * Normally the initialization only occurs once after the panel is loaded.  When edge
     * connectors are used, an incomplete LayoutConnectivity table can occur due to the
     * panel loading sequence and the complexity of edge connector relationships.
     * An additional initialization is allowed to build the final LayoutConnectivity table.
     */
    public void initializeBlockConnectivity() {
        cList.clear();
        List<LayoutConnectivity> lcs = null;

        for (LayoutTrackView ltv : models.getLayoutTrackViews()) {
            if ((ltv instanceof PositionablePointView)    // effectively, skip LevelXing and LayoutTurntable - why?
                    || (ltv instanceof TrackSegmentView)
                    || (ltv instanceof LayoutTurnoutView)) { // <== includes Wye. LayoutSlips, XOvers
                lcs = ltv.getLayoutConnectivity();
                cList.addAll(lcs); // append to list
            }
        }
        initialized = true;
    }   // initializeBlockConnectivity

    /**
     * Updates the block connectivity (block boundaries) for a Layout Editor
     * panel after changes may have been made.
     */
    private void updateBlockConnectivity() {
        int sz = cList.size();
        boolean[] found = new boolean[sz];
        Arrays.fill(found, false);

        List<LayoutConnectivity> lcs = null;

        // Check for block boundaries at positionable points.
        for (PositionablePoint p : models.getPositionablePoints()) {
            lcs = p.getLayoutConnectivity();
            for (LayoutConnectivity lc : lcs) {
                // add to list, if not already present
                checkConnectivity(lc, found);
            }
        }

        // Check for block boundaries at layout turnouts and level crossings
        for (TrackSegment ts : models.getTrackSegments()) {
            lcs = ts.getLayoutConnectivity();
            for (LayoutConnectivity lc : lcs) {
                // add to list, if not already present
                checkConnectivity(lc, found);
            }
        }

        // check for block boundaries internal to crossover turnouts
        for (LayoutTurnout lt : models.getLayoutTurnouts()) {
            lcs = lt.getLayoutConnectivity();
            for (LayoutConnectivity lc : lcs) {
                // add to list, if not already present
                checkConnectivity(lc, found);
            }
        }

        // check for block boundaries internal to slips
        for (LayoutSlip ls : models.getLayoutSlips()) {
            lcs = ls.getLayoutConnectivity();
            for (LayoutConnectivity lc : lcs) {
                // add to list, if not already present
                checkConnectivity(lc, found);
            }
        }

        // delete any LayoutConnectivity objects no longer needed
        for (int i = sz - 1; i >= 0; i--) {
            if (!found[i]) {
                // djd debugging - message to list connectivity being removed
                //    LayoutConnectivity xx = (LayoutConnectivity)cList.get(i);
                //    log.error("  Deleting Layout Connectivity - " + xx.getBlock1().getId() + ", " + xx.getBlock2().getId());
                // end debugging
                cList.remove(i);
            }
        }
        blockConnectivityChanged = false;
    }   // updateBlockConnectivity

    //
    private void checkConnectivity(LayoutConnectivity c, boolean[] found) {
        // initialize input LayoutConnectivity components
        LayoutBlock blk1 = c.getBlock1();
        LayoutBlock blk2 = c.getBlock2();

        int dir = c.getDirection();
        int rDir = c.getReverseDirection();

        TrackSegment track = c.getTrackSegment();
        LayoutTrack connected = c.getConnectedObject();
        HitPointType type = c.getConnectedType();

        LayoutTurnout xOver = c.getXover();
        int xOverType = c.getXoverBoundaryType();

        // loop over connectivity list, looking for this layout connectivity
        for (int i = 0; i < cList.size(); i++) {
            LayoutConnectivity lc = cList.get(i);
            // compare input LayoutConnectivity with LayoutConnectivity from the list
            if (xOver == null) {
                // not a crossover block boundary
                if ((blk1 == lc.getBlock1()) && (blk2 == lc.getBlock2()) && (track == lc.getTrackSegment())
                        && (connected == lc.getConnectedObject()) && (type == lc.getConnectedType())
                        && (dir == lc.getDirection())) {
                    found[i] = true;
                    break;
                }
            } else {
                // boundary is in a crossover turnout
                if ((xOver == lc.getXover()) && (xOverType == lc.getXoverBoundaryType())) {
                    if ((blk1 == lc.getBlock1()) && (blk2 == lc.getBlock2()) && (dir == lc.getDirection())) {
                        found[i] = true;
                        break;
                    } else if ((blk2 == lc.getBlock1()) && (blk1 == lc.getBlock2()) && (rDir == lc.getDirection())) {
                        found[i] = true;
                        break;
                    }
                }
            }
        }

        // Check to see if this connectivity is already in the list
        // This occurs for the first layout editor panel when there
        // are multiple panels connected by edge connectors.
        if (cList.contains(c)) {
            log.debug("checkConnectivity: Duplicate connection: '{}'", c);  // NOI18N
        } else {
            cList.add(c);
        }
    }   // checkConnectivity

    /**
     * Searches for and adds BeanSetting's to a Path as needed.
     * <p>
     * This method starts at the entry point to the LayoutBlock given in the
     * Path at the block boundary specified in the LayoutConnectivity. It
     * follows the track looking for turnout settings that are required for a
     * train entering on this block boundary point to exit the block. If a
     * required turnout setting is found, the turnout and its required state are
     * used to create a BeanSetting, which is added to the Path. Such a setting
     * can occur, for example, if a track enters a right-handed turnout from
     * either the diverging track or the continuing track.
     * <p>
     * If the track branches into two tracks (for example, by entering a
     * right-handed turnout via the throat track), the search is stopped. The
     * search is also stopped when the track reaches a different block (or an
     * undefined block), or reaches an end bumper.
     * @param p path to follow until branch.
     * @param lc layout connectivity.
     * @param layoutBlock the layout block.
     */
    public void addBeanSettings(Path p, LayoutConnectivity lc, LayoutBlock layoutBlock) {
        p.clearSettings();
        LayoutTrack curConnection = null;
        LayoutTrack prevConnection = null;
        HitPointType typeCurConnection = HitPointType.NONE;
        BeanSetting bs = null;
        LayoutTurnout lt = null;
        // process track at block boundary
        if (lc.getBlock1() == layoutBlock) {    // block1 is this LayoutBlock
            curConnection = lc.getTrackSegment();
            if (curConnection != null) {        // connected track in this block is a track segment
                prevConnection = lc.getConnectedObject();
                typeCurConnection = HitPointType.TRACK;
                // is this Track Segment connected to a RH, LH, or WYE turnout at the continuing or diverging track?
                if ((lc.getConnectedType() == HitPointType.TURNOUT_B
                        || lc.getConnectedType() == HitPointType.TURNOUT_C)
                        && ((LayoutTurnout) prevConnection).getTurnoutType() != LayoutTurnout.TurnoutType.NONE
                        && LayoutTurnout.hasEnteringSingleTrack(((LayoutTurnout) prevConnection).getTurnoutType())) {
                    LayoutTurnout ltx = (LayoutTurnout) prevConnection;
                    // Track Segment connected to continuing track of turnout?
                    if (lc.getConnectedType() == HitPointType.TURNOUT_B) {
                        Turnout ltxto = ltx.getTurnout();
                        if ( ltxto != null) {
                            bs = new BeanSetting(ltxto, ltx.getTurnoutName(), ltx.getContinuingSense());
                            p.addSetting(bs);
                        } else {
                            log.error("No assigned turnout (A): LTO = {}, blk = {}", ltx.getName(), ltx.getLayoutBlock().getDisplayName());  // NOI18N
                        }
                    } else if (lc.getConnectedType() == HitPointType.TURNOUT_C) {
                        // is Track Segment connected to diverging track of turnout?
                        Turnout ltxto = ltx.getTurnout();
                        if (ltxto != null) {
                            if (ltx.getContinuingSense() == Turnout.CLOSED) {
                                bs = new BeanSetting(ltxto, ltx.getTurnoutName(), Turnout.THROWN);
                            } else {
                                bs = new BeanSetting(ltxto, ltx.getTurnoutName(), Turnout.CLOSED);
                            }
                            p.addSetting(bs);
                        } else {
                            log.error("No assigned turnout (B): LTO = {}, blk = {}", ltx.getName(), ltx.getLayoutBlock().getDisplayName());  // NOI18N
                        }
                    } else {
                        log.warn("Did not decode lc.getConnectedType() of {}", lc.getConnectedType());  // NOI18N
                    }
                } // is this Track Segment connected to the continuing track of a RH_XOVER or LH_XOVER?
                else if (HitPointType.isTurnoutHitType(lc.getConnectedType())
                        && ((((LayoutTurnout) prevConnection).getTurnoutType() == LayoutTurnout.TurnoutType.RH_XOVER)
                        || (((LayoutTurnout) prevConnection).getTurnoutType() == LayoutTurnout.TurnoutType.LH_XOVER))) {
                    LayoutTurnout ltz = (LayoutTurnout) prevConnection;
                    if (((ltz.getTurnoutType() == LayoutTurnout.TurnoutType.RH_XOVER)
                            && ((lc.getConnectedType() == HitPointType.TURNOUT_B)
                            || (lc.getConnectedType() == HitPointType.TURNOUT_D)))
                            || ((ltz.getTurnoutType() == LayoutTurnout.TurnoutType.LH_XOVER)
                            && ((lc.getConnectedType() == HitPointType.TURNOUT_A)
                            || (lc.getConnectedType() == HitPointType.TURNOUT_C)))) {

                        Turnout ltzto = ltz.getTurnout();
                        if (ltzto != null) {
                            bs = new BeanSetting(ltzto, ltz.getTurnoutName(), Turnout.CLOSED);
                            p.addSetting(bs);
                        } else {
                            log.error("No assigned turnout (C): LTO = {}, blk = {}, TO type = {}, conn type = {}", // NOI18N
                                    ltz.getName(), ltz.getLayoutBlock().getDisplayName(), ltz.getTurnoutType(), lc.getConnectedType());
                        }
                    }
                } // is this track section is connected to a slip?
                else if (HitPointType.isSlipHitType(lc.getConnectedType())) {
                    LayoutSlip lsz = (LayoutSlip) prevConnection;
                    if (lsz.getSlipType() == LayoutSlip.TurnoutType.SINGLE_SLIP) {
                        if (lc.getConnectedType() == HitPointType.SLIP_C) {
                            Turnout lszto = lsz.getTurnout();
                            if (lszto != null) {
                                bs = new BeanSetting(lszto, lsz.getTurnoutName(), lsz.getTurnoutState(LayoutTurnout.STATE_AC));
                                p.addSetting(bs);
                            } else {
                                log.error("No assigned turnout (D): LTO = {}, blk = {}", lsz.getName(), lsz.getLayoutBlock().getDisplayName());  // NOI18N
                            }
                            Turnout lsztob = lsz.getTurnoutB();
                            if (lsztob != null) {
                                bs = new BeanSetting(lsztob, lsz.getTurnoutBName(), lsz.getTurnoutBState(LayoutTurnout.STATE_AC));
                                p.addSetting(bs);
                            } else {
                                log.error("No assigned turnoutB (E): LTO = {}, blk = {}", lsz.getName(), lsz.getLayoutBlock().getDisplayName());  // NOI18N
                            }
                        } else if (lc.getConnectedType() == HitPointType.SLIP_B) {
                            Turnout lszto = lsz.getTurnout();
                            if (lszto != null) {
                                bs = new BeanSetting(lszto, lsz.getTurnoutName(), lsz.getTurnoutState(LayoutTurnout.STATE_BD));
                                p.addSetting(bs);
                            } else {
                                log.error("No assigned turnout (F): LTO = {}, blk = {}", lsz.getName(), lsz.getLayoutBlock().getDisplayName());  // NOI18N
                            }

                            Turnout lsztob = lsz.getTurnoutB();
                            if (lsztob != null) {
                                bs = new BeanSetting(lsztob, lsz.getTurnoutBName(), lsz.getTurnoutBState(LayoutTurnout.STATE_BD));
                                p.addSetting(bs);
                            } else {
                                log.error("No assigned turnoutB (G): LTO = {}, blk = {}", lsz.getName(), lsz.getLayoutBlock().getDisplayName());  // NOI18N
                            }
                        } else if (lc.getConnectedType() == HitPointType.SLIP_A) {
                            log.debug("At connection A of a single slip which could go in two different directions");  // NOI18N
                        } else if (lc.getConnectedType() == HitPointType.SLIP_D) {
                            log.debug("At connection D of a single slip which could go in two different directions");  // NOI18N
                        }
                    } else {
                        //note: I'm adding these logs as a prequel to adding the correct code for double slips
                        if (lc.getConnectedType() == HitPointType.SLIP_A) {
                            log.debug("At connection A of a double slip which could go in two different directions");  // NOI18N
                        } else if (lc.getConnectedType() == HitPointType.SLIP_B) {
                            log.debug("At connection B of a double slip which could go in two different directions");  // NOI18N
                        } else if (lc.getConnectedType() == HitPointType.SLIP_C) {
                            log.debug("At connection C of a double slip which could go in two different directions");  // NOI18N
                        } else if (lc.getConnectedType() == HitPointType.SLIP_D) {
                            log.debug("At connection D of a double slip which could go in two different directions");  // NOI18N
                        } else {    // this should NEVER happen (it should always be SLIP_A, _B, _C or _D.
                            log.info("At a double slip we could go in two different directions");  // NOI18N
                        }
                    }
                }
            } else {
                // block boundary is internal to a crossover turnout
                lt = lc.getXover();
                prevConnection = lt;
                if ((lt != null) && (lt.getTurnout() != null)) {
                    int type = lc.getXoverBoundaryType();
                    // bs is known to be null at this point
                    if (lt.getTurnout() != null) {
                        if (type == LayoutConnectivity.XOVER_BOUNDARY_AB) {
                            bs = new BeanSetting(lt.getTurnout(), lt.getTurnoutName(), Turnout.CLOSED);
                            curConnection = lt.getConnectA();
                        } else if (type == LayoutConnectivity.XOVER_BOUNDARY_CD) {
                            bs = new BeanSetting(lt.getTurnout(), lt.getTurnoutName(), Turnout.CLOSED);
                            curConnection = lt.getConnectC();
                        } else if (type == LayoutConnectivity.XOVER_BOUNDARY_AC) {
                            bs = new BeanSetting(lt.getTurnout(), lt.getTurnoutName(), Turnout.THROWN);
                            curConnection = lt.getConnectA();
                        } else if (type == LayoutConnectivity.XOVER_BOUNDARY_BD) {
                            bs = new BeanSetting(lt.getTurnout(), lt.getTurnoutName(), Turnout.THROWN);
                            curConnection = lt.getConnectB();
                        } else {
                            log.warn("failed to decode lc.getXoverBoundaryType() of {} (A)", lc.getXoverBoundaryType());  // NOI18N
                        }
                    }
                    typeCurConnection = HitPointType.TRACK;
                    if (bs != null) {
                        p.addSetting(bs);
                    } else {
                        log.error("No assigned turnout (H): LTO = {}, blk = {}, type = {}", lt.getName(), lt.getLayoutBlock().getDisplayName(), type);  // NOI18N
                    }
                }
            }
        } else if (lc.getXover() != null) {
            // first Block is not in a Track Segment, must be block boundary internal to a crossover turnout
            lt = lc.getXover();
            if ((lt != null) && (lt.getTurnout() != null)) {
                int type = lc.getXoverBoundaryType();
                // bs is known to be null at this point
                if (lt.getTurnout() != null) {
                    if (type == LayoutConnectivity.XOVER_BOUNDARY_AB) {
                        bs = new BeanSetting(lt.getTurnout(), lt.getTurnoutName(), Turnout.CLOSED);
                        curConnection = lt.getConnectB();
                    } else if (type == LayoutConnectivity.XOVER_BOUNDARY_CD) {
                        bs = new BeanSetting(lt.getTurnout(), lt.getTurnoutName(), Turnout.CLOSED);
                        curConnection = lt.getConnectD();
                    } else if (type == LayoutConnectivity.XOVER_BOUNDARY_AC) {
                        bs = new BeanSetting(lt.getTurnout(), lt.getTurnoutName(), Turnout.THROWN);
                        curConnection = lt.getConnectC();
                    } else if (type == LayoutConnectivity.XOVER_BOUNDARY_BD) {
                        bs = new BeanSetting(lt.getTurnout(), lt.getTurnoutName(), Turnout.THROWN);
                        curConnection = lt.getConnectD();
                    } else {
                        log.warn("failed to decode lc.getXoverBoundaryType() of {} (B)", lc.getXoverBoundaryType());  // NOI18N
                    }
                }
                typeCurConnection = HitPointType.TRACK;
                if (bs != null) {
                    p.addSetting(bs);
                } else {
                    log.error("No assigned turnout (I): LTO = {}, blk = {}, type = {}", lt.getName(), lt.getLayoutBlock().getDisplayName(), type);  // NOI18N
                }
            }
        } else {
            // block2 is this LayoutBlock, and block1 is in a track segment
            if (lc.getConnectedObject() != null) {
                // connected object in this block is a turnout or levelxing
                curConnection = lc.getConnectedObject();
                prevConnection = lc.getTrackSegment();
                typeCurConnection = lc.getConnectedType();
                if (HitPointType.isTurnoutHitType(typeCurConnection)) {
                    // connected object is a turnout
                    LayoutTurnout.TurnoutType turnoutType = ((LayoutTurnout) curConnection).getTurnoutType();
                    if (LayoutTurnout.hasEnteringDoubleTrack(turnoutType)) {
                        // have crossover turnout
                        if ((turnoutType == LayoutTurnout.TurnoutType.DOUBLE_XOVER)
                                || ((turnoutType == LayoutTurnout.TurnoutType.RH_XOVER) && ((typeCurConnection == HitPointType.TURNOUT_A) || (typeCurConnection == HitPointType.TURNOUT_C)))
                                || ((turnoutType == LayoutTurnout.TurnoutType.LH_XOVER) && ((typeCurConnection == HitPointType.TURNOUT_B) || (typeCurConnection == HitPointType.TURNOUT_D)))) {
                            // entering turnout at a throat, cannot follow path any further
                            curConnection = null;
                        } else {
                            // entering turnout at continuing track
                            if (((LayoutTurnout) curConnection).getTurnout() != null) {
                                bs = new BeanSetting(((LayoutTurnout) curConnection).getTurnout(), ((LayoutTurnout) curConnection).getTurnoutName(), Turnout.CLOSED);
                                p.addSetting(bs);
                            } else {
                                log.error("No assigned turnout (J): LTO = {}, blk = {}", // NOI18N
                                        ((LayoutTurnout) curConnection).getName(), ((LayoutTurnout) curConnection).getLayoutBlock().getDisplayName());
                            }
                            prevConnection = curConnection;
                            if (typeCurConnection == HitPointType.TURNOUT_A) {
                                curConnection = ((LayoutTurnout) curConnection).getConnectB();
                            } else if (typeCurConnection == HitPointType.TURNOUT_B) {
                                curConnection = ((LayoutTurnout) curConnection).getConnectA();
                            } else if (typeCurConnection == HitPointType.TURNOUT_C) {
                                curConnection = ((LayoutTurnout) curConnection).getConnectD();
                            } else { // typeCurConnection == LayoutEditor.HitPointTypes.TURNOUT_D per if statement 3 levels up
                                curConnection = ((LayoutTurnout) curConnection).getConnectC();
                            }
                            typeCurConnection = HitPointType.TRACK;
                        }
                    } // must be RH, LH, or WYE turnout
                    else if (typeCurConnection == HitPointType.TURNOUT_A) {
                        // turnout throat, no bean setting needed and cannot follow Path any further
                        log.debug("At connection A of a turnout which could go in two different directions");  // NOI18N
                        curConnection = null;
                    } else if (typeCurConnection == HitPointType.TURNOUT_B) {
                        // continuing track of turnout
                        if (((LayoutTurnout) curConnection).getTurnout() != null) {
                            if (((LayoutTurnout) curConnection).getContinuingSense() == Turnout.CLOSED) {
                                bs = new BeanSetting(((LayoutTurnout) curConnection).getTurnout(), ((LayoutTurnout) curConnection).getTurnoutName(), Turnout.CLOSED);
                            } else {
                                bs = new BeanSetting(((LayoutTurnout) curConnection).getTurnout(), ((LayoutTurnout) curConnection).getTurnoutName(), Turnout.THROWN);
                            }
                            p.addSetting(bs);
                        } else {
                            log.error("No assigned turnout (K): LTO = {}, blk = {}", // NOI18N
                                    ((LayoutTurnout) curConnection).getName(), ((LayoutTurnout) curConnection).getLayoutBlock().getDisplayName());
                        }
                        prevConnection = curConnection;
                        curConnection = ((LayoutTurnout) curConnection).getConnectA();
                        typeCurConnection = HitPointType.TRACK;
                    } else if (typeCurConnection == HitPointType.TURNOUT_C) {
                        // diverging track of turnout
                        if (((LayoutTurnout) curConnection).getTurnout() != null) {
                            if (((LayoutTurnout) curConnection).getContinuingSense() == Turnout.CLOSED) {
                                bs = new BeanSetting(((LayoutTurnout) curConnection).getTurnout(), ((LayoutTurnout) curConnection).getTurnoutName(), Turnout.THROWN);
                            } else {
                                bs = new BeanSetting(((LayoutTurnout) curConnection).getTurnout(), ((LayoutTurnout) curConnection).getTurnoutName(), Turnout.CLOSED);
                            }
                            p.addSetting(bs);
                        } else {
                            log.error("No assigned turnout (L): LTO = {}, blk = {}", // NOI18N
                                    ((LayoutTurnout) curConnection).getName(), ((LayoutTurnout) curConnection).getLayoutBlock().getDisplayName());
                        }
                        prevConnection = curConnection;
                        curConnection = ((LayoutTurnout) curConnection).getConnectA();
                        typeCurConnection = HitPointType.TRACK;
                    }
                } // if level crossing, skip to the connected track segment on opposite side
                else if (typeCurConnection == HitPointType.LEVEL_XING_A) {
                    prevConnection = curConnection;
                    curConnection = ((LevelXing) curConnection).getConnectC();
                    typeCurConnection = HitPointType.TRACK;
                } else if (typeCurConnection == HitPointType.LEVEL_XING_C) {
                    prevConnection = curConnection;
                    curConnection = ((LevelXing) curConnection).getConnectA();
                    typeCurConnection = HitPointType.TRACK;
                } else if (typeCurConnection == HitPointType.LEVEL_XING_B) {
                    prevConnection = curConnection;
                    curConnection = ((LevelXing) curConnection).getConnectD();
                    typeCurConnection = HitPointType.TRACK;
                } else if (typeCurConnection == HitPointType.LEVEL_XING_D) {
                    prevConnection = curConnection;
                    curConnection = ((LevelXing) curConnection).getConnectB();
                    typeCurConnection = HitPointType.TRACK;
                }
            } else {
                // block boundary is internal to a crossover turnout
                lt = lc.getXover();
                prevConnection = lt;
                if ((lt != null) && (lt.getTurnout() != null)) {
                    int type = lc.getXoverBoundaryType();
                    // bs is known to be null at this point
                    if (lt.getTurnout() != null) {
                        if (type == LayoutConnectivity.XOVER_BOUNDARY_AB) {
                            bs = new BeanSetting(lt.getTurnout(), lt.getTurnoutName(), Turnout.CLOSED);
                            curConnection = lt.getConnectB();
                        } else if (type == LayoutConnectivity.XOVER_BOUNDARY_CD) {
                            bs = new BeanSetting(lt.getTurnout(), lt.getTurnoutName(), Turnout.CLOSED);
                            curConnection = lt.getConnectD();
                        } else if (type == LayoutConnectivity.XOVER_BOUNDARY_AC) {
                            bs = new BeanSetting(lt.getTurnout(), lt.getTurnoutName(), Turnout.THROWN);
                            curConnection = lt.getConnectC();
                        } else if (type == LayoutConnectivity.XOVER_BOUNDARY_BD) {
                            bs = new BeanSetting(lt.getTurnout(), lt.getTurnoutName(), Turnout.THROWN);
                            curConnection = lt.getConnectD();
                        }
                    }
                    typeCurConnection = HitPointType.TRACK;
                    if (bs != null) {
                        p.addSetting(bs);
                    } else {
                        log.error("No assigned turnout (Q): LTO = {}, blk = {}", lt.getName(), lt.getLayoutBlock().getDisplayName());  // NOI18N
                    }
                }
            }
        }
        // follow path through this block - done when reaching another block, or a branching of Path
        while (curConnection != null) {
            if (typeCurConnection == HitPointType.TRACK) {
                TrackSegment curTS = (TrackSegment) curConnection;
                // track segment is current connection
                if (curTS.getLayoutBlock() != layoutBlock) {
                    curConnection = null;
                } else {
                    // skip over to other end of Track Segment
                    if (curTS.getConnect1() == prevConnection) {
                        prevConnection = curConnection;
                        typeCurConnection = curTS.getType2();
                        curConnection = curTS.getConnect2();
                    } else {
                        prevConnection = curConnection;
                        typeCurConnection = curTS.getType1();
                        curConnection = curTS.getConnect1();
                    }
                    // skip further if positionable point (possible anchor point)
                    if (typeCurConnection == HitPointType.POS_POINT) {
                        PositionablePoint pt = (PositionablePoint) curConnection;
                        if (pt.getType() == PositionablePoint.PointType.END_BUMPER) {
                            // reached end of track
                            curConnection = null;
                        } else {
                            // at an anchor point, find track segment on other side
                            TrackSegment track = null;
                            if (pt.getConnect1() == prevConnection) {
                                track = pt.getConnect2();
                            } else {
                                track = pt.getConnect1();
                            }
                            // check for block boundary
                            if ((track == null) || (track.getLayoutBlock() != layoutBlock)) {
                                // moved outside of block - anchor point was a block boundary -OR-
                                //  reached the end of the defined track
                                curConnection = null;
                            } else {
                                prevConnection = curConnection;
                                curConnection = track;
                                typeCurConnection = HitPointType.TRACK;
                            }
                        }
                    }
                }
            } else if (HitPointType.isTurnoutHitType(typeCurConnection)) {
                lt = (LayoutTurnout) curConnection;
                // test for crossover turnout
                if (lt.hasEnteringSingleTrack()) {
                    // have RH, LH, or WYE turnout

                    if (lt.getLayoutBlock() != layoutBlock) {
                        curConnection = null;
                    } else {
                        // turnout is in current block, test connection point
                        if (typeCurConnection == HitPointType.TURNOUT_A) {
                            // turnout throat, no bean setting needed and cannot follow possible path any further
                            curConnection = null;
                        } else if (typeCurConnection == HitPointType.TURNOUT_B) {
                            // continuing track of turnout, add a bean setting
                            if (lt.getTurnout() != null) {
                                if (lt.getContinuingSense() == Turnout.CLOSED) {
                                    bs = new BeanSetting(lt.getTurnout(), lt.getTurnoutName(), Turnout.CLOSED);
                                } else {
                                    bs = new BeanSetting(lt.getTurnout(), lt.getTurnoutName(), Turnout.THROWN);
                                }
                                p.addSetting(bs);
                            } else {
                                log.error("No assigned turnout (R): LTO = {}, blk = {}", lt.getName(), lt.getLayoutBlock().getDisplayName());  // NOI18N
                            }
                            if (lt.getLayoutBlock() != layoutBlock) {
                                curConnection = null;
                            } else {
                                prevConnection = curConnection;
                                curConnection = lt.getConnectA();
                                typeCurConnection = HitPointType.TRACK;
                            }
                        } else if (typeCurConnection == HitPointType.TURNOUT_C) {
                            // diverging track of turnout
                            if (lt.getTurnout() != null) {
                                if (lt.getContinuingSense() == Turnout.CLOSED) {
                                    bs = new BeanSetting(lt.getTurnout(), lt.getTurnoutName(), Turnout.THROWN);
                                } else {
                                    bs = new BeanSetting(lt.getTurnout(), lt.getTurnoutName(), Turnout.CLOSED);
                                }
                                p.addSetting(bs);
                            } else {
                                log.error("No assigned turnout (S): LTO = {}, blk = {}", lt.getName(), lt.getLayoutBlock().getDisplayName());  // NOI18N
                            }
                            if (lt.getLayoutBlock() != layoutBlock) {
                                curConnection = null;
                            } else {
                                prevConnection = curConnection;
                                curConnection = lt.getConnectA();
                                typeCurConnection = HitPointType.TRACK;
                            }
                        }
                    }
                } else if (lt.getTurnoutType() == LayoutTurnout.TurnoutType.DOUBLE_XOVER) {
                    // have a double crossover turnout, cannot follow possible path any further
                    curConnection = null;
                } else if (lt.getTurnoutType() == LayoutTurnout.TurnoutType.RH_XOVER) {
                    // have a right-handed crossover turnout
                    if ((typeCurConnection == HitPointType.TURNOUT_A)
                            || (typeCurConnection == HitPointType.TURNOUT_C)) {
                        // entry is at turnout throat, cannot follow possible path any further
                        curConnection = null;
                    } else if (typeCurConnection == HitPointType.TURNOUT_B) {
                        // entry is at continuing track of turnout
                        if (lt.getLayoutBlockB() != layoutBlock) {
                            // cross-over block different, end of current block
                            break;
                        }
                        if (lt.getTurnout() != null) {
                            bs = new BeanSetting(lt.getTurnout(), lt.getTurnoutName(), Turnout.CLOSED);
                            p.addSetting(bs);
                        } else {
                            log.error("No assigned turnout (T): LTO = {}, blk = {}", lt.getName(), lt.getLayoutBlock().getDisplayName());  // NOI18N
                        }
                        if (lt.getLayoutBlock() != layoutBlock) {
                            // left current block
                            curConnection = null;
                        } else {
                            prevConnection = curConnection;
                            curConnection = lt.getConnectA();
                            typeCurConnection = HitPointType.TRACK;
                        }
                    } else { // typeCurConnection == LayoutEditor.HitPointTypes.TURNOUT_D
                        // entry is at continuing track of turnout
                        if (lt.getLayoutBlockD() != layoutBlock) {
                            // cross-over block different, end of current block
                            break;
                        }
                        if (lt.getTurnout() != null) {
                            bs = new BeanSetting(lt.getTurnout(), lt.getTurnoutName(), Turnout.CLOSED);
                            p.addSetting(bs);
                        } else {
                            log.error("No assigned turnout (U): LTO = {}, blk = {}", lt.getName(), lt.getLayoutBlock().getDisplayName());  // NOI18N
                        }
                        if (lt.getLayoutBlockC() != layoutBlock) {
                            // left current block
                            curConnection = null;
                        } else {
                            prevConnection = curConnection;
                            curConnection = lt.getConnectC();
                            typeCurConnection = HitPointType.TRACK;
                        }
                    }
                } else if (lt.getTurnoutType() == LayoutTurnout.TurnoutType.LH_XOVER) {
                    // have a left-handed crossover turnout
                    if ((typeCurConnection == HitPointType.TURNOUT_B)
                            || (typeCurConnection == HitPointType.TURNOUT_D)) {
                        // entry is at turnout throat, cannot follow possible path any further
                        curConnection = null;
                    } else if (typeCurConnection == HitPointType.TURNOUT_A) {
                        // entry is at continuing track of turnout
                        if (lt.getLayoutBlock() != layoutBlock) {
                            // cross-over block different, end of current block
                            break;
                        }
                        if (lt.getTurnout() != null) {
                            bs = new BeanSetting(lt.getTurnout(), lt.getTurnoutName(), Turnout.CLOSED);
                            p.addSetting(bs);
                        } else {
                            log.error("No assigned turnout (V): LTO = {}, blk = {}", lt.getName(), lt.getLayoutBlock().getDisplayName());  // NOI18N
                        }
                        if (lt.getLayoutBlockB() != layoutBlock) {
                            // left current block
                            curConnection = null;
                        } else {
                            prevConnection = curConnection;
                            curConnection = lt.getConnectB();
                            typeCurConnection = HitPointType.TRACK;
                        }
                    } else { // typeCurConnection == LayoutEditor.HitPointTypes.TURNOUT_C per if statement 2 levels up
                        // entry is at continuing track of turnout
                        if (lt.getLayoutBlockC() != layoutBlock) {
                            // cross-over block different, end of current block
                            break;
                        }
                        if (lt.getTurnout() != null) {
                            bs = new BeanSetting(lt.getTurnout(), lt.getTurnoutName(), Turnout.CLOSED);
                            p.addSetting(bs);
                        } else {
                            log.error("No assigned turnout (W): LTO = {}, blk = {}", lt.getName(), lt.getLayoutBlock().getDisplayName());  // NOI18N
                        }
                        if (lt.getLayoutBlockD() != layoutBlock) {
                            // left current block
                            curConnection = null;
                        } else {
                            prevConnection = curConnection;
                            curConnection = lt.getConnectD();
                            typeCurConnection = HitPointType.TRACK;
                        }
                    }
                }
            } else if (typeCurConnection == HitPointType.LEVEL_XING_A) {
                // have a level crossing connected at A
                if (((LevelXing) curConnection).getLayoutBlockAC() != layoutBlock) {
                    // moved outside of this block
                    curConnection = null;
                } else {
                    // move to other end of this section of this level crossing track
                    prevConnection = curConnection;
                    curConnection = ((LevelXing) curConnection).getConnectC();
                    typeCurConnection = HitPointType.TRACK;
                }
            } else if (typeCurConnection == HitPointType.LEVEL_XING_B) {
                // have a level crossing connected at B
                if (((LevelXing) curConnection).getLayoutBlockBD() != layoutBlock) {
                    // moved outside of this block
                    curConnection = null;
                } else {
                    // move to other end of this section of this level crossing track
                    prevConnection = curConnection;
                    curConnection = ((LevelXing) curConnection).getConnectD();
                    typeCurConnection = HitPointType.TRACK;
                }
            } else if (typeCurConnection == HitPointType.LEVEL_XING_C) {
                // have a level crossing connected at C
                if (((LevelXing) curConnection).getLayoutBlockAC() != layoutBlock) {
                    // moved outside of this block
                    curConnection = null;
                } else {
                    // move to other end of this section of this level crossing track
                    prevConnection = curConnection;
                    curConnection = ((LevelXing) curConnection).getConnectA();
                    typeCurConnection = HitPointType.TRACK;
                }
            } else if (typeCurConnection == HitPointType.LEVEL_XING_D) {
                // have a level crossing connected at D
                if (((LevelXing) curConnection).getLayoutBlockBD() != layoutBlock) {
                    // moved outside of this block
                    curConnection = null;
                } else {
                    // move to other end of this section of this level crossing track
                    prevConnection = curConnection;
                    curConnection = ((LevelXing) curConnection).getConnectB();
                    typeCurConnection = HitPointType.TRACK;
                }
            } else if (HitPointType.isSlipHitType(typeCurConnection)) {
                LayoutSlip ls = (LayoutSlip) curConnection;
                if (ls.getLayoutBlock() != layoutBlock) {
                    curConnection = null;
                } else if (ls.getSlipType() == LayoutSlip.TurnoutType.SINGLE_SLIP) {
                    if (typeCurConnection == HitPointType.SLIP_C) {
                        if (ls.getTurnout() != null) {
                            bs = new BeanSetting(ls.getTurnout(), ls.getTurnoutName(), ls.getTurnoutState(LayoutTurnout.STATE_AC));
                            p.addSetting(bs);
                        } else {
                            log.error("No assigned turnout (X): LTO = {}, blk = {}", ls.getName(), ls.getLayoutBlock().getDisplayName());  // NOI18N
                        }
                        if (ls.getTurnoutB() != null) {
                            bs = new BeanSetting(ls.getTurnoutB(), ls.getTurnoutBName(), ls.getTurnoutBState(LayoutTurnout.STATE_AC));
                            p.addSetting(bs);
                        } else {
                            log.error("No assigned turnoutB (Y): LTO = {}, blk = {}", ls.getName(), ls.getLayoutBlock().getDisplayName());  // NOI18N
                        }
                        prevConnection = curConnection;
                        curConnection = ((LayoutSlip) curConnection).getConnectC();
                        typeCurConnection = HitPointType.TRACK;
                    } else if (typeCurConnection == HitPointType.SLIP_B) {
                        if (ls.getTurnout() != null) {
                            bs = new BeanSetting(ls.getTurnout(), ls.getTurnoutName(), ls.getTurnoutState(LayoutTurnout.STATE_BD));
                            p.addSetting(bs);
                        } else {
                            log.error("No assigned turnout (Z): LTO = {}, blk = {}", ls.getName(), ls.getLayoutBlock().getDisplayName());  // NOI18N
                        }

                        if (ls.getTurnoutB() != null) {
                            bs = new BeanSetting(ls.getTurnoutB(), ls.getTurnoutBName(), ls.getTurnoutBState(LayoutTurnout.STATE_BD));
                            p.addSetting(bs);
                        } else {
                            log.error("No assigned turnoutB (1): LTO = {}, blk = {}", ls.getName(), ls.getLayoutBlock().getDisplayName());  // NOI18N
                        }
                        prevConnection = curConnection;
                        curConnection = ((LayoutSlip) curConnection).getConnectB();
                        typeCurConnection = HitPointType.TRACK;
                    } else {
                        //Else could be going in the slip direction
                        curConnection = null;
                    }

                } else {
                    //At double slip, can not follow any further
                    curConnection = null;
                }
            } else if (HitPointType.isTurntableRayHitType(typeCurConnection)) {
                if (log.isDebugEnabled()) {
                    log.debug("Layout Block: {}, found track type: {}, to " // NOI18N
                            + "Block: {}, is potentially assigned to turntable ray", // NOI18N
                            layoutBlock.getDisplayName(),
                            typeCurConnection,
                            p.getBlock().getDisplayName()
                    );
                }
                curConnection = null;
            } else {
                // catch when some new type got added
                log.error("Layout Block: {} found unknown track type: {}" // NOI18N
                        + " to Block: {}",
                        layoutBlock.getDisplayName(),
                        typeCurConnection,
                        p.getBlock().getDisplayName()
                );
                break;
            }
        }
    }   // addBeanSettings

    // initialize logging
    private final static Logger log
            = LoggerFactory.getLogger(LayoutEditorAuxTools.class);
}