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