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