001package jmri.jmrit.display.layoutEditor;
002
003import jmri.Path;
004import org.slf4j.Logger;
005import org.slf4j.LoggerFactory;
006
007/**
008 * A LayoutConnectivity object represents a junction between two LayoutBlocks on
009 * a LayoutEditor panel.
010 * <p>
011 * LayoutConnectivity objects do not persist (are not saved when panels are
012 * saved). Instead they are initialized when a panel is loaded and changed
013 * dynamically as a panel is edited.
014 * <p>
015 * The direction stored here is the direction for proceeding across the block
016 * boundary from block1 to block2. The directions represent directions on the
017 * LayoutEditor panel. Allowed values (using Path object definitions) are:
018 * Path.NORTH (up on panel) Path.SOUTH (down on panel) Path.EAST (right on
019 * panel) Path.WEST (left on panel) and points in between: Path.NORTH +
020 * Path.EAST Path.NORTH_WEST, Path.SOUTH_EAST Path.SOUTH + Path.WEST
021 * <p>
022 * The connected object in the first block is usually a track segment. This
023 * track segment is connected to an object in the second block. The connection
024 * point in the second block can be either one end of a track segment, or one of
025 * the connection points on a turnout, or one of the connection points on a
026 * level crossing. The allowed values for the connection points at the second
027 * block are defined in LayoutEditor.
028 * <p>
029 * The exception to the above is when a crossover turnout has multiple blocks.
030 * If so, at least one block boundary is internal to the crossover turnout. Such
031 * cases are handled differently, as "crossover block boundary types", see
032 * definition of the type codes below. The first letter in the boundary type
033 * corresponds to the first block, and the second letter corresponds to the
034 * second block. All four block boundaries are possible for the double
035 * crossover. One of the crossed over boundaries is not possible with each
036 * single crossover.
037 * <p>
038 * Note that each LayoutEditor panel has its own list of LayoutConnectivity
039 * objects, nominally called its "block connectivity". In contrast, there is
040 * only one set of LayoutBlocks, Blocks, and Paths, which are used by all
041 * LayoutEditor panels.
042 *
043 * @author Dave Duchamp Copyright (c) 2007-2008
044 * @author George Warner Copyright (c) 2017-2018
045 */
046public class LayoutConnectivity {
047
048    /**
049     * Constructor.
050     * @param b1 layout block 1.
051     * @param b2 layout block 2.
052     */
053    public LayoutConnectivity(LayoutBlock b1, LayoutBlock b2) {
054        block1 = b1;
055        if (block1 == null) {
056            log.error("null block1 when creating Layout Connectivity");
057        }
058        block2 = b2;
059        if (block2 == null) {
060            log.error("null block2 when creating Layout Connectivity");
061        }
062    }
063
064    // defined constants for crossover block boundary types.
065    final public static int NONE = 0;
066    final public static int XOVER_BOUNDARY_AB = 1;  // continuing
067    final public static int XOVER_BOUNDARY_CD = 2;  // continuing
068    final public static int XOVER_BOUNDARY_AC = 3;  // xed over
069    final public static int XOVER_BOUNDARY_BD = 4;  // xed over
070    final public static int XOVER_BOUNDARY_AD = 1;  // continuing (slips)
071    final public static int XOVER_BOUNDARY_BC = 2;  // continuing (slips)
072
073    // instance variables
074    private LayoutBlock block1 = null;
075    private LayoutBlock block2 = null;
076
077    private int direction = Path.NONE;
078    private TrackSegment track1 = null;
079
080    private LayoutTrack connect2 = null;
081    private HitPointType typeConnect2 = HitPointType.NONE;
082
083    private LayoutTurnout xover = null;
084    private int xoverBoundaryType = NONE;
085
086    private PositionablePoint anchor = null;
087
088    // this should only be used for debugging...
089    @Override
090    public String toString() {
091        String result = "between " + block1.getDisplayName() + " and " + block2.getDisplayName() + " in direction " + Path.decodeDirection(direction);
092        if (track1 != null) {
093            result = result + ", track: " + track1.getId();
094        }
095        if (connect2 != null) {
096            result = result + ", connect2: " + connect2.getId() + ", type2: " + typeConnect2;
097        }
098        if (xover != null) {
099            result = result + ", xover: " + xover.getId() + ", xoverBoundaryType: " + xoverBoundaryType;
100        }
101        return result;
102    }
103
104    /**
105     * Get Block 1.
106     * @return block 1, may be null.
107     */
108    public LayoutBlock getBlock1() {
109        return block1;
110    }
111
112    public LayoutBlock getBlock2() {
113        return block2;
114    }
115
116    public int getDirection() {
117        return direction;
118    }
119
120    public int getReverseDirection() {
121        return Path.reverseDirection(direction);
122    }
123
124    public boolean setDirection(int dir) {
125        if ((dir == Path.NORTH) || (dir == Path.SOUTH)
126                || (dir == Path.EAST) || (dir == Path.WEST)
127                || (dir == Path.NORTH_WEST) || (dir == (Path.NORTH_EAST))
128                || (dir == (Path.SOUTH_WEST)) || (dir == (Path.SOUTH_EAST))) {
129            direction = dir;
130            return (true);
131        }
132        // not one of the allowed directions
133        direction = Path.NONE;
134        return (false);
135    }
136
137    public void setConnections(TrackSegment t, LayoutTrack o, HitPointType type, PositionablePoint p) {
138        track1 = t;
139        if (t == null) {
140            log.error("null track1 when setting up LayoutConnectivity");
141        }
142        connect2 = o;
143        if (o == null) {
144            log.error("null connect track when setting up LayoutConnectivity");
145        }
146        typeConnect2 = type;
147        anchor = p;
148    }
149
150    public void setXoverBoundary(LayoutTurnout t, int type) {
151        xover = t;
152        if (t == null) {
153            log.error("null XOver when setting up LayoutConnectivity");
154        }
155        xoverBoundaryType = type;
156    }
157
158    public TrackSegment getTrackSegment() {
159        return track1;
160    }
161
162    public LayoutTrack getConnectedObject() {
163        return connect2;
164    }
165
166    public HitPointType getConnectedType() {
167        return typeConnect2;
168    }
169
170    public LayoutTurnout getXover() {
171        return xover;
172    }
173
174    public int getXoverBoundaryType() {
175        return xoverBoundaryType;
176    }
177
178    public PositionablePoint getAnchor() {
179        return anchor;
180    }
181
182    @Override
183    public boolean equals(Object o) {
184        boolean result = false; // assume failure (pessimist!)
185        if (o instanceof LayoutConnectivity) {
186            LayoutConnectivity lc = (LayoutConnectivity) o;
187            do {    // poor mans throw block
188                if (((block1 == null) != (lc.getBlock1() == null))
189                        || ((block1 != null) && !block1.equals(lc.getBlock1()))) {
190                    break;
191                }
192                if (((block2 == null) != (lc.getBlock2() == null))
193                        || ((block2 != null) && !block2.equals(lc.getBlock2()))) {
194                    break;
195                }
196                if (direction != lc.getDirection()) {
197                    break;
198                }
199                if (((track1 == null) != (lc.getTrackSegment() == null))
200                        || ((track1 != null) && !track1.equals(lc.getTrackSegment()))) {
201                    break;
202                }
203                if (((connect2 == null) != (lc.getConnectedObject() == null))
204                        || ((connect2 != null) && !connect2.equals(lc.getConnectedObject()))) {
205                    break;
206                }
207                if (typeConnect2 != lc.getConnectedType()) {
208                    break;
209                }
210                if (((xover == null) != (lc.getXover() == null))
211                        || ((xover != null) && !xover.equals(lc.getXover()))) {
212                    break;
213                }
214                if (((anchor == null) != (lc.getAnchor() == null))
215                        || ((anchor != null) && !anchor.equals(lc.getAnchor()))) {
216                    break;
217                }
218                result = true;
219            } while (false);
220        }
221        return result;
222    }
223
224    @Override
225    public int hashCode() {
226        int hash = 7;
227        hash = 37 * hash + (this.block1 != null ? this.block1.hashCode() : 0);
228        hash = 37 * hash + (this.block2 != null ? this.block2.hashCode() : 0);
229        hash = 37 * hash + direction;
230        hash = 37 * hash + (this.track1 != null ? this.track1.hashCode() : 0);
231        hash = 37 * hash + (this.connect2 != null ? this.connect2.hashCode() : 0);
232        hash = 37 * hash + typeConnect2.hashCode();
233        hash = 37 * hash + (this.xover != null ? this.xover.hashCode() : 0);
234        hash = 37 * hash + (this.anchor != null ? this.anchor.hashCode() : 0);
235        return hash;
236    }
237
238    private final static Logger log
239            = LoggerFactory.getLogger(LayoutConnectivity.class);
240}   // class LayoutConnectivity