001package jmri.jmrit.display.layoutEditor;
002
003import java.util.ArrayList;
004import java.util.List;
005
006import javax.annotation.CheckReturnValue;
007import javax.annotation.CheckForNull;
008
009import jmri.NamedBean;
010import jmri.Sensor;
011import jmri.SignalHead;
012import jmri.SignalMast;
013import jmri.Turnout;
014
015/**
016 * A collection of tools to find various objects within the layout model(s)
017 *
018 * (temporary) Consider renaming to LayoutModelFindItems, or even merge to LayoutModels
019 *
020 * @author Dave Duchamp Copyright (c) 2004-2007
021 * @author George Warner Copyright (c) 2017-2018
022 * @author Bob Jacobsen Copyright (c) 2019-2020
023 */
024final public class LayoutEditorFindItems {
025
026    private final LayoutModels layoutModels;
027
028    public LayoutEditorFindItems(LayoutModels models) {
029        layoutModels = models;
030    }
031
032    public TrackSegment findTrackSegmentByName(String name) {
033        if (!name.isEmpty()) {
034            for (TrackSegment t : layoutModels.getTrackSegments()) {
035                if (t.getId().equals(name)) {
036                    return t;
037                }
038            }
039        }
040        return null;
041    }
042
043    public PositionablePoint findPositionablePointByName(String name) {
044        if (!name.isEmpty()) {
045            for (PositionablePoint p : layoutModels.getPositionablePoints()) {
046                if (p.getId().equals(name)) {
047                    return p;
048                }
049            }
050        }
051        return null;
052    }
053
054    public PositionablePoint findPositionablePointAtTrackSegments(TrackSegment tr1, TrackSegment tr2) {
055        for (PositionablePoint p : layoutModels.getPositionablePoints()) {
056            if (((p.getConnect1() == tr1) && (p.getConnect2() == tr2))
057                    || ((p.getConnect1() == tr2) && (p.getConnect2() == tr1))) {
058                return p;
059            }
060        }
061        return null;
062    }
063
064    public PositionablePoint findPositionableLinkPoint(LayoutBlock blk1) {
065        for (PositionablePoint p : layoutModels.getPositionablePoints()) {
066            if (p.getType() == PositionablePoint.PointType.EDGE_CONNECTOR) {
067                if ((p.getConnect1() != null && p.getConnect1().getLayoutBlock() == blk1)
068                        || (p.getConnect2() != null && p.getConnect2().getLayoutBlock() == blk1)) {
069                    return p;
070                }
071            }
072        }
073        return null;
074    }
075
076    /**
077     * Returns an array list of track segments matching the block name.
078     * @param name block name.
079     * @return array of segments, may be null.
080     */
081    public ArrayList<TrackSegment> findTrackSegmentByBlock(String name) {
082        if (name.isEmpty()) {
083            return null;
084        }
085        ArrayList<TrackSegment> ts = new ArrayList<>();
086        for (TrackSegment t : layoutModels.getTrackSegments()) {
087            if (t.getBlockName().equals(name)) {
088                ts.add(t);
089            }
090        }
091        return ts;
092    }
093
094    public PositionablePoint findPositionablePointByEastBoundSignal(String signalName) {
095        for (PositionablePoint p : layoutModels.getPositionablePoints()) {
096            if (p.getEastBoundSignal().equals(signalName)) {
097                return p;
098            }
099        }
100        return null;
101    }
102
103    public PositionablePoint findPositionablePointByWestBoundSignal(String signalName) {
104        for (PositionablePoint p : layoutModels.getPositionablePoints()) {
105            if (p.getWestBoundSignal().equals(signalName)) {
106                return p;
107            }
108        }
109        return null;
110    }
111
112    public PositionablePoint findPositionablePointByWestBoundBean(NamedBean bean) {
113        if (bean instanceof SignalMast) {
114            for (PositionablePoint p : layoutModels.getPositionablePoints()) {
115                if (p.getWestBoundSignalMast() == bean) {
116                    return p;
117                }
118            }
119        } else if (bean instanceof Sensor) {
120            for (PositionablePoint p : layoutModels.getPositionablePoints()) {
121                if (p.getWestBoundSensor() == bean) {
122                    return p;
123                }
124            }
125        } else if (bean instanceof SignalHead) {
126            for (PositionablePoint p : layoutModels.getPositionablePoints()) {
127                if (p.getWestBoundSignal().equals(bean.getSystemName())) {
128                    return p;
129                }
130            }
131        }
132        return null;
133    }
134
135    public PositionablePoint findPositionablePointByEastBoundBean(NamedBean bean) {
136        if (bean instanceof SignalMast) {
137            for (PositionablePoint p : layoutModels.getPositionablePoints()) {
138                if (p.getEastBoundSignalMast() == bean) {
139                    return p;
140                }
141            }
142        } else if (bean instanceof Sensor) {
143            for (PositionablePoint p : layoutModels.getPositionablePoints()) {
144                if (p.getEastBoundSensor() == bean) {
145                    return p;
146                }
147            }
148        } else if (bean instanceof SignalHead) {
149            for (PositionablePoint p : layoutModels.getPositionablePoints()) {
150                if (p.getEastBoundSignal().equals(bean.getSystemName())) {
151                    return p;
152                }
153            }
154        }
155        return null;
156    }
157
158    public PositionablePoint findPositionablePointByWestBoundSignalMast(String signalMastName) {
159        for (PositionablePoint p : layoutModels.getPositionablePoints()) {
160            if (p.getWestBoundSignalMastName().equals(signalMastName)) {
161                return p;
162            }
163        }
164        return null;
165    }
166
167    public PositionablePoint findPositionablePointByBean(NamedBean bean) {
168        if (bean instanceof SignalMast) {
169            for (PositionablePoint p : layoutModels.getPositionablePoints()) {
170                if (p.getWestBoundSignalMast() == bean
171                        || p.getEastBoundSignalMast() == bean) {
172                    return p;
173                }
174            }
175        } else if (bean instanceof Sensor) {
176            for (PositionablePoint p : layoutModels.getPositionablePoints()) {
177                if (p.getWestBoundSensor() == bean
178                        || p.getEastBoundSensor() == bean) {
179                    return p;
180                }
181            }
182        } else if (bean instanceof SignalHead) {
183            for (PositionablePoint p : layoutModels.getPositionablePoints()) {
184                if (p.getEastBoundSignal().equals(bean.getSystemName())
185                        || p.getWestBoundSignal().equals(bean.getSystemName())) {
186                    return p;
187                }
188                if (bean.getUserName() != null && (p.getEastBoundSignal().equals(bean.getSystemName())
189                        || p.getWestBoundSignal().equals(bean.getSystemName()))) {
190                    return p;
191                }
192            }
193        }
194        return null;
195
196    }
197
198    @CheckReturnValue
199    public LayoutTurnout findLayoutTurnoutBySignalMast(String signalMastName) throws IllegalArgumentException {
200        return findLayoutTurnoutByBean(jmri.InstanceManager.getDefault(jmri.SignalMastManager.class).provideSignalMast(signalMastName));
201    }
202
203    @CheckReturnValue
204    public LayoutTurnout findLayoutTurnoutByBean(@CheckForNull NamedBean bean) {
205        List<LayoutTurnout> layoutTurnouts = layoutModels.getLayoutTurnouts();
206        if (bean instanceof SignalMast) {
207            for (LayoutTurnout t : layoutTurnouts) {
208                if (t.getSignalAMast() == bean
209                        || t.getSignalBMast() == bean
210                        || t.getSignalCMast() == bean
211                        || t.getSignalDMast() == bean) {
212                    return t;
213                }
214            }
215        } else if (bean instanceof Sensor) {
216            for (LayoutTurnout t : layoutTurnouts) {
217                if (t.getSensorA() == bean
218                        || t.getSensorB() == bean
219                        || t.getSensorC() == bean
220                        || t.getSensorD() == bean) {
221                    return t;
222                }
223            }
224        } else if (bean instanceof SignalHead) {
225            for (LayoutTurnout t : layoutTurnouts) {
226                if (t.getSignalA1Name().equals(bean.getSystemName())
227                        || t.getSignalA2Name().equals(bean.getSystemName())
228                        || t.getSignalA3Name().equals(bean.getSystemName())) {
229                    return t;
230                }
231
232                if (t.getSignalB1Name().equals(bean.getSystemName())
233                        || t.getSignalB2Name().equals(bean.getSystemName())) {
234                    return t;
235                }
236                if (t.getSignalC1Name().equals(bean.getSystemName())
237                        || t.getSignalC2Name().equals(bean.getSystemName())) {
238                    return t;
239                }
240                if (t.getSignalD1Name().equals(bean.getSystemName())
241                        || t.getSignalD2Name().equals(bean.getSystemName())) {
242                    return t;
243                }
244                if (bean.getUserName() != null) {
245                    if (t.getSignalA1Name().equals(bean.getUserName())
246                            || t.getSignalA2Name().equals(bean.getUserName())
247                            || t.getSignalA3Name().equals(bean.getUserName())) {
248                        return t;
249                    }
250
251                    if (t.getSignalB1Name().equals(bean.getUserName())
252                            || t.getSignalB2Name().equals(bean.getUserName())) {
253                        return t;
254                    }
255                    if (t.getSignalC1Name().equals(bean.getUserName())
256                            || t.getSignalC2Name().equals(bean.getUserName())) {
257                        return t;
258                    }
259                    if (t.getSignalD1Name().equals(bean.getUserName())
260                            || t.getSignalD2Name().equals(bean.getUserName())) {
261                        return t;
262                    }
263                }
264            }
265        } else if (bean instanceof Turnout) {
266            for (LayoutTurnout t : layoutTurnouts) {
267                if (bean.equals(t.getTurnout())) {
268                    return t;
269                }
270            }
271        }
272        return null;
273    }   // findLayoutTurnoutByBean
274
275    @CheckReturnValue
276    public LayoutTurnout findLayoutTurnoutBySensor(String sensorName) throws IllegalArgumentException {
277        return findLayoutTurnoutByBean(jmri.InstanceManager.sensorManagerInstance().provideSensor(sensorName));
278    }
279
280    public LevelXing findLevelXingBySignalMast(String signalMastName) throws IllegalArgumentException {
281        return findLevelXingByBean(jmri.InstanceManager.getDefault(jmri.SignalMastManager.class).provideSignalMast(signalMastName));
282    }
283
284    public LevelXing findLevelXingBySensor(String sensorName) throws IllegalArgumentException {
285        return findLevelXingByBean(jmri.InstanceManager.sensorManagerInstance().provideSensor(sensorName));
286    }
287
288    public LevelXing findLevelXingByBean(NamedBean bean) {
289        List<LevelXing> levelXings = layoutModels.getLevelXings();
290        if (bean instanceof SignalMast) {
291            for (LevelXing l : levelXings) {
292                if (l.getSignalAMast() == bean
293                        || l.getSignalBMast() == bean
294                        || l.getSignalCMast() == bean
295                        || l.getSignalDMast() == bean) {
296                    return l;
297                }
298            }
299        } else if (bean instanceof Sensor) {
300            for (LevelXing l : levelXings) {
301                if (l.getSensorA() == bean
302                        || l.getSensorB() == bean
303                        || l.getSensorC() == bean
304                        || l.getSensorD() == bean) {
305                    return l;
306                }
307            }
308
309        } else if (bean instanceof SignalHead) {
310            for (LevelXing l : levelXings) {
311                if (l.getSignalAName().equals(bean.getSystemName())
312                        || l.getSignalBName().equals(bean.getSystemName())
313                        || l.getSignalCName().equals(bean.getSystemName())
314                        || l.getSignalDName().equals(bean.getSystemName())) {
315                    return l;
316                }
317                if (bean.getUserName() != null && (l.getSignalAName().equals(bean.getUserName())
318                        || l.getSignalBName().equals(bean.getUserName())
319                        || l.getSignalCName().equals(bean.getUserName())
320                        || l.getSignalDName().equals(bean.getUserName()))) {
321                    return l;
322                }
323            }
324        }
325        return null;
326    }
327
328    public LayoutSlip findLayoutSlipByBean(NamedBean bean) {
329        List<LayoutSlip> layoutSlips = layoutModels.getLayoutSlips();
330        if (bean instanceof SignalMast) {
331            for (LayoutSlip l : layoutSlips) {
332                if (l.getSignalAMast() == bean
333                        || l.getSignalBMast() == bean
334                        || l.getSignalCMast() == bean
335                        || l.getSignalDMast() == bean) {
336                    return l;
337                }
338            }
339        } else if (bean instanceof Sensor) {
340            for (LayoutSlip l : layoutSlips) {
341                if (l.getSensorA() == bean
342                        || l.getSensorB() == bean
343                        || l.getSensorC() == bean
344                        || l.getSensorD() == bean) {
345                    return l;
346                }
347            }
348        } else if (bean instanceof SignalHead) {
349            for (LayoutSlip l : layoutSlips) {
350                if (l.getSignalA1Name().equals(bean.getSystemName())
351                        || l.getSignalA2Name().equals(bean.getSystemName())
352                        || l.getSignalA3Name().equals(bean.getSystemName())) {
353                    return l;
354                }
355
356                if (l.getSignalB1Name().equals(bean.getSystemName())
357                        || l.getSignalB2Name().equals(bean.getSystemName())) {
358                    return l;
359                }
360                if (l.getSignalC1Name().equals(bean.getSystemName())
361                        || l.getSignalC2Name().equals(bean.getSystemName())) {
362                    return l;
363                }
364                if (l.getSignalD1Name().equals(bean.getSystemName())
365                        || l.getSignalD2Name().equals(bean.getSystemName())) {
366                    return l;
367                }
368                if (l.getSignalA1Name().equals(bean.getUserName())
369                        || l.getSignalA2Name().equals(bean.getUserName())
370                        || l.getSignalA3Name().equals(bean.getUserName())) {
371                    return l;
372                }
373                if (bean.getUserName() != null) {
374                    if (l.getSignalB1Name().equals(bean.getUserName())
375                            || l.getSignalB2Name().equals(bean.getUserName())) {
376                        return l;
377                    }
378                    if (l.getSignalC1Name().equals(bean.getUserName())
379                            || l.getSignalC2Name().equals(bean.getUserName())) {
380                        return l;
381                    }
382                    if (l.getSignalD1Name().equals(bean.getUserName())
383                            || l.getSignalD2Name().equals(bean.getUserName())) {
384                        return l;
385                    }
386                }
387            }
388        } else if (bean instanceof Turnout) {
389            for (LayoutSlip l : layoutSlips) {
390                if (bean.equals(l.getTurnout())) {
391                    return l;
392                }
393                if (bean.equals(l.getTurnoutB())) {
394                    return l;
395                }
396            }
397        }
398        return null;
399    }
400
401    public LayoutSlip findLayoutSlipBySignalMast(String signalMastName) throws IllegalArgumentException {
402        return findLayoutSlipByBean(jmri.InstanceManager.getDefault(jmri.SignalMastManager.class).provideSignalMast(signalMastName));
403    }
404
405    public LayoutSlip findLayoutSlipBySensor(String sensorName) throws IllegalArgumentException {
406        return findLayoutSlipByBean(jmri.InstanceManager.sensorManagerInstance().provideSensor(sensorName));
407    }
408
409    public PositionablePoint findPositionablePointByEastBoundSensor(String sensorName) {
410        PositionablePoint result = null;
411        for (PositionablePoint p : layoutModels.getPositionablePoints()) {
412            if (p.getEastBoundSensorName().equals(sensorName)) {
413                result = p;
414                break;
415            }
416        }
417        return result;
418    }
419
420    public PositionablePoint findPositionablePointByWestBoundSensor(String sensorName) {
421        PositionablePoint result = null;
422        for (PositionablePoint p : layoutModels.getPositionablePoints()) {
423            if (p.getWestBoundSensorName().equals(sensorName)) {
424                result = p;
425                break;
426            }
427        }
428        return result;
429    }
430
431    @CheckReturnValue
432    public LayoutTurnout findLayoutTurnoutByName(String name) {
433        LayoutTurnout result = null;
434        if ((name != null) && !name.isEmpty()) {
435            for (LayoutTurnout t : layoutModels.getLayoutTurnouts()) {
436                if (t.getName().equals(name)) {
437                    result = t;
438                    break;
439                }
440            }
441        }
442        return result;
443    }
444
445    @CheckReturnValue
446    public LayoutTurnout findLayoutTurnoutByTurnoutName(String turnoutName) {
447        LayoutTurnout result = null;
448        if ((turnoutName != null) && !turnoutName.isEmpty()) {
449            for (LayoutTurnout t : layoutModels.getLayoutTurnouts()) {
450                if (t.getTurnoutName().equals(turnoutName)) {
451                    result = t;
452                }
453            }
454        }
455        return result;
456    }
457
458    public LevelXing findLevelXingByName(String name) {
459        LevelXing result = null;
460        if ((name != null) && !name.isEmpty()) {
461            for (LevelXing x : layoutModels.getLevelXings()) {
462                if (x.getId().equals(name)) {
463                    result = x;
464                    break;
465                }
466            }
467        }
468        return result;
469    }
470
471    public LayoutSlip findLayoutSlipByName(String name) {
472        LayoutSlip result = null;
473        if ((name != null) && !name.isEmpty()) {
474            for (LayoutSlip x : layoutModels.getLayoutSlips()) {
475                if (x.getName().equals(name)) {
476                    result = x;
477                    break;
478                }
479            }
480        }
481        return result;
482    }
483
484    public LayoutTurntable findLayoutTurntableByName(String name) {
485        LayoutTurntable result = null;
486        if ((name != null) && !name.isEmpty()) {
487            for (LayoutTurntable x : layoutModels.getLayoutTurntables()) {
488                if (x.getId().equals(name)) {
489                    result = x;
490                    break;
491                }
492            }
493        }
494        return result;
495    }
496
497    public LayoutShape findLayoutShapeByName(String name) {
498        LayoutShape result = null;
499        if ((name != null) && !name.isEmpty()) {
500            for (LayoutShape x : layoutModels.getLayoutShapes()) {
501                if (x.getName().equals(name)) {
502                    result = x;
503                    break;
504                }
505            }
506        }
507        return result;
508    }
509
510    /**
511     * find object by name
512     *
513     * @param name the name of the object that you are looking for
514     * @return object the named object
515     */
516    // NOTE: This replacement routine for findObjectByTypeAndName (above)
517    // uses the unique name prefixes to determine what type of item to find.
518    // Currently this routine (like the one above that it replaces) is only
519    // called by the setObjects routine in TrackSegment.java however in the
520    // move toward encapsulation this routine should see a lot more usage;
521    // specifically, instead of a TON of "if (type == XXX) { findXXXByName(...)...}"
522    // code you would just call this method instead.
523    public LayoutTrack findObjectByName(String name) {
524        LayoutTrack result = null;   // assume failure (pessimist!)
525        if ((name != null) && !name.isEmpty()) {
526            if (name.startsWith("TO")) {
527                result = findLayoutTurnoutByName(name);
528            } else if (name.startsWith("A") || name.startsWith("EB") || name.startsWith("EC") || name.matches("F\\d+-A-\\d+")) {
529                result = findPositionablePointByName(name);
530            } else if (name.startsWith("X")) {
531                result = findLevelXingByName(name);
532            } else if (name.startsWith("SL")) {
533                result = findLayoutSlipByName(name);
534            } else if (name.startsWith("TUR")) {
535                result = findLayoutTurntableByName(name);
536            } else if (name.startsWith("T") || name.matches("F\\d+-S-\\d+")) {  // (this prefix has to go after "TO" & "TUR" prefixes above)
537                result = findTrackSegmentByName(name);
538            } else if (name.endsWith("-EB")) {  //BUGFIX: a 3rd party JMRI exporter gets this one wrong.
539                result = findPositionablePointByName(name);
540            } else {
541                log.warn("findObjectByName({}): unknown type name prefix", name);
542            }
543            if (result == null) {
544                log.debug("findObjectByName({}) returned null", name);
545            }
546        }
547        return result;
548    }
549
550    /**
551     * Determine the first unused object name...
552     *
553     * @param inPrefix     ...with this prefix...
554     * @param inStartIndex ...and this starting index...
555     * @return the first unused object name
556     */
557    public String uniqueName(String inPrefix, int inStartIndex) {
558        String result;
559        for (int idx = inStartIndex; true; idx++) {
560            result = String.format("%s%d", inPrefix, idx);
561            if (inPrefix.equals("S")) {     // LayoutShape?
562                if (findLayoutShapeByName(result) == null) {
563                    break;
564                }
565            } else if (findObjectByName(result) == null) {
566                break;
567            }
568        }
569        return result;
570    }
571
572    /**
573     * Determine the first unused object name...
574     *
575     * @param inPrefix ...with this prefix...
576     * @return the first unused object name
577     */
578    public String uniqueName(String inPrefix) {
579        return uniqueName(inPrefix, 1);
580    }
581
582    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LayoutEditorFindItems.class);
583}