001package jmri.jmrit.display.layoutEditor; 002 003import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 004 005import java.util.*; 006 007import javax.annotation.CheckForNull; 008import javax.annotation.Nonnull; 009 010import jmri.*; 011 012/** 013 * TrackSegment is a segment of track on a layout linking two nodes of the 014 * layout. A node may be a LayoutTurnout, a LevelXing or a PositionablePoint. 015 * <p> 016 * PositionablePoints have 1 or 2 connection points. LayoutTurnouts have 3 or 4 017 * (crossovers) connection points, designated A, B, C, and D. LevelXing's have 4 018 * connection points, designated A, B, C, and D. 019 * <p> 020 * TrackSegments carry the connectivity information between the three types of 021 * nodes. Track Segments serve as the lines in a graph which shows layout 022 * connectivity. For the connectivity graph to be valid, all connections between 023 * nodes must be via TrackSegments. 024 * <p> 025 * TrackSegments carry Block information, as do LayoutTurnouts and LevelXings. 026 * <p> 027 * Arrows and bumpers are visual, presentation aspects handled in the View. 028 * 029 * @author Dave Duchamp Copyright (p) 2004-2009 030 * @author George Warner Copyright (c) 2017-2019 031 */ 032public class TrackSegment extends LayoutTrack { 033 034 public TrackSegment(@Nonnull String id, 035 @CheckForNull LayoutTrack c1, HitPointType t1, 036 @CheckForNull LayoutTrack c2, HitPointType t2, 037 boolean main, 038 @Nonnull LayoutEditor models) { 039 super(id, models); 040 041 // validate input 042 if ((c1 == null) || (c2 == null)) { 043 log.error("Invalid object in TrackSegment constructor call - {}", id); 044 } 045 046 if (HitPointType.isConnectionHitType(t1)) { 047 connect1 = c1; 048 type1 = t1; 049 } else { 050 log.error("Invalid connect type 1 ('{}') in TrackSegment constructor - {}", t1, id); 051 } 052 if (HitPointType.isConnectionHitType(t2)) { 053 connect2 = c2; 054 type2 = t2; 055 } else { 056 log.error("Invalid connect type 2 ('{}') in TrackSegment constructor - {}", t2, id); 057 } 058 059 mainline = main; 060 } 061 062 // alternate constructor for loading layout editor panels 063 public TrackSegment(@Nonnull String id, 064 @CheckForNull String c1Name, HitPointType t1, 065 @CheckForNull String c2Name, HitPointType t2, 066 boolean main, 067 @Nonnull LayoutEditor models) { 068 super(id, models); 069 070 tConnect1Name = c1Name; 071 type1 = t1; 072 tConnect2Name = c2Name; 073 type2 = t2; 074 075 mainline = main; 076 } 077 078 079 // defined constants 080 // operational instance variables (not saved between sessions) 081 private NamedBeanHandle<LayoutBlock> namedLayoutBlock = null; 082 083 // persistent instances variables (saved between sessions) 084 protected LayoutTrack connect1 = null; 085 protected HitPointType type1 = HitPointType.NONE; 086 protected LayoutTrack connect2 = null; 087 protected HitPointType type2 = HitPointType.NONE; 088 private boolean mainline = false; 089 090 /** 091 * Get debugging string for the TrackSegment. 092 * 093 * @return text showing id and connections of this segment 094 */ 095 @Override 096 public String toString() { 097 return "TrackSegment " + getName() 098 + " c1:{" + getConnect1Name() + " (" + type1 + ")}," 099 + " c2:{" + getConnect2Name() + " (" + type2 + ")}"; 100 101 } 102 103 /* 104 * Accessor methods 105 */ 106 @Nonnull 107 public String getBlockName() { 108 String result = null; 109 if (namedLayoutBlock != null) { 110 result = namedLayoutBlock.getName(); 111 } 112 return ((result == null) ? "" : result); 113 } 114 115 public HitPointType getType1() { 116 return type1; 117 } 118 119 public HitPointType getType2() { 120 return type2; 121 } 122 123 public LayoutTrack getConnect1() { 124 return connect1; 125 } 126 127 public LayoutTrack getConnect2() { 128 return connect2; 129 } 130 131 /** 132 * set a new connection 1 133 * 134 * @param connectTrack the track we want to connect to 135 * @param connectionType where on that track we want to be connected 136 */ 137 protected void setNewConnect1(@CheckForNull LayoutTrack connectTrack, HitPointType connectionType) { 138 connect1 = connectTrack; 139 type1 = connectionType; 140 } 141 142 /** 143 * set a new connection 2 144 * 145 * @param connectTrack the track we want to connect to 146 * @param connectionType where on that track we want to be connected 147 */ 148 protected void setNewConnect2(@CheckForNull LayoutTrack connectTrack, HitPointType connectionType) { 149 connect2 = connectTrack; 150 type2 = connectionType; 151 } 152 153 /** 154 * Replace old track connection with new track connection. 155 * 156 * @param oldTrack the old track connection. 157 * @param newTrack the new track connection. 158 * @param newType the hit point type. 159 * @return true if successful. 160 */ 161 public boolean replaceTrackConnection(@CheckForNull LayoutTrack oldTrack, @CheckForNull LayoutTrack newTrack, HitPointType newType) { 162 boolean result = false; // assume failure (pessimist!) 163 // trying to replace old track with null? 164 if (newTrack == null) { 165 result = true; // assume success (optimist!) 166 //(yes) remove old connection 167 if (oldTrack != null) { 168 if (connect1 == oldTrack) { 169 connect1 = null; 170 type1 = HitPointType.NONE; 171 } else if (connect2 == oldTrack) { 172 connect2 = null; 173 type2 = HitPointType.NONE; 174 } else { 175 log.error("{}.replaceTrackConnection({}, null, {}); Attempt to remove invalid track connection", 176 getName(), oldTrack.getName(), newType); 177 result = false; 178 } 179 } else { 180 log.warn("{}.replaceTrackConnection(null, null, {}); Can't replace null track connection with null", 181 getName(), newType); 182 result = false; 183 } 184 } else // already connected to newTrack? 185 if ((connect1 != newTrack) && (connect2 != newTrack)) { 186 //(no) find a connection we can connect to 187 result = true; // assume success (optimist!) 188 if (connect1 == oldTrack) { 189 connect1 = newTrack; 190 type1 = newType; 191 } else if (connect2 == oldTrack) { 192 connect2 = newTrack; 193 type2 = newType; 194 } else { 195 log.error("{}.replaceTrackConnection({}, {}, {}); Attempt to replace invalid track connection", 196 getName(), (oldTrack == null) ? "null" : oldTrack.getName(), newTrack.getName(), newType); 197 result = false; 198 } 199 } 200 return result; 201 } 202 203 /** 204 * @return true if track segment is a main line 205 */ 206 @Override 207 public boolean isMainline() { 208 return mainline; 209 } 210 211 public void setMainline(boolean main) { 212 if (mainline != main) { 213 mainline = main; 214 models.redrawPanel(); 215 models.setDirty(); 216 } 217 } 218 219 public LayoutBlock getLayoutBlock() { 220 return (namedLayoutBlock != null) ? namedLayoutBlock.getBean() : null; 221 } 222 223 public String getConnect1Name() { 224 return getConnectName(connect1, type1); 225 } 226 227 public String getConnect2Name() { 228 return getConnectName(connect2, type2); 229 } 230 231 private String getConnectName(@CheckForNull LayoutTrack layoutTrack, HitPointType type) { 232 return (layoutTrack == null) ? null : layoutTrack.getName(); 233 } 234 235 /** 236 * {@inheritDoc} 237 * <p> 238 * This implementation returns null because {@link #getConnect1} and 239 * {@link #getConnect2} should be used instead. 240 */ 241 // only implemented here to suppress "does not override abstract method " error in compiler 242 @Override 243 public LayoutTrack getConnection(HitPointType connectionType) throws jmri.JmriException { 244 // nothing to see here, move along 245 throw new jmri.JmriException("Use getConnect1() or getConnect2() instead."); 246 } 247 248 /** 249 * {@inheritDoc} 250 * <p> 251 * This implementation does nothing because {@link #setNewConnect1} and 252 * {@link #setNewConnect2} should be used instead. 253 */ 254 // only implemented here to suppress "does not override abstract method " error in compiler 255 @Override 256 public void setConnection(HitPointType connectionType, @CheckForNull LayoutTrack o, HitPointType type) throws jmri.JmriException { 257 // nothing to see here, move along 258 throw new jmri.JmriException("Use setConnect1() or setConnect2() instead."); 259 } 260 261 public void setConnect1(@CheckForNull LayoutTrack o, HitPointType type) { 262 type1 = type; 263 connect1 = o; 264 } 265 266 public void setConnect2(@CheckForNull LayoutTrack o, HitPointType type) { 267 type2 = type; 268 connect2 = o; 269 } 270 271 /** 272 * Set up a LayoutBlock for this Track Segment. 273 * 274 * @param newLayoutBlock the LayoutBlock to set 275 */ 276 public void setLayoutBlock(@CheckForNull LayoutBlock newLayoutBlock) { 277 LayoutBlock layoutBlock = getLayoutBlock(); 278 if (layoutBlock != newLayoutBlock) { 279 //block has changed, if old block exists, decrement use 280 if (layoutBlock != null) { 281 layoutBlock.decrementUse(); 282 } 283 namedLayoutBlock = null; 284 if (newLayoutBlock != null) { 285 String newName = newLayoutBlock.getUserName(); 286 if ((newName != null) && !newName.isEmpty()) { 287 namedLayoutBlock = InstanceManager.getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle(newName, newLayoutBlock); 288 } 289 } 290 } 291 } 292 293 /** 294 * Set up a LayoutBlock for this Track Segment. 295 * 296 * @param name the name of the new LayoutBlock 297 */ 298 public void setLayoutBlockByName(@CheckForNull String name) { 299 if ((name != null) && !name.isEmpty()) { 300 LayoutBlock b = models.provideLayoutBlock(name); 301 if (b != null) { 302 namedLayoutBlock = InstanceManager.getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle(name, b); 303 } else { 304 namedLayoutBlock = null; 305 } 306 } else { 307 namedLayoutBlock = null; 308 } 309 } 310 311 // initialization instance variables (used when loading a LayoutEditor) 312 public String tConnect1Name = ""; 313 public String tConnect2Name = ""; 314 315 public String tLayoutBlockName = ""; 316 317 /** 318 * Initialization method. The above variables are initialized by 319 * PositionablePointXml, then the following method is called after the 320 * entire LayoutEditor is loaded to set the specific TrackSegment objects. 321 */ 322 @Override 323 @SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE", justification = "Null check performed before using return value") 324 public void setObjects(LayoutEditor p) { 325 326 LayoutBlock lb; 327 if (!tLayoutBlockName.isEmpty()) { 328 lb = p.provideLayoutBlock(tLayoutBlockName); 329 if (lb != null) { 330 namedLayoutBlock = InstanceManager.getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle(lb.getUserName(), lb); 331 lb.incrementUse(); 332 } else { 333 log.error("{}.setObjects(...); bad blockname '{}' in tracksegment {}", 334 getName(), tLayoutBlockName, getName()); 335 namedLayoutBlock = null; 336 } 337 tLayoutBlockName = null; //release this memory 338 } 339 340 connect1 = p.getFinder().findObjectByName(tConnect1Name); 341 connect2 = p.getFinder().findObjectByName(tConnect2Name); 342 } 343 344 public void updateBlockInfo() { 345 LayoutBlock layoutBlock = getLayoutBlock(); 346 if (layoutBlock != null) { 347 layoutBlock.updatePaths(); 348 } 349 LayoutBlock b1 = getBlock(connect1, type1); 350 if ((b1 != null) && (b1 != layoutBlock)) { 351 b1.updatePaths(); 352 } 353 LayoutBlock b2 = getBlock(connect2, type2); 354 if ((b2 != null) && (b2 != layoutBlock) && (b2 != b1)) { 355 b2.updatePaths(); 356 } 357 358 getConnect1().reCheckBlockBoundary(); 359 getConnect2().reCheckBlockBoundary(); 360 } 361 362 private LayoutBlock getBlock(LayoutTrack connect, HitPointType type) { 363 LayoutBlock result = null; 364 if (connect != null) { 365 if (type == HitPointType.POS_POINT) { 366 PositionablePoint p = (PositionablePoint) connect; 367 if (p.getConnect1() != this) { 368 if (p.getConnect1() != null) { 369 result = p.getConnect1().getLayoutBlock(); 370 } 371 } else { 372 if (p.getConnect2() != null) { 373 result = p.getConnect2().getLayoutBlock(); 374 } 375 } 376 } else { 377 result = models.getAffectedBlock(connect, type); 378 } 379 } 380 return result; 381 } 382 383 /** 384 * {@inheritDoc} 385 */ 386 @Override 387 public boolean canRemove() { 388 List<String> itemList = new ArrayList<>(); 389 390 HitPointType type1 = getType1(); 391 LayoutTrack conn1 = getConnect1(); 392 itemList.addAll(getPointReferences(type1, conn1)); 393 394 HitPointType type2 = getType2(); 395 LayoutTrack conn2 = getConnect2(); 396 itemList.addAll(getPointReferences(type2, conn2)); 397 398 if (!itemList.isEmpty()) { 399 models.displayRemoveWarning(this, itemList, "TrackSegment"); // NOI18N 400 } 401 return itemList.isEmpty(); 402 } 403 404 public ArrayList<String> getPointReferences(HitPointType type, LayoutTrack conn) { 405 ArrayList<String> result = new ArrayList<>(); 406 407 if (type == HitPointType.POS_POINT && conn instanceof PositionablePoint) { 408 PositionablePoint pt = (PositionablePoint) conn; 409 if (!pt.getEastBoundSignal().isEmpty()) { 410 result.add(pt.getEastBoundSignal()); 411 } 412 if (!pt.getWestBoundSignal().isEmpty()) { 413 result.add(pt.getWestBoundSignal()); 414 } 415 if (!pt.getEastBoundSignalMastName().isEmpty()) { 416 result.add(pt.getEastBoundSignalMastName()); 417 } 418 if (!pt.getWestBoundSignalMastName().isEmpty()) { 419 result.add(pt.getWestBoundSignalMastName()); 420 } 421 if (!pt.getEastBoundSensorName().isEmpty()) { 422 result.add(pt.getEastBoundSensorName()); 423 } 424 if (!pt.getWestBoundSensorName().isEmpty()) { 425 result.add(pt.getWestBoundSensorName()); 426 } 427 if (pt.getType() == PositionablePoint.PointType.EDGE_CONNECTOR && pt.getLinkedPoint() != null) { 428 result.add(Bundle.getMessage("DeleteECisActive")); // NOI18N 429 } 430 } 431 432 if (HitPointType.isTurnoutHitType(type) && conn instanceof LayoutTurnout) { 433 LayoutTurnout lt = (LayoutTurnout) conn; 434 switch (type) { 435 case TURNOUT_A: { 436 result = lt.getBeanReferences("A"); // NOI18N 437 break; 438 } 439 case TURNOUT_B: { 440 result = lt.getBeanReferences("B"); // NOI18N 441 break; 442 } 443 case TURNOUT_C: { 444 result = lt.getBeanReferences("C"); // NOI18N 445 break; 446 } 447 case TURNOUT_D: { 448 result = lt.getBeanReferences("D"); // NOI18N 449 break; 450 } 451 default: { 452 log.error("Unexpected HitPointType: {}", type); 453 } 454 } 455 } 456 457 if (HitPointType.isLevelXingHitType(type) && conn instanceof LevelXing) { 458 LevelXing lx = (LevelXing) conn; 459 switch (type) { 460 case LEVEL_XING_A: { 461 result = lx.getBeanReferences("A"); // NOI18N 462 break; 463 } 464 case LEVEL_XING_B: { 465 result = lx.getBeanReferences("B"); // NOI18N 466 break; 467 } 468 case LEVEL_XING_C: { 469 result = lx.getBeanReferences("C"); // NOI18N 470 break; 471 } 472 case LEVEL_XING_D: { 473 result = lx.getBeanReferences("D"); // NOI18N 474 break; 475 } 476 default: { 477 log.error("Unexpected HitPointType: {}", type); 478 } 479 } 480 } 481 482 if (HitPointType.isSlipHitType(type) && conn instanceof LayoutSlip) { 483 LayoutSlip ls = (LayoutSlip) conn; 484 switch (type) { 485 case SLIP_A: { 486 result = ls.getBeanReferences("A"); // NOI18N 487 break; 488 } 489 case SLIP_B: { 490 result = ls.getBeanReferences("B"); // NOI18N 491 break; 492 } 493 case SLIP_C: { 494 result = ls.getBeanReferences("C"); // NOI18N 495 break; 496 } 497 case SLIP_D: { 498 result = ls.getBeanReferences("D"); // NOI18N 499 break; 500 } 501 default: { 502 log.error("Unexpected HitPointType: {}", type); 503 } 504 } 505 } 506 507 return result; 508 } 509 510 /** 511 * Remove this object from display and persistance. 512 */ 513 public void remove() { 514 // remove from persistance by flagging inactive 515 active = false; 516 } 517 518 private boolean active = true; 519 520 /** 521 * Get state. "active" means that the object is still displayed, and should 522 * be stored. 523 * 524 * @return true if still displayed, else false. 525 */ 526 public boolean isActive() { 527 return active; 528 } 529 530 /** 531 * temporary fill of abstract from above 532 */ 533 @Override 534 public void reCheckBlockBoundary() { 535 log.info("reCheckBlockBoundary is temporary, but was invoked", new Exception("traceback")); 536 } 537 538 539 /** 540 * {@inheritDoc} 541 */ 542 @Override 543 protected List<LayoutConnectivity> getLayoutConnectivity() { 544 List<LayoutConnectivity> results = new ArrayList<>(); 545 546 LayoutConnectivity lc = null; 547 LayoutBlock lb1 = getLayoutBlock(), lb2 = null; 548 // ensure that block is assigned 549 if (lb1 != null) { 550 // check first connection for turnout 551 if (HitPointType.isTurnoutHitType(type1)) { 552 // have connection to a turnout, is block different 553 LayoutTurnout lt = (LayoutTurnout) getConnect1(); 554 lb2 = lt.getLayoutBlock(); 555 if (lt.hasEnteringDoubleTrack()) { 556 // not RH, LH, or WYE turnout - other blocks possible 557 if ((type1 == HitPointType.TURNOUT_B) && (lt.getLayoutBlockB() != null)) { 558 lb2 = lt.getLayoutBlockB(); 559 } 560 if ((type1 == HitPointType.TURNOUT_C) && (lt.getLayoutBlockC() != null)) { 561 lb2 = lt.getLayoutBlockC(); 562 } 563 if ((type1 == HitPointType.TURNOUT_D) && (lt.getLayoutBlockD() != null)) { 564 lb2 = lt.getLayoutBlockD(); 565 } 566 } 567 if ((lb2 != null) && (lb1 != lb2)) { 568 // have a block boundary, create a LayoutConnectivity 569 log.debug("Block boundary (''{}''<->''{}'') found at {}", lb1, lb2, this); 570 lc = new LayoutConnectivity(lb1, lb2); 571 lc.setConnections(this, lt, type1, null); 572 lc.setDirection(models.computeDirection( 573 getConnect2(), type2, 574 getConnect1(), type1 ) ); 575 results.add(lc); 576 } 577 } else if (HitPointType.isLevelXingHitType(type1)) { 578 // have connection to a level crossing 579 LevelXing lx = (LevelXing) getConnect1(); 580 if ((type1 == HitPointType.LEVEL_XING_A) || (type1 == HitPointType.LEVEL_XING_C)) { 581 lb2 = lx.getLayoutBlockAC(); 582 } else { 583 lb2 = lx.getLayoutBlockBD(); 584 } 585 if ((lb2 != null) && (lb1 != lb2)) { 586 // have a block boundary, create a LayoutConnectivity 587 log.debug("Block boundary (''{}''<->''{}'') found at {}", lb1, lb2, this); 588 lc = new LayoutConnectivity(lb1, lb2); 589 lc.setConnections(this, lx, type1, null); 590 lc.setDirection(models.computeDirection( 591 getConnect2(), type2, 592 getConnect1(), type1 ) ); 593 results.add(lc); 594 } 595 } else if (HitPointType.isSlipHitType(type1)) { 596 // have connection to a slip crossing 597 LayoutSlip ls = (LayoutSlip) getConnect1(); 598 lb2 = ls.getLayoutBlock(); 599 if ((lb2 != null) && (lb1 != lb2)) { 600 // have a block boundary, create a LayoutConnectivity 601 log.debug("Block boundary (''{}''<->''{}'') found at {}", lb1, lb2, this); 602 lc = new LayoutConnectivity(lb1, lb2); 603 lc.setConnections(this, ls, type1, null); 604 lc.setDirection(models.computeDirection( 605 getConnect2(), type2, 606 getConnect1(), type1 ) ); 607 results.add(lc); 608 } 609 } 610 // check second connection for turnout 611 if (HitPointType.isTurnoutHitType(type2)) { 612 // have connection to a turnout 613 LayoutTurnout lt = (LayoutTurnout) getConnect2(); 614 lb2 = lt.getLayoutBlock(); 615 if (lt.hasEnteringDoubleTrack()) { 616 // not RH, LH, or WYE turnout - other blocks possible 617 if ((type2 == HitPointType.TURNOUT_B) && (lt.getLayoutBlockB() != null)) { 618 lb2 = lt.getLayoutBlockB(); 619 } 620 if ((type2 == HitPointType.TURNOUT_C) && (lt.getLayoutBlockC() != null)) { 621 lb2 = lt.getLayoutBlockC(); 622 } 623 if ((type2 == HitPointType.TURNOUT_D) && (lt.getLayoutBlockD() != null)) { 624 lb2 = lt.getLayoutBlockD(); 625 } 626 } 627 if ((lb2 != null) && (lb1 != lb2)) { 628 // have a block boundary, create a LayoutConnectivity 629 log.debug("Block boundary (''{}''<->''{}'') found at {}", lb1, lb2, this); 630 lc = new LayoutConnectivity(lb1, lb2); 631 lc.setConnections(this, lt, type2, null); 632 lc.setDirection(models.computeDirection( 633 getConnect1(), type1, 634 getConnect2(), type2 ) ); 635 results.add(lc); 636 } 637 } else if (HitPointType.isLevelXingHitType(type2)) { 638 // have connection to a level crossing 639 LevelXing lx = (LevelXing) getConnect2(); 640 if ((type2 == HitPointType.LEVEL_XING_A) || (type2 == HitPointType.LEVEL_XING_C)) { 641 lb2 = lx.getLayoutBlockAC(); 642 } else { 643 lb2 = lx.getLayoutBlockBD(); 644 } 645 if ((lb2 != null) && (lb1 != lb2)) { 646 // have a block boundary, create a LayoutConnectivity 647 log.debug("Block boundary (''{}''<->''{}'') found at {}", lb1, lb2, this); 648 lc = new LayoutConnectivity(lb1, lb2); 649 lc.setConnections(this, lx, type2, null); 650 lc.setDirection(models.computeDirection( 651 getConnect1(), type1, 652 getConnect2(), type2 ) ); 653 results.add(lc); 654 } 655 } else if (HitPointType.isSlipHitType(type2)) { 656 // have connection to a slip crossing 657 LayoutSlip ls = (LayoutSlip) getConnect2(); 658 lb2 = ls.getLayoutBlock(); 659 if ((lb2 != null) && (lb1 != lb2)) { 660 // have a block boundary, create a LayoutConnectivity 661 log.debug("Block boundary (''{}''<->''{}'') found at {}", lb1, lb2, this); 662 lc = new LayoutConnectivity(lb1, lb2); 663 lc.setConnections(this, ls, type2, null); 664 lc.setDirection(models.computeDirection( 665 getConnect1(), type1, 666 getConnect2(), type2 ) ); 667 results.add(lc); 668 } 669 } 670 } // if (lb1 != null) 671 return results; 672 } // getLayoutConnectivity() 673 674 /** 675 * {@inheritDoc} 676 */ 677 @Override 678 public List<HitPointType> checkForFreeConnections() { 679 return new ArrayList<>(); 680 } 681 682 /** 683 * {@inheritDoc} 684 */ 685 @Override 686 public boolean checkForUnAssignedBlocks() { 687 return (getLayoutBlock() != null); 688 } 689 690 /** 691 * {@inheritDoc} 692 */ 693 @Override 694 public void checkForNonContiguousBlocks( 695 @Nonnull HashMap<String, List<Set<String>>> blockNamesToTrackNameSetsMap) { 696 /* 697 * For each (non-null) blocks of this track do: 698 * #1) If it's got an entry in the blockNamesToTrackNameSetMap then 699 * #2) If this track is already in the TrackNameSet for this block 700 * then return (done!) 701 * #3) else add a new set (with this block/track) to 702 * blockNamesToTrackNameSetMap and 703 * #4) collect all the connections in this block 704 * <p> 705 * Basically, we're maintaining contiguous track sets for each block found 706 * (in blockNamesToTrackNameSetMap) 707 */ 708 List<Set<String>> TrackNameSets = null; 709 Set<String> TrackNameSet = null; // assume not found (pessimist!) 710 String blockName = getBlockName(); 711 if (!blockName.isEmpty()) { 712 TrackNameSets = blockNamesToTrackNameSetsMap.get(blockName); 713 if (TrackNameSets != null) { //(#1) 714 for (Set<String> checkTrackNameSet : TrackNameSets) { 715 if (checkTrackNameSet.contains(getName())) { //(#2) 716 TrackNameSet = checkTrackNameSet; 717 break; 718 } 719 } 720 } else { //(#3) 721 log.debug("*New block (''{}'') trackNameSets", blockName); 722 TrackNameSets = new ArrayList<>(); 723 blockNamesToTrackNameSetsMap.put(blockName, TrackNameSets); 724 } 725 if (TrackNameSet == null) { 726 TrackNameSet = new LinkedHashSet<>(); 727 TrackNameSets.add(TrackNameSet); 728 } 729 if (TrackNameSet.add(getName())) { 730 log.debug("* Add track ''{}'' to TrackNameSets for block ''{}''", getName(), blockName); 731 } 732 //(#4) 733 if (connect1 != null) { 734 connect1.collectContiguousTracksNamesInBlockNamed(blockName, TrackNameSet); 735 } 736 if (connect2 != null) { //(#4) 737 connect2.collectContiguousTracksNamesInBlockNamed(blockName, TrackNameSet); 738 } 739 } 740 } 741 742 /** 743 * {@inheritDoc} 744 */ 745 @Override 746 public void collectContiguousTracksNamesInBlockNamed(@Nonnull String blockName, 747 @Nonnull Set<String> TrackNameSet) { 748 if (!TrackNameSet.contains(getName())) { 749 // is this the blockName we're looking for? 750 if (getBlockName().equals(blockName)) { 751 // if we are added to the TrackNameSet 752 if (TrackNameSet.add(getName())) { 753 log.debug("* Add track ''{}''for block ''{}''", getName(), blockName); 754 } 755 // these should never be null... but just in case... 756 // it's time to play... flood your neighbours! 757 if (connect1 != null) { 758 connect1.collectContiguousTracksNamesInBlockNamed(blockName, TrackNameSet); 759 } 760 if (connect2 != null) { 761 connect2.collectContiguousTracksNamesInBlockNamed(blockName, TrackNameSet); 762 } 763 } 764 } 765 } 766 767 /** 768 * {@inheritDoc} 769 */ 770 @Override 771 public void setAllLayoutBlocks(LayoutBlock layoutBlock) { 772 setLayoutBlock(layoutBlock); 773 } 774 775 /** 776 * {@inheritDoc} 777 */ 778 @Override 779 public String getTypeName() { 780 return Bundle.getMessage("TypeName_TrackSegment"); 781 } 782 783 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(TrackSegment.class); 784}