001package jmri.jmrit.operations.rollingstock.cars; 002 003import java.beans.PropertyChangeEvent; 004import java.text.NumberFormat; 005import java.util.*; 006 007import org.jdom2.Element; 008import org.slf4j.Logger; 009import org.slf4j.LoggerFactory; 010 011import jmri.*; 012import jmri.jmrit.operations.locations.Track; 013import jmri.jmrit.operations.rollingstock.RollingStock; 014import jmri.jmrit.operations.rollingstock.RollingStockManager; 015import jmri.jmrit.operations.routes.Route; 016import jmri.jmrit.operations.routes.RouteLocation; 017import jmri.jmrit.operations.setup.OperationsSetupXml; 018import jmri.jmrit.operations.setup.Setup; 019import jmri.jmrit.operations.trains.Train; 020import jmri.jmrit.operations.trains.TrainManifestHeaderText; 021 022/** 023 * Manages the cars. 024 * 025 * @author Daniel Boudreau Copyright (C) 2008 026 */ 027public class CarManager extends RollingStockManager<Car> 028 implements InstanceManagerAutoDefault, InstanceManagerAutoInitialize { 029 030 public CarManager() { 031 } 032 033 /** 034 * Finds an existing Car or creates a new Car if needed requires car's road and 035 * number 036 * 037 * @param road car road 038 * @param number car number 039 * @return new car or existing Car 040 */ 041 @Override 042 public Car newRS(String road, String number) { 043 Car car = getByRoadAndNumber(road, number); 044 if (car == null) { 045 car = new Car(road, number); 046 register(car); 047 } 048 return car; 049 } 050 051 @Override 052 public void deregister(Car car) { 053 super.deregister(car); 054 InstanceManager.getDefault(CarManagerXml.class).setDirty(true); 055 } 056 057 /** 058 * Sort by rolling stock location 059 * 060 * @return list of cars ordered by the Car's location 061 */ 062 @Override 063 public List<Car> getByLocationList() { 064 List<Car> byFinal = getByList(getByNumberList(), BY_FINAL_DEST); 065 List<Car> byKernel = getByList(byFinal, BY_KERNEL); 066 return getByList(byKernel, BY_LOCATION); 067 } 068 069 /** 070 * Sort by car kernel names 071 * 072 * @return list of cars ordered by car kernel 073 */ 074 public List<Car> getByKernelList() { 075 return getByList(getByList(getByNumberList(), BY_BLOCKING), BY_KERNEL); 076 } 077 078 /** 079 * Sort by car loads 080 * 081 * @return list of cars ordered by car loads 082 */ 083 public List<Car> getByLoadList() { 084 return getByList(getByLocationList(), BY_LOAD); 085 } 086 087 /** 088 * Sort by car return when empty location and track 089 * 090 * @return list of cars ordered by car return when empty 091 */ 092 public List<Car> getByRweList() { 093 return getByList(getByLocationList(), BY_RWE); 094 } 095 096 public List<Car> getByRwlList() { 097 return getByList(getByLocationList(), BY_RWL); 098 } 099 100 public List<Car> getByRouteList() { 101 return getByList(getByLocationList(), BY_ROUTE); 102 } 103 104 public List<Car> getByDivisionList() { 105 return getByList(getByLocationList(), BY_DIVISION); 106 } 107 108 public List<Car> getByFinalDestinationList() { 109 return getByList(getByDestinationList(), BY_FINAL_DEST); 110 } 111 112 /** 113 * Sort by car wait count 114 * 115 * @return list of cars ordered by wait count 116 */ 117 public List<Car> getByWaitList() { 118 return getByList(getByIdList(), BY_WAIT); 119 } 120 121 @Override 122 public List<Car> getByPickupList() { 123 return getByList(getByDestinationList(), BY_PICKUP); 124 } 125 126 // The special sort options for cars 127 private static final int BY_LOAD = 30; 128 private static final int BY_KERNEL = 31; 129 private static final int BY_RWE = 32; // Return When Empty 130 private static final int BY_FINAL_DEST = 33; 131 private static final int BY_WAIT = 34; 132 private static final int BY_PICKUP = 35; 133 private static final int BY_HAZARD = 36; 134 private static final int BY_RWL = 37; // Return When loaded 135 private static final int BY_ROUTE = 38; 136 private static final int BY_DIVISION = 39; 137 138 // the name of the location and track is "split" 139 private static final int BY_SPLIT_FINAL_DEST = 40; 140 private static final int BY_SPLIT_LOCATION = 41; 141 private static final int BY_SPLIT_DESTINATION = 42; 142 143 // add car options to sort comparator 144 @Override 145 protected java.util.Comparator<Car> getComparator(int attribute) { 146 switch (attribute) { 147 case BY_LOAD: 148 return (c1, c2) -> (c1.getLoadName().compareToIgnoreCase(c2.getLoadName())); 149 case BY_KERNEL: 150 return (c1, c2) -> (c1.getKernelName().compareToIgnoreCase(c2.getKernelName())); 151 case BY_RWE: 152 return (c1, c2) -> (c1.getReturnWhenEmptyDestinationName() + c1.getReturnWhenEmptyDestTrackName()) 153 .compareToIgnoreCase( 154 c2.getReturnWhenEmptyDestinationName() + c2.getReturnWhenEmptyDestTrackName()); 155 case BY_RWL: 156 return (c1, c2) -> (c1.getReturnWhenLoadedDestinationName() + c1.getReturnWhenLoadedDestTrackName()) 157 .compareToIgnoreCase( 158 c2.getReturnWhenLoadedDestinationName() + c2.getReturnWhenLoadedDestTrackName()); 159 case BY_FINAL_DEST: 160 return (c1, c2) -> (c1.getFinalDestinationName() + c1.getFinalDestinationTrackName()) 161 .compareToIgnoreCase(c2.getFinalDestinationName() + c2.getFinalDestinationTrackName()); 162 case BY_ROUTE: 163 return (c1, c2) -> (c1.getRoutePath().compareToIgnoreCase(c2.getRoutePath())); 164 case BY_DIVISION: 165 return (c1, c2) -> (c1.getDivisionName().compareToIgnoreCase(c2.getDivisionName())); 166 case BY_WAIT: 167 return (c1, c2) -> (c1.getWait() - c2.getWait()); 168 case BY_PICKUP: 169 return (c1, c2) -> (c1.getPickupScheduleName().compareToIgnoreCase(c2.getPickupScheduleName())); 170 case BY_HAZARD: 171 return (c1, c2) -> ((c1.isHazardous() ? 1 : 0) - (c2.isHazardous() ? 1 : 0)); 172 case BY_SPLIT_FINAL_DEST: 173 return (c1, c2) -> (c1.getSplitFinalDestinationName() + c1.getSplitFinalDestinationTrackName()) 174 .compareToIgnoreCase( 175 c2.getSplitFinalDestinationName() + c2.getSplitFinalDestinationTrackName()); 176 case BY_SPLIT_LOCATION: 177 return (c1, c2) -> (c1.getStatus() + c1.getSplitLocationName() + c1.getSplitTrackName()) 178 .compareToIgnoreCase(c2.getStatus() + c2.getSplitLocationName() + c2.getSplitTrackName()); 179 case BY_SPLIT_DESTINATION: 180 return (c1, c2) -> (c1.getSplitDestinationName() + c1.getSplitDestinationTrackName()) 181 .compareToIgnoreCase(c2.getSplitDestinationName() + c2.getSplitDestinationTrackName()); 182 default: 183 return super.getComparator(attribute); 184 } 185 } 186 187 /** 188 * Return a list available cars (no assigned train or car already assigned 189 * to this train) on a route, cars are ordered least recently moved to most 190 * recently moved. Note that it is possible for a car to have a location, 191 * but no track assignment. 192 * 193 * @param train The Train to use. 194 * @return List of cars with no assigned train on a route 195 */ 196 public List<Car> getAvailableTrainList(Train train) { 197 List<Car> out = new ArrayList<>(); 198 Route route = train.getRoute(); 199 if (route == null) { 200 return out; 201 } 202 // get a list of locations served by this route 203 List<RouteLocation> routeList = route.getLocationsBySequenceList(); 204 // don't include Car at route destination 205 RouteLocation destination = null; 206 if (routeList.size() > 1) { 207 destination = routeList.get(routeList.size() - 1); 208 // However, if the destination is visited more than once, must 209 // include all cars 210 for (int i = 0; i < routeList.size() - 1; i++) { 211 if (destination.getName().equals(routeList.get(i).getName())) { 212 destination = null; // include cars at destination 213 break; 214 } 215 } 216 // pickup allowed at destination? Don't include cars in staging 217 if (destination != null && 218 destination.isPickUpAllowed() && 219 destination.getLocation() != null && 220 !destination.getLocation().isStaging()) { 221 destination = null; // include cars at destination 222 } 223 } 224 // get rolling stock by track priority, load priority and then by moves 225 List<Car> sortByPriority = sortByTrackPriority(sortByLoadPriority(getByMovesList())); 226 // now build list of available Car for this route 227 for (Car car : sortByPriority) { 228 // only use Car with a location 229 if (car.getLocation() == null) { 230 continue; 231 } 232 RouteLocation rl = route.getLastLocationByName(car.getLocationName()); 233 // get Car that don't have an assigned train, or the 234 // assigned train is this one 235 if (rl != null && rl != destination && (car.getTrain() == null || train.equals(car.getTrain()))) { 236 out.add(car); 237 } 238 } 239 return out; 240 } 241 242 // sorts the high priority cars to the start of the list 243 protected List<Car> sortByLoadPriority(List<Car> list) { 244 List<Car> out = new ArrayList<>(); 245 // move high priority cars to the start 246 for (Car car : list) { 247 if (car.getLoadPriority().equals(CarLoad.PRIORITY_HIGH)) { 248 out.add(car); 249 } 250 } 251 for (Car car : list) { 252 if (car.getLoadPriority().equals(CarLoad.PRIORITY_MEDIUM)) { 253 out.add(car); 254 } 255 } 256 // now load all of the remaining low priority cars 257 for (Car car : list) { 258 if (!out.contains(car)) { 259 out.add(car); 260 } 261 } 262 return out; 263 } 264 265 /** 266 * Provides a very sorted list of cars assigned to the train. Note that this 267 * isn't the final sort as the cars must be sorted by each location the 268 * train visits. 269 * <p> 270 * The sort priority is as follows: 271 * <ol> 272 * <li>Caboose or car with FRED to the end of the list, unless passenger. 273 * <li>Passenger cars have blocking numbers which places them relative to 274 * each other. Passenger cars with positive blocking numbers to the end of 275 * the list, but before cabooses or car with FRED. Passenger cars with 276 * negative blocking numbers are placed at the front of the train. 277 * <li>Car's destination (alphabetical by location and track name or by 278 * track blocking order) 279 * <li>Car is hazardous (hazardous placed after a non-hazardous car) 280 * <li>Car's current location (alphabetical by location and track name) 281 * <li>Car's final destination (alphabetical by location and track name) 282 * </ol> 283 * <p> 284 * Cars in a kernel are placed together by their kernel blocking numbers, 285 * except if they are type passenger. The kernel's position in the list is 286 * based on the lead car in the kernel. 287 * <p> 288 * If the train is to be blocked by track blocking order, all of the tracks 289 * at that location need a blocking number greater than 0. 290 * 291 * @param train The selected Train. 292 * @return Ordered list of cars assigned to the train 293 */ 294 public List<Car> getByTrainDestinationList(Train train) { 295 List<Car> byFinal = getByList(getList(train), BY_SPLIT_FINAL_DEST); 296 List<Car> byLocation = getByList(byFinal, BY_SPLIT_LOCATION); 297 List<Car> byHazard = getByList(byLocation, BY_HAZARD); 298 List<Car> byDestination = getByList(byHazard, BY_SPLIT_DESTINATION); 299 // now place cabooses, cars with FRED, and passenger cars at the rear of the 300 // train 301 List<Car> out = new ArrayList<>(); 302 int lastCarsIndex = 0; // incremented each time a car is added to the end of the list 303 for (Car car : byDestination) { 304 if (car.getKernel() != null && !car.isLead() && !car.isPassenger()) { 305 continue; // not the lead car, skip for now. 306 } 307 if (!car.isCaboose() && !car.hasFred() && !car.isPassenger()) { 308 // sort order based on train direction when serving track, low to high if West 309 // or North bound trains 310 if (car.getDestinationTrack() != null && car.getDestinationTrack().getBlockingOrder() > 0) { 311 for (int j = 0; j < out.size(); j++) { 312 if (out.get(j).getDestinationTrack() == null) { 313 continue; 314 } 315 if (car.getRouteDestination() != null && 316 (car.getRouteDestination().getTrainDirectionString().equals(RouteLocation.WEST_DIR) || 317 car.getRouteDestination().getTrainDirectionString() 318 .equals(RouteLocation.NORTH_DIR))) { 319 if (car.getDestinationTrack().getBlockingOrder() < out.get(j).getDestinationTrack() 320 .getBlockingOrder()) { 321 out.add(j, car); 322 break; 323 } 324 // Train is traveling East or South when setting out the car 325 } else { 326 if (car.getDestinationTrack().getBlockingOrder() > out.get(j).getDestinationTrack() 327 .getBlockingOrder()) { 328 out.add(j, car); 329 break; 330 } 331 } 332 } 333 } 334 if (!out.contains(car)) { 335 out.add(out.size() - lastCarsIndex, car); 336 } 337 } else if (car.isPassenger()) { 338 if (car.getBlocking() < 0) { 339 // block passenger cars with negative blocking numbers at 340 // front of train 341 int index; 342 for (index = 0; index < out.size(); index++) { 343 Car carTest = out.get(index); 344 if (!carTest.isPassenger() || carTest.getBlocking() > car.getBlocking()) { 345 break; 346 } 347 } 348 out.add(index, car); 349 } else { 350 // block passenger cars at end of list, but before cabooses 351 // or car with FRED 352 int index; 353 for (index = 0; index < lastCarsIndex; index++) { 354 Car carTest = out.get(out.size() - 1 - index); 355 log.debug("Car ({}) has blocking number: {}", carTest.toString(), carTest.getBlocking()); 356 if (carTest.isPassenger() && 357 !carTest.isCaboose() && 358 !carTest.hasFred() && 359 carTest.getBlocking() < car.getBlocking()) { 360 break; 361 } 362 } 363 out.add(out.size() - index, car); 364 lastCarsIndex++; 365 } 366 } else if (car.isCaboose() || car.hasFred()) { 367 out.add(car); // place at end of list 368 lastCarsIndex++; 369 } 370 // group the cars in the kernel together, except passenger 371 if (car.isLead()) { 372 int index = out.indexOf(car); 373 int numberOfCars = 1; // already added the lead car to the list 374 for (Car kcar : car.getKernel().getCars()) { 375 if (car != kcar && !kcar.isPassenger()) { 376 // Block cars in kernel 377 for (int j = 0; j < numberOfCars; j++) { 378 if (kcar.getBlocking() < out.get(index + j).getBlocking()) { 379 out.add(index + j, kcar); 380 break; 381 } 382 } 383 if (!out.contains(kcar)) { 384 out.add(index + numberOfCars, kcar); 385 } 386 numberOfCars++; 387 if (car.hasFred() || car.isCaboose() || car.isPassenger() && car.getBlocking() > 0) { 388 lastCarsIndex++; // place entire kernel at the end of list 389 } 390 } 391 } 392 } 393 } 394 return out; 395 } 396 397 /** 398 * Get a list of car road names where the car was flagged as a caboose. 399 * 400 * @return List of caboose road names. 401 */ 402 public List<String> getCabooseRoadNames() { 403 List<String> names = new ArrayList<>(); 404 Enumeration<String> en = _hashTable.keys(); 405 while (en.hasMoreElements()) { 406 Car car = getById(en.nextElement()); 407 if (car.isCaboose() && !names.contains(car.getRoadName())) { 408 names.add(car.getRoadName()); 409 } 410 } 411 java.util.Collections.sort(names); 412 return names; 413 } 414 415 /** 416 * Get a list of car road names where the car was flagged with FRED 417 * 418 * @return List of road names of cars with FREDs 419 */ 420 public List<String> getFredRoadNames() { 421 List<String> names = new ArrayList<>(); 422 Enumeration<String> en = _hashTable.keys(); 423 while (en.hasMoreElements()) { 424 Car car = getById(en.nextElement()); 425 if (car.hasFred() && !names.contains(car.getRoadName())) { 426 names.add(car.getRoadName()); 427 } 428 } 429 java.util.Collections.sort(names); 430 return names; 431 } 432 433 /** 434 * Replace car loads 435 * 436 * @param type type of car 437 * @param oldLoadName old load name 438 * @param newLoadName new load name 439 */ 440 public void replaceLoad(String type, String oldLoadName, String newLoadName) { 441 List<Car> cars = getList(); 442 for (Car car : cars) { 443 if (car.getTypeName().equals(type) && car.getLoadName().equals(oldLoadName)) { 444 if (newLoadName != null) { 445 car.setLoadName(newLoadName); 446 } else { 447 car.setLoadName(InstanceManager.getDefault(CarLoads.class).getDefaultEmptyName()); 448 } 449 } 450 if (car.getTypeName().equals(type) && car.getReturnWhenEmptyLoadName().equals(oldLoadName)) { 451 if (newLoadName != null) { 452 car.setReturnWhenEmptyLoadName(newLoadName); 453 } else { 454 car.setReturnWhenEmptyLoadName(InstanceManager.getDefault(CarLoads.class).getDefaultEmptyName()); 455 } 456 } 457 if (car.getTypeName().equals(type) && car.getReturnWhenLoadedLoadName().equals(oldLoadName)) { 458 if (newLoadName != null) { 459 car.setReturnWhenLoadedLoadName(newLoadName); 460 } else { 461 car.setReturnWhenLoadedLoadName(InstanceManager.getDefault(CarLoads.class).getDefaultLoadName()); 462 } 463 } 464 } 465 } 466 467 public List<Car> getCarsLocationUnknown() { 468 List<Car> mias = new ArrayList<>(); 469 for (Car car : getByIdList()) { 470 if (car.isLocationUnknown()) { 471 mias.add(car); // return unknown location car 472 } 473 } 474 return mias; 475 } 476 477 /** 478 * Determines a car's weight in ounces based on car's scale length 479 * 480 * @param carLength Car's scale length 481 * @return car's weight in ounces 482 * @throws NumberFormatException if length isn't a number 483 */ 484 public static String calculateCarWeight(String carLength) throws NumberFormatException { 485 double doubleCarLength = Double.parseDouble(carLength) * 12 / Setup.getScaleRatio(); 486 double doubleCarWeight = (Setup.getInitalWeight() + doubleCarLength * Setup.getAddWeight()) / 1000; 487 NumberFormat nf = NumberFormat.getNumberInstance(); 488 nf.setMaximumFractionDigits(1); 489 return nf.format(doubleCarWeight); // car weight in ounces. 490 } 491 492 /** 493 * Used to determine if any car has been assigned a division 494 * 495 * @return true if any car has been assigned a division, otherwise false 496 */ 497 public boolean isThereDivisions() { 498 for (Car car : getList()) { 499 if (car.getDivision() != null) { 500 return true; 501 } 502 } 503 return false; 504 } 505 506 /** 507 * Used to determine if there are clone cars. 508 * 509 * @return true if there are clone cars, otherwise false. 510 */ 511 public boolean isThereClones() { 512 for (Car car : getList()) { 513 if (car.isClone()) { 514 return true; 515 } 516 } 517 return false; 518 } 519 520 /** 521 * Creates a clone for the car, and clones if the car is part of a kernel. 522 * Note that a car have have multiple clones. 523 * 524 * @param car The car to clone 525 * @param track The destination track for the clones 526 * @param train The train transporting the clones 527 * @param startTime The date and time the clones were moved 528 * @return clone for this car 529 */ 530 public Car createClone(Car car, Track track, Train train, Date startTime) { 531 int cloneCreationOrder = getCloneCreationOrder(); 532 String creationOrder = padNumber(cloneCreationOrder); 533 Car cloneCar = car.copy(); 534 cloneCar.setNumber(car.getNumber() + Car.CLONE + creationOrder); 535 cloneCar.setClone(true); 536 // register car before setting location so the car gets logged 537 register(cloneCar); 538 cloneCar.setLocation(car.getLocation(), car.getTrack(), RollingStock.FORCE); 539 // for reset 540 cloneCar.setPreviousFinalDestination(car.getPreviousFinalDestination()); 541 cloneCar.setPreviousFinalDestinationTrack(car.getPreviousFinalDestinationTrack()); 542 cloneCar.setPreviousScheduleId(car.getScheduleItemId()); 543 cloneCar.setLastRouteId(car.getLastRouteId()); 544 cloneCar.setMoves(car.getMoves()); 545 if (car.getKernel() != null) { 546 String kernelName = car.getKernelName() + Car.CLONE + creationOrder; 547 Kernel kernel = InstanceManager.getDefault(KernelManager.class).newKernel(kernelName); 548 cloneCar.setKernel(kernel); 549 for (Car kar : car.getKernel().getCars()) { 550 if (kar != car) { 551 Car nCar = kar.copy(); 552 nCar.setNumber(kar.getNumber() + Car.CLONE + creationOrder); 553 nCar.setClone(true); 554 nCar.setKernel(kernel); 555 nCar.setMoves(kar.getMoves()); 556 register(nCar); 557 nCar.setLocation(car.getLocation(), car.getTrack(), RollingStock.FORCE); 558 // for reset 559 nCar.setPreviousFinalDestination(car.getPreviousFinalDestination()); 560 nCar.setPreviousFinalDestinationTrack(car.getPreviousFinalDestinationTrack()); 561 // move car to new location for later pick up 562 kar.setLocation(track.getLocation(), track, RollingStock.FORCE); 563 kar.setLastTrain(train); 564 kar.setLastLocationId(car.getLocationId()); 565 kar.setLastTrackId(car.getTrackId()); 566 kar.setLastDate(startTime); 567 kar.setMoves(kar.getMoves() + 1); // bump count 568 kar.setCloneOrder(cloneCreationOrder); // for reset 569 } 570 } 571 } 572 // move car to new location for later pick up 573 car.setLocation(track.getLocation(), track, RollingStock.FORCE); 574 car.setLastTrain(train); 575 car.setLastLocationId(cloneCar.getLocationId()); 576 car.setLastTrackId(cloneCar.getTrackId()); 577 car.setLastRouteId(train.getRoute().getId()); 578 // this car was moved during the build process 579 car.setLastDate(startTime); 580 car.setMoves(car.getMoves() + 1); // bump count 581 car.setCloneOrder(cloneCreationOrder); // for reset 582 car.setDestination(null, null); 583 return cloneCar; 584 } 585 586 int _commentLength = 0; 587 588 @edu.umd.cs.findbugs.annotations.SuppressFBWarnings( value="SLF4J_FORMAT_SHOULD_BE_CONST", 589 justification="I18N of Info Message") 590 public int getMaxCommentLength() { 591 if (_commentLength == 0) { 592 _commentLength = TrainManifestHeaderText.getStringHeader_Comment().length(); 593 String comment = ""; 594 Car carMax = null; 595 for (Car car : getList()) { 596 if (car.getComment().length() > _commentLength) { 597 _commentLength = car.getComment().length(); 598 comment = car.getComment(); 599 carMax = car; 600 } 601 } 602 if (carMax != null) { 603 log.info(Bundle.getMessage("InfoMaxComment", carMax.toString(), comment, _commentLength)); 604 } 605 } 606 return _commentLength; 607 } 608 609 public void load(Element root) { 610 if (root.getChild(Xml.CARS) != null) { 611 List<Element> eCars = root.getChild(Xml.CARS).getChildren(Xml.CAR); 612 log.debug("readFile sees {} cars", eCars.size()); 613 for (Element eCar : eCars) { 614 register(new Car(eCar)); 615 } 616 } 617 } 618 619 /** 620 * Create an XML element to represent this Entry. This member has to remain 621 * synchronized with the detailed DTD in operations-cars.dtd. 622 * 623 * @param root The common Element for operations-cars.dtd. 624 */ 625 public void store(Element root) { 626 // nothing to save under options 627 root.addContent(new Element(Xml.OPTIONS)); 628 629 Element values; 630 root.addContent(values = new Element(Xml.CARS)); 631 // add entries 632 List<Car> carList = getByIdList(); 633 for (Car rs : carList) { 634 Car car = rs; 635 values.addContent(car.store()); 636 } 637 } 638 639 protected void setDirtyAndFirePropertyChange(String p, Object old, Object n) { 640 // Set dirty 641 InstanceManager.getDefault(CarManagerXml.class).setDirty(true); 642 super.firePropertyChange(p, old, n); 643 } 644 645 @Override 646 public void propertyChange(PropertyChangeEvent evt) { 647 if (evt.getPropertyName().equals(Car.COMMENT_CHANGED_PROPERTY)) { 648 _commentLength = 0; 649 } 650 super.propertyChange(evt); 651 } 652 653 private final static Logger log = LoggerFactory.getLogger(CarManager.class); 654 655 @Override 656 public void initialize() { 657 InstanceManager.getDefault(OperationsSetupXml.class); // load setup 658 // create manager to load cars and their attributes 659 InstanceManager.getDefault(CarManagerXml.class); 660 } 661 662}