001package jmri.jmrit.operations.trains; 002 003import java.beans.PropertyChangeListener; 004import java.io.File; 005import java.io.PrintWriter; 006import java.util.*; 007 008import javax.swing.JComboBox; 009 010import org.jdom2.Attribute; 011import org.jdom2.Element; 012 013import jmri.*; 014import jmri.beans.PropertyChangeSupport; 015import jmri.jmrit.operations.OperationsPanel; 016import jmri.jmrit.operations.locations.Location; 017import jmri.jmrit.operations.rollingstock.cars.*; 018import jmri.jmrit.operations.rollingstock.engines.EngineManagerXml; 019import jmri.jmrit.operations.routes.Route; 020import jmri.jmrit.operations.routes.RouteLocation; 021import jmri.jmrit.operations.setup.OperationsSetupXml; 022import jmri.jmrit.operations.setup.Setup; 023import jmri.jmrit.operations.trains.excel.TrainCustomManifest; 024import jmri.jmrit.operations.trains.excel.TrainCustomSwitchList; 025import jmri.jmrit.operations.trains.gui.TrainsTableFrame; 026import jmri.jmrit.operations.trains.schedules.TrainScheduleManager; 027import jmri.jmrit.operations.trains.trainbuilder.TrainCommon; 028import jmri.script.JmriScriptEngineManager; 029import jmri.util.ColorUtil; 030import jmri.util.swing.JmriJOptionPane; 031 032/** 033 * Manages trains. 034 * 035 * @author Bob Jacobsen Copyright (C) 2003 036 * @author Daniel Boudreau Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013, 037 * 2014 038 */ 039public class TrainManager extends PropertyChangeSupport 040 implements InstanceManagerAutoDefault, InstanceManagerAutoInitialize, PropertyChangeListener { 041 042 static final String NONE = ""; 043 044 // Train frame attributes 045 private String _trainAction = TrainsTableFrame.MOVE; // Trains frame table button action 046 private boolean _buildMessages = true; // when true, show build messages 047 private boolean _buildReport = false; // when true, print/preview build reports 048 private boolean _printPreview = false; // when true, preview train manifest 049 private boolean _openFile = false; // when true, open CSV file manifest 050 private boolean _runFile = false; // when true, run CSV file manifest 051 052 // Conductor attributes 053 private boolean _showLocationHyphenName = false; 054 055 // Trains window row colors 056 private boolean _rowColorManual = true; // when true train colors are manually assigned 057 private String _rowColorBuilt = NONE; // row color when train is built 058 private String _rowColorBuildFailed = NONE; // row color when train build failed 059 private String _rowColorTrainEnRoute = NONE; // row color when train is en route 060 private String _rowColorTerminated = NONE; // row color when train is terminated 061 private String _rowColorReset = NONE; // row color when train is reset 062 063 // Scripts 064 protected List<String> _startUpScripts = new ArrayList<>(); // list of script pathnames to run at start up 065 protected List<String> _shutDownScripts = new ArrayList<>(); // list of script pathnames to run at shut down 066 067 // property changes 068 public static final String LISTLENGTH_CHANGED_PROPERTY = "TrainsListLength"; // NOI18N 069 public static final String PRINTPREVIEW_CHANGED_PROPERTY = "TrainsPrintPreview"; // NOI18N 070 public static final String OPEN_FILE_CHANGED_PROPERTY = "TrainsOpenFile"; // NOI18N 071 public static final String RUN_FILE_CHANGED_PROPERTY = "TrainsRunFile"; // NOI18N 072 public static final String TRAIN_ACTION_CHANGED_PROPERTY = "TrainsAction"; // NOI18N 073 public static final String ROW_COLOR_NAME_CHANGED_PROPERTY = "TrainsRowColorChange"; // NOI18N 074 public static final String TRAINS_BUILT_CHANGED_PROPERTY = "TrainsBuiltChange"; // NOI18N 075 public static final String TRAINS_SHOW_FULL_NAME_PROPERTY = "TrainsShowFullName"; // NOI18N 076 public static final String TRAINS_SAVED_PROPERTY = "TrainsSaved"; // NOI18N 077 078 public TrainManager() { 079 } 080 081 private int _id = 0; // train ids 082 083 /** 084 * Get the number of items in the roster 085 * 086 * @return Number of trains in the roster 087 */ 088 public int getNumEntries() { 089 return _trainHashTable.size(); 090 } 091 092 /** 093 * 094 * @return true if build messages are enabled 095 */ 096 public boolean isBuildMessagesEnabled() { 097 return _buildMessages; 098 } 099 100 public void setBuildMessagesEnabled(boolean enable) { 101 boolean old = _buildMessages; 102 _buildMessages = enable; 103 setDirtyAndFirePropertyChange("BuildMessagesEnabled", enable, old); // NOI18N 104 } 105 106 /** 107 * 108 * @return true if build reports are enabled 109 */ 110 public boolean isBuildReportEnabled() { 111 return _buildReport; 112 } 113 114 public void setBuildReportEnabled(boolean enable) { 115 boolean old = _buildReport; 116 _buildReport = enable; 117 setDirtyAndFirePropertyChange("BuildReportEnabled", enable, old); // NOI18N 118 } 119 120 /** 121 * 122 * @return true if open file is enabled 123 */ 124 public boolean isOpenFileEnabled() { 125 return _openFile; 126 } 127 128 public void setOpenFileEnabled(boolean enable) { 129 boolean old = _openFile; 130 _openFile = enable; 131 setDirtyAndFirePropertyChange(OPEN_FILE_CHANGED_PROPERTY, old ? "true" : "false", enable ? "true" // NOI18N 132 : "false"); // NOI18N 133 } 134 135 /** 136 * 137 * @return true if open file is enabled 138 */ 139 public boolean isRunFileEnabled() { 140 return _runFile; 141 } 142 143 public void setRunFileEnabled(boolean enable) { 144 boolean old = _runFile; 145 _runFile = enable; 146 setDirtyAndFirePropertyChange(RUN_FILE_CHANGED_PROPERTY, old ? "true" : "false", enable ? "true" // NOI18N 147 : "false"); // NOI18N 148 } 149 150 /** 151 * 152 * @return true if print preview is enabled 153 */ 154 public boolean isPrintPreviewEnabled() { 155 return _printPreview; 156 } 157 158 public void setPrintPreviewEnabled(boolean enable) { 159 boolean old = _printPreview; 160 _printPreview = enable; 161 setDirtyAndFirePropertyChange(PRINTPREVIEW_CHANGED_PROPERTY, old ? "Preview" : "Print", // NOI18N 162 enable ? "Preview" : "Print"); // NOI18N 163 } 164 165 /** 166 * When true show entire location name including hyphen 167 * 168 * @return true when showing entire location name 169 */ 170 public boolean isShowLocationHyphenNameEnabled() { 171 return _showLocationHyphenName; 172 } 173 174 public void setShowLocationHyphenNameEnabled(boolean enable) { 175 boolean old = _showLocationHyphenName; 176 _showLocationHyphenName = enable; 177 setDirtyAndFirePropertyChange(TRAINS_SHOW_FULL_NAME_PROPERTY, old, enable); 178 } 179 180 public String getTrainsFrameTrainAction() { 181 return _trainAction; 182 } 183 184 public void setTrainsFrameTrainAction(String action) { 185 String old = _trainAction; 186 _trainAction = action; 187 if (!old.equals(action)) { 188 setDirtyAndFirePropertyChange(TRAIN_ACTION_CHANGED_PROPERTY, old, action); 189 } 190 } 191 192 /** 193 * Add a script to run after trains have been loaded 194 * 195 * @param pathname The script's pathname 196 */ 197 public void addStartUpScript(String pathname) { 198 _startUpScripts.add(pathname); 199 setDirtyAndFirePropertyChange("addStartUpScript", pathname, null); // NOI18N 200 } 201 202 public void deleteStartUpScript(String pathname) { 203 _startUpScripts.remove(pathname); 204 setDirtyAndFirePropertyChange("deleteStartUpScript", null, pathname); // NOI18N 205 } 206 207 /** 208 * Gets a list of pathnames to run after trains have been loaded 209 * 210 * @return A list of pathnames to run after trains have been loaded 211 */ 212 public List<String> getStartUpScripts() { 213 return _startUpScripts; 214 } 215 216 public void runStartUpScripts() { 217 // use thread to prevent object (Train) thread lock 218 Thread scripts = jmri.util.ThreadingUtil.newThread(new Runnable() { 219 @Override 220 public void run() { 221 for (String scriptPathName : getStartUpScripts()) { 222 try { 223 JmriScriptEngineManager.getDefault() 224 .runScript(new File(jmri.util.FileUtil.getExternalFilename(scriptPathName))); 225 } catch (Exception e) { 226 log.error("Problem with script: {}", scriptPathName); 227 } 228 } 229 } 230 }); 231 scripts.setName("Startup Scripts"); // NOI18N 232 scripts.start(); 233 } 234 235 /** 236 * Add a script to run at shutdown 237 * 238 * @param pathname The script's pathname 239 */ 240 public void addShutDownScript(String pathname) { 241 _shutDownScripts.add(pathname); 242 setDirtyAndFirePropertyChange("addShutDownScript", pathname, null); // NOI18N 243 } 244 245 public void deleteShutDownScript(String pathname) { 246 _shutDownScripts.remove(pathname); 247 setDirtyAndFirePropertyChange("deleteShutDownScript", null, pathname); // NOI18N 248 } 249 250 /** 251 * Gets a list of pathnames to run at shutdown 252 * 253 * @return A list of pathnames to run at shutdown 254 */ 255 public List<String> getShutDownScripts() { 256 return _shutDownScripts; 257 } 258 259 public void runShutDownScripts() { 260 for (String scriptPathName : getShutDownScripts()) { 261 try { 262 JmriScriptEngineManager.getDefault() 263 .runScript(new File(jmri.util.FileUtil.getExternalFilename(scriptPathName))); 264 } catch (Exception e) { 265 log.error("Problem with script: {}", scriptPathName); 266 } 267 } 268 } 269 270 /** 271 * Used to determine if a train has any restrictions with regard to car 272 * built dates. 273 * 274 * @return true if there's a restriction 275 */ 276 public boolean isBuiltRestricted() { 277 for (Train train : getList()) { 278 if (!train.getBuiltStartYear().equals(Train.NONE) || !train.getBuiltEndYear().equals(Train.NONE)) { 279 return true; 280 } 281 } 282 return false; 283 } 284 285 /** 286 * Used to determine if a train has any restrictions with regard to car 287 * loads. 288 * 289 * @return true if there's a restriction 290 */ 291 public boolean isLoadRestricted() { 292 for (Train train : getList()) { 293 if (!train.getLoadOption().equals(Train.ALL_LOADS)) { 294 return true; 295 } 296 } 297 return false; 298 } 299 300 /** 301 * Used to determine if a train has any restrictions with regard to car 302 * roads. 303 * 304 * @return true if there's a restriction 305 */ 306 public boolean isCarRoadRestricted() { 307 for (Train train : getList()) { 308 if (!train.getCarRoadOption().equals(Train.ALL_ROADS)) { 309 return true; 310 } 311 } 312 return false; 313 } 314 315 /** 316 * Used to determine if a train has any restrictions with regard to caboose 317 * roads. 318 * 319 * @return true if there's a restriction 320 */ 321 public boolean isCabooseRoadRestricted() { 322 for (Train train : getList()) { 323 if (!train.getCabooseRoadOption().equals(Train.ALL_ROADS)) { 324 return true; 325 } 326 } 327 return false; 328 } 329 330 /** 331 * Used to determine if a train has any restrictions with regard to 332 * Locomotive roads. 333 * 334 * @return true if there's a restriction 335 */ 336 public boolean isLocoRoadRestricted() { 337 for (Train train : getList()) { 338 if (!train.getLocoRoadOption().equals(Train.ALL_ROADS)) { 339 return true; 340 } 341 } 342 return false; 343 } 344 345 /** 346 * Used to determine if a train has any restrictions with regard to car 347 * owners. 348 * 349 * @return true if there's a restriction 350 */ 351 public boolean isOwnerRestricted() { 352 for (Train train : getList()) { 353 if (!train.getOwnerOption().equals(Train.ALL_OWNERS)) { 354 return true; 355 } 356 } 357 return false; 358 } 359 360 public void dispose() { 361 _trainHashTable.clear(); 362 _id = 0; 363 } 364 365 // stores known Train instances by id 366 private final Hashtable<String, Train> _trainHashTable = new Hashtable<>(); 367 368 /** 369 * @param name The train's name. 370 * @return requested Train object or null if none exists 371 */ 372 public Train getTrainByName(String name) { 373 if (!InstanceManager.getDefault(TrainManagerXml.class).isTrainFileLoaded()) { 374 log.error("TrainManager getTrainByName called before trains completely loaded!"); 375 } 376 Train train; 377 Enumeration<Train> en = _trainHashTable.elements(); 378 while (en.hasMoreElements()) { 379 train = en.nextElement(); 380 // windows file names are case independent 381 if (train.getName().toLowerCase().equals(name.toLowerCase())) { 382 return train; 383 } 384 } 385 log.debug("Train ({}) doesn't exist", name); 386 return null; 387 } 388 389 public Train getTrainById(String id) { 390 if (!InstanceManager.getDefault(TrainManagerXml.class).isTrainFileLoaded()) { 391 log.error("TrainManager getTrainById called before trains completely loaded!"); 392 } 393 return _trainHashTable.get(id); 394 } 395 396 /** 397 * Finds an existing train or creates a new train if needed. Requires train's 398 * name and creates a unique id for a new train 399 * 400 * @param name The train's name. 401 * 402 * 403 * @return new train or existing train 404 */ 405 public Train newTrain(String name) { 406 Train train = getTrainByName(name); 407 if (train == null) { 408 _id++; 409 train = new Train(Integer.toString(_id), name); 410 int oldSize = getNumEntries(); 411 _trainHashTable.put(train.getId(), train); 412 setDirtyAndFirePropertyChange(LISTLENGTH_CHANGED_PROPERTY, oldSize, 413 getNumEntries()); 414 } 415 return train; 416 } 417 418 /** 419 * Remember a NamedBean Object created outside the manager. 420 * 421 * @param train The Train to be added. 422 */ 423 public void register(Train train) { 424 int oldSize = getNumEntries(); 425 _trainHashTable.put(train.getId(), train); 426 // find last id created 427 int id = Integer.parseInt(train.getId()); 428 if (id > _id) { 429 _id = id; 430 } 431 setDirtyAndFirePropertyChange(LISTLENGTH_CHANGED_PROPERTY, oldSize, getNumEntries()); 432 } 433 434 /** 435 * Forget a NamedBean Object created outside the manager. 436 * 437 * @param train The Train to delete. 438 */ 439 public void deregister(Train train) { 440 if (train == null) { 441 return; 442 } 443 train.dispose(); 444 int oldSize = getNumEntries(); 445 _trainHashTable.remove(train.getId()); 446 setDirtyAndFirePropertyChange(LISTLENGTH_CHANGED_PROPERTY, oldSize, getNumEntries()); 447 } 448 449 public void replaceLoad(String type, String oldLoadName, String newLoadName) { 450 for (Train train : getList()) { 451 for (String loadName : train.getLoadNames()) { 452 if (loadName.equals(oldLoadName)) { 453 train.deleteLoadName(oldLoadName); 454 if (newLoadName != null) { 455 train.addLoadName(newLoadName); 456 } 457 } 458 // adjust combination car type and load name 459 String[] splitLoad = loadName.split(CarLoad.SPLIT_CHAR); 460 if (splitLoad.length > 1) { 461 if (splitLoad[0].equals(type) && splitLoad[1].equals(oldLoadName)) { 462 train.deleteLoadName(loadName); 463 if (newLoadName != null) { 464 train.addLoadName(type + CarLoad.SPLIT_CHAR + newLoadName); 465 } 466 } 467 } 468 } 469 } 470 } 471 472 /** 473 * 474 * @return true if there's a built train 475 */ 476 public boolean isAnyTrainBuilt() { 477 for (Train train : getList()) { 478 if (train.isBuilt()) { 479 return true; 480 } 481 } 482 return false; 483 } 484 485 /** 486 * 487 * @return true if there's a train being built 488 */ 489 public boolean isAnyTrainBuilding() { 490 for (Train train : getList()) { 491 if (train.getStatusCode() == Train.CODE_BUILDING) { 492 log.debug("Train {} is currently building", train.getName()); 493 return true; 494 } 495 } 496 return false; 497 } 498 499 /** 500 * @param car The car looking for a train. 501 * @param buildReport The optional build report for logging. 502 * @return Train that can service car from its current location to the its 503 * destination. 504 */ 505 public Train getTrainForCar(Car car, PrintWriter buildReport) { 506 return getTrainForCar(car, new ArrayList<>(), buildReport, false); 507 } 508 509 /** 510 * @param car The car looking for a train. 511 * @param excludeTrains The trains not to try. 512 * @param buildReport The optional build report for logging. 513 * @param isExcludeRoutes When true eliminate trains that have the same route in the exclude trains list. 514 * @return Train that can service car from its current location to the its 515 * destination. 516 */ 517 public Train getTrainForCar(Car car, List<Train> excludeTrains, PrintWriter buildReport, boolean isExcludeRoutes) { 518 addLine(buildReport, TrainCommon.BLANK_LINE); 519 addLine(buildReport, Bundle.getMessage("trainFindForCar", car.toString(), car.getLocationName(), 520 car.getTrackName(), car.getDestinationName(), car.getDestinationTrackName())); 521 522 main: for (Train train : getTrainsByNameList()) { 523 if (excludeTrains.contains(train)) { 524 continue; 525 } 526 if (Setup.isOnlyActiveTrainsEnabled() && !train.isBuildEnabled()) { 527 continue; 528 } 529 if (isExcludeRoutes) { 530 for (Train t : excludeTrains) { 531 if (t != null && train.getRoute() == t.getRoute()) { 532 addLine(buildReport, Bundle.getMessage("trainHasSameRoute", train, t)); 533 continue main; 534 } 535 } 536 } 537 // does this train service this car? 538 if (train.isServiceable(buildReport, car)) { 539 log.debug("Found train ({}) for car ({}) location ({}, {}) destination ({}, {})", train.getName(), 540 car.toString(), car.getLocationName(), car.getTrackName(), car.getDestinationName(), 541 car.getDestinationTrackName()); // NOI18N 542 return train; 543 } 544 } 545 return null; 546 } 547 548 public List<Train> getExcludeTrainListForCar(Car car, PrintWriter buildReport) { 549 List<Train> excludeTrains = new ArrayList<>(); 550 for (Train train : getTrainsByNameList()) { 551 if (Setup.isOnlyActiveTrainsEnabled() && !train.isBuildEnabled()) { 552 addLine(buildReport, Bundle.getMessage("trainRoutingDisabled", train.getName())); 553 excludeTrains.add(train); 554 } 555 else if (!train.isTrainAbleToService(buildReport, car)) { 556 excludeTrains.add(train); 557 } 558 } 559 return excludeTrains; 560 } 561 562 protected static final String SEVEN = Setup.BUILD_REPORT_VERY_DETAILED; 563 564 private void addLine(PrintWriter buildReport, String string) { 565 if (Setup.getRouterBuildReportLevel().equals(SEVEN)) { 566 TrainCommon.addLine(buildReport, SEVEN, string); 567 } 568 } 569 570 /** 571 * Sort by train name 572 * 573 * @return list of trains ordered by name 574 */ 575 public List<Train> getTrainsByNameList() { 576 return getTrainsByList(getList(), GET_TRAIN_NAME); 577 } 578 579 /** 580 * Sort by train departure time 581 * 582 * @return list of trains ordered by departure time 583 */ 584 public List<Train> getTrainsByTimeList() { 585 return getTrainsByIntList(getTrainsByNameList(), GET_TRAIN_TIME); 586 } 587 588 /** 589 * Sort by train departure location name 590 * 591 * @return list of trains ordered by departure name 592 */ 593 public List<Train> getTrainsByDepartureList() { 594 return getTrainsByList(getTrainsByTimeList(), GET_TRAIN_DEPARTES_NAME); 595 } 596 597 /** 598 * Sort by train termination location name 599 * 600 * @return list of trains ordered by termination name 601 */ 602 public List<Train> getTrainsByTerminatesList() { 603 return getTrainsByList(getTrainsByTimeList(), GET_TRAIN_TERMINATES_NAME); 604 } 605 606 /** 607 * Sort by train route name 608 * 609 * @return list of trains ordered by route name 610 */ 611 public List<Train> getTrainsByRouteList() { 612 return getTrainsByList(getTrainsByTimeList(), GET_TRAIN_ROUTE_NAME); 613 } 614 615 /** 616 * Sort by train status 617 * 618 * @return list of trains ordered by status 619 */ 620 public List<Train> getTrainsByStatusList() { 621 return getTrainsByList(getTrainsByTimeList(), GET_TRAIN_STATUS); 622 } 623 624 /** 625 * Sort by train description 626 * 627 * @return list of trains ordered by train description 628 */ 629 public List<Train> getTrainsByDescriptionList() { 630 return getTrainsByList(getTrainsByTimeList(), GET_TRAIN_DESCRIPTION); 631 } 632 633 /** 634 * Sort by train id 635 * 636 * @return list of trains ordered by id 637 */ 638 public List<Train> getTrainsByIdList() { 639 return getTrainsByIntList(getList(), GET_TRAIN_ID); 640 } 641 642 private List<Train> getTrainsByList(List<Train> sortList, int attribute) { 643 List<Train> out = new ArrayList<>(); 644 for (Train train : sortList) { 645 String trainAttribute = (String) getTrainAttribute(train, attribute); 646 for (int j = 0; j < out.size(); j++) { 647 if (trainAttribute.compareToIgnoreCase((String) getTrainAttribute(out.get(j), attribute)) < 0) { 648 out.add(j, train); 649 break; 650 } 651 } 652 if (!out.contains(train)) { 653 out.add(train); 654 } 655 } 656 return out; 657 } 658 659 private List<Train> getTrainsByIntList(List<Train> sortList, int attribute) { 660 List<Train> out = new ArrayList<>(); 661 for (Train train : sortList) { 662 int trainAttribute = (Integer) getTrainAttribute(train, attribute); 663 for (int j = 0; j < out.size(); j++) { 664 if (trainAttribute < (Integer) getTrainAttribute(out.get(j), attribute)) { 665 out.add(j, train); 666 break; 667 } 668 } 669 if (!out.contains(train)) { 670 out.add(train); 671 } 672 } 673 return out; 674 } 675 676 // the various sort options for trains 677 private static final int GET_TRAIN_DEPARTES_NAME = 0; 678 private static final int GET_TRAIN_NAME = 1; 679 private static final int GET_TRAIN_ROUTE_NAME = 2; 680 private static final int GET_TRAIN_TERMINATES_NAME = 3; 681 private static final int GET_TRAIN_TIME = 4; 682 private static final int GET_TRAIN_STATUS = 5; 683 private static final int GET_TRAIN_ID = 6; 684 private static final int GET_TRAIN_DESCRIPTION = 7; 685 686 private Object getTrainAttribute(Train train, int attribute) { 687 switch (attribute) { 688 case GET_TRAIN_DEPARTES_NAME: 689 return train.getTrainDepartsName(); 690 case GET_TRAIN_NAME: 691 return train.getName(); 692 case GET_TRAIN_ROUTE_NAME: 693 return train.getTrainRouteName(); 694 case GET_TRAIN_TERMINATES_NAME: 695 return train.getTrainTerminatesName(); 696 case GET_TRAIN_TIME: 697 return train.getDepartTimeMinutes(); 698 case GET_TRAIN_STATUS: 699 return train.getStatus(); 700 case GET_TRAIN_ID: 701 return Integer.parseInt(train.getId()); 702 case GET_TRAIN_DESCRIPTION: 703 return train.getDescription(); 704 default: 705 return "unknown"; // NOI18N 706 } 707 } 708 709 public List<Train> getList() { 710 if (!InstanceManager.getDefault(TrainManagerXml.class).isTrainFileLoaded()) { 711 log.error("TrainManager getList called before trains completely loaded!"); 712 } 713 List<Train> out = new ArrayList<>(); 714 Enumeration<Train> en = _trainHashTable.elements(); 715 while (en.hasMoreElements()) { 716 out.add(en.nextElement()); 717 } 718 return out; 719 } 720 721 public JComboBox<Train> getTrainComboBox() { 722 JComboBox<Train> box = new JComboBox<>(); 723 updateTrainComboBox(box); 724 OperationsPanel.padComboBox(box); 725 return box; 726 } 727 728 public void updateTrainComboBox(JComboBox<Train> box) { 729 box.removeAllItems(); 730 box.addItem(null); 731 for (Train train : getTrainsByNameList()) { 732 box.addItem(train); 733 } 734 } 735 736 /** 737 * Update combo box with trains that will service this car 738 * 739 * @param box the combo box to update 740 * @param car the car to be serviced 741 */ 742 public void updateTrainComboBox(JComboBox<Train> box, Car car) { 743 box.removeAllItems(); 744 box.addItem(null); 745 for (Train train : getTrainsByNameList()) { 746 if (train.isServiceable(car)) { 747 box.addItem(train); 748 } 749 } 750 } 751 752 public boolean isRowColorManual() { 753 return _rowColorManual; 754 } 755 756 public void setRowColorsManual(boolean manual) { 757 boolean old = _rowColorManual; 758 _rowColorManual = manual; 759 setDirtyAndFirePropertyChange(ROW_COLOR_NAME_CHANGED_PROPERTY, old, manual); 760 } 761 762 public String getRowColorNameForBuilt() { 763 return _rowColorBuilt; 764 } 765 766 public void setRowColorNameForBuilt(String colorName) { 767 String old = _rowColorBuilt; 768 _rowColorBuilt = colorName; 769 setDirtyAndFirePropertyChange(ROW_COLOR_NAME_CHANGED_PROPERTY, old, colorName); 770 } 771 772 public String getRowColorNameForBuildFailed() { 773 return _rowColorBuildFailed; 774 } 775 776 public void setRowColorNameForBuildFailed(String colorName) { 777 String old = _rowColorBuildFailed; 778 _rowColorBuildFailed = colorName; 779 setDirtyAndFirePropertyChange(ROW_COLOR_NAME_CHANGED_PROPERTY, old, colorName); 780 } 781 782 public String getRowColorNameForTrainEnRoute() { 783 return _rowColorTrainEnRoute; 784 } 785 786 public void setRowColorNameForTrainEnRoute(String colorName) { 787 String old = _rowColorTrainEnRoute; 788 _rowColorTrainEnRoute = colorName; 789 setDirtyAndFirePropertyChange(ROW_COLOR_NAME_CHANGED_PROPERTY, old, colorName); 790 } 791 792 public String getRowColorNameForTerminated() { 793 return _rowColorTerminated; 794 } 795 796 public void setRowColorNameForTerminated(String colorName) { 797 String old = _rowColorTerminated; 798 _rowColorTerminated = colorName; 799 setDirtyAndFirePropertyChange(ROW_COLOR_NAME_CHANGED_PROPERTY, old, colorName); 800 } 801 802 public String getRowColorNameForReset() { 803 return _rowColorReset; 804 } 805 806 public void setRowColorNameForReset(String colorName) { 807 String old = _rowColorReset; 808 _rowColorReset = colorName; 809 setDirtyAndFirePropertyChange(ROW_COLOR_NAME_CHANGED_PROPERTY, old, colorName); 810 } 811 812 /** 813 * JColorChooser is not a replacement for getRowColorComboBox as it doesn't 814 * support no color as a selection. 815 * 816 * @return the available colors used highlighting table rows including no color. 817 */ 818 public JComboBox<String> getRowColorComboBox() { 819 JComboBox<String> box = new JComboBox<>(); 820 box.addItem(NONE); 821 box.addItem(ColorUtil.ColorBlack); 822 box.addItem(ColorUtil.ColorRed); 823 box.addItem(ColorUtil.ColorPink); 824 box.addItem(ColorUtil.ColorOrange); 825 box.addItem(ColorUtil.ColorYellow); 826 box.addItem(ColorUtil.ColorGreen); 827 box.addItem(ColorUtil.ColorMagenta); 828 box.addItem(ColorUtil.ColorCyan); 829 box.addItem(ColorUtil.ColorBlue); 830 box.addItem(ColorUtil.ColorGray); 831 return box; 832 } 833 834 /** 835 * Makes a copy of an existing train. 836 * 837 * @param train the train to copy 838 * @param trainName the name of the new train 839 * @return a copy of train 840 */ 841 public Train copyTrain(Train train, String trainName) { 842 Train newTrain = newTrain(trainName); 843 // route, departure time and types 844 newTrain.setRoute(train.getRoute()); 845 newTrain.setTrainSkipsLocations(train.getTrainSkipsLocations()); 846 newTrain.setDepartureTime(train.getDepartureTimeHour(), train.getDepartureTimeMinute()); 847 newTrain._typeList.clear(); // remove all types loaded by create 848 newTrain.setTypeNames(train.getTypeNames()); 849 // set road, load, and owner options 850 newTrain.setCarRoadOption(train.getCarRoadOption()); 851 newTrain.setCarRoadNames(train.getCarRoadNames()); 852 newTrain.setCabooseRoadNames(train.getCabooseRoadNames()); 853 newTrain.setLocoRoadOption(train.getLocoRoadOption()); 854 newTrain.setLocoRoadNames(train.getLocoRoadNames()); 855 newTrain.setLoadOption(train.getLoadOption()); 856 newTrain.setLoadNames(train.getLoadNames()); 857 newTrain.setOwnerOption(train.getOwnerOption()); 858 newTrain.setOwnerNames(train.getOwnerNames()); 859 // build dates 860 newTrain.setBuiltStartYear(train.getBuiltStartYear()); 861 newTrain.setBuiltEndYear(train.getBuiltEndYear()); 862 // locos start of route 863 newTrain.setNumberEngines(train.getNumberEngines()); 864 newTrain.setEngineModel(train.getEngineModel()); 865 newTrain.setEngineRoad(train.getEngineRoad()); 866 newTrain.setRequirements(train.getRequirements()); 867 newTrain.setCabooseRoad(train.getCabooseRoad()); 868 // second leg 869 newTrain.setSecondLegNumberEngines(train.getSecondLegNumberEngines()); 870 newTrain.setSecondLegEngineModel(train.getSecondLegEngineModel()); 871 newTrain.setSecondLegEngineRoad(train.getSecondLegEngineRoad()); 872 newTrain.setSecondLegOptions(train.getSecondLegOptions()); 873 newTrain.setSecondLegCabooseRoad(train.getSecondLegCabooseRoad()); 874 newTrain.setSecondLegStartRouteLocation(train.getSecondLegStartRouteLocation()); 875 newTrain.setSecondLegEndRouteLocation(train.getSecondLegEndRouteLocation()); 876 // third leg 877 newTrain.setThirdLegNumberEngines(train.getThirdLegNumberEngines()); 878 newTrain.setThirdLegEngineModel(train.getThirdLegEngineModel()); 879 newTrain.setThirdLegEngineRoad(train.getThirdLegEngineRoad()); 880 newTrain.setThirdLegOptions(train.getThirdLegOptions()); 881 newTrain.setThirdLegCabooseRoad(train.getThirdLegCabooseRoad()); 882 newTrain.setThirdLegStartRouteLocation(train.getThirdLegStartRouteLocation()); 883 newTrain.setThirdLegEndRouteLocation(train.getThirdLegEndRouteLocation()); 884 // scripts 885 for (String scriptName : train.getBuildScripts()) { 886 newTrain.addBuildScript(scriptName); 887 } 888 for (String scriptName : train.getMoveScripts()) { 889 newTrain.addMoveScript(scriptName); 890 } 891 for (String scriptName : train.getTerminationScripts()) { 892 newTrain.addTerminationScript(scriptName); 893 } 894 // manifest options 895 newTrain.setRailroadName(train.getRailroadName()); 896 newTrain.setManifestLogoPathName(train.getManifestLogoPathName()); 897 newTrain.setShowArrivalAndDepartureTimes(train.isShowArrivalAndDepartureTimesEnabled()); 898 // build options 899 newTrain.setAllowLocalMovesEnabled(train.isAllowLocalMovesEnabled()); 900 newTrain.setAllowReturnToStagingEnabled(train.isAllowReturnToStagingEnabled()); 901 newTrain.setAllowThroughCarsEnabled(train.isAllowThroughCarsEnabled()); 902 newTrain.setBuildConsistEnabled(train.isBuildConsistEnabled()); 903 newTrain.setSendCarsWithCustomLoadsToStagingEnabled(train.isSendCarsWithCustomLoadsToStagingEnabled()); 904 newTrain.setBuildTrainNormalEnabled(train.isBuildTrainNormalEnabled()); 905 newTrain.setSendCarsToTerminalEnabled(train.isSendCarsToTerminalEnabled()); 906 newTrain.setServiceAllCarsWithFinalDestinationsEnabled(train.isServiceAllCarsWithFinalDestinationsEnabled()); 907 // comment 908 newTrain.setComment(train.getCommentWithColor()); 909 // description 910 newTrain.setDescription(train.getRawDescription()); 911 return newTrain; 912 } 913 914 /** 915 * Provides a list of trains ordered by arrival time to a location 916 * 917 * @param location The location 918 * @return A list of trains ordered by arrival time. 919 */ 920 public List<Train> getTrainsArrivingThisLocationList(Location location) { 921 // get a list of trains 922 List<Train> out = new ArrayList<>(); 923 List<Integer> arrivalTimes = new ArrayList<>(); 924 for (Train train : getTrainsByTimeList()) { 925 if (!train.isBuilt()) { 926 continue; // train wasn't built so skip 927 } 928 Route route = train.getRoute(); 929 if (route == null) { 930 continue; // no route for this train 931 } 932 for (RouteLocation rl : route.getLocationsBySequenceList()) { 933 if (rl.getSplitName().equals(location.getSplitName())) { 934 int expectedArrivalTime = train.getExpectedTravelTimeInMinutes(rl); 935 // is already serviced then "-1" 936 if (expectedArrivalTime == -1) { 937 out.add(0, train); // place all trains that have already been serviced at the start 938 arrivalTimes.add(0, expectedArrivalTime); 939 } // if the train is in route, then expected arrival time is in minutes 940 else if (train.isTrainEnRoute()) { 941 for (int j = 0; j < out.size(); j++) { 942 Train t = out.get(j); 943 int time = arrivalTimes.get(j); 944 if (t.isTrainEnRoute() && expectedArrivalTime < time) { 945 out.add(j, train); 946 arrivalTimes.add(j, expectedArrivalTime); 947 break; 948 } 949 if (!t.isTrainEnRoute()) { 950 out.add(j, train); 951 arrivalTimes.add(j, expectedArrivalTime); 952 break; 953 } 954 } 955 // Train has not departed 956 } else { 957 for (int j = 0; j < out.size(); j++) { 958 Train t = out.get(j); 959 int time = arrivalTimes.get(j); 960 if (!t.isTrainEnRoute() && expectedArrivalTime < time) { 961 out.add(j, train); 962 arrivalTimes.add(j, expectedArrivalTime); 963 break; 964 } 965 } 966 } 967 if (!out.contains(train)) { 968 out.add(train); 969 arrivalTimes.add(expectedArrivalTime); 970 } 971 break; // done 972 } 973 } 974 } 975 return out; 976 } 977 978 /** 979 * Loads train icons if needed 980 */ 981 public void loadTrainIcons() { 982 for (Train train : getTrainsByIdList()) { 983 train.loadTrainIcon(); 984 } 985 } 986 987 /** 988 * Sets the switch list status for all built trains. Used for switch lists in 989 * consolidated mode. 990 * 991 * @param status Train.PRINTED, Train.UNKNOWN 992 */ 993 public void setTrainsSwitchListStatus(String status) { 994 for (Train train : getTrainsByTimeList()) { 995 if (!train.isBuilt()) { 996 continue; // train isn't built so skip 997 } 998 train.setSwitchListStatus(status); 999 } 1000 } 1001 1002 /** 1003 * Sets all built trains manifests to modified. This causes the train's manifest 1004 * to be recreated. 1005 */ 1006 public void setTrainsModified() { 1007 for (Train train : getTrainsByTimeList()) { 1008 if (!train.isBuilt() || train.isTrainEnRoute()) { 1009 continue; // train wasn't built or in route, so skip 1010 } 1011 train.setModified(true); 1012 } 1013 } 1014 1015 public void buildSelectedTrains(List<Train> trains) { 1016 // use a thread to allow table updates during build 1017 Thread build = jmri.util.ThreadingUtil.newThread(new Runnable() { 1018 @Override 1019 public void run() { 1020 for (Train train : trains) { 1021 if (train.buildIfSelected()) { 1022 continue; 1023 } 1024 if (isBuildMessagesEnabled() && train.isBuildEnabled() && !train.isBuilt()) { 1025 if (JmriJOptionPane.showConfirmDialog(null, Bundle.getMessage("ContinueBuilding"), 1026 Bundle.getMessage("buildFailedMsg", 1027 train.getName()), 1028 JmriJOptionPane.YES_NO_OPTION) == JmriJOptionPane.NO_OPTION) { 1029 break; 1030 } 1031 } 1032 } 1033 setDirtyAndFirePropertyChange(TRAINS_BUILT_CHANGED_PROPERTY, false, true); 1034 } 1035 }); 1036 build.setName("Build Trains"); // NOI18N 1037 build.start(); 1038 } 1039 1040 public boolean printSelectedTrains(List<Train> trains) { 1041 boolean status = true; 1042 for (Train train : trains) { 1043 if (train.isBuildEnabled()) { 1044 if (train.printManifestIfBuilt()) { 1045 continue; 1046 } 1047 status = false; // failed to print all selected trains 1048 if (isBuildMessagesEnabled()) { 1049 int response = JmriJOptionPane.showConfirmDialog(null, 1050 Bundle.getMessage("NeedToBuildBeforePrinting", 1051 train.getName(), 1052 (isPrintPreviewEnabled() ? Bundle.getMessage("preview") 1053 : Bundle.getMessage("print"))), 1054 Bundle.getMessage("CanNotPrintManifest", 1055 isPrintPreviewEnabled() ? Bundle.getMessage("preview") 1056 : Bundle.getMessage("print")), 1057 JmriJOptionPane.OK_CANCEL_OPTION); 1058 if (response != JmriJOptionPane.OK_OPTION ) { 1059 break; 1060 } 1061 } 1062 } 1063 } 1064 return status; 1065 } 1066 1067 public boolean terminateSelectedTrains(List<Train> trains) { 1068 boolean status = true; 1069 for (Train train : trains) { 1070 if (train.isBuildEnabled() && train.isBuilt()) { 1071 if (train.isPrinted()) { 1072 train.terminate(); 1073 } else { 1074 status = false; 1075 int response = JmriJOptionPane.showConfirmDialog(null, 1076 Bundle.getMessage("WarningTrainManifestNotPrinted"), 1077 Bundle.getMessage("TerminateTrain", 1078 train.getName(), train.getDescription()), 1079 JmriJOptionPane.YES_NO_CANCEL_OPTION); 1080 if (response == JmriJOptionPane.YES_OPTION) { 1081 train.terminate(); 1082 } 1083 // else Quit? 1084 if (response == JmriJOptionPane.CLOSED_OPTION || response == JmriJOptionPane.CANCEL_OPTION) { 1085 break; 1086 } 1087 } 1088 } 1089 } 1090 return status; 1091 } 1092 1093 public void resetBuildFailedTrains() { 1094 for (Train train : getList()) { 1095 if (train.isBuildFailed()) 1096 train.reset(); 1097 } 1098 } 1099 1100 int _maxTrainNameLength = 0; 1101 1102 @edu.umd.cs.findbugs.annotations.SuppressFBWarnings( value="SLF4J_FORMAT_SHOULD_BE_CONST", 1103 justification="I18N of Info Message") 1104 public int getMaxTrainNameLength() { 1105 String trainName = ""; 1106 if (_maxTrainNameLength == 0) { 1107 for (Train train : getList()) { 1108 if (train.getName().length() > _maxTrainNameLength) { 1109 trainName = train.getName(); 1110 _maxTrainNameLength = train.getName().length(); 1111 } 1112 } 1113 log.info(Bundle.getMessage("InfoMaxName", trainName, _maxTrainNameLength)); 1114 } 1115 return _maxTrainNameLength; 1116 } 1117 1118 public void load(Element root) { 1119 if (root.getChild(Xml.OPTIONS) != null) { 1120 Element options = root.getChild(Xml.OPTIONS); 1121 InstanceManager.getDefault(TrainCustomManifest.class).load(options); 1122 InstanceManager.getDefault(TrainCustomSwitchList.class).load(options); 1123 Element e = options.getChild(Xml.TRAIN_OPTIONS); 1124 Attribute a; 1125 if (e != null) { 1126 if ((a = e.getAttribute(Xml.BUILD_MESSAGES)) != null) { 1127 _buildMessages = a.getValue().equals(Xml.TRUE); 1128 } 1129 if ((a = e.getAttribute(Xml.BUILD_REPORT)) != null) { 1130 _buildReport = a.getValue().equals(Xml.TRUE); 1131 } 1132 if ((a = e.getAttribute(Xml.PRINT_PREVIEW)) != null) { 1133 _printPreview = a.getValue().equals(Xml.TRUE); 1134 } 1135 if ((a = e.getAttribute(Xml.OPEN_FILE)) != null) { 1136 _openFile = a.getValue().equals(Xml.TRUE); 1137 } 1138 if ((a = e.getAttribute(Xml.RUN_FILE)) != null) { 1139 _runFile = a.getValue().equals(Xml.TRUE); 1140 } 1141 // verify that the Trains Window action is valid 1142 if ((a = e.getAttribute(Xml.TRAIN_ACTION)) != null && 1143 (a.getValue().equals(TrainsTableFrame.MOVE) || 1144 a.getValue().equals(TrainsTableFrame.RESET) || 1145 a.getValue().equals(TrainsTableFrame.TERMINATE) || 1146 a.getValue().equals(TrainsTableFrame.CONDUCTOR))) { 1147 _trainAction = a.getValue(); 1148 } 1149 } 1150 1151 // Conductor options 1152 Element eConductorOptions = options.getChild(Xml.CONDUCTOR_OPTIONS); 1153 if (eConductorOptions != null) { 1154 if ((a = eConductorOptions.getAttribute(Xml.SHOW_HYPHEN_NAME)) != null) { 1155 _showLocationHyphenName = a.getValue().equals(Xml.TRUE); 1156 } 1157 } 1158 1159 // Row color options 1160 Element eRowColorOptions = options.getChild(Xml.ROW_COLOR_OPTIONS); 1161 if (eRowColorOptions != null) { 1162 if ((a = eRowColorOptions.getAttribute(Xml.ROW_COLOR_MANUAL)) != null) { 1163 _rowColorManual = a.getValue().equals(Xml.TRUE); 1164 } 1165 if ((a = eRowColorOptions.getAttribute(Xml.ROW_COLOR_BUILD_FAILED)) != null) { 1166 _rowColorBuildFailed = a.getValue().toLowerCase(); 1167 } 1168 if ((a = eRowColorOptions.getAttribute(Xml.ROW_COLOR_BUILT)) != null) { 1169 _rowColorBuilt = a.getValue().toLowerCase(); 1170 } 1171 if ((a = eRowColorOptions.getAttribute(Xml.ROW_COLOR_TRAIN_EN_ROUTE)) != null) { 1172 _rowColorTrainEnRoute = a.getValue().toLowerCase(); 1173 } 1174 if ((a = eRowColorOptions.getAttribute(Xml.ROW_COLOR_TERMINATED)) != null) { 1175 _rowColorTerminated = a.getValue().toLowerCase(); 1176 } 1177 if ((a = eRowColorOptions.getAttribute(Xml.ROW_COLOR_RESET)) != null) { 1178 _rowColorReset = a.getValue().toLowerCase(); 1179 } 1180 } 1181 1182 // moved to train schedule manager 1183 e = options.getChild(jmri.jmrit.operations.trains.schedules.Xml.TRAIN_SCHEDULE_OPTIONS); 1184 if (e != null) { 1185 if ((a = e.getAttribute(jmri.jmrit.operations.trains.schedules.Xml.ACTIVE_ID)) != null) { 1186 InstanceManager.getDefault(TrainScheduleManager.class).setTrainScheduleActiveId(a.getValue()); 1187 } 1188 } 1189 // check for scripts 1190 if (options.getChild(Xml.SCRIPTS) != null) { 1191 List<Element> lm = options.getChild(Xml.SCRIPTS).getChildren(Xml.START_UP); 1192 for (Element es : lm) { 1193 if ((a = es.getAttribute(Xml.NAME)) != null) { 1194 addStartUpScript(a.getValue()); 1195 } 1196 } 1197 List<Element> lt = options.getChild(Xml.SCRIPTS).getChildren(Xml.SHUT_DOWN); 1198 for (Element es : lt) { 1199 if ((a = es.getAttribute(Xml.NAME)) != null) { 1200 addShutDownScript(a.getValue()); 1201 } 1202 } 1203 } 1204 } 1205 if (root.getChild(Xml.TRAINS) != null) { 1206 List<Element> eTrains = root.getChild(Xml.TRAINS).getChildren(Xml.TRAIN); 1207 log.debug("readFile sees {} trains", eTrains.size()); 1208 for (Element eTrain : eTrains) { 1209 register(new Train(eTrain)); 1210 } 1211 } 1212 } 1213 1214 /** 1215 * Create an XML element to represent this Entry. This member has to remain 1216 * synchronized with the detailed DTD in operations-trains.dtd. 1217 * 1218 * @param root common Element for operations-trains.dtd. 1219 * 1220 */ 1221 public void store(Element root) { 1222 Element options = new Element(Xml.OPTIONS); 1223 Element e = new Element(Xml.TRAIN_OPTIONS); 1224 e.setAttribute(Xml.BUILD_MESSAGES, isBuildMessagesEnabled() ? Xml.TRUE : Xml.FALSE); 1225 e.setAttribute(Xml.BUILD_REPORT, isBuildReportEnabled() ? Xml.TRUE : Xml.FALSE); 1226 e.setAttribute(Xml.PRINT_PREVIEW, isPrintPreviewEnabled() ? Xml.TRUE : Xml.FALSE); 1227 e.setAttribute(Xml.OPEN_FILE, isOpenFileEnabled() ? Xml.TRUE : Xml.FALSE); 1228 e.setAttribute(Xml.RUN_FILE, isRunFileEnabled() ? Xml.TRUE : Xml.FALSE); 1229 e.setAttribute(Xml.TRAIN_ACTION, getTrainsFrameTrainAction()); 1230 options.addContent(e); 1231 1232 // Conductor options 1233 e = new Element(Xml.CONDUCTOR_OPTIONS); 1234 e.setAttribute(Xml.SHOW_HYPHEN_NAME, isShowLocationHyphenNameEnabled() ? Xml.TRUE : Xml.FALSE); 1235 options.addContent(e); 1236 1237 // Trains table row color options 1238 e = new Element(Xml.ROW_COLOR_OPTIONS); 1239 e.setAttribute(Xml.ROW_COLOR_MANUAL, isRowColorManual() ? Xml.TRUE : Xml.FALSE); 1240 e.setAttribute(Xml.ROW_COLOR_BUILD_FAILED, getRowColorNameForBuildFailed()); 1241 e.setAttribute(Xml.ROW_COLOR_BUILT, getRowColorNameForBuilt()); 1242 e.setAttribute(Xml.ROW_COLOR_TRAIN_EN_ROUTE, getRowColorNameForTrainEnRoute()); 1243 e.setAttribute(Xml.ROW_COLOR_TERMINATED, getRowColorNameForTerminated()); 1244 e.setAttribute(Xml.ROW_COLOR_RESET, getRowColorNameForReset()); 1245 options.addContent(e); 1246 1247 if (getStartUpScripts().size() > 0 || getShutDownScripts().size() > 0) { 1248 // save list of shutdown scripts 1249 Element es = new Element(Xml.SCRIPTS); 1250 for (String scriptName : getStartUpScripts()) { 1251 Element em = new Element(Xml.START_UP); 1252 em.setAttribute(Xml.NAME, scriptName); 1253 es.addContent(em); 1254 } 1255 // save list of termination scripts 1256 for (String scriptName : getShutDownScripts()) { 1257 Element et = new Element(Xml.SHUT_DOWN); 1258 et.setAttribute(Xml.NAME, scriptName); 1259 es.addContent(et); 1260 } 1261 options.addContent(es); 1262 } 1263 1264 InstanceManager.getDefault(TrainCustomManifest.class).store(options); // save custom manifest elements 1265 InstanceManager.getDefault(TrainCustomSwitchList.class).store(options); // save custom switch list elements 1266 1267 root.addContent(options); 1268 1269 Element trains = new Element(Xml.TRAINS); 1270 root.addContent(trains); 1271 // add entries 1272 for (Train train : getTrainsByIdList()) { 1273 trains.addContent(train.store()); 1274 } 1275 firePropertyChange(TRAINS_SAVED_PROPERTY, true, false); 1276 } 1277 1278 /** 1279 * Not currently used. 1280 */ 1281 @Override 1282 public void propertyChange(java.beans.PropertyChangeEvent e) { 1283 log.debug("TrainManager sees property change: {} old: {} new: {}", e.getPropertyName(), e.getOldValue(), 1284 e.getNewValue()); 1285 } 1286 1287 private void setDirtyAndFirePropertyChange(String p, Object old, Object n) { 1288 InstanceManager.getDefault(TrainManagerXml.class).setDirty(true); 1289 firePropertyChange(p, old, n); 1290 } 1291 1292 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(TrainManager.class); 1293 1294 @Override 1295 public void initialize() { 1296 InstanceManager.getDefault(OperationsSetupXml.class); // load setup 1297 InstanceManager.getDefault(CarManagerXml.class); // load cars 1298 InstanceManager.getDefault(EngineManagerXml.class); // load engines 1299 InstanceManager.getDefault(TrainManagerXml.class); // load trains 1300 } 1301 1302}