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