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