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}