001package jmri.jmrit.operations.rollingstock.cars; 002 003import java.beans.PropertyChangeEvent; 004import java.util.ArrayList; 005import java.util.List; 006 007import org.slf4j.Logger; 008import org.slf4j.LoggerFactory; 009 010import jmri.InstanceManager; 011import jmri.jmrit.operations.locations.*; 012import jmri.jmrit.operations.locations.schedules.Schedule; 013import jmri.jmrit.operations.locations.schedules.ScheduleItem; 014import jmri.jmrit.operations.rollingstock.RollingStock; 015import jmri.jmrit.operations.routes.RouteLocation; 016import jmri.jmrit.operations.trains.schedules.TrainSchedule; 017import jmri.jmrit.operations.trains.schedules.TrainScheduleManager; 018import jmri.jmrit.operations.trains.trainbuilder.TrainCommon; 019 020/** 021 * Represents a car on the layout 022 * 023 * @author Daniel Boudreau Copyright (C) 2008, 2009, 2010, 2012, 2013, 2014, 024 * 2015, 2023, 2025 025 */ 026public class Car extends RollingStock { 027 028 CarLoads carLoads = InstanceManager.getDefault(CarLoads.class); 029 030 protected boolean _passenger = false; 031 protected boolean _hazardous = false; 032 protected boolean _caboose = false; 033 protected boolean _fred = false; 034 protected boolean _utility = false; 035 protected boolean _loadGeneratedByStaging = false; 036 protected Kernel _kernel = null; 037 protected String _loadName = carLoads.getDefaultEmptyName(); 038 protected int _wait = 0; 039 040 protected Location _rweDestination = null; // return when empty destination 041 protected Track _rweDestTrack = null; // return when empty track 042 protected String _rweLoadName = carLoads.getDefaultEmptyName(); 043 044 protected Location _rwlDestination = null; // return when loaded destination 045 protected Track _rwlDestTrack = null; // return when loaded track 046 protected String _rwlLoadName = carLoads.getDefaultLoadName(); 047 048 // schedule items 049 protected String _scheduleId = NONE; // the schedule id assigned to this car 050 protected String _nextLoadName = NONE; // next load by schedule 051 protected Location _finalDestination = null; 052 protected Track _finalDestTrack = null; // final track by schedule or router 053 protected Location _previousFinalDestination = null; 054 protected Track _previousFinalDestTrack = null; 055 protected String _previousScheduleId = NONE; 056 protected String _pickupScheduleId = NONE; 057 058 protected String _routePath = NONE; 059 060 public static final String EXTENSION_REGEX = " "; 061 public static final String CABOOSE_EXTENSION = Bundle.getMessage("(C)"); 062 public static final String FRED_EXTENSION = Bundle.getMessage("(F)"); 063 public static final String PASSENGER_EXTENSION = Bundle.getMessage("(P)"); 064 public static final String UTILITY_EXTENSION = Bundle.getMessage("(U)"); 065 public static final String HAZARDOUS_EXTENSION = Bundle.getMessage("(H)"); 066 067 public static final String LOAD_CHANGED_PROPERTY = "Car load changed"; // NOI18N 068 public static final String RWE_LOAD_CHANGED_PROPERTY = "Car RWE load changed"; // NOI18N 069 public static final String RWL_LOAD_CHANGED_PROPERTY = "Car RWL load changed"; // NOI18N 070 public static final String WAIT_CHANGED_PROPERTY = "Car wait changed"; // NOI18N 071 public static final String FINAL_DESTINATION_CHANGED_PROPERTY = "Car final destination changed"; // NOI18N 072 public static final String FINAL_DESTINATION_TRACK_CHANGED_PROPERTY = "Car final destination track changed"; // NOI18N 073 public static final String RETURN_WHEN_EMPTY_CHANGED_PROPERTY = "Car return when empty changed"; // NOI18N 074 public static final String RETURN_WHEN_LOADED_CHANGED_PROPERTY = "Car return when loaded changed"; // NOI18N 075 public static final String SCHEDULE_ID_CHANGED_PROPERTY = "car schedule id changed"; // NOI18N 076 public static final String KERNEL_NAME_CHANGED_PROPERTY = "kernel name changed"; // NOI18N 077 078 public Car() { 079 super(); 080 loaded = true; 081 } 082 083 public Car(String road, String number) { 084 super(road, number); 085 loaded = true; 086 log.debug("New car ({} {})", road, number); 087 addPropertyChangeListeners(); 088 } 089 090 @Override 091 public Car copy() { 092 Car car = new Car(); 093 super.copy(car); 094 car.setLoadName(getLoadName()); 095 car.setReturnWhenEmptyLoadName(getReturnWhenEmptyLoadName()); 096 car.setReturnWhenLoadedLoadName(getReturnWhenLoadedLoadName()); 097 car.setCarHazardous(isCarHazardous()); 098 car.setCaboose(isCaboose()); 099 car.setFred(hasFred()); 100 car.setPassenger(isPassenger()); 101 car.setLoadGeneratedFromStaging(isLoadGeneratedFromStaging()); 102 car.loaded = true; 103 return car; 104 } 105 106 public void setCarHazardous(boolean hazardous) { 107 boolean old = _hazardous; 108 _hazardous = hazardous; 109 if (!old == hazardous) { 110 setDirtyAndFirePropertyChange("car hazardous", old ? "true" : "false", hazardous ? "true" : "false"); // NOI18N 111 } 112 } 113 114 public boolean isCarHazardous() { 115 return _hazardous; 116 } 117 118 public boolean isCarLoadHazardous() { 119 return carLoads.isHazardous(getTypeName(), getLoadName()); 120 } 121 122 /** 123 * Used to determine if the car is hazardous or the car's load is hazardous. 124 * 125 * @return true if the car or car's load is hazardous. 126 */ 127 public boolean isHazardous() { 128 return isCarHazardous() || isCarLoadHazardous(); 129 } 130 131 public void setPassenger(boolean passenger) { 132 boolean old = _passenger; 133 _passenger = passenger; 134 if (!old == passenger) { 135 setDirtyAndFirePropertyChange("car passenger", old ? "true" : "false", passenger ? "true" : "false"); // NOI18N 136 } 137 } 138 139 public boolean isPassenger() { 140 return _passenger; 141 } 142 143 public void setFred(boolean fred) { 144 boolean old = _fred; 145 _fred = fred; 146 if (!old == fred) { 147 setDirtyAndFirePropertyChange("car has fred", old ? "true" : "false", fred ? "true" : "false"); // NOI18N 148 } 149 } 150 151 /** 152 * Used to determine if car has FRED (Flashing Rear End Device). 153 * 154 * @return true if car has FRED. 155 */ 156 public boolean hasFred() { 157 return _fred; 158 } 159 160 public void setLoadName(String load) { 161 String old = _loadName; 162 _loadName = load; 163 if (!old.equals(load)) { 164 setDirtyAndFirePropertyChange(LOAD_CHANGED_PROPERTY, old, load); 165 } 166 } 167 168 /** 169 * The load name assigned to this car. 170 * 171 * @return The load name assigned to this car. 172 */ 173 public String getLoadName() { 174 return _loadName; 175 } 176 177 public void setReturnWhenEmptyLoadName(String load) { 178 String old = _rweLoadName; 179 _rweLoadName = load; 180 if (!old.equals(load)) { 181 setDirtyAndFirePropertyChange(RWE_LOAD_CHANGED_PROPERTY, old, load); 182 } 183 } 184 185 public String getReturnWhenEmptyLoadName() { 186 return _rweLoadName; 187 } 188 189 public void setReturnWhenLoadedLoadName(String load) { 190 String old = _rwlLoadName; 191 _rwlLoadName = load; 192 if (!old.equals(load)) { 193 setDirtyAndFirePropertyChange(RWL_LOAD_CHANGED_PROPERTY, old, load); 194 } 195 } 196 197 public String getReturnWhenLoadedLoadName() { 198 return _rwlLoadName; 199 } 200 201 /** 202 * Gets the car's load's priority. 203 * 204 * @return The car's load priority. 205 */ 206 public String getLoadPriority() { 207 return (carLoads.getPriority(getTypeName(), getLoadName())); 208 } 209 210 /** 211 * Gets the car load's type, empty or load. 212 * 213 * @return type empty or type load 214 */ 215 public String getLoadType() { 216 return (carLoads.getLoadType(getTypeName(), getLoadName())); 217 } 218 219 public String getPickupComment() { 220 return carLoads.getPickupComment(getTypeName(), getLoadName()); 221 } 222 223 public String getDropComment() { 224 return carLoads.getDropComment(getTypeName(), getLoadName()); 225 } 226 227 public void setLoadGeneratedFromStaging(boolean fromStaging) { 228 _loadGeneratedByStaging = fromStaging; 229 } 230 231 public boolean isLoadGeneratedFromStaging() { 232 return _loadGeneratedByStaging; 233 } 234 235 /** 236 * Used to keep track of which item in a schedule was used for this car. 237 * 238 * @param id The ScheduleItem id for this car. 239 */ 240 public void setScheduleItemId(String id) { 241 log.debug("Set schedule item id ({}) for car ({})", id, toString()); 242 String old = _scheduleId; 243 _scheduleId = id; 244 if (!old.equals(id)) { 245 setDirtyAndFirePropertyChange(SCHEDULE_ID_CHANGED_PROPERTY, old, id); 246 } 247 } 248 249 public String getScheduleItemId() { 250 return _scheduleId; 251 } 252 253 public ScheduleItem getScheduleItem(Track track) { 254 ScheduleItem si = null; 255 // arrived at spur? 256 if (track != null && track.isSpur() && !getScheduleItemId().equals(NONE)) { 257 Schedule sch = track.getSchedule(); 258 if (sch == null) { 259 log.error("Schedule null for car ({}) at spur ({})", toString(), track.getName()); 260 } else { 261 si = sch.getItemById(getScheduleItemId()); 262 } 263 } 264 return si; 265 } 266 267 /** 268 * Only here for backwards compatibility before version 5.1.4. The next load 269 * name for this car. Normally set by a schedule. 270 * 271 * @param load the next load name. 272 */ 273 public void setNextLoadName(String load) { 274 String old = _nextLoadName; 275 _nextLoadName = load; 276 if (!old.equals(load)) { 277 setDirtyAndFirePropertyChange(LOAD_CHANGED_PROPERTY, old, load); 278 } 279 } 280 281 public String getNextLoadName() { 282 return _nextLoadName; 283 } 284 285 @Override 286 public String getWeightTons() { 287 String weight = super.getWeightTons(); 288 if (!_weightTons.equals(DEFAULT_WEIGHT)) { 289 return weight; 290 } 291 if (!isCaboose() && !isPassenger()) { 292 return weight; 293 } 294 // .9 tons/foot for caboose and passenger cars 295 try { 296 weight = Integer.toString((int) (Double.parseDouble(getLength()) * .9)); 297 } catch (Exception e) { 298 log.debug("Car ({}) length not set for caboose or passenger car", toString()); 299 } 300 return weight; 301 } 302 303 /** 304 * Returns a car's weight adjusted for load. An empty car's weight is 1/3 305 * the car's loaded weight. 306 */ 307 @Override 308 public int getAdjustedWeightTons() { 309 int weightTons = 0; 310 try { 311 // get loaded weight 312 weightTons = Integer.parseInt(getWeightTons()); 313 // adjust for empty weight if car is empty, 1/3 of loaded weight 314 if (!isCaboose() && !isPassenger() && getLoadType().equals(CarLoad.LOAD_TYPE_EMPTY)) { 315 weightTons = weightTons / 3; 316 } 317 } catch (NumberFormatException e) { 318 log.debug("Car ({}) weight not set", toString()); 319 } 320 return weightTons; 321 } 322 323 public void setWait(int count) { 324 int old = _wait; 325 _wait = count; 326 if (old != count) { 327 setDirtyAndFirePropertyChange(WAIT_CHANGED_PROPERTY, old, count); 328 } 329 } 330 331 public int getWait() { 332 return _wait; 333 } 334 335 /** 336 * Sets when this car will be picked up (day of the week) 337 * 338 * @param id See TrainSchedule.java 339 */ 340 public void setPickupScheduleId(String id) { 341 String old = _pickupScheduleId; 342 _pickupScheduleId = id; 343 if (!old.equals(id)) { 344 setDirtyAndFirePropertyChange("car pickup schedule changes", old, id); // NOI18N 345 } 346 } 347 348 public String getPickupScheduleId() { 349 return _pickupScheduleId; 350 } 351 352 public String getPickupScheduleName() { 353 if (getTrain() != null) { 354 return getPickupTime(); 355 } 356 TrainSchedule sch = InstanceManager.getDefault(TrainScheduleManager.class) 357 .getScheduleById(getPickupScheduleId()); 358 if (sch != null) { 359 return sch.getName(); 360 } 361 return NONE; 362 } 363 364 /** 365 * Sets the final destination for a car. 366 * 367 * @param destination The final destination for this car. 368 */ 369 public void setFinalDestination(Location destination) { 370 Location old = _finalDestination; 371 if (old != null) { 372 old.removePropertyChangeListener(this); 373 } 374 _finalDestination = destination; 375 if (_finalDestination != null) { 376 _finalDestination.addPropertyChangeListener(this); 377 } 378 if ((old != null && !old.equals(destination)) || (destination != null && !destination.equals(old))) { 379 setRoutePath(NONE); 380 setDirtyAndFirePropertyChange(FINAL_DESTINATION_CHANGED_PROPERTY, old, destination); 381 } 382 } 383 384 public Location getFinalDestination() { 385 return _finalDestination; 386 } 387 388 public String getFinalDestinationName() { 389 if (getFinalDestination() != null) { 390 return getFinalDestination().getName(); 391 } 392 return NONE; 393 } 394 395 public String getSplitFinalDestinationName() { 396 return TrainCommon.splitString(getFinalDestinationName()); 397 } 398 399 public void setFinalDestinationTrack(Track track) { 400 Track old = _finalDestTrack; 401 _finalDestTrack = track; 402 if ((old != null && !old.equals(track)) || (track != null && !track.equals(old))) { 403 if (old != null) { 404 old.removePropertyChangeListener(this); 405 old.deleteReservedInRoute(this); 406 } 407 if (_finalDestTrack != null) { 408 _finalDestTrack.addReservedInRoute(this); 409 _finalDestTrack.addPropertyChangeListener(this); 410 } 411 setDirtyAndFirePropertyChange(FINAL_DESTINATION_TRACK_CHANGED_PROPERTY, old, track); 412 } 413 } 414 415 public Track getFinalDestinationTrack() { 416 return _finalDestTrack; 417 } 418 419 public String getFinalDestinationTrackName() { 420 if (getFinalDestinationTrack() != null) { 421 return getFinalDestinationTrack().getName(); 422 } 423 return NONE; 424 } 425 426 public String getSplitFinalDestinationTrackName() { 427 return TrainCommon.splitString(getFinalDestinationTrackName()); 428 } 429 430 public void setPreviousFinalDestination(Location location) { 431 _previousFinalDestination = location; 432 } 433 434 public Location getPreviousFinalDestination() { 435 return _previousFinalDestination; 436 } 437 438 public String getPreviousFinalDestinationName() { 439 if (getPreviousFinalDestination() != null) { 440 return getPreviousFinalDestination().getName(); 441 } 442 return NONE; 443 } 444 445 public void setPreviousFinalDestinationTrack(Track track) { 446 _previousFinalDestTrack = track; 447 } 448 449 public Track getPreviousFinalDestinationTrack() { 450 return _previousFinalDestTrack; 451 } 452 453 public String getPreviousFinalDestinationTrackName() { 454 if (getPreviousFinalDestinationTrack() != null) { 455 return getPreviousFinalDestinationTrack().getName(); 456 } 457 return NONE; 458 } 459 460 public void setPreviousScheduleId(String id) { 461 _previousScheduleId = id; 462 } 463 464 public String getPreviousScheduleId() { 465 return _previousScheduleId; 466 } 467 468 public void setReturnWhenEmptyDestination(Location destination) { 469 Location old = _rweDestination; 470 _rweDestination = destination; 471 if ((old != null && !old.equals(destination)) || (destination != null && !destination.equals(old))) { 472 setDirtyAndFirePropertyChange(RETURN_WHEN_EMPTY_CHANGED_PROPERTY, null, null); 473 } 474 } 475 476 public Location getReturnWhenEmptyDestination() { 477 return _rweDestination; 478 } 479 480 public String getReturnWhenEmptyDestinationName() { 481 if (getReturnWhenEmptyDestination() != null) { 482 return getReturnWhenEmptyDestination().getName(); 483 } 484 return NONE; 485 } 486 487 public String getSplitReturnWhenEmptyDestinationName() { 488 return TrainCommon.splitString(getReturnWhenEmptyDestinationName()); 489 } 490 491 public void setReturnWhenEmptyDestTrack(Track track) { 492 Track old = _rweDestTrack; 493 _rweDestTrack = track; 494 if ((old != null && !old.equals(track)) || (track != null && !track.equals(old))) { 495 setDirtyAndFirePropertyChange(RETURN_WHEN_EMPTY_CHANGED_PROPERTY, null, null); 496 } 497 } 498 499 public Track getReturnWhenEmptyDestTrack() { 500 return _rweDestTrack; 501 } 502 503 public String getReturnWhenEmptyDestTrackName() { 504 if (getReturnWhenEmptyDestTrack() != null) { 505 return getReturnWhenEmptyDestTrack().getName(); 506 } 507 return NONE; 508 } 509 510 public String getSplitReturnWhenEmptyDestinationTrackName() { 511 return TrainCommon.splitString(getReturnWhenEmptyDestTrackName()); 512 } 513 514 public void setReturnWhenLoadedDestination(Location destination) { 515 Location old = _rwlDestination; 516 _rwlDestination = destination; 517 if ((old != null && !old.equals(destination)) || (destination != null && !destination.equals(old))) { 518 setDirtyAndFirePropertyChange(RETURN_WHEN_LOADED_CHANGED_PROPERTY, null, null); 519 } 520 } 521 522 public Location getReturnWhenLoadedDestination() { 523 return _rwlDestination; 524 } 525 526 public String getReturnWhenLoadedDestinationName() { 527 if (getReturnWhenLoadedDestination() != null) { 528 return getReturnWhenLoadedDestination().getName(); 529 } 530 return NONE; 531 } 532 533 public void setReturnWhenLoadedDestTrack(Track track) { 534 Track old = _rwlDestTrack; 535 _rwlDestTrack = track; 536 if ((old != null && !old.equals(track)) || (track != null && !track.equals(old))) { 537 setDirtyAndFirePropertyChange(RETURN_WHEN_LOADED_CHANGED_PROPERTY, null, null); 538 } 539 } 540 541 public Track getReturnWhenLoadedDestTrack() { 542 return _rwlDestTrack; 543 } 544 545 public String getReturnWhenLoadedDestTrackName() { 546 if (getReturnWhenLoadedDestTrack() != null) { 547 return getReturnWhenLoadedDestTrack().getName(); 548 } 549 return NONE; 550 } 551 552 /** 553 * Used to determine is car has been given a Return When Loaded (RWL) 554 * address or custom load 555 * 556 * @return true if car has RWL 557 */ 558 protected boolean isRwlEnabled() { 559 if (!getReturnWhenLoadedLoadName().equals(carLoads.getDefaultLoadName()) || 560 getReturnWhenLoadedDestination() != null) { 561 return true; 562 } 563 return false; 564 } 565 566 public void setRoutePath(String routePath) { 567 String old = _routePath; 568 _routePath = routePath; 569 if (!old.equals(routePath)) { 570 setDirtyAndFirePropertyChange("Route path change", old, routePath); 571 } 572 } 573 574 public String getRoutePath() { 575 return _routePath; 576 } 577 578 public void setCaboose(boolean caboose) { 579 boolean old = _caboose; 580 _caboose = caboose; 581 if (!old == caboose) { 582 setDirtyAndFirePropertyChange("car is caboose", old ? "true" : "false", caboose ? "true" : "false"); // NOI18N 583 } 584 } 585 586 public boolean isCaboose() { 587 return _caboose; 588 } 589 590 public void setUtility(boolean utility) { 591 boolean old = _utility; 592 _utility = utility; 593 if (!old == utility) { 594 setDirtyAndFirePropertyChange("car is utility", old ? "true" : "false", utility ? "true" : "false"); // NOI18N 595 } 596 } 597 598 public boolean isUtility() { 599 return _utility; 600 } 601 602 /** 603 * Used to determine if car is performing a local move. A local move is when 604 * a car is moved to a different track at the same location. 605 * 606 * @return true if local move 607 */ 608 public boolean isLocalMove() { 609 if (getTrain() == null && getLocation() != null) { 610 return getSplitLocationName().equals(getSplitDestinationName()); 611 } 612 if (getRouteLocation() == null || getRouteDestination() == null) { 613 return false; 614 } 615 if (getRouteLocation().equals(getRouteDestination()) && getTrack() != null) { 616 return true; 617 } 618 if (getTrain().isLocalSwitcher() && 619 getRouteLocation().getSplitName() 620 .equals(getRouteDestination().getSplitName()) && 621 getTrack() != null) { 622 return true; 623 } 624 // look for sequential locations with the "same" name 625 if (getRouteLocation().getSplitName().equals( 626 getRouteDestination().getSplitName()) && getTrain().getRoute() != null) { 627 boolean foundRl = false; 628 for (RouteLocation rl : getTrain().getRoute().getLocationsBySequenceList()) { 629 if (foundRl) { 630 if (getRouteDestination().getSplitName() 631 .equals(rl.getSplitName())) { 632 // user can specify the "same" location two more more 633 // times in a row 634 if (getRouteDestination() != rl) { 635 continue; 636 } else { 637 return true; 638 } 639 } else { 640 return false; 641 } 642 } 643 if (getRouteLocation().equals(rl)) { 644 foundRl = true; 645 } 646 } 647 } 648 return false; 649 } 650 651 /** 652 * A kernel is a group of cars that are switched as a unit. 653 * 654 * @param kernel The assigned Kernel for this car. 655 */ 656 public void setKernel(Kernel kernel) { 657 if (_kernel == kernel) { 658 return; 659 } 660 String old = ""; 661 if (_kernel != null) { 662 old = _kernel.getName(); 663 _kernel.delete(this); 664 } 665 _kernel = kernel; 666 String newName = ""; 667 if (_kernel != null) { 668 _kernel.add(this); 669 newName = _kernel.getName(); 670 } 671 if (!old.equals(newName)) { 672 setDirtyAndFirePropertyChange(KERNEL_NAME_CHANGED_PROPERTY, old, newName); // NOI18N 673 } 674 } 675 676 public Kernel getKernel() { 677 return _kernel; 678 } 679 680 public String getKernelName() { 681 if (_kernel != null) { 682 return _kernel.getName(); 683 } 684 return NONE; 685 } 686 687 /** 688 * Used to determine if car is lead car in a kernel 689 * 690 * @return true if lead car in a kernel 691 */ 692 public boolean isLead() { 693 if (getKernel() != null) { 694 return getKernel().isLead(this); 695 } 696 return false; 697 } 698 699 /** 700 * Updates all cars in a kernel. After the update, the cars will all have 701 * the same final destination, load, and route path. 702 */ 703 public void updateKernel() { 704 if (isLead()) { 705 for (Car car : getKernel().getCars()) { 706 if (car != this) { 707 car.setScheduleItemId(getScheduleItemId()); 708 car.setFinalDestination(getFinalDestination()); 709 car.setFinalDestinationTrack(getFinalDestinationTrack()); 710 car.setLoadGeneratedFromStaging(isLoadGeneratedFromStaging()); 711 car.setRoutePath(getRoutePath()); 712 car.setWait(getWait()); 713 if (carLoads.containsName(car.getTypeName(), getLoadName())) { 714 car.setLoadName(getLoadName()); 715 } else { 716 updateKernelCarCustomLoad(car); 717 } 718 } 719 } 720 } 721 } 722 723 /** 724 * The non-lead car in a kernel can't use the custom load of the lead car. 725 * Determine if car has custom loads, and if the departure and arrival 726 * tracks allows one of the custom loads. 727 * 728 * @param car the non-lead car in a kernel 729 */ 730 private void updateKernelCarCustomLoad(Car car) { 731 // only update car's load if departing staging or spur 732 if (getTrack() != null) { 733 if (getTrack().isStaging() || getTrack().isSpur()) { 734 List<String> carLoadNames = carLoads.getNames(car.getTypeName()); 735 List<String> okLoadNames = new ArrayList<>(); 736 for (String name : carLoadNames) { 737 if (getTrack().isLoadNameAndCarTypeShipped(name, car.getTypeName())) { 738 if (getTrain() != null && !getTrain().isLoadNameAccepted(name, car.getTypeName())) { 739 continue; // load not carried by train 740 } 741 if (getFinalDestinationTrack() != null && 742 getDestinationTrack() != null && 743 !getDestinationTrack().isSpur()) { 744 if (getFinalDestinationTrack().isLoadNameAndCarTypeAccepted(name, car.getTypeName())) { 745 okLoadNames.add(name); 746 } 747 } else if (getDestinationTrack() != null && 748 getDestinationTrack().isLoadNameAndCarTypeAccepted(name, car.getTypeName())) { 749 okLoadNames.add(name); 750 } 751 } 752 } 753 // remove default names leaving only custom 754 okLoadNames.remove(carLoads.getDefaultEmptyName()); 755 okLoadNames.remove(carLoads.getDefaultLoadName()); 756 // randomly pick one of the available car loads 757 if (okLoadNames.size() > 0) { 758 int rnd = (int) (Math.random() * okLoadNames.size()); 759 car.setLoadName(okLoadNames.get(rnd)); 760 } else { 761 log.debug("Car ({}) in kernel ({}) leaving staging ({}, {}) with load ({})", car.toString(), 762 getKernelName(), getLocationName(), getTrackName(), car.getLoadName()); 763 } 764 } 765 } 766 } 767 768 /** 769 * Returns the car length or the length of the car's kernel including 770 * couplers. 771 * 772 * @return length of car or kernel 773 */ 774 public int getTotalKernelLength() { 775 if (getKernel() != null) { 776 return getKernel().getTotalLength(); 777 } 778 return getTotalLength(); 779 } 780 781 /** 782 * Used to determine if a car can be set out at a destination (location). 783 * Track is optional. In addition to all of the tests that checkDestination 784 * performs, spurs with schedules are also checked. 785 * 786 * @return status OKAY, TYPE, ROAD, LENGTH, ERROR_TRACK, CAPACITY, SCHEDULE, 787 * CUSTOM 788 */ 789 @Override 790 public String checkDestination(Location destination, Track track) { 791 String status = super.checkDestination(destination, track); 792 if (!status.equals(Track.OKAY) && !status.startsWith(Track.LENGTH)) { 793 return status; 794 } 795 // now check to see if the track has a schedule 796 if (track == null) { 797 return status; 798 } 799 String statusSchedule = track.checkSchedule(this); 800 if (status.startsWith(Track.LENGTH) && statusSchedule.equals(Track.OKAY)) { 801 return status; 802 } 803 return statusSchedule; 804 } 805 806 /** 807 * Sets the car's destination on the layout 808 * 809 * @param track (yard, spur, staging, or interchange track) 810 * @return "okay" if successful, "type" if the rolling stock's type isn't 811 * acceptable, or "length" if the rolling stock length didn't fit, 812 * or Schedule if the destination will not accept the car because 813 * the spur has a schedule and the car doesn't meet the schedule 814 * requirements. Also changes the car load status when the car 815 * reaches its destination. 816 */ 817 @Override 818 public String setDestination(Location destination, Track track) { 819 return setDestination(destination, track, !Car.FORCE); 820 } 821 822 /** 823 * Sets the car's destination on the layout 824 * 825 * @param track (yard, spur, staging, or interchange track) 826 * @param force when true ignore track length, type, and road when setting 827 * destination 828 * @return "okay" if successful, "type" if the rolling stock's type isn't 829 * acceptable, or "length" if the rolling stock length didn't fit, 830 * or Schedule if the destination will not accept the car because 831 * the spur has a schedule and the car doesn't meet the schedule 832 * requirements. Also changes the car load status when the car 833 * reaches its destination. Removes car if clone. 834 */ 835 @Override 836 public String setDestination(Location destination, Track track, boolean force) { 837 // save destination name and track in case car has reached its 838 // destination 839 String destinationName = getDestinationName(); 840 Track destinationTrack = getDestinationTrack(); 841 String status = super.setDestination(destination, track, force); 842 // return if not Okay 843 if (!status.equals(Track.OKAY)) { 844 return status; 845 } 846 // is car going to its final destination? 847 removeCarFinalDestination(); 848 // now check to see if the track has a schedule 849 if (track != null && destinationTrack != track && loaded) { 850 status = track.scheduleNext(this); 851 if (!status.equals(Track.OKAY)) { 852 return status; 853 } 854 } 855 // done? 856 if (destinationName.equals(NONE) || (destination != null) || getTrain() == null) { 857 return status; 858 } 859 // car was in a train and has been dropped off, update load, RWE could 860 // set a new final destination 861 if (isClone()) { 862 // destroy clone 863 InstanceManager.getDefault(KernelManager.class).deleteKernel(getKernelName()); 864 InstanceManager.getDefault(CarManager.class).deregister(this); 865 } else { 866 loadNext(destinationTrack); 867 } 868 return status; 869 } 870 871 /** 872 * Called when setting a car's destination to this spur. Loads the car with 873 * a final destination which is the ship address for the schedule item. 874 * 875 * @param scheduleItem The schedule item to be applied this this car 876 */ 877 public void loadCarFinalDestination(ScheduleItem scheduleItem) { 878 if (scheduleItem != null) { 879 // set the car's final destination and track 880 setFinalDestination(scheduleItem.getDestination()); 881 setFinalDestinationTrack(scheduleItem.getDestinationTrack()); 882 // set all cars in kernel same final destination 883 updateKernel(); 884 } 885 } 886 887 /* 888 * remove the car's final destination if sent to that destination 889 */ 890 private void removeCarFinalDestination() { 891 if (getDestination() != null && 892 getDestination().equals(getFinalDestination()) && 893 getDestinationTrack() != null && 894 (getDestinationTrack().equals(getFinalDestinationTrack()) || 895 getFinalDestinationTrack() == null)) { 896 setFinalDestination(null); 897 setFinalDestinationTrack(null); 898 } 899 } 900 901 /** 902 * Called when car is delivered to track. Updates the car's wait, pickup 903 * day, and load if spur. If staging, can swap default loads, force load to 904 * default empty, or replace custom loads with the default empty load. Can 905 * trigger RWE or RWL. 906 * 907 * @param track the destination track for this car 908 */ 909 public void loadNext(Track track) { 910 setLoadGeneratedFromStaging(false); 911 if (track != null) { 912 if (track.isSpur()) { 913 ScheduleItem si = getScheduleItem(track); 914 if (si == null) { 915 log.debug("Schedule item ({}) is null for car ({}) at spur ({})", getScheduleItemId(), toString(), 916 track.getName()); 917 } else { 918 setWait(si.getWait()); 919 setPickupScheduleId(si.getPickupTrainScheduleId()); 920 } 921 updateLoad(track); 922 } 923 // update load optionally when car reaches staging 924 else if (track.isStaging()) { 925 if (track.isLoadSwapEnabled() && getLoadName().equals(carLoads.getDefaultEmptyName())) { 926 setLoadLoaded(); 927 } else if ((track.isLoadSwapEnabled() || track.isLoadEmptyEnabled()) && 928 getLoadName().equals(carLoads.getDefaultLoadName())) { 929 setLoadEmpty(); 930 } else if (track.isRemoveCustomLoadsEnabled() && 931 !getLoadName().equals(carLoads.getDefaultEmptyName()) && 932 !getLoadName().equals(carLoads.getDefaultLoadName())) { 933 // remove this car's final destination if it has one 934 setFinalDestination(null); 935 setFinalDestinationTrack(null); 936 if (getLoadType().equals(CarLoad.LOAD_TYPE_EMPTY) && isRwlEnabled()) { 937 setLoadLoaded(); 938 // car arriving into staging with the RWE load? 939 } else if (getLoadName().equals(getReturnWhenEmptyLoadName())) { 940 setLoadName(carLoads.getDefaultEmptyName()); 941 } else { 942 setLoadEmpty(); // note that RWE sets the car's final 943 // destination 944 } 945 } 946 } 947 } 948 } 949 950 /** 951 * Updates a car's load when placed at a spur. Load change delayed if wait 952 * count is greater than zero. 953 * 954 * @param track The spur the car is sitting on 955 */ 956 public void updateLoad(Track track) { 957 if (track.isDisableLoadChangeEnabled()) { 958 return; 959 } 960 if (getWait() > 0) { 961 return; // change load name when wait count reaches 0 962 } 963 // arriving at spur with a schedule? 964 String loadName = NONE; 965 ScheduleItem si = getScheduleItem(track); 966 if (si != null) { 967 loadName = si.getShipLoadName(); // can be NONE 968 } else { 969 // for backwards compatibility before version 5.1.4 970 log.debug("Schedule item ({}) is null for car ({}) at spur ({}), using next load name", getScheduleItemId(), 971 toString(), track.getName()); 972 loadName = getNextLoadName(); 973 } 974 setNextLoadName(NONE); // never used again 975 // car could be part of a kernel 976 if (getKernel() != null && !carLoads.containsName(getTypeName(), loadName)) { 977 loadName = NONE; 978 } 979 if (!loadName.equals(NONE)) { 980 setLoadName(loadName); 981 if (getLoadName().equals(getReturnWhenEmptyLoadName())) { 982 setReturnWhenEmpty(); 983 } else if (getLoadName().equals(getReturnWhenLoadedLoadName())) { 984 setReturnWhenLoaded(); 985 } 986 } else { 987 // flip load names 988 if (getLoadType().equals(CarLoad.LOAD_TYPE_EMPTY)) { 989 setLoadLoaded(); 990 } else { 991 setLoadEmpty(); 992 } 993 } 994 loadCarFinalDestination(si); 995 setScheduleItemId(Car.NONE); 996 } 997 998 /** 999 * Sets the car's load to empty, triggers RWE load and destination if 1000 * enabled. 1001 */ 1002 private void setLoadEmpty() { 1003 if (!getLoadName().equals(getReturnWhenEmptyLoadName())) { 1004 setLoadName(getReturnWhenEmptyLoadName()); // default RWE load is 1005 // the "E" load 1006 setReturnWhenEmpty(); 1007 } 1008 } 1009 1010 /* 1011 * Don't set return address if in staging with the same RWE address and 1012 * don't set return address if at the RWE address 1013 */ 1014 private void setReturnWhenEmpty() { 1015 if (getFinalDestination() == null && 1016 getReturnWhenEmptyDestination() != null && 1017 (getLocation() != getReturnWhenEmptyDestination() || 1018 (!getReturnWhenEmptyDestination().isStaging() && 1019 getTrack() != getReturnWhenEmptyDestTrack()))) { 1020 setFinalDestination(getReturnWhenEmptyDestination()); 1021 setFinalDestinationTrack(getReturnWhenEmptyDestTrack()); 1022 log.debug("Car ({}) has return when empty destination ({}, {}) load {}", toString(), 1023 getFinalDestinationName(), getFinalDestinationTrackName(), getLoadName()); 1024 } 1025 } 1026 1027 /** 1028 * Sets the car's load to loaded, triggers RWL load and destination if 1029 * enabled. 1030 */ 1031 private void setLoadLoaded() { 1032 if (!getLoadName().equals(getReturnWhenLoadedLoadName())) { 1033 setLoadName(getReturnWhenLoadedLoadName()); // default RWL load is 1034 // the "L" load 1035 setReturnWhenLoaded(); 1036 } 1037 } 1038 1039 /* 1040 * Don't set return address if in staging with the same RWL address and 1041 * don't set return address if at the RWL address 1042 */ 1043 private void setReturnWhenLoaded() { 1044 if (getFinalDestination() == null && 1045 getReturnWhenLoadedDestination() != null && 1046 (getLocation() != getReturnWhenLoadedDestination() || 1047 (!getReturnWhenLoadedDestination().isStaging() && 1048 getTrack() != getReturnWhenLoadedDestTrack()))) { 1049 setFinalDestination(getReturnWhenLoadedDestination()); 1050 setFinalDestinationTrack(getReturnWhenLoadedDestTrack()); 1051 log.debug("Car ({}) has return when loaded destination ({}, {}) load {}", toString(), 1052 getFinalDestinationName(), getFinalDestinationTrackName(), getLoadName()); 1053 } 1054 } 1055 1056 public String getTypeExtensions() { 1057 StringBuffer buf = new StringBuffer(); 1058 if (isCaboose()) { 1059 buf.append(EXTENSION_REGEX + CABOOSE_EXTENSION); 1060 } 1061 if (hasFred()) { 1062 buf.append(EXTENSION_REGEX + FRED_EXTENSION); 1063 } 1064 if (isPassenger()) { 1065 buf.append(EXTENSION_REGEX + PASSENGER_EXTENSION + EXTENSION_REGEX + getBlocking()); 1066 } 1067 if (isUtility()) { 1068 buf.append(EXTENSION_REGEX + UTILITY_EXTENSION); 1069 } 1070 if (isCarHazardous()) { 1071 buf.append(EXTENSION_REGEX + HAZARDOUS_EXTENSION); 1072 } 1073 return buf.toString(); 1074 } 1075 1076 @Override 1077 public void reset() { 1078 setScheduleItemId(getPreviousScheduleId()); // revert to previous 1079 setNextLoadName(NONE); 1080 setFinalDestination(getPreviousFinalDestination()); 1081 setFinalDestinationTrack(getPreviousFinalDestinationTrack()); 1082 if (isLoadGeneratedFromStaging()) { 1083 setLoadGeneratedFromStaging(false); 1084 setLoadName(carLoads.getDefaultEmptyName()); 1085 } 1086 super.reset(); 1087 destroyClone(); 1088 } 1089 1090 /* 1091 * This routine destroys the clone and restores the cloned car to its 1092 * original location and settings. Note there can be multiple clones for a 1093 * car. A clone has uses the original car's road, number, and the creation 1094 * order number which is appended to the road number using the CLONE_REGEX 1095 */ 1096 private void destroyClone() { 1097 if (isClone()) { 1098 // move cloned car back to original location 1099 CarManager carManager = InstanceManager.getDefault(CarManager.class); 1100 // get the original car's road and number 1101 String[] number = getNumber().split(Car.CLONE_REGEX); 1102 Car car = carManager.getByRoadAndNumber(getRoadName(), number[0]); 1103 if (car != null) { 1104 int cloneCreationNumber = Integer.parseInt(number[1]); 1105 if (cloneCreationNumber <= car.getCloneOrder()) { 1106 // move car back and restore 1107 destroyCloneReset(car); 1108 car.setLoadName(getLoadName()); 1109 car.setFinalDestination(getPreviousFinalDestination()); 1110 car.setFinalDestinationTrack(getPreviousFinalDestinationTrack()); 1111 car.setPreviousFinalDestination(getPreviousFinalDestination()); 1112 car.setPreviousFinalDestinationTrack(getPreviousFinalDestinationTrack()); 1113 car.setScheduleItemId(getPreviousScheduleId()); 1114 car.setWait(0); 1115 // remember the last clone destroyed 1116 car.setCloneOrder(cloneCreationNumber); 1117 } 1118 } else { 1119 log.error("Not able to find and restore car ({}, {})", getRoadName(), number[0]); 1120 } 1121 InstanceManager.getDefault(KernelManager.class).deleteKernel(getKernelName()); 1122 carManager.deregister(this); 1123 } 1124 } 1125 1126 @Override 1127 public void dispose() { 1128 setKernel(null); 1129 setFinalDestination(null); // removes property change listener 1130 setFinalDestinationTrack(null); // removes property change listener 1131 InstanceManager.getDefault(CarTypes.class).removePropertyChangeListener(this); 1132 InstanceManager.getDefault(CarLengths.class).removePropertyChangeListener(this); 1133 super.dispose(); 1134 } 1135 1136 // used to stop a track's schedule from bumping when loading car database 1137 private boolean loaded = false; 1138 1139 /** 1140 * Construct this Entry from XML. This member has to remain synchronized 1141 * with the detailed DTD in operations-cars.dtd 1142 * 1143 * @param e Car XML element 1144 */ 1145 public Car(org.jdom2.Element e) { 1146 super(e); 1147 loaded = true; 1148 org.jdom2.Attribute a; 1149 if ((a = e.getAttribute(Xml.PASSENGER)) != null) { 1150 _passenger = a.getValue().equals(Xml.TRUE); 1151 } 1152 if ((a = e.getAttribute(Xml.HAZARDOUS)) != null) { 1153 _hazardous = a.getValue().equals(Xml.TRUE); 1154 } 1155 if ((a = e.getAttribute(Xml.CABOOSE)) != null) { 1156 _caboose = a.getValue().equals(Xml.TRUE); 1157 } 1158 if ((a = e.getAttribute(Xml.FRED)) != null) { 1159 _fred = a.getValue().equals(Xml.TRUE); 1160 } 1161 if ((a = e.getAttribute(Xml.UTILITY)) != null) { 1162 _utility = a.getValue().equals(Xml.TRUE); 1163 } 1164 if ((a = e.getAttribute(Xml.KERNEL)) != null) { 1165 Kernel k = InstanceManager.getDefault(KernelManager.class).getKernelByName(a.getValue()); 1166 if (k != null) { 1167 setKernel(k); 1168 if ((a = e.getAttribute(Xml.LEAD_KERNEL)) != null && a.getValue().equals(Xml.TRUE)) { 1169 _kernel.setLead(this); 1170 } 1171 } else { 1172 log.error("Kernel {} does not exist", a.getValue()); 1173 } 1174 } 1175 if ((a = e.getAttribute(Xml.LOAD)) != null) { 1176 _loadName = a.getValue(); 1177 } 1178 if ((a = e.getAttribute(Xml.LOAD_FROM_STAGING)) != null && a.getValue().equals(Xml.TRUE)) { 1179 setLoadGeneratedFromStaging(true); 1180 } 1181 if ((a = e.getAttribute(Xml.WAIT)) != null) { 1182 try { 1183 _wait = Integer.parseInt(a.getValue()); 1184 } catch (NumberFormatException nfe) { 1185 log.error("Wait count ({}) for car ({}) isn't a valid number!", a.getValue(), toString()); 1186 } 1187 } 1188 if ((a = e.getAttribute(Xml.PICKUP_SCHEDULE_ID)) != null) { 1189 _pickupScheduleId = a.getValue(); 1190 } 1191 if ((a = e.getAttribute(Xml.SCHEDULE_ID)) != null) { 1192 _scheduleId = a.getValue(); 1193 } 1194 // for backwards compatibility before version 5.1.4 1195 if ((a = e.getAttribute(Xml.NEXT_LOAD)) != null) { 1196 _nextLoadName = a.getValue(); 1197 } 1198 if ((a = e.getAttribute(Xml.NEXT_DEST_ID)) != null) { 1199 setFinalDestination(InstanceManager.getDefault(LocationManager.class).getLocationById(a.getValue())); 1200 } 1201 if (getFinalDestination() != null && (a = e.getAttribute(Xml.NEXT_DEST_TRACK_ID)) != null) { 1202 setFinalDestinationTrack(getFinalDestination().getTrackById(a.getValue())); 1203 } 1204 if ((a = e.getAttribute(Xml.PREVIOUS_NEXT_DEST_ID)) != null) { 1205 setPreviousFinalDestination( 1206 InstanceManager.getDefault(LocationManager.class).getLocationById(a.getValue())); 1207 } 1208 if (getPreviousFinalDestination() != null && (a = e.getAttribute(Xml.PREVIOUS_NEXT_DEST_TRACK_ID)) != null) { 1209 setPreviousFinalDestinationTrack(getPreviousFinalDestination().getTrackById(a.getValue())); 1210 } 1211 if ((a = e.getAttribute(Xml.PREVIOUS_SCHEDULE_ID)) != null) { 1212 setPreviousScheduleId(a.getValue()); 1213 } 1214 if ((a = e.getAttribute(Xml.RWE_DEST_ID)) != null) { 1215 _rweDestination = InstanceManager.getDefault(LocationManager.class).getLocationById(a.getValue()); 1216 } 1217 if (_rweDestination != null && (a = e.getAttribute(Xml.RWE_DEST_TRACK_ID)) != null) { 1218 _rweDestTrack = _rweDestination.getTrackById(a.getValue()); 1219 } 1220 if ((a = e.getAttribute(Xml.RWE_LOAD)) != null) { 1221 _rweLoadName = a.getValue(); 1222 } 1223 if ((a = e.getAttribute(Xml.RWL_DEST_ID)) != null) { 1224 _rwlDestination = InstanceManager.getDefault(LocationManager.class).getLocationById(a.getValue()); 1225 } 1226 if (_rwlDestination != null && (a = e.getAttribute(Xml.RWL_DEST_TRACK_ID)) != null) { 1227 _rwlDestTrack = _rwlDestination.getTrackById(a.getValue()); 1228 } 1229 if ((a = e.getAttribute(Xml.RWL_LOAD)) != null) { 1230 _rwlLoadName = a.getValue(); 1231 } 1232 if ((a = e.getAttribute(Xml.ROUTE_PATH)) != null) { 1233 _routePath = a.getValue(); 1234 } 1235 addPropertyChangeListeners(); 1236 } 1237 1238 /** 1239 * Create an XML element to represent this Entry. This member has to remain 1240 * synchronized with the detailed DTD in operations-cars.dtd. 1241 * 1242 * @return Contents in a JDOM Element 1243 */ 1244 public org.jdom2.Element store() { 1245 org.jdom2.Element e = new org.jdom2.Element(Xml.CAR); 1246 super.store(e); 1247 if (isPassenger()) { 1248 e.setAttribute(Xml.PASSENGER, isPassenger() ? Xml.TRUE : Xml.FALSE); 1249 } 1250 if (isCarHazardous()) { 1251 e.setAttribute(Xml.HAZARDOUS, isCarHazardous() ? Xml.TRUE : Xml.FALSE); 1252 } 1253 if (isCaboose()) { 1254 e.setAttribute(Xml.CABOOSE, isCaboose() ? Xml.TRUE : Xml.FALSE); 1255 } 1256 if (hasFred()) { 1257 e.setAttribute(Xml.FRED, hasFred() ? Xml.TRUE : Xml.FALSE); 1258 } 1259 if (isUtility()) { 1260 e.setAttribute(Xml.UTILITY, isUtility() ? Xml.TRUE : Xml.FALSE); 1261 } 1262 if (getKernel() != null) { 1263 e.setAttribute(Xml.KERNEL, getKernelName()); 1264 if (isLead()) { 1265 e.setAttribute(Xml.LEAD_KERNEL, Xml.TRUE); 1266 } 1267 } 1268 1269 e.setAttribute(Xml.LOAD, getLoadName()); 1270 1271 if (isLoadGeneratedFromStaging()) { 1272 e.setAttribute(Xml.LOAD_FROM_STAGING, Xml.TRUE); 1273 } 1274 1275 if (getWait() != 0) { 1276 e.setAttribute(Xml.WAIT, Integer.toString(getWait())); 1277 } 1278 1279 if (!getPickupScheduleId().equals(NONE)) { 1280 e.setAttribute(Xml.PICKUP_SCHEDULE_ID, getPickupScheduleId()); 1281 } 1282 1283 if (!getScheduleItemId().equals(NONE)) { 1284 e.setAttribute(Xml.SCHEDULE_ID, getScheduleItemId()); 1285 } 1286 1287 // for backwards compatibility before version 5.1.4 1288 if (!getNextLoadName().equals(NONE)) { 1289 e.setAttribute(Xml.NEXT_LOAD, getNextLoadName()); 1290 } 1291 1292 if (getFinalDestination() != null) { 1293 e.setAttribute(Xml.NEXT_DEST_ID, getFinalDestination().getId()); 1294 if (getFinalDestinationTrack() != null) { 1295 e.setAttribute(Xml.NEXT_DEST_TRACK_ID, getFinalDestinationTrack().getId()); 1296 } 1297 } 1298 1299 if (getPreviousFinalDestination() != null) { 1300 e.setAttribute(Xml.PREVIOUS_NEXT_DEST_ID, getPreviousFinalDestination().getId()); 1301 if (getPreviousFinalDestinationTrack() != null) { 1302 e.setAttribute(Xml.PREVIOUS_NEXT_DEST_TRACK_ID, getPreviousFinalDestinationTrack().getId()); 1303 } 1304 } 1305 1306 if (!getPreviousScheduleId().equals(NONE)) { 1307 e.setAttribute(Xml.PREVIOUS_SCHEDULE_ID, getPreviousScheduleId()); 1308 } 1309 1310 if (getReturnWhenEmptyDestination() != null) { 1311 e.setAttribute(Xml.RWE_DEST_ID, getReturnWhenEmptyDestination().getId()); 1312 if (getReturnWhenEmptyDestTrack() != null) { 1313 e.setAttribute(Xml.RWE_DEST_TRACK_ID, getReturnWhenEmptyDestTrack().getId()); 1314 } 1315 } 1316 if (!getReturnWhenEmptyLoadName().equals(carLoads.getDefaultEmptyName())) { 1317 e.setAttribute(Xml.RWE_LOAD, getReturnWhenEmptyLoadName()); 1318 } 1319 1320 if (getReturnWhenLoadedDestination() != null) { 1321 e.setAttribute(Xml.RWL_DEST_ID, getReturnWhenLoadedDestination().getId()); 1322 if (getReturnWhenLoadedDestTrack() != null) { 1323 e.setAttribute(Xml.RWL_DEST_TRACK_ID, getReturnWhenLoadedDestTrack().getId()); 1324 } 1325 } 1326 if (!getReturnWhenLoadedLoadName().equals(carLoads.getDefaultLoadName())) { 1327 e.setAttribute(Xml.RWL_LOAD, getReturnWhenLoadedLoadName()); 1328 } 1329 1330 if (!getRoutePath().isEmpty()) { 1331 e.setAttribute(Xml.ROUTE_PATH, getRoutePath()); 1332 } 1333 1334 return e; 1335 } 1336 1337 @Override 1338 protected void setDirtyAndFirePropertyChange(String p, Object old, Object n) { 1339 // Set dirty 1340 InstanceManager.getDefault(CarManagerXml.class).setDirty(true); 1341 super.setDirtyAndFirePropertyChange(p, old, n); 1342 } 1343 1344 private void addPropertyChangeListeners() { 1345 InstanceManager.getDefault(CarTypes.class).addPropertyChangeListener(this); 1346 InstanceManager.getDefault(CarLengths.class).addPropertyChangeListener(this); 1347 } 1348 1349 @Override 1350 public void propertyChange(PropertyChangeEvent e) { 1351 super.propertyChange(e); 1352 if (e.getPropertyName().equals(CarTypes.CARTYPES_NAME_CHANGED_PROPERTY)) { 1353 if (e.getOldValue().equals(getTypeName())) { 1354 log.debug("Car ({}) sees type name change old: ({}) new: ({})", toString(), e.getOldValue(), 1355 e.getNewValue()); // NOI18N 1356 setTypeName((String) e.getNewValue()); 1357 } 1358 } 1359 if (e.getPropertyName().equals(CarLengths.CARLENGTHS_NAME_CHANGED_PROPERTY)) { 1360 if (e.getOldValue().equals(getLength())) { 1361 log.debug("Car ({}) sees length name change old: ({}) new: ({})", toString(), e.getOldValue(), 1362 e.getNewValue()); // NOI18N 1363 setLength((String) e.getNewValue()); 1364 } 1365 } 1366 if (e.getPropertyName().equals(Location.DISPOSE_CHANGED_PROPERTY)) { 1367 if (e.getSource() == getFinalDestination()) { 1368 log.debug("delete final destination for car: ({})", toString()); 1369 setFinalDestination(null); 1370 } 1371 } 1372 if (e.getPropertyName().equals(Track.DISPOSE_CHANGED_PROPERTY)) { 1373 if (e.getSource() == getFinalDestinationTrack()) { 1374 log.debug("delete final destination for car: ({})", toString()); 1375 setFinalDestinationTrack(null); 1376 } 1377 } 1378 } 1379 1380 private final static Logger log = LoggerFactory.getLogger(Car.class); 1381 1382}