001package jmri.jmrit.display.layoutEditor; 002 003import java.util.*; 004 005import javax.annotation.Nonnull; 006 007import jmri.JmriException; 008import jmri.Turnout; 009 010/** 011 * Abstract base class for all layout track objects (PositionablePoint, 012 * TrackSegment, LayoutTurnout, LayoutSlip, LevelXing and LayoutTurntable) 013 * <p> 014 * This is the connectivity/topology information for the layout; the 015 * display information, including screen geometry, is held in {@link LayoutTrackView} subclasses. 016 * <ul> 017 * <li>One or more connections, consisting of a LayoutTrack name and {@link HitPointType} 018 * <li>Mainline status 019 * <li>Associated 020 * <ul> 021 * <li>Blocks 022 * <li>Signal heads and masts 023 * <li>Sensors 024 * <li>Turnout controls 025 * </ul> 026 * </ul> 027 * 028 * @author Dave Duchamp Copyright (C) 2009 029 * @author George Warner Copyright (c) 2017-2020 030 * @author Bob Jacobsen Copyright (c) 2020 031 */ 032abstract public class LayoutTrack { 033 034 // final protected LayoutModels models; // preferred 035 final protected LayoutEditor models; // temporary type 036 037 /** 038 * Constructor method. 039 * @param ident track ID. 040 * @param models main layout editor. 041 */ 042 // public LayoutTrack(@Nonnull String ident, @Nonnull LayoutModels models) { // preferred 043 public LayoutTrack(@Nonnull String ident, @Nonnull LayoutEditor models) { // temporary 044 this.ident = ident; 045 this.models = models; 046 } 047 048 /** 049 * Get the track ID. 050 * @return track ident. 051 */ 052 @Nonnull 053 final public String getId() { 054 return ident; 055 } 056 057 @Nonnull 058 final public String getName() { 059 return ident; 060 } 061 062 /** 063 * Get the type of this item. 064 * @return the type 065 */ 066 public abstract String getTypeName(); 067 068 private String ident = ""; 069 070 final protected void setIdent(@Nonnull String ident) { 071 this.ident = ident; 072 } 073 074 abstract public boolean isMainline(); 075 076 /* 077 * non-accessor methods 078 */ 079 /** 080 * get turnout state string 081 * 082 * @param turnoutState of the turnout 083 * @return the turnout state string 084 */ 085 final public String getTurnoutStateString(int turnoutState) { 086 String result = ""; 087 if (turnoutState == Turnout.CLOSED) { 088 result = Bundle.getMessage("TurnoutStateClosed"); 089 } else if (turnoutState == Turnout.THROWN) { 090 result = Bundle.getMessage("TurnoutStateThrown"); 091 } else { 092 result = Bundle.getMessage("BeanStateUnknown"); 093 } 094 return result; 095 } 096 097 /** 098 * Check for active block boundaries. 099 * <p> 100 * If any connection point of a layout track object has attached objects, 101 * such as signal masts, signal heads or NX sensors, the layout track object 102 * cannot be deleted. 103 * 104 * @return true if the layout track object can be deleted. 105 */ 106 abstract public boolean canRemove(); 107 108 /** 109 * Initialization method for LayoutTrack sub-classes. The following method 110 * is called for each instance after the entire LayoutEditor is loaded to 111 * set the specific objects for that instance 112 * 113 * @param le the layout editor 114 */ 115 abstract public void setObjects(@Nonnull LayoutEditor le); 116 117 /** 118 * get the LayoutTrack connected at the specified connection type 119 * 120 * @param connectionType where on us to get the connection 121 * @return the LayoutTrack connected at the specified connection type 122 * @throws JmriException - if the connectionType is invalid 123 */ 124 abstract public LayoutTrack getConnection(HitPointType connectionType) throws JmriException; 125 126 /** 127 * set the LayoutTrack connected at the specified connection type 128 * 129 * @param connectionType where on us to set the connection 130 * @param o the LayoutTrack that is to be connected 131 * @param type where on the LayoutTrack we are connected 132 * @throws JmriException - if connectionType or type are invalid 133 */ 134 abstract public void setConnection(HitPointType connectionType, LayoutTrack o, HitPointType type) throws JmriException; 135 136 /** 137 * abstract method... subclasses should implement _IF_ they need to recheck 138 * their block boundaries 139 */ 140 abstract protected void reCheckBlockBoundary(); 141 142 /** 143 * get the layout connectivity for this track 144 * 145 * @return the list of Layout Connectivity objects 146 */ 147 abstract protected List<LayoutConnectivity> getLayoutConnectivity(); 148 149 /** 150 * return true if this connection type is disconnected 151 * 152 * @param connectionType the connection type to test 153 * @return true if the connection for this connection type is free 154 */ 155 public boolean isDisconnected(HitPointType connectionType) { 156 boolean result = false; 157 if (HitPointType.isConnectionHitType(connectionType)) { 158 try { 159 result = (null == getConnection(connectionType)); 160 } catch (JmriException e) { 161 // this should never happen because isConnectionType() above would have caught an invalid connectionType. 162 log.error("Unexpected exception", e); 163 } 164 } 165 return result; 166 } 167 168 /** 169 * return a list of the available connections for this layout track 170 * 171 * @return the list of available connections 172 */ 173 // note: used by LayoutEditorChecks.setupCheckUnConnectedTracksMenu() 174 // 175 // This could have just returned a boolean but I thought a list might be 176 // more useful (eventually... not currently being used; we just check to see 177 // if it's not empty.) 178 @Nonnull 179 abstract public List<HitPointType> checkForFreeConnections(); 180 181 /** 182 * determine if all the appropriate blocks have been assigned to this track 183 * 184 * @return true if all appropriate blocks have been assigned 185 */ 186 // note: used by LayoutEditorChecks.setupCheckUnBlockedTracksMenu() 187 // 188 abstract public boolean checkForUnAssignedBlocks(); 189 190 /** 191 * check this track and its neighbors for non-contiguous blocks 192 * <p> 193 * For each (non-null) blocks of this track do: #1) If it's got an entry in 194 * the blockNamesToTrackNameSetMap then #2) If this track is not in one of 195 * the TrackNameSets for this block #3) add a new set (with this 196 * block/track) to blockNamesToTrackNameSetMap and #4) check all the 197 * connections in this block (by calling the 2nd method below) 198 * <p> 199 * Basically, we're maintaining contiguous track sets for each block found 200 * (in blockNamesToTrackNameSetMap) 201 * 202 * @param blockNamesToTrackNameSetMaps hashmap of key:block names to lists 203 * of track name sets for those blocks 204 */ 205 // note: used by LayoutEditorChecks.setupCheckNonContiguousBlocksMenu() 206 // 207 abstract public void checkForNonContiguousBlocks( 208 @Nonnull HashMap<String, List<Set<String>>> blockNamesToTrackNameSetMaps); 209 210 /** 211 * recursive routine to check for all contiguous tracks in this blockName 212 * 213 * @param blockName the block that we're checking for 214 * @param TrackNameSet the set of track names in this block 215 */ 216 abstract public void collectContiguousTracksNamesInBlockNamed( 217 @Nonnull String blockName, 218 @Nonnull Set<String> TrackNameSet); 219 220 /** 221 * Assign all the layout blocks in this track 222 * 223 * @param layoutBlock to this layout block (used by the Tools menu's "Assign 224 * block to selection" item) 225 */ 226 abstract public void setAllLayoutBlocks(LayoutBlock layoutBlock); 227 228 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LayoutTrack.class); 229}