001package jmri.jmrit.operations.rollingstock; 002 003import java.beans.PropertyChangeEvent; 004import java.beans.PropertyChangeListener; 005import java.text.SimpleDateFormat; 006import java.util.Date; 007 008import org.slf4j.Logger; 009import org.slf4j.LoggerFactory; 010 011import jmri.*; 012import jmri.beans.Identifiable; 013import jmri.beans.PropertyChangeSupport; 014import jmri.jmrit.operations.locations.*; 015import jmri.jmrit.operations.locations.divisions.Division; 016import jmri.jmrit.operations.locations.divisions.DivisionManager; 017import jmri.jmrit.operations.rollingstock.cars.*; 018import jmri.jmrit.operations.routes.RouteLocation; 019import jmri.jmrit.operations.setup.Setup; 020import jmri.jmrit.operations.trains.Train; 021import jmri.jmrit.operations.trains.TrainManager; 022import jmri.jmrit.operations.trains.trainbuilder.TrainCommon; 023 024/** 025 * Represents rolling stock, both powered (locomotives) and not powered (cars) 026 * on the layout. 027 * 028 * @author Daniel Boudreau Copyright (C) 2009, 2010, 2013, 2023 029 */ 030public abstract class RollingStock extends PropertyChangeSupport implements Identifiable, PropertyChangeListener { 031 032 public static final String NONE = ""; 033 public static final int DEFAULT_BLOCKING_ORDER = 0; 034 public static final int MAX_BLOCKING_ORDER = 100; 035 public static final boolean FORCE = true; // ignore length, type, etc. when setting car's track 036 protected static final String DEFAULT_WEIGHT = "0"; 037 038 protected String _id = NONE; 039 protected String _number = NONE; 040 protected String _road = NONE; 041 protected String _type = NONE; 042 protected String _length = "0"; 043 protected String _color = NONE; 044 protected String _weight = DEFAULT_WEIGHT; 045 protected String _weightTons = DEFAULT_WEIGHT; 046 protected String _built = NONE; 047 protected String _owner = NONE; 048 protected String _comment = NONE; 049 protected String _routeId = NONE; // saved route for interchange tracks 050 protected String _rfid = NONE; 051 protected String _value = NONE; 052 protected Date _lastDate = null; 053 protected boolean _locationUnknown = false; 054 protected boolean _outOfService = false; 055 protected boolean _selected = false; 056 057 protected Location _location = null; 058 protected Track _track = null; 059 protected Location _destination = null; 060 protected Track _trackDestination = null; 061 protected Train _train = null; 062 protected RouteLocation _routeLocation = null; 063 protected RouteLocation _routeDestination = null; 064 protected Division _division = null; 065 protected int _moves = 0; 066 protected String _lastLocationId = LOCATION_UNKNOWN; // the rollingstock's last location id 067 protected String _lastTrackId = LOCATION_UNKNOWN; // the rollingstock's last track id 068 protected Train _lastTrain = null; // the last train moving this rs 069 protected int _blocking = DEFAULT_BLOCKING_ORDER; 070 protected String _pickupTime = NONE; 071 072 protected IdTag _tag = null; 073 protected PropertyChangeListener _tagListener = null; 074 protected Location _whereLastSeen = null; // location reported by tag reader 075 protected Date _whenLastSeen = null; // date reported by tag reader 076 077 public static final String LOCATION_UNKNOWN = "0"; 078 079 protected int number = 0; // used by rolling stock manager for sort by number 080 081 public static final String ERROR_TRACK = "ERROR wrong track for location"; // checks for coding error // NOI18N 082 083 // property changes 084 public static final String TRACK_CHANGED_PROPERTY = "rolling stock track location"; // NOI18N 085 public static final String DESTINATION_TRACK_CHANGED_PROPERTY = "rolling stock track destination"; // NOI18N 086 public static final String TRAIN_CHANGED_PROPERTY = "rolling stock train"; // NOI18N 087 public static final String LENGTH_CHANGED_PROPERTY = "rolling stock length"; // NOI18N 088 public static final String TYPE_CHANGED_PROPERTY = "rolling stock type"; // NOI18N 089 public static final String ROUTE_LOCATION_CHANGED_PROPERTY = "rolling stock route location"; // NOI18N 090 public static final String ROUTE_DESTINATION_CHANGED_PROPERTY = "rolling stock route destination"; // NOI18N 091 public static final String COMMENT_CHANGED_PROPERTY = "rolling stock comment"; // NOI18N 092 093 // the draw bar length must only be calculated once at startup 094 public static final int COUPLERS = Setup.getLengthUnit().equals(Setup.FEET) 095 ? Integer.parseInt(Bundle.getMessage("DrawBarLengthFeet")) 096 : Integer.parseInt(Bundle.getMessage("DrawBarLengthMeter")); // stocks TODO catch empty/non-integer value 097 098 LocationManager locationManager = InstanceManager.getDefault(LocationManager.class); 099 100 public RollingStock() { 101 _lastDate = (new java.util.GregorianCalendar()).getGregorianChange(); // set to change date of the Gregorian 102 // Calendar. 103 } 104 105 public RollingStock(String road, String number) { 106 this(); 107 log.debug("New rolling stock ({} {})", road, number); 108 _road = road; 109 _number = number; 110 _id = createId(road, number); 111 addPropertyChangeListeners(); 112 } 113 114 public static String createId(String road, String number) { 115 return road + number; 116 } 117 118 @Override 119 public String getId() { 120 return _id; 121 } 122 123 /** 124 * Set the rolling stock identification or road number 125 * 126 * @param number The rolling stock road number. 127 * 128 */ 129 public void setNumber(String number) { 130 String oldNumber = _number; 131 _number = number; 132 if (!oldNumber.equals(number)) { 133 firePropertyChange("rolling stock number", oldNumber, number); // NOI18N 134 String oldId = _id; 135 _id = createId(_road, number); 136 setDirtyAndFirePropertyChange(Xml.ID, oldId, _id); 137 } 138 } 139 140 public String getNumber() { 141 return _number; 142 } 143 144 public void setRoadName(String road) { 145 String old = _road; 146 _road = road; 147 if (!old.equals(road)) { 148 firePropertyChange("rolling stock road", old, road); // NOI18N 149 String oldId = _id; 150 _id = createId(road, _number); 151 setDirtyAndFirePropertyChange(Xml.ID, oldId, _id); 152 } 153 } 154 155 public String getRoadName() { 156 return _road; 157 } 158 159 /** 160 * For combobox and identification 161 */ 162 @Override 163 public String toString() { 164 return getRoadName() + " " + getNumber(); 165 } 166 167 /** 168 * Sets the type of rolling stock. "Boxcar" for example is a type of car. 169 * 170 * @param type The type of rolling stock. 171 */ 172 public void setTypeName(String type) { 173 String old = _type; 174 _type = type; 175 if (!old.equals(type)) { 176 setDirtyAndFirePropertyChange("rolling stock type", old, type); // NOI18N 177 } 178 } 179 180 public String getTypeName() { 181 return _type; 182 } 183 184 protected boolean _lengthChange = false; // used for loco length change 185 186 /** 187 * Sets the body length of the rolling stock. For example, a 40' boxcar would be 188 * entered as 40 feet. Coupler length is added by the program when determining 189 * if a car could fit on a track. 190 * 191 * @see #getTotalLength() 192 * @param length the body length in feet or meters 193 */ 194 public void setLength(String length) { 195 String old = _length; 196 if (!old.equals(length)) { 197 // adjust used length if rolling stock is at a location 198 if (getLocation() != null && getTrack() != null) { 199 getLocation().setUsedLength(getLocation().getUsedLength() + Integer.parseInt(length) - Integer.parseInt(old)); 200 getTrack().setUsedLength(getTrack().getUsedLength() + Integer.parseInt(length) - Integer.parseInt(old)); 201 if (getDestination() != null && getDestinationTrack() != null && !_lengthChange) { 202 _lengthChange = true; // prevent recursive loop, and we want the "old" engine length 203 log.debug("Rolling stock ({}) has destination ({}, {})", this, getDestination().getName(), 204 getDestinationTrack().getName()); 205 getTrack().deletePickupRS(this); 206 getDestinationTrack().deleteDropRS(this); 207 // now change the length and update tracks 208 _length = length; 209 getTrack().addPickupRS(this); 210 getDestinationTrack().addDropRS(this); 211 _lengthChange = false; // done 212 } 213 } 214 _length = length; 215 setDirtyAndFirePropertyChange(LENGTH_CHANGED_PROPERTY, old, length); 216 } 217 } 218 219 /** 220 * gets the body length of the rolling stock 221 * 222 * @return length in feet or meters 223 * 224 * @see #getTotalLength() 225 */ 226 public String getLength() { 227 return _length; 228 } 229 230 public int getLengthInteger() { 231 try { 232 return Integer.parseInt(getLength()); 233 } catch (NumberFormatException e) { 234 log.error("Rolling stock ({}) length ({}) is not valid ", this, getLength()); 235 } 236 return 0; 237 } 238 239 /** 240 * Returns the length of the rolling stock including the couplers 241 * 242 * 243 * @return total length of the rolling stock in feet or meters 244 */ 245 public int getTotalLength() { 246 return getLengthInteger() + RollingStock.COUPLERS; 247 } 248 249 public void setColor(String color) { 250 String old = _color; 251 _color = color; 252 if (!old.equals(color)) { 253 setDirtyAndFirePropertyChange("rolling stock color", old, color); // NOI18N 254 } 255 } 256 257 public String getColor() { 258 return _color; 259 } 260 261 /** 262 * 263 * @param weight rolling stock weight in ounces. 264 */ 265 public void setWeight(String weight) { 266 String old = _weight; 267 _weight = weight; 268 if (!old.equals(weight)) { 269 setDirtyAndFirePropertyChange("rolling stock weight", old, weight); // NOI18N 270 } 271 } 272 273 public String getWeight() { 274 return _weight; 275 } 276 277 /** 278 * Sets the full scale weight in tons. 279 * 280 * @param weight full scale rolling stock weight in tons. 281 */ 282 public void setWeightTons(String weight) { 283 String old = _weightTons; 284 _weightTons = weight; 285 if (!old.equals(weight)) { 286 setDirtyAndFirePropertyChange("rolling stock weight tons", old, weight); // NOI18N 287 } 288 } 289 290 public String getWeightTons() { 291 if (!_weightTons.equals(DEFAULT_WEIGHT)) { 292 return _weightTons; 293 } 294 // calculate the ton weight based on actual weight 295 double weight = 0; 296 try { 297 weight = Double.parseDouble(getWeight()); 298 } catch (NumberFormatException e) { 299 log.trace("Weight not set for rolling stock ({})", this); 300 } 301 return Integer.toString((int) (weight * Setup.getScaleTonRatio())); 302 } 303 304 public int getAdjustedWeightTons() { 305 int weightTons = 0; 306 try { 307 // get loaded weight 308 weightTons = Integer.parseInt(getWeightTons()); 309 } catch (NumberFormatException e) { 310 log.debug("Rolling stock ({}) weight not set", this); 311 } 312 return weightTons; 313 } 314 315 /** 316 * Set the date that the rolling stock was built. Use 4 digits for the year, or 317 * the format MM-YY where MM is the two digit month, and YY is the last two 318 * years if the rolling stock was built in the 1900s. Use MM-YYYY for units 319 * build after 1999. 320 * 321 * @param built The string built date. 322 * 323 */ 324 public void setBuilt(String built) { 325 String old = _built; 326 _built = built; 327 if (!old.equals(built)) { 328 setDirtyAndFirePropertyChange("rolling stock built", old, built); // NOI18N 329 } 330 } 331 332 public String getBuilt() { 333 return _built; 334 } 335 336 /** 337 * 338 * @return location unknown symbol, out of service symbol, or none if car okay 339 */ 340 public String getStatus() { 341 return (isLocationUnknown() ? "<?> " : (isOutOfService() ? "<O> " : NONE)); // NOI18N 342 } 343 344 public Location getLocation() { 345 return _location; 346 } 347 348 /** 349 * Get rolling stock's location name 350 * 351 * @return empty string if rolling stock isn't on layout 352 */ 353 public String getLocationName() { 354 if (getLocation() != null) { 355 return getLocation().getName(); 356 } 357 return NONE; 358 } 359 360 public String getSplitLocationName() { 361 return TrainCommon.splitString(getLocationName()); 362 } 363 364 /** 365 * Get rolling stock's location id 366 * 367 * @return empty string if rolling stock isn't on the layout 368 */ 369 public String getLocationId() { 370 if (getLocation() != null) { 371 return getLocation().getId(); 372 } 373 return NONE; 374 } 375 376 public Track getTrack() { 377 return _track; 378 } 379 380 /** 381 * Set the rolling stock's location and track. Doesn't do any checking and does 382 * not fire a property change. Used exclusively by the Router code. Use 383 * setLocation(Location, Track) instead. 384 * 385 * @param track to place the rolling stock on. 386 */ 387 public void setTrack(Track track) { 388 if (track != null) { 389 _location = track.getLocation(); 390 } 391 _track = track; 392 } 393 394 /** 395 * Get rolling stock's track name 396 * 397 * @return empty string if rolling stock isn't on a track 398 */ 399 public String getTrackName() { 400 if (getTrack() != null) { 401 return getTrack().getName(); 402 } 403 return NONE; 404 } 405 406 public String getSplitTrackName() { 407 return TrainCommon.splitString(getTrackName()); 408 } 409 410 public String getTrackType() { 411 if (getTrack() != null) { 412 return getTrack().getTrackTypeName(); 413 } 414 return NONE; 415 } 416 417 /** 418 * Get rolling stock's track id 419 * 420 * @return empty string if rolling stock isn't on a track 421 */ 422 public String getTrackId() { 423 if (getTrack() != null) { 424 return getTrack().getId(); 425 } 426 return NONE; 427 } 428 429 /** 430 * Sets rolling stock location on the layout 431 * 432 * @param location The Location. 433 * @param track (yard, spur, staging, or interchange track) 434 * @return "okay" if successful, "type" if the rolling stock's type isn't 435 * acceptable, "road" if rolling stock road isn't acceptable, or 436 * "length" if the rolling stock length didn't fit. 437 */ 438 public String setLocation(Location location, Track track) { 439 return setLocation(location, track, !FORCE); // don't force 440 } 441 442 /** 443 * Sets rolling stock location on the layout 444 * 445 * @param location The Location. 446 * @param track (yard, spur, staging, or interchange track) 447 * @param force when true place rolling stock ignore track length, type, and 448 * road 449 * @return "okay" if successful, "type" if the rolling stock's type isn't 450 * acceptable, "road" if rolling stock road isn't acceptable, or 451 * "length" if the rolling stock length didn't fit. 452 */ 453 public String setLocation(Location location, Track track, boolean force) { 454 Location oldLocation = getLocation(); 455 Track oldTrack = getTrack(); 456 // first determine if rolling stock can be move to the new location 457 if (!force && (oldLocation != location || oldTrack != track)) { 458 String status = testLocation(location, track); 459 if (!status.equals(Track.OKAY)) { 460 return status; 461 } 462 } 463 // now update 464 _location = location; 465 _track = track; 466 467 if (oldLocation != location || oldTrack != track) { 468 // update rolling stock location on layout, maybe this should be a property 469 // change? 470 // first remove rolling stock from existing location 471 if (oldLocation != null) { 472 oldLocation.deleteRS(this); 473 oldLocation.removePropertyChangeListener(this); 474 // if track is null, then rolling stock is in a train 475 if (oldTrack != null) { 476 oldTrack.deleteRS(this); 477 oldTrack.removePropertyChangeListener(this); 478 // if there's a destination then pickup complete 479 if (getDestination() != null) { 480 oldLocation.deletePickupRS(); 481 oldTrack.deletePickupRS(this); 482 // don't update rs's previous location if just re-staging 483 if (getTrain() != null && getTrain().getRoute() != null && getTrain().getRoute().size() > 2) { 484 setLastLocationId(oldLocation.getId()); 485 setLastTrackId(oldTrack.getId()); 486 } 487 } 488 } 489 } 490 if (getLocation() != null) { 491 getLocation().addRS(this); 492 // Need to know if location name changes so we can forward to listeners 493 getLocation().addPropertyChangeListener(this); 494 } 495 if (getTrack() != null) { 496 getTrack().addRS(this); 497 // Need to know if location name changes so we can forward to listeners 498 getTrack().addPropertyChangeListener(this); 499 // if there's a destination then there's a pick up 500 if (getDestination() != null) { 501 getLocation().addPickupRS(); 502 getTrack().addPickupRS(this); 503 } 504 } 505 setDirtyAndFirePropertyChange(TRACK_CHANGED_PROPERTY, oldTrack, track); 506 } 507 return Track.OKAY; 508 } 509 510 /** 511 * Used to confirm that a track is associated with a location, and that rolling 512 * stock can be placed on track. 513 * 514 * @param location The location 515 * @param track The track, can be null 516 * @return OKAY if track exists at location and rolling stock can be placed on 517 * track, ERROR_TRACK if track isn't at location, other if rolling stock 518 * can't be placed on track. 519 */ 520 public String testLocation(Location location, Track track) { 521 if (track == null) { 522 return Track.OKAY; 523 } 524 if (location != null && !location.isTrackAtLocation(track)) { 525 return ERROR_TRACK; 526 } 527 return track.isRollingStockAccepted(this); 528 } 529 530 /** 531 * Sets rolling stock destination on the layout 532 * 533 * @param destination The Location. 534 * 535 * @param track (yard, spur, staging, or interchange track) 536 * @return "okay" if successful, "type" if the rolling stock's type isn't 537 * acceptable, or "length" if the rolling stock length didn't fit. 538 */ 539 public String setDestination(Location destination, Track track) { 540 return setDestination(destination, track, false); 541 } 542 543 /** 544 * Sets rolling stock destination on the layout 545 * 546 * @param destination The Location. 547 * 548 * @param track (yard, spur, staging, or interchange track) 549 * @param force when true ignore track length, type, and road when setting 550 * destination 551 * @return "okay" if successful, "type" if the rolling stock's type isn't 552 * acceptable, or "length" if the rolling stock length didn't fit. 553 */ 554 public String setDestination(Location destination, Track track, boolean force) { 555 // first determine if rolling stock can be move to the new destination 556 if (!force) { 557 String status = rsCheckDestination(destination, track); 558 if (!status.equals(Track.OKAY)) { 559 return status; 560 } 561 } 562 // now set the rolling stock destination! 563 Location oldDestination = getDestination(); 564 _destination = destination; 565 Track oldTrack = getDestinationTrack(); 566 _trackDestination = track; 567 568 if (oldDestination != destination || oldTrack != track) { 569 if (oldDestination != null) { 570 oldDestination.deleteDropRS(); 571 oldDestination.removePropertyChangeListener(this); 572 // delete pick up in case destination is null 573 if (getLocation() != null && getTrack() != null) { 574 getLocation().deletePickupRS(); 575 getTrack().deletePickupRS(this); 576 } 577 } 578 if (oldTrack != null) { 579 oldTrack.deleteDropRS(this); 580 oldTrack.removePropertyChangeListener(this); 581 } 582 if (getDestination() != null) { 583 getDestination().addDropRS(); 584 if (getLocation() != null && getTrack() != null) { 585 getLocation().addPickupRS(); 586 getTrack().addPickupRS(this); 587 } 588 // Need to know if destination name changes so we can forward to listeners 589 getDestination().addPropertyChangeListener(this); 590 } 591 if (getDestinationTrack() != null) { 592 getDestinationTrack().addDropRS(this); 593 // Need to know if destination name changes so we can forward to listeners 594 getDestinationTrack().addPropertyChangeListener(this); 595 } else { 596 // rolling stock has been terminated or reset 597 if (getTrain() != null && getTrain().getRoute() != null) { 598 setLastRouteId(getTrain().getRoute().getId()); 599 } 600 setRouteLocation(null); 601 setRouteDestination(null); 602 } 603 setDirtyAndFirePropertyChange(DESTINATION_TRACK_CHANGED_PROPERTY, oldTrack, track); 604 } 605 return Track.OKAY; 606 } 607 608 /** 609 * Used to check destination track to see if it will accept rolling stock 610 * 611 * @param destination The Location. 612 * @param track The Track at destination. 613 * 614 * @return status OKAY, TYPE, ROAD, LENGTH, ERROR_TRACK 615 */ 616 public String checkDestination(Location destination, Track track) { 617 return rsCheckDestination(destination, track); 618 } 619 620 private String rsCheckDestination(Location destination, Track track) { 621 // first perform a code check 622 if (destination != null && !destination.isTrackAtLocation(track)) { 623 return ERROR_TRACK; 624 } 625 if (destination != null && !destination.acceptsTypeName(getTypeName())) { 626 return Track.TYPE + " (" + getTypeName() + ")"; 627 } 628 if (destination == null || track == null) { 629 return Track.OKAY; 630 } 631 return track.isRollingStockAccepted(this); 632 } 633 634 public Location getDestination() { 635 return _destination; 636 } 637 638 /** 639 * Sets rolling stock destination without reserving destination track space or 640 * drop count. Does not fire a property change. Used by car router to test 641 * destinations. Use setDestination(Location, Track) instead. 642 * 643 * @param destination for the rolling stock 644 */ 645 public void setDestination(Location destination) { 646 _destination = destination; 647 } 648 649 public String getDestinationName() { 650 if (getDestination() != null) { 651 return getDestination().getName(); 652 } 653 return NONE; 654 } 655 656 public String getSplitDestinationName() { 657 return TrainCommon.splitString(getDestinationName()); 658 } 659 660 public String getDestinationId() { 661 if (getDestination() != null) { 662 return getDestination().getId(); 663 } 664 return NONE; 665 } 666 667 /** 668 * Sets rolling stock destination track without reserving destination track 669 * space or drop count. Used by car router to test destinations. Does not fire a 670 * property change. Use setDestination(Location, Track) instead. 671 * 672 * @param track The Track for set out at destination. 673 * 674 */ 675 public void setDestinationTrack(Track track) { 676 if (track != null) { 677 _destination = track.getLocation(); 678 } 679 _trackDestination = track; 680 } 681 682 public Track getDestinationTrack() { 683 return _trackDestination; 684 } 685 686 public String getDestinationTrackName() { 687 if (getDestinationTrack() != null) { 688 return getDestinationTrack().getName(); 689 } 690 return NONE; 691 } 692 693 public String getSplitDestinationTrackName() { 694 return TrainCommon.splitString(getDestinationTrackName()); 695 } 696 697 public String getDestinationTrackId() { 698 if (getDestinationTrack() != null) { 699 return getDestinationTrack().getId(); 700 } 701 return NONE; 702 } 703 704 public void setDivision(Division division) { 705 Division old = _division; 706 _division = division; 707 if (old != _division) { 708 setDirtyAndFirePropertyChange("homeDivisionChange", old, division); 709 } 710 } 711 712 public Division getDivision() { 713 return _division; 714 } 715 716 public String getDivisionName() { 717 if (getDivision() != null) { 718 return getDivision().getName(); 719 } 720 return NONE; 721 } 722 723 public String getDivisionId() { 724 if (getDivision() != null) { 725 return getDivision().getId(); 726 } 727 return NONE; 728 } 729 730 /** 731 * Used to block cars from staging 732 * 733 * @param id The location id from where the car came from before going into 734 * staging. 735 */ 736 public void setLastLocationId(String id) { 737 _lastLocationId = id; 738 } 739 740 public String getLastLocationId() { 741 return _lastLocationId; 742 } 743 744 public String getLastLocationName() { 745 Location location = locationManager.getLocationById(getLastLocationId()); 746 if (location != null) { 747 return location.getName(); 748 } 749 return NONE; 750 } 751 752 public void setLastTrackId(String id) { 753 _lastTrackId = id; 754 } 755 756 public String getLastTrackId() { 757 return _lastTrackId; 758 } 759 760 public String getLastTrackName() { 761 Location location = locationManager.getLocationById(getLastLocationId()); 762 if (location != null) { 763 Track track = location.getTrackById(getLastTrackId()); 764 if (track != null) { 765 return track.getName(); 766 } 767 } 768 return NONE; 769 } 770 771 public void setMoves(int moves) { 772 int old = _moves; 773 _moves = moves; 774 if (old != moves) { 775 setDirtyAndFirePropertyChange("rolling stock moves", Integer.toString(old), // NOI18N 776 Integer.toString(moves)); 777 } 778 } 779 780 public int getMoves() { 781 return _moves; 782 } 783 784 /** 785 * Sets the train that will service this rolling stock. 786 * 787 * @param train The Train. 788 * 789 */ 790 public void setTrain(Train train) { 791 Train old = _train; 792 _train = train; 793 if (old != train) { 794 if (old != null) { 795 old.removePropertyChangeListener(this); 796 } 797 if (train != null) { 798 train.addPropertyChangeListener(this); 799 } 800 setDirtyAndFirePropertyChange(TRAIN_CHANGED_PROPERTY, old, train); 801 } 802 } 803 804 public Train getTrain() { 805 return _train; 806 } 807 808 public String getTrainName() { 809 if (getTrain() != null) { 810 return getTrain().getName(); 811 } 812 return NONE; 813 } 814 815 /** 816 * Sets the last train that serviced this rolling stock. 817 * 818 * @param train The last Train. 819 */ 820 public void setLastTrain(Train train) { 821 Train old = _lastTrain; 822 _lastTrain = train; 823 if (old != train) { 824 if (old != null) { 825 old.removePropertyChangeListener(this); 826 } 827 if (train != null) { 828 train.addPropertyChangeListener(this); 829 } 830 setDirtyAndFirePropertyChange(TRAIN_CHANGED_PROPERTY, old, train); 831 } 832 } 833 834 public Train getLastTrain() { 835 return _lastTrain; 836 } 837 838 public String getLastTrainName() { 839 if (getLastTrain() != null) { 840 return getLastTrain().getName(); 841 } 842 return NONE; 843 } 844 845 /** 846 * Sets the location where the rolling stock will be picked up by the train. 847 * 848 * @param routeLocation the pick up location for this rolling stock. 849 */ 850 public void setRouteLocation(RouteLocation routeLocation) { 851 // a couple of error checks before setting the route location 852 if (getLocation() == null && routeLocation != null) { 853 log.debug("WARNING rolling stock ({}) does not have an assigned location", this); // NOI18N 854 } else if (routeLocation != null && getLocation() != null && !routeLocation.getName().equals(getLocation().getName())) { 855 log.error("ERROR route location name({}) not equal to location name ({}) for rolling stock ({})", 856 routeLocation.getName(), getLocation().getName(), this); // NOI18N 857 } 858 RouteLocation old = _routeLocation; 859 _routeLocation = routeLocation; 860 if (old != routeLocation) { 861 setDirtyAndFirePropertyChange(ROUTE_LOCATION_CHANGED_PROPERTY, old, routeLocation); 862 } 863 } 864 865 /** 866 * Where in a train's route this car resides 867 * 868 * @return the location in a train's route 869 */ 870 public RouteLocation getRouteLocation() { 871 return _routeLocation; 872 } 873 874 public String getRouteLocationId() { 875 if (getRouteLocation() != null) { 876 return getRouteLocation().getId(); 877 } 878 return NONE; 879 } 880 881 /** 882 * Used to determine which train delivered a car to an interchange track. 883 * 884 * @return the route id of the last train delivering this car. 885 */ 886 public String getLastRouteId() { 887 return _routeId; 888 } 889 890 /** 891 * Sets the id of the route that was used to set out the rolling stock. Used to 892 * determine if the rolling stock can be pick ups from an interchange track. 893 * 894 * @param id The route id. 895 */ 896 public void setLastRouteId(String id) { 897 _routeId = id; 898 } 899 900 public String getValue() { 901 return _value; 902 } 903 904 /** 905 * Sets the value (cost, price) for this rolling stock. Currently has nothing to 906 * do with operations. But nice to have. 907 * 908 * @param value a string representing what this item is worth. 909 */ 910 public void setValue(String value) { 911 String old = _value; 912 _value = value; 913 if (!old.equals(value)) { 914 setDirtyAndFirePropertyChange("rolling stock value", old, value); // NOI18N 915 } 916 } 917 918 public String getRfid() { 919 return _rfid; 920 } 921 922 /** 923 * Sets the RFID for this rolling stock. 924 * 925 * @param id 12 character RFID string. 926 */ 927 public void setRfid(String id) { 928 String old = _rfid; 929 if (id != null && !id.equals(old)) { 930 log.debug("Setting IdTag for {} to {}", this, id); 931 _rfid = id; 932 if (!id.equals(NONE)) { 933 try { 934 IdTag tag = InstanceManager.getDefault(IdTagManager.class).provideIdTag(id); 935 setIdTag(tag); 936 } catch (IllegalArgumentException e) { 937 log.error("Exception recording tag {} - exception value {}", id, e.getMessage()); 938 } 939 } 940 setDirtyAndFirePropertyChange("rolling stock rfid", old, id); // NOI18N 941 } 942 } 943 944 public IdTag getIdTag() { 945 return _tag; 946 } 947 948 /** 949 * Sets the id tag for this rolling stock. The id tag isn't saved, between 950 * session but the tag label is saved as _rfid. 951 * 952 * @param tag the id tag 953 */ 954 public void setIdTag(IdTag tag) { 955 if (_tag != null) { 956 _tag.removePropertyChangeListener(_tagListener); 957 } 958 _tag = tag; 959 if (_tagListener == null) { 960 // store the tag listener so we can reuse it and 961 // dispose of it as necessary. 962 _tagListener = new PropertyChangeListener() { 963 @Override 964 public void propertyChange(java.beans.PropertyChangeEvent e) { 965 if (e.getPropertyName().equals("whereLastSeen")) { 966 log.debug("Tag Reader Position update received for {}", this); 967 // update the position of this piece of rolling 968 // stock when its IdTag is seen, but only if 969 // the actual location changes. 970 if (e.getNewValue() != null) { 971 // first, check to see if this reader is 972 // associated with a track. 973 Track newTrack = locationManager.getTrackByReporter((jmri.Reporter) e.getNewValue()); 974 if (newTrack != null) { 975 if (newTrack != getTrack()) { 976 // set the car's location based on the track. 977 setLocation(newTrack.getLocation(), newTrack); 978 // also notify listeners that the last seen 979 // location has changed. 980 setDirtyAndFirePropertyChange("rolling stock whereLastSeen", _whereLastSeen, 981 _whereLastSeen = newTrack.getLocation()); 982 983 } 984 } else { 985 // the reader isn't associated with a track, 986 Location newLocation = locationManager 987 .getLocationByReporter((jmri.Reporter) e.getNewValue()); 988 if (newLocation != getLocation()) { 989 // we really should be able to set the 990 // location where we last saw the tag: 991 // setLocation(newLocation,null); 992 // for now, notify listeners that the 993 // location changed. 994 setDirtyAndFirePropertyChange("rolling stock whereLastSeen", _whereLastSeen, 995 _whereLastSeen = newLocation); 996 997 } 998 } 999 } 1000 } 1001 if (e.getPropertyName().equals("whenLastSeen")) { 1002 log.debug("Tag Reader Time at Location update received for {}", this); 1003 // update the time when this car was last moved 1004 // stock when its IdTag is seen. 1005 if (e.getNewValue() != null) { 1006 Date newDate = ((Date) e.getNewValue()); 1007 setLastDate(newDate); 1008 // and notify listeners when last seen was updated. 1009 setDirtyAndFirePropertyChange("rolling stock whenLastSeen", _whenLastSeen, 1010 _whenLastSeen = newDate); 1011 } 1012 } 1013 } 1014 }; 1015 } 1016 if (_tag != null) { 1017 _tag.addPropertyChangeListener(_tagListener); 1018 setRfid(_tag.getSystemName()); 1019 } else { 1020 setRfid(NONE); 1021 } 1022 // initilize _whenLastSeen and _whereLastSeen for property 1023 // change notification. 1024 _whereLastSeen = getWhereLastSeen(); 1025 _whenLastSeen = getWhenLastSeen(); 1026 } 1027 1028 public String getWhereLastSeenName() { 1029 if (getWhereLastSeen() != null) { 1030 return getWhereLastSeen().getName(); 1031 } 1032 return NONE; 1033 } 1034 1035 public Location getWhereLastSeen() { 1036 if (_tag == null) { 1037 return null; 1038 } 1039 jmri.Reporter r = _tag.getWhereLastSeen(); 1040 Track t = locationManager.getTrackByReporter(r); 1041 if (t != null) { 1042 return t.getLocation(); 1043 } 1044 // the reader isn't associated with a track, return 1045 // the location it is associated with, which might be null. 1046 return locationManager.getLocationByReporter(r); 1047 } 1048 1049 public Track getTrackLastSeen() { 1050 if (_tag == null) { 1051 // there isn't a tag associated with this piece of rolling stock. 1052 return null; 1053 } 1054 jmri.Reporter r = _tag.getWhereLastSeen(); 1055 if (r == null) { 1056 // there is a tag associated with this piece 1057 // of rolling stock, but no reporter has seen it (or it was reset). 1058 return null; 1059 } 1060 // this return value will be null, if there isn't an associated track 1061 // for the last reporter. 1062 return locationManager.getTrackByReporter(r); 1063 } 1064 1065 public String getTrackLastSeenName() { 1066 // let getTrackLastSeen() find the track, if it exists. 1067 Track t = getTrackLastSeen(); 1068 if (t != null) { 1069 // if there is a track, return the name. 1070 return t.getName(); 1071 } 1072 // otherwise, there is no track to return the name of. 1073 return NONE; 1074 } 1075 1076 public Date getWhenLastSeen() { 1077 if (_tag == null) { 1078 return null; // never seen, so no date. 1079 } 1080 return _tag.getWhenLastSeen(); 1081 } 1082 1083 /** 1084 * Provides the last date when this rolling stock was moved, or was reset from a 1085 * built train, as a string. 1086 * 1087 * @return date 1088 */ 1089 public String getWhenLastSeenDate() { 1090 if (getWhenLastSeen() == null) { 1091 return NONE; // return an empty string for the default date. 1092 } 1093 SimpleDateFormat format = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); // NOI18N 1094 return format.format(getWhenLastSeen()); 1095 } 1096 1097 /** 1098 * Provides the last date when this rolling stock was moved 1099 * 1100 * @return String yyyy/MM/dd HH:mm:ss 1101 */ 1102 public String getSortDate() { 1103 if (_lastDate.equals((new java.util.GregorianCalendar()).getGregorianChange())) { 1104 return NONE; // return an empty string for the default date. 1105 } 1106 SimpleDateFormat format = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); // NOI18N 1107 return format.format(_lastDate); 1108 } 1109 1110 /** 1111 * Provides the last date when this rolling stock was moved 1112 * 1113 * @return String MM/dd/yyyy HH:mm:ss 1114 */ 1115 public String getLastDate() { 1116 if (_lastDate.equals((new java.util.GregorianCalendar()).getGregorianChange())) { 1117 return NONE; // return an empty string for the default date. 1118 } 1119 SimpleDateFormat format = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss"); // NOI18N 1120 return format.format(_lastDate); 1121 } 1122 1123 /** 1124 * Sets the last date when this rolling stock was moved 1125 * 1126 * @param date The Date when this rolling stock was last moved. 1127 * 1128 */ 1129 public void setLastDate(Date date) { 1130 Date old = _lastDate; 1131 _lastDate = date; 1132 if (!old.equals(_lastDate)) { 1133 setDirtyAndFirePropertyChange("rolling stock date", old, date); // NOI18N 1134 } 1135 } 1136 1137 /** 1138 * Provides the last date when this rolling stock was moved 1139 * 1140 * @return date 1141 */ 1142 public Date getLastMoveDate() { 1143 return _lastDate; 1144 } 1145 1146 /** 1147 * Sets the last date when this rolling stock was moved. This method is used 1148 * only for loading data from a file. Use setLastDate(Date) instead. 1149 * 1150 * @param date yyyy/MM/dd HH:mm:ss, MM/dd/yyyy HH:mm:ss, MM/dd/yyyy hh:mmaa, 1151 * or MM/dd/yyyy HH:mm 1152 */ 1153 private void setLastDate(String date) { 1154 Date d = TrainCommon.convertStringToDate(date); 1155 if (d != null) { 1156 _lastDate = d; 1157 } 1158 } 1159 1160 public void setBlocking(int number) { 1161 int old = _blocking; 1162 _blocking = number; 1163 if (old != number) { 1164 setDirtyAndFirePropertyChange("rolling stock blocking changed", old, number); // NOI18N 1165 } 1166 } 1167 1168 public int getBlocking() { 1169 return _blocking; 1170 } 1171 1172 /** 1173 * Set where in a train's route this rolling stock will be set out. 1174 * 1175 * @param routeDestination the location where the rolling stock is to leave the 1176 * train. 1177 */ 1178 public void setRouteDestination(RouteLocation routeDestination) { 1179 if (routeDestination != null && 1180 getDestination() != null && 1181 !routeDestination.getName().equals(getDestination().getName())) { 1182 log.debug("WARNING route destination name ({}) not equal to destination name ({}) for rolling stock ({})", 1183 routeDestination.getName(), getDestination().getName(), this); // NOI18N 1184 } 1185 RouteLocation old = _routeDestination; 1186 _routeDestination = routeDestination; 1187 if (old != routeDestination) { 1188 setDirtyAndFirePropertyChange(ROUTE_DESTINATION_CHANGED_PROPERTY, old, routeDestination); 1189 } 1190 } 1191 1192 public RouteLocation getRouteDestination() { 1193 return _routeDestination; 1194 } 1195 1196 public String getRouteDestinationId() { 1197 if (getRouteDestination() != null) { 1198 return getRouteDestination().getId(); 1199 } 1200 return NONE; 1201 } 1202 1203 public void setOwnerName(String owner) { 1204 String old = _owner; 1205 _owner = owner; 1206 if (!old.equals(owner)) { 1207 setDirtyAndFirePropertyChange("rolling stock owner", old, owner); // NOI18N 1208 } 1209 } 1210 1211 public String getOwnerName() { 1212 return _owner; 1213 } 1214 1215 /** 1216 * Set the rolling stock location as unknown. 1217 * 1218 * @param unknown when true, the rolling stock location is unknown. 1219 */ 1220 public void setLocationUnknown(boolean unknown) { 1221 boolean old = _locationUnknown; 1222 _locationUnknown = unknown; 1223 if (!old == unknown) { 1224 setDirtyAndFirePropertyChange("car location known", old ? "true" : "false", unknown ? "true" // NOI18N 1225 : "false"); // NOI18N 1226 } 1227 } 1228 1229 /** 1230 * 1231 * @return true when car's location is unknown 1232 */ 1233 public boolean isLocationUnknown() { 1234 return _locationUnknown; 1235 } 1236 1237 /** 1238 * Sets the rolling stock service state. When true, rolling stock is out of 1239 * service. Normal state is false, the rolling stock is in service and 1240 * available. 1241 * 1242 * @param outOfService when true, out of service 1243 */ 1244 public void setOutOfService(boolean outOfService) { 1245 boolean old = _outOfService; 1246 _outOfService = outOfService; 1247 if (!old == outOfService) { 1248 setDirtyAndFirePropertyChange("car out of service", old ? "true" : "false", outOfService ? "true" // NOI18N 1249 : "false"); // NOI18N 1250 } 1251 } 1252 1253 /** 1254 * 1255 * @return true when rolling stock is out of service 1256 */ 1257 public boolean isOutOfService() { 1258 return _outOfService; 1259 } 1260 1261 public void setSelected(boolean selected) { 1262 boolean old = _selected; 1263 _selected = selected; 1264 if (!old == selected) { 1265 setDirtyAndFirePropertyChange("selected", old ? "true" : "false", selected ? "true" // NOI18N 1266 : "false"); // NOI18N 1267 } 1268 } 1269 1270 /** 1271 * 1272 * @return true when rolling stock is selected 1273 */ 1274 public boolean isSelected() { 1275 return _selected; 1276 } 1277 1278 public void setComment(String comment) { 1279 String old = _comment; 1280 _comment = comment; 1281 if (!old.equals(comment)) { 1282 setDirtyAndFirePropertyChange(COMMENT_CHANGED_PROPERTY, old, comment); // NOI18N 1283 } 1284 } 1285 1286 public String getComment() { 1287 return _comment; 1288 } 1289 1290 public void setPickupTime(String time) { 1291 String old = _pickupTime; 1292 _pickupTime = time; 1293 setDirtyAndFirePropertyChange("Pickup Time Changed", old, time); // NOI18N 1294 } 1295 1296 public String getPickupTime() { 1297 if (getTrain() != null) { 1298 return _pickupTime; 1299 } 1300 return NONE; 1301 } 1302 1303 protected void moveRollingStock(RouteLocation current, RouteLocation next) { 1304 if (current == getRouteLocation()) { 1305 setLastDate(java.util.Calendar.getInstance().getTime()); 1306 // Arriving at destination? 1307 if (getRouteLocation() == getRouteDestination() || next == null) { 1308 if (getRouteLocation() == getRouteDestination()) { 1309 log.debug("Rolling stock ({}) has arrived at destination ({})", this, getDestination()); 1310 } else { 1311 log.error("Rolling stock ({}) has a null route location for next", this); // NOI18N 1312 } 1313 setLocation(getDestination(), getDestinationTrack(), RollingStock.FORCE); // force RS to destination 1314 setDestination(null, null); // this also clears the route locations 1315 setLastTrain(getTrain()); // save the last train moving this rs 1316 setTrain(null); // this must come after setDestination (route id is set) 1317 setMoves(getMoves() + 1); // bump count 1318 } else { 1319 log.debug("Rolling stock ({}) is in train ({}) leaves location ({}) destination ({})", this, 1320 getTrainName(), current.getName(), next.getName()); 1321 setLocation(next.getLocation(), null, RollingStock.FORCE); // force RS to location 1322 setRouteLocation(next); 1323 } 1324 } 1325 } 1326 1327 public void reset() { 1328 // the order of the next two instructions is important, otherwise rs will have 1329 // train's route id 1330 setTrain(null); 1331 setDestination(null, null); 1332 } 1333 1334 /** 1335 * Remove rolling stock. Releases all listeners. 1336 */ 1337 public void dispose() { 1338 setTrain(null); 1339 setDestination(null, null); 1340 setLocation(null, null); 1341 InstanceManager.getDefault(CarRoads.class).removePropertyChangeListener(this); 1342 InstanceManager.getDefault(CarOwners.class).removePropertyChangeListener(this); 1343 InstanceManager.getDefault(CarColors.class).removePropertyChangeListener(this); 1344 if (getIdTag() != null) { 1345 getIdTag().removePropertyChangeListener(_tagListener); 1346 } 1347 } 1348 1349 /** 1350 * Construct this Entry from XML. 1351 * 1352 * @param e RollingStock XML element 1353 */ 1354 public RollingStock(org.jdom2.Element e) { 1355 this(); 1356 org.jdom2.Attribute a; 1357 if ((a = e.getAttribute(Xml.ID)) != null) { 1358 _id = a.getValue(); 1359 } else { 1360 log.warn("no id attribute in rolling stock element when reading operations"); 1361 } 1362 if ((a = e.getAttribute(Xml.ROAD_NUMBER)) != null) { 1363 _number = a.getValue(); 1364 } 1365 if ((a = e.getAttribute(Xml.ROAD_NAME)) != null) { 1366 _road = a.getValue(); 1367 } 1368 if (_id == null || !_id.equals(createId(_road, _number))) { 1369 _id = createId(_road, _number); 1370 } 1371 if ((a = e.getAttribute(Xml.TYPE)) != null) { 1372 _type = a.getValue(); 1373 } 1374 if ((a = e.getAttribute(Xml.LENGTH)) != null) { 1375 _length = a.getValue(); 1376 } 1377 if ((a = e.getAttribute(Xml.COLOR)) != null) { 1378 _color = a.getValue(); 1379 } 1380 if ((a = e.getAttribute(Xml.WEIGHT)) != null) { 1381 _weight = a.getValue(); 1382 } 1383 if ((a = e.getAttribute(Xml.WEIGHT_TONS)) != null) { 1384 _weightTons = a.getValue(); 1385 } 1386 if ((a = e.getAttribute(Xml.BUILT)) != null) { 1387 _built = a.getValue(); 1388 } 1389 1390 Location location = null; 1391 Track track = null; 1392 if ((a = e.getAttribute(Xml.LOCATION_ID)) != null) { 1393 location = locationManager.getLocationById(a.getValue()); 1394 } 1395 if ((a = e.getAttribute(Xml.SEC_LOCATION_ID)) != null && location != null) { 1396 track = location.getTrackById(a.getValue()); 1397 } 1398 setLocation(location, track, RollingStock.FORCE); // force location 1399 1400 Location destination = null; 1401 track = null; 1402 if ((a = e.getAttribute(Xml.DESTINATION_ID)) != null) { 1403 destination = locationManager.getLocationById(a.getValue()); 1404 } 1405 if ((a = e.getAttribute(Xml.SEC_DESTINATION_ID)) != null && destination != null) { 1406 track = destination.getTrackById(a.getValue()); 1407 } 1408 setDestination(destination, track, true); // force destination 1409 1410 if ((a = e.getAttribute(Xml.DIVISION_ID)) != null) { 1411 _division = InstanceManager.getDefault(DivisionManager.class).getDivisionById(a.getValue()); 1412 } 1413 // TODO remove the following 3 lines in 2022 1414 if ((a = e.getAttribute(Xml.DIVISION_ID_ERROR)) != null) { 1415 _division = InstanceManager.getDefault(DivisionManager.class).getDivisionById(a.getValue()); 1416 } 1417 if ((a = e.getAttribute(Xml.MOVES)) != null) { 1418 try { 1419 _moves = Integer.parseInt(a.getValue()); 1420 } catch (NumberFormatException nfe) { 1421 log.error("Move count ({}) for rollingstock ({}) isn't a valid number!", a.getValue(), toString()); 1422 } 1423 } 1424 if ((a = e.getAttribute(Xml.LAST_LOCATION_ID)) != null) { 1425 _lastLocationId = a.getValue(); 1426 } 1427 if ((a = e.getAttribute(Xml.LAST_TRACK_ID)) != null) { 1428 _lastTrackId = a.getValue(); 1429 } 1430 if ((a = e.getAttribute(Xml.TRAIN_ID)) != null) { 1431 setTrain(InstanceManager.getDefault(TrainManager.class).getTrainById(a.getValue())); 1432 } else if ((a = e.getAttribute(Xml.TRAIN)) != null) { 1433 setTrain(InstanceManager.getDefault(TrainManager.class).getTrainByName(a.getValue())); 1434 } 1435 if (getTrain() != null && 1436 getTrain().getRoute() != null && 1437 (a = e.getAttribute(Xml.ROUTE_LOCATION_ID)) != null) { 1438 _routeLocation = getTrain().getRoute().getRouteLocationById(a.getValue()); 1439 if ((a = e.getAttribute(Xml.ROUTE_DESTINATION_ID)) != null) { 1440 _routeDestination = getTrain().getRoute().getRouteLocationById(a.getValue()); 1441 } 1442 } 1443 if ((a = e.getAttribute(Xml.LAST_TRAIN_ID)) != null) { 1444 setLastTrain(InstanceManager.getDefault(TrainManager.class).getTrainById(a.getValue())); 1445 } 1446 if ((a = e.getAttribute(Xml.LAST_ROUTE_ID)) != null) { 1447 _routeId = a.getValue(); 1448 } 1449 if ((a = e.getAttribute(Xml.OWNER)) != null) { 1450 _owner = a.getValue(); 1451 } 1452 if ((a = e.getAttribute(Xml.COMMENT)) != null) { 1453 _comment = a.getValue(); 1454 } 1455 if ((a = e.getAttribute(Xml.VALUE)) != null) { 1456 _value = a.getValue(); 1457 } 1458 if ((a = e.getAttribute(Xml.RFID)) != null) { 1459 setRfid(a.getValue()); 1460 } 1461 if ((a = e.getAttribute(Xml.LOC_UNKNOWN)) != null) { 1462 _locationUnknown = a.getValue().equals(Xml.TRUE); 1463 } 1464 if ((a = e.getAttribute(Xml.OUT_OF_SERVICE)) != null) { 1465 _outOfService = a.getValue().equals(Xml.TRUE); 1466 } 1467 if ((a = e.getAttribute(Xml.SELECTED)) != null) { 1468 _selected = a.getValue().equals(Xml.TRUE); 1469 } 1470 if ((a = e.getAttribute(Xml.DATE)) != null) { 1471 setLastDate(a.getValue()); // uses the setLastDate(String) method. 1472 } 1473 if ((a = e.getAttribute(Xml.PICKUP_TIME)) != null) { 1474 _pickupTime = a.getValue(); 1475 } 1476 if ((a = e.getAttribute(Xml.BLOCKING)) != null) { 1477 try { 1478 _blocking = Integer.parseInt(a.getValue()); 1479 } catch (NumberFormatException nfe) { 1480 log.error("Blocking ({}) for rollingstock ({}) isn't a valid number!", a.getValue(), toString()); 1481 } 1482 } 1483 // check for rolling stock without a track assignment 1484 if (getLocation() != null && getTrack() == null && getTrain() == null) { 1485 log.warn("Rollingstock ({}) at ({}) doesn't have a track assignment", this, getLocationName()); 1486 } 1487 addPropertyChangeListeners(); 1488 } 1489 1490 /** 1491 * Add XML elements to represent this Entry. 1492 * 1493 * @param e Element for car or engine store. 1494 * 1495 * @return Contents in a JDOM Element 1496 */ 1497 protected org.jdom2.Element store(org.jdom2.Element e) { 1498 e.setAttribute(Xml.ID, getId()); 1499 e.setAttribute(Xml.ROAD_NAME, getRoadName()); 1500 e.setAttribute(Xml.ROAD_NUMBER, getNumber()); 1501 e.setAttribute(Xml.TYPE, getTypeName()); 1502 e.setAttribute(Xml.LENGTH, getLength()); 1503 if (!getColor().equals(NONE)) { 1504 e.setAttribute(Xml.COLOR, getColor()); 1505 } 1506 if (!getWeight().equals(DEFAULT_WEIGHT)) { 1507 e.setAttribute(Xml.WEIGHT, getWeight()); 1508 } 1509 if (!getWeightTons().equals(NONE)) { 1510 e.setAttribute(Xml.WEIGHT_TONS, getWeightTons()); 1511 } 1512 if (!getBuilt().equals(NONE)) { 1513 e.setAttribute(Xml.BUILT, getBuilt()); 1514 } 1515 if (!getLocationId().equals(NONE)) { 1516 e.setAttribute(Xml.LOCATION_ID, getLocationId()); 1517 } 1518 if (!getRouteLocationId().equals(NONE)) { 1519 e.setAttribute(Xml.ROUTE_LOCATION_ID, getRouteLocationId()); 1520 } 1521 if (!getTrackId().equals(NONE)) { 1522 e.setAttribute(Xml.SEC_LOCATION_ID, getTrackId()); 1523 } 1524 if (!getDestinationId().equals(NONE)) { 1525 e.setAttribute(Xml.DESTINATION_ID, getDestinationId()); 1526 } 1527 if (!getRouteDestinationId().equals(NONE)) { 1528 e.setAttribute(Xml.ROUTE_DESTINATION_ID, getRouteDestinationId()); 1529 } 1530 if (!getDestinationTrackId().equals(NONE)) { 1531 e.setAttribute(Xml.SEC_DESTINATION_ID, getDestinationTrackId()); 1532 } 1533 if (!getDivisionId().equals(NONE)) { 1534 e.setAttribute(Xml.DIVISION_ID, getDivisionId()); 1535 } 1536 if (!getLastRouteId().equals(NONE)) { 1537 e.setAttribute(Xml.LAST_ROUTE_ID, getLastRouteId()); 1538 } 1539 e.setAttribute(Xml.MOVES, Integer.toString(getMoves())); 1540 e.setAttribute(Xml.DATE, getLastDate()); 1541 e.setAttribute(Xml.SELECTED, isSelected() ? Xml.TRUE : Xml.FALSE); 1542 if (!getLastLocationId().equals(LOCATION_UNKNOWN)) { 1543 e.setAttribute(Xml.LAST_LOCATION_ID, getLastLocationId()); 1544 } 1545 if (!getLastTrackId().equals(LOCATION_UNKNOWN)) { 1546 e.setAttribute(Xml.LAST_TRACK_ID, getLastTrackId()); 1547 } 1548 if (!getTrainName().equals(NONE)) { 1549 e.setAttribute(Xml.TRAIN, getTrainName()); 1550 e.setAttribute(Xml.TRAIN_ID, getTrain().getId()); 1551 } 1552 if (!getLastTrainName().equals(NONE)) { 1553 e.setAttribute(Xml.LAST_TRAIN, getLastTrainName()); 1554 e.setAttribute(Xml.LAST_TRAIN_ID, getLastTrain().getId()); 1555 } 1556 if (!getOwnerName().equals(NONE)) { 1557 e.setAttribute(Xml.OWNER, getOwnerName()); 1558 } 1559 if (!getValue().equals(NONE)) { 1560 e.setAttribute(Xml.VALUE, getValue()); 1561 } 1562 if (!getRfid().equals(NONE)) { 1563 e.setAttribute(Xml.RFID, getRfid()); 1564 } 1565 if (isLocationUnknown()) { 1566 e.setAttribute(Xml.LOC_UNKNOWN, isLocationUnknown() ? Xml.TRUE : Xml.FALSE); 1567 } 1568 if (isOutOfService()) { 1569 e.setAttribute(Xml.OUT_OF_SERVICE, isOutOfService() ? Xml.TRUE : Xml.FALSE); 1570 } 1571 if (!getPickupTime().equals(NONE)) { 1572 e.setAttribute(Xml.PICKUP_TIME, getPickupTime()); 1573 } 1574 if (getBlocking() != 0) { 1575 e.setAttribute(Xml.BLOCKING, Integer.toString(getBlocking())); 1576 } 1577 if (!getComment().equals(NONE)) { 1578 e.setAttribute(Xml.COMMENT, getComment()); 1579 } 1580 return e; 1581 } 1582 1583 private void addPropertyChangeListeners() { 1584 InstanceManager.getDefault(CarRoads.class).addPropertyChangeListener(this); 1585 InstanceManager.getDefault(CarOwners.class).addPropertyChangeListener(this); 1586 InstanceManager.getDefault(CarColors.class).addPropertyChangeListener(this); 1587 } 1588 1589 // rolling stock listens for changes in a location name or if a location is 1590 // deleted 1591 @Override 1592 public void propertyChange(PropertyChangeEvent e) { 1593 // log.debug("Property change for rolling stock: " + toString()+ " property 1594 // name: " 1595 // +e.getPropertyName()+ " old: "+e.getOldValue()+ " new: "+e.getNewValue()); 1596 // notify if track or location name changes 1597 if (e.getPropertyName().equals(Location.NAME_CHANGED_PROPERTY)) { 1598 log.debug("Property change for rolling stock: ({}) property name: ({}) old: ({}) new: ({})", this, 1599 e.getPropertyName(), e.getOldValue(), e.getNewValue()); 1600 setDirtyAndFirePropertyChange(e.getPropertyName(), e.getOldValue(), e.getNewValue()); 1601 } 1602 if (e.getPropertyName().equals(Location.DISPOSE_CHANGED_PROPERTY)) { 1603 if (e.getSource() == getLocation()) { 1604 log.debug("delete location for rolling stock: ({})", this); 1605 setLocation(null, null); 1606 } 1607 if (e.getSource() == getDestination()) { 1608 log.debug("delete destination for rolling stock: ({})", this); 1609 setDestination(null, null); 1610 } 1611 } 1612 if (e.getPropertyName().equals(Track.DISPOSE_CHANGED_PROPERTY)) { 1613 if (e.getSource() == getTrack()) { 1614 log.debug("delete location for rolling stock: ({})", this); 1615 setLocation(getLocation(), null); 1616 } 1617 if (e.getSource() == getDestinationTrack()) { 1618 log.debug("delete destination for rolling stock: ({})", this); 1619 setDestination(getDestination(), null); 1620 } 1621 } 1622 if (e.getPropertyName().equals(Train.DISPOSE_CHANGED_PROPERTY) && e.getSource() == getTrain()) { 1623 log.debug("delete train for rolling stock: ({})", this); 1624 setTrain(null); 1625 } 1626 if (e.getPropertyName().equals(Train.TRAIN_LOCATION_CHANGED_PROPERTY) && e.getSource() == getTrain()) { 1627 log.debug("Rolling stock ({}) is serviced by train ({})", this, getTrainName()); 1628 moveRollingStock((RouteLocation) e.getOldValue(), (RouteLocation) e.getNewValue()); 1629 } 1630 if (e.getPropertyName().equals(Train.STATUS_CHANGED_PROPERTY) && 1631 e.getNewValue().equals(Train.TRAIN_RESET) && 1632 e.getSource() == getTrain()) { 1633 log.debug("Rolling stock ({}) is removed from train ({}) by reset", this, getTrainName()); // NOI18N 1634 reset(); 1635 } 1636 if (e.getPropertyName().equals(Train.NAME_CHANGED_PROPERTY)) { 1637 setDirtyAndFirePropertyChange(e.getPropertyName(), e.getOldValue(), e.getNewValue()); 1638 } 1639 if (e.getPropertyName().equals(CarRoads.CARROADS_NAME_CHANGED_PROPERTY)) { 1640 if (e.getOldValue().equals(getRoadName())) { 1641 log.debug("Rolling stock ({}) sees road name change from ({}) to ({})", this, e.getOldValue(), 1642 e.getNewValue()); // NOI18N 1643 if (e.getNewValue() != null) { 1644 setRoadName((String) e.getNewValue()); 1645 } 1646 } 1647 } 1648 if (e.getPropertyName().equals(CarOwners.CAROWNERS_NAME_CHANGED_PROPERTY)) { 1649 if (e.getOldValue().equals(getOwnerName())) { 1650 log.debug("Rolling stock ({}) sees owner name change from ({}) to ({})", this, e.getOldValue(), 1651 e.getNewValue()); // NOI18N 1652 setOwnerName((String) e.getNewValue()); 1653 } 1654 } 1655 if (e.getPropertyName().equals(CarColors.CARCOLORS_NAME_CHANGED_PROPERTY)) { 1656 if (e.getOldValue().equals(getColor())) { 1657 log.debug("Rolling stock ({}) sees color name change from ({}) to ({})", this, e.getOldValue(), 1658 e.getNewValue()); // NOI18N 1659 setColor((String) e.getNewValue()); 1660 } 1661 } 1662 } 1663 1664 protected void setDirtyAndFirePropertyChange(String p, Object old, Object n) { 1665 firePropertyChange(p, old, n); 1666 } 1667 1668 private final static Logger log = LoggerFactory.getLogger(RollingStock.class); 1669 1670}