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