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