001package jmri.jmrit.operations.trains; 002 003import java.awt.Color; 004import java.beans.PropertyChangeListener; 005import java.io.*; 006import java.text.MessageFormat; 007import java.text.SimpleDateFormat; 008import java.util.*; 009 010import org.jdom2.Element; 011 012import jmri.InstanceManager; 013import jmri.beans.Identifiable; 014import jmri.beans.PropertyChangeSupport; 015import jmri.jmrit.display.Editor; 016import jmri.jmrit.display.EditorManager; 017import jmri.jmrit.operations.locations.*; 018import jmri.jmrit.operations.rollingstock.RollingStock; 019import jmri.jmrit.operations.rollingstock.RollingStockManager; 020import jmri.jmrit.operations.rollingstock.cars.*; 021import jmri.jmrit.operations.rollingstock.engines.*; 022import jmri.jmrit.operations.routes.*; 023import jmri.jmrit.operations.setup.Control; 024import jmri.jmrit.operations.setup.Setup; 025import jmri.jmrit.operations.trains.excel.TrainCustomManifest; 026import jmri.jmrit.roster.RosterEntry; 027import jmri.script.JmriScriptEngineManager; 028import jmri.util.FileUtil; 029import jmri.util.swing.JmriJOptionPane; 030 031/** 032 * Represents a train on the layout 033 * 034 * @author Daniel Boudreau Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013, 035 * 2014, 2015 036 * @author Rodney Black Copyright (C) 2011 037 */ 038public class Train extends PropertyChangeSupport implements Identifiable, PropertyChangeListener { 039 040 /* 041 * WARNING DO NOT LOAD CAR OR ENGINE MANAGERS WHEN Train.java IS CREATED IT 042 * CAUSES A RECURSIVE LOOP AT LOAD TIME, SEE EXAMPLES BELOW CarManager 043 * carManager = InstanceManager.getDefault(CarManager.class); EngineManager 044 * engineManager = InstanceManager.getDefault(EngineManager.class); 045 */ 046 047 // The release date for JMRI operations 10/29/2008 048 049 public static final String NONE = ""; 050 051 protected String _id = NONE; 052 protected String _name = NONE; 053 protected String _description = NONE; 054 protected RouteLocation _current = null;// where the train is located in its route 055 protected String _buildFailedMessage = NONE; // the build failed message for this train 056 protected boolean _built = false; // when true, a train manifest has been built 057 protected boolean _modified = false; // when true, user has modified train after being built 058 protected boolean _build = true; // when true, build this train 059 protected boolean _buildFailed = false; // when true, build for this train failed 060 protected boolean _printed = false; // when true, manifest has been printed 061 protected boolean _sendToTerminal = false; // when true, cars picked up by train only go to terminal 062 protected boolean _allowLocalMoves = true; // when true, cars with custom loads can be moved locally 063 protected boolean _allowThroughCars = true; // when true, cars from the origin can be sent to the terminal 064 protected boolean _buildNormal = false; // when true build this train in normal mode 065 protected boolean _allowCarsReturnStaging = false; // when true allow cars to return to staging 066 protected boolean _serviceAllCarsWithFinalDestinations = false; // when true, service cars with final destinations 067 protected boolean _buildConsist = false; // when true, build a consist for this train using single locomotives 068 protected boolean _sendCarsWithCustomLoadsToStaging = false; // when true, send cars to staging if spurs full 069 protected Route _route = null; 070 protected Track _departureTrack; // the departure track from staging 071 protected Track _terminationTrack; // the termination track into staging 072 protected String _carRoadOption = ALL_ROADS;// train car road name restrictions 073 protected List<String> _carRoadList = new ArrayList<>(); 074 protected String _cabooseRoadOption = ALL_ROADS;// train caboose road name restrictions 075 protected List<String> _cabooseRoadList = new ArrayList<>(); 076 protected String _locoRoadOption = ALL_ROADS;// train engine road name restrictions 077 protected List<String> _locoRoadList = new ArrayList<>(); 078 protected int _requires = NO_CABOOSE_OR_FRED; // train requirements, caboose, FRED 079 protected String _numberEngines = "0"; // number of engines this train requires 080 protected String _engineRoad = NONE; // required road name for engines assigned to this train 081 protected String _engineModel = NONE; // required model of engines assigned to this train 082 protected String _cabooseRoad = NONE; // required road name for cabooses assigned to this train 083 protected String _departureTime = "00:00"; // NOI18N departure time for this train 084 protected String _leadEngineId = NONE; // lead engine for train icon info 085 protected String _builtStartYear = NONE; // built start year 086 protected String _builtEndYear = NONE; // built end year 087 protected String _loadOption = ALL_LOADS;// train load restrictions 088 protected String _ownerOption = ALL_OWNERS;// train owner name restrictions 089 protected List<String> _buildScripts = new ArrayList<>(); // list of script pathnames to run before train is built 090 protected List<String> _afterBuildScripts = new ArrayList<>(); // script pathnames to run after train is built 091 protected List<String> _moveScripts = new ArrayList<>(); // list of script pathnames to run when train is moved 092 protected List<String> _terminationScripts = new ArrayList<>(); // script pathnames to run when train is terminated 093 protected String _railroadName = NONE; // optional railroad name for this train 094 protected String _logoPathName = NONE; // optional manifest logo for this train 095 protected boolean _showTimes = true; // when true, show arrival and departure times for this train 096 protected Engine _leadEngine = null; // lead engine for icon 097 protected String _switchListStatus = UNKNOWN; // print switch list status 098 protected String _comment = NONE; 099 protected String _serviceStatus = NONE; // status only if train is being built 100 protected int _statusCode = CODE_UNKNOWN; 101 protected int _oldStatusCode = CODE_UNKNOWN; 102 protected Date _date; // date for last status change for this train 103 protected int _statusCarsRequested = 0; 104 protected String _tableRowColorName = NONE; // color of row in Trains table 105 protected String _tableRowColorResetName = NONE; // color of row in Trains table when reset 106 107 // Engine change and helper engines 108 protected int _leg2Options = NO_CABOOSE_OR_FRED; // options 109 protected RouteLocation _leg2Start = null; // route location when 2nd leg begins 110 protected RouteLocation _end2Leg = null; // route location where 2nd leg ends 111 protected String _leg2Engines = "0"; // number of engines 2nd leg 112 protected String _leg2Road = NONE; // engine road name 2nd leg 113 protected String _leg2Model = NONE; // engine model 2nd leg 114 protected String _leg2CabooseRoad = NONE; // road name for caboose 2nd leg 115 116 protected int _leg3Options = NO_CABOOSE_OR_FRED; // options 117 protected RouteLocation _leg3Start = null; // route location when 3rd leg begins 118 protected RouteLocation _leg3End = null; // route location where 3rd leg ends 119 protected String _leg3Engines = "0"; // number of engines 3rd leg 120 protected String _leg3Road = NONE; // engine road name 3rd leg 121 protected String _leg3Model = NONE; // engine model 3rd leg 122 protected String _leg3CabooseRoad = NONE; // road name for caboose 3rd leg 123 124 // engine change and helper options 125 public static final int CHANGE_ENGINES = 1; // change engines 126 public static final int HELPER_ENGINES = 2; // add helper engines 127 public static final int ADD_CABOOSE = 4; // add caboose 128 public static final int REMOVE_CABOOSE = 8; // remove caboose 129 public static final int ADD_ENGINES = 16; // add engines 130 public static final int REMOVE_ENGINES = 32; // remove engines 131 132 // property change names 133 public static final String DISPOSE_CHANGED_PROPERTY = "TrainDispose"; // NOI18N 134 public static final String STOPS_CHANGED_PROPERTY = "TrainStops"; // NOI18N 135 public static final String TYPES_CHANGED_PROPERTY = "TrainTypes"; // NOI18N 136 public static final String BUILT_CHANGED_PROPERTY = "TrainBuilt"; // NOI18N 137 public static final String BUILT_YEAR_CHANGED_PROPERTY = "TrainBuiltYear"; // NOI18N 138 public static final String BUILD_CHANGED_PROPERTY = "TrainBuild"; // NOI18N 139 public static final String ROADS_CHANGED_PROPERTY = "TrainRoads"; // NOI18N 140 public static final String LOADS_CHANGED_PROPERTY = "TrainLoads"; // NOI18N 141 public static final String OWNERS_CHANGED_PROPERTY = "TrainOwners"; // NOI18N 142 public static final String NAME_CHANGED_PROPERTY = "TrainName"; // NOI18N 143 public static final String DESCRIPTION_CHANGED_PROPERTY = "TrainDescription"; // NOI18N 144 public static final String STATUS_CHANGED_PROPERTY = "TrainStatus"; // NOI18N 145 public static final String DEPARTURETIME_CHANGED_PROPERTY = "TrainDepartureTime"; // NOI18N 146 public static final String TRAIN_LOCATION_CHANGED_PROPERTY = "TrainLocation"; // NOI18N 147 public static final String TRAIN_ROUTE_CHANGED_PROPERTY = "TrainRoute"; // NOI18N 148 public static final String TRAIN_REQUIREMENTS_CHANGED_PROPERTY = "TrainRequirements"; // NOI18N 149 public static final String TRAIN_MOVE_COMPLETE_CHANGED_PROPERTY = "TrainMoveComplete"; // NOI18N 150 public static final String TRAIN_ROW_COLOR_CHANGED_PROPERTY = "TrianRowColor"; // NOI18N 151 public static final String TRAIN_ROW_COLOR_RESET_CHANGED_PROPERTY = "TrianRowColorReset"; // NOI18N 152 public static final String TRAIN_MODIFIED_CHANGED_PROPERTY = "TrainModified"; // NOI18N 153 154 // Train status 155 public static final String TRAIN_RESET = Bundle.getMessage("TrainReset"); 156 public static final String RUN_SCRIPTS = Bundle.getMessage("RunScripts"); 157 public static final String BUILDING = Bundle.getMessage("Building"); 158 public static final String BUILD_FAILED = Bundle.getMessage("BuildFailed"); 159 public static final String BUILT = Bundle.getMessage("Built"); 160 public static final String PARTIAL_BUILT = Bundle.getMessage("Partial"); 161 public static final String TRAIN_EN_ROUTE = Bundle.getMessage("TrainEnRoute"); 162 public static final String TERMINATED = Bundle.getMessage("Terminated"); 163 public static final String MANIFEST_MODIFIED = Bundle.getMessage("Modified"); 164 165 // Train status codes 166 public static final int CODE_TRAIN_RESET = 0; 167 public static final int CODE_RUN_SCRIPTS = 0x100; 168 public static final int CODE_BUILDING = 0x01; 169 public static final int CODE_BUILD_FAILED = 0x02; 170 public static final int CODE_BUILT = 0x10; 171 public static final int CODE_PARTIAL_BUILT = CODE_BUILT + 0x04; 172 public static final int CODE_TRAIN_EN_ROUTE = CODE_BUILT + 0x08; 173 public static final int CODE_TERMINATED = 0x80; 174 public static final int CODE_MANIFEST_MODIFIED = 0x200; 175 public static final int CODE_UNKNOWN = 0xFFFF; 176 177 // train requirements 178 public static final int NO_CABOOSE_OR_FRED = 0; // default 179 public static final int CABOOSE = 1; 180 public static final int FRED = 2; 181 182 // road options 183 public static final String ALL_ROADS = Bundle.getMessage("All"); 184 public static final String INCLUDE_ROADS = Bundle.getMessage("Include"); 185 public static final String EXCLUDE_ROADS = Bundle.getMessage("Exclude"); 186 187 // owner options 188 public static final String ALL_OWNERS = Bundle.getMessage("All"); 189 public static final String INCLUDE_OWNERS = Bundle.getMessage("Include"); 190 public static final String EXCLUDE_OWNERS = Bundle.getMessage("Exclude"); 191 192 // load options 193 public static final String ALL_LOADS = Bundle.getMessage("All"); 194 public static final String INCLUDE_LOADS = Bundle.getMessage("Include"); 195 public static final String EXCLUDE_LOADS = Bundle.getMessage("Exclude"); 196 197 // Switch list status 198 public static final String UNKNOWN = ""; 199 public static final String PRINTED = Bundle.getMessage("Printed"); 200 201 public static final String AUTO = Bundle.getMessage("Auto"); 202 public static final String AUTO_HPT = Bundle.getMessage("AutoHPT"); 203 204 public Train(String id, String name) { 205 // log.debug("New train ({}) id: {}", name, id); 206 _name = name; 207 _id = id; 208 // a new train accepts all types 209 setTypeNames(InstanceManager.getDefault(CarTypes.class).getNames()); 210 setTypeNames(InstanceManager.getDefault(EngineTypes.class).getNames()); 211 addPropertyChangeListerners(); 212 } 213 214 @Override 215 public String getId() { 216 return _id; 217 } 218 219 /** 220 * Sets the name of this train, normally a short name that can fit within 221 * the train icon. 222 * 223 * @param name the train's name. 224 */ 225 public void setName(String name) { 226 String old = _name; 227 _name = name; 228 if (!old.equals(name)) { 229 setDirtyAndFirePropertyChange(NAME_CHANGED_PROPERTY, old, name); 230 } 231 } 232 233 // for combo boxes 234 /** 235 * Get's a train's name 236 * 237 * @return train's name 238 */ 239 @Override 240 public String toString() { 241 return _name; 242 } 243 244 /** 245 * Get's a train's name 246 * 247 * @return train's name 248 */ 249 public String getName() { 250 return _name; 251 } 252 253 /** 254 * @return The name of the color when highlighting the train's row 255 */ 256 public String getTableRowColorName() { 257 return _tableRowColorName; 258 } 259 260 public void setTableRowColorName(String colorName) { 261 String old = _tableRowColorName; 262 _tableRowColorName = colorName; 263 if (!old.equals(colorName)) { 264 setDirtyAndFirePropertyChange(TRAIN_ROW_COLOR_CHANGED_PROPERTY, old, colorName); 265 } 266 } 267 268 /** 269 * @return The name of the train row color when the train is reset 270 */ 271 public String getTableRowColorNameReset() { 272 return _tableRowColorResetName; 273 } 274 275 public void setTableRowColorNameReset(String colorName) { 276 String old = _tableRowColorResetName; 277 _tableRowColorResetName = colorName; 278 if (!old.equals(colorName)) { 279 setDirtyAndFirePropertyChange(TRAIN_ROW_COLOR_RESET_CHANGED_PROPERTY, old, colorName); 280 } 281 } 282 283 /** 284 * @return The color when highlighting the train's row 285 */ 286 public Color getTableRowColor() { 287 String colorName = getTableRowColorName(); 288 if (colorName.equals(NONE)) { 289 return null; 290 } else { 291 return Setup.getColor(colorName); 292 } 293 } 294 295 /** 296 * Get's train's departure time 297 * 298 * @return train's departure time in the String format hh:mm 299 */ 300 public String getDepartureTime() { 301 // check to see if the route has a departure time 302 RouteLocation rl = getTrainDepartsRouteLocation(); 303 if (rl != null) { 304 rl.removePropertyChangeListener(this); 305 rl.addPropertyChangeListener(this); 306 if (!rl.getDepartureTime().equals(RouteLocation.NONE)) { 307 return rl.getDepartureTime(); 308 } 309 } 310 return _departureTime; 311 } 312 313 /** 314 * Get's train's departure time in 12hr or 24hr format 315 * 316 * @return train's departure time in the String format hh:mm or hh:mm(AM/PM) 317 */ 318 public String getFormatedDepartureTime() { 319 // check to see if the route has a departure time 320 RouteLocation rl = getTrainDepartsRouteLocation(); 321 if (rl != null && !rl.getDepartureTime().equals(RouteLocation.NONE)) { 322 // need to forward any changes to departure time 323 rl.removePropertyChangeListener(this); 324 rl.addPropertyChangeListener(this); 325 return rl.getFormatedDepartureTime(); 326 } 327 return (parseTime(getDepartTimeMinutes())); 328 } 329 330 /** 331 * Get train's departure time in minutes from midnight for sorting 332 * 333 * @return int hh*60+mm 334 */ 335 public int getDepartTimeMinutes() { 336 int hour = Integer.parseInt(getDepartureTimeHour()); 337 int minute = Integer.parseInt(getDepartureTimeMinute()); 338 return (hour * 60) + minute; 339 } 340 341 public void setDepartureTime(String hour, String minute) { 342 String old = _departureTime; 343 int h = Integer.parseInt(hour); 344 if (h < 10) { 345 hour = "0" + h; 346 } 347 int m = Integer.parseInt(minute); 348 if (m < 10) { 349 minute = "0" + m; 350 } 351 String time = hour + ":" + minute; 352 _departureTime = time; 353 if (!old.equals(time)) { 354 setDirtyAndFirePropertyChange(DEPARTURETIME_CHANGED_PROPERTY, old, _departureTime); 355 setModified(true); 356 } 357 } 358 359 public String getDepartureTimeHour() { 360 String[] time = getDepartureTime().split(":"); 361 return time[0]; 362 } 363 364 public String getDepartureTimeMinute() { 365 String[] time = getDepartureTime().split(":"); 366 return time[1]; 367 } 368 369 public static final String ALREADY_SERVICED = "-1"; // NOI18N 370 371 /** 372 * Gets the expected time when this train will arrive at the location rl. 373 * Expected arrival time is based on the number of car pick up and set outs 374 * for this train. TODO Doesn't provide expected arrival time if train is in 375 * route, instead provides relative time. If train is at or has passed the 376 * location return -1. 377 * 378 * @param routeLocation The RouteLocation. 379 * @return expected arrival time in minutes (append AM or PM if 12 hour 380 * format) 381 */ 382 public String getExpectedArrivalTime(RouteLocation routeLocation) { 383 int minutes = getExpectedTravelTimeInMinutes(routeLocation); 384 if (minutes == -1) { 385 return ALREADY_SERVICED; 386 } 387 log.debug("Expected arrival time for train ({}) at ({}), {} minutes", getName(), routeLocation.getName(), 388 minutes); 389 // TODO use fast clock to get current time vs departure time 390 // for now use relative 391 return parseTime(minutes); 392 } 393 394 public String getExpectedDepartureTime(RouteLocation routeLocation) { 395 int minutes = getExpectedTravelTimeInMinutes(routeLocation); 396 if (minutes == -1) { 397 return ALREADY_SERVICED; 398 } 399 if (!routeLocation.getDepartureTime().equals(RouteLocation.NONE)) { 400 return routeLocation.getFormatedDepartureTime(); 401 } 402 // figure out the work at this location, note that there can be 403 // consecutive locations with the same name 404 if (getRoute() != null) { 405 boolean foundRouteLocation = false; 406 for (RouteLocation rl : getRoute().getLocationsBySequenceList()) { 407 if (rl == routeLocation) { 408 foundRouteLocation = true; 409 } 410 if (foundRouteLocation) { 411 if (rl.getSplitName() 412 .equals(routeLocation.getSplitName())) { 413 minutes = minutes + getWorkTimeAtLocation(rl); 414 } else { 415 break; // done 416 } 417 } 418 } 419 } 420 log.debug("Expected departure time {} for train ({}) at ({})", minutes, getName(), routeLocation.getName()); 421 return parseTime(minutes); 422 } 423 424 public int getWorkTimeAtLocation(RouteLocation routeLocation) { 425 int minutes = 0; 426 // departure? 427 if (routeLocation == getTrainDepartsRouteLocation()) { 428 return minutes; 429 } 430 // add any work at this location 431 for (Car rs : InstanceManager.getDefault(CarManager.class).getList(this)) { 432 if (rs.getRouteLocation() == routeLocation && !rs.getTrackName().equals(RollingStock.NONE)) { 433 minutes += Setup.getSwitchTime(); 434 } 435 if (rs.getRouteDestination() == routeLocation) { 436 minutes += Setup.getSwitchTime(); 437 } 438 } 439 return minutes; 440 } 441 442 public int getExpectedTravelTimeInMinutes(RouteLocation routeLocation) { 443 int minutes = 0; 444 if (!isTrainEnRoute()) { 445 minutes += Integer.parseInt(getDepartureTimeMinute()); 446 minutes += 60 * Integer.parseInt(getDepartureTimeHour()); 447 } else { 448 minutes = -1; // -1 means train has already served the location 449 } 450 // boolean trainAt = false; 451 boolean trainLocFound = false; 452 if (getRoute() != null) { 453 List<RouteLocation> routeList = getRoute().getLocationsBySequenceList(); 454 for (int i = 0; i < routeList.size(); i++) { 455 RouteLocation rl = routeList.get(i); 456 if (rl == routeLocation) { 457 break; // done 458 } 459 // start recording time after finding where the train is 460 if (!trainLocFound && isTrainEnRoute()) { 461 if (rl == getCurrentRouteLocation()) { 462 trainLocFound = true; 463 // add travel time 464 minutes = Setup.getTravelTime(); 465 } 466 continue; 467 } 468 // is there a departure time from this location? 469 if (!rl.getDepartureTime().equals(RouteLocation.NONE)) { 470 String dt = rl.getDepartureTime(); 471 log.debug("Location {} departure time {}", rl.getName(), dt); 472 String[] time = dt.split(":"); 473 minutes = 60 * Integer.parseInt(time[0]) + Integer.parseInt(time[1]); 474 // log.debug("New minutes: "+minutes); 475 } 476 // add wait time 477 minutes += rl.getWait(); 478 // add travel time if new location 479 RouteLocation next = routeList.get(i + 1); 480 if (next != null && 481 !rl.getSplitName().equals(next.getSplitName())) { 482 minutes += Setup.getTravelTime(); 483 } 484 // don't count work if there's a departure time 485 if (i == 0 || !rl.getDepartureTime().equals(RouteLocation.NONE)) { 486 continue; 487 } 488 // now add the work at the location 489 minutes = minutes + getWorkTimeAtLocation(rl); 490 } 491 } 492 return minutes; 493 } 494 495 /** 496 * Returns time in hour:minute format 497 * 498 * @param minutes number of minutes from midnight 499 * @return hour:minute (optionally AM:PM format) 500 */ 501 private String parseTime(int minutes) { 502 int hours = 0; 503 int days = 0; 504 505 if (minutes >= 60) { 506 int h = minutes / 60; 507 minutes = minutes - h * 60; 508 hours += h; 509 } 510 511 String d = ""; 512 if (hours >= 24) { 513 int nd = hours / 24; 514 hours = hours - nd * 24; 515 days += nd; 516 d = Integer.toString(days) + ":"; 517 } 518 519 // AM_PM field 520 String am_pm = ""; 521 if (Setup.is12hrFormatEnabled()) { 522 am_pm = " " + Bundle.getMessage("AM"); 523 if (hours >= 12) { 524 hours = hours - 12; 525 am_pm = " " + Bundle.getMessage("PM"); 526 } 527 if (hours == 0) { 528 hours = 12; 529 } 530 } 531 532 String h = Integer.toString(hours); 533 if (hours < 10) { 534 h = "0" + h; 535 } 536 if (minutes < 10) { 537 return d + h + ":0" + minutes + am_pm; // NOI18N 538 } 539 return d + h + ":" + minutes + am_pm; 540 } 541 542 /** 543 * Set train requirements. If NO_CABOOSE_OR_FRED, then train doesn't require 544 * a caboose or car with FRED. 545 * 546 * @param requires NO_CABOOSE_OR_FRED, CABOOSE, FRED 547 */ 548 public void setRequirements(int requires) { 549 int old = _requires; 550 _requires = requires; 551 if (old != requires) { 552 setDirtyAndFirePropertyChange(TRAIN_REQUIREMENTS_CHANGED_PROPERTY, Integer.toString(old), 553 Integer.toString(requires)); 554 } 555 } 556 557 /** 558 * Get a train's requirements with regards to the last car in the train. 559 * 560 * @return NONE CABOOSE FRED 561 */ 562 public int getRequirements() { 563 return _requires; 564 } 565 566 public boolean isCabooseNeeded() { 567 return (getRequirements() & CABOOSE) == CABOOSE; 568 } 569 570 public boolean isFredNeeded() { 571 return (getRequirements() & FRED) == FRED; 572 } 573 574 public void setRoute(Route route) { 575 Route old = _route; 576 String oldRoute = NONE; 577 String newRoute = NONE; 578 if (old != null) { 579 old.removePropertyChangeListener(this); 580 oldRoute = old.toString(); 581 } 582 if (route != null) { 583 route.addPropertyChangeListener(this); 584 newRoute = route.toString(); 585 } 586 _route = route; 587 _skipLocationsList.clear(); 588 if (old == null || !old.equals(route)) { 589 setDirtyAndFirePropertyChange(TRAIN_ROUTE_CHANGED_PROPERTY, oldRoute, newRoute); 590 } 591 } 592 593 /** 594 * Gets the train's route 595 * 596 * @return train's route 597 */ 598 public Route getRoute() { 599 return _route; 600 } 601 602 /** 603 * Get's the train's route name. 604 * 605 * @return Train's route name. 606 */ 607 public String getTrainRouteName() { 608 if (getRoute() == null) { 609 return NONE; 610 } 611 return getRoute().getName(); 612 } 613 614 /** 615 * Get the train's departure location's name 616 * 617 * @return train's departure location's name 618 */ 619 public String getTrainDepartsName() { 620 if (getTrainDepartsRouteLocation() != null) { 621 return getTrainDepartsRouteLocation().getName(); 622 } 623 return NONE; 624 } 625 626 public RouteLocation getTrainDepartsRouteLocation() { 627 if (getRoute() == null) { 628 return null; 629 } 630 return getRoute().getDepartsRouteLocation(); 631 } 632 633 public String getTrainDepartsDirection() { 634 String direction = NONE; 635 if (getTrainDepartsRouteLocation() != null) { 636 direction = getTrainDepartsRouteLocation().getTrainDirectionString(); 637 } 638 return direction; 639 } 640 641 /** 642 * Get train's final location's name 643 * 644 * @return train's final location's name 645 */ 646 public String getTrainTerminatesName() { 647 if (getTrainTerminatesRouteLocation() != null) { 648 return getTrainTerminatesRouteLocation().getName(); 649 } 650 return NONE; 651 } 652 653 public RouteLocation getTrainTerminatesRouteLocation() { 654 if (getRoute() == null) { 655 return null; 656 } 657 return getRoute().getTerminatesRouteLocation(); 658 } 659 660 /** 661 * Returns the order the train should be blocked. 662 * 663 * @return routeLocations for this train. 664 */ 665 public List<RouteLocation> getTrainBlockingOrder() { 666 if (getRoute() == null) { 667 return null; 668 } 669 return getRoute().getBlockingOrder(); 670 } 671 672 /** 673 * Set train's current route location 674 * 675 * @param location The current RouteLocation. 676 */ 677 protected void setCurrentLocation(RouteLocation location) { 678 RouteLocation old = _current; 679 _current = location; 680 if ((old != null && !old.equals(location)) || (old == null && location != null)) { 681 setDirtyAndFirePropertyChange("current", old, location); // NOI18N 682 } 683 } 684 685 /** 686 * Get train's current location name 687 * 688 * @return Train's current route location name 689 */ 690 public String getCurrentLocationName() { 691 if (getCurrentRouteLocation() == null) { 692 return NONE; 693 } 694 return getCurrentRouteLocation().getName(); 695 } 696 697 /** 698 * Get train's current route location 699 * 700 * @return Train's current route location 701 */ 702 public RouteLocation getCurrentRouteLocation() { 703 if (getRoute() == null) { 704 return null; 705 } 706 if (_current == null) { 707 return null; 708 } 709 // this will verify that the current location still exists 710 return getRoute().getLocationById(_current.getId()); 711 } 712 713 /** 714 * Get the train's next location name 715 * 716 * @return Train's next route location name 717 */ 718 public String getNextLocationName() { 719 return getNextLocationName(1); 720 } 721 722 /** 723 * Get a location name in a train's route from the current train's location. 724 * A number of "1" means get the next location name in a train's route. 725 * 726 * @param number The stop number, must be greater than 0 727 * @return Name of the location that is the number of stops away from the 728 * train's current location. 729 */ 730 public String getNextLocationName(int number) { 731 RouteLocation rl = getCurrentRouteLocation(); 732 while (number-- > 0) { 733 rl = getNextRouteLocation(rl); 734 if (rl == null) { 735 return NONE; 736 } 737 } 738 return rl.getName(); 739 } 740 741 public RouteLocation getNextRouteLocation(RouteLocation currentRouteLocation) { 742 if (getRoute() == null) { 743 return null; 744 } 745 List<RouteLocation> routeList = getRoute().getLocationsBySequenceList(); 746 for (int i = 0; i < routeList.size(); i++) { 747 RouteLocation rl = routeList.get(i); 748 if (rl == currentRouteLocation) { 749 i++; 750 if (i < routeList.size()) { 751 return routeList.get(i); 752 } 753 break; 754 } 755 } 756 return null; // At end of route 757 } 758 759 public void setDepartureTrack(Track track) { 760 Track old = _departureTrack; 761 _departureTrack = track; 762 if (old != track) { 763 setDirtyAndFirePropertyChange("DepartureTrackChanged", old, track); // NOI18N 764 } 765 } 766 767 public Track getDepartureTrack() { 768 return _departureTrack; 769 } 770 771 public boolean isDepartingStaging() { 772 return getDepartureTrack() != null; 773 } 774 775 public void setTerminationTrack(Track track) { 776 Track old = _terminationTrack; 777 _terminationTrack = track; 778 if (old != track) { 779 setDirtyAndFirePropertyChange("TerminationTrackChanged", old, track); // NOI18N 780 } 781 } 782 783 public Track getTerminationTrack() { 784 return _terminationTrack; 785 } 786 787 /** 788 * Set the train's machine readable status. Calls update train table row 789 * color. 790 * 791 * @param code machine readable 792 */ 793 public void setStatusCode(int code) { 794 String oldStatus = getStatus(); 795 int oldCode = getStatusCode(); 796 _statusCode = code; 797 setDate(Calendar.getInstance().getTime()); 798 // always fire property change for train en route 799 if (oldCode != getStatusCode() || code == CODE_TRAIN_EN_ROUTE) { 800 setDirtyAndFirePropertyChange(STATUS_CHANGED_PROPERTY, oldStatus, getStatus()); 801 } 802 updateTrainTableRowColor(); 803 } 804 805 public void updateTrainTableRowColor() { 806 if (!InstanceManager.getDefault(TrainManager.class).isRowColorManual()) { 807 switch (getStatusCode()) { 808 case CODE_TRAIN_RESET: 809 String color = getTableRowColorNameReset(); 810 if (color.equals(NONE)) { 811 color = InstanceManager.getDefault(TrainManager.class).getRowColorNameForReset(); 812 } 813 setTableRowColorName(color); 814 break; 815 case CODE_BUILT: 816 case CODE_PARTIAL_BUILT: 817 setTableRowColorName(InstanceManager.getDefault(TrainManager.class).getRowColorNameForBuilt()); 818 break; 819 case CODE_BUILD_FAILED: 820 setTableRowColorName( 821 InstanceManager.getDefault(TrainManager.class).getRowColorNameForBuildFailed()); 822 break; 823 case CODE_TRAIN_EN_ROUTE: 824 setTableRowColorName( 825 InstanceManager.getDefault(TrainManager.class).getRowColorNameForTrainEnRoute()); 826 break; 827 case CODE_TERMINATED: 828 setTableRowColorName(InstanceManager.getDefault(TrainManager.class).getRowColorNameForTerminated()); 829 break; 830 default: // all other cases do nothing 831 break; 832 } 833 } 834 } 835 836 /** 837 * Get train's status in the default locale. 838 * 839 * @return Human-readable status 840 */ 841 public String getStatus() { 842 return this.getStatus(Locale.getDefault()); 843 } 844 845 /** 846 * Get train's status in the specified locale. 847 * 848 * @param locale The Locale. 849 * @return Human-readable status 850 */ 851 public String getStatus(Locale locale) { 852 return this.getStatus(locale, this.getStatusCode()); 853 } 854 855 /** 856 * Get the human-readable status for the requested status code. 857 * 858 * @param locale The Locale. 859 * @param code requested status 860 * @return Human-readable status 861 */ 862 public String getStatus(Locale locale, int code) { 863 switch (code) { 864 case CODE_RUN_SCRIPTS: 865 return RUN_SCRIPTS; 866 case CODE_BUILDING: 867 return BUILDING; 868 case CODE_BUILD_FAILED: 869 return BUILD_FAILED; 870 case CODE_BUILT: 871 return Bundle.getMessage(locale, "StatusBuilt", this.getNumberCarsWorked()); // NOI18N 872 case CODE_PARTIAL_BUILT: 873 return Bundle.getMessage(locale, "StatusPartialBuilt", this.getNumberCarsWorked(), 874 this.getNumberCarsRequested()); // NOI18N 875 case CODE_TERMINATED: 876 return Bundle.getMessage(locale, "StatusTerminated", this.getSortDate()); // NOI18N 877 case CODE_TRAIN_EN_ROUTE: 878 return Bundle.getMessage(locale, "StatusEnRoute", this.getNumberCarsInTrain(), this.getTrainLength(), 879 Setup.getLengthUnit().toLowerCase(), this.getTrainWeight()); // NOI18N 880 case CODE_TRAIN_RESET: 881 return TRAIN_RESET; 882 case CODE_MANIFEST_MODIFIED: 883 return MANIFEST_MODIFIED; 884 case CODE_UNKNOWN: 885 default: 886 return UNKNOWN; 887 } 888 } 889 890 public String getMRStatus() { 891 switch (getStatusCode()) { 892 case CODE_PARTIAL_BUILT: 893 return getStatusCode() + "||" + this.getNumberCarsRequested(); // NOI18N 894 case CODE_TERMINATED: 895 return getStatusCode() + "||" + this.getSortDate(); // NOI18N 896 default: 897 return Integer.toString(getStatusCode()); 898 } 899 } 900 901 public int getStatusCode() { 902 return _statusCode; 903 } 904 905 protected void setOldStatusCode(int code) { 906 _oldStatusCode = code; 907 } 908 909 protected int getOldStatusCode() { 910 return _oldStatusCode; 911 } 912 913 /** 914 * Used to determine if train has departed the first location in the train's 915 * route 916 * 917 * @return true if train has departed 918 */ 919 public boolean isTrainEnRoute() { 920 return !getCurrentLocationName().equals(NONE) && getTrainDepartsRouteLocation() != getCurrentRouteLocation(); 921 } 922 923 /** 924 * Used to determine if train is a local switcher serving one location. Note 925 * the train can have more than location in its route, but all location 926 * names must be "same". See TrainCommon.splitString(String name) for the 927 * definition of the "same" name. 928 * 929 * @return true if local switcher 930 */ 931 public boolean isLocalSwitcher() { 932 String departureName = TrainCommon.splitString(getTrainDepartsName()); 933 Route route = getRoute(); 934 if (route != null) { 935 for (RouteLocation rl : route.getLocationsBySequenceList()) { 936 if (!departureName.equals(rl.getSplitName())) { 937 return false; // not a local switcher 938 } 939 } 940 } 941 return true; 942 } 943 944 public boolean isTurn() { 945 return !isLocalSwitcher() && 946 TrainCommon.splitString(getTrainDepartsName()) 947 .equals(TrainCommon.splitString(getTrainTerminatesName())); 948 } 949 950 /** 951 * Used to determine if train is carrying only passenger cars. 952 * 953 * @return true if only passenger cars have been assigned to this train. 954 */ 955 public boolean isOnlyPassengerCars() { 956 for (Car car : InstanceManager.getDefault(CarManager.class).getList(this)) { 957 if (!car.isPassenger()) { 958 return false; 959 } 960 } 961 return true; 962 } 963 964 List<String> _skipLocationsList = new ArrayList<>(); 965 966 protected String[] getTrainSkipsLocations() { 967 String[] locationIds = new String[_skipLocationsList.size()]; 968 for (int i = 0; i < _skipLocationsList.size(); i++) { 969 locationIds[i] = _skipLocationsList.get(i); 970 } 971 return locationIds; 972 } 973 974 protected void setTrainSkipsLocations(String[] locationIds) { 975 if (locationIds.length > 0) { 976 Arrays.sort(locationIds); 977 for (String id : locationIds) { 978 _skipLocationsList.add(id); 979 } 980 } 981 } 982 983 /** 984 * Train will skip the RouteLocation 985 * 986 * @param routelocationId RouteLocation Id 987 */ 988 public void addTrainSkipsLocation(String routelocationId) { 989 // insert at start of _skipLocationsList, sort later 990 if (!_skipLocationsList.contains(routelocationId)) { 991 _skipLocationsList.add(0, routelocationId); 992 log.debug("train does not stop at {}", routelocationId); 993 setDirtyAndFirePropertyChange(STOPS_CHANGED_PROPERTY, _skipLocationsList.size() - 1, 994 _skipLocationsList.size()); 995 } 996 } 997 998 public void deleteTrainSkipsLocation(String locationId) { 999 _skipLocationsList.remove(locationId); 1000 log.debug("train will stop at {}", locationId); 1001 setDirtyAndFirePropertyChange(STOPS_CHANGED_PROPERTY, _skipLocationsList.size() + 1, _skipLocationsList.size()); 1002 } 1003 1004 /** 1005 * Determines if this train skips a location (doesn't service the location). 1006 * 1007 * @param locationId The route location id. 1008 * @return true if the train will not service the location. 1009 */ 1010 public boolean isLocationSkipped(String locationId) { 1011 return _skipLocationsList.contains(locationId); 1012 } 1013 1014 List<String> _typeList = new ArrayList<>(); 1015 1016 /** 1017 * Get's the type names of rolling stock this train will service 1018 * 1019 * @return The type names for cars and or engines 1020 */ 1021 protected String[] getTypeNames() { 1022 return _typeList.toArray(new String[0]); 1023 } 1024 1025 public String[] getCarTypeNames() { 1026 List<String> list = new ArrayList<>(); 1027 for (String type : _typeList) { 1028 if (InstanceManager.getDefault(CarTypes.class).containsName(type)) { 1029 list.add(type); 1030 } 1031 } 1032 return list.toArray(new String[0]); 1033 } 1034 1035 public String[] getLocoTypeNames() { 1036 List<String> list = new ArrayList<>(); 1037 for (String type : _typeList) { 1038 if (InstanceManager.getDefault(EngineTypes.class).containsName(type)) { 1039 list.add(type); 1040 } 1041 } 1042 return list.toArray(new String[0]); 1043 } 1044 1045 /** 1046 * Set the type of cars or engines this train will service, see types in 1047 * Cars and Engines. 1048 * 1049 * @param types The type names for cars and or engines 1050 */ 1051 protected void setTypeNames(String[] types) { 1052 if (types.length > 0) { 1053 Arrays.sort(types); 1054 for (String type : types) { 1055 _typeList.add(type); 1056 } 1057 } 1058 } 1059 1060 /** 1061 * Add a car or engine type name that this train will service. 1062 * 1063 * @param type The new type name to service. 1064 */ 1065 public void addTypeName(String type) { 1066 // insert at start of list, sort later 1067 if (type == null || _typeList.contains(type)) { 1068 return; 1069 } 1070 _typeList.add(0, type); 1071 log.debug("Train ({}) add car type ({})", getName(), type); 1072 setDirtyAndFirePropertyChange(TYPES_CHANGED_PROPERTY, _typeList.size() - 1, _typeList.size()); 1073 } 1074 1075 public void deleteTypeName(String type) { 1076 if (_typeList.remove(type)) { 1077 log.debug("Train ({}) delete car type ({})", getName(), type); 1078 setDirtyAndFirePropertyChange(TYPES_CHANGED_PROPERTY, _typeList.size() + 1, _typeList.size()); 1079 } 1080 } 1081 1082 /** 1083 * Returns true if this train will service the type of car or engine. 1084 * 1085 * @param type The car or engine type name. 1086 * @return true if this train will service the particular type. 1087 */ 1088 public boolean isTypeNameAccepted(String type) { 1089 return _typeList.contains(type); 1090 } 1091 1092 protected void replaceType(String oldType, String newType) { 1093 if (isTypeNameAccepted(oldType)) { 1094 deleteTypeName(oldType); 1095 addTypeName(newType); 1096 // adjust loads with type in them 1097 for (String load : getLoadNames()) { 1098 String[] splitLoad = load.split(CarLoad.SPLIT_CHAR); 1099 if (splitLoad.length > 1) { 1100 if (splitLoad[0].equals(oldType)) { 1101 deleteLoadName(load); 1102 if (newType != null) { 1103 load = newType + CarLoad.SPLIT_CHAR + splitLoad[1]; 1104 addLoadName(load); 1105 } 1106 } 1107 } 1108 } 1109 } 1110 } 1111 1112 /** 1113 * Get how this train deals with car road names. 1114 * 1115 * @return ALL_ROADS INCLUDE_ROADS EXCLUDE_ROADS 1116 */ 1117 public String getCarRoadOption() { 1118 return _carRoadOption; 1119 } 1120 1121 /** 1122 * Set how this train deals with car road names. 1123 * 1124 * @param option ALL_ROADS INCLUDE_ROADS EXCLUDE_ROADS 1125 */ 1126 public void setCarRoadOption(String option) { 1127 String old = _carRoadOption; 1128 _carRoadOption = option; 1129 setDirtyAndFirePropertyChange(ROADS_CHANGED_PROPERTY, old, option); 1130 } 1131 1132 protected void setCarRoadNames(String[] roads) { 1133 setRoadNames(roads, _carRoadList); 1134 } 1135 1136 /** 1137 * Provides a list of car road names that the train will either service or 1138 * exclude. See setCarRoadOption 1139 * 1140 * @return Array of sorted road names as Strings 1141 */ 1142 public String[] getCarRoadNames() { 1143 String[] roads = _carRoadList.toArray(new String[0]); 1144 if (_carRoadList.size() > 0) { 1145 Arrays.sort(roads); 1146 } 1147 return roads; 1148 } 1149 1150 /** 1151 * Add a car road name that the train will either service or exclude. See 1152 * setCarRoadOption 1153 * 1154 * @param road The string road name. 1155 * @return true if road name was added, false if road name wasn't in the 1156 * list. 1157 */ 1158 public boolean addCarRoadName(String road) { 1159 if (_carRoadList.contains(road)) { 1160 return false; 1161 } 1162 _carRoadList.add(road); 1163 log.debug("train ({}) add car road {}", getName(), road); 1164 setDirtyAndFirePropertyChange(ROADS_CHANGED_PROPERTY, _carRoadList.size() - 1, _carRoadList.size()); 1165 return true; 1166 } 1167 1168 /** 1169 * Delete a car road name that the train will either service or exclude. See 1170 * setRoadOption 1171 * 1172 * @param road The string road name to delete. 1173 * @return true if road name was removed, false if road name wasn't in the 1174 * list. 1175 */ 1176 public boolean deleteCarRoadName(String road) { 1177 if (_carRoadList.remove(road)) { 1178 log.debug("train ({}) delete car road {}", getName(), road); 1179 setDirtyAndFirePropertyChange(ROADS_CHANGED_PROPERTY, _carRoadList.size() + 1, _carRoadList.size()); 1180 return true; 1181 } 1182 return false; 1183 } 1184 1185 /** 1186 * Determine if train will service a specific road name for a car. 1187 * 1188 * @param road the road name to check. 1189 * @return true if train will service this road name. 1190 */ 1191 public boolean isCarRoadNameAccepted(String road) { 1192 if (_carRoadOption.equals(ALL_ROADS)) { 1193 return true; 1194 } 1195 if (_carRoadOption.equals(INCLUDE_ROADS)) { 1196 return _carRoadList.contains(road); 1197 } 1198 // exclude! 1199 return !_carRoadList.contains(road); 1200 } 1201 1202 /** 1203 * Get how this train deals with caboose road names. 1204 * 1205 * @return ALL_ROADS INCLUDE_ROADS EXCLUDE_ROADS 1206 */ 1207 public String getCabooseRoadOption() { 1208 return _cabooseRoadOption; 1209 } 1210 1211 /** 1212 * Set how this train deals with caboose road names. 1213 * 1214 * @param option ALL_ROADS INCLUDE_ROADS EXCLUDE_ROADS 1215 */ 1216 public void setCabooseRoadOption(String option) { 1217 String old = _cabooseRoadOption; 1218 _cabooseRoadOption = option; 1219 setDirtyAndFirePropertyChange(ROADS_CHANGED_PROPERTY, old, option); 1220 } 1221 1222 protected void setCabooseRoadNames(String[] roads) { 1223 setRoadNames(roads, _cabooseRoadList); 1224 } 1225 1226 /** 1227 * Provides a list of caboose road names that the train will either service 1228 * or exclude. See setCabooseRoadOption 1229 * 1230 * @return Array of sorted road names as Strings 1231 */ 1232 public String[] getCabooseRoadNames() { 1233 String[] roads = _cabooseRoadList.toArray(new String[0]); 1234 if (_cabooseRoadList.size() > 0) { 1235 Arrays.sort(roads); 1236 } 1237 return roads; 1238 } 1239 1240 /** 1241 * Add a caboose road name that the train will either service or exclude. 1242 * See setCabooseRoadOption 1243 * 1244 * @param road The string road name. 1245 * @return true if road name was added, false if road name wasn't in the 1246 * list. 1247 */ 1248 public boolean addCabooseRoadName(String road) { 1249 if (_cabooseRoadList.contains(road)) { 1250 return false; 1251 } 1252 _cabooseRoadList.add(road); 1253 log.debug("train ({}) add caboose road {}", getName(), road); 1254 setDirtyAndFirePropertyChange(ROADS_CHANGED_PROPERTY, _cabooseRoadList.size() - 1, _cabooseRoadList.size()); 1255 return true; 1256 } 1257 1258 /** 1259 * Delete a caboose road name that the train will either service or exclude. 1260 * See setRoadOption 1261 * 1262 * @param road The string road name to delete. 1263 * @return true if road name was removed, false if road name wasn't in the 1264 * list. 1265 */ 1266 public boolean deleteCabooseRoadName(String road) { 1267 if (_cabooseRoadList.remove(road)) { 1268 log.debug("train ({}) delete caboose road {}", getName(), road); 1269 setDirtyAndFirePropertyChange(ROADS_CHANGED_PROPERTY, _cabooseRoadList.size() + 1, _cabooseRoadList.size()); 1270 return true; 1271 } 1272 return false; 1273 } 1274 1275 /** 1276 * Determine if train will service a specific road name for a caboose. 1277 * 1278 * @param road the road name to check. 1279 * @return true if train will service this road name. 1280 */ 1281 public boolean isCabooseRoadNameAccepted(String road) { 1282 if (_cabooseRoadOption.equals(ALL_ROADS)) { 1283 return true; 1284 } 1285 if (_cabooseRoadOption.equals(INCLUDE_ROADS)) { 1286 return _cabooseRoadList.contains(road); 1287 } 1288 // exclude! 1289 return !_cabooseRoadList.contains(road); 1290 } 1291 1292 /** 1293 * Get how this train deals with locomotive road names. 1294 * 1295 * @return ALL_ROADS INCLUDE_ROADS EXCLUDE_ROADS 1296 */ 1297 public String getLocoRoadOption() { 1298 return _locoRoadOption; 1299 } 1300 1301 /** 1302 * Set how this train deals with locomotive road names. 1303 * 1304 * @param option ALL_ROADS INCLUDE_ROADS EXCLUDE_ROADS 1305 */ 1306 public void setLocoRoadOption(String option) { 1307 String old = _locoRoadOption; 1308 _locoRoadOption = option; 1309 setDirtyAndFirePropertyChange(ROADS_CHANGED_PROPERTY, old, option); 1310 } 1311 1312 protected void setLocoRoadNames(String[] roads) { 1313 setRoadNames(roads, _locoRoadList); 1314 } 1315 1316 private void setRoadNames(String[] roads, List<String> list) { 1317 if (roads.length > 0) { 1318 Arrays.sort(roads); 1319 for (String road : roads) { 1320 if (!road.isEmpty()) { 1321 list.add(road); 1322 } 1323 } 1324 } 1325 } 1326 1327 /** 1328 * Provides a list of engine road names that the train will either service 1329 * or exclude. See setLocoRoadOption 1330 * 1331 * @return Array of sorted road names as Strings 1332 */ 1333 public String[] getLocoRoadNames() { 1334 String[] roads = _locoRoadList.toArray(new String[0]); 1335 if (_locoRoadList.size() > 0) { 1336 Arrays.sort(roads); 1337 } 1338 return roads; 1339 } 1340 1341 /** 1342 * Add a engine road name that the train will either service or exclude. See 1343 * setLocoRoadOption 1344 * 1345 * @param road The string road name. 1346 * @return true if road name was added, false if road name wasn't in the 1347 * list. 1348 */ 1349 public boolean addLocoRoadName(String road) { 1350 if (road.isBlank() || _locoRoadList.contains(road)) { 1351 return false; 1352 } 1353 _locoRoadList.add(road); 1354 log.debug("train ({}) add engine road {}", getName(), road); 1355 setDirtyAndFirePropertyChange(ROADS_CHANGED_PROPERTY, _locoRoadList.size() - 1, _locoRoadList.size()); 1356 return true; 1357 } 1358 1359 /** 1360 * Delete a engine road name that the train will either service or exclude. 1361 * See setLocoRoadOption 1362 * 1363 * @param road The string road name to delete. 1364 * @return true if road name was removed, false if road name wasn't in the 1365 * list. 1366 */ 1367 public boolean deleteLocoRoadName(String road) { 1368 if (_locoRoadList.remove(road)) { 1369 log.debug("train ({}) delete engine road {}", getName(), road); 1370 setDirtyAndFirePropertyChange(ROADS_CHANGED_PROPERTY, _locoRoadList.size() + 1, _locoRoadList.size()); 1371 return true; 1372 } 1373 return false; 1374 } 1375 1376 /** 1377 * Determine if train will service a specific road name for an engine. 1378 * 1379 * @param road the road name to check. 1380 * @return true if train will service this road name. 1381 */ 1382 public boolean isLocoRoadNameAccepted(String road) { 1383 if (_locoRoadOption.equals(ALL_ROADS)) { 1384 return true; 1385 } 1386 if (_locoRoadOption.equals(INCLUDE_ROADS)) { 1387 return _locoRoadList.contains(road); 1388 } 1389 // exclude! 1390 return !_locoRoadList.contains(road); 1391 } 1392 1393 protected void replaceRoad(String oldRoad, String newRoad) { 1394 if (newRoad != null) { 1395 if (deleteCarRoadName(oldRoad)) { 1396 addCarRoadName(newRoad); 1397 } 1398 if (deleteCabooseRoadName(oldRoad)) { 1399 addCabooseRoadName(newRoad); 1400 } 1401 if (deleteLocoRoadName(oldRoad)) { 1402 addLocoRoadName(newRoad); 1403 } 1404 if (getEngineRoad().equals(oldRoad)) { 1405 setEngineRoad(newRoad); 1406 } 1407 if (getCabooseRoad().equals(oldRoad)) { 1408 setCabooseRoad(newRoad); 1409 } 1410 if (getSecondLegEngineRoad().equals(oldRoad)) { 1411 setSecondLegEngineRoad(newRoad); 1412 } 1413 if (getSecondLegCabooseRoad().equals(oldRoad)) { 1414 setSecondLegCabooseRoad(newRoad); 1415 } 1416 if (getThirdLegEngineRoad().equals(oldRoad)) { 1417 setThirdLegEngineRoad(newRoad); 1418 } 1419 if (getThirdLegCabooseRoad().equals(oldRoad)) { 1420 setThirdLegCabooseRoad(newRoad); 1421 } 1422 } 1423 } 1424 1425 /** 1426 * Gets the car load option for this train. 1427 * 1428 * @return ALL_LOADS INCLUDE_LOADS EXCLUDE_LOADS 1429 */ 1430 public String getLoadOption() { 1431 return _loadOption; 1432 } 1433 1434 /** 1435 * Set how this train deals with car loads 1436 * 1437 * @param option ALL_LOADS INCLUDE_LOADS EXCLUDE_LOADS 1438 */ 1439 public void setLoadOption(String option) { 1440 String old = _loadOption; 1441 _loadOption = option; 1442 setDirtyAndFirePropertyChange(LOADS_CHANGED_PROPERTY, old, option); 1443 } 1444 1445 List<String> _loadList = new ArrayList<>(); 1446 1447 protected void setLoadNames(String[] loads) { 1448 if (loads.length > 0) { 1449 Arrays.sort(loads); 1450 for (String load : loads) { 1451 if (!load.isEmpty()) { 1452 _loadList.add(load); 1453 } 1454 } 1455 } 1456 } 1457 1458 /** 1459 * Provides a list of loads that the train will either service or exclude. 1460 * See setLoadOption 1461 * 1462 * @return Array of load names as Strings 1463 */ 1464 public String[] getLoadNames() { 1465 String[] loads = _loadList.toArray(new String[0]); 1466 if (_loadList.size() > 0) { 1467 Arrays.sort(loads); 1468 } 1469 return loads; 1470 } 1471 1472 /** 1473 * Add a load that the train will either service or exclude. See 1474 * setLoadOption 1475 * 1476 * @param load The string load name. 1477 * @return true if load name was added, false if load name wasn't in the 1478 * list. 1479 */ 1480 public boolean addLoadName(String load) { 1481 if (_loadList.contains(load)) { 1482 return false; 1483 } 1484 _loadList.add(load); 1485 log.debug("train ({}) add car load {}", getName(), load); 1486 setDirtyAndFirePropertyChange(LOADS_CHANGED_PROPERTY, _loadList.size() - 1, _loadList.size()); 1487 return true; 1488 } 1489 1490 /** 1491 * Delete a load name that the train will either service or exclude. See 1492 * setLoadOption 1493 * 1494 * @param load The string load name. 1495 * @return true if load name was removed, false if load name wasn't in the 1496 * list. 1497 */ 1498 public boolean deleteLoadName(String load) { 1499 if (_loadList.remove(load)) { 1500 log.debug("train ({}) delete car load {}", getName(), load); 1501 setDirtyAndFirePropertyChange(LOADS_CHANGED_PROPERTY, _loadList.size() + 1, _loadList.size()); 1502 return true; 1503 } 1504 return false; 1505 } 1506 1507 /** 1508 * Determine if train will service a specific load name. 1509 * 1510 * @param load the load name to check. 1511 * @return true if train will service this load. 1512 */ 1513 public boolean isLoadNameAccepted(String load) { 1514 if (_loadOption.equals(ALL_LOADS)) { 1515 return true; 1516 } 1517 if (_loadOption.equals(INCLUDE_LOADS)) { 1518 return _loadList.contains(load); 1519 } 1520 // exclude! 1521 return !_loadList.contains(load); 1522 } 1523 1524 /** 1525 * Determine if train will service a specific load and car type. 1526 * 1527 * @param load the load name to check. 1528 * @param type the type of car used to carry the load. 1529 * @return true if train will service this load. 1530 */ 1531 public boolean isLoadNameAccepted(String load, String type) { 1532 if (_loadOption.equals(ALL_LOADS)) { 1533 return true; 1534 } 1535 if (_loadOption.equals(INCLUDE_LOADS)) { 1536 return _loadList.contains(load) || _loadList.contains(type + CarLoad.SPLIT_CHAR + load); 1537 } 1538 // exclude! 1539 return !_loadList.contains(load) && !_loadList.contains(type + CarLoad.SPLIT_CHAR + load); 1540 } 1541 1542 public String getOwnerOption() { 1543 return _ownerOption; 1544 } 1545 1546 /** 1547 * Set how this train deals with car owner names 1548 * 1549 * @param option ALL_OWNERS INCLUDE_OWNERS EXCLUDE_OWNERS 1550 */ 1551 public void setOwnerOption(String option) { 1552 String old = _ownerOption; 1553 _ownerOption = option; 1554 setDirtyAndFirePropertyChange(OWNERS_CHANGED_PROPERTY, old, option); 1555 } 1556 1557 List<String> _ownerList = new ArrayList<>(); 1558 1559 protected void setOwnerNames(String[] owners) { 1560 if (owners.length > 0) { 1561 Arrays.sort(owners); 1562 for (String owner : owners) { 1563 if (!owner.isEmpty()) { 1564 _ownerList.add(owner); 1565 } 1566 } 1567 } 1568 } 1569 1570 /** 1571 * Provides a list of owner names that the train will either service or 1572 * exclude. See setOwnerOption 1573 * 1574 * @return Array of owner names as Strings 1575 */ 1576 public String[] getOwnerNames() { 1577 String[] owners = _ownerList.toArray(new String[0]); 1578 if (_ownerList.size() > 0) { 1579 Arrays.sort(owners); 1580 } 1581 return owners; 1582 } 1583 1584 /** 1585 * Add a owner name that the train will either service or exclude. See 1586 * setOwnerOption 1587 * 1588 * @param owner The string representing the owner's name. 1589 * @return true if owner name was added, false if owner name wasn't in the 1590 * list. 1591 */ 1592 public boolean addOwnerName(String owner) { 1593 if (_ownerList.contains(owner)) { 1594 return false; 1595 } 1596 _ownerList.add(owner); 1597 log.debug("train ({}) add car owner {}", getName(), owner); 1598 setDirtyAndFirePropertyChange(OWNERS_CHANGED_PROPERTY, _ownerList.size() - 1, _ownerList.size()); 1599 return true; 1600 } 1601 1602 /** 1603 * Delete a owner name that the train will either service or exclude. See 1604 * setOwnerOption 1605 * 1606 * @param owner The string representing the owner's name. 1607 * @return true if owner name was removed, false if owner name wasn't in the 1608 * list. 1609 */ 1610 public boolean deleteOwnerName(String owner) { 1611 if (_ownerList.remove(owner)) { 1612 log.debug("train ({}) delete car owner {}", getName(), owner); 1613 setDirtyAndFirePropertyChange(OWNERS_CHANGED_PROPERTY, _ownerList.size() + 1, _ownerList.size()); 1614 return true; 1615 } 1616 return false; 1617 } 1618 1619 /** 1620 * Determine if train will service a specific owner name. 1621 * 1622 * @param owner the owner name to check. 1623 * @return true if train will service this owner name. 1624 */ 1625 public boolean isOwnerNameAccepted(String owner) { 1626 if (_ownerOption.equals(ALL_OWNERS)) { 1627 return true; 1628 } 1629 if (_ownerOption.equals(INCLUDE_OWNERS)) { 1630 return _ownerList.contains(owner); 1631 } 1632 // exclude! 1633 return !_ownerList.contains(owner); 1634 } 1635 1636 protected void replaceOwner(String oldName, String newName) { 1637 if (deleteOwnerName(oldName)) { 1638 addOwnerName(newName); 1639 } 1640 } 1641 1642 /** 1643 * Only rolling stock built in or after this year will be used. 1644 * 1645 * @param year A string representing a year. 1646 */ 1647 public void setBuiltStartYear(String year) { 1648 String old = _builtStartYear; 1649 _builtStartYear = year; 1650 if (!old.equals(year)) { 1651 setDirtyAndFirePropertyChange(BUILT_YEAR_CHANGED_PROPERTY, old, year); 1652 } 1653 } 1654 1655 public String getBuiltStartYear() { 1656 return _builtStartYear; 1657 } 1658 1659 /** 1660 * Only rolling stock built in or before this year will be used. 1661 * 1662 * @param year A string representing a year. 1663 */ 1664 public void setBuiltEndYear(String year) { 1665 String old = _builtEndYear; 1666 _builtEndYear = year; 1667 if (!old.equals(year)) { 1668 setDirtyAndFirePropertyChange(BUILT_YEAR_CHANGED_PROPERTY, old, year); 1669 } 1670 } 1671 1672 public String getBuiltEndYear() { 1673 return _builtEndYear; 1674 } 1675 1676 /** 1677 * Determine if train will service rolling stock by built date. 1678 * 1679 * @param date A string representing the built date for a car or engine. 1680 * @return true is built date is in the acceptable range. 1681 */ 1682 public boolean isBuiltDateAccepted(String date) { 1683 if (getBuiltStartYear().equals(NONE) && getBuiltEndYear().equals(NONE)) { 1684 return true; // range dates not defined 1685 } 1686 int startYear = 0; // default start year; 1687 int endYear = 99999; // default end year; 1688 int builtYear = -1900; 1689 if (!getBuiltStartYear().equals(NONE)) { 1690 try { 1691 startYear = Integer.parseInt(getBuiltStartYear()); 1692 } catch (NumberFormatException e) { 1693 log.debug("Train ({}) built start date not initialized, start: {}", getName(), getBuiltStartYear()); 1694 } 1695 } 1696 if (!getBuiltEndYear().equals(NONE)) { 1697 try { 1698 endYear = Integer.parseInt(getBuiltEndYear()); 1699 } catch (NumberFormatException e) { 1700 log.debug("Train ({}) built end date not initialized, end: {}", getName(), getBuiltEndYear()); 1701 } 1702 } 1703 try { 1704 builtYear = Integer.parseInt(RollingStockManager.convertBuildDate(date)); 1705 } catch (NumberFormatException e) { 1706 log.debug("Unable to parse car built date {}", date); 1707 } 1708 if (startYear < builtYear && builtYear < endYear) { 1709 return true; 1710 } 1711 return false; 1712 } 1713 1714 private final boolean debugFlag = false; 1715 1716 /** 1717 * Determines if this train will service this car. Note this code doesn't 1718 * check the location or tracks that needs to be done separately. See 1719 * Router.java. 1720 * 1721 * @param car The car to be tested. 1722 * @return true if this train can service the car. 1723 */ 1724 public boolean isServiceable(Car car) { 1725 return isServiceable(null, car); 1726 } 1727 1728 /** 1729 * Note that this code was written after TrainBuilder. It does pretty much 1730 * the same as TrainBuilder but with much fewer build report messages. 1731 * 1732 * @param buildReport PrintWriter 1733 * @param car the car to be tested 1734 * @return true if this train can service the car. 1735 */ 1736 public boolean isServiceable(PrintWriter buildReport, Car car) { 1737 setServiceStatus(NONE); 1738 // check to see if train can carry car 1739 if (!isTypeNameAccepted(car.getTypeName())) { 1740 addLine(buildReport, Bundle.getMessage("trainCanNotServiceCarType", 1741 getName(), car.toString(), car.getTypeName())); 1742 return false; 1743 } 1744 if (!isLoadNameAccepted(car.getLoadName(), car.getTypeName())) { 1745 addLine(buildReport, Bundle.getMessage("trainCanNotServiceCarLoad", 1746 getName(), car.toString(), car.getTypeName(), car.getLoadName())); 1747 return false; 1748 } 1749 if (!isBuiltDateAccepted(car.getBuilt()) || 1750 !isOwnerNameAccepted(car.getOwnerName()) || 1751 (!car.isCaboose() && !isCarRoadNameAccepted(car.getRoadName())) || 1752 (car.isCaboose() && !isCabooseRoadNameAccepted(car.getRoadName()))) { 1753 addLine(buildReport, Bundle.getMessage("trainCanNotServiceCar", 1754 getName(), car.toString())); 1755 return false; 1756 } 1757 1758 Route route = getRoute(); 1759 if (route == null) { 1760 return false; 1761 } 1762 1763 if (car.getLocation() == null || car.getTrack() == null) { 1764 return false; 1765 } 1766 1767 // determine if the car's location is serviced by this train 1768 if (route.getLastLocationByName(car.getLocationName()) == null) { 1769 addLine(buildReport, Bundle.getMessage("trainNotThisLocation", 1770 getName(), car.getLocationName())); 1771 return false; 1772 } 1773 // determine if the car's destination is serviced by this train 1774 // check to see if destination is staging and is also the last location in the train's route 1775 if (car.getDestination() != null && 1776 (route.getLastLocationByName(car.getDestinationName()) == null || 1777 (car.getDestination().isStaging() && 1778 getTrainTerminatesRouteLocation().getLocation() != car.getDestination()))) { 1779 addLine(buildReport, Bundle.getMessage("trainNotThisLocation", 1780 getName(), car.getDestinationName())); 1781 return false; 1782 } 1783 // now find the car in the train's route 1784 List<RouteLocation> rLocations = route.getLocationsBySequenceList(); 1785 for (RouteLocation rLoc : rLocations) { 1786 if (rLoc.getName().equals(car.getLocationName())) { 1787 if (!rLoc.isPickUpAllowed() || rLoc.getMaxCarMoves() <= 0 || isLocationSkipped(rLoc.getId())) { 1788 addLine(buildReport, Bundle.getMessage("trainCanNotServiceCarFrom", 1789 getName(), car.toString(), car.getLocationName(), car.getTrackName(), rLoc.getId())); 1790 continue; 1791 } 1792 // check train and car's location direction 1793 if ((car.getLocation().getTrainDirections() & rLoc.getTrainDirection()) == 0 && !isLocalSwitcher()) { 1794 addLine(buildReport, 1795 Bundle.getMessage("trainCanNotServiceCarLocation", 1796 getName(), car.toString(), car.getLocationName(), car.getTrackName(), 1797 rLoc.getId(), car.getLocationName(), rLoc.getTrainDirectionString())); 1798 continue; 1799 } 1800 // check train and car's track direction 1801 if ((car.getTrack().getTrainDirections() & rLoc.getTrainDirection()) == 0 && !isLocalSwitcher()) { 1802 addLine(buildReport, 1803 Bundle.getMessage("trainCanNotServiceCarTrack", 1804 getName(), car.toString(), car.getLocationName(), car.getTrackName(), 1805 rLoc.getId(), car.getTrackName(), rLoc.getTrainDirectionString())); 1806 continue; 1807 } 1808 // can train pull this car? 1809 if (!car.getTrack().isPickupTrainAccepted(this)) { 1810 addLine(buildReport, 1811 Bundle.getMessage("trainCanNotServiceCarPickup", 1812 getName(), car.toString(), car.getLocationName(), car.getTrackName(), 1813 rLoc.getId(), car.getTrackName(), getName())); 1814 continue; 1815 } 1816 if (debugFlag) { 1817 log.debug("Car ({}) can be picked up by train ({}) location ({}, {}) destination ({}, {})", 1818 car.toString(), getName(), car.getLocationName(), car.getTrackName(), 1819 car.getDestinationName(), car.getDestinationTrackName()); 1820 } 1821 addLine(buildReport, Bundle.getMessage("trainCanPickUpCar", 1822 getName(), car.toString(), car.getLocationName(), car.getTrackName(), rLoc.getId())); 1823 if (car.getDestination() == null) { 1824 if (debugFlag) { 1825 log.debug("Car ({}) does not have a destination", car.toString()); 1826 } 1827 return true; // done 1828 } 1829 // now check car's destination 1830 return isServiceableDestination(buildReport, car, rLoc, rLocations); 1831 } 1832 } 1833 if (debugFlag) { 1834 log.debug("Train ({}) can't service car ({}) from ({}, {})", getName(), car.toString(), 1835 car.getLocationName(), car.getTrackName()); 1836 } 1837 return false; 1838 } 1839 1840 /** 1841 * Second step in determining if train can service car, check to see if 1842 * car's destination is serviced by this train's route. 1843 * 1844 * @param buildReport add messages if needed to build report 1845 * @param car The test car 1846 * @param rLoc Where in the train's route the car was found 1847 * @param rLocations The ordered routeLocations in this train's route 1848 * @return true if car's destination can be serviced 1849 */ 1850 private boolean isServiceableDestination(PrintWriter buildReport, Car car, RouteLocation rLoc, 1851 List<RouteLocation> rLocations) { 1852 // need the car's length when building train 1853 int length = car.getTotalLength(); 1854 // car can be a kernel so get total length 1855 if (car.getKernel() != null) { 1856 length = car.getKernel().getTotalLength(); 1857 } 1858 // now see if the train's route services the car's destination 1859 for (int k = rLocations.indexOf(rLoc); k < rLocations.size(); k++) { 1860 RouteLocation rldest = rLocations.get(k); 1861 if (rldest.getName().equals(car.getDestinationName()) && 1862 rldest.isDropAllowed() && 1863 rldest.getMaxCarMoves() > 0 && 1864 !isLocationSkipped(rldest.getId()) && 1865 (!Setup.isCheckCarDestinationEnabled() || 1866 car.getTrack().isDestinationAccepted(car.getDestination()))) { 1867 // found the car's destination 1868 // check track and train direction 1869 if ((car.getDestination().getTrainDirections() & rldest.getTrainDirection()) == 0 && 1870 !isLocalSwitcher()) { 1871 addLine(buildReport, Bundle.getMessage("trainCanNotServiceCarDestination", 1872 getName(), car.toString(), car.getDestinationName(), rldest.getId(), 1873 rldest.getTrainDirectionString())); 1874 continue; 1875 } 1876 //check destination track 1877 if (car.getDestinationTrack() != null) { 1878 if (!isServicableTrack(buildReport, car, rldest, car.getDestinationTrack())) { 1879 continue; 1880 } 1881 // car doesn't have a destination track 1882 // car going to staging? 1883 } else if (!isCarToStaging(buildReport, rldest, car)) { 1884 continue; 1885 } else { 1886 if (debugFlag) { 1887 log.debug("Find track for car ({}) at destination ({})", car.toString(), 1888 car.getDestinationName()); 1889 } 1890 // determine if there's a destination track that is willing to accept this car 1891 String status = ""; 1892 List<Track> tracks = rldest.getLocation().getTracksList(); 1893 for (Track track : tracks) { 1894 if (!isServicableTrack(buildReport, car, rldest, track)) { 1895 continue; 1896 } 1897 // will the track accept this car? 1898 status = track.isRollingStockAccepted(car); 1899 if (status.equals(Track.OKAY) || status.startsWith(Track.LENGTH)) { 1900 if (debugFlag) { 1901 log.debug("Found track ({}) for car ({})", track.getName(), car.toString()); 1902 } 1903 break; // found track 1904 } 1905 } 1906 if (!status.equals(Track.OKAY) && !status.startsWith(Track.LENGTH)) { 1907 if (debugFlag) { 1908 log.debug("Destination ({}) can not service car ({}) using train ({}) no track available", 1909 car.getDestinationName(), car.toString(), getName()); // NOI18N 1910 } 1911 addLine(buildReport, Bundle.getMessage("trainCanNotDeliverNoTracks", 1912 getName(), car.toString(), car.getDestinationName(), rldest.getId())); 1913 continue; 1914 } 1915 } 1916 // restriction to only carry cars to terminal? 1917 if (!isOnlyToTerminal(buildReport, car)) { 1918 continue; 1919 } 1920 // don't allow local move when car is in staging 1921 if (!isTurn() && 1922 car.getTrack().isStaging() && 1923 rldest.getLocation() == car.getLocation()) { 1924 log.debug( 1925 "Car ({}) at ({}, {}) not allowed to perform local move in staging ({})", 1926 car.toString(), car.getLocationName(), car.getTrackName(), rldest.getName()); 1927 continue; 1928 } 1929 // allow car to return to staging? 1930 if (isAllowReturnToStagingEnabled() && 1931 car.getTrack().isStaging() && 1932 rldest.getLocation() == car.getLocation()) { 1933 addLine(buildReport, 1934 Bundle.getMessage("trainCanReturnCarToStaging", 1935 getName(), car.toString(), car.getDestinationName(), 1936 car.getDestinationTrackName())); 1937 return true; // done 1938 } 1939 // is this a local move allowed? 1940 if (!isLocalMove(buildReport, car)) { 1941 continue; 1942 } 1943 // Can cars travel from origin to terminal? 1944 if (!isTravelOriginToTerminalOkay(buildReport, rLoc, rldest, car)) { 1945 continue; 1946 } 1947 // check to see if moves are available 1948 if (!isRouteMovesAvailable(buildReport, rldest)) { 1949 continue; 1950 } 1951 if (debugFlag) { 1952 log.debug("Car ({}) can be dropped by train ({}) to ({}, {})", car.toString(), getName(), 1953 car.getDestinationName(), car.getDestinationTrackName()); 1954 } 1955 return true; // done 1956 } 1957 // check to see if train length is okay 1958 if (!isTrainLengthOkay(buildReport, car, rldest, length)) { 1959 return false; 1960 } 1961 } 1962 addLine(buildReport, Bundle.getMessage("trainCanNotDeliverToDestination", 1963 getName(), car.toString(), car.getDestinationName(), car.getDestinationTrackName())); 1964 return false; 1965 } 1966 1967 private boolean isServicableTrack(PrintWriter buildReport, Car car, RouteLocation rldest, Track track) { 1968 // train and track direction 1969 if ((track.getTrainDirections() & rldest.getTrainDirection()) == 0 && !isLocalSwitcher()) { 1970 addLine(buildReport, Bundle.getMessage("buildCanNotDropRsUsingTrain", 1971 car.toString(), rldest.getTrainDirectionString(), track.getName())); 1972 return false; 1973 } 1974 if (!track.isDropTrainAccepted(this)) { 1975 addLine(buildReport, Bundle.getMessage("buildCanNotDropTrain", 1976 car.toString(), getName(), track.getTrackTypeName(), track.getLocation().getName(), 1977 track.getName())); 1978 return false; 1979 } 1980 return true; 1981 } 1982 1983 private boolean isCarToStaging(PrintWriter buildReport, RouteLocation rldest, Car car) { 1984 if (rldest.getLocation().isStaging() && 1985 getStatusCode() == CODE_BUILDING && 1986 getTerminationTrack() != null && 1987 getTerminationTrack().getLocation() == rldest.getLocation()) { 1988 if (debugFlag) { 1989 log.debug("Car ({}) destination is staging, check train ({}) termination track ({})", 1990 car.toString(), getName(), getTerminationTrack().getName()); 1991 } 1992 String status = car.checkDestination(getTerminationTrack().getLocation(), getTerminationTrack()); 1993 if (!status.equals(Track.OKAY)) { 1994 addLine(buildReport, 1995 Bundle.getMessage("trainCanNotDeliverToStaging", 1996 getName(), car.toString(), 1997 getTerminationTrack().getLocation().getName(), 1998 getTerminationTrack().getName(), status)); 1999 setServiceStatus(status); 2000 return false; 2001 } 2002 } 2003 return true; 2004 } 2005 2006 private boolean isOnlyToTerminal(PrintWriter buildReport, Car car) { 2007 // ignore send to terminal if a local move 2008 if (isSendCarsToTerminalEnabled() && 2009 !car.isLocalMove() && 2010 !car.getSplitLocationName() 2011 .equals(TrainCommon.splitString(getTrainDepartsName())) && 2012 !car.getSplitDestinationName() 2013 .equals(TrainCommon.splitString(getTrainTerminatesName()))) { 2014 if (debugFlag) { 2015 log.debug("option send cars to terminal is enabled"); 2016 } 2017 addLine(buildReport, 2018 Bundle.getMessage("trainCanNotCarryCarOption", 2019 getName(), car.toString(), car.getLocationName(), 2020 car.getTrackName(), car.getDestinationName(), 2021 car.getDestinationTrackName())); 2022 return false; 2023 } 2024 return true; 2025 } 2026 2027 private boolean isLocalMove(PrintWriter buildReport, Car car) { 2028 if (!isLocalSwitcher() && 2029 !isAllowLocalMovesEnabled() && 2030 !car.isCaboose() && 2031 !car.hasFred() && 2032 !car.isPassenger() && 2033 car.isLocalMove()) { 2034 if (debugFlag) { 2035 log.debug("Local move not allowed"); 2036 } 2037 addLine(buildReport, Bundle.getMessage("trainCanNotPerformLocalMove", 2038 getName(), car.toString(), car.getLocationName())); 2039 return false; 2040 } 2041 return true; 2042 } 2043 2044 private boolean isTravelOriginToTerminalOkay(PrintWriter buildReport, RouteLocation rLoc, RouteLocation rldest, 2045 Car car) { 2046 if (!isAllowThroughCarsEnabled() && 2047 TrainCommon.splitString(getTrainDepartsName()) 2048 .equals(rLoc.getSplitName()) && 2049 TrainCommon.splitString(getTrainTerminatesName()) 2050 .equals(rldest.getSplitName()) && 2051 !TrainCommon.splitString(getTrainDepartsName()) 2052 .equals(TrainCommon.splitString(getTrainTerminatesName())) && 2053 !isLocalSwitcher() && 2054 !car.isCaboose() && 2055 !car.hasFred() && 2056 !car.isPassenger()) { 2057 if (debugFlag) { 2058 log.debug("Through car ({}) not allowed", car.toString()); 2059 } 2060 addLine(buildReport, Bundle.getMessage("trainDoesNotCarryOriginTerminal", 2061 getName(), car.getLocationName(), car.getDestinationName())); 2062 return false; 2063 } 2064 return true; 2065 } 2066 2067 private boolean isRouteMovesAvailable(PrintWriter buildReport, RouteLocation rldest) { 2068 if (getStatusCode() == CODE_BUILDING && rldest.getMaxCarMoves() - rldest.getCarMoves() <= 0) { 2069 setServiceStatus(Bundle.getMessage("trainNoMoves", 2070 getName(), getRoute().getName(), rldest.getId(), rldest.getName())); 2071 if (debugFlag) { 2072 log.debug("No available moves for destination {}", rldest.getName()); 2073 } 2074 addLine(buildReport, getServiceStatus()); 2075 return false; 2076 } 2077 return true; 2078 } 2079 2080 private boolean isTrainLengthOkay(PrintWriter buildReport, Car car, RouteLocation rldest, int length) { 2081 if (getStatusCode() == CODE_BUILDING && rldest.getTrainLength() + length > rldest.getMaxTrainLength()) { 2082 setServiceStatus(Bundle.getMessage("trainExceedsMaximumLength", 2083 getName(), getRoute().getName(), rldest.getId(), rldest.getMaxTrainLength(), 2084 Setup.getLengthUnit().toLowerCase(), rldest.getName(), car.toString(), 2085 rldest.getTrainLength() + length - rldest.getMaxTrainLength())); 2086 if (debugFlag) { 2087 log.debug("Car ({}) exceeds maximum train length {} when departing ({})", car.toString(), 2088 rldest.getMaxTrainLength(), rldest.getName()); 2089 } 2090 addLine(buildReport, getServiceStatus()); 2091 return false; 2092 } 2093 return true; 2094 } 2095 2096 protected static final String SEVEN = Setup.BUILD_REPORT_VERY_DETAILED; 2097 2098 private void addLine(PrintWriter buildReport, String string) { 2099 if (Setup.getRouterBuildReportLevel().equals(SEVEN)) { 2100 TrainCommon.addLine(buildReport, SEVEN, string); 2101 } 2102 } 2103 2104 protected void setServiceStatus(String status) { 2105 _serviceStatus = status; 2106 } 2107 2108 /** 2109 * Returns the statusCode of the "isServiceable(Car)" routine. There are two 2110 * statusCodes that need special consideration when the train is being 2111 * built, the moves in a train's route and the maximum train length. NOTE: 2112 * The code using getServiceStatus() currently assumes that if there's a 2113 * service status that the issue is either route moves or maximum train 2114 * length. 2115 * 2116 * @return The statusCode. 2117 */ 2118 public String getServiceStatus() { 2119 return _serviceStatus; 2120 } 2121 2122 /** 2123 * @return The number of cars worked by this train 2124 */ 2125 public int getNumberCarsWorked() { 2126 int count = 0; 2127 for (Car rs : InstanceManager.getDefault(CarManager.class).getList(this)) { 2128 if (rs.getRouteLocation() != null) { 2129 count++; 2130 } 2131 } 2132 return count; 2133 } 2134 2135 public void setNumberCarsRequested(int number) { 2136 _statusCarsRequested = number; 2137 } 2138 2139 public int getNumberCarsRequested() { 2140 return _statusCarsRequested; 2141 } 2142 2143 public void setDate(Date date) { 2144 _date = date; 2145 } 2146 2147 public String getSortDate() { 2148 if (_date == null) { 2149 return NONE; 2150 } 2151 SimpleDateFormat format = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); // NOI18N 2152 return format.format(_date); 2153 } 2154 2155 public String getDate() { 2156 if (_date == null) { 2157 return NONE; 2158 } 2159 SimpleDateFormat format = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss"); // NOI18N 2160 return format.format(_date); 2161 } 2162 2163 /** 2164 * Gets the number of cars in the train at the current location in the 2165 * train's route. 2166 * 2167 * @return The number of cars currently in the train 2168 */ 2169 public int getNumberCarsInTrain() { 2170 return getNumberCarsInTrain(getCurrentRouteLocation()); 2171 } 2172 2173 /** 2174 * Gets the number of cars in the train when train departs the route 2175 * location. 2176 * 2177 * @param routeLocation The RouteLocation. 2178 * @return The number of cars in the train departing the route location. 2179 */ 2180 public int getNumberCarsInTrain(RouteLocation routeLocation) { 2181 int number = 0; 2182 Route route = getRoute(); 2183 if (route != null) { 2184 for (RouteLocation rl : route.getLocationsBySequenceList()) { 2185 for (Car rs : InstanceManager.getDefault(CarManager.class).getList(this)) { 2186 if (rs.getRouteLocation() == rl) { 2187 number++; 2188 } 2189 if (rs.getRouteDestination() == rl) { 2190 number--; 2191 } 2192 } 2193 if (rl == routeLocation) { 2194 break; 2195 } 2196 } 2197 } 2198 return number; 2199 } 2200 2201 /** 2202 * Gets the number of empty cars in the train when train departs the route 2203 * location. 2204 * 2205 * @param routeLocation The RouteLocation. 2206 * @return The number of empty cars in the train departing the route 2207 * location. 2208 */ 2209 public int getNumberEmptyCarsInTrain(RouteLocation routeLocation) { 2210 int number = 0; 2211 Route route = getRoute(); 2212 if (route != null) { 2213 for (RouteLocation rl : route.getLocationsBySequenceList()) { 2214 for (Car car : InstanceManager.getDefault(CarManager.class).getList(this)) { 2215 if (!car.getLoadType().equals(CarLoad.LOAD_TYPE_EMPTY)) { 2216 continue; 2217 } 2218 if (car.getRouteLocation() == rl) { 2219 number++; 2220 } 2221 if (car.getRouteDestination() == rl) { 2222 number--; 2223 } 2224 } 2225 if (rl == routeLocation) { 2226 break; 2227 } 2228 } 2229 } 2230 2231 return number; 2232 } 2233 2234 public int getNumberLoadedCarsInTrain(RouteLocation routeLocation) { 2235 return getNumberCarsInTrain(routeLocation) - getNumberEmptyCarsInTrain(routeLocation); 2236 } 2237 2238 /** 2239 * Gets the number of cars pulled from a location 2240 * 2241 * @param routeLocation the location 2242 * @return number of pick ups 2243 */ 2244 public int getNumberCarsPickedUp(RouteLocation routeLocation) { 2245 int number = 0; 2246 for (Car rs : InstanceManager.getDefault(CarManager.class).getList(this)) { 2247 if (rs.getRouteLocation() == routeLocation) { 2248 number++; 2249 } 2250 } 2251 return number; 2252 } 2253 2254 /** 2255 * Gets the number of cars delivered to a location 2256 * 2257 * @param routeLocation the location 2258 * @return number of set outs 2259 */ 2260 public int getNumberCarsSetout(RouteLocation routeLocation) { 2261 int number = 0; 2262 for (Car rs : InstanceManager.getDefault(CarManager.class).getList(this)) { 2263 if (rs.getRouteDestination() == routeLocation) { 2264 number++; 2265 } 2266 } 2267 return number; 2268 } 2269 2270 /** 2271 * Gets the train's length at the current location in the train's route. 2272 * 2273 * @return The train length at the train's current location 2274 */ 2275 public int getTrainLength() { 2276 return getTrainLength(getCurrentRouteLocation()); 2277 } 2278 2279 /** 2280 * Gets the train's length at the route location specified 2281 * 2282 * @param routeLocation The route location 2283 * @return The train length at the route location 2284 */ 2285 public int getTrainLength(RouteLocation routeLocation) { 2286 int length = 0; 2287 Route route = getRoute(); 2288 if (route != null) { 2289 for (RouteLocation rl : route.getLocationsBySequenceList()) { 2290 for (RollingStock rs : InstanceManager.getDefault(EngineManager.class).getList(this)) { 2291 if (rs.getRouteLocation() == rl) { 2292 length += rs.getTotalLength(); 2293 } 2294 if (rs.getRouteDestination() == rl) { 2295 length += -rs.getTotalLength(); 2296 } 2297 } 2298 for (RollingStock rs : InstanceManager.getDefault(CarManager.class).getList(this)) { 2299 if (rs.getRouteLocation() == rl) { 2300 length += rs.getTotalLength(); 2301 } 2302 if (rs.getRouteDestination() == rl) { 2303 length += -rs.getTotalLength(); 2304 } 2305 } 2306 if (rl == routeLocation) { 2307 break; 2308 } 2309 } 2310 } 2311 return length; 2312 } 2313 2314 /** 2315 * Get the train's weight at the current location. 2316 * 2317 * @return Train's weight in tons. 2318 */ 2319 public int getTrainWeight() { 2320 return getTrainWeight(getCurrentRouteLocation()); 2321 } 2322 2323 public int getTrainWeight(RouteLocation routeLocation) { 2324 int weight = 0; 2325 Route route = getRoute(); 2326 if (route != null) { 2327 for (RouteLocation rl : route.getLocationsBySequenceList()) { 2328 for (RollingStock rs : InstanceManager.getDefault(EngineManager.class).getList(this)) { 2329 if (rs.getRouteLocation() == rl) { 2330 weight += rs.getAdjustedWeightTons(); 2331 } 2332 if (rs.getRouteDestination() == rl) { 2333 weight += -rs.getAdjustedWeightTons(); 2334 } 2335 } 2336 for (Car car : InstanceManager.getDefault(CarManager.class).getList(this)) { 2337 if (car.getRouteLocation() == rl) { 2338 weight += car.getAdjustedWeightTons(); // weight depends 2339 // on car load 2340 } 2341 if (car.getRouteDestination() == rl) { 2342 weight += -car.getAdjustedWeightTons(); 2343 } 2344 } 2345 if (rl == routeLocation) { 2346 break; 2347 } 2348 } 2349 } 2350 return weight; 2351 } 2352 2353 /** 2354 * Gets the train's locomotive horsepower at the route location specified 2355 * 2356 * @param routeLocation The route location 2357 * @return The train's locomotive horsepower at the route location 2358 */ 2359 public int getTrainHorsePower(RouteLocation routeLocation) { 2360 int hp = 0; 2361 Route route = getRoute(); 2362 if (route != null) { 2363 for (RouteLocation rl : route.getLocationsBySequenceList()) { 2364 for (Engine eng : InstanceManager.getDefault(EngineManager.class).getList(this)) { 2365 if (eng.getRouteLocation() == rl) { 2366 hp += eng.getHpInteger(); 2367 } 2368 if (eng.getRouteDestination() == rl) { 2369 hp += -eng.getHpInteger(); 2370 } 2371 } 2372 if (rl == routeLocation) { 2373 break; 2374 } 2375 } 2376 } 2377 return hp; 2378 } 2379 2380 /** 2381 * Gets the current caboose road and number if there's one assigned to the 2382 * train. 2383 * 2384 * @return Road and number of caboose. 2385 */ 2386 public String getCabooseRoadAndNumber() { 2387 String cabooseRoadNumber = NONE; 2388 RouteLocation rl = getCurrentRouteLocation(); 2389 List<Car> cars = InstanceManager.getDefault(CarManager.class).getByTrainList(this); 2390 for (Car car : cars) { 2391 if (car.getRouteLocation() == rl && car.isCaboose()) { 2392 cabooseRoadNumber = 2393 car.getRoadName().split(TrainCommon.HYPHEN)[0] + " " + TrainCommon.splitString(car.getNumber()); 2394 } 2395 } 2396 return cabooseRoadNumber; 2397 } 2398 2399 public void setDescription(String description) { 2400 String old = _description; 2401 _description = description; 2402 if (!old.equals(description)) { 2403 setDirtyAndFirePropertyChange(DESCRIPTION_CHANGED_PROPERTY, old, description); 2404 } 2405 } 2406 2407 public String getRawDescription() { 2408 return _description; 2409 } 2410 2411 /** 2412 * Returns a formated string providing the train's description. {0} = lead 2413 * engine number, {1} = train's departure direction {2} = lead engine road 2414 * {3} = DCC address of lead engine. 2415 * 2416 * @return The train's description. 2417 */ 2418 public String getDescription() { 2419 try { 2420 String description = MessageFormat.format(getRawDescription(), new Object[]{getLeadEngineNumber(), 2421 getTrainDepartsDirection(), getLeadEngineRoadName(), getLeadEngineDccAddress()}); 2422 return description; 2423 } catch (IllegalArgumentException e) { 2424 return "ERROR IN FORMATTING: " + getRawDescription(); 2425 } 2426 } 2427 2428 public void setNumberEngines(String number) { 2429 String old = _numberEngines; 2430 _numberEngines = number; 2431 if (!old.equals(number)) { 2432 setDirtyAndFirePropertyChange("trainNmberEngines", old, number); // NOI18N 2433 } 2434 } 2435 2436 /** 2437 * Get the number of engines that this train requires. 2438 * 2439 * @return The number of engines that this train requires. 2440 */ 2441 public String getNumberEngines() { 2442 return _numberEngines; 2443 } 2444 2445 /** 2446 * Get the number of engines needed for the second set. 2447 * 2448 * @return The number of engines needed in route 2449 */ 2450 public String getSecondLegNumberEngines() { 2451 return _leg2Engines; 2452 } 2453 2454 public void setSecondLegNumberEngines(String number) { 2455 String old = _leg2Engines; 2456 _leg2Engines = number; 2457 if (!old.equals(number)) { 2458 setDirtyAndFirePropertyChange("trainNmberEngines", old, number); // NOI18N 2459 } 2460 } 2461 2462 /** 2463 * Get the number of engines needed for the third set. 2464 * 2465 * @return The number of engines needed in route 2466 */ 2467 public String getThirdLegNumberEngines() { 2468 return _leg3Engines; 2469 } 2470 2471 public void setThirdLegNumberEngines(String number) { 2472 String old = _leg3Engines; 2473 _leg3Engines = number; 2474 if (!old.equals(number)) { 2475 setDirtyAndFirePropertyChange("trainNmberEngines", old, number); // NOI18N 2476 } 2477 } 2478 2479 /** 2480 * Set the road name of engines servicing this train. 2481 * 2482 * @param road The road name of engines servicing this train. 2483 */ 2484 public void setEngineRoad(String road) { 2485 String old = _engineRoad; 2486 _engineRoad = road; 2487 if (!old.equals(road)) { 2488 setDirtyAndFirePropertyChange("trainEngineRoad", old, road); // NOI18N 2489 } 2490 } 2491 2492 /** 2493 * Get the road name of engines servicing this train. 2494 * 2495 * @return The road name of engines servicing this train. 2496 */ 2497 public String getEngineRoad() { 2498 return _engineRoad; 2499 } 2500 2501 /** 2502 * Set the road name of engines servicing this train 2nd leg. 2503 * 2504 * @param road The road name of engines servicing this train. 2505 */ 2506 public void setSecondLegEngineRoad(String road) { 2507 String old = _leg2Road; 2508 _leg2Road = road; 2509 if (!old.equals(road)) { 2510 setDirtyAndFirePropertyChange("trainEngineRoad", old, road); // NOI18N 2511 } 2512 } 2513 2514 /** 2515 * Get the road name of engines servicing this train 2nd leg. 2516 * 2517 * @return The road name of engines servicing this train. 2518 */ 2519 public String getSecondLegEngineRoad() { 2520 return _leg2Road; 2521 } 2522 2523 /** 2524 * Set the road name of engines servicing this train 3rd leg. 2525 * 2526 * @param road The road name of engines servicing this train. 2527 */ 2528 public void setThirdLegEngineRoad(String road) { 2529 String old = _leg3Road; 2530 _leg3Road = road; 2531 if (!old.equals(road)) { 2532 setDirtyAndFirePropertyChange("trainEngineRoad", old, road); // NOI18N 2533 } 2534 } 2535 2536 /** 2537 * Get the road name of engines servicing this train 3rd leg. 2538 * 2539 * @return The road name of engines servicing this train. 2540 */ 2541 public String getThirdLegEngineRoad() { 2542 return _leg3Road; 2543 } 2544 2545 /** 2546 * Set the model name of engines servicing this train. 2547 * 2548 * @param model The model name of engines servicing this train. 2549 */ 2550 public void setEngineModel(String model) { 2551 String old = _engineModel; 2552 _engineModel = model; 2553 if (!old.equals(model)) { 2554 setDirtyAndFirePropertyChange("trainEngineModel", old, model); // NOI18N 2555 } 2556 } 2557 2558 public String getEngineModel() { 2559 return _engineModel; 2560 } 2561 2562 /** 2563 * Set the model name of engines servicing this train's 2nd leg. 2564 * 2565 * @param model The model name of engines servicing this train. 2566 */ 2567 public void setSecondLegEngineModel(String model) { 2568 String old = _leg2Model; 2569 _leg2Model = model; 2570 if (!old.equals(model)) { 2571 setDirtyAndFirePropertyChange("trainEngineModel", old, model); // NOI18N 2572 } 2573 } 2574 2575 public String getSecondLegEngineModel() { 2576 return _leg2Model; 2577 } 2578 2579 /** 2580 * Set the model name of engines servicing this train's 3rd leg. 2581 * 2582 * @param model The model name of engines servicing this train. 2583 */ 2584 public void setThirdLegEngineModel(String model) { 2585 String old = _leg3Model; 2586 _leg3Model = model; 2587 if (!old.equals(model)) { 2588 setDirtyAndFirePropertyChange("trainEngineModel", old, model); // NOI18N 2589 } 2590 } 2591 2592 public String getThirdLegEngineModel() { 2593 return _leg3Model; 2594 } 2595 2596 protected void replaceModel(String oldModel, String newModel) { 2597 if (getEngineModel().equals(oldModel)) { 2598 setEngineModel(newModel); 2599 } 2600 if (getSecondLegEngineModel().equals(oldModel)) { 2601 setSecondLegEngineModel(newModel); 2602 } 2603 if (getThirdLegEngineModel().equals(oldModel)) { 2604 setThirdLegEngineModel(newModel); 2605 } 2606 } 2607 2608 /** 2609 * Set the road name of the caboose servicing this train. 2610 * 2611 * @param road The road name of the caboose servicing this train. 2612 */ 2613 public void setCabooseRoad(String road) { 2614 String old = _cabooseRoad; 2615 _cabooseRoad = road; 2616 if (!old.equals(road)) { 2617 setDirtyAndFirePropertyChange("trainCabooseRoad", old, road); // NOI18N 2618 } 2619 } 2620 2621 public String getCabooseRoad() { 2622 return _cabooseRoad; 2623 } 2624 2625 /** 2626 * Set the road name of the second leg caboose servicing this train. 2627 * 2628 * @param road The road name of the caboose servicing this train's 2nd leg. 2629 */ 2630 public void setSecondLegCabooseRoad(String road) { 2631 String old = _leg2CabooseRoad; 2632 _leg2CabooseRoad = road; 2633 if (!old.equals(road)) { 2634 setDirtyAndFirePropertyChange("trainCabooseRoad", old, road); // NOI18N 2635 } 2636 } 2637 2638 public String getSecondLegCabooseRoad() { 2639 return _leg2CabooseRoad; 2640 } 2641 2642 /** 2643 * Set the road name of the third leg caboose servicing this train. 2644 * 2645 * @param road The road name of the caboose servicing this train's 3rd leg. 2646 */ 2647 public void setThirdLegCabooseRoad(String road) { 2648 String old = _leg3CabooseRoad; 2649 _leg3CabooseRoad = road; 2650 if (!old.equals(road)) { 2651 setDirtyAndFirePropertyChange("trainCabooseRoad", old, road); // NOI18N 2652 } 2653 } 2654 2655 public String getThirdLegCabooseRoad() { 2656 return _leg3CabooseRoad; 2657 } 2658 2659 public void setSecondLegStartRouteLocation(RouteLocation rl) { 2660 _leg2Start = rl; 2661 } 2662 2663 public RouteLocation getSecondLegStartRouteLocation() { 2664 return _leg2Start; 2665 } 2666 2667 public String getSecondLegStartLocationName() { 2668 if (getSecondLegStartRouteLocation() == null) { 2669 return NONE; 2670 } 2671 return getSecondLegStartRouteLocation().getName(); 2672 } 2673 2674 public void setThirdLegStartRouteLocation(RouteLocation rl) { 2675 _leg3Start = rl; 2676 } 2677 2678 public RouteLocation getThirdLegStartRouteLocation() { 2679 return _leg3Start; 2680 } 2681 2682 public String getThirdLegStartLocationName() { 2683 if (getThirdLegStartRouteLocation() == null) { 2684 return NONE; 2685 } 2686 return getThirdLegStartRouteLocation().getName(); 2687 } 2688 2689 public void setSecondLegEndRouteLocation(RouteLocation rl) { 2690 _end2Leg = rl; 2691 } 2692 2693 public String getSecondLegEndLocationName() { 2694 if (getSecondLegEndRouteLocation() == null) { 2695 return NONE; 2696 } 2697 return getSecondLegEndRouteLocation().getName(); 2698 } 2699 2700 public RouteLocation getSecondLegEndRouteLocation() { 2701 return _end2Leg; 2702 } 2703 2704 public void setThirdLegEndRouteLocation(RouteLocation rl) { 2705 _leg3End = rl; 2706 } 2707 2708 public RouteLocation getThirdLegEndRouteLocation() { 2709 return _leg3End; 2710 } 2711 2712 public String getThirdLegEndLocationName() { 2713 if (getThirdLegEndRouteLocation() == null) { 2714 return NONE; 2715 } 2716 return getThirdLegEndRouteLocation().getName(); 2717 } 2718 2719 /** 2720 * Optional changes to train while en route. 2721 * 2722 * @param options NO_CABOOSE_OR_FRED, CHANGE_ENGINES, ADD_CABOOSE, 2723 * HELPER_ENGINES, REMOVE_CABOOSE 2724 */ 2725 public void setSecondLegOptions(int options) { 2726 int old = _leg2Options; 2727 _leg2Options = options; 2728 if (old != options) { 2729 setDirtyAndFirePropertyChange("trainLegOptions", old, options); // NOI18N 2730 } 2731 } 2732 2733 public int getSecondLegOptions() { 2734 return _leg2Options; 2735 } 2736 2737 /** 2738 * Optional changes to train while en route. 2739 * 2740 * @param options NO_CABOOSE_OR_FRED, CHANGE_ENGINES, ADD_CABOOSE, 2741 * HELPER_ENGINES, REMOVE_CABOOSE 2742 */ 2743 public void setThirdLegOptions(int options) { 2744 int old = _leg3Options; 2745 _leg3Options = options; 2746 if (old != options) { 2747 setDirtyAndFirePropertyChange("trainLegOptions", old, options); // NOI18N 2748 } 2749 } 2750 2751 public int getThirdLegOptions() { 2752 return _leg3Options; 2753 } 2754 2755 public void setComment(String comment) { 2756 String old = _comment; 2757 _comment = comment; 2758 if (!old.equals(comment)) { 2759 setDirtyAndFirePropertyChange("trainComment", old, comment); // NOI18N 2760 } 2761 } 2762 2763 public String getComment() { 2764 return TrainCommon.getTextColorString(getCommentWithColor()); 2765 } 2766 2767 public String getCommentWithColor() { 2768 return _comment; 2769 } 2770 2771 /** 2772 * Add a script to run before a train is built 2773 * 2774 * @param pathname The script's pathname 2775 */ 2776 public void addBuildScript(String pathname) { 2777 _buildScripts.add(pathname); 2778 setDirtyAndFirePropertyChange("addBuildScript", pathname, null); // NOI18N 2779 } 2780 2781 public void deleteBuildScript(String pathname) { 2782 _buildScripts.remove(pathname); 2783 setDirtyAndFirePropertyChange("deleteBuildScript", null, pathname); // NOI18N 2784 } 2785 2786 /** 2787 * Gets a list of pathnames (scripts) to run before this train is built 2788 * 2789 * @return A list of pathnames to run before this train is built 2790 */ 2791 public List<String> getBuildScripts() { 2792 return _buildScripts; 2793 } 2794 2795 /** 2796 * Add a script to run after a train is built 2797 * 2798 * @param pathname The script's pathname 2799 */ 2800 public void addAfterBuildScript(String pathname) { 2801 _afterBuildScripts.add(pathname); 2802 setDirtyAndFirePropertyChange("addAfterBuildScript", pathname, null); // NOI18N 2803 } 2804 2805 public void deleteAfterBuildScript(String pathname) { 2806 _afterBuildScripts.remove(pathname); 2807 setDirtyAndFirePropertyChange("deleteAfterBuildScript", null, pathname); // NOI18N 2808 } 2809 2810 /** 2811 * Gets a list of pathnames (scripts) to run after this train is built 2812 * 2813 * @return A list of pathnames to run after this train is built 2814 */ 2815 public List<String> getAfterBuildScripts() { 2816 return _afterBuildScripts; 2817 } 2818 2819 /** 2820 * Add a script to run when train is moved 2821 * 2822 * @param pathname The script's pathname 2823 */ 2824 public void addMoveScript(String pathname) { 2825 _moveScripts.add(pathname); 2826 setDirtyAndFirePropertyChange("addMoveScript", pathname, null); // NOI18N 2827 } 2828 2829 public void deleteMoveScript(String pathname) { 2830 _moveScripts.remove(pathname); 2831 setDirtyAndFirePropertyChange("deleteMoveScript", null, pathname); // NOI18N 2832 } 2833 2834 /** 2835 * Gets a list of pathnames (scripts) to run when this train moved 2836 * 2837 * @return A list of pathnames to run when this train moved 2838 */ 2839 public List<String> getMoveScripts() { 2840 return _moveScripts; 2841 } 2842 2843 /** 2844 * Add a script to run when train is terminated 2845 * 2846 * @param pathname The script's pathname 2847 */ 2848 public void addTerminationScript(String pathname) { 2849 _terminationScripts.add(pathname); 2850 setDirtyAndFirePropertyChange("addTerminationScript", pathname, null); // NOI18N 2851 } 2852 2853 public void deleteTerminationScript(String pathname) { 2854 _terminationScripts.remove(pathname); 2855 setDirtyAndFirePropertyChange("deleteTerminationScript", null, pathname); // NOI18N 2856 } 2857 2858 /** 2859 * Gets a list of pathnames (scripts) to run when this train terminates 2860 * 2861 * @return A list of pathnames to run when this train terminates 2862 */ 2863 public List<String> getTerminationScripts() { 2864 return _terminationScripts; 2865 } 2866 2867 /** 2868 * Gets the optional railroad name for this train. 2869 * 2870 * @return Train's railroad name. 2871 */ 2872 public String getRailroadName() { 2873 return _railroadName; 2874 } 2875 2876 /** 2877 * Overrides the default railroad name for this train. 2878 * 2879 * @param name The railroad name for this train. 2880 */ 2881 public void setRailroadName(String name) { 2882 String old = _railroadName; 2883 _railroadName = name; 2884 if (!old.equals(name)) { 2885 setDirtyAndFirePropertyChange("trainRailroadName", old, name); // NOI18N 2886 } 2887 } 2888 2889 public String getManifestLogoPathName() { 2890 return _logoPathName; 2891 } 2892 2893 /** 2894 * Overrides the default logo for this train. 2895 * 2896 * @param pathName file location for the logo. 2897 */ 2898 public void setManifestLogoPathName(String pathName) { 2899 _logoPathName = pathName; 2900 } 2901 2902 public boolean isShowArrivalAndDepartureTimesEnabled() { 2903 return _showTimes; 2904 } 2905 2906 public void setShowArrivalAndDepartureTimes(boolean enable) { 2907 boolean old = _showTimes; 2908 _showTimes = enable; 2909 if (old != enable) { 2910 setDirtyAndFirePropertyChange("showArrivalAndDepartureTimes", old ? "true" : "false", // NOI18N 2911 enable ? "true" : "false"); // NOI18N 2912 } 2913 } 2914 2915 public boolean isSendCarsToTerminalEnabled() { 2916 return _sendToTerminal; 2917 } 2918 2919 public void setSendCarsToTerminalEnabled(boolean enable) { 2920 boolean old = _sendToTerminal; 2921 _sendToTerminal = enable; 2922 if (old != enable) { 2923 setDirtyAndFirePropertyChange("send cars to terminal", old ? "true" : "false", enable ? "true" // NOI18N 2924 : "false"); // NOI18N 2925 } 2926 } 2927 2928 /** 2929 * Allow local moves if car has a custom load or Final Destination 2930 * 2931 * @return true if local move is allowed 2932 */ 2933 public boolean isAllowLocalMovesEnabled() { 2934 return _allowLocalMoves; 2935 } 2936 2937 public void setAllowLocalMovesEnabled(boolean enable) { 2938 boolean old = _allowLocalMoves; 2939 _allowLocalMoves = enable; 2940 if (old != enable) { 2941 setDirtyAndFirePropertyChange("allow local moves", old ? "true" : "false", enable ? "true" // NOI18N 2942 : "false"); // NOI18N 2943 } 2944 } 2945 2946 public boolean isAllowThroughCarsEnabled() { 2947 return _allowThroughCars; 2948 } 2949 2950 public void setAllowThroughCarsEnabled(boolean enable) { 2951 boolean old = _allowThroughCars; 2952 _allowThroughCars = enable; 2953 if (old != enable) { 2954 setDirtyAndFirePropertyChange("allow through cars", old ? "true" : "false", enable ? "true" // NOI18N 2955 : "false"); // NOI18N 2956 } 2957 } 2958 2959 public boolean isBuildTrainNormalEnabled() { 2960 return _buildNormal; 2961 } 2962 2963 public void setBuildTrainNormalEnabled(boolean enable) { 2964 boolean old = _buildNormal; 2965 _buildNormal = enable; 2966 if (old != enable) { 2967 setDirtyAndFirePropertyChange("build train normal", old ? "true" : "false", enable ? "true" // NOI18N 2968 : "false"); // NOI18N 2969 } 2970 } 2971 2972 /** 2973 * When true allow a turn to return cars to staging. A turn is a train that 2974 * departs and terminates at the same location. 2975 * 2976 * @return true if cars can return to staging 2977 */ 2978 public boolean isAllowReturnToStagingEnabled() { 2979 return _allowCarsReturnStaging; 2980 } 2981 2982 public void setAllowReturnToStagingEnabled(boolean enable) { 2983 boolean old = _allowCarsReturnStaging; 2984 _allowCarsReturnStaging = enable; 2985 if (old != enable) { 2986 setDirtyAndFirePropertyChange("allow cars to return to staging", old ? "true" : "false", // NOI18N 2987 enable ? "true" : "false"); // NOI18N 2988 } 2989 } 2990 2991 public boolean isServiceAllCarsWithFinalDestinationsEnabled() { 2992 return _serviceAllCarsWithFinalDestinations; 2993 } 2994 2995 public void setServiceAllCarsWithFinalDestinationsEnabled(boolean enable) { 2996 boolean old = _serviceAllCarsWithFinalDestinations; 2997 _serviceAllCarsWithFinalDestinations = enable; 2998 if (old != enable) { 2999 setDirtyAndFirePropertyChange("TrainServiceAllCarsWithFinalDestinations", old ? "true" : "false", // NOI18N 3000 enable ? "true" : "false"); // NOI18N 3001 } 3002 } 3003 3004 public boolean isBuildConsistEnabled() { 3005 return _buildConsist; 3006 } 3007 3008 public void setBuildConsistEnabled(boolean enable) { 3009 boolean old = _buildConsist; 3010 _buildConsist = enable; 3011 if (old != enable) { 3012 setDirtyAndFirePropertyChange("TrainBuildConsist", old ? "true" : "false", // NOI18N 3013 enable ? "true" : "false"); // NOI18N 3014 } 3015 } 3016 3017 public boolean isSendCarsWithCustomLoadsToStagingEnabled() { 3018 return _sendCarsWithCustomLoadsToStaging; 3019 } 3020 3021 public void setSendCarsWithCustomLoadsToStagingEnabled(boolean enable) { 3022 boolean old = _sendCarsWithCustomLoadsToStaging; 3023 _sendCarsWithCustomLoadsToStaging = enable; 3024 if (old != enable) { 3025 setDirtyAndFirePropertyChange("SendCarsWithCustomLoadsToStaging", old ? "true" : "false", // NOI18N 3026 enable ? "true" : "false"); // NOI18N 3027 } 3028 } 3029 3030 protected void setBuilt(boolean built) { 3031 boolean old = _built; 3032 _built = built; 3033 if (old != built) { 3034 setDirtyAndFirePropertyChange(BUILT_CHANGED_PROPERTY, old, built); // NOI18N 3035 } 3036 } 3037 3038 /** 3039 * Used to determine if this train has been built. 3040 * 3041 * @return true if the train was successfully built. 3042 */ 3043 public boolean isBuilt() { 3044 return _built; 3045 } 3046 3047 /** 3048 * Set true whenever the train's manifest has been modified. For example 3049 * adding or removing a car from a train, or changing the manifest format. 3050 * Once the manifest has been regenerated (modified == false), the old 3051 * status for the train is restored. 3052 * 3053 * @param modified True if train's manifest has been modified. 3054 */ 3055 public void setModified(boolean modified) { 3056 log.debug("Set modified {}", modified); 3057 if (!isBuilt()) { 3058 _modified = false; 3059 return; // there isn't a manifest to modify 3060 } 3061 boolean old = _modified; 3062 _modified = modified; 3063 if (modified) { 3064 setPrinted(false); 3065 } 3066 if (old != modified) { 3067 if (modified) { 3068 // scripts can call setModified() for a train 3069 if (getStatusCode() != CODE_RUN_SCRIPTS) { 3070 setOldStatusCode(getStatusCode()); 3071 } 3072 setStatusCode(CODE_MANIFEST_MODIFIED); 3073 } else { 3074 setStatusCode(getOldStatusCode()); // restore previous train 3075 // status 3076 } 3077 } 3078 setDirtyAndFirePropertyChange(TRAIN_MODIFIED_CHANGED_PROPERTY, null, modified); // NOI18N 3079 } 3080 3081 public boolean isModified() { 3082 return _modified; 3083 } 3084 3085 /** 3086 * Control flag used to decide if this train is to be built. 3087 * 3088 * @param build When true, build this train. 3089 */ 3090 public void setBuildEnabled(boolean build) { 3091 boolean old = _build; 3092 _build = build; 3093 if (old != build) { 3094 setDirtyAndFirePropertyChange(BUILD_CHANGED_PROPERTY, old, build); // NOI18N 3095 } 3096 } 3097 3098 /** 3099 * Used to determine if train is to be built. 3100 * 3101 * @return true if train is to be built. 3102 */ 3103 public boolean isBuildEnabled() { 3104 return _build; 3105 } 3106 3107 /** 3108 * Build this train if the build control flag is true. 3109 * 3110 * @return True only if train is successfully built. 3111 */ 3112 public boolean buildIfSelected() { 3113 if (isBuildEnabled() && !isBuilt()) { 3114 return build(); 3115 } 3116 log.debug("Train ({}) not selected or already built, skipping build", getName()); 3117 return false; 3118 } 3119 3120 /** 3121 * Build this train. Creates a train manifest. 3122 * 3123 * @return True if build successful. 3124 */ 3125 public synchronized boolean build() { 3126 reset(); 3127 // check to see if any other trains are building 3128 int count = 1200; // wait up to 120 seconds 3129 while (InstanceManager.getDefault(TrainManager.class).isAnyTrainBuilding() && count > 0) { 3130 count--; 3131 try { 3132 wait(100); // 100 msec 3133 } catch (InterruptedException e) { 3134 // TODO Auto-generated catch block 3135 log.error("Thread unexpectedly interrupted", e); 3136 } 3137 } 3138 // timed out? 3139 if (count <= 0) { 3140 log.warn("Build timeout for train ({})", getName()); 3141 setBuildFailed(true); 3142 setStatusCode(CODE_BUILD_FAILED); 3143 return false; 3144 } 3145 // run before build scripts 3146 runScripts(getBuildScripts()); 3147 TrainBuilder tb = new TrainBuilder(); 3148 boolean results = tb.build(this); 3149 // run after build scripts 3150 runScripts(getAfterBuildScripts()); 3151 return results; 3152 } 3153 3154 /** 3155 * Run train scripts, waits for completion before returning. 3156 */ 3157 private synchronized void runScripts(List<String> scripts) { 3158 if (scripts.size() > 0) { 3159 // save the current status 3160 setOldStatusCode(getStatusCode()); 3161 setStatusCode(CODE_RUN_SCRIPTS); 3162 // create the python interpreter thread 3163 JmriScriptEngineManager.getDefault().initializeAllEngines(); 3164 // find the number of active threads 3165 ThreadGroup root = Thread.currentThread().getThreadGroup(); 3166 int numberOfThreads = root.activeCount(); 3167 // log.debug("Number of active threads: {}", numberOfThreads); 3168 for (String scriptPathname : scripts) { 3169 try { 3170 JmriScriptEngineManager.getDefault() 3171 .runScript(new File(jmri.util.FileUtil.getExternalFilename(scriptPathname))); 3172 } catch (Exception e) { 3173 log.error("Problem with script: {}", scriptPathname); 3174 } 3175 } 3176 // need to wait for scripts to complete or 4 seconds maximum 3177 int count = 0; 3178 while (root.activeCount() > numberOfThreads) { 3179 log.debug("Number of active threads: {}, at start: {}", root.activeCount(), numberOfThreads); 3180 try { 3181 wait(40); 3182 } catch (InterruptedException e) { 3183 Thread.currentThread().interrupt(); 3184 } 3185 if (count++ > 100) { 3186 break; // 4 seconds maximum 40*100 = 4000 3187 } 3188 } 3189 setStatusCode(getOldStatusCode()); 3190 } 3191 } 3192 3193 public boolean printBuildReport() { 3194 boolean isPreview = (InstanceManager.getDefault(TrainManager.class).isPrintPreviewEnabled() || 3195 Setup.isBuildReportAlwaysPreviewEnabled()); 3196 return printBuildReport(isPreview); 3197 } 3198 3199 public boolean printBuildReport(boolean isPreview) { 3200 File buildFile = InstanceManager.getDefault(TrainManagerXml.class).getTrainBuildReportFile(getName()); 3201 if (!buildFile.exists()) { 3202 log.warn("Build file missing for train {}", getName()); 3203 return false; 3204 } 3205 3206 if (isPreview && Setup.isBuildReportEditorEnabled()) { 3207 TrainPrintUtilities.editReport(buildFile, getName()); 3208 } else { 3209 TrainPrintUtilities.printReport(buildFile, 3210 Bundle.getMessage("buildReport", getDescription()), 3211 isPreview, NONE, true, NONE, NONE, Setup.PORTRAIT, Setup.getBuildReportFontSize(), true); 3212 } 3213 return true; 3214 } 3215 3216 protected void setBuildFailed(boolean status) { 3217 boolean old = _buildFailed; 3218 _buildFailed = status; 3219 if (old != status) { 3220 setDirtyAndFirePropertyChange("buildFailed", old ? "true" : "false", status ? "true" : "false"); // NOI18N 3221 } 3222 } 3223 3224 /** 3225 * Returns true if the train build failed. Note that returning false doesn't 3226 * mean the build was successful. 3227 * 3228 * @return true if train build failed. 3229 */ 3230 public boolean isBuildFailed() { 3231 return _buildFailed; 3232 } 3233 3234 protected void setBuildFailedMessage(String message) { 3235 String old = _buildFailedMessage; 3236 _buildFailedMessage = message; 3237 if (!old.equals(message)) { 3238 setDirtyAndFirePropertyChange("buildFailedMessage", old, message); // NOI18N 3239 } 3240 } 3241 3242 protected String getBuildFailedMessage() { 3243 return _buildFailedMessage; 3244 } 3245 3246 /** 3247 * Print manifest for train if already built. 3248 * 3249 * @return true if print successful. 3250 */ 3251 public boolean printManifestIfBuilt() { 3252 if (isBuilt()) { 3253 boolean isPreview = InstanceManager.getDefault(TrainManager.class).isPrintPreviewEnabled(); 3254 try { 3255 return (printManifest(isPreview)); 3256 } catch (BuildFailedException e) { 3257 log.error("Print Manifest failed: {}", e.getMessage()); 3258 } 3259 } else { 3260 log.debug("Need to build train ({}) before printing manifest", getName()); 3261 } 3262 return false; 3263 } 3264 3265 /** 3266 * Print manifest for train. 3267 * 3268 * @param isPreview True if preview. 3269 * @return true if print successful, false if train print file not found. 3270 * @throws BuildFailedException if unable to create new Manifests 3271 */ 3272 public boolean printManifest(boolean isPreview) throws BuildFailedException { 3273 if (isModified()) { 3274 new TrainManifest(this); 3275 try { 3276 new JsonManifest(this).build(); 3277 } catch (IOException ex) { 3278 log.error("Unable to create JSON manifest {}", ex.getLocalizedMessage()); 3279 } 3280 new TrainCsvManifest(this); 3281 } 3282 File file = InstanceManager.getDefault(TrainManagerXml.class).getTrainManifestFile(getName()); 3283 if (!file.exists()) { 3284 log.warn("Manifest file missing for train ({})", getName()); 3285 return false; 3286 } 3287 if (isPreview && Setup.isManifestEditorEnabled()) { 3288 TrainUtilities.openDesktop(file); 3289 return true; 3290 } 3291 String logoURL = Setup.NONE; 3292 if (!getManifestLogoPathName().equals(NONE)) { 3293 logoURL = FileUtil.getExternalFilename(getManifestLogoPathName()); 3294 } else if (!Setup.getManifestLogoURL().equals(Setup.NONE)) { 3295 logoURL = FileUtil.getExternalFilename(Setup.getManifestLogoURL()); 3296 } 3297 Location departs = InstanceManager.getDefault(LocationManager.class).getLocationByName(getTrainDepartsName()); 3298 String printerName = Location.NONE; 3299 if (departs != null) { 3300 printerName = departs.getDefaultPrinterName(); 3301 } 3302 // the train description shouldn't exceed half of the page width or the 3303 // page number will be overwritten 3304 String name = getDescription(); 3305 if (name.length() > TrainCommon.getManifestHeaderLineLength() / 2) { 3306 name = name.substring(0, TrainCommon.getManifestHeaderLineLength() / 2); 3307 } 3308 TrainPrintUtilities.printReport(file, name, isPreview, Setup.getFontName(), false, logoURL, printerName, 3309 Setup.getManifestOrientation(), Setup.getManifestFontSize(), Setup.isPrintPageHeaderEnabled()); 3310 if (!isPreview) { 3311 setPrinted(true); 3312 } 3313 return true; 3314 } 3315 3316 public boolean openFile() { 3317 File file = createCsvManifestFile(); 3318 if (file == null || !file.exists()) { 3319 log.warn("CSV manifest file missing for train {}", getName()); 3320 return false; 3321 } 3322 TrainUtilities.openDesktop(file); 3323 return true; 3324 } 3325 3326 public boolean runFile() { 3327 File file = createCsvManifestFile(); 3328 if (file == null || !file.exists()) { 3329 log.warn("CSV manifest file missing for train {}", getName()); 3330 return false; 3331 } 3332 // Set up to process the CSV file by the external Manifest program 3333 InstanceManager.getDefault(TrainCustomManifest.class).addCsvFile(file); 3334 if (!InstanceManager.getDefault(TrainCustomManifest.class).process()) { 3335 if (!InstanceManager.getDefault(TrainCustomManifest.class).excelFileExists()) { 3336 JmriJOptionPane.showMessageDialog(null, 3337 Bundle.getMessage("LoadDirectoryNameFileName", 3338 InstanceManager.getDefault(TrainCustomManifest.class).getDirectoryPathName(), 3339 InstanceManager.getDefault(TrainCustomManifest.class).getFileName()), 3340 Bundle.getMessage("ManifestCreatorNotFound"), JmriJOptionPane.ERROR_MESSAGE); 3341 } 3342 return false; 3343 } 3344 return true; 3345 } 3346 3347 public File createCsvManifestFile() { 3348 if (isModified()) { 3349 try { 3350 new TrainManifest(this); 3351 try { 3352 new JsonManifest(this).build(); 3353 } catch (IOException ex) { 3354 log.error("Unable to create JSON manifest {}", ex.getLocalizedMessage()); 3355 } 3356 new TrainCsvManifest(this); 3357 } catch (BuildFailedException e) { 3358 log.error("Could not create CVS Manifest files"); 3359 } 3360 } 3361 File file = InstanceManager.getDefault(TrainManagerXml.class).getTrainCsvManifestFile(getName()); 3362 if (!file.exists()) { 3363 log.warn("CSV manifest file was not created for train ({})", getName()); 3364 return null; 3365 } 3366 return file; 3367 } 3368 3369 public void setPrinted(boolean printed) { 3370 boolean old = _printed; 3371 _printed = printed; 3372 if (old != printed) { 3373 setDirtyAndFirePropertyChange("trainPrinted", old ? "true" : "false", printed ? "true" : "false"); // NOI18N 3374 } 3375 } 3376 3377 /** 3378 * Used to determine if train manifest was printed. 3379 * 3380 * @return true if the train manifest was printed. 3381 */ 3382 public boolean isPrinted() { 3383 return _printed; 3384 } 3385 3386 /** 3387 * Sets the panel position for the train icon for the current route 3388 * location. 3389 * 3390 * @return true if train coordinates can be set 3391 */ 3392 public boolean setTrainIconCoordinates() { 3393 if (Setup.isTrainIconCordEnabled() && getCurrentRouteLocation() != null && _trainIcon != null) { 3394 getCurrentRouteLocation().setTrainIconX(_trainIcon.getX()); 3395 getCurrentRouteLocation().setTrainIconY(_trainIcon.getY()); 3396 return true; 3397 } 3398 return false; 3399 } 3400 3401 /** 3402 * Terminate train. 3403 */ 3404 public void terminate() { 3405 while (isBuilt()) { 3406 move(); 3407 } 3408 } 3409 3410 /** 3411 * Move train to next location in the route. Will move engines, cars, and 3412 * train icon. Will also terminate a train after it arrives at its final 3413 * destination. 3414 */ 3415 public void move() { 3416 log.debug("Move train ({})", getName()); 3417 if (getRoute() == null || getCurrentRouteLocation() == null) { 3418 setBuilt(false); // break terminate loop 3419 return; 3420 } 3421 if (!isBuilt()) { 3422 log.error("ERROR attempt to move train ({}) that hasn't been built", getName()); 3423 return; 3424 } 3425 RouteLocation rl = getCurrentRouteLocation(); 3426 RouteLocation rlNext = getNextRouteLocation(rl); 3427 3428 setCurrentLocation(rlNext); 3429 3430 // cars and engines will move via property change 3431 setDirtyAndFirePropertyChange(TRAIN_LOCATION_CHANGED_PROPERTY, rl, rlNext); 3432 moveTrainIcon(rlNext); 3433 updateStatus(rl, rlNext); 3434 // tell GUI that train has complete its move 3435 setDirtyAndFirePropertyChange(TRAIN_MOVE_COMPLETE_CHANGED_PROPERTY, rl, rlNext); 3436 } 3437 3438 /** 3439 * Move train to a location in the train's route. Code checks to see if the 3440 * location requested is part of the train's route and if the train hasn't 3441 * already visited the location. This command can only move the train 3442 * forward in its route. Note that you can not terminate the train using 3443 * this command. See move() or terminate(). 3444 * 3445 * @param locationName The name of the location to move this train. 3446 * @return true if train was able to move to the named location. 3447 */ 3448 public boolean move(String locationName) { 3449 log.info("Move train ({}) to location ({})", getName(), locationName); 3450 if (getRoute() == null || getCurrentRouteLocation() == null) { 3451 return false; 3452 } 3453 List<RouteLocation> routeList = getRoute().getLocationsBySequenceList(); 3454 for (int i = 0; i < routeList.size(); i++) { 3455 RouteLocation rl = routeList.get(i); 3456 if (getCurrentRouteLocation() == rl) { 3457 for (int j = i + 1; j < routeList.size(); j++) { 3458 rl = routeList.get(j); 3459 if (rl.getName().equals(locationName)) { 3460 log.debug("Found location ({}) moving train to this location", locationName); 3461 for (j = i + 1; j < routeList.size(); j++) { 3462 rl = routeList.get(j); 3463 move(); 3464 if (rl.getName().equals(locationName)) { 3465 return true; 3466 } 3467 } 3468 } 3469 } 3470 break; // done 3471 } 3472 } 3473 return false; 3474 } 3475 3476 /** 3477 * Moves the train to the specified route location 3478 * 3479 * @param rl route location 3480 * @return true if successful 3481 */ 3482 public boolean move(RouteLocation rl) { 3483 if (rl == null) { 3484 return false; 3485 } 3486 log.debug("Move train ({}) to location ({})", getName(), rl.getName()); 3487 if (getRoute() == null || getCurrentRouteLocation() == null) { 3488 return false; 3489 } 3490 boolean foundCurrent = false; 3491 for (RouteLocation xrl : getRoute().getLocationsBySequenceList()) { 3492 if (getCurrentRouteLocation() == xrl) { 3493 foundCurrent = true; 3494 } 3495 if (xrl == rl) { 3496 if (foundCurrent) { 3497 return true; // done 3498 } else { 3499 break; // train passed this location 3500 } 3501 } 3502 if (foundCurrent) { 3503 move(); 3504 } 3505 } 3506 return false; 3507 } 3508 3509 /** 3510 * Move train to the next location in the train's route. The location name 3511 * provided must be equal to the next location name in the train's route. 3512 * 3513 * @param locationName The next location name in the train's route. 3514 * @return true if successful. 3515 */ 3516 public boolean moveToNextLocation(String locationName) { 3517 if (getNextLocationName().equals(locationName)) { 3518 move(); 3519 return true; 3520 } 3521 return false; 3522 } 3523 3524 public void loadTrainIcon() { 3525 if (getCurrentRouteLocation() != null) { 3526 moveTrainIcon(getCurrentRouteLocation()); 3527 } 3528 } 3529 3530 private final boolean animation = true; // when true use animation for icon 3531 // moves 3532 TrainIconAnimation _ta; 3533 3534 /* 3535 * The train icon is moved to route location (rl) for this train 3536 */ 3537 protected void moveTrainIcon(RouteLocation rl) { 3538 // create train icon if at departure, if program has been restarted, or removed 3539 if (rl == getTrainDepartsRouteLocation() || _trainIcon == null || !_trainIcon.isActive()) { 3540 createTrainIcon(rl); 3541 } 3542 // is the lead engine still in train 3543 if (getLeadEngine() != null && getLeadEngine().getRouteDestination() == rl && rl != null) { 3544 log.debug("Engine ({}) arriving at destination {}", getLeadEngine().toString(), rl.getName()); 3545 } 3546 if (_trainIcon != null && _trainIcon.isActive()) { 3547 setTrainIconColor(); 3548 _trainIcon.setShowToolTip(true); 3549 String txt = null; 3550 if (getCurrentLocationName().equals(NONE)) { 3551 txt = getDescription() + " " + Bundle.getMessage("Terminated") + " (" + getTrainTerminatesName() + ")"; 3552 } else { 3553 txt = Bundle.getMessage("TrainAtNext", 3554 getDescription(), getCurrentLocationName(), getNextLocationName(), getTrainLength(), 3555 Setup.getLengthUnit().toLowerCase()); 3556 } 3557 _trainIcon.getToolTip().setText(txt); 3558 _trainIcon.getToolTip().setBackgroundColor(Color.white); 3559 // rl can be null when train is terminated. 3560 if (rl != null) { 3561 if (rl.getTrainIconX() != 0 || rl.getTrainIconY() != 0) { 3562 if (animation) { 3563 TrainIconAnimation ta = new TrainIconAnimation(_trainIcon, rl, _ta); 3564 ta.start(); // start the animation 3565 _ta = ta; 3566 } else { 3567 _trainIcon.setLocation(rl.getTrainIconX(), rl.getTrainIconY()); 3568 } 3569 } 3570 } 3571 } 3572 } 3573 3574 public String getIconName() { 3575 String name = getName(); 3576 if (isBuilt() && getLeadEngine() != null && Setup.isTrainIconAppendEnabled()) { 3577 name += " " + getLeadEngine().getNumber(); 3578 } 3579 return name; 3580 } 3581 3582 public String getLeadEngineNumber() { 3583 if (getLeadEngine() == null) { 3584 return NONE; 3585 } 3586 return getLeadEngine().getNumber(); 3587 } 3588 3589 public String getLeadEngineRoadName() { 3590 if (getLeadEngine() == null) { 3591 return NONE; 3592 } 3593 return getLeadEngine().getRoadName(); 3594 } 3595 3596 public String getLeadEngineRoadAndNumber() { 3597 if (getLeadEngine() == null) { 3598 return NONE; 3599 } 3600 return getLeadEngine().toString(); 3601 } 3602 3603 public String getLeadEngineDccAddress() { 3604 if (getLeadEngine() == null) { 3605 return NONE; 3606 } 3607 return getLeadEngine().getDccAddress(); 3608 } 3609 3610 /** 3611 * Gets the lead engine, will create it if the program has been restarted 3612 * 3613 * @return lead engine for this train 3614 */ 3615 public Engine getLeadEngine() { 3616 if (_leadEngine == null && !_leadEngineId.equals(NONE)) { 3617 _leadEngine = InstanceManager.getDefault(EngineManager.class).getById(_leadEngineId); 3618 } 3619 return _leadEngine; 3620 } 3621 3622 public void setLeadEngine(Engine engine) { 3623 if (engine == null) { 3624 _leadEngineId = NONE; 3625 } 3626 _leadEngine = engine; 3627 } 3628 3629 /** 3630 * Returns the lead engine in a train's route. There can be up to two 3631 * changes in the lead engine for a train. 3632 * 3633 * @param routeLocation where in the train's route to find the lead engine. 3634 * @return lead engine 3635 */ 3636 public Engine getLeadEngine(RouteLocation routeLocation) { 3637 Engine lead = null; 3638 for (RouteLocation rl : getRoute().getLocationsBySequenceList()) { 3639 for (Engine engine : InstanceManager.getDefault(EngineManager.class).getByTrainList(this)) { 3640 if (engine.getRouteLocation() == rl && (engine.getConsist() == null || engine.isLead())) { 3641 lead = engine; 3642 break; 3643 } 3644 } 3645 if (rl == routeLocation) { 3646 break; 3647 } 3648 } 3649 return lead; 3650 } 3651 3652 protected TrainIcon _trainIcon = null; 3653 3654 public TrainIcon getTrainIcon() { 3655 return _trainIcon; 3656 } 3657 3658 public void createTrainIcon(RouteLocation rl) { 3659 if (_trainIcon != null && _trainIcon.isActive()) { 3660 _trainIcon.remove(); 3661 } 3662 // if there's a panel specified, get it and place icon 3663 if (!Setup.getPanelName().isEmpty()) { 3664 Editor editor = InstanceManager.getDefault(EditorManager.class).getTargetFrame(Setup.getPanelName()); 3665 if (editor != null) { 3666 try { 3667 _trainIcon = editor.addTrainIcon(getIconName()); 3668 } catch (Exception e) { 3669 log.error("Error placing train ({}) icon on panel ({})", getName(), Setup.getPanelName(), e); 3670 return; 3671 } 3672 _trainIcon.setTrain(this); 3673 if (getIconName().length() > 9) { 3674 _trainIcon.setFont(_trainIcon.getFont().deriveFont(8.f)); 3675 } 3676 if (rl != null) { 3677 _trainIcon.setLocation(rl.getTrainIconX(), rl.getTrainIconY()); 3678 } 3679 // add throttle if there's a throttle manager 3680 if (jmri.InstanceManager.getNullableDefault(jmri.ThrottleManager.class) != null) { 3681 // add throttle if JMRI loco roster entry exist 3682 RosterEntry entry = null; 3683 if (getLeadEngine() != null) { 3684 // first try and find a match based on loco road number 3685 entry = getLeadEngine().getRosterEntry(); 3686 } 3687 if (entry != null) { 3688 _trainIcon.setRosterEntry(entry); 3689 if (getLeadEngine().getConsist() != null) { 3690 _trainIcon.setConsistNumber(getLeadEngine().getConsist().getConsistNumber()); 3691 } 3692 } else { 3693 log.debug("Loco roster entry not found for train ({})", getName()); 3694 } 3695 } 3696 } 3697 } 3698 } 3699 3700 private void setTrainIconColor() { 3701 // Terminated train? 3702 if (getCurrentLocationName().equals(NONE)) { 3703 _trainIcon.setLocoColor(Setup.getTrainIconColorTerminate()); 3704 return; 3705 } 3706 // local train serving only one location? 3707 if (isLocalSwitcher()) { 3708 _trainIcon.setLocoColor(Setup.getTrainIconColorLocal()); 3709 return; 3710 } 3711 // set color based on train direction at current location 3712 if (getCurrentRouteLocation().getTrainDirection() == RouteLocation.NORTH) { 3713 _trainIcon.setLocoColor(Setup.getTrainIconColorNorth()); 3714 } 3715 if (getCurrentRouteLocation().getTrainDirection() == RouteLocation.SOUTH) { 3716 _trainIcon.setLocoColor(Setup.getTrainIconColorSouth()); 3717 } 3718 if (getCurrentRouteLocation().getTrainDirection() == RouteLocation.EAST) { 3719 _trainIcon.setLocoColor(Setup.getTrainIconColorEast()); 3720 } 3721 if (getCurrentRouteLocation().getTrainDirection() == RouteLocation.WEST) { 3722 _trainIcon.setLocoColor(Setup.getTrainIconColorWest()); 3723 } 3724 } 3725 3726 private void updateStatus(RouteLocation old, RouteLocation next) { 3727 if (next != null) { 3728 setStatusCode(CODE_TRAIN_EN_ROUTE); 3729 // run move scripts 3730 runScripts(getMoveScripts()); 3731 } else { 3732 log.debug("Train ({}) terminated", getName()); 3733 setStatusCode(CODE_TERMINATED); 3734 setBuilt(false); 3735 // run termination scripts 3736 runScripts(getTerminationScripts()); 3737 } 3738 } 3739 3740 /** 3741 * Sets the print status for switch lists 3742 * 3743 * @param status UNKNOWN PRINTED 3744 */ 3745 public void setSwitchListStatus(String status) { 3746 String old = _switchListStatus; 3747 _switchListStatus = status; 3748 if (!old.equals(status)) { 3749 setDirtyAndFirePropertyChange("switch list train status", old, status); // NOI18N 3750 } 3751 } 3752 3753 public String getSwitchListStatus() { 3754 return _switchListStatus; 3755 } 3756 3757 /** 3758 * Resets the train, removes engines and cars from this train. 3759 * 3760 * @return true if reset successful 3761 */ 3762 public boolean reset() { 3763 // is this train in route? 3764 if (isTrainEnRoute()) { 3765 log.info("Train ({}) has started its route, can not be reset", getName()); 3766 return false; 3767 } 3768 setCurrentLocation(null); 3769 setDepartureTrack(null); 3770 setTerminationTrack(null); 3771 setBuilt(false); 3772 setBuildFailed(false); 3773 setBuildFailedMessage(NONE); 3774 setPrinted(false); 3775 setModified(false); 3776 // remove cars and engines from this train via property change 3777 setStatusCode(CODE_TRAIN_RESET); 3778 // remove train icon 3779 if (_trainIcon != null && _trainIcon.isActive()) { 3780 _trainIcon.remove(); 3781 } 3782 return true; 3783 } 3784 3785 public void dispose() { 3786 if (getRoute() != null) { 3787 getRoute().removePropertyChangeListener(this); 3788 } 3789 InstanceManager.getDefault(CarRoads.class).removePropertyChangeListener(this); 3790 InstanceManager.getDefault(CarTypes.class).removePropertyChangeListener(this); 3791 InstanceManager.getDefault(EngineTypes.class).removePropertyChangeListener(this); 3792 InstanceManager.getDefault(CarOwners.class).removePropertyChangeListener(this); 3793 InstanceManager.getDefault(EngineModels.class).removePropertyChangeListener(this); 3794 3795 setDirtyAndFirePropertyChange(DISPOSE_CHANGED_PROPERTY, null, "Dispose"); // NOI18N 3796 } 3797 3798 /** 3799 * Construct this Entry from XML. This member has to remain synchronized 3800 * with the detailed DTD in operations-trains.dtd 3801 * 3802 * @param e Consist XML element 3803 */ 3804 public Train(Element e) { 3805 org.jdom2.Attribute a; 3806 if ((a = e.getAttribute(Xml.ID)) != null) { 3807 _id = a.getValue(); 3808 } else { 3809 log.warn("no id attribute in train element when reading operations"); 3810 } 3811 if ((a = e.getAttribute(Xml.NAME)) != null) { 3812 _name = a.getValue(); 3813 } 3814 if ((a = e.getAttribute(Xml.DESCRIPTION)) != null) { 3815 _description = a.getValue(); 3816 } 3817 if ((a = e.getAttribute(Xml.DEPART_HOUR)) != null) { 3818 String hour = a.getValue(); 3819 if ((a = e.getAttribute(Xml.DEPART_MINUTE)) != null) { 3820 String minute = a.getValue(); 3821 _departureTime = hour + ":" + minute; 3822 } 3823 } 3824 3825 // Trains table row color 3826 Element eRowColor = e.getChild(Xml.ROW_COLOR); 3827 if (eRowColor != null && (a = eRowColor.getAttribute(Xml.NAME)) != null) { 3828 _tableRowColorName = a.getValue().toLowerCase(); 3829 } 3830 if (eRowColor != null && (a = eRowColor.getAttribute(Xml.RESET_ROW_COLOR)) != null) { 3831 _tableRowColorResetName = a.getValue().toLowerCase(); 3832 } 3833 3834 Element eRoute = e.getChild(Xml.ROUTE); 3835 if (eRoute != null) { 3836 if ((a = eRoute.getAttribute(Xml.ID)) != null) { 3837 setRoute(InstanceManager.getDefault(RouteManager.class).getRouteById(a.getValue())); 3838 } 3839 if (eRoute.getChild(Xml.SKIPS) != null) { 3840 List<Element> skips = eRoute.getChild(Xml.SKIPS).getChildren(Xml.LOCATION); 3841 String[] locs = new String[skips.size()]; 3842 for (int i = 0; i < skips.size(); i++) { 3843 Element loc = skips.get(i); 3844 if ((a = loc.getAttribute(Xml.ID)) != null) { 3845 locs[i] = a.getValue(); 3846 } 3847 } 3848 setTrainSkipsLocations(locs); 3849 } 3850 } else { 3851 // old format 3852 // try and first get the route by id then by name 3853 if ((a = e.getAttribute(Xml.ROUTE_ID)) != null) { 3854 setRoute(InstanceManager.getDefault(RouteManager.class).getRouteById(a.getValue())); 3855 } else if ((a = e.getAttribute(Xml.ROUTE)) != null) { 3856 setRoute(InstanceManager.getDefault(RouteManager.class).getRouteByName(a.getValue())); 3857 } 3858 if ((a = e.getAttribute(Xml.SKIP)) != null) { 3859 String locationIds = a.getValue(); 3860 String[] locs = locationIds.split("%%"); // NOI18N 3861 // log.debug("Train skips: {}", locationIds); 3862 setTrainSkipsLocations(locs); 3863 } 3864 } 3865 // new way of reading car types using elements 3866 if (e.getChild(Xml.TYPES) != null) { 3867 List<Element> carTypes = e.getChild(Xml.TYPES).getChildren(Xml.CAR_TYPE); 3868 String[] types = new String[carTypes.size()]; 3869 for (int i = 0; i < carTypes.size(); i++) { 3870 Element type = carTypes.get(i); 3871 if ((a = type.getAttribute(Xml.NAME)) != null) { 3872 types[i] = a.getValue(); 3873 } 3874 } 3875 setTypeNames(types); 3876 List<Element> locoTypes = e.getChild(Xml.TYPES).getChildren(Xml.LOCO_TYPE); 3877 types = new String[locoTypes.size()]; 3878 for (int i = 0; i < locoTypes.size(); i++) { 3879 Element type = locoTypes.get(i); 3880 if ((a = type.getAttribute(Xml.NAME)) != null) { 3881 types[i] = a.getValue(); 3882 } 3883 } 3884 setTypeNames(types); 3885 } // old way of reading car types up to version 2.99.6 3886 else if ((a = e.getAttribute(Xml.CAR_TYPES)) != null) { 3887 String names = a.getValue(); 3888 String[] types = names.split("%%"); // NOI18N 3889 // log.debug("Car types: {}", names); 3890 setTypeNames(types); 3891 } 3892 // old misspelled format 3893 if ((a = e.getAttribute(Xml.CAR_ROAD_OPERATION)) != null) { 3894 _carRoadOption = a.getValue(); 3895 } 3896 if ((a = e.getAttribute(Xml.CAR_ROAD_OPTION)) != null) { 3897 _carRoadOption = a.getValue(); 3898 } 3899 // new way of reading car roads using elements 3900 if (e.getChild(Xml.CAR_ROADS) != null) { 3901 List<Element> carRoads = e.getChild(Xml.CAR_ROADS).getChildren(Xml.CAR_ROAD); 3902 String[] roads = new String[carRoads.size()]; 3903 for (int i = 0; i < carRoads.size(); i++) { 3904 Element road = carRoads.get(i); 3905 if ((a = road.getAttribute(Xml.NAME)) != null) { 3906 roads[i] = a.getValue(); 3907 } 3908 } 3909 setCarRoadNames(roads); 3910 } // old way of reading car roads up to version 2.99.6 3911 else if ((a = e.getAttribute(Xml.CAR_ROADS)) != null) { 3912 String names = a.getValue(); 3913 String[] roads = names.split("%%"); // NOI18N 3914 log.debug("Train ({}) {} car roads: {}", getName(), getCarRoadOption(), names); 3915 setCarRoadNames(roads); 3916 } 3917 3918 if ((a = e.getAttribute(Xml.CABOOSE_ROAD_OPTION)) != null) { 3919 _cabooseRoadOption = a.getValue(); 3920 } 3921 // new way of reading caboose roads using elements 3922 if (e.getChild(Xml.CABOOSE_ROADS) != null) { 3923 List<Element> carRoads = e.getChild(Xml.CABOOSE_ROADS).getChildren(Xml.CAR_ROAD); 3924 String[] roads = new String[carRoads.size()]; 3925 for (int i = 0; i < carRoads.size(); i++) { 3926 Element road = carRoads.get(i); 3927 if ((a = road.getAttribute(Xml.NAME)) != null) { 3928 roads[i] = a.getValue(); 3929 } 3930 } 3931 setCabooseRoadNames(roads); 3932 } 3933 3934 if ((a = e.getAttribute(Xml.LOCO_ROAD_OPTION)) != null) { 3935 _locoRoadOption = a.getValue(); 3936 } 3937 // new way of reading engine roads using elements 3938 if (e.getChild(Xml.LOCO_ROADS) != null) { 3939 List<Element> locoRoads = e.getChild(Xml.LOCO_ROADS).getChildren(Xml.LOCO_ROAD); 3940 String[] roads = new String[locoRoads.size()]; 3941 for (int i = 0; i < locoRoads.size(); i++) { 3942 Element road = locoRoads.get(i); 3943 if ((a = road.getAttribute(Xml.NAME)) != null) { 3944 roads[i] = a.getValue(); 3945 } 3946 } 3947 setLocoRoadNames(roads); 3948 } 3949 3950 if ((a = e.getAttribute(Xml.CAR_LOAD_OPTION)) != null) { 3951 _loadOption = a.getValue(); 3952 } 3953 if ((a = e.getAttribute(Xml.CAR_OWNER_OPTION)) != null) { 3954 _ownerOption = a.getValue(); 3955 } 3956 if ((a = e.getAttribute(Xml.BUILT_START_YEAR)) != null) { 3957 _builtStartYear = a.getValue(); 3958 } 3959 if ((a = e.getAttribute(Xml.BUILT_END_YEAR)) != null) { 3960 _builtEndYear = a.getValue(); 3961 } 3962 // new way of reading car loads using elements 3963 if (e.getChild(Xml.CAR_LOADS) != null) { 3964 List<Element> carLoads = e.getChild(Xml.CAR_LOADS).getChildren(Xml.CAR_LOAD); 3965 String[] loads = new String[carLoads.size()]; 3966 for (int i = 0; i < carLoads.size(); i++) { 3967 Element load = carLoads.get(i); 3968 if ((a = load.getAttribute(Xml.NAME)) != null) { 3969 loads[i] = a.getValue(); 3970 } 3971 } 3972 setLoadNames(loads); 3973 } // old way of reading car loads up to version 2.99.6 3974 else if ((a = e.getAttribute(Xml.CAR_LOADS)) != null) { 3975 String names = a.getValue(); 3976 String[] loads = names.split("%%"); // NOI18N 3977 log.debug("Train ({}) {} car loads: {}", getName(), getLoadOption(), names); 3978 setLoadNames(loads); 3979 } 3980 // new way of reading car owners using elements 3981 if (e.getChild(Xml.CAR_OWNERS) != null) { 3982 List<Element> carOwners = e.getChild(Xml.CAR_OWNERS).getChildren(Xml.CAR_OWNER); 3983 String[] owners = new String[carOwners.size()]; 3984 for (int i = 0; i < carOwners.size(); i++) { 3985 Element owner = carOwners.get(i); 3986 if ((a = owner.getAttribute(Xml.NAME)) != null) { 3987 owners[i] = a.getValue(); 3988 } 3989 } 3990 setOwnerNames(owners); 3991 } // old way of reading car owners up to version 2.99.6 3992 else if ((a = e.getAttribute(Xml.CAR_OWNERS)) != null) { 3993 String names = a.getValue(); 3994 String[] owners = names.split("%%"); // NOI18N 3995 log.debug("Train ({}) {} car owners: {}", getName(), getOwnerOption(), names); 3996 setOwnerNames(owners); 3997 } 3998 3999 if ((a = e.getAttribute(Xml.NUMBER_ENGINES)) != null) { 4000 _numberEngines = a.getValue(); 4001 } 4002 if ((a = e.getAttribute(Xml.LEG2_ENGINES)) != null) { 4003 _leg2Engines = a.getValue(); 4004 } 4005 if ((a = e.getAttribute(Xml.LEG3_ENGINES)) != null) { 4006 _leg3Engines = a.getValue(); 4007 } 4008 if ((a = e.getAttribute(Xml.ENGINE_ROAD)) != null) { 4009 _engineRoad = a.getValue(); 4010 } 4011 if ((a = e.getAttribute(Xml.LEG2_ROAD)) != null) { 4012 _leg2Road = a.getValue(); 4013 } 4014 if ((a = e.getAttribute(Xml.LEG3_ROAD)) != null) { 4015 _leg3Road = a.getValue(); 4016 } 4017 if ((a = e.getAttribute(Xml.ENGINE_MODEL)) != null) { 4018 _engineModel = a.getValue(); 4019 } 4020 if ((a = e.getAttribute(Xml.LEG2_MODEL)) != null) { 4021 _leg2Model = a.getValue(); 4022 } 4023 if ((a = e.getAttribute(Xml.LEG3_MODEL)) != null) { 4024 _leg3Model = a.getValue(); 4025 } 4026 if ((a = e.getAttribute(Xml.REQUIRES)) != null) { 4027 try { 4028 _requires = Integer.parseInt(a.getValue()); 4029 } catch (NumberFormatException ee) { 4030 log.error("Requires ({}) isn't a valid number for train ({})", a.getValue(), getName()); 4031 } 4032 } 4033 if ((a = e.getAttribute(Xml.CABOOSE_ROAD)) != null) { 4034 _cabooseRoad = a.getValue(); 4035 } 4036 if ((a = e.getAttribute(Xml.LEG2_CABOOSE_ROAD)) != null) { 4037 _leg2CabooseRoad = a.getValue(); 4038 } 4039 if ((a = e.getAttribute(Xml.LEG3_CABOOSE_ROAD)) != null) { 4040 _leg3CabooseRoad = a.getValue(); 4041 } 4042 if ((a = e.getAttribute(Xml.LEG2_OPTIONS)) != null) { 4043 try { 4044 _leg2Options = Integer.parseInt(a.getValue()); 4045 } catch (NumberFormatException ee) { 4046 log.error("Leg 2 options ({}) isn't a valid number for train ({})", a.getValue(), getName()); 4047 } 4048 } 4049 if ((a = e.getAttribute(Xml.LEG3_OPTIONS)) != null) { 4050 try { 4051 _leg3Options = Integer.parseInt(a.getValue()); 4052 } catch (NumberFormatException ee) { 4053 log.error("Leg 3 options ({}) isn't a valid number for train ({})", a.getValue(), getName()); 4054 } 4055 } 4056 if ((a = e.getAttribute(Xml.BUILD_NORMAL)) != null) { 4057 _buildNormal = a.getValue().equals(Xml.TRUE); 4058 } 4059 if ((a = e.getAttribute(Xml.TO_TERMINAL)) != null) { 4060 _sendToTerminal = a.getValue().equals(Xml.TRUE); 4061 } 4062 if ((a = e.getAttribute(Xml.ALLOW_LOCAL_MOVES)) != null) { 4063 _allowLocalMoves = a.getValue().equals(Xml.TRUE); 4064 } 4065 if ((a = e.getAttribute(Xml.ALLOW_THROUGH_CARS)) != null) { 4066 _allowThroughCars = a.getValue().equals(Xml.TRUE); 4067 } 4068 if ((a = e.getAttribute(Xml.ALLOW_RETURN)) != null) { 4069 _allowCarsReturnStaging = a.getValue().equals(Xml.TRUE); 4070 } 4071 if ((a = e.getAttribute(Xml.SERVICE_ALL)) != null) { 4072 _serviceAllCarsWithFinalDestinations = a.getValue().equals(Xml.TRUE); 4073 } 4074 if ((a = e.getAttribute(Xml.BUILD_CONSIST)) != null) { 4075 _buildConsist = a.getValue().equals(Xml.TRUE); 4076 } 4077 if ((a = e.getAttribute(Xml.SEND_CUSTOM_STAGING)) != null) { 4078 _sendCarsWithCustomLoadsToStaging = a.getValue().equals(Xml.TRUE); 4079 } 4080 if ((a = e.getAttribute(Xml.BUILT)) != null) { 4081 _built = a.getValue().equals(Xml.TRUE); 4082 } 4083 if ((a = e.getAttribute(Xml.BUILD)) != null) { 4084 _build = a.getValue().equals(Xml.TRUE); 4085 } 4086 if ((a = e.getAttribute(Xml.BUILD_FAILED)) != null) { 4087 _buildFailed = a.getValue().equals(Xml.TRUE); 4088 } 4089 if ((a = e.getAttribute(Xml.BUILD_FAILED_MESSAGE)) != null) { 4090 _buildFailedMessage = a.getValue(); 4091 } 4092 if ((a = e.getAttribute(Xml.PRINTED)) != null) { 4093 _printed = a.getValue().equals(Xml.TRUE); 4094 } 4095 if ((a = e.getAttribute(Xml.MODIFIED)) != null) { 4096 _modified = a.getValue().equals(Xml.TRUE); 4097 } 4098 if ((a = e.getAttribute(Xml.SWITCH_LIST_STATUS)) != null) { 4099 _switchListStatus = a.getValue(); 4100 } 4101 if ((a = e.getAttribute(Xml.LEAD_ENGINE)) != null) { 4102 _leadEngineId = a.getValue(); 4103 } 4104 if ((a = e.getAttribute(Xml.TERMINATION_DATE)) != null) { 4105 _date = TrainCommon.convertStringToDate(a.getValue()); 4106 } 4107 if ((a = e.getAttribute(Xml.REQUESTED_CARS)) != null) { 4108 try { 4109 _statusCarsRequested = Integer.parseInt(a.getValue()); 4110 } catch (NumberFormatException ee) { 4111 log.error("Status cars requested ({}) isn't a valid number for train ({})", a.getValue(), getName()); 4112 } 4113 } 4114 if ((a = e.getAttribute(Xml.STATUS_CODE)) != null) { 4115 try { 4116 _statusCode = Integer.parseInt(a.getValue()); 4117 } catch (NumberFormatException ee) { 4118 log.error("Status code ({}) isn't a valid number for train ({})", a.getValue(), getName()); 4119 } 4120 } else if ((a = e.getAttribute(Xml.STATUS)) != null) { 4121 // attempt to recover status code 4122 String status = a.getValue(); 4123 if (status.startsWith(BUILD_FAILED)) { 4124 _statusCode = CODE_BUILD_FAILED; 4125 } else if (status.startsWith(BUILT)) { 4126 _statusCode = CODE_BUILT; 4127 } else if (status.startsWith(PARTIAL_BUILT)) { 4128 _statusCode = CODE_PARTIAL_BUILT; 4129 } else if (status.startsWith(TERMINATED)) { 4130 _statusCode = CODE_TERMINATED; 4131 } else if (status.startsWith(TRAIN_EN_ROUTE)) { 4132 _statusCode = CODE_TRAIN_EN_ROUTE; 4133 } else if (status.startsWith(TRAIN_RESET)) { 4134 _statusCode = CODE_TRAIN_RESET; 4135 } else { 4136 _statusCode = CODE_UNKNOWN; 4137 } 4138 } 4139 if ((a = e.getAttribute(Xml.OLD_STATUS_CODE)) != null) { 4140 try { 4141 _oldStatusCode = Integer.parseInt(a.getValue()); 4142 } catch (NumberFormatException ee) { 4143 log.error("Old status code ({}) isn't a valid number for train ({})", a.getValue(), getName()); 4144 } 4145 } else { 4146 _oldStatusCode = getStatusCode(); // use current status code if one 4147 // wasn't saved 4148 } 4149 if ((a = e.getAttribute(Xml.COMMENT)) != null) { 4150 _comment = a.getValue(); 4151 } 4152 if (getRoute() != null) { 4153 if ((a = e.getAttribute(Xml.CURRENT)) != null) { 4154 _current = getRoute().getLocationById(a.getValue()); 4155 } 4156 if ((a = e.getAttribute(Xml.LEG2_START)) != null) { 4157 _leg2Start = getRoute().getLocationById(a.getValue()); 4158 } 4159 if ((a = e.getAttribute(Xml.LEG3_START)) != null) { 4160 _leg3Start = getRoute().getLocationById(a.getValue()); 4161 } 4162 if ((a = e.getAttribute(Xml.LEG2_END)) != null) { 4163 _end2Leg = getRoute().getLocationById(a.getValue()); 4164 } 4165 if ((a = e.getAttribute(Xml.LEG3_END)) != null) { 4166 _leg3End = getRoute().getLocationById(a.getValue()); 4167 } 4168 if ((a = e.getAttribute(Xml.DEPARTURE_TRACK)) != null) { 4169 Location location = InstanceManager.getDefault(LocationManager.class) 4170 .getLocationByName(getTrainDepartsName()); 4171 if (location != null) { 4172 _departureTrack = location.getTrackById(a.getValue()); 4173 } else { 4174 log.error("Departure location not found for track {}", a.getValue()); 4175 } 4176 } 4177 if ((a = e.getAttribute(Xml.TERMINATION_TRACK)) != null) { 4178 Location location = InstanceManager.getDefault(LocationManager.class) 4179 .getLocationByName(getTrainTerminatesName()); 4180 if (location != null) { 4181 _terminationTrack = location.getTrackById(a.getValue()); 4182 } else { 4183 log.error("Termiation location not found for track {}", a.getValue()); 4184 } 4185 } 4186 } 4187 4188 // check for scripts 4189 if (e.getChild(Xml.SCRIPTS) != null) { 4190 List<Element> lb = e.getChild(Xml.SCRIPTS).getChildren(Xml.BUILD); 4191 for (Element es : lb) { 4192 if ((a = es.getAttribute(Xml.NAME)) != null) { 4193 addBuildScript(a.getValue()); 4194 } 4195 } 4196 List<Element> lab = e.getChild(Xml.SCRIPTS).getChildren(Xml.AFTER_BUILD); 4197 for (Element es : lab) { 4198 if ((a = es.getAttribute(Xml.NAME)) != null) { 4199 addAfterBuildScript(a.getValue()); 4200 } 4201 } 4202 List<Element> lm = e.getChild(Xml.SCRIPTS).getChildren(Xml.MOVE); 4203 for (Element es : lm) { 4204 if ((a = es.getAttribute(Xml.NAME)) != null) { 4205 addMoveScript(a.getValue()); 4206 } 4207 } 4208 List<Element> lt = e.getChild(Xml.SCRIPTS).getChildren(Xml.TERMINATE); 4209 for (Element es : lt) { 4210 if ((a = es.getAttribute(Xml.NAME)) != null) { 4211 addTerminationScript(a.getValue()); 4212 } 4213 } 4214 } 4215 // check for optional railroad name and logo 4216 if ((e.getChild(Xml.RAIL_ROAD) != null) && (a = e.getChild(Xml.RAIL_ROAD).getAttribute(Xml.NAME)) != null) { 4217 String name = a.getValue(); 4218 setRailroadName(name); 4219 } 4220 if ((e.getChild(Xml.MANIFEST_LOGO) != null)) { 4221 if ((a = e.getChild(Xml.MANIFEST_LOGO).getAttribute(Xml.NAME)) != null) { 4222 setManifestLogoPathName(a.getValue()); 4223 } 4224 } 4225 if ((a = e.getAttribute(Xml.SHOW_TIMES)) != null) { 4226 _showTimes = a.getValue().equals(Xml.TRUE); 4227 } 4228 4229 addPropertyChangeListerners(); 4230 } 4231 4232 private void addPropertyChangeListerners() { 4233 InstanceManager.getDefault(CarRoads.class).addPropertyChangeListener(this); 4234 InstanceManager.getDefault(CarTypes.class).addPropertyChangeListener(this); 4235 InstanceManager.getDefault(EngineTypes.class).addPropertyChangeListener(this); 4236 InstanceManager.getDefault(CarOwners.class).addPropertyChangeListener(this); 4237 InstanceManager.getDefault(EngineModels.class).addPropertyChangeListener(this); 4238 } 4239 4240 /** 4241 * Create an XML element to represent this Entry. This member has to remain 4242 * synchronized with the detailed DTD in operations-trains.dtd. 4243 * 4244 * @return Contents in a JDOM Element 4245 */ 4246 public Element store() { 4247 Element e = new Element(Xml.TRAIN); 4248 e.setAttribute(Xml.ID, getId()); 4249 e.setAttribute(Xml.NAME, getName()); 4250 e.setAttribute(Xml.DESCRIPTION, getRawDescription()); 4251 e.setAttribute(Xml.DEPART_HOUR, getDepartureTimeHour()); 4252 e.setAttribute(Xml.DEPART_MINUTE, getDepartureTimeMinute()); 4253 4254 Element eRowColor = new Element(Xml.ROW_COLOR); 4255 eRowColor.setAttribute(Xml.NAME, getTableRowColorName()); 4256 eRowColor.setAttribute(Xml.RESET_ROW_COLOR, getTableRowColorNameReset()); 4257 e.addContent(eRowColor); 4258 4259 Element eRoute = new Element(Xml.ROUTE); 4260 if (getRoute() != null) { 4261 eRoute.setAttribute(Xml.NAME, getRoute().getName()); 4262 eRoute.setAttribute(Xml.ID, getRoute().getId()); 4263 e.addContent(eRoute); 4264 // build list of locations that this train skips 4265 String[] locationIds = getTrainSkipsLocations(); 4266 if (locationIds.length > 0) { 4267 Element eSkips = new Element(Xml.SKIPS); 4268 for (String id : locationIds) { 4269 Element eLoc = new Element(Xml.LOCATION); 4270 RouteLocation rl = getRoute().getLocationById(id); 4271 if (rl != null) { 4272 eLoc.setAttribute(Xml.NAME, rl.getName()); 4273 eLoc.setAttribute(Xml.ID, id); 4274 eSkips.addContent(eLoc); 4275 } 4276 } 4277 eRoute.addContent(eSkips); 4278 } 4279 } 4280 // build list of locations that this train skips 4281 if (getCurrentRouteLocation() != null) { 4282 e.setAttribute(Xml.CURRENT, getCurrentRouteLocation().getId()); 4283 } 4284 if (getDepartureTrack() != null) { 4285 e.setAttribute(Xml.DEPARTURE_TRACK, getDepartureTrack().getId()); 4286 } 4287 if (getTerminationTrack() != null) { 4288 e.setAttribute(Xml.TERMINATION_TRACK, getTerminationTrack().getId()); 4289 } 4290 e.setAttribute(Xml.BUILT_START_YEAR, getBuiltStartYear()); 4291 e.setAttribute(Xml.BUILT_END_YEAR, getBuiltEndYear()); 4292 e.setAttribute(Xml.NUMBER_ENGINES, getNumberEngines()); 4293 e.setAttribute(Xml.ENGINE_ROAD, getEngineRoad()); 4294 e.setAttribute(Xml.ENGINE_MODEL, getEngineModel()); 4295 e.setAttribute(Xml.REQUIRES, Integer.toString(getRequirements())); 4296 e.setAttribute(Xml.CABOOSE_ROAD, getCabooseRoad()); 4297 e.setAttribute(Xml.BUILD_NORMAL, isBuildTrainNormalEnabled() ? Xml.TRUE : Xml.FALSE); 4298 e.setAttribute(Xml.TO_TERMINAL, isSendCarsToTerminalEnabled() ? Xml.TRUE : Xml.FALSE); 4299 e.setAttribute(Xml.ALLOW_LOCAL_MOVES, isAllowLocalMovesEnabled() ? Xml.TRUE : Xml.FALSE); 4300 e.setAttribute(Xml.ALLOW_RETURN, isAllowReturnToStagingEnabled() ? Xml.TRUE : Xml.FALSE); 4301 e.setAttribute(Xml.ALLOW_THROUGH_CARS, isAllowThroughCarsEnabled() ? Xml.TRUE : Xml.FALSE); 4302 e.setAttribute(Xml.SERVICE_ALL, isServiceAllCarsWithFinalDestinationsEnabled() ? Xml.TRUE : Xml.FALSE); 4303 e.setAttribute(Xml.SEND_CUSTOM_STAGING, isSendCarsWithCustomLoadsToStagingEnabled() ? Xml.TRUE : Xml.FALSE); 4304 e.setAttribute(Xml.BUILD_CONSIST, isBuildConsistEnabled() ? Xml.TRUE : Xml.FALSE); 4305 e.setAttribute(Xml.BUILT, isBuilt() ? Xml.TRUE : Xml.FALSE); 4306 e.setAttribute(Xml.BUILD, isBuildEnabled() ? Xml.TRUE : Xml.FALSE); 4307 e.setAttribute(Xml.BUILD_FAILED, isBuildFailed() ? Xml.TRUE : Xml.FALSE); 4308 e.setAttribute(Xml.BUILD_FAILED_MESSAGE, getBuildFailedMessage()); 4309 e.setAttribute(Xml.PRINTED, isPrinted() ? Xml.TRUE : Xml.FALSE); 4310 e.setAttribute(Xml.MODIFIED, isModified() ? Xml.TRUE : Xml.FALSE); 4311 e.setAttribute(Xml.SWITCH_LIST_STATUS, getSwitchListStatus()); 4312 if (getLeadEngine() != null) { 4313 e.setAttribute(Xml.LEAD_ENGINE, getLeadEngine().getId()); 4314 } 4315 e.setAttribute(Xml.STATUS, getStatus()); 4316 e.setAttribute(Xml.TERMINATION_DATE, getDate()); 4317 e.setAttribute(Xml.REQUESTED_CARS, Integer.toString(getNumberCarsRequested())); 4318 e.setAttribute(Xml.STATUS_CODE, Integer.toString(getStatusCode())); 4319 e.setAttribute(Xml.OLD_STATUS_CODE, Integer.toString(getOldStatusCode())); 4320 e.setAttribute(Xml.COMMENT, getCommentWithColor()); 4321 e.setAttribute(Xml.SHOW_TIMES, isShowArrivalAndDepartureTimesEnabled() ? Xml.TRUE : Xml.FALSE); 4322 // build list of car types for this train 4323 String[] types = getTypeNames(); 4324 // new way of saving car types 4325 Element eTypes = new Element(Xml.TYPES); 4326 for (String type : types) { 4327 // don't save types that have been deleted by user 4328 if (InstanceManager.getDefault(EngineTypes.class).containsName(type)) { 4329 Element eType = new Element(Xml.LOCO_TYPE); 4330 eType.setAttribute(Xml.NAME, type); 4331 eTypes.addContent(eType); 4332 } else if (InstanceManager.getDefault(CarTypes.class).containsName(type)) { 4333 Element eType = new Element(Xml.CAR_TYPE); 4334 eType.setAttribute(Xml.NAME, type); 4335 eTypes.addContent(eType); 4336 } 4337 } 4338 e.addContent(eTypes); 4339 // save list of car roads for this train 4340 if (!getCarRoadOption().equals(ALL_ROADS)) { 4341 e.setAttribute(Xml.CAR_ROAD_OPTION, getCarRoadOption()); 4342 String[] roads = getCarRoadNames(); 4343 // new way of saving road names 4344 Element eRoads = new Element(Xml.CAR_ROADS); 4345 for (String road : roads) { 4346 Element eRoad = new Element(Xml.CAR_ROAD); 4347 eRoad.setAttribute(Xml.NAME, road); 4348 eRoads.addContent(eRoad); 4349 } 4350 e.addContent(eRoads); 4351 } 4352 // save list of caboose roads for this train 4353 if (!getCabooseRoadOption().equals(ALL_ROADS)) { 4354 e.setAttribute(Xml.CABOOSE_ROAD_OPTION, getCabooseRoadOption()); 4355 String[] roads = getCabooseRoadNames(); 4356 // new way of saving road names 4357 Element eRoads = new Element(Xml.CABOOSE_ROADS); 4358 for (String road : roads) { 4359 Element eRoad = new Element(Xml.CAR_ROAD); 4360 eRoad.setAttribute(Xml.NAME, road); 4361 eRoads.addContent(eRoad); 4362 } 4363 e.addContent(eRoads); 4364 } 4365 // save list of engine roads for this train 4366 if (!getLocoRoadOption().equals(ALL_ROADS)) { 4367 e.setAttribute(Xml.LOCO_ROAD_OPTION, getLocoRoadOption()); 4368 String[] roads = getLocoRoadNames(); 4369 Element eRoads = new Element(Xml.LOCO_ROADS); 4370 for (String road : roads) { 4371 Element eRoad = new Element(Xml.LOCO_ROAD); 4372 eRoad.setAttribute(Xml.NAME, road); 4373 eRoads.addContent(eRoad); 4374 } 4375 e.addContent(eRoads); 4376 } 4377 // save list of car loads for this train 4378 if (!getLoadOption().equals(ALL_LOADS)) { 4379 e.setAttribute(Xml.CAR_LOAD_OPTION, getLoadOption()); 4380 String[] loads = getLoadNames(); 4381 // new way of saving car loads 4382 Element eLoads = new Element(Xml.CAR_LOADS); 4383 for (String load : loads) { 4384 Element eLoad = new Element(Xml.CAR_LOAD); 4385 eLoad.setAttribute(Xml.NAME, load); 4386 eLoads.addContent(eLoad); 4387 } 4388 e.addContent(eLoads); 4389 } 4390 // save list of car owners for this train 4391 if (!getOwnerOption().equals(ALL_OWNERS)) { 4392 e.setAttribute(Xml.CAR_OWNER_OPTION, getOwnerOption()); 4393 String[] owners = getOwnerNames(); 4394 // new way of saving car owners 4395 Element eOwners = new Element(Xml.CAR_OWNERS); 4396 for (String owner : owners) { 4397 Element eOwner = new Element(Xml.CAR_OWNER); 4398 eOwner.setAttribute(Xml.NAME, owner); 4399 eOwners.addContent(eOwner); 4400 } 4401 e.addContent(eOwners); 4402 } 4403 // save list of scripts for this train 4404 if (getBuildScripts().size() > 0 || 4405 getAfterBuildScripts().size() > 0 || 4406 getMoveScripts().size() > 0 || 4407 getTerminationScripts().size() > 0) { 4408 Element es = new Element(Xml.SCRIPTS); 4409 if (getBuildScripts().size() > 0) { 4410 for (String scriptPathname : getBuildScripts()) { 4411 Element em = new Element(Xml.BUILD); 4412 em.setAttribute(Xml.NAME, scriptPathname); 4413 es.addContent(em); 4414 } 4415 } 4416 if (getAfterBuildScripts().size() > 0) { 4417 for (String scriptPathname : getAfterBuildScripts()) { 4418 Element em = new Element(Xml.AFTER_BUILD); 4419 em.setAttribute(Xml.NAME, scriptPathname); 4420 es.addContent(em); 4421 } 4422 } 4423 if (getMoveScripts().size() > 0) { 4424 for (String scriptPathname : getMoveScripts()) { 4425 Element em = new Element(Xml.MOVE); 4426 em.setAttribute(Xml.NAME, scriptPathname); 4427 es.addContent(em); 4428 } 4429 } 4430 // save list of termination scripts for this train 4431 if (getTerminationScripts().size() > 0) { 4432 for (String scriptPathname : getTerminationScripts()) { 4433 Element et = new Element(Xml.TERMINATE); 4434 et.setAttribute(Xml.NAME, scriptPathname); 4435 es.addContent(et); 4436 } 4437 } 4438 e.addContent(es); 4439 } 4440 if (!getRailroadName().equals(NONE)) { 4441 Element r = new Element(Xml.RAIL_ROAD); 4442 r.setAttribute(Xml.NAME, getRailroadName()); 4443 e.addContent(r); 4444 } 4445 if (!getManifestLogoPathName().equals(NONE)) { 4446 Element l = new Element(Xml.MANIFEST_LOGO); 4447 l.setAttribute(Xml.NAME, getManifestLogoPathName()); 4448 e.addContent(l); 4449 } 4450 4451 if (getSecondLegOptions() != NO_CABOOSE_OR_FRED) { 4452 e.setAttribute(Xml.LEG2_OPTIONS, Integer.toString(getSecondLegOptions())); 4453 e.setAttribute(Xml.LEG2_ENGINES, getSecondLegNumberEngines()); 4454 e.setAttribute(Xml.LEG2_ROAD, getSecondLegEngineRoad()); 4455 e.setAttribute(Xml.LEG2_MODEL, getSecondLegEngineModel()); 4456 e.setAttribute(Xml.LEG2_CABOOSE_ROAD, getSecondLegCabooseRoad()); 4457 if (getSecondLegStartRouteLocation() != null) { 4458 e.setAttribute(Xml.LEG2_START, getSecondLegStartRouteLocation().getId()); 4459 } 4460 if (getSecondLegEndRouteLocation() != null) { 4461 e.setAttribute(Xml.LEG2_END, getSecondLegEndRouteLocation().getId()); 4462 } 4463 } 4464 if (getThirdLegOptions() != NO_CABOOSE_OR_FRED) { 4465 e.setAttribute(Xml.LEG3_OPTIONS, Integer.toString(getThirdLegOptions())); 4466 e.setAttribute(Xml.LEG3_ENGINES, getThirdLegNumberEngines()); 4467 e.setAttribute(Xml.LEG3_ROAD, getThirdLegEngineRoad()); 4468 e.setAttribute(Xml.LEG3_MODEL, getThirdLegEngineModel()); 4469 e.setAttribute(Xml.LEG3_CABOOSE_ROAD, getThirdLegCabooseRoad()); 4470 if (getThirdLegStartRouteLocation() != null) { 4471 e.setAttribute(Xml.LEG3_START, getThirdLegStartRouteLocation().getId()); 4472 } 4473 if (getThirdLegEndRouteLocation() != null) { 4474 e.setAttribute(Xml.LEG3_END, getThirdLegEndRouteLocation().getId()); 4475 } 4476 } 4477 return e; 4478 } 4479 4480 @Override 4481 public void propertyChange(java.beans.PropertyChangeEvent e) { 4482 if (Control.SHOW_PROPERTY) { 4483 log.debug("Train ({}) sees property change: ({}) old: ({}) new: ({})", getName(), e.getPropertyName(), 4484 e.getOldValue(), e.getNewValue()); 4485 } 4486 if (e.getPropertyName().equals(Route.DISPOSE)) { 4487 setRoute(null); 4488 } 4489 if (e.getPropertyName().equals(CarTypes.CARTYPES_NAME_CHANGED_PROPERTY) || 4490 e.getPropertyName().equals(CarTypes.CARTYPES_CHANGED_PROPERTY) || 4491 e.getPropertyName().equals(EngineTypes.ENGINETYPES_NAME_CHANGED_PROPERTY)) { 4492 replaceType((String) e.getOldValue(), (String) e.getNewValue()); 4493 } 4494 if (e.getPropertyName().equals(CarRoads.CARROADS_NAME_CHANGED_PROPERTY)) { 4495 replaceRoad((String) e.getOldValue(), (String) e.getNewValue()); 4496 } 4497 if (e.getPropertyName().equals(CarOwners.CAROWNERS_NAME_CHANGED_PROPERTY)) { 4498 replaceOwner((String) e.getOldValue(), (String) e.getNewValue()); 4499 } 4500 if (e.getPropertyName().equals(EngineModels.ENGINEMODELS_NAME_CHANGED_PROPERTY)) { 4501 replaceModel((String) e.getOldValue(), (String) e.getNewValue()); 4502 } 4503 // forward route departure time property changes 4504 if (e.getPropertyName().equals(RouteLocation.DEPARTURE_TIME_CHANGED_PROPERTY)) { 4505 setDirtyAndFirePropertyChange(DEPARTURETIME_CHANGED_PROPERTY, e.getOldValue(), e.getNewValue()); 4506 } 4507 // forward any property changes in this train's route 4508 if (e.getSource().getClass().equals(Route.class)) { 4509 setDirtyAndFirePropertyChange(e.getPropertyName(), e.getOldValue(), e.getNewValue()); 4510 } 4511 } 4512 4513 protected void setDirtyAndFirePropertyChange(String p, Object old, Object n) { 4514 InstanceManager.getDefault(TrainManagerXml.class).setDirty(true); 4515 firePropertyChange(p, old, n); 4516 } 4517 4518 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(Train.class); 4519 4520}