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