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 public static final String TRAIN_CURRENT_CHANGED_PROPERTY = "TrainCurrentLocation"; // NOI18N 154 155 // Train status 156 public static final String TRAIN_RESET = Bundle.getMessage("TrainReset"); 157 public static final String RUN_SCRIPTS = Bundle.getMessage("RunScripts"); 158 public static final String BUILDING = Bundle.getMessage("Building"); 159 public static final String BUILD_FAILED = Bundle.getMessage("BuildFailed"); 160 public static final String BUILT = Bundle.getMessage("Built"); 161 public static final String PARTIAL_BUILT = Bundle.getMessage("Partial"); 162 public static final String TRAIN_EN_ROUTE = Bundle.getMessage("TrainEnRoute"); 163 public static final String TERMINATED = Bundle.getMessage("Terminated"); 164 public static final String MANIFEST_MODIFIED = Bundle.getMessage("Modified"); 165 166 // Train status codes 167 public static final int CODE_TRAIN_RESET = 0; 168 public static final int CODE_RUN_SCRIPTS = 0x100; 169 public static final int CODE_BUILDING = 0x01; 170 public static final int CODE_BUILD_FAILED = 0x02; 171 public static final int CODE_BUILT = 0x10; 172 public static final int CODE_PARTIAL_BUILT = CODE_BUILT + 0x04; 173 public static final int CODE_TRAIN_EN_ROUTE = CODE_BUILT + 0x08; 174 public static final int CODE_TERMINATED = 0x80; 175 public static final int CODE_MANIFEST_MODIFIED = 0x200; 176 public static final int CODE_UNKNOWN = 0xFFFF; 177 178 // train requirements 179 public static final int NO_CABOOSE_OR_FRED = 0; // default 180 public static final int CABOOSE = 1; 181 public static final int FRED = 2; 182 183 // road options 184 public static final String ALL_ROADS = Bundle.getMessage("All"); 185 public static final String INCLUDE_ROADS = Bundle.getMessage("Include"); 186 public static final String EXCLUDE_ROADS = Bundle.getMessage("Exclude"); 187 188 // owner options 189 public static final String ALL_OWNERS = Bundle.getMessage("All"); 190 public static final String INCLUDE_OWNERS = Bundle.getMessage("Include"); 191 public static final String EXCLUDE_OWNERS = Bundle.getMessage("Exclude"); 192 193 // load options 194 public static final String ALL_LOADS = Bundle.getMessage("All"); 195 public static final String INCLUDE_LOADS = Bundle.getMessage("Include"); 196 public static final String EXCLUDE_LOADS = Bundle.getMessage("Exclude"); 197 198 // Switch list status 199 public static final String UNKNOWN = ""; 200 public static final String PRINTED = Bundle.getMessage("Printed"); 201 202 public static final String AUTO = Bundle.getMessage("Auto"); 203 public static final String AUTO_HPT = Bundle.getMessage("AutoHPT"); 204 205 public Train(String id, String name) { 206 // log.debug("New train ({}) id: {}", name, id); 207 _name = name; 208 _id = id; 209 // a new train accepts all types 210 setTypeNames(InstanceManager.getDefault(CarTypes.class).getNames()); 211 setTypeNames(InstanceManager.getDefault(EngineTypes.class).getNames()); 212 addPropertyChangeListerners(); 213 } 214 215 @Override 216 public String getId() { 217 return _id; 218 } 219 220 /** 221 * Sets the name of this train, normally a short name that can fit within 222 * the train icon. 223 * 224 * @param name the train's name. 225 */ 226 public void setName(String name) { 227 String old = _name; 228 _name = name; 229 if (!old.equals(name)) { 230 setDirtyAndFirePropertyChange(NAME_CHANGED_PROPERTY, old, name); 231 } 232 } 233 234 // for combo boxes 235 /** 236 * Get's a train's name 237 * 238 * @return train's name 239 */ 240 @Override 241 public String toString() { 242 return _name; 243 } 244 245 /** 246 * Get's a train's name 247 * 248 * @return train's name 249 */ 250 public String getName() { 251 return _name; 252 } 253 254 /** 255 * @return The name of the color when highlighting the train's row 256 */ 257 public String getTableRowColorName() { 258 return _tableRowColorName; 259 } 260 261 public void setTableRowColorName(String colorName) { 262 String old = _tableRowColorName; 263 _tableRowColorName = colorName; 264 if (!old.equals(colorName)) { 265 setDirtyAndFirePropertyChange(TRAIN_ROW_COLOR_CHANGED_PROPERTY, old, colorName); 266 } 267 } 268 269 /** 270 * @return The name of the train row color when the train is reset 271 */ 272 public String getTableRowColorNameReset() { 273 return _tableRowColorResetName; 274 } 275 276 public void setTableRowColorNameReset(String colorName) { 277 String old = _tableRowColorResetName; 278 _tableRowColorResetName = colorName; 279 if (!old.equals(colorName)) { 280 setDirtyAndFirePropertyChange(TRAIN_ROW_COLOR_RESET_CHANGED_PROPERTY, old, colorName); 281 } 282 } 283 284 /** 285 * @return The color when highlighting the train's row 286 */ 287 public Color getTableRowColor() { 288 String colorName = getTableRowColorName(); 289 if (colorName.equals(NONE)) { 290 return null; 291 } else { 292 return Setup.getColor(colorName); 293 } 294 } 295 296 /** 297 * Get's train's departure time 298 * 299 * @return train's departure time in the String format hh:mm 300 */ 301 public String getDepartureTime() { 302 // check to see if the route has a departure time 303 RouteLocation rl = getTrainDepartsRouteLocation(); 304 if (rl != null) { 305 rl.removePropertyChangeListener(this); 306 rl.addPropertyChangeListener(this); 307 if (!rl.getDepartureTime().equals(RouteLocation.NONE)) { 308 return rl.getDepartureTime(); 309 } 310 } 311 return _departureTime; 312 } 313 314 /** 315 * Get's train's departure time in 12hr or 24hr format 316 * 317 * @return train's departure time in the String format hh:mm or hh:mm(AM/PM) 318 */ 319 public String getFormatedDepartureTime() { 320 // check to see if the route has a departure time 321 RouteLocation rl = getTrainDepartsRouteLocation(); 322 if (rl != null && !rl.getDepartureTime().equals(RouteLocation.NONE)) { 323 // need to forward any changes to departure time 324 rl.removePropertyChangeListener(this); 325 rl.addPropertyChangeListener(this); 326 return rl.getFormatedDepartureTime(); 327 } 328 return (parseTime(getDepartTimeMinutes())); 329 } 330 331 /** 332 * Get train's departure time in minutes from midnight for sorting 333 * 334 * @return int hh*60+mm 335 */ 336 public int getDepartTimeMinutes() { 337 int hour = Integer.parseInt(getDepartureTimeHour()); 338 int minute = Integer.parseInt(getDepartureTimeMinute()); 339 return (hour * 60) + minute; 340 } 341 342 public void setDepartureTime(String hour, String minute) { 343 String old = _departureTime; 344 int h = Integer.parseInt(hour); 345 if (h < 10) { 346 hour = "0" + h; 347 } 348 int m = Integer.parseInt(minute); 349 if (m < 10) { 350 minute = "0" + m; 351 } 352 String time = hour + ":" + minute; 353 _departureTime = time; 354 if (!old.equals(time)) { 355 setDirtyAndFirePropertyChange(DEPARTURETIME_CHANGED_PROPERTY, old, _departureTime); 356 setModified(true); 357 } 358 } 359 360 public String getDepartureTimeHour() { 361 String[] time = getDepartureTime().split(":"); 362 return time[0]; 363 } 364 365 public String getDepartureTimeMinute() { 366 String[] time = getDepartureTime().split(":"); 367 return time[1]; 368 } 369 370 public static final String ALREADY_SERVICED = "-1"; // NOI18N 371 372 /** 373 * Gets the expected time when this train will arrive at the location rl. 374 * Expected arrival time is based on the number of car pick up and set outs 375 * for this train. TODO Doesn't provide expected arrival time if train is in 376 * route, instead provides relative time. If train is at or has passed the 377 * location return -1. 378 * 379 * @param routeLocation The RouteLocation. 380 * @return expected arrival time in minutes (append AM or PM if 12 hour 381 * format) 382 */ 383 public String getExpectedArrivalTime(RouteLocation routeLocation) { 384 int minutes = getExpectedTravelTimeInMinutes(routeLocation); 385 if (minutes == -1) { 386 return ALREADY_SERVICED; 387 } 388 log.debug("Expected arrival time for train ({}) at ({}), {} minutes", getName(), routeLocation.getName(), 389 minutes); 390 // TODO use fast clock to get current time vs departure time 391 // for now use relative 392 return parseTime(minutes); 393 } 394 395 public String getExpectedDepartureTime(RouteLocation routeLocation) { 396 int minutes = getExpectedTravelTimeInMinutes(routeLocation); 397 if (minutes == -1) { 398 return ALREADY_SERVICED; 399 } 400 if (!routeLocation.getDepartureTime().equals(RouteLocation.NONE)) { 401 return routeLocation.getFormatedDepartureTime(); 402 } 403 // figure out the work at this location, note that there can be 404 // consecutive locations with the same name 405 if (getRoute() != null) { 406 boolean foundRouteLocation = false; 407 for (RouteLocation rl : getRoute().getLocationsBySequenceList()) { 408 if (rl == routeLocation) { 409 foundRouteLocation = true; 410 } 411 if (foundRouteLocation) { 412 if (rl.getSplitName() 413 .equals(routeLocation.getSplitName())) { 414 minutes = minutes + getWorkTimeAtLocation(rl); 415 } else { 416 break; // done 417 } 418 } 419 } 420 } 421 log.debug("Expected departure time {} for train ({}) at ({})", minutes, getName(), routeLocation.getName()); 422 return parseTime(minutes); 423 } 424 425 public int getWorkTimeAtLocation(RouteLocation routeLocation) { 426 int minutes = 0; 427 // departure? 428 if (routeLocation == getTrainDepartsRouteLocation()) { 429 return minutes; 430 } 431 // add any work at this location 432 for (Car rs : InstanceManager.getDefault(CarManager.class).getList(this)) { 433 if (rs.getRouteLocation() == routeLocation && !rs.getTrackName().equals(RollingStock.NONE)) { 434 minutes += Setup.getSwitchTime(); 435 } 436 if (rs.getRouteDestination() == routeLocation) { 437 minutes += Setup.getSwitchTime(); 438 } 439 } 440 return minutes; 441 } 442 443 /** 444 * Used to determine when a train will arrive at a train's route location. 445 * Once a train departs, provides an estimated time in route and ignores the 446 * departure times from each route location. 447 * 448 * @param routeLocation where in the train's route to get time 449 * @return Time in minutes 450 */ 451 public int getExpectedTravelTimeInMinutes(RouteLocation routeLocation) { 452 int minutes = 0; 453 if (!isTrainEnRoute()) { 454 minutes += Integer.parseInt(getDepartureTimeMinute()); 455 minutes += 60 * Integer.parseInt(getDepartureTimeHour()); 456 } else { 457 minutes = -1; // -1 means train has already served the location 458 } 459 // boolean trainAt = false; 460 boolean trainLocFound = false; 461 if (getRoute() != null) { 462 List<RouteLocation> routeList = getRoute().getLocationsBySequenceList(); 463 for (int i = 0; i < routeList.size(); i++) { 464 RouteLocation rl = routeList.get(i); 465 if (rl == routeLocation) { 466 break; // done 467 } 468 // start recording time after finding where the train is 469 if (!trainLocFound && isTrainEnRoute()) { 470 if (rl == getCurrentRouteLocation()) { 471 trainLocFound = true; 472 // add travel time 473 minutes = Setup.getTravelTime(); 474 } 475 continue; 476 } 477 // is there a departure time from this location? 478 if (!rl.getDepartureTime().equals(RouteLocation.NONE) && !isTrainEnRoute()) { 479 String dt = rl.getDepartureTime(); 480 log.debug("Location {} departure time {}", rl.getName(), dt); 481 String[] time = dt.split(":"); 482 minutes = 60 * Integer.parseInt(time[0]) + Integer.parseInt(time[1]); 483 // log.debug("New minutes: "+minutes); 484 } 485 // add wait time 486 minutes += rl.getWait(); 487 // add travel time if new location 488 RouteLocation next = routeList.get(i + 1); 489 if (next != null && 490 !rl.getSplitName().equals(next.getSplitName())) { 491 minutes += Setup.getTravelTime(); 492 } 493 // don't count work if there's a departure time 494 if (i == 0 || !rl.getDepartureTime().equals(RouteLocation.NONE) && !isTrainEnRoute()) { 495 continue; 496 } 497 // now add the work at the location 498 minutes = minutes + getWorkTimeAtLocation(rl); 499 } 500 } 501 return minutes; 502 } 503 504 /** 505 * Returns time in hour:minute format 506 * 507 * @param minutes number of minutes from midnight 508 * @return hour:minute (optionally AM:PM format) 509 */ 510 private String parseTime(int minutes) { 511 int hours = 0; 512 int days = 0; 513 514 if (minutes >= 60) { 515 int h = minutes / 60; 516 minutes = minutes - h * 60; 517 hours += h; 518 } 519 520 String d = ""; 521 if (hours >= 24) { 522 int nd = hours / 24; 523 hours = hours - nd * 24; 524 days += nd; 525 d = Integer.toString(days) + ":"; 526 } 527 528 // AM_PM field 529 String am_pm = ""; 530 if (Setup.is12hrFormatEnabled()) { 531 am_pm = " " + Bundle.getMessage("AM"); 532 if (hours >= 12) { 533 hours = hours - 12; 534 am_pm = " " + Bundle.getMessage("PM"); 535 } 536 if (hours == 0) { 537 hours = 12; 538 } 539 } 540 541 String h = Integer.toString(hours); 542 if (hours < 10) { 543 h = "0" + h; 544 } 545 if (minutes < 10) { 546 return d + h + ":0" + minutes + am_pm; // NOI18N 547 } 548 return d + h + ":" + minutes + am_pm; 549 } 550 551 /** 552 * Set train requirements. If NO_CABOOSE_OR_FRED, then train doesn't require 553 * a caboose or car with FRED. 554 * 555 * @param requires NO_CABOOSE_OR_FRED, CABOOSE, FRED 556 */ 557 public void setRequirements(int requires) { 558 int old = _requires; 559 _requires = requires; 560 if (old != requires) { 561 setDirtyAndFirePropertyChange(TRAIN_REQUIREMENTS_CHANGED_PROPERTY, Integer.toString(old), 562 Integer.toString(requires)); 563 } 564 } 565 566 /** 567 * Get a train's requirements with regards to the last car in the train. 568 * 569 * @return NONE CABOOSE FRED 570 */ 571 public int getRequirements() { 572 return _requires; 573 } 574 575 public boolean isCabooseNeeded() { 576 return (getRequirements() & CABOOSE) == CABOOSE; 577 } 578 579 public boolean isFredNeeded() { 580 return (getRequirements() & FRED) == FRED; 581 } 582 583 public void setRoute(Route route) { 584 Route old = _route; 585 String oldRoute = NONE; 586 String newRoute = NONE; 587 if (old != null) { 588 old.removePropertyChangeListener(this); 589 oldRoute = old.toString(); 590 } 591 if (route != null) { 592 route.addPropertyChangeListener(this); 593 newRoute = route.toString(); 594 } 595 _route = route; 596 _skipLocationsList.clear(); 597 if (old == null || !old.equals(route)) { 598 setDirtyAndFirePropertyChange(TRAIN_ROUTE_CHANGED_PROPERTY, oldRoute, newRoute); 599 } 600 } 601 602 /** 603 * Gets the train's route 604 * 605 * @return train's route 606 */ 607 public Route getRoute() { 608 return _route; 609 } 610 611 /** 612 * Get's the train's route name. 613 * 614 * @return Train's route name. 615 */ 616 public String getTrainRouteName() { 617 if (getRoute() == null) { 618 return NONE; 619 } 620 return getRoute().getName(); 621 } 622 623 /** 624 * Get the train's departure location's name 625 * 626 * @return train's departure location's name 627 */ 628 public String getTrainDepartsName() { 629 if (getTrainDepartsRouteLocation() != null) { 630 return getTrainDepartsRouteLocation().getName(); 631 } 632 return NONE; 633 } 634 635 public RouteLocation getTrainDepartsRouteLocation() { 636 if (getRoute() == null) { 637 return null; 638 } 639 return getRoute().getDepartsRouteLocation(); 640 } 641 642 public String getTrainDepartsDirection() { 643 String direction = NONE; 644 if (getTrainDepartsRouteLocation() != null) { 645 direction = getTrainDepartsRouteLocation().getTrainDirectionString(); 646 } 647 return direction; 648 } 649 650 /** 651 * Get train's final location's name 652 * 653 * @return train's final location's name 654 */ 655 public String getTrainTerminatesName() { 656 if (getTrainTerminatesRouteLocation() != null) { 657 return getTrainTerminatesRouteLocation().getName(); 658 } 659 return NONE; 660 } 661 662 public RouteLocation getTrainTerminatesRouteLocation() { 663 if (getRoute() == null) { 664 return null; 665 } 666 return getRoute().getTerminatesRouteLocation(); 667 } 668 669 /** 670 * Returns the order the train should be blocked. 671 * 672 * @return routeLocations for this train. 673 */ 674 public List<RouteLocation> getTrainBlockingOrder() { 675 if (getRoute() == null) { 676 return null; 677 } 678 return getRoute().getBlockingOrder(); 679 } 680 681 /** 682 * Set train's current route location 683 * 684 * @param location The current RouteLocation. 685 */ 686 protected void setCurrentLocation(RouteLocation location) { 687 RouteLocation old = _current; 688 _current = location; 689 if ((old != null && !old.equals(location)) || (old == null && location != null)) { 690 setDirtyAndFirePropertyChange(TRAIN_CURRENT_CHANGED_PROPERTY, old, location); // NOI18N 691 } 692 } 693 694 /** 695 * Get train's current location name 696 * 697 * @return Train's current route location name 698 */ 699 public String getCurrentLocationName() { 700 if (getCurrentRouteLocation() == null) { 701 return NONE; 702 } 703 return getCurrentRouteLocation().getName(); 704 } 705 706 /** 707 * Get train's current route location 708 * 709 * @return Train's current route location 710 */ 711 public RouteLocation getCurrentRouteLocation() { 712 if (getRoute() == null) { 713 return null; 714 } 715 if (_current == null) { 716 return null; 717 } 718 // this will verify that the current location still exists 719 return getRoute().getLocationById(_current.getId()); 720 } 721 722 /** 723 * Get the train's next location name 724 * 725 * @return Train's next route location name 726 */ 727 public String getNextLocationName() { 728 return getNextLocationName(1); 729 } 730 731 /** 732 * Get a location name in a train's route from the current train's location. 733 * A number of "1" means get the next location name in a train's route. 734 * 735 * @param number The stop number, must be greater than 0 736 * @return Name of the location that is the number of stops away from the 737 * train's current location. 738 */ 739 public String getNextLocationName(int number) { 740 RouteLocation rl = getCurrentRouteLocation(); 741 while (number-- > 0) { 742 rl = getNextRouteLocation(rl); 743 if (rl == null) { 744 return NONE; 745 } 746 } 747 return rl.getName(); 748 } 749 750 public RouteLocation getNextRouteLocation(RouteLocation currentRouteLocation) { 751 if (getRoute() == null) { 752 return null; 753 } 754 List<RouteLocation> routeList = getRoute().getLocationsBySequenceList(); 755 for (int i = 0; i < routeList.size(); i++) { 756 RouteLocation rl = routeList.get(i); 757 if (rl == currentRouteLocation) { 758 i++; 759 if (i < routeList.size()) { 760 return routeList.get(i); 761 } 762 break; 763 } 764 } 765 return null; // At end of route 766 } 767 768 public void setDepartureTrack(Track track) { 769 Track old = _departureTrack; 770 _departureTrack = track; 771 if (old != track) { 772 setDirtyAndFirePropertyChange("DepartureTrackChanged", old, track); // NOI18N 773 } 774 } 775 776 public Track getDepartureTrack() { 777 return _departureTrack; 778 } 779 780 public boolean isDepartingStaging() { 781 return getDepartureTrack() != null; 782 } 783 784 public void setTerminationTrack(Track track) { 785 Track old = _terminationTrack; 786 _terminationTrack = track; 787 if (old != track) { 788 setDirtyAndFirePropertyChange("TerminationTrackChanged", old, track); // NOI18N 789 } 790 } 791 792 public Track getTerminationTrack() { 793 return _terminationTrack; 794 } 795 796 /** 797 * Set the train's machine readable status. Calls update train table row 798 * color. 799 * 800 * @param code machine readable 801 */ 802 public void setStatusCode(int code) { 803 String oldStatus = getStatus(); 804 int oldCode = getStatusCode(); 805 _statusCode = code; 806 setDate(Calendar.getInstance().getTime()); 807 if (oldCode != getStatusCode()) { 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 public int getNumberCarsPickedUp() { 2247 return getNumberCarsPickedUp(getCurrentRouteLocation()); 2248 } 2249 2250 /** 2251 * Gets the number of cars pulled from a location 2252 * 2253 * @param routeLocation the location 2254 * @return number of pick ups 2255 */ 2256 public int getNumberCarsPickedUp(RouteLocation routeLocation) { 2257 int number = 0; 2258 for (Car rs : InstanceManager.getDefault(CarManager.class).getList(this)) { 2259 if (rs.getRouteLocation() == routeLocation && rs.getTrack() != null) { 2260 number++; 2261 } 2262 } 2263 return number; 2264 } 2265 2266 public int getNumberCarsSetout() { 2267 return getNumberCarsSetout(getCurrentRouteLocation()); 2268 } 2269 2270 /** 2271 * Gets the number of cars delivered to a location 2272 * 2273 * @param routeLocation the location 2274 * @return number of set outs 2275 */ 2276 public int getNumberCarsSetout(RouteLocation routeLocation) { 2277 int number = 0; 2278 for (Car rs : InstanceManager.getDefault(CarManager.class).getList(this)) { 2279 if (rs.getRouteDestination() == routeLocation) { 2280 number++; 2281 } 2282 } 2283 return number; 2284 } 2285 2286 /** 2287 * Gets the train's length at the current location in the train's route. 2288 * 2289 * @return The train length at the train's current location 2290 */ 2291 public int getTrainLength() { 2292 return getTrainLength(getCurrentRouteLocation()); 2293 } 2294 2295 /** 2296 * Gets the train's length at the route location specified 2297 * 2298 * @param routeLocation The route location 2299 * @return The train length at the route location 2300 */ 2301 public int getTrainLength(RouteLocation routeLocation) { 2302 int length = 0; 2303 Route route = getRoute(); 2304 if (route != null) { 2305 for (RouteLocation rl : route.getLocationsBySequenceList()) { 2306 for (RollingStock rs : InstanceManager.getDefault(EngineManager.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 for (RollingStock rs : InstanceManager.getDefault(CarManager.class).getList(this)) { 2315 if (rs.getRouteLocation() == rl) { 2316 length += rs.getTotalLength(); 2317 } 2318 if (rs.getRouteDestination() == rl) { 2319 length += -rs.getTotalLength(); 2320 } 2321 } 2322 if (rl == routeLocation) { 2323 break; 2324 } 2325 } 2326 } 2327 return length; 2328 } 2329 2330 /** 2331 * Get the train's weight at the current location. 2332 * 2333 * @return Train's weight in tons. 2334 */ 2335 public int getTrainWeight() { 2336 return getTrainWeight(getCurrentRouteLocation()); 2337 } 2338 2339 public int getTrainWeight(RouteLocation routeLocation) { 2340 int weight = 0; 2341 Route route = getRoute(); 2342 if (route != null) { 2343 for (RouteLocation rl : route.getLocationsBySequenceList()) { 2344 for (RollingStock rs : InstanceManager.getDefault(EngineManager.class).getList(this)) { 2345 if (rs.getRouteLocation() == rl) { 2346 weight += rs.getAdjustedWeightTons(); 2347 } 2348 if (rs.getRouteDestination() == rl) { 2349 weight += -rs.getAdjustedWeightTons(); 2350 } 2351 } 2352 for (Car car : InstanceManager.getDefault(CarManager.class).getList(this)) { 2353 if (car.getRouteLocation() == rl) { 2354 weight += car.getAdjustedWeightTons(); // weight depends 2355 // on car load 2356 } 2357 if (car.getRouteDestination() == rl) { 2358 weight += -car.getAdjustedWeightTons(); 2359 } 2360 } 2361 if (rl == routeLocation) { 2362 break; 2363 } 2364 } 2365 } 2366 return weight; 2367 } 2368 2369 /** 2370 * Gets the train's locomotive horsepower at the route location specified 2371 * 2372 * @param routeLocation The route location 2373 * @return The train's locomotive horsepower at the route location 2374 */ 2375 public int getTrainHorsePower(RouteLocation routeLocation) { 2376 int hp = 0; 2377 Route route = getRoute(); 2378 if (route != null) { 2379 for (RouteLocation rl : route.getLocationsBySequenceList()) { 2380 for (Engine eng : InstanceManager.getDefault(EngineManager.class).getList(this)) { 2381 if (eng.getRouteLocation() == rl) { 2382 hp += eng.getHpInteger(); 2383 } 2384 if (eng.getRouteDestination() == rl) { 2385 hp += -eng.getHpInteger(); 2386 } 2387 } 2388 if (rl == routeLocation) { 2389 break; 2390 } 2391 } 2392 } 2393 return hp; 2394 } 2395 2396 /** 2397 * Gets the current caboose road and number if there's one assigned to the 2398 * train. 2399 * 2400 * @return Road and number of caboose. 2401 */ 2402 public String getCabooseRoadAndNumber() { 2403 String cabooseRoadNumber = NONE; 2404 RouteLocation rl = getCurrentRouteLocation(); 2405 List<Car> cars = InstanceManager.getDefault(CarManager.class).getByTrainList(this); 2406 for (Car car : cars) { 2407 if (car.getRouteLocation() == rl && car.isCaboose()) { 2408 cabooseRoadNumber = 2409 car.getRoadName().split(TrainCommon.HYPHEN)[0] + " " + TrainCommon.splitString(car.getNumber()); 2410 } 2411 } 2412 return cabooseRoadNumber; 2413 } 2414 2415 public void setDescription(String description) { 2416 String old = _description; 2417 _description = description; 2418 if (!old.equals(description)) { 2419 setDirtyAndFirePropertyChange(DESCRIPTION_CHANGED_PROPERTY, old, description); 2420 } 2421 } 2422 2423 public String getRawDescription() { 2424 return _description; 2425 } 2426 2427 /** 2428 * Returns a formated string providing the train's description. {0} = lead 2429 * engine number, {1} = train's departure direction {2} = lead engine road 2430 * {3} = DCC address of lead engine. 2431 * 2432 * @return The train's description. 2433 */ 2434 public String getDescription() { 2435 try { 2436 String description = MessageFormat.format(getRawDescription(), new Object[]{getLeadEngineNumber(), 2437 getTrainDepartsDirection(), getLeadEngineRoadName(), getLeadEngineDccAddress()}); 2438 return description; 2439 } catch (IllegalArgumentException e) { 2440 return "ERROR IN FORMATTING: " + getRawDescription(); 2441 } 2442 } 2443 2444 public void setNumberEngines(String number) { 2445 String old = _numberEngines; 2446 _numberEngines = number; 2447 if (!old.equals(number)) { 2448 setDirtyAndFirePropertyChange("trainNmberEngines", old, number); // NOI18N 2449 } 2450 } 2451 2452 /** 2453 * Get the number of engines that this train requires. 2454 * 2455 * @return The number of engines that this train requires. 2456 */ 2457 public String getNumberEngines() { 2458 return _numberEngines; 2459 } 2460 2461 /** 2462 * Get the number of engines needed for the second set. 2463 * 2464 * @return The number of engines needed in route 2465 */ 2466 public String getSecondLegNumberEngines() { 2467 return _leg2Engines; 2468 } 2469 2470 public void setSecondLegNumberEngines(String number) { 2471 String old = _leg2Engines; 2472 _leg2Engines = number; 2473 if (!old.equals(number)) { 2474 setDirtyAndFirePropertyChange("trainNmberEngines", old, number); // NOI18N 2475 } 2476 } 2477 2478 /** 2479 * Get the number of engines needed for the third set. 2480 * 2481 * @return The number of engines needed in route 2482 */ 2483 public String getThirdLegNumberEngines() { 2484 return _leg3Engines; 2485 } 2486 2487 public void setThirdLegNumberEngines(String number) { 2488 String old = _leg3Engines; 2489 _leg3Engines = number; 2490 if (!old.equals(number)) { 2491 setDirtyAndFirePropertyChange("trainNmberEngines", old, number); // NOI18N 2492 } 2493 } 2494 2495 /** 2496 * Set the road name of engines servicing this train. 2497 * 2498 * @param road The road name of engines servicing this train. 2499 */ 2500 public void setEngineRoad(String road) { 2501 String old = _engineRoad; 2502 _engineRoad = road; 2503 if (!old.equals(road)) { 2504 setDirtyAndFirePropertyChange("trainEngineRoad", old, road); // NOI18N 2505 } 2506 } 2507 2508 /** 2509 * Get the road name of engines servicing this train. 2510 * 2511 * @return The road name of engines servicing this train. 2512 */ 2513 public String getEngineRoad() { 2514 return _engineRoad; 2515 } 2516 2517 /** 2518 * Set the road name of engines servicing this train 2nd leg. 2519 * 2520 * @param road The road name of engines servicing this train. 2521 */ 2522 public void setSecondLegEngineRoad(String road) { 2523 String old = _leg2Road; 2524 _leg2Road = road; 2525 if (!old.equals(road)) { 2526 setDirtyAndFirePropertyChange("trainEngineRoad", old, road); // NOI18N 2527 } 2528 } 2529 2530 /** 2531 * Get the road name of engines servicing this train 2nd leg. 2532 * 2533 * @return The road name of engines servicing this train. 2534 */ 2535 public String getSecondLegEngineRoad() { 2536 return _leg2Road; 2537 } 2538 2539 /** 2540 * Set the road name of engines servicing this train 3rd leg. 2541 * 2542 * @param road The road name of engines servicing this train. 2543 */ 2544 public void setThirdLegEngineRoad(String road) { 2545 String old = _leg3Road; 2546 _leg3Road = road; 2547 if (!old.equals(road)) { 2548 setDirtyAndFirePropertyChange("trainEngineRoad", old, road); // NOI18N 2549 } 2550 } 2551 2552 /** 2553 * Get the road name of engines servicing this train 3rd leg. 2554 * 2555 * @return The road name of engines servicing this train. 2556 */ 2557 public String getThirdLegEngineRoad() { 2558 return _leg3Road; 2559 } 2560 2561 /** 2562 * Set the model name of engines servicing this train. 2563 * 2564 * @param model The model name of engines servicing this train. 2565 */ 2566 public void setEngineModel(String model) { 2567 String old = _engineModel; 2568 _engineModel = model; 2569 if (!old.equals(model)) { 2570 setDirtyAndFirePropertyChange("trainEngineModel", old, model); // NOI18N 2571 } 2572 } 2573 2574 public String getEngineModel() { 2575 return _engineModel; 2576 } 2577 2578 /** 2579 * Set the model name of engines servicing this train's 2nd leg. 2580 * 2581 * @param model The model name of engines servicing this train. 2582 */ 2583 public void setSecondLegEngineModel(String model) { 2584 String old = _leg2Model; 2585 _leg2Model = model; 2586 if (!old.equals(model)) { 2587 setDirtyAndFirePropertyChange("trainEngineModel", old, model); // NOI18N 2588 } 2589 } 2590 2591 public String getSecondLegEngineModel() { 2592 return _leg2Model; 2593 } 2594 2595 /** 2596 * Set the model name of engines servicing this train's 3rd leg. 2597 * 2598 * @param model The model name of engines servicing this train. 2599 */ 2600 public void setThirdLegEngineModel(String model) { 2601 String old = _leg3Model; 2602 _leg3Model = model; 2603 if (!old.equals(model)) { 2604 setDirtyAndFirePropertyChange("trainEngineModel", old, model); // NOI18N 2605 } 2606 } 2607 2608 public String getThirdLegEngineModel() { 2609 return _leg3Model; 2610 } 2611 2612 protected void replaceModel(String oldModel, String newModel) { 2613 if (getEngineModel().equals(oldModel)) { 2614 setEngineModel(newModel); 2615 } 2616 if (getSecondLegEngineModel().equals(oldModel)) { 2617 setSecondLegEngineModel(newModel); 2618 } 2619 if (getThirdLegEngineModel().equals(oldModel)) { 2620 setThirdLegEngineModel(newModel); 2621 } 2622 } 2623 2624 /** 2625 * Set the road name of the caboose servicing this train. 2626 * 2627 * @param road The road name of the caboose servicing this train. 2628 */ 2629 public void setCabooseRoad(String road) { 2630 String old = _cabooseRoad; 2631 _cabooseRoad = road; 2632 if (!old.equals(road)) { 2633 setDirtyAndFirePropertyChange("trainCabooseRoad", old, road); // NOI18N 2634 } 2635 } 2636 2637 public String getCabooseRoad() { 2638 return _cabooseRoad; 2639 } 2640 2641 /** 2642 * Set the road name of the second leg caboose servicing this train. 2643 * 2644 * @param road The road name of the caboose servicing this train's 2nd leg. 2645 */ 2646 public void setSecondLegCabooseRoad(String road) { 2647 String old = _leg2CabooseRoad; 2648 _leg2CabooseRoad = road; 2649 if (!old.equals(road)) { 2650 setDirtyAndFirePropertyChange("trainCabooseRoad", old, road); // NOI18N 2651 } 2652 } 2653 2654 public String getSecondLegCabooseRoad() { 2655 return _leg2CabooseRoad; 2656 } 2657 2658 /** 2659 * Set the road name of the third leg caboose servicing this train. 2660 * 2661 * @param road The road name of the caboose servicing this train's 3rd leg. 2662 */ 2663 public void setThirdLegCabooseRoad(String road) { 2664 String old = _leg3CabooseRoad; 2665 _leg3CabooseRoad = road; 2666 if (!old.equals(road)) { 2667 setDirtyAndFirePropertyChange("trainCabooseRoad", old, road); // NOI18N 2668 } 2669 } 2670 2671 public String getThirdLegCabooseRoad() { 2672 return _leg3CabooseRoad; 2673 } 2674 2675 public void setSecondLegStartRouteLocation(RouteLocation rl) { 2676 _leg2Start = rl; 2677 } 2678 2679 public RouteLocation getSecondLegStartRouteLocation() { 2680 return _leg2Start; 2681 } 2682 2683 public String getSecondLegStartLocationName() { 2684 if (getSecondLegStartRouteLocation() == null) { 2685 return NONE; 2686 } 2687 return getSecondLegStartRouteLocation().getName(); 2688 } 2689 2690 public void setThirdLegStartRouteLocation(RouteLocation rl) { 2691 _leg3Start = rl; 2692 } 2693 2694 public RouteLocation getThirdLegStartRouteLocation() { 2695 return _leg3Start; 2696 } 2697 2698 public String getThirdLegStartLocationName() { 2699 if (getThirdLegStartRouteLocation() == null) { 2700 return NONE; 2701 } 2702 return getThirdLegStartRouteLocation().getName(); 2703 } 2704 2705 public void setSecondLegEndRouteLocation(RouteLocation rl) { 2706 _end2Leg = rl; 2707 } 2708 2709 public String getSecondLegEndLocationName() { 2710 if (getSecondLegEndRouteLocation() == null) { 2711 return NONE; 2712 } 2713 return getSecondLegEndRouteLocation().getName(); 2714 } 2715 2716 public RouteLocation getSecondLegEndRouteLocation() { 2717 return _end2Leg; 2718 } 2719 2720 public void setThirdLegEndRouteLocation(RouteLocation rl) { 2721 _leg3End = rl; 2722 } 2723 2724 public RouteLocation getThirdLegEndRouteLocation() { 2725 return _leg3End; 2726 } 2727 2728 public String getThirdLegEndLocationName() { 2729 if (getThirdLegEndRouteLocation() == null) { 2730 return NONE; 2731 } 2732 return getThirdLegEndRouteLocation().getName(); 2733 } 2734 2735 /** 2736 * Optional changes to train while en route. 2737 * 2738 * @param options NO_CABOOSE_OR_FRED, CHANGE_ENGINES, ADD_CABOOSE, 2739 * HELPER_ENGINES, REMOVE_CABOOSE 2740 */ 2741 public void setSecondLegOptions(int options) { 2742 int old = _leg2Options; 2743 _leg2Options = options; 2744 if (old != options) { 2745 setDirtyAndFirePropertyChange("trainLegOptions", old, options); // NOI18N 2746 } 2747 } 2748 2749 public int getSecondLegOptions() { 2750 return _leg2Options; 2751 } 2752 2753 /** 2754 * Optional changes to train while en route. 2755 * 2756 * @param options NO_CABOOSE_OR_FRED, CHANGE_ENGINES, ADD_CABOOSE, 2757 * HELPER_ENGINES, REMOVE_CABOOSE 2758 */ 2759 public void setThirdLegOptions(int options) { 2760 int old = _leg3Options; 2761 _leg3Options = options; 2762 if (old != options) { 2763 setDirtyAndFirePropertyChange("trainLegOptions", old, options); // NOI18N 2764 } 2765 } 2766 2767 public int getThirdLegOptions() { 2768 return _leg3Options; 2769 } 2770 2771 public void setComment(String comment) { 2772 String old = _comment; 2773 _comment = comment; 2774 if (!old.equals(comment)) { 2775 setDirtyAndFirePropertyChange("trainComment", old, comment); // NOI18N 2776 } 2777 } 2778 2779 public String getComment() { 2780 return TrainCommon.getTextColorString(getCommentWithColor()); 2781 } 2782 2783 public String getCommentWithColor() { 2784 return _comment; 2785 } 2786 2787 /** 2788 * Add a script to run before a train is built 2789 * 2790 * @param pathname The script's pathname 2791 */ 2792 public void addBuildScript(String pathname) { 2793 _buildScripts.add(pathname); 2794 setDirtyAndFirePropertyChange("addBuildScript", pathname, null); // NOI18N 2795 } 2796 2797 public void deleteBuildScript(String pathname) { 2798 _buildScripts.remove(pathname); 2799 setDirtyAndFirePropertyChange("deleteBuildScript", null, pathname); // NOI18N 2800 } 2801 2802 /** 2803 * Gets a list of pathnames (scripts) to run before this train is built 2804 * 2805 * @return A list of pathnames to run before this train is built 2806 */ 2807 public List<String> getBuildScripts() { 2808 return _buildScripts; 2809 } 2810 2811 /** 2812 * Add a script to run after a train is built 2813 * 2814 * @param pathname The script's pathname 2815 */ 2816 public void addAfterBuildScript(String pathname) { 2817 _afterBuildScripts.add(pathname); 2818 setDirtyAndFirePropertyChange("addAfterBuildScript", pathname, null); // NOI18N 2819 } 2820 2821 public void deleteAfterBuildScript(String pathname) { 2822 _afterBuildScripts.remove(pathname); 2823 setDirtyAndFirePropertyChange("deleteAfterBuildScript", null, pathname); // NOI18N 2824 } 2825 2826 /** 2827 * Gets a list of pathnames (scripts) to run after this train is built 2828 * 2829 * @return A list of pathnames to run after this train is built 2830 */ 2831 public List<String> getAfterBuildScripts() { 2832 return _afterBuildScripts; 2833 } 2834 2835 /** 2836 * Add a script to run when train is moved 2837 * 2838 * @param pathname The script's pathname 2839 */ 2840 public void addMoveScript(String pathname) { 2841 _moveScripts.add(pathname); 2842 setDirtyAndFirePropertyChange("addMoveScript", pathname, null); // NOI18N 2843 } 2844 2845 public void deleteMoveScript(String pathname) { 2846 _moveScripts.remove(pathname); 2847 setDirtyAndFirePropertyChange("deleteMoveScript", null, pathname); // NOI18N 2848 } 2849 2850 /** 2851 * Gets a list of pathnames (scripts) to run when this train moved 2852 * 2853 * @return A list of pathnames to run when this train moved 2854 */ 2855 public List<String> getMoveScripts() { 2856 return _moveScripts; 2857 } 2858 2859 /** 2860 * Add a script to run when train is terminated 2861 * 2862 * @param pathname The script's pathname 2863 */ 2864 public void addTerminationScript(String pathname) { 2865 _terminationScripts.add(pathname); 2866 setDirtyAndFirePropertyChange("addTerminationScript", pathname, null); // NOI18N 2867 } 2868 2869 public void deleteTerminationScript(String pathname) { 2870 _terminationScripts.remove(pathname); 2871 setDirtyAndFirePropertyChange("deleteTerminationScript", null, pathname); // NOI18N 2872 } 2873 2874 /** 2875 * Gets a list of pathnames (scripts) to run when this train terminates 2876 * 2877 * @return A list of pathnames to run when this train terminates 2878 */ 2879 public List<String> getTerminationScripts() { 2880 return _terminationScripts; 2881 } 2882 2883 /** 2884 * Gets the optional railroad name for this train. 2885 * 2886 * @return Train's railroad name. 2887 */ 2888 public String getRailroadName() { 2889 return _railroadName; 2890 } 2891 2892 /** 2893 * Overrides the default railroad name for this train. 2894 * 2895 * @param name The railroad name for this train. 2896 */ 2897 public void setRailroadName(String name) { 2898 String old = _railroadName; 2899 _railroadName = name; 2900 if (!old.equals(name)) { 2901 setDirtyAndFirePropertyChange("trainRailroadName", old, name); // NOI18N 2902 } 2903 } 2904 2905 public String getManifestLogoPathName() { 2906 return _logoPathName; 2907 } 2908 2909 /** 2910 * Overrides the default logo for this train. 2911 * 2912 * @param pathName file location for the logo. 2913 */ 2914 public void setManifestLogoPathName(String pathName) { 2915 _logoPathName = pathName; 2916 } 2917 2918 public boolean isShowArrivalAndDepartureTimesEnabled() { 2919 return _showTimes; 2920 } 2921 2922 public void setShowArrivalAndDepartureTimes(boolean enable) { 2923 boolean old = _showTimes; 2924 _showTimes = enable; 2925 if (old != enable) { 2926 setDirtyAndFirePropertyChange("showArrivalAndDepartureTimes", old ? "true" : "false", // NOI18N 2927 enable ? "true" : "false"); // NOI18N 2928 } 2929 } 2930 2931 public boolean isSendCarsToTerminalEnabled() { 2932 return _sendToTerminal; 2933 } 2934 2935 public void setSendCarsToTerminalEnabled(boolean enable) { 2936 boolean old = _sendToTerminal; 2937 _sendToTerminal = enable; 2938 if (old != enable) { 2939 setDirtyAndFirePropertyChange("send cars to terminal", old ? "true" : "false", enable ? "true" // NOI18N 2940 : "false"); // NOI18N 2941 } 2942 } 2943 2944 /** 2945 * Allow local moves if car has a custom load or Final Destination 2946 * 2947 * @return true if local move is allowed 2948 */ 2949 public boolean isAllowLocalMovesEnabled() { 2950 return _allowLocalMoves; 2951 } 2952 2953 public void setAllowLocalMovesEnabled(boolean enable) { 2954 boolean old = _allowLocalMoves; 2955 _allowLocalMoves = enable; 2956 if (old != enable) { 2957 setDirtyAndFirePropertyChange("allow local moves", old ? "true" : "false", enable ? "true" // NOI18N 2958 : "false"); // NOI18N 2959 } 2960 } 2961 2962 public boolean isAllowThroughCarsEnabled() { 2963 return _allowThroughCars; 2964 } 2965 2966 public void setAllowThroughCarsEnabled(boolean enable) { 2967 boolean old = _allowThroughCars; 2968 _allowThroughCars = enable; 2969 if (old != enable) { 2970 setDirtyAndFirePropertyChange("allow through cars", old ? "true" : "false", enable ? "true" // NOI18N 2971 : "false"); // NOI18N 2972 } 2973 } 2974 2975 public boolean isBuildTrainNormalEnabled() { 2976 return _buildNormal; 2977 } 2978 2979 public void setBuildTrainNormalEnabled(boolean enable) { 2980 boolean old = _buildNormal; 2981 _buildNormal = enable; 2982 if (old != enable) { 2983 setDirtyAndFirePropertyChange("build train normal", old ? "true" : "false", enable ? "true" // NOI18N 2984 : "false"); // NOI18N 2985 } 2986 } 2987 2988 /** 2989 * When true allow a turn to return cars to staging. A turn is a train that 2990 * departs and terminates at the same location. 2991 * 2992 * @return true if cars can return to staging 2993 */ 2994 public boolean isAllowReturnToStagingEnabled() { 2995 return _allowCarsReturnStaging; 2996 } 2997 2998 public void setAllowReturnToStagingEnabled(boolean enable) { 2999 boolean old = _allowCarsReturnStaging; 3000 _allowCarsReturnStaging = enable; 3001 if (old != enable) { 3002 setDirtyAndFirePropertyChange("allow cars to return to staging", old ? "true" : "false", // NOI18N 3003 enable ? "true" : "false"); // NOI18N 3004 } 3005 } 3006 3007 public boolean isServiceAllCarsWithFinalDestinationsEnabled() { 3008 return _serviceAllCarsWithFinalDestinations; 3009 } 3010 3011 public void setServiceAllCarsWithFinalDestinationsEnabled(boolean enable) { 3012 boolean old = _serviceAllCarsWithFinalDestinations; 3013 _serviceAllCarsWithFinalDestinations = enable; 3014 if (old != enable) { 3015 setDirtyAndFirePropertyChange("TrainServiceAllCarsWithFinalDestinations", old ? "true" : "false", // NOI18N 3016 enable ? "true" : "false"); // NOI18N 3017 } 3018 } 3019 3020 public boolean isBuildConsistEnabled() { 3021 return _buildConsist; 3022 } 3023 3024 public void setBuildConsistEnabled(boolean enable) { 3025 boolean old = _buildConsist; 3026 _buildConsist = enable; 3027 if (old != enable) { 3028 setDirtyAndFirePropertyChange("TrainBuildConsist", old ? "true" : "false", // NOI18N 3029 enable ? "true" : "false"); // NOI18N 3030 } 3031 } 3032 3033 public boolean isSendCarsWithCustomLoadsToStagingEnabled() { 3034 return _sendCarsWithCustomLoadsToStaging; 3035 } 3036 3037 public void setSendCarsWithCustomLoadsToStagingEnabled(boolean enable) { 3038 boolean old = _sendCarsWithCustomLoadsToStaging; 3039 _sendCarsWithCustomLoadsToStaging = enable; 3040 if (old != enable) { 3041 setDirtyAndFirePropertyChange("SendCarsWithCustomLoadsToStaging", old ? "true" : "false", // NOI18N 3042 enable ? "true" : "false"); // NOI18N 3043 } 3044 } 3045 3046 protected void setBuilt(boolean built) { 3047 boolean old = _built; 3048 _built = built; 3049 if (old != built) { 3050 setDirtyAndFirePropertyChange(BUILT_CHANGED_PROPERTY, old, built); // NOI18N 3051 } 3052 } 3053 3054 /** 3055 * Used to determine if this train has been built. 3056 * 3057 * @return true if the train was successfully built. 3058 */ 3059 public boolean isBuilt() { 3060 return _built; 3061 } 3062 3063 /** 3064 * Set true whenever the train's manifest has been modified. For example 3065 * adding or removing a car from a train, or changing the manifest format. 3066 * Once the manifest has been regenerated (modified == false), the old 3067 * status for the train is restored. 3068 * 3069 * @param modified True if train's manifest has been modified. 3070 */ 3071 public void setModified(boolean modified) { 3072 log.debug("Set modified {}", modified); 3073 if (!isBuilt()) { 3074 _modified = false; 3075 return; // there isn't a manifest to modify 3076 } 3077 boolean old = _modified; 3078 _modified = modified; 3079 if (modified) { 3080 setPrinted(false); 3081 } 3082 if (old != modified) { 3083 if (modified) { 3084 // scripts can call setModified() for a train 3085 if (getStatusCode() != CODE_RUN_SCRIPTS) { 3086 setOldStatusCode(getStatusCode()); 3087 } 3088 setStatusCode(CODE_MANIFEST_MODIFIED); 3089 } else { 3090 setStatusCode(getOldStatusCode()); // restore previous train 3091 // status 3092 } 3093 } 3094 setDirtyAndFirePropertyChange(TRAIN_MODIFIED_CHANGED_PROPERTY, null, modified); // NOI18N 3095 } 3096 3097 public boolean isModified() { 3098 return _modified; 3099 } 3100 3101 /** 3102 * Control flag used to decide if this train is to be built. 3103 * 3104 * @param build When true, build this train. 3105 */ 3106 public void setBuildEnabled(boolean build) { 3107 boolean old = _build; 3108 _build = build; 3109 if (old != build) { 3110 setDirtyAndFirePropertyChange(BUILD_CHANGED_PROPERTY, old, build); // NOI18N 3111 } 3112 } 3113 3114 /** 3115 * Used to determine if train is to be built. 3116 * 3117 * @return true if train is to be built. 3118 */ 3119 public boolean isBuildEnabled() { 3120 return _build; 3121 } 3122 3123 /** 3124 * Build this train if the build control flag is true. 3125 * 3126 * @return True only if train is successfully built. 3127 */ 3128 public boolean buildIfSelected() { 3129 if (isBuildEnabled() && !isBuilt()) { 3130 return build(); 3131 } 3132 log.debug("Train ({}) not selected or already built, skipping build", getName()); 3133 return false; 3134 } 3135 3136 /** 3137 * Build this train. Creates a train manifest. 3138 * 3139 * @return True if build successful. 3140 */ 3141 public synchronized boolean build() { 3142 reset(); 3143 // check to see if any other trains are building 3144 int count = 1200; // wait up to 120 seconds 3145 while (InstanceManager.getDefault(TrainManager.class).isAnyTrainBuilding() && count > 0) { 3146 count--; 3147 try { 3148 wait(100); // 100 msec 3149 } catch (InterruptedException e) { 3150 // TODO Auto-generated catch block 3151 log.error("Thread unexpectedly interrupted", e); 3152 } 3153 } 3154 // timed out? 3155 if (count <= 0) { 3156 log.warn("Build timeout for train ({})", getName()); 3157 setBuildFailed(true); 3158 setStatusCode(CODE_BUILD_FAILED); 3159 return false; 3160 } 3161 // run before build scripts 3162 runScripts(getBuildScripts()); 3163 TrainBuilder tb = new TrainBuilder(); 3164 boolean results = tb.build(this); 3165 // run after build scripts 3166 runScripts(getAfterBuildScripts()); 3167 return results; 3168 } 3169 3170 /** 3171 * Run train scripts, waits for completion before returning. 3172 */ 3173 private synchronized void runScripts(List<String> scripts) { 3174 if (scripts.size() > 0) { 3175 // save the current status 3176 setOldStatusCode(getStatusCode()); 3177 setStatusCode(CODE_RUN_SCRIPTS); 3178 // create the python interpreter thread 3179 JmriScriptEngineManager.getDefault().initializeAllEngines(); 3180 // find the number of active threads 3181 ThreadGroup root = Thread.currentThread().getThreadGroup(); 3182 int numberOfThreads = root.activeCount(); 3183 // log.debug("Number of active threads: {}", numberOfThreads); 3184 for (String scriptPathname : scripts) { 3185 try { 3186 JmriScriptEngineManager.getDefault() 3187 .runScript(new File(jmri.util.FileUtil.getExternalFilename(scriptPathname))); 3188 } catch (Exception e) { 3189 log.error("Problem with script: {}", scriptPathname); 3190 } 3191 } 3192 // need to wait for scripts to complete or 4 seconds maximum 3193 int count = 0; 3194 while (root.activeCount() > numberOfThreads) { 3195 log.debug("Number of active threads: {}, at start: {}", root.activeCount(), numberOfThreads); 3196 try { 3197 wait(40); 3198 } catch (InterruptedException e) { 3199 Thread.currentThread().interrupt(); 3200 } 3201 if (count++ > 100) { 3202 break; // 4 seconds maximum 40*100 = 4000 3203 } 3204 } 3205 setStatusCode(getOldStatusCode()); 3206 } 3207 } 3208 3209 public boolean printBuildReport() { 3210 boolean isPreview = (InstanceManager.getDefault(TrainManager.class).isPrintPreviewEnabled() || 3211 Setup.isBuildReportAlwaysPreviewEnabled()); 3212 return printBuildReport(isPreview); 3213 } 3214 3215 public boolean printBuildReport(boolean isPreview) { 3216 File buildFile = InstanceManager.getDefault(TrainManagerXml.class).getTrainBuildReportFile(getName()); 3217 if (!buildFile.exists()) { 3218 log.warn("Build file missing for train {}", getName()); 3219 return false; 3220 } 3221 3222 if (isPreview && Setup.isBuildReportEditorEnabled()) { 3223 TrainPrintUtilities.editReport(buildFile, getName()); 3224 } else { 3225 TrainPrintUtilities.printReport(buildFile, 3226 Bundle.getMessage("buildReport", getDescription()), 3227 isPreview, NONE, true, NONE, NONE, Setup.PORTRAIT, Setup.getBuildReportFontSize(), true, null); 3228 } 3229 return true; 3230 } 3231 3232 protected void setBuildFailed(boolean status) { 3233 boolean old = _buildFailed; 3234 _buildFailed = status; 3235 if (old != status) { 3236 setDirtyAndFirePropertyChange("buildFailed", old ? "true" : "false", status ? "true" : "false"); // NOI18N 3237 } 3238 } 3239 3240 /** 3241 * Returns true if the train build failed. Note that returning false doesn't 3242 * mean the build was successful. 3243 * 3244 * @return true if train build failed. 3245 */ 3246 public boolean isBuildFailed() { 3247 return _buildFailed; 3248 } 3249 3250 protected void setBuildFailedMessage(String message) { 3251 String old = _buildFailedMessage; 3252 _buildFailedMessage = message; 3253 if (!old.equals(message)) { 3254 setDirtyAndFirePropertyChange("buildFailedMessage", old, message); // NOI18N 3255 } 3256 } 3257 3258 protected String getBuildFailedMessage() { 3259 return _buildFailedMessage; 3260 } 3261 3262 /** 3263 * Print manifest for train if already built. 3264 * 3265 * @return true if print successful. 3266 */ 3267 public boolean printManifestIfBuilt() { 3268 if (isBuilt()) { 3269 boolean isPreview = InstanceManager.getDefault(TrainManager.class).isPrintPreviewEnabled(); 3270 try { 3271 return (printManifest(isPreview)); 3272 } catch (BuildFailedException e) { 3273 log.error("Print Manifest failed: {}", e.getMessage()); 3274 } 3275 } else { 3276 log.debug("Need to build train ({}) before printing manifest", getName()); 3277 } 3278 return false; 3279 } 3280 3281 /** 3282 * Print manifest for train. 3283 * 3284 * @param isPreview True if preview. 3285 * @return true if print successful, false if train print file not found. 3286 * @throws BuildFailedException if unable to create new Manifests 3287 */ 3288 public boolean printManifest(boolean isPreview) throws BuildFailedException { 3289 if (isModified()) { 3290 new TrainManifest(this); 3291 try { 3292 new JsonManifest(this).build(); 3293 } catch (IOException ex) { 3294 log.error("Unable to create JSON manifest {}", ex.getLocalizedMessage()); 3295 } 3296 new TrainCsvManifest(this); 3297 } 3298 File file = InstanceManager.getDefault(TrainManagerXml.class).getTrainManifestFile(getName()); 3299 if (!file.exists()) { 3300 log.warn("Manifest file missing for train ({})", getName()); 3301 return false; 3302 } 3303 if (isPreview && Setup.isManifestEditorEnabled()) { 3304 TrainUtilities.openDesktop(file); 3305 return true; 3306 } 3307 String logoURL = Setup.NONE; 3308 if (!getManifestLogoPathName().equals(NONE)) { 3309 logoURL = FileUtil.getExternalFilename(getManifestLogoPathName()); 3310 } else if (!Setup.getManifestLogoURL().equals(Setup.NONE)) { 3311 logoURL = FileUtil.getExternalFilename(Setup.getManifestLogoURL()); 3312 } 3313 Location departs = InstanceManager.getDefault(LocationManager.class).getLocationByName(getTrainDepartsName()); 3314 String printerName = Location.NONE; 3315 if (departs != null) { 3316 printerName = departs.getDefaultPrinterName(); 3317 } 3318 // the train description shouldn't exceed half of the page width or the 3319 // page number will be overwritten 3320 String name = getDescription(); 3321 if (name.length() > TrainCommon.getManifestHeaderLineLength() / 2) { 3322 name = name.substring(0, TrainCommon.getManifestHeaderLineLength() / 2); 3323 } 3324 TrainPrintUtilities.printReport(file, name, isPreview, Setup.getFontName(), false, logoURL, printerName, 3325 Setup.getManifestOrientation(), Setup.getManifestFontSize(), Setup.isPrintPageHeaderEnabled(), 3326 Setup.getPrintDuplexSides()); 3327 if (!isPreview) { 3328 setPrinted(true); 3329 } 3330 return true; 3331 } 3332 3333 public boolean openFile() { 3334 File file = createCsvManifestFile(); 3335 if (file == null || !file.exists()) { 3336 log.warn("CSV manifest file missing for train {}", getName()); 3337 return false; 3338 } 3339 TrainUtilities.openDesktop(file); 3340 return true; 3341 } 3342 3343 public boolean runFile() { 3344 File file = createCsvManifestFile(); 3345 if (file == null || !file.exists()) { 3346 log.warn("CSV manifest file missing for train {}", getName()); 3347 return false; 3348 } 3349 // Set up to process the CSV file by the external Manifest program 3350 InstanceManager.getDefault(TrainCustomManifest.class).addCsvFile(file); 3351 if (!InstanceManager.getDefault(TrainCustomManifest.class).process()) { 3352 if (!InstanceManager.getDefault(TrainCustomManifest.class).excelFileExists()) { 3353 JmriJOptionPane.showMessageDialog(null, 3354 Bundle.getMessage("LoadDirectoryNameFileName", 3355 InstanceManager.getDefault(TrainCustomManifest.class).getDirectoryPathName(), 3356 InstanceManager.getDefault(TrainCustomManifest.class).getFileName()), 3357 Bundle.getMessage("ManifestCreatorNotFound"), JmriJOptionPane.ERROR_MESSAGE); 3358 } 3359 return false; 3360 } 3361 return true; 3362 } 3363 3364 public File createCsvManifestFile() { 3365 if (isModified()) { 3366 try { 3367 new TrainManifest(this); 3368 try { 3369 new JsonManifest(this).build(); 3370 } catch (IOException ex) { 3371 log.error("Unable to create JSON manifest {}", ex.getLocalizedMessage()); 3372 } 3373 new TrainCsvManifest(this); 3374 } catch (BuildFailedException e) { 3375 log.error("Could not create CVS Manifest files"); 3376 } 3377 } 3378 File file = InstanceManager.getDefault(TrainManagerXml.class).getTrainCsvManifestFile(getName()); 3379 if (!file.exists()) { 3380 log.warn("CSV manifest file was not created for train ({})", getName()); 3381 return null; 3382 } 3383 return file; 3384 } 3385 3386 public void setPrinted(boolean printed) { 3387 boolean old = _printed; 3388 _printed = printed; 3389 if (old != printed) { 3390 setDirtyAndFirePropertyChange("trainPrinted", old ? "true" : "false", printed ? "true" : "false"); // NOI18N 3391 } 3392 } 3393 3394 /** 3395 * Used to determine if train manifest was printed. 3396 * 3397 * @return true if the train manifest was printed. 3398 */ 3399 public boolean isPrinted() { 3400 return _printed; 3401 } 3402 3403 /** 3404 * Sets the panel position for the train icon for the current route 3405 * location. 3406 * 3407 * @return true if train coordinates can be set 3408 */ 3409 public boolean setTrainIconCoordinates() { 3410 if (Setup.isTrainIconCordEnabled() && getCurrentRouteLocation() != null && _trainIcon != null) { 3411 getCurrentRouteLocation().setTrainIconX(_trainIcon.getX()); 3412 getCurrentRouteLocation().setTrainIconY(_trainIcon.getY()); 3413 return true; 3414 } 3415 return false; 3416 } 3417 3418 /** 3419 * Terminate train. 3420 */ 3421 public void terminate() { 3422 while (isBuilt()) { 3423 move(); 3424 } 3425 } 3426 3427 /** 3428 * Move train to next location in the route. Will move engines, cars, and 3429 * train icon. Will also terminate a train after it arrives at its final 3430 * destination. 3431 */ 3432 public void move() { 3433 log.debug("Move train ({})", getName()); 3434 if (getRoute() == null || getCurrentRouteLocation() == null) { 3435 setBuilt(false); // break terminate loop 3436 return; 3437 } 3438 if (!isBuilt()) { 3439 log.error("ERROR attempt to move train ({}) that hasn't been built", getName()); 3440 return; 3441 } 3442 RouteLocation rl = getCurrentRouteLocation(); 3443 RouteLocation rlNext = getNextRouteLocation(rl); 3444 3445 setCurrentLocation(rlNext); 3446 3447 // cars and engines will move via property change 3448 setDirtyAndFirePropertyChange(TRAIN_LOCATION_CHANGED_PROPERTY, rl, rlNext); 3449 moveTrainIcon(rlNext); 3450 updateStatus(rl, rlNext); 3451 // tell GUI that train has complete its move 3452 setDirtyAndFirePropertyChange(TRAIN_MOVE_COMPLETE_CHANGED_PROPERTY, rl, rlNext); 3453 } 3454 3455 /** 3456 * Move train to a location in the train's route. Code checks to see if the 3457 * location requested is part of the train's route and if the train hasn't 3458 * already visited the location. This command can only move the train 3459 * forward in its route. Note that you can not terminate the train using 3460 * this command. See move() or terminate(). 3461 * 3462 * @param locationName The name of the location to move this train. 3463 * @return true if train was able to move to the named location. 3464 */ 3465 public boolean move(String locationName) { 3466 log.info("Move train ({}) to location ({})", getName(), locationName); 3467 if (getRoute() == null || getCurrentRouteLocation() == null) { 3468 return false; 3469 } 3470 List<RouteLocation> routeList = getRoute().getLocationsBySequenceList(); 3471 for (int i = 0; i < routeList.size(); i++) { 3472 RouteLocation rl = routeList.get(i); 3473 if (getCurrentRouteLocation() == rl) { 3474 for (int j = i + 1; j < routeList.size(); j++) { 3475 rl = routeList.get(j); 3476 if (rl.getName().equals(locationName)) { 3477 log.debug("Found location ({}) moving train to this location", locationName); 3478 for (j = i + 1; j < routeList.size(); j++) { 3479 rl = routeList.get(j); 3480 move(); 3481 if (rl.getName().equals(locationName)) { 3482 return true; 3483 } 3484 } 3485 } 3486 } 3487 break; // done 3488 } 3489 } 3490 return false; 3491 } 3492 3493 /** 3494 * Moves the train to the specified route location 3495 * 3496 * @param rl route location 3497 * @return true if successful 3498 */ 3499 public boolean move(RouteLocation rl) { 3500 if (rl == null) { 3501 return false; 3502 } 3503 log.debug("Move train ({}) to location ({})", getName(), rl.getName()); 3504 if (getRoute() == null || getCurrentRouteLocation() == null) { 3505 return false; 3506 } 3507 boolean foundCurrent = false; 3508 for (RouteLocation xrl : getRoute().getLocationsBySequenceList()) { 3509 if (getCurrentRouteLocation() == xrl) { 3510 foundCurrent = true; 3511 } 3512 if (xrl == rl) { 3513 if (foundCurrent) { 3514 return true; // done 3515 } else { 3516 break; // train passed this location 3517 } 3518 } 3519 if (foundCurrent) { 3520 move(); 3521 } 3522 } 3523 return false; 3524 } 3525 3526 /** 3527 * Move train to the next location in the train's route. The location name 3528 * provided must be equal to the next location name in the train's route. 3529 * 3530 * @param locationName The next location name in the train's route. 3531 * @return true if successful. 3532 */ 3533 public boolean moveToNextLocation(String locationName) { 3534 if (getNextLocationName().equals(locationName)) { 3535 move(); 3536 return true; 3537 } 3538 return false; 3539 } 3540 3541 public void loadTrainIcon() { 3542 if (getCurrentRouteLocation() != null) { 3543 moveTrainIcon(getCurrentRouteLocation()); 3544 } 3545 } 3546 3547 private final boolean animation = true; // when true use animation for icon 3548 // moves 3549 TrainIconAnimation _ta; 3550 3551 /* 3552 * The train icon is moved to route location (rl) for this train 3553 */ 3554 protected void moveTrainIcon(RouteLocation rl) { 3555 // create train icon if at departure, if program has been restarted, or removed 3556 if (rl == getTrainDepartsRouteLocation() || _trainIcon == null || !_trainIcon.isActive()) { 3557 createTrainIcon(rl); 3558 } 3559 // is the lead engine still in train 3560 if (getLeadEngine() != null && getLeadEngine().getRouteDestination() == rl && rl != null) { 3561 log.debug("Engine ({}) arriving at destination {}", getLeadEngine().toString(), rl.getName()); 3562 } 3563 if (_trainIcon != null && _trainIcon.isActive()) { 3564 setTrainIconColor(); 3565 _trainIcon.setShowToolTip(true); 3566 String txt = null; 3567 if (getCurrentLocationName().equals(NONE)) { 3568 txt = getDescription() + " " + Bundle.getMessage("Terminated") + " (" + getTrainTerminatesName() + ")"; 3569 } else { 3570 txt = Bundle.getMessage("TrainAtNext", 3571 getDescription(), getCurrentLocationName(), getNextLocationName(), getTrainLength(), 3572 Setup.getLengthUnit().toLowerCase()); 3573 } 3574 _trainIcon.getToolTip().setText(txt); 3575 _trainIcon.getToolTip().setBackgroundColor(Color.white); 3576 // rl can be null when train is terminated. 3577 if (rl != null) { 3578 if (rl.getTrainIconX() != 0 || rl.getTrainIconY() != 0) { 3579 if (animation) { 3580 TrainIconAnimation ta = new TrainIconAnimation(_trainIcon, rl, _ta); 3581 ta.start(); // start the animation 3582 _ta = ta; 3583 } else { 3584 _trainIcon.setLocation(rl.getTrainIconX(), rl.getTrainIconY()); 3585 } 3586 } 3587 } 3588 } 3589 } 3590 3591 public String getIconName() { 3592 String name = getName(); 3593 if (isBuilt() && getLeadEngine() != null && Setup.isTrainIconAppendEnabled()) { 3594 name += " " + getLeadEngine().getNumber(); 3595 } 3596 return name; 3597 } 3598 3599 public String getLeadEngineNumber() { 3600 if (getLeadEngine() == null) { 3601 return NONE; 3602 } 3603 return getLeadEngine().getNumber(); 3604 } 3605 3606 public String getLeadEngineRoadName() { 3607 if (getLeadEngine() == null) { 3608 return NONE; 3609 } 3610 return getLeadEngine().getRoadName(); 3611 } 3612 3613 public String getLeadEngineRoadAndNumber() { 3614 if (getLeadEngine() == null) { 3615 return NONE; 3616 } 3617 return getLeadEngine().toString(); 3618 } 3619 3620 public String getLeadEngineDccAddress() { 3621 if (getLeadEngine() == null) { 3622 return NONE; 3623 } 3624 return getLeadEngine().getDccAddress(); 3625 } 3626 3627 /** 3628 * Gets the lead engine, will create it if the program has been restarted 3629 * 3630 * @return lead engine for this train 3631 */ 3632 public Engine getLeadEngine() { 3633 if (_leadEngine == null && !_leadEngineId.equals(NONE)) { 3634 _leadEngine = InstanceManager.getDefault(EngineManager.class).getById(_leadEngineId); 3635 } 3636 return _leadEngine; 3637 } 3638 3639 public void setLeadEngine(Engine engine) { 3640 if (engine == null) { 3641 _leadEngineId = NONE; 3642 } 3643 _leadEngine = engine; 3644 } 3645 3646 /** 3647 * Returns the lead engine in a train's route. There can be up to two 3648 * changes in the lead engine for a train. 3649 * 3650 * @param routeLocation where in the train's route to find the lead engine. 3651 * @return lead engine 3652 */ 3653 public Engine getLeadEngine(RouteLocation routeLocation) { 3654 Engine lead = null; 3655 for (RouteLocation rl : getRoute().getLocationsBySequenceList()) { 3656 for (Engine engine : InstanceManager.getDefault(EngineManager.class).getByTrainList(this)) { 3657 if (engine.getRouteLocation() == rl && (engine.getConsist() == null || engine.isLead())) { 3658 lead = engine; 3659 break; 3660 } 3661 } 3662 if (rl == routeLocation) { 3663 break; 3664 } 3665 } 3666 return lead; 3667 } 3668 3669 protected TrainIcon _trainIcon = null; 3670 3671 public TrainIcon getTrainIcon() { 3672 return _trainIcon; 3673 } 3674 3675 public void createTrainIcon(RouteLocation rl) { 3676 if (_trainIcon != null && _trainIcon.isActive()) { 3677 _trainIcon.remove(); 3678 } 3679 // if there's a panel specified, get it and place icon 3680 if (!Setup.getPanelName().isEmpty()) { 3681 Editor editor = InstanceManager.getDefault(EditorManager.class).getTargetFrame(Setup.getPanelName()); 3682 if (editor != null) { 3683 try { 3684 _trainIcon = editor.addTrainIcon(getIconName()); 3685 } catch (Exception e) { 3686 log.error("Error placing train ({}) icon on panel ({})", getName(), Setup.getPanelName(), e); 3687 return; 3688 } 3689 _trainIcon.setTrain(this); 3690 if (getIconName().length() > 9) { 3691 _trainIcon.setFont(_trainIcon.getFont().deriveFont(8.f)); 3692 } 3693 if (rl != null) { 3694 _trainIcon.setLocation(rl.getTrainIconX(), rl.getTrainIconY()); 3695 } 3696 // add throttle if there's a throttle manager 3697 if (jmri.InstanceManager.getNullableDefault(jmri.ThrottleManager.class) != null) { 3698 // add throttle if JMRI loco roster entry exist 3699 RosterEntry entry = null; 3700 if (getLeadEngine() != null) { 3701 // first try and find a match based on loco road number 3702 entry = getLeadEngine().getRosterEntry(); 3703 } 3704 if (entry != null) { 3705 _trainIcon.setRosterEntry(entry); 3706 if (getLeadEngine().getConsist() != null) { 3707 _trainIcon.setConsistNumber(getLeadEngine().getConsist().getConsistNumber()); 3708 } 3709 } else { 3710 log.debug("Loco roster entry not found for train ({})", getName()); 3711 } 3712 } 3713 } 3714 } 3715 } 3716 3717 private void setTrainIconColor() { 3718 // Terminated train? 3719 if (getCurrentLocationName().equals(NONE)) { 3720 _trainIcon.setLocoColor(Setup.getTrainIconColorTerminate()); 3721 return; 3722 } 3723 // local train serving only one location? 3724 if (isLocalSwitcher()) { 3725 _trainIcon.setLocoColor(Setup.getTrainIconColorLocal()); 3726 return; 3727 } 3728 // set color based on train direction at current location 3729 if (getCurrentRouteLocation().getTrainDirection() == RouteLocation.NORTH) { 3730 _trainIcon.setLocoColor(Setup.getTrainIconColorNorth()); 3731 } 3732 if (getCurrentRouteLocation().getTrainDirection() == RouteLocation.SOUTH) { 3733 _trainIcon.setLocoColor(Setup.getTrainIconColorSouth()); 3734 } 3735 if (getCurrentRouteLocation().getTrainDirection() == RouteLocation.EAST) { 3736 _trainIcon.setLocoColor(Setup.getTrainIconColorEast()); 3737 } 3738 if (getCurrentRouteLocation().getTrainDirection() == RouteLocation.WEST) { 3739 _trainIcon.setLocoColor(Setup.getTrainIconColorWest()); 3740 } 3741 } 3742 3743 private void updateStatus(RouteLocation old, RouteLocation next) { 3744 if (next != null) { 3745 setStatusCode(CODE_TRAIN_EN_ROUTE); 3746 // run move scripts 3747 runScripts(getMoveScripts()); 3748 } else { 3749 log.debug("Train ({}) terminated", getName()); 3750 setStatusCode(CODE_TERMINATED); 3751 setBuilt(false); 3752 // run termination scripts 3753 runScripts(getTerminationScripts()); 3754 } 3755 } 3756 3757 /** 3758 * Sets the print status for switch lists 3759 * 3760 * @param status UNKNOWN PRINTED 3761 */ 3762 public void setSwitchListStatus(String status) { 3763 String old = _switchListStatus; 3764 _switchListStatus = status; 3765 if (!old.equals(status)) { 3766 setDirtyAndFirePropertyChange("switch list train status", old, status); // NOI18N 3767 } 3768 } 3769 3770 public String getSwitchListStatus() { 3771 return _switchListStatus; 3772 } 3773 3774 /** 3775 * Resets the train, removes engines and cars from this train. 3776 * 3777 * @return true if reset successful 3778 */ 3779 public boolean reset() { 3780 // is this train in route? 3781 if (isTrainEnRoute()) { 3782 log.info("Train ({}) has started its route, can not be reset", getName()); 3783 return false; 3784 } 3785 setCurrentLocation(null); 3786 setDepartureTrack(null); 3787 setTerminationTrack(null); 3788 setBuilt(false); 3789 setBuildFailed(false); 3790 setBuildFailedMessage(NONE); 3791 setPrinted(false); 3792 setModified(false); 3793 // remove cars and engines from this train via property change 3794 setStatusCode(CODE_TRAIN_RESET); 3795 // remove train icon 3796 if (_trainIcon != null && _trainIcon.isActive()) { 3797 _trainIcon.remove(); 3798 } 3799 return true; 3800 } 3801 3802 public void dispose() { 3803 if (getRoute() != null) { 3804 getRoute().removePropertyChangeListener(this); 3805 } 3806 InstanceManager.getDefault(CarRoads.class).removePropertyChangeListener(this); 3807 InstanceManager.getDefault(CarTypes.class).removePropertyChangeListener(this); 3808 InstanceManager.getDefault(EngineTypes.class).removePropertyChangeListener(this); 3809 InstanceManager.getDefault(CarOwners.class).removePropertyChangeListener(this); 3810 InstanceManager.getDefault(EngineModels.class).removePropertyChangeListener(this); 3811 3812 setDirtyAndFirePropertyChange(DISPOSE_CHANGED_PROPERTY, null, "Dispose"); // NOI18N 3813 } 3814 3815 /** 3816 * Construct this Entry from XML. This member has to remain synchronized 3817 * with the detailed DTD in operations-trains.dtd 3818 * 3819 * @param e Consist XML element 3820 */ 3821 public Train(Element e) { 3822 org.jdom2.Attribute a; 3823 if ((a = e.getAttribute(Xml.ID)) != null) { 3824 _id = a.getValue(); 3825 } else { 3826 log.warn("no id attribute in train element when reading operations"); 3827 } 3828 if ((a = e.getAttribute(Xml.NAME)) != null) { 3829 _name = a.getValue(); 3830 } 3831 if ((a = e.getAttribute(Xml.DESCRIPTION)) != null) { 3832 _description = a.getValue(); 3833 } 3834 if ((a = e.getAttribute(Xml.DEPART_HOUR)) != null) { 3835 String hour = a.getValue(); 3836 if ((a = e.getAttribute(Xml.DEPART_MINUTE)) != null) { 3837 String minute = a.getValue(); 3838 _departureTime = hour + ":" + minute; 3839 } 3840 } 3841 3842 // Trains table row color 3843 Element eRowColor = e.getChild(Xml.ROW_COLOR); 3844 if (eRowColor != null && (a = eRowColor.getAttribute(Xml.NAME)) != null) { 3845 _tableRowColorName = a.getValue().toLowerCase(); 3846 } 3847 if (eRowColor != null && (a = eRowColor.getAttribute(Xml.RESET_ROW_COLOR)) != null) { 3848 _tableRowColorResetName = a.getValue().toLowerCase(); 3849 } 3850 3851 Element eRoute = e.getChild(Xml.ROUTE); 3852 if (eRoute != null) { 3853 if ((a = eRoute.getAttribute(Xml.ID)) != null) { 3854 setRoute(InstanceManager.getDefault(RouteManager.class).getRouteById(a.getValue())); 3855 } 3856 if (eRoute.getChild(Xml.SKIPS) != null) { 3857 List<Element> skips = eRoute.getChild(Xml.SKIPS).getChildren(Xml.LOCATION); 3858 String[] locs = new String[skips.size()]; 3859 for (int i = 0; i < skips.size(); i++) { 3860 Element loc = skips.get(i); 3861 if ((a = loc.getAttribute(Xml.ID)) != null) { 3862 locs[i] = a.getValue(); 3863 } 3864 } 3865 setTrainSkipsLocations(locs); 3866 } 3867 } else { 3868 // old format 3869 // try and first get the route by id then by name 3870 if ((a = e.getAttribute(Xml.ROUTE_ID)) != null) { 3871 setRoute(InstanceManager.getDefault(RouteManager.class).getRouteById(a.getValue())); 3872 } else if ((a = e.getAttribute(Xml.ROUTE)) != null) { 3873 setRoute(InstanceManager.getDefault(RouteManager.class).getRouteByName(a.getValue())); 3874 } 3875 if ((a = e.getAttribute(Xml.SKIP)) != null) { 3876 String locationIds = a.getValue(); 3877 String[] locs = locationIds.split("%%"); // NOI18N 3878 // log.debug("Train skips: {}", locationIds); 3879 setTrainSkipsLocations(locs); 3880 } 3881 } 3882 // new way of reading car types using elements 3883 if (e.getChild(Xml.TYPES) != null) { 3884 List<Element> carTypes = e.getChild(Xml.TYPES).getChildren(Xml.CAR_TYPE); 3885 String[] types = new String[carTypes.size()]; 3886 for (int i = 0; i < carTypes.size(); i++) { 3887 Element type = carTypes.get(i); 3888 if ((a = type.getAttribute(Xml.NAME)) != null) { 3889 types[i] = a.getValue(); 3890 } 3891 } 3892 setTypeNames(types); 3893 List<Element> locoTypes = e.getChild(Xml.TYPES).getChildren(Xml.LOCO_TYPE); 3894 types = new String[locoTypes.size()]; 3895 for (int i = 0; i < locoTypes.size(); i++) { 3896 Element type = locoTypes.get(i); 3897 if ((a = type.getAttribute(Xml.NAME)) != null) { 3898 types[i] = a.getValue(); 3899 } 3900 } 3901 setTypeNames(types); 3902 } // old way of reading car types up to version 2.99.6 3903 else if ((a = e.getAttribute(Xml.CAR_TYPES)) != null) { 3904 String names = a.getValue(); 3905 String[] types = names.split("%%"); // NOI18N 3906 // log.debug("Car types: {}", names); 3907 setTypeNames(types); 3908 } 3909 // old misspelled format 3910 if ((a = e.getAttribute(Xml.CAR_ROAD_OPERATION)) != null) { 3911 _carRoadOption = a.getValue(); 3912 } 3913 if ((a = e.getAttribute(Xml.CAR_ROAD_OPTION)) != null) { 3914 _carRoadOption = a.getValue(); 3915 } 3916 // new way of reading car roads using elements 3917 if (e.getChild(Xml.CAR_ROADS) != null) { 3918 List<Element> carRoads = e.getChild(Xml.CAR_ROADS).getChildren(Xml.CAR_ROAD); 3919 String[] roads = new String[carRoads.size()]; 3920 for (int i = 0; i < carRoads.size(); i++) { 3921 Element road = carRoads.get(i); 3922 if ((a = road.getAttribute(Xml.NAME)) != null) { 3923 roads[i] = a.getValue(); 3924 } 3925 } 3926 setCarRoadNames(roads); 3927 } // old way of reading car roads up to version 2.99.6 3928 else if ((a = e.getAttribute(Xml.CAR_ROADS)) != null) { 3929 String names = a.getValue(); 3930 String[] roads = names.split("%%"); // NOI18N 3931 log.debug("Train ({}) {} car roads: {}", getName(), getCarRoadOption(), names); 3932 setCarRoadNames(roads); 3933 } 3934 3935 if ((a = e.getAttribute(Xml.CABOOSE_ROAD_OPTION)) != null) { 3936 _cabooseRoadOption = a.getValue(); 3937 } 3938 // new way of reading caboose roads using elements 3939 if (e.getChild(Xml.CABOOSE_ROADS) != null) { 3940 List<Element> carRoads = e.getChild(Xml.CABOOSE_ROADS).getChildren(Xml.CAR_ROAD); 3941 String[] roads = new String[carRoads.size()]; 3942 for (int i = 0; i < carRoads.size(); i++) { 3943 Element road = carRoads.get(i); 3944 if ((a = road.getAttribute(Xml.NAME)) != null) { 3945 roads[i] = a.getValue(); 3946 } 3947 } 3948 setCabooseRoadNames(roads); 3949 } 3950 3951 if ((a = e.getAttribute(Xml.LOCO_ROAD_OPTION)) != null) { 3952 _locoRoadOption = a.getValue(); 3953 } 3954 // new way of reading engine roads using elements 3955 if (e.getChild(Xml.LOCO_ROADS) != null) { 3956 List<Element> locoRoads = e.getChild(Xml.LOCO_ROADS).getChildren(Xml.LOCO_ROAD); 3957 String[] roads = new String[locoRoads.size()]; 3958 for (int i = 0; i < locoRoads.size(); i++) { 3959 Element road = locoRoads.get(i); 3960 if ((a = road.getAttribute(Xml.NAME)) != null) { 3961 roads[i] = a.getValue(); 3962 } 3963 } 3964 setLocoRoadNames(roads); 3965 } 3966 3967 if ((a = e.getAttribute(Xml.CAR_LOAD_OPTION)) != null) { 3968 _loadOption = a.getValue(); 3969 } 3970 if ((a = e.getAttribute(Xml.CAR_OWNER_OPTION)) != null) { 3971 _ownerOption = a.getValue(); 3972 } 3973 if ((a = e.getAttribute(Xml.BUILT_START_YEAR)) != null) { 3974 _builtStartYear = a.getValue(); 3975 } 3976 if ((a = e.getAttribute(Xml.BUILT_END_YEAR)) != null) { 3977 _builtEndYear = a.getValue(); 3978 } 3979 // new way of reading car loads using elements 3980 if (e.getChild(Xml.CAR_LOADS) != null) { 3981 List<Element> carLoads = e.getChild(Xml.CAR_LOADS).getChildren(Xml.CAR_LOAD); 3982 String[] loads = new String[carLoads.size()]; 3983 for (int i = 0; i < carLoads.size(); i++) { 3984 Element load = carLoads.get(i); 3985 if ((a = load.getAttribute(Xml.NAME)) != null) { 3986 loads[i] = a.getValue(); 3987 } 3988 } 3989 setLoadNames(loads); 3990 } // old way of reading car loads up to version 2.99.6 3991 else if ((a = e.getAttribute(Xml.CAR_LOADS)) != null) { 3992 String names = a.getValue(); 3993 String[] loads = names.split("%%"); // NOI18N 3994 log.debug("Train ({}) {} car loads: {}", getName(), getLoadOption(), names); 3995 setLoadNames(loads); 3996 } 3997 // new way of reading car owners using elements 3998 if (e.getChild(Xml.CAR_OWNERS) != null) { 3999 List<Element> carOwners = e.getChild(Xml.CAR_OWNERS).getChildren(Xml.CAR_OWNER); 4000 String[] owners = new String[carOwners.size()]; 4001 for (int i = 0; i < carOwners.size(); i++) { 4002 Element owner = carOwners.get(i); 4003 if ((a = owner.getAttribute(Xml.NAME)) != null) { 4004 owners[i] = a.getValue(); 4005 } 4006 } 4007 setOwnerNames(owners); 4008 } // old way of reading car owners up to version 2.99.6 4009 else if ((a = e.getAttribute(Xml.CAR_OWNERS)) != null) { 4010 String names = a.getValue(); 4011 String[] owners = names.split("%%"); // NOI18N 4012 log.debug("Train ({}) {} car owners: {}", getName(), getOwnerOption(), names); 4013 setOwnerNames(owners); 4014 } 4015 4016 if ((a = e.getAttribute(Xml.NUMBER_ENGINES)) != null) { 4017 _numberEngines = a.getValue(); 4018 } 4019 if ((a = e.getAttribute(Xml.LEG2_ENGINES)) != null) { 4020 _leg2Engines = a.getValue(); 4021 } 4022 if ((a = e.getAttribute(Xml.LEG3_ENGINES)) != null) { 4023 _leg3Engines = a.getValue(); 4024 } 4025 if ((a = e.getAttribute(Xml.ENGINE_ROAD)) != null) { 4026 _engineRoad = a.getValue(); 4027 } 4028 if ((a = e.getAttribute(Xml.LEG2_ROAD)) != null) { 4029 _leg2Road = a.getValue(); 4030 } 4031 if ((a = e.getAttribute(Xml.LEG3_ROAD)) != null) { 4032 _leg3Road = a.getValue(); 4033 } 4034 if ((a = e.getAttribute(Xml.ENGINE_MODEL)) != null) { 4035 _engineModel = a.getValue(); 4036 } 4037 if ((a = e.getAttribute(Xml.LEG2_MODEL)) != null) { 4038 _leg2Model = a.getValue(); 4039 } 4040 if ((a = e.getAttribute(Xml.LEG3_MODEL)) != null) { 4041 _leg3Model = a.getValue(); 4042 } 4043 if ((a = e.getAttribute(Xml.REQUIRES)) != null) { 4044 try { 4045 _requires = Integer.parseInt(a.getValue()); 4046 } catch (NumberFormatException ee) { 4047 log.error("Requires ({}) isn't a valid number for train ({})", a.getValue(), getName()); 4048 } 4049 } 4050 if ((a = e.getAttribute(Xml.CABOOSE_ROAD)) != null) { 4051 _cabooseRoad = a.getValue(); 4052 } 4053 if ((a = e.getAttribute(Xml.LEG2_CABOOSE_ROAD)) != null) { 4054 _leg2CabooseRoad = a.getValue(); 4055 } 4056 if ((a = e.getAttribute(Xml.LEG3_CABOOSE_ROAD)) != null) { 4057 _leg3CabooseRoad = a.getValue(); 4058 } 4059 if ((a = e.getAttribute(Xml.LEG2_OPTIONS)) != null) { 4060 try { 4061 _leg2Options = Integer.parseInt(a.getValue()); 4062 } catch (NumberFormatException ee) { 4063 log.error("Leg 2 options ({}) isn't a valid number for train ({})", a.getValue(), getName()); 4064 } 4065 } 4066 if ((a = e.getAttribute(Xml.LEG3_OPTIONS)) != null) { 4067 try { 4068 _leg3Options = Integer.parseInt(a.getValue()); 4069 } catch (NumberFormatException ee) { 4070 log.error("Leg 3 options ({}) isn't a valid number for train ({})", a.getValue(), getName()); 4071 } 4072 } 4073 if ((a = e.getAttribute(Xml.BUILD_NORMAL)) != null) { 4074 _buildNormal = a.getValue().equals(Xml.TRUE); 4075 } 4076 if ((a = e.getAttribute(Xml.TO_TERMINAL)) != null) { 4077 _sendToTerminal = a.getValue().equals(Xml.TRUE); 4078 } 4079 if ((a = e.getAttribute(Xml.ALLOW_LOCAL_MOVES)) != null) { 4080 _allowLocalMoves = a.getValue().equals(Xml.TRUE); 4081 } 4082 if ((a = e.getAttribute(Xml.ALLOW_THROUGH_CARS)) != null) { 4083 _allowThroughCars = a.getValue().equals(Xml.TRUE); 4084 } 4085 if ((a = e.getAttribute(Xml.ALLOW_RETURN)) != null) { 4086 _allowCarsReturnStaging = a.getValue().equals(Xml.TRUE); 4087 } 4088 if ((a = e.getAttribute(Xml.SERVICE_ALL)) != null) { 4089 _serviceAllCarsWithFinalDestinations = a.getValue().equals(Xml.TRUE); 4090 } 4091 if ((a = e.getAttribute(Xml.BUILD_CONSIST)) != null) { 4092 _buildConsist = a.getValue().equals(Xml.TRUE); 4093 } 4094 if ((a = e.getAttribute(Xml.SEND_CUSTOM_STAGING)) != null) { 4095 _sendCarsWithCustomLoadsToStaging = a.getValue().equals(Xml.TRUE); 4096 } 4097 if ((a = e.getAttribute(Xml.BUILT)) != null) { 4098 _built = a.getValue().equals(Xml.TRUE); 4099 } 4100 if ((a = e.getAttribute(Xml.BUILD)) != null) { 4101 _build = a.getValue().equals(Xml.TRUE); 4102 } 4103 if ((a = e.getAttribute(Xml.BUILD_FAILED)) != null) { 4104 _buildFailed = a.getValue().equals(Xml.TRUE); 4105 } 4106 if ((a = e.getAttribute(Xml.BUILD_FAILED_MESSAGE)) != null) { 4107 _buildFailedMessage = a.getValue(); 4108 } 4109 if ((a = e.getAttribute(Xml.PRINTED)) != null) { 4110 _printed = a.getValue().equals(Xml.TRUE); 4111 } 4112 if ((a = e.getAttribute(Xml.MODIFIED)) != null) { 4113 _modified = a.getValue().equals(Xml.TRUE); 4114 } 4115 if ((a = e.getAttribute(Xml.SWITCH_LIST_STATUS)) != null) { 4116 _switchListStatus = a.getValue(); 4117 } 4118 if ((a = e.getAttribute(Xml.LEAD_ENGINE)) != null) { 4119 _leadEngineId = a.getValue(); 4120 } 4121 if ((a = e.getAttribute(Xml.TERMINATION_DATE)) != null) { 4122 _date = TrainCommon.convertStringToDate(a.getValue()); 4123 } 4124 if ((a = e.getAttribute(Xml.REQUESTED_CARS)) != null) { 4125 try { 4126 _statusCarsRequested = Integer.parseInt(a.getValue()); 4127 } catch (NumberFormatException ee) { 4128 log.error("Status cars requested ({}) isn't a valid number for train ({})", a.getValue(), getName()); 4129 } 4130 } 4131 if ((a = e.getAttribute(Xml.STATUS_CODE)) != null) { 4132 try { 4133 _statusCode = Integer.parseInt(a.getValue()); 4134 } catch (NumberFormatException ee) { 4135 log.error("Status code ({}) isn't a valid number for train ({})", a.getValue(), getName()); 4136 } 4137 } else if ((a = e.getAttribute(Xml.STATUS)) != null) { 4138 // attempt to recover status code 4139 String status = a.getValue(); 4140 if (status.startsWith(BUILD_FAILED)) { 4141 _statusCode = CODE_BUILD_FAILED; 4142 } else if (status.startsWith(BUILT)) { 4143 _statusCode = CODE_BUILT; 4144 } else if (status.startsWith(PARTIAL_BUILT)) { 4145 _statusCode = CODE_PARTIAL_BUILT; 4146 } else if (status.startsWith(TERMINATED)) { 4147 _statusCode = CODE_TERMINATED; 4148 } else if (status.startsWith(TRAIN_EN_ROUTE)) { 4149 _statusCode = CODE_TRAIN_EN_ROUTE; 4150 } else if (status.startsWith(TRAIN_RESET)) { 4151 _statusCode = CODE_TRAIN_RESET; 4152 } else { 4153 _statusCode = CODE_UNKNOWN; 4154 } 4155 } 4156 if ((a = e.getAttribute(Xml.OLD_STATUS_CODE)) != null) { 4157 try { 4158 _oldStatusCode = Integer.parseInt(a.getValue()); 4159 } catch (NumberFormatException ee) { 4160 log.error("Old status code ({}) isn't a valid number for train ({})", a.getValue(), getName()); 4161 } 4162 } else { 4163 _oldStatusCode = getStatusCode(); // use current status code if one 4164 // wasn't saved 4165 } 4166 if ((a = e.getAttribute(Xml.COMMENT)) != null) { 4167 _comment = a.getValue(); 4168 } 4169 if (getRoute() != null) { 4170 if ((a = e.getAttribute(Xml.CURRENT)) != null) { 4171 _current = getRoute().getLocationById(a.getValue()); 4172 } 4173 if ((a = e.getAttribute(Xml.LEG2_START)) != null) { 4174 _leg2Start = getRoute().getLocationById(a.getValue()); 4175 } 4176 if ((a = e.getAttribute(Xml.LEG3_START)) != null) { 4177 _leg3Start = getRoute().getLocationById(a.getValue()); 4178 } 4179 if ((a = e.getAttribute(Xml.LEG2_END)) != null) { 4180 _end2Leg = getRoute().getLocationById(a.getValue()); 4181 } 4182 if ((a = e.getAttribute(Xml.LEG3_END)) != null) { 4183 _leg3End = getRoute().getLocationById(a.getValue()); 4184 } 4185 if ((a = e.getAttribute(Xml.DEPARTURE_TRACK)) != null) { 4186 Location location = InstanceManager.getDefault(LocationManager.class) 4187 .getLocationByName(getTrainDepartsName()); 4188 if (location != null) { 4189 _departureTrack = location.getTrackById(a.getValue()); 4190 } else { 4191 log.error("Departure location not found for track {}", a.getValue()); 4192 } 4193 } 4194 if ((a = e.getAttribute(Xml.TERMINATION_TRACK)) != null) { 4195 Location location = InstanceManager.getDefault(LocationManager.class) 4196 .getLocationByName(getTrainTerminatesName()); 4197 if (location != null) { 4198 _terminationTrack = location.getTrackById(a.getValue()); 4199 } else { 4200 log.error("Termiation location not found for track {}", a.getValue()); 4201 } 4202 } 4203 } 4204 4205 // check for scripts 4206 if (e.getChild(Xml.SCRIPTS) != null) { 4207 List<Element> lb = e.getChild(Xml.SCRIPTS).getChildren(Xml.BUILD); 4208 for (Element es : lb) { 4209 if ((a = es.getAttribute(Xml.NAME)) != null) { 4210 addBuildScript(a.getValue()); 4211 } 4212 } 4213 List<Element> lab = e.getChild(Xml.SCRIPTS).getChildren(Xml.AFTER_BUILD); 4214 for (Element es : lab) { 4215 if ((a = es.getAttribute(Xml.NAME)) != null) { 4216 addAfterBuildScript(a.getValue()); 4217 } 4218 } 4219 List<Element> lm = e.getChild(Xml.SCRIPTS).getChildren(Xml.MOVE); 4220 for (Element es : lm) { 4221 if ((a = es.getAttribute(Xml.NAME)) != null) { 4222 addMoveScript(a.getValue()); 4223 } 4224 } 4225 List<Element> lt = e.getChild(Xml.SCRIPTS).getChildren(Xml.TERMINATE); 4226 for (Element es : lt) { 4227 if ((a = es.getAttribute(Xml.NAME)) != null) { 4228 addTerminationScript(a.getValue()); 4229 } 4230 } 4231 } 4232 // check for optional railroad name and logo 4233 if ((e.getChild(Xml.RAIL_ROAD) != null) && (a = e.getChild(Xml.RAIL_ROAD).getAttribute(Xml.NAME)) != null) { 4234 String name = a.getValue(); 4235 setRailroadName(name); 4236 } 4237 if ((e.getChild(Xml.MANIFEST_LOGO) != null)) { 4238 if ((a = e.getChild(Xml.MANIFEST_LOGO).getAttribute(Xml.NAME)) != null) { 4239 setManifestLogoPathName(a.getValue()); 4240 } 4241 } 4242 if ((a = e.getAttribute(Xml.SHOW_TIMES)) != null) { 4243 _showTimes = a.getValue().equals(Xml.TRUE); 4244 } 4245 4246 addPropertyChangeListerners(); 4247 } 4248 4249 private void addPropertyChangeListerners() { 4250 InstanceManager.getDefault(CarRoads.class).addPropertyChangeListener(this); 4251 InstanceManager.getDefault(CarTypes.class).addPropertyChangeListener(this); 4252 InstanceManager.getDefault(EngineTypes.class).addPropertyChangeListener(this); 4253 InstanceManager.getDefault(CarOwners.class).addPropertyChangeListener(this); 4254 InstanceManager.getDefault(EngineModels.class).addPropertyChangeListener(this); 4255 } 4256 4257 /** 4258 * Create an XML element to represent this Entry. This member has to remain 4259 * synchronized with the detailed DTD in operations-trains.dtd. 4260 * 4261 * @return Contents in a JDOM Element 4262 */ 4263 public Element store() { 4264 Element e = new Element(Xml.TRAIN); 4265 e.setAttribute(Xml.ID, getId()); 4266 e.setAttribute(Xml.NAME, getName()); 4267 e.setAttribute(Xml.DESCRIPTION, getRawDescription()); 4268 e.setAttribute(Xml.DEPART_HOUR, getDepartureTimeHour()); 4269 e.setAttribute(Xml.DEPART_MINUTE, getDepartureTimeMinute()); 4270 4271 Element eRowColor = new Element(Xml.ROW_COLOR); 4272 eRowColor.setAttribute(Xml.NAME, getTableRowColorName()); 4273 eRowColor.setAttribute(Xml.RESET_ROW_COLOR, getTableRowColorNameReset()); 4274 e.addContent(eRowColor); 4275 4276 Element eRoute = new Element(Xml.ROUTE); 4277 if (getRoute() != null) { 4278 eRoute.setAttribute(Xml.NAME, getRoute().getName()); 4279 eRoute.setAttribute(Xml.ID, getRoute().getId()); 4280 e.addContent(eRoute); 4281 // build list of locations that this train skips 4282 String[] locationIds = getTrainSkipsLocations(); 4283 if (locationIds.length > 0) { 4284 Element eSkips = new Element(Xml.SKIPS); 4285 for (String id : locationIds) { 4286 Element eLoc = new Element(Xml.LOCATION); 4287 RouteLocation rl = getRoute().getLocationById(id); 4288 if (rl != null) { 4289 eLoc.setAttribute(Xml.NAME, rl.getName()); 4290 eLoc.setAttribute(Xml.ID, id); 4291 eSkips.addContent(eLoc); 4292 } 4293 } 4294 eRoute.addContent(eSkips); 4295 } 4296 } 4297 // build list of locations that this train skips 4298 if (getCurrentRouteLocation() != null) { 4299 e.setAttribute(Xml.CURRENT, getCurrentRouteLocation().getId()); 4300 } 4301 if (getDepartureTrack() != null) { 4302 e.setAttribute(Xml.DEPARTURE_TRACK, getDepartureTrack().getId()); 4303 } 4304 if (getTerminationTrack() != null) { 4305 e.setAttribute(Xml.TERMINATION_TRACK, getTerminationTrack().getId()); 4306 } 4307 e.setAttribute(Xml.BUILT_START_YEAR, getBuiltStartYear()); 4308 e.setAttribute(Xml.BUILT_END_YEAR, getBuiltEndYear()); 4309 e.setAttribute(Xml.NUMBER_ENGINES, getNumberEngines()); 4310 e.setAttribute(Xml.ENGINE_ROAD, getEngineRoad()); 4311 e.setAttribute(Xml.ENGINE_MODEL, getEngineModel()); 4312 e.setAttribute(Xml.REQUIRES, Integer.toString(getRequirements())); 4313 e.setAttribute(Xml.CABOOSE_ROAD, getCabooseRoad()); 4314 e.setAttribute(Xml.BUILD_NORMAL, isBuildTrainNormalEnabled() ? Xml.TRUE : Xml.FALSE); 4315 e.setAttribute(Xml.TO_TERMINAL, isSendCarsToTerminalEnabled() ? Xml.TRUE : Xml.FALSE); 4316 e.setAttribute(Xml.ALLOW_LOCAL_MOVES, isAllowLocalMovesEnabled() ? Xml.TRUE : Xml.FALSE); 4317 e.setAttribute(Xml.ALLOW_RETURN, isAllowReturnToStagingEnabled() ? Xml.TRUE : Xml.FALSE); 4318 e.setAttribute(Xml.ALLOW_THROUGH_CARS, isAllowThroughCarsEnabled() ? Xml.TRUE : Xml.FALSE); 4319 e.setAttribute(Xml.SERVICE_ALL, isServiceAllCarsWithFinalDestinationsEnabled() ? Xml.TRUE : Xml.FALSE); 4320 e.setAttribute(Xml.SEND_CUSTOM_STAGING, isSendCarsWithCustomLoadsToStagingEnabled() ? Xml.TRUE : Xml.FALSE); 4321 e.setAttribute(Xml.BUILD_CONSIST, isBuildConsistEnabled() ? Xml.TRUE : Xml.FALSE); 4322 e.setAttribute(Xml.BUILT, isBuilt() ? Xml.TRUE : Xml.FALSE); 4323 e.setAttribute(Xml.BUILD, isBuildEnabled() ? Xml.TRUE : Xml.FALSE); 4324 e.setAttribute(Xml.BUILD_FAILED, isBuildFailed() ? Xml.TRUE : Xml.FALSE); 4325 e.setAttribute(Xml.BUILD_FAILED_MESSAGE, getBuildFailedMessage()); 4326 e.setAttribute(Xml.PRINTED, isPrinted() ? Xml.TRUE : Xml.FALSE); 4327 e.setAttribute(Xml.MODIFIED, isModified() ? Xml.TRUE : Xml.FALSE); 4328 e.setAttribute(Xml.SWITCH_LIST_STATUS, getSwitchListStatus()); 4329 if (getLeadEngine() != null) { 4330 e.setAttribute(Xml.LEAD_ENGINE, getLeadEngine().getId()); 4331 } 4332 e.setAttribute(Xml.STATUS, getStatus()); 4333 e.setAttribute(Xml.TERMINATION_DATE, getDate()); 4334 e.setAttribute(Xml.REQUESTED_CARS, Integer.toString(getNumberCarsRequested())); 4335 e.setAttribute(Xml.STATUS_CODE, Integer.toString(getStatusCode())); 4336 e.setAttribute(Xml.OLD_STATUS_CODE, Integer.toString(getOldStatusCode())); 4337 e.setAttribute(Xml.COMMENT, getCommentWithColor()); 4338 e.setAttribute(Xml.SHOW_TIMES, isShowArrivalAndDepartureTimesEnabled() ? Xml.TRUE : Xml.FALSE); 4339 // build list of car types for this train 4340 String[] types = getTypeNames(); 4341 // new way of saving car types 4342 Element eTypes = new Element(Xml.TYPES); 4343 for (String type : types) { 4344 // don't save types that have been deleted by user 4345 if (InstanceManager.getDefault(EngineTypes.class).containsName(type)) { 4346 Element eType = new Element(Xml.LOCO_TYPE); 4347 eType.setAttribute(Xml.NAME, type); 4348 eTypes.addContent(eType); 4349 } else if (InstanceManager.getDefault(CarTypes.class).containsName(type)) { 4350 Element eType = new Element(Xml.CAR_TYPE); 4351 eType.setAttribute(Xml.NAME, type); 4352 eTypes.addContent(eType); 4353 } 4354 } 4355 e.addContent(eTypes); 4356 // save list of car roads for this train 4357 if (!getCarRoadOption().equals(ALL_ROADS)) { 4358 e.setAttribute(Xml.CAR_ROAD_OPTION, getCarRoadOption()); 4359 String[] roads = getCarRoadNames(); 4360 // new way of saving road names 4361 Element eRoads = new Element(Xml.CAR_ROADS); 4362 for (String road : roads) { 4363 Element eRoad = new Element(Xml.CAR_ROAD); 4364 eRoad.setAttribute(Xml.NAME, road); 4365 eRoads.addContent(eRoad); 4366 } 4367 e.addContent(eRoads); 4368 } 4369 // save list of caboose roads for this train 4370 if (!getCabooseRoadOption().equals(ALL_ROADS)) { 4371 e.setAttribute(Xml.CABOOSE_ROAD_OPTION, getCabooseRoadOption()); 4372 String[] roads = getCabooseRoadNames(); 4373 // new way of saving road names 4374 Element eRoads = new Element(Xml.CABOOSE_ROADS); 4375 for (String road : roads) { 4376 Element eRoad = new Element(Xml.CAR_ROAD); 4377 eRoad.setAttribute(Xml.NAME, road); 4378 eRoads.addContent(eRoad); 4379 } 4380 e.addContent(eRoads); 4381 } 4382 // save list of engine roads for this train 4383 if (!getLocoRoadOption().equals(ALL_ROADS)) { 4384 e.setAttribute(Xml.LOCO_ROAD_OPTION, getLocoRoadOption()); 4385 String[] roads = getLocoRoadNames(); 4386 Element eRoads = new Element(Xml.LOCO_ROADS); 4387 for (String road : roads) { 4388 Element eRoad = new Element(Xml.LOCO_ROAD); 4389 eRoad.setAttribute(Xml.NAME, road); 4390 eRoads.addContent(eRoad); 4391 } 4392 e.addContent(eRoads); 4393 } 4394 // save list of car loads for this train 4395 if (!getLoadOption().equals(ALL_LOADS)) { 4396 e.setAttribute(Xml.CAR_LOAD_OPTION, getLoadOption()); 4397 String[] loads = getLoadNames(); 4398 // new way of saving car loads 4399 Element eLoads = new Element(Xml.CAR_LOADS); 4400 for (String load : loads) { 4401 Element eLoad = new Element(Xml.CAR_LOAD); 4402 eLoad.setAttribute(Xml.NAME, load); 4403 eLoads.addContent(eLoad); 4404 } 4405 e.addContent(eLoads); 4406 } 4407 // save list of car owners for this train 4408 if (!getOwnerOption().equals(ALL_OWNERS)) { 4409 e.setAttribute(Xml.CAR_OWNER_OPTION, getOwnerOption()); 4410 String[] owners = getOwnerNames(); 4411 // new way of saving car owners 4412 Element eOwners = new Element(Xml.CAR_OWNERS); 4413 for (String owner : owners) { 4414 Element eOwner = new Element(Xml.CAR_OWNER); 4415 eOwner.setAttribute(Xml.NAME, owner); 4416 eOwners.addContent(eOwner); 4417 } 4418 e.addContent(eOwners); 4419 } 4420 // save list of scripts for this train 4421 if (getBuildScripts().size() > 0 || 4422 getAfterBuildScripts().size() > 0 || 4423 getMoveScripts().size() > 0 || 4424 getTerminationScripts().size() > 0) { 4425 Element es = new Element(Xml.SCRIPTS); 4426 if (getBuildScripts().size() > 0) { 4427 for (String scriptPathname : getBuildScripts()) { 4428 Element em = new Element(Xml.BUILD); 4429 em.setAttribute(Xml.NAME, scriptPathname); 4430 es.addContent(em); 4431 } 4432 } 4433 if (getAfterBuildScripts().size() > 0) { 4434 for (String scriptPathname : getAfterBuildScripts()) { 4435 Element em = new Element(Xml.AFTER_BUILD); 4436 em.setAttribute(Xml.NAME, scriptPathname); 4437 es.addContent(em); 4438 } 4439 } 4440 if (getMoveScripts().size() > 0) { 4441 for (String scriptPathname : getMoveScripts()) { 4442 Element em = new Element(Xml.MOVE); 4443 em.setAttribute(Xml.NAME, scriptPathname); 4444 es.addContent(em); 4445 } 4446 } 4447 // save list of termination scripts for this train 4448 if (getTerminationScripts().size() > 0) { 4449 for (String scriptPathname : getTerminationScripts()) { 4450 Element et = new Element(Xml.TERMINATE); 4451 et.setAttribute(Xml.NAME, scriptPathname); 4452 es.addContent(et); 4453 } 4454 } 4455 e.addContent(es); 4456 } 4457 if (!getRailroadName().equals(NONE)) { 4458 Element r = new Element(Xml.RAIL_ROAD); 4459 r.setAttribute(Xml.NAME, getRailroadName()); 4460 e.addContent(r); 4461 } 4462 if (!getManifestLogoPathName().equals(NONE)) { 4463 Element l = new Element(Xml.MANIFEST_LOGO); 4464 l.setAttribute(Xml.NAME, getManifestLogoPathName()); 4465 e.addContent(l); 4466 } 4467 4468 if (getSecondLegOptions() != NO_CABOOSE_OR_FRED) { 4469 e.setAttribute(Xml.LEG2_OPTIONS, Integer.toString(getSecondLegOptions())); 4470 e.setAttribute(Xml.LEG2_ENGINES, getSecondLegNumberEngines()); 4471 e.setAttribute(Xml.LEG2_ROAD, getSecondLegEngineRoad()); 4472 e.setAttribute(Xml.LEG2_MODEL, getSecondLegEngineModel()); 4473 e.setAttribute(Xml.LEG2_CABOOSE_ROAD, getSecondLegCabooseRoad()); 4474 if (getSecondLegStartRouteLocation() != null) { 4475 e.setAttribute(Xml.LEG2_START, getSecondLegStartRouteLocation().getId()); 4476 } 4477 if (getSecondLegEndRouteLocation() != null) { 4478 e.setAttribute(Xml.LEG2_END, getSecondLegEndRouteLocation().getId()); 4479 } 4480 } 4481 if (getThirdLegOptions() != NO_CABOOSE_OR_FRED) { 4482 e.setAttribute(Xml.LEG3_OPTIONS, Integer.toString(getThirdLegOptions())); 4483 e.setAttribute(Xml.LEG3_ENGINES, getThirdLegNumberEngines()); 4484 e.setAttribute(Xml.LEG3_ROAD, getThirdLegEngineRoad()); 4485 e.setAttribute(Xml.LEG3_MODEL, getThirdLegEngineModel()); 4486 e.setAttribute(Xml.LEG3_CABOOSE_ROAD, getThirdLegCabooseRoad()); 4487 if (getThirdLegStartRouteLocation() != null) { 4488 e.setAttribute(Xml.LEG3_START, getThirdLegStartRouteLocation().getId()); 4489 } 4490 if (getThirdLegEndRouteLocation() != null) { 4491 e.setAttribute(Xml.LEG3_END, getThirdLegEndRouteLocation().getId()); 4492 } 4493 } 4494 return e; 4495 } 4496 4497 @Override 4498 public void propertyChange(java.beans.PropertyChangeEvent e) { 4499 if (Control.SHOW_PROPERTY) { 4500 log.debug("Train ({}) sees property change: ({}) old: ({}) new: ({})", getName(), e.getPropertyName(), 4501 e.getOldValue(), e.getNewValue()); 4502 } 4503 if (e.getPropertyName().equals(Route.DISPOSE)) { 4504 setRoute(null); 4505 } 4506 if (e.getPropertyName().equals(CarTypes.CARTYPES_NAME_CHANGED_PROPERTY) || 4507 e.getPropertyName().equals(CarTypes.CARTYPES_CHANGED_PROPERTY) || 4508 e.getPropertyName().equals(EngineTypes.ENGINETYPES_NAME_CHANGED_PROPERTY)) { 4509 replaceType((String) e.getOldValue(), (String) e.getNewValue()); 4510 } 4511 if (e.getPropertyName().equals(CarRoads.CARROADS_NAME_CHANGED_PROPERTY)) { 4512 replaceRoad((String) e.getOldValue(), (String) e.getNewValue()); 4513 } 4514 if (e.getPropertyName().equals(CarOwners.CAROWNERS_NAME_CHANGED_PROPERTY)) { 4515 replaceOwner((String) e.getOldValue(), (String) e.getNewValue()); 4516 } 4517 if (e.getPropertyName().equals(EngineModels.ENGINEMODELS_NAME_CHANGED_PROPERTY)) { 4518 replaceModel((String) e.getOldValue(), (String) e.getNewValue()); 4519 } 4520 // forward route departure time property changes 4521 if (e.getPropertyName().equals(RouteLocation.DEPARTURE_TIME_CHANGED_PROPERTY)) { 4522 setDirtyAndFirePropertyChange(DEPARTURETIME_CHANGED_PROPERTY, e.getOldValue(), e.getNewValue()); 4523 } 4524 // forward any property changes in this train's route 4525 if (e.getSource().getClass().equals(Route.class)) { 4526 setDirtyAndFirePropertyChange(e.getPropertyName(), e.getOldValue(), e.getNewValue()); 4527 } 4528 } 4529 4530 protected void setDirtyAndFirePropertyChange(String p, Object old, Object n) { 4531 InstanceManager.getDefault(TrainManagerXml.class).setDirty(true); 4532 firePropertyChange(p, old, n); 4533 } 4534 4535 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(Train.class); 4536 4537}