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