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