001package jmri.jmrit.display.layoutEditor; 002 003import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 004 005import java.text.MessageFormat; 006import java.util.*; 007 008import javax.annotation.Nonnull; 009 010import jmri.*; 011import jmri.jmrit.signalling.SignallingGuiTools; 012 013/** 014 * A LevelXing is two track segment on a layout that cross at an angle. 015 * <p> 016 * A LevelXing has four connection points, designated A, B, C, and D. At the 017 * crossing, A-C and B-D are straight segments. A train proceeds through the 018 * crossing on either of these segments. 019 * <br> 020 * <pre> 021 * A D 022 * \\ // 023 * X 024 * // \\ 025 * B C 026 * </pre> 027 * <br> 028 * Each straight segment carries Block information. A-C and B-D may be in the 029 * same or different Layout Blocks. 030 * <p> 031 * For drawing purposes, each LevelXing carries a center point and displacements 032 * for A and B. The displacements for C = - the displacement for A, and the 033 * displacement for D = - the displacement for B. The center point and these 034 * displacements may be adjusted by the user when in edit mode. 035 * <p> 036 * When LevelXings are first created, there are no connections. Block 037 * information and connections are added when available. 038 * <p> 039 * Signal Head names are saved here to keep track of where signals are. 040 * LevelXing only serves as a storage place for signal head names. The names are 041 * placed here by Set Signals at Level Crossing in Tools menu. 042 * 043 * @author Dave Duchamp Copyright (c) 2004-2007 044 * @author George Warner Copyright (c) 2017-2019 045 */ 046public class LevelXing extends LayoutTrack { 047 048 /** 049 * Constructor. 050 * @param id ID string. 051 * @param models the main layout editor. 052 */ 053 public LevelXing(String id, LayoutEditor models) { 054 super(id, models); 055 } 056 057 // defined constants 058 // operational instance variables (not saved between sessions) 059 private NamedBeanHandle<LayoutBlock> namedLayoutBlockAC = null; 060 private NamedBeanHandle<LayoutBlock> namedLayoutBlockBD = null; 061 062 protected NamedBeanHandle<SignalHead> signalAHeadNamed = null; // signal at A track junction 063 protected NamedBeanHandle<SignalHead> signalBHeadNamed = null; // signal at B track junction 064 protected NamedBeanHandle<SignalHead> signalCHeadNamed = null; // signal at C track junction 065 protected NamedBeanHandle<SignalHead> signalDHeadNamed = null; // signal at D track junction 066 067 protected NamedBeanHandle<SignalMast> signalAMastNamed = null; // signal at A track junction 068 protected NamedBeanHandle<SignalMast> signalBMastNamed = null; // signal at B track junction 069 protected NamedBeanHandle<SignalMast> signalCMastNamed = null; // signal at C track junction 070 protected NamedBeanHandle<SignalMast> signalDMastNamed = null; // signal at D track junction 071 072 private NamedBeanHandle<Sensor> sensorANamed = null; // sensor at A track junction 073 private NamedBeanHandle<Sensor> sensorBNamed = null; // sensor at B track junction 074 private NamedBeanHandle<Sensor> sensorCNamed = null; // sensor at C track junction 075 private NamedBeanHandle<Sensor> sensorDNamed = null; // sensor at D track junction 076 077 private LayoutTrack connectA = null; 078 private LayoutTrack connectB = null; 079 private LayoutTrack connectC = null; 080 private LayoutTrack connectD = null; 081 082 public enum Geometry { 083 POINTA, POINTB, POINTC, POINTD 084 } 085 086 // temporary reference to the Editor that will eventually be part of View 087 //private final jmri.jmrit.display.models.LayoutEditorDialogs.LevelXingEditor editor; 088 089 // this should only be used for debugging 090 @Override 091 public String toString() { 092 return "LevelXing " + getName(); 093 } 094 095 /* 096 * Accessor methods 097 */ 098 @Nonnull 099 public String getBlockNameAC() { 100 String result = null; 101 if (namedLayoutBlockAC != null) { 102 result = namedLayoutBlockAC.getName(); 103 } 104 return ((result == null) ? "" : result); 105 } 106 107 @Nonnull 108 public String getBlockNameBD() { 109 String result = getBlockNameAC(); 110 if (namedLayoutBlockBD != null) { 111 result = namedLayoutBlockBD.getName(); 112 } 113 return result; 114 } 115 116 public SignalHead getSignalHead(Geometry loc) { 117 NamedBeanHandle<SignalHead> namedBean = null; 118 switch (loc) { 119 case POINTA: 120 namedBean = signalAHeadNamed; 121 break; 122 case POINTB: 123 namedBean = signalBHeadNamed; 124 break; 125 case POINTC: 126 namedBean = signalCHeadNamed; 127 break; 128 case POINTD: 129 namedBean = signalDHeadNamed; 130 break; 131 default: 132 log.warn("{}.getSignalHead({})", getName(), loc); 133 break; 134 } 135 if (namedBean != null) { 136 return namedBean.getBean(); 137 } 138 return null; 139 } 140 141 public SignalMast getSignalMast(Geometry loc) { 142 NamedBeanHandle<SignalMast> namedBean = null; 143 switch (loc) { 144 case POINTA: 145 namedBean = signalAMastNamed; 146 break; 147 case POINTB: 148 namedBean = signalBMastNamed; 149 break; 150 case POINTC: 151 namedBean = signalCMastNamed; 152 break; 153 case POINTD: 154 namedBean = signalDMastNamed; 155 break; 156 default: 157 log.warn("{}.getSignalMast({})", getName(), loc); 158 break; 159 } 160 if (namedBean != null) { 161 return namedBean.getBean(); 162 } 163 return null; 164 } 165 166 public Sensor getSensor(Geometry loc) { 167 NamedBeanHandle<Sensor> namedBean = null; 168 switch (loc) { 169 case POINTA: 170 namedBean = sensorANamed; 171 break; 172 case POINTB: 173 namedBean = sensorBNamed; 174 break; 175 case POINTC: 176 namedBean = sensorCNamed; 177 break; 178 case POINTD: 179 namedBean = sensorDNamed; 180 break; 181 default: 182 log.warn("{}.getSensor({})", getName(), loc); 183 break; 184 } 185 if (namedBean != null) { 186 return namedBean.getBean(); 187 } 188 return null; 189 } 190 191 @Nonnull 192 public String getSignalAName() { 193 if (signalAHeadNamed != null) { 194 return signalAHeadNamed.getName(); 195 } 196 return ""; 197 } 198 199 public void setSignalAName(String signalHead) { 200 if (signalHead == null || signalHead.isEmpty()) { 201 signalAHeadNamed = null; 202 return; 203 } 204 205 SignalHead head = InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(signalHead); 206 if (head != null) { 207 signalAHeadNamed = InstanceManager.getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle(signalHead, head); 208 } else { 209 signalAHeadNamed = null; 210 } 211 } 212 213 @Nonnull 214 public String getSignalBName() { 215 if (signalBHeadNamed != null) { 216 return signalBHeadNamed.getName(); 217 } 218 return ""; 219 } 220 221 public void setSignalBName(String signalHead) { 222 if (signalHead == null || signalHead.isEmpty()) { 223 signalBHeadNamed = null; 224 return; 225 } 226 227 SignalHead head = InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(signalHead); 228 if (head != null) { 229 signalBHeadNamed = InstanceManager.getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle(signalHead, head); 230 } else { 231 signalBHeadNamed = null; 232 } 233 } 234 235 @Nonnull 236 public String getSignalCName() { 237 if (signalCHeadNamed != null) { 238 return signalCHeadNamed.getName(); 239 } 240 return ""; 241 } 242 243 public void setSignalCName(String signalHead) { 244 if (signalHead == null || signalHead.isEmpty()) { 245 signalCHeadNamed = null; 246 return; 247 } 248 249 SignalHead head = InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(signalHead); 250 if (head != null) { 251 signalCHeadNamed = InstanceManager.getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle(signalHead, head); 252 } else { 253 signalCHeadNamed = null; 254 } 255 } 256 257 @Nonnull 258 public String getSignalDName() { 259 if (signalDHeadNamed != null) { 260 return signalDHeadNamed.getName(); 261 } 262 return ""; 263 } 264 265 public void setSignalDName(String signalHead) { 266 if (signalHead == null || signalHead.isEmpty()) { 267 signalDHeadNamed = null; 268 return; 269 } 270 271 SignalHead head = InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(signalHead); 272 if (head != null) { 273 signalDHeadNamed = InstanceManager.getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle(signalHead, head); 274 } else { 275 signalDHeadNamed = null; 276 } 277 } 278 279 public void removeBeanReference(jmri.NamedBean nb) { 280 if (nb == null) { 281 return; 282 } 283 if (nb instanceof SignalMast) { 284 if (nb.equals(getSignalAMast())) { 285 setSignalAMast(null); 286 return; 287 } 288 if (nb.equals(getSignalBMast())) { 289 setSignalBMast(null); 290 return; 291 } 292 if (nb.equals(getSignalCMast())) { 293 setSignalCMast(null); 294 return; 295 } 296 if (nb.equals(getSignalDMast())) { 297 setSignalDMast(null); 298 return; 299 } 300 } 301 if (nb instanceof Sensor) { 302 if (nb.equals(getSensorA())) { 303 setSensorAName(null); 304 return; 305 } 306 if (nb.equals(getSensorB())) { 307 setSensorBName(null); 308 return; 309 } 310 if (nb.equals(getSensorC())) { 311 setSensorCName(null); 312 return; 313 } 314 if (nb.equals(getSensorD())) { 315 setSensorDName(null); 316 return; 317 } 318 } 319 if (nb instanceof SignalHead) { 320 if (nb.equals(getSignalHead(Geometry.POINTA))) { 321 setSignalAName(null); 322 return; 323 } 324 if (nb.equals(getSignalHead(Geometry.POINTB))) { 325 setSignalBName(null); 326 return; 327 } 328 if (nb.equals(getSignalHead(Geometry.POINTC))) { 329 setSignalCName(null); 330 return; 331 } 332 if (nb.equals(getSignalHead(Geometry.POINTD))) { 333 setSignalDName(null); 334 } 335 } 336 } 337 338 public String getSignalAMastName() { 339 if (signalAMastNamed != null) { 340 return signalAMastNamed.getName(); 341 } 342 return ""; 343 } 344 345 public SignalMast getSignalAMast() { 346 if (signalAMastNamed != null) { 347 return signalAMastNamed.getBean(); 348 } 349 return null; 350 } 351 352 public void setSignalAMast(String signalMast) { 353 if (signalMast == null || signalMast.isEmpty()) { 354 signalAMastNamed = null; 355 return; 356 } 357 358 try { 359 SignalMast mast = InstanceManager.getDefault(jmri.SignalMastManager.class).provideSignalMast(signalMast); 360 signalAMastNamed = InstanceManager.getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle(signalMast, mast); 361 } catch (IllegalArgumentException ex) { 362 signalAMastNamed = null; 363 } 364 } 365 366 public String getSignalBMastName() { 367 if (signalBMastNamed != null) { 368 return signalBMastNamed.getName(); 369 } 370 return ""; 371 } 372 373 public SignalMast getSignalBMast() { 374 if (signalBMastNamed != null) { 375 return signalBMastNamed.getBean(); 376 } 377 return null; 378 } 379 380 public void setSignalBMast(String signalMast) { 381 if (signalMast == null || signalMast.isEmpty()) { 382 signalBMastNamed = null; 383 return; 384 } 385 386 try { 387 SignalMast mast = InstanceManager.getDefault(jmri.SignalMastManager.class).provideSignalMast(signalMast); 388 signalBMastNamed = InstanceManager.getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle(signalMast, mast); 389 } catch (IllegalArgumentException ex) { 390 signalBMastNamed = null; 391 } 392 } 393 394 public String getSignalCMastName() { 395 if (signalCMastNamed != null) { 396 return signalCMastNamed.getName(); 397 } 398 return ""; 399 } 400 401 public SignalMast getSignalCMast() { 402 if (signalCMastNamed != null) { 403 return signalCMastNamed.getBean(); 404 } 405 return null; 406 } 407 408 public void setSignalCMast(String signalMast) { 409 if (signalMast == null || signalMast.isEmpty()) { 410 signalCMastNamed = null; 411 return; 412 } 413 414 try { 415 SignalMast mast = InstanceManager.getDefault(jmri.SignalMastManager.class).provideSignalMast(signalMast); 416 signalCMastNamed = InstanceManager.getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle(signalMast, mast); 417 } catch (IllegalArgumentException ex) { 418 signalCMastNamed = null; 419 } 420 } 421 422 public String getSignalDMastName() { 423 if (signalDMastNamed != null) { 424 return signalDMastNamed.getName(); 425 } 426 return ""; 427 } 428 429 public SignalMast getSignalDMast() { 430 if (signalDMastNamed != null) { 431 return signalDMastNamed.getBean(); 432 } 433 return null; 434 } 435 436 public void setSignalDMast(String signalMast) { 437 if (signalMast == null || signalMast.isEmpty()) { 438 signalDMastNamed = null; 439 return; 440 } 441 442 try { 443 SignalMast mast = InstanceManager.getDefault(jmri.SignalMastManager.class).provideSignalMast(signalMast); 444 signalDMastNamed = InstanceManager.getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle(signalMast, mast); 445 } catch (IllegalArgumentException ex) { 446 signalDMastNamed = null; 447 } 448 } 449 450 public String getSensorAName() { 451 if (sensorANamed != null) { 452 return sensorANamed.getName(); 453 } 454 return ""; 455 } 456 457 public Sensor getSensorA() { 458 if (sensorANamed != null) { 459 return sensorANamed.getBean(); 460 } 461 return null; 462 } 463 464 public void setSensorAName(String sensorName) { 465 if (sensorName == null || sensorName.isEmpty()) { 466 sensorANamed = null; 467 return; 468 } 469 470 try { 471 Sensor sensor = InstanceManager.sensorManagerInstance().provideSensor(sensorName); 472 sensorANamed = InstanceManager.getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle(sensorName, sensor); 473 } catch (IllegalArgumentException ex) { 474 sensorANamed = null; 475 } 476 } 477 478 public String getSensorBName() { 479 if (sensorBNamed != null) { 480 return sensorBNamed.getName(); 481 } 482 return ""; 483 } 484 485 public Sensor getSensorB() { 486 if (sensorBNamed != null) { 487 return sensorBNamed.getBean(); 488 } 489 return null; 490 } 491 492 public void setSensorBName(String sensorName) { 493 if (sensorName == null || sensorName.isEmpty()) { 494 sensorBNamed = null; 495 return; 496 } 497 498 try { 499 Sensor sensor = InstanceManager.sensorManagerInstance().provideSensor(sensorName); 500 sensorBNamed = InstanceManager.getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle(sensorName, sensor); 501 } catch (IllegalArgumentException ex) { 502 sensorBNamed = null; 503 } 504 } 505 506 public String getSensorCName() { 507 if (sensorCNamed != null) { 508 return sensorCNamed.getName(); 509 } 510 return ""; 511 } 512 513 public Sensor getSensorC() { 514 if (sensorCNamed != null) { 515 return sensorCNamed.getBean(); 516 } 517 return null; 518 } 519 520 public void setSensorCName(String sensorName) { 521 if (sensorName == null || sensorName.isEmpty()) { 522 sensorCNamed = null; 523 return; 524 } 525 526 try { 527 Sensor sensor = InstanceManager.sensorManagerInstance().provideSensor(sensorName); 528 sensorCNamed = InstanceManager.getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle(sensorName, sensor); 529 } catch (IllegalArgumentException ex) { 530 sensorCNamed = null; 531 } 532 } 533 534 public String getSensorDName() { 535 if (sensorDNamed != null) { 536 return sensorDNamed.getName(); 537 } 538 return ""; 539 } 540 541 public Sensor getSensorD() { 542 if (sensorDNamed != null) { 543 return sensorDNamed.getBean(); 544 } 545 return null; 546 } 547 548 public void setSensorDName(String sensorName) { 549 if (sensorName == null || sensorName.isEmpty()) { 550 sensorDNamed = null; 551 return; 552 } 553 554 try { 555 Sensor sensor = InstanceManager.sensorManagerInstance().provideSensor(sensorName); 556 sensorDNamed = InstanceManager.getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle(sensorName, sensor); 557 } catch (IllegalArgumentException ex) { 558 sensorDNamed = null; 559 } 560 } 561 562 /** 563 * {@inheritDoc} 564 */ 565 @Override 566 public LayoutTrack getConnection(HitPointType connectionType) throws jmri.JmriException { 567 switch (connectionType) { 568 case LEVEL_XING_A: 569 return connectA; 570 case LEVEL_XING_B: 571 return connectB; 572 case LEVEL_XING_C: 573 return connectC; 574 case LEVEL_XING_D: 575 return connectD; 576 default: 577 break; 578 } 579 String errstring = MessageFormat.format("{0}.getConnection({1}); invalid connection type", getName(), connectionType); //I18IN 580 log.error("will throw {}", errstring); 581 throw new jmri.JmriException(errstring); 582 } 583 584 /** 585 * {@inheritDoc} 586 */ 587 @Override 588 public void setConnection(HitPointType connectionType, LayoutTrack o, HitPointType type) throws jmri.JmriException { 589 if ((type != HitPointType.TRACK) && (type != HitPointType.NONE)) { 590 String errString = MessageFormat.format("{0}.setConnection({1}, {2}, {3}); invalid type", 591 getName(), connectionType, (o == null) ? "null" : o.getName(), type); 592 log.error("will throw {}", errString); 593 throw new jmri.JmriException(errString); 594 } 595 switch (connectionType) { 596 case LEVEL_XING_A: 597 connectA = o; 598 break; 599 case LEVEL_XING_B: 600 connectB = o; 601 break; 602 case LEVEL_XING_C: 603 connectC = o; 604 break; 605 case LEVEL_XING_D: 606 connectD = o; 607 break; 608 default: 609 String errString = MessageFormat.format("{0}.setConnection({1}, {2}, {3}); invalid connection type", 610 getName(), connectionType, (o == null) ? "null" : o.getName(), type); 611 log.error("will throw {}", errString); 612 throw new jmri.JmriException(errString); 613 } 614 } 615 616 public LayoutTrack getConnectA() { 617 return connectA; 618 } 619 620 public LayoutTrack getConnectB() { 621 return connectB; 622 } 623 624 public LayoutTrack getConnectC() { 625 return connectC; 626 } 627 628 public LayoutTrack getConnectD() { 629 return connectD; 630 } 631 632 public void setConnectA(LayoutTrack o, HitPointType type) { 633 connectA = o; 634 if ((connectA != null) && (type != HitPointType.TRACK)) { 635 log.error("{}.setConnectA(({}, {}); invalid type", 636 getName(), o.getName(), type); 637 } 638 } 639 640 public void setConnectB(LayoutTrack o, HitPointType type) { 641 connectB = o; 642 if ((connectB != null) && (type != HitPointType.TRACK)) { 643 log.error("{}.setConnectB(({}, {}); invalid type", 644 getName(), o.getName(), type); 645 } 646 } 647 648 public void setConnectC(LayoutTrack o, HitPointType type) { 649 connectC = o; 650 if ((connectC != null) && (type != HitPointType.TRACK)) { 651 log.error("{}.setConnectC(({}, {}); invalid type", 652 getName(), o.getName(), type); 653 } 654 } 655 656 public void setConnectD(LayoutTrack o, HitPointType type) { 657 connectD = o; 658 if ((connectD != null) && (type != HitPointType.TRACK)) { 659 log.error("{}.setConnectD(({}, {}); invalid type", 660 getName(), o.getName(), type); 661 } 662 } 663 664 public LayoutBlock getLayoutBlockAC() { 665 return (namedLayoutBlockAC != null) ? namedLayoutBlockAC.getBean() : null; 666 } 667 668 public LayoutBlock getLayoutBlockBD() { 669 return (namedLayoutBlockBD != null) ? namedLayoutBlockBD.getBean() : getLayoutBlockAC(); 670 } 671 672 /** 673 * Add Layout Blocks. 674 * @param newLayoutBlock the layout block to add. 675 */ 676 @SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE", justification = "Null is accepted as a valid value") // temporary, as added in error 677 public void setLayoutBlockAC(LayoutBlock newLayoutBlock) { 678 LayoutBlock blockAC = getLayoutBlockAC(); 679 LayoutBlock blockBD = getLayoutBlockBD(); 680 if (blockAC != newLayoutBlock) { 681 // block 1 has changed, if old block exists, decrement use 682 if ((blockAC != null) && (blockAC != blockBD)) { 683 blockAC.decrementUse(); 684 } 685 blockAC = newLayoutBlock; 686 if (newLayoutBlock != null) { 687 namedLayoutBlockAC = InstanceManager.getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle(newLayoutBlock.getUserName(), newLayoutBlock); 688 } else { 689 namedLayoutBlockAC = null; 690 } 691 692 // decrement use if block was previously counted 693 if ((blockAC != null) && (blockAC == blockBD)) { 694 blockAC.decrementUse(); 695 } 696 } 697 } 698 699 @SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE", justification = "Null is accepted as a valid value") // temporary, as added in error 700 public void setLayoutBlockBD(LayoutBlock newLayoutBlock) { 701 LayoutBlock blockAC = getLayoutBlockAC(); 702 LayoutBlock blockBD = getLayoutBlockBD(); 703 if (blockBD != newLayoutBlock) { 704 // block 1 has changed, if old block exists, decrement use 705 if ((blockBD != null) && (blockBD != blockAC)) { 706 blockBD.decrementUse(); 707 } 708 blockBD = newLayoutBlock; 709 if (newLayoutBlock != null) { 710 namedLayoutBlockBD = InstanceManager.getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle(newLayoutBlock.getUserName(), newLayoutBlock); 711 } else { 712 namedLayoutBlockBD = null; 713 } 714 // decrement use if block was previously counted 715 if ((blockBD != null) && (blockBD == blockAC)) { 716 blockBD.decrementUse(); 717 } 718 } 719 720 } 721 722 public void updateBlockInfo() { 723 LayoutBlock blockAC = getLayoutBlockAC(); 724 LayoutBlock blockBD = getLayoutBlockBD(); 725 LayoutBlock b1 = null; 726 LayoutBlock b2 = null; 727 if (blockAC != null) { 728 blockAC.updatePaths(); 729 } 730 if (connectA != null) { 731 b1 = ((TrackSegment) connectA).getLayoutBlock(); 732 if ((b1 != null) && (b1 != blockAC)) { 733 b1.updatePaths(); 734 } 735 } 736 if (connectC != null) { 737 b2 = ((TrackSegment) connectC).getLayoutBlock(); 738 if ((b2 != null) && (b2 != blockAC) && (b2 != b1)) { 739 b2.updatePaths(); 740 } 741 } 742 if (blockBD != null) { 743 blockBD.updatePaths(); 744 } 745 if (connectB != null) { 746 b1 = ((TrackSegment) connectB).getLayoutBlock(); 747 if ((b1 != null) && (b1 != blockBD)) { 748 b1.updatePaths(); 749 } 750 } 751 if (connectD != null) { 752 b2 = ((TrackSegment) connectD).getLayoutBlock(); 753 if ((b2 != null) && (b2 != blockBD) && (b2 != b1)) { 754 b2.updatePaths(); 755 } 756 } 757 reCheckBlockBoundary(); 758 } 759 760 void removeSML(SignalMast signalMast) { 761 if (signalMast == null) { 762 return; 763 } 764 if (jmri.InstanceManager.getDefault(LayoutBlockManager.class).isAdvancedRoutingEnabled() && InstanceManager.getDefault(jmri.SignalMastLogicManager.class).isSignalMastUsed(signalMast)) { 765 SignallingGuiTools.removeSignalMastLogic(null, signalMast); 766 } 767 } 768 769 /** 770 * Test if mainline track or not. 771 * 772 * @return true if either connecting track segment is mainline; Defaults to 773 * not mainline if connecting track segments are missing 774 */ 775 public boolean isMainlineAC() { 776 if (((connectA != null) && (((TrackSegment) connectA).isMainline())) 777 || ((connectC != null) && (((TrackSegment) connectC).isMainline()))) { 778 return true; 779 } else { 780 return false; 781 } 782 } 783 784 public boolean isMainlineBD() { 785 if (((connectB != null) && (((TrackSegment) connectB).isMainline())) 786 || ((connectD != null) && (((TrackSegment) connectD).isMainline()))) { 787 return true; 788 } else { 789 return false; 790 } 791 } 792 793 @Override 794 public boolean isMainline() { 795 return (isMainlineAC() || isMainlineBD()); 796 } 797 798 // initialization instance variables (used when loading a LayoutEditor) 799 public String connectAName = ""; 800 public String connectBName = ""; 801 public String connectCName = ""; 802 public String connectDName = ""; 803 804 public String tLayoutBlockNameAC = ""; 805 public String tLayoutBlockNameBD = ""; 806 807 /** 808 * Initialization method The above variables are initialized by 809 * PositionablePointXml, then the following method is called after the 810 * entire LayoutEditor is loaded to set the specific TrackSegment objects. 811 */ 812 @Override 813 public void setObjects(LayoutEditor p) { 814 connectA = p.getFinder().findTrackSegmentByName(connectAName); 815 connectB = p.getFinder().findTrackSegmentByName(connectBName); 816 connectC = p.getFinder().findTrackSegmentByName(connectCName); 817 connectD = p.getFinder().findTrackSegmentByName(connectDName); 818 819 LayoutBlock lb; 820 if (!tLayoutBlockNameAC.isEmpty()) { 821 lb = p.provideLayoutBlock(tLayoutBlockNameAC); 822 String userName = lb.getUserName(); 823 if (userName != null) { 824 namedLayoutBlockAC = InstanceManager.getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle(userName, lb); 825 if (namedLayoutBlockBD != namedLayoutBlockAC) { 826 lb.incrementUse(); 827 } 828 } else { 829 log.error("LevelXing.setObjects(); bad blockname AC ''{}''", tLayoutBlockNameAC); 830 namedLayoutBlockAC = null; 831 } 832 tLayoutBlockNameAC = null; //release this memory 833 } 834 835 if (!tLayoutBlockNameBD.isEmpty()) { 836 lb = p.provideLayoutBlock(tLayoutBlockNameBD); 837 String userName = lb.getUserName(); 838 if (userName != null) { 839 namedLayoutBlockBD = InstanceManager.getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle(userName, lb); 840 if (namedLayoutBlockBD != namedLayoutBlockAC) { 841 lb.incrementUse(); 842 } 843 } else { 844 log.error("{}.setObjects(); bad blockname BD ''{}''", this, tLayoutBlockNameBD); 845 namedLayoutBlockBD = null; 846 } 847 tLayoutBlockNameBD = null; //release this memory 848 } 849 } 850 851 /** 852 * {@inheritDoc} 853 */ 854 @Override 855 public boolean canRemove() { 856 ArrayList<String> beanReferences = getBeanReferences("All"); // NOI18N 857 if (!beanReferences.isEmpty()) { 858 models.displayRemoveWarning(this, beanReferences, "LevelCrossing"); // NOI18N 859 } 860 return beanReferences.isEmpty(); 861 } 862 863 /** 864 * Build a list of sensors, signal heads, and signal masts attached to a 865 * level crossing point. 866 * 867 * @param pointName Specify the point (A-D) or all (All) points. 868 * @return a list of bean reference names. 869 */ 870 public ArrayList<String> getBeanReferences(String pointName) { 871 ArrayList<String> references = new ArrayList<>(); 872 if (pointName.equals("A") || pointName.equals("All")) { // NOI18N 873 if (!getSignalAMastName().isEmpty()) { 874 references.add(getSignalAMastName()); 875 } 876 if (!getSensorAName().isEmpty()) { 877 references.add(getSensorAName()); 878 } 879 if (!getSignalAName().isEmpty()) { 880 references.add(getSignalAName()); 881 } 882 } 883 if (pointName.equals("B") || pointName.equals("All")) { // NOI18N 884 if (!getSignalBMastName().isEmpty()) { 885 references.add(getSignalBMastName()); 886 } 887 if (!getSensorBName().isEmpty()) { 888 references.add(getSensorBName()); 889 } 890 if (!getSignalBName().isEmpty()) { 891 references.add(getSignalBName()); 892 } 893 } 894 if (pointName.equals("C") || pointName.equals("All")) { // NOI18N 895 if (!getSignalCMastName().isEmpty()) { 896 references.add(getSignalCMastName()); 897 } 898 if (!getSensorCName().isEmpty()) { 899 references.add(getSensorCName()); 900 } 901 if (!getSignalCName().isEmpty()) { 902 references.add(getSignalCName()); 903 } 904 } 905 if (pointName.equals("D") || pointName.equals("All")) { // NOI18N 906 if (!getSignalDMastName().isEmpty()) { 907 references.add(getSignalDMastName()); 908 } 909 if (!getSensorDName().isEmpty()) { 910 references.add(getSensorDName()); 911 } 912 if (!getSignalDName().isEmpty()) { 913 references.add(getSignalDName()); 914 } 915 } 916 return references; 917 } 918 919 920 public String[] getBlockBoundaries() { 921 final String[] boundaryBetween = new String[4]; 922 923 String blockNameAC = getBlockNameAC(); 924 String blockNameBD = getBlockNameBD(); 925 926 LayoutBlock blockAC = getLayoutBlockAC(); 927 LayoutBlock blockBD = getLayoutBlockAC(); 928 929 if (!blockNameAC.isEmpty() && (blockAC != null)) { 930 if ((connectA instanceof TrackSegment) && (((TrackSegment) connectA).getLayoutBlock() != blockAC)) { 931 try { 932 boundaryBetween[0] = (((TrackSegment) connectA).getLayoutBlock().getDisplayName() + " - " + blockAC.getDisplayName()); 933 } catch (java.lang.NullPointerException e) { 934 //Can be considered normal if tracksegement hasn't yet been allocated a block 935 log.debug("TrackSegement at connection A doesn't contain a layout block"); 936 } 937 } 938 if ((connectC instanceof TrackSegment) && (((TrackSegment) connectC).getLayoutBlock() != blockAC)) { 939 try { 940 boundaryBetween[2] = (((TrackSegment) connectC).getLayoutBlock().getDisplayName() + " - " + blockAC.getDisplayName()); 941 } catch (java.lang.NullPointerException e) { 942 //Can be considered normal if tracksegement hasn't yet been allocated a block 943 log.debug("TrackSegement at connection C doesn't contain a layout block"); 944 } 945 } 946 } 947 if (!blockNameBD.isEmpty() && (blockBD != null)) { 948 if ((connectB instanceof TrackSegment) && (((TrackSegment) connectB).getLayoutBlock() != blockBD)) { 949 try { 950 boundaryBetween[1] = (((TrackSegment) connectB).getLayoutBlock().getDisplayName() + " - " + blockBD.getDisplayName()); 951 } catch (java.lang.NullPointerException e) { 952 //Can be considered normal if tracksegement hasn't yet been allocated a block 953 log.debug("TrackSegement at connection B doesn't contain a layout block"); 954 } 955 } 956 if ((connectD instanceof TrackSegment) && (((TrackSegment) connectD).getLayoutBlock() != blockBD)) { 957 try { 958 boundaryBetween[3] = (((TrackSegment) connectD).getLayoutBlock().getDisplayName() + " - " + blockBD.getDisplayName()); 959 } catch (java.lang.NullPointerException e) { 960 //Can be considered normal if tracksegement hasn't yet been allocated a block 961 log.debug("TrackSegement at connection D doesn't contain a layout block"); 962 } 963 } 964 } 965 return boundaryBetween; 966 } 967 968 /** 969 * Remove this object from display and persistance. 970 */ 971 public void remove() { 972 // remove from persistance by flagging inactive 973 active = false; 974 } 975 976 boolean active = true; 977 978 /** 979 * Get if active. 980 * "active" means that the object is still displayed, and should be stored. 981 * @return true if still displayed, else false. 982 */ 983 public boolean isActive() { 984 return active; 985 } 986 987 ArrayList<SignalMast> sml = new ArrayList<>(); 988 989 public void addSignalMastLogic(SignalMast sm) { 990 if (sml.contains(sm)) { 991 return; 992 } 993 if (sml.isEmpty()) { 994 sml.add(sm); 995 return; 996 } 997 SignalMastLogic sl = InstanceManager.getDefault(jmri.SignalMastLogicManager.class).getSignalMastLogic(sm); 998 for (SignalMast signalMast : sml) { 999 SignalMastLogic s = InstanceManager.getDefault(SignalMastLogicManager.class).getSignalMastLogic(signalMast); 1000 if (s != null) { 1001 s.setConflictingLogic(sm, this); 1002 } 1003 sl.setConflictingLogic(signalMast, this); 1004 } 1005 sml.add(sm); 1006 } 1007 1008 public void removeSignalMastLogic(SignalMast sm) { 1009 if (!sml.contains(sm)) { 1010 return; 1011 } 1012 sml.remove(sm); 1013 if (sml.isEmpty()) { 1014 return; 1015 } 1016 for (int i = 0; i < sml.size(); i++) { 1017 SignalMastLogic s = InstanceManager.getDefault(jmri.SignalMastLogicManager.class).getSignalMastLogic(sm); 1018 if (s != null) { 1019 s.removeConflictingLogic(sm, this); 1020 } 1021 } 1022 } 1023 1024 /* 1025 * {@inheritDoc} 1026 */ 1027 @Override 1028 public void reCheckBlockBoundary() { 1029 // nothing to see here... move along... 1030 } 1031 1032 /* 1033 * {@inheritDoc} 1034 */ 1035 @Override 1036 protected ArrayList<LayoutConnectivity> getLayoutConnectivity() { 1037 // nothing to see here... move along... 1038 return null; 1039 } 1040 1041 /** 1042 * {@inheritDoc} 1043 */ 1044 @Override 1045 public List<HitPointType> checkForFreeConnections() { 1046 List<HitPointType> result = new ArrayList<>(); 1047 1048 //check the A connection point 1049 if (getConnectA() == null) { 1050 result.add(HitPointType.LEVEL_XING_A); 1051 } 1052 1053 //check the B connection point 1054 if (getConnectB() == null) { 1055 result.add(HitPointType.LEVEL_XING_B); 1056 } 1057 1058 //check the C connection point 1059 if (getConnectC() == null) { 1060 result.add(HitPointType.LEVEL_XING_C); 1061 } 1062 1063 //check the D connection point 1064 if (getConnectD() == null) { 1065 result.add(HitPointType.LEVEL_XING_D); 1066 } 1067 return result; 1068 } 1069 1070 /** 1071 * {@inheritDoc} 1072 */ 1073 @Override 1074 public boolean checkForUnAssignedBlocks() { 1075 return ((getLayoutBlockAC() != null) && (getLayoutBlockBD() != null)); 1076 } 1077 1078 /** 1079 * {@inheritDoc} 1080 */ 1081 @Override 1082 public void checkForNonContiguousBlocks( 1083 @Nonnull HashMap<String, List<Set<String>>> blockNamesToTrackNameSetsMap) { 1084 /* 1085 * For each (non-null) blocks of this track do: 1086 * #1) If it's got an entry in the blockNamesToTrackNameSetMap then 1087 * #2) If this track is already in the TrackNameSet for this block 1088 * then return (done!) 1089 * #3) else add a new set (with this block/track) to 1090 * blockNamesToTrackNameSetMap and check all the connections in this 1091 * block (by calling the 2nd method below) 1092 * <p> 1093 * Basically, we're maintaining contiguous track sets for each block found 1094 * (in blockNamesToTrackNameSetMap) 1095 */ 1096 1097 // We're only using a map here because it's convient to 1098 // use it to pair up blocks and connections 1099 Map<LayoutTrack, String> blocksAndTracksMap = new HashMap<>(); 1100 if ((getLayoutBlockAC() != null) && (connectA != null)) { 1101 blocksAndTracksMap.put(connectA, getLayoutBlockAC().getDisplayName()); 1102 } 1103 if ((getLayoutBlockAC() != null) && (connectC != null)) { 1104 blocksAndTracksMap.put(connectC, getLayoutBlockAC().getDisplayName()); 1105 } 1106 if ((getLayoutBlockBD() != null) && (connectB != null)) { 1107 blocksAndTracksMap.put(connectB, getLayoutBlockBD().getDisplayName()); 1108 } 1109 if ((getLayoutBlockBD() != null) && (connectD != null)) { 1110 blocksAndTracksMap.put(connectD, getLayoutBlockBD().getDisplayName()); 1111 } 1112 1113 List<Set<String>> TrackNameSets = null; 1114 Set<String> TrackNameSet = null; 1115 for (Map.Entry<LayoutTrack, String> entry : blocksAndTracksMap.entrySet()) { 1116 LayoutTrack theConnect = entry.getKey(); 1117 String theBlockName = entry.getValue(); 1118 1119 TrackNameSet = null; // assume not found (pessimist!) 1120 TrackNameSets = blockNamesToTrackNameSetsMap.get(theBlockName); 1121 if (TrackNameSets != null) { // (#1) 1122 for (Set<String> checkTrackNameSet : TrackNameSets) { 1123 if (checkTrackNameSet.contains(getName())) { // (#2) 1124 TrackNameSet = checkTrackNameSet; 1125 break; 1126 } 1127 } 1128 } else { // (#3) 1129 log.debug("*New block ('{}') trackNameSets", theBlockName); 1130 TrackNameSets = new ArrayList<>(); 1131 blockNamesToTrackNameSetsMap.put(theBlockName, TrackNameSets); 1132 } 1133 if (TrackNameSet == null) { 1134 TrackNameSet = new LinkedHashSet<>(); 1135 TrackNameSets.add(TrackNameSet); 1136 } 1137 if (TrackNameSet.add(getName())) { 1138 log.debug("* Add track ''{}'' to trackNameSet for block ''{}''", getName(), theBlockName); 1139 } 1140 theConnect.collectContiguousTracksNamesInBlockNamed(theBlockName, TrackNameSet); 1141 } 1142 } // collectContiguousTracksNamesInBlockNamed 1143 1144 /** 1145 * {@inheritDoc} 1146 */ 1147 @Override 1148 public void collectContiguousTracksNamesInBlockNamed(@Nonnull String blockName, 1149 @Nonnull Set<String> TrackNameSet) { 1150 if (!TrackNameSet.contains(getName())) { 1151 // check all the matching blocks in this track and... 1152 // #1) add us to TrackNameSet and... 1153 // #2) flood them 1154 //check the AC blockName 1155 if (getBlockNameAC().equals(blockName)) { 1156 // if we are added to the TrackNameSet 1157 if (TrackNameSet.add(getName())) { 1158 log.debug("* Add track ''{}'for block ''{}''", getName(), blockName); 1159 } 1160 // it's time to play... flood your neighbours! 1161 if (connectA != null) { 1162 connectA.collectContiguousTracksNamesInBlockNamed(blockName, TrackNameSet); 1163 } 1164 if (connectC != null) { 1165 connectC.collectContiguousTracksNamesInBlockNamed(blockName, TrackNameSet); 1166 } 1167 } 1168 //check the BD blockName 1169 if (getBlockNameBD().equals(blockName)) { 1170 // if we are added to the TrackNameSet 1171 if (TrackNameSet.add(getName())) { 1172 log.debug("* Add track ''{}''for block ''{}''", getName(), blockName); 1173 } 1174 // it's time to play... flood your neighbours! 1175 if (connectB != null) { 1176 connectB.collectContiguousTracksNamesInBlockNamed(blockName, TrackNameSet); 1177 } 1178 if (connectD != null) { 1179 connectD.collectContiguousTracksNamesInBlockNamed(blockName, TrackNameSet); 1180 } 1181 } 1182 } 1183 } 1184 1185 /** 1186 * {@inheritDoc} 1187 */ 1188 @Override 1189 public void setAllLayoutBlocks(LayoutBlock layoutBlock) { 1190 setLayoutBlockAC(layoutBlock); 1191 setLayoutBlockBD(layoutBlock); 1192 } 1193 1194 /** 1195 * {@inheritDoc} 1196 */ 1197 @Override 1198 public String getTypeName() { 1199 return Bundle.getMessage("TypeName_LevelXing"); 1200 } 1201 1202 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LevelXing.class); 1203}