001package jmri.jmrit.operations.trains; 002 003import java.io.*; 004import java.nio.charset.StandardCharsets; 005import java.text.MessageFormat; 006import java.util.*; 007 008import org.slf4j.Logger; 009import org.slf4j.LoggerFactory; 010 011import jmri.InstanceManager; 012import jmri.jmrit.operations.locations.Location; 013import jmri.jmrit.operations.locations.Track; 014import jmri.jmrit.operations.rollingstock.cars.*; 015import jmri.jmrit.operations.rollingstock.engines.Engine; 016import jmri.jmrit.operations.routes.Route; 017import jmri.jmrit.operations.routes.RouteLocation; 018import jmri.jmrit.operations.setup.Control; 019import jmri.jmrit.operations.setup.Setup; 020import jmri.jmrit.operations.trains.schedules.TrainSchedule; 021import jmri.jmrit.operations.trains.schedules.TrainScheduleManager; 022import jmri.jmrit.operations.trains.trainbuilder.TrainCommon; 023import jmri.util.FileUtil; 024 025/** 026 * Builds a switch list for a location on the railroad 027 * 028 * @author Daniel Boudreau (C) Copyright 2008, 2011, 2012, 2013, 2015, 2024, 029 * 2026 030 */ 031public class TrainSwitchLists extends TrainCommon { 032 033 TrainManager trainManager = InstanceManager.getDefault(TrainManager.class); 034 private static final char FORM_FEED = '\f'; 035 private static final boolean IS_PRINT_HEADER = true; 036 037 String messageFormatText = ""; // the text being formated in case there's an exception 038 039 /** 040 * Builds a switch list for a location showing the work by train arrival 041 * time. If not running in real time, new train work is appended to the end 042 * of the file. User has the ability to modify the text of the messages 043 * which can cause an IllegalArgumentException. Some messages have more 044 * arguments than the default message allowing the user to customize the 045 * message to their liking. There also an option to list all of the car work 046 * by track name. This option is only available in real time and is shown 047 * after the switch list by train. 048 * 049 * @param location The Location needing a switch list 050 */ 051 public void buildSwitchList(Location location) { 052 053 boolean append = false; // add text to end of file when true 054 boolean checkFormFeed = true; // used to determine if FF needed between trains 055 056 // Append switch list data if not operating in real time 057 if (!Setup.isSwitchListRealTime()) { 058 if (!location.getStatus().equals(Location.MODIFIED) && !Setup.isSwitchListAllTrainsEnabled()) { 059 return; // nothing to add 060 } 061 append = location.getSwitchListState() == Location.SW_APPEND; 062 location.setSwitchListState(Location.SW_APPEND); 063 } 064 065 log.debug("Append: {} for location ({})", append, location.getName()); 066 067 // create switch list file 068 File file = InstanceManager.getDefault(TrainManagerXml.class).createSwitchListFile(location.getName()); 069 070 PrintWriter fileOut = null; 071 try { 072 fileOut = new PrintWriter(new BufferedWriter( 073 new OutputStreamWriter(new FileOutputStream(file, append), StandardCharsets.UTF_8)), true); 074 } catch (IOException e) { 075 log.error("Can not open switchlist file: {}", e.getLocalizedMessage()); 076 return; 077 } 078 try { 079 // build header 080 if (!append) { 081 newLine(fileOut, Setup.getRailroadName()); 082 newLine(fileOut); 083 newLine(fileOut, MessageFormat.format(messageFormatText = TrainSwitchListText.getStringSwitchListFor(), 084 new Object[]{location.getSplitName()})); 085 if (!location.getSwitchListCommentWithColor().isEmpty()) { 086 newLine(fileOut, location.getSwitchListCommentWithColor()); 087 } 088 } else { 089 newLine(fileOut); 090 } 091 092 // get a list of built trains sorted by arrival time 093 List<Train> trains = trainManager.getTrainsArrivingThisLocationList(location, true); 094 List<Train> trainsAdded = new ArrayList<>(); 095 for (Train train : trains) { 096 if (!Setup.isSwitchListRealTime() && train.getSwitchListStatus().equals(Train.PRINTED)) { 097 continue; // already printed this train 098 } 099 if (Setup.getSwitchListPageFormat().equals(Setup.PAGE_PER_TRAIN) && 100 Collections.frequency(trainsAdded, train) > 0) { 101 continue; 102 } 103 Route route = train.getRoute(); 104 // TODO throw exception? only built trains should be in the list, so no route is 105 // an error 106 if (route == null) { 107 continue; // no route for this train 108 } // determine if train works this location 109 int count = Collections.frequency(trainsAdded, train); 110 boolean works = isThereWorkAtLocation(train, location); 111 if (!works && !Setup.isSwitchListAllTrainsEnabled()) { 112 log.debug("No work for train ({}) at location ({})", train.getName(), location.getName()); 113 continue; 114 } 115 // we're now going to add to the switch list 116 if (checkFormFeed) { 117 if (append && !Setup.getSwitchListPageFormat().equals(Setup.PAGE_NORMAL)) { 118 fileOut.write(FORM_FEED); 119 } 120 if (Setup.isPrintValidEnabled()) { 121 newLine(fileOut, getValid()); 122 } 123 } else if (!Setup.getSwitchListPageFormat().equals(Setup.PAGE_NORMAL)) { 124 fileOut.write(FORM_FEED); 125 } 126 checkFormFeed = false; // done with FF for this train 127 _pickupCars = false; // when true there was a car pick up 128 _dropCars = false; // when true there was a car set out 129 int stops = 0; 130 boolean trainDone = false; 131 // get engine and car lists 132 List<Engine> engineList = engineManager.getByTrainBlockingList(train); 133 List<Car> carList = carManager.getByTrainDestinationList(train); 134 List<RouteLocation> routeList = route.getLocationsBySequenceList(); 135 RouteLocation rlPrevious = null; 136 for (RouteLocation rl : routeList) { 137 if (!rl.getSplitName().equals(location.getSplitName())) { 138 rlPrevious = rl; 139 if (Setup.getSwitchListPageFormat().equals(Setup.PAGE_PER_TRAIN)) { 140 _pickupCars = false; // reset 141 _dropCars = false; 142 } 143 continue; 144 } 145 146 if (train.getExpectedArrivalTime(rl).equals(Train.ALREADY_SERVICED) && 147 train.getCurrentRouteLocation() != rl) { 148 trainDone = true; 149 } 150 151 if (count == stops || Setup.getSwitchListPageFormat().equals(Setup.PAGE_PER_TRAIN)) { 152 if (rlPrevious == null || 153 !rl.getSplitName().equals(rlPrevious.getSplitName())) { 154 // does train visit this location more than once? 155 int visits = Collections.frequency(trains, train); 156 if (visits == 1) { 157 firstTimeMessages(fileOut, train, rl); 158 } else { 159 // multiple visits to this location 160 multipleVisitMessages(fileOut, train, rl, rlPrevious, stops + 1, visits); 161 } 162 } else { 163 // Does the train reverse direction? 164 reverseDirectionMessage(fileOut, train, rl, rlPrevious); 165 } 166 printWork(fileOut, train, rl, carList, engineList); 167 // done with work, now print summary for this location if we're done 168 if (rl != train.getTrainTerminatesRouteLocation()) { 169 RouteLocation nextRl = train.getRoute().getNextRouteLocation(rl); 170 if (!rl.getSplitName().equals(nextRl.getSplitName())) { 171 // print departure text if not a switcher 172 if (!train.isLocalSwitcher() && !trainDone) { 173 departureMessages(fileOut, train, rl); 174 } 175 // report if no pick ups or set outs or train has left 176 trainSummaryMessages(fileOut, train, location, trainDone, stops); 177 } 178 } else { 179 // report if no pick ups or set outs or train has left 180 trainSummaryMessages(fileOut, train, location, trainDone, stops); 181 } 182 } 183 if (rl != train.getTrainTerminatesRouteLocation()) { 184 RouteLocation nextRl = train.getRoute().getNextRouteLocation(rl); 185 if (!rl.getSplitName().equals(nextRl.getSplitName())) { 186 stops++; 187 } 188 } 189 // save current location in case there's back to back location with the same name 190 rlPrevious = rl; 191 } 192 trainsAdded.add(train); 193 } 194 195 // now report car movement by tracks at location 196 reportByTrack(fileOut, location); 197 198 } catch ( 199 200 IllegalArgumentException e) { 201 newLine(fileOut, Bundle.getMessage("ErrorIllegalArgument", 202 Bundle.getMessage("TitleSwitchListText"), e.getLocalizedMessage())); 203 newLine(fileOut, messageFormatText); 204 log.error("Illegal argument", e); 205 } 206 207 // Are there any cars that need to be found? 208 addCarsLocationUnknown(fileOut, !IS_MANIFEST); 209 fileOut.flush(); 210 fileOut.close(); 211 location.setStatus(Location.UPDATED); 212 } 213 214 private String getValid() { 215 String valid = MessageFormat.format(messageFormatText = TrainManifestText.getStringValid(), 216 new Object[]{getDate(true)}); 217 if (Setup.isPrintTrainScheduleNameEnabled()) { 218 TrainSchedule sch = InstanceManager.getDefault(TrainScheduleManager.class).getActiveSchedule(); 219 if (sch != null) { 220 valid = valid + " (" + sch.getName() + ")"; 221 } 222 } 223 return valid; 224 } 225 226 /* 227 * Messages for the switch list when the train first arrives 228 */ 229 private void firstTimeMessages(PrintWriter fileOut, Train train, RouteLocation rl) { 230 newLine(fileOut); 231 newLine(fileOut, 232 MessageFormat.format(messageFormatText = TrainSwitchListText.getStringScheduledWork(), 233 new Object[]{train.getSplitName(), train.getDescription()})); 234 newLine(fileOut, getSwitchListTrainStatus(train, rl)); 235 } 236 237 /* 238 * Messages when a train services the location two or more times 239 */ 240 private void multipleVisitMessages(PrintWriter fileOut, Train train, RouteLocation rl, RouteLocation rlPrevious, 241 int stops, int visits) { 242 newLine(fileOut); 243 if (stops == 1) { 244 newLine(fileOut, MessageFormat.format(messageFormatText = TrainSwitchListText.getStringTrainVisits(), 245 new Object[]{train.getName(), rl.getLocation().getName(), visits})); 246 firstTimeMessages(fileOut, train, rl); 247 } else { 248 String expectedArrivalTime = train.getExpectedArrivalTime(rl); 249 if (train.isTrainEnRoute()) { 250 if (expectedArrivalTime.equals(Train.ALREADY_SERVICED)) { 251 // Visit number {0} for train ({1}) 252 newLine(fileOut, 253 MessageFormat.format( 254 messageFormatText = TrainSwitchListText.getStringVisitNumberDone(), 255 new Object[]{stops, train.getSplitName(), 256 train.getDescription()})); 257 } else if (rl != train.getTrainTerminatesRouteLocation()) { 258 // Visit number {0} for train ({1}) expect to arrive in {2}, arrives {3}bound 259 newLine(fileOut, MessageFormat.format( 260 messageFormatText = TrainSwitchListText.getStringVisitNumberDeparted(), 261 new Object[]{stops, train.getSplitName(), expectedArrivalTime, 262 rl.getTrainDirectionString(), train.getDescription()})); 263 } else { 264 // Visit number {0} for train ({1}) expect to arrive in {2}, terminates {3} 265 newLine(fileOut, 266 MessageFormat.format( 267 messageFormatText = TrainSwitchListText 268 .getStringVisitNumberTerminatesDeparted(), 269 new Object[]{stops, train.getSplitName(), 270 expectedArrivalTime, rl.getSplitName(), train.getDescription()})); 271 } 272 } else { 273 // train hasn't departed 274 if (rl != train.getTrainTerminatesRouteLocation()) { 275 // Visit number {0} for train ({1}) expected arrival {2}, arrives {3}bound 276 newLine(fileOut, 277 MessageFormat.format( 278 messageFormatText = TrainSwitchListText.getStringVisitNumber(), 279 new Object[]{stops, train.getSplitName(), 280 expectedArrivalTime, rl.getTrainDirectionString(), 281 train.getDescription()})); 282 if (Setup.isUseSwitchListDepartureTimeEnabled()) { 283 // Departs {0} {1}bound at {2} 284 newLine(fileOut, MessageFormat.format( 285 messageFormatText = TrainSwitchListText.getStringDepartsAt(), 286 new Object[]{splitString(rl.getName()), 287 rl.getTrainDirectionString(), 288 train.getExpectedDepartureTime(rl)})); 289 } 290 } else { 291 // Visit number {0} for train ({1}) expected arrival {2}, terminates {3} 292 newLine(fileOut, MessageFormat.format( 293 messageFormatText = TrainSwitchListText.getStringVisitNumberTerminates(), 294 new Object[]{stops, train.getSplitName(), expectedArrivalTime, 295 rl.getSplitName(), train.getDescription()})); 296 } 297 } 298 } 299 } 300 301 private void reverseDirectionMessage(PrintWriter fileOut, Train train, RouteLocation rl, RouteLocation rlPrevious) { 302 // Does the train reverse direction? 303 if (rl.getTrainDirection() != rlPrevious.getTrainDirection() && 304 !TrainSwitchListText.getStringTrainDirectionChange().isEmpty()) { 305 // Train ({0}) direction change, departs {1}bound 306 newLine(fileOut, 307 MessageFormat.format( 308 messageFormatText = TrainSwitchListText.getStringTrainDirectionChange(), 309 new Object[]{train.getSplitName(), rl.getTrainDirectionString(), 310 train.getDescription(), train.getTrainTerminatesName()})); 311 } 312 } 313 314 private void printWork(PrintWriter fileOut, Train train, RouteLocation rl, List<Car> carList, 315 List<Engine> engineList) { 316 // add route location comment 317 if (Setup.isSwitchListRouteLocationCommentEnabled() && !rl.getComment().trim().isEmpty()) { 318 newLine(fileOut, rl.getCommentWithColor()); 319 } 320 321 printTrackComments(fileOut, rl, carList, !IS_MANIFEST); 322 323 if (isThereWorkAtLocation(carList, engineList, rl)) { 324 // now print out the work for this location 325 if (Setup.getManifestFormat().equals(Setup.STANDARD_FORMAT)) { 326 pickupEngines(fileOut, engineList, rl, !IS_MANIFEST); 327 // if switcher show loco drop at end of list 328 if (train.isLocalSwitcher() || Setup.isPrintLocoLastEnabled()) { 329 blockCarsByTrack(fileOut, train, carList, rl, IS_PRINT_HEADER, !IS_MANIFEST); 330 dropEngines(fileOut, engineList, rl, !IS_MANIFEST); 331 } else { 332 dropEngines(fileOut, engineList, rl, !IS_MANIFEST); 333 blockCarsByTrack(fileOut, train, carList, rl, IS_PRINT_HEADER, !IS_MANIFEST); 334 } 335 } else if (Setup.getManifestFormat().equals(Setup.TWO_COLUMN_FORMAT)) { 336 // if switcher show loco drop at end of list 337 if (train.isLocalSwitcher() || 338 Setup.isPrintLocoLastEnabled() && train.getTrainTerminatesRouteLocation() == rl) { 339 blockCarsTwoColumn(fileOut, train, carList, rl, IS_PRINT_HEADER, !IS_MANIFEST); 340 blockLocosTwoColumn(fileOut, engineList, rl, !IS_MANIFEST); 341 } else { 342 blockLocosTwoColumn(fileOut, engineList, rl, !IS_MANIFEST); 343 blockCarsTwoColumn(fileOut, train, carList, rl, IS_PRINT_HEADER, !IS_MANIFEST); 344 } 345 } else { 346 // if switcher show loco drop at end of list 347 if (train.isLocalSwitcher() || 348 Setup.isPrintLocoLastEnabled() && train.getTrainTerminatesRouteLocation() == rl) { 349 blockCarsByTrackNameTwoColumn(fileOut, train, carList, rl, IS_PRINT_HEADER, !IS_MANIFEST); 350 blockLocosByTrackNameTwoColumn(fileOut, engineList, rl, !IS_MANIFEST); 351 } else { 352 blockLocosByTrackNameTwoColumn(fileOut, engineList, rl, !IS_MANIFEST); 353 blockCarsByTrackNameTwoColumn(fileOut, train, carList, rl, IS_PRINT_HEADER, !IS_MANIFEST); 354 } 355 } 356 // print horizontal line if there was work and enabled 357 printHorizontalLine3(fileOut, !IS_MANIFEST); 358 } 359 } 360 361 /* 362 * Train departure messages at the end of the switch list 363 */ 364 private void departureMessages(PrintWriter fileOut, Train train, RouteLocation rl) { 365 String trainDeparts = ""; 366 if (Setup.isPrintLoadsAndEmptiesEnabled()) { 367 int emptyCars = train.getNumberEmptyCarsInTrain(rl); 368 // Train departs {0} {1}bound with {2} loads, {3} empties, {4} {5}, {6} tons 369 trainDeparts = MessageFormat.format(TrainSwitchListText.getStringTrainDepartsLoads(), 370 new Object[]{rl.getSplitName(), 371 rl.getTrainDirectionString(), 372 train.getNumberCarsInTrain(rl) - emptyCars, emptyCars, 373 train.getTrainLength(rl), Setup.getLengthUnit().toLowerCase(), 374 train.getTrainWeight(rl), train.getTrainTerminatesName(), 375 train.getSplitName()}); 376 } else { 377 // Train departs {0} {1}bound with {2} cars, {3} {4}, {5} tons 378 trainDeparts = MessageFormat.format(TrainSwitchListText.getStringTrainDepartsCars(), 379 new Object[]{rl.getSplitName(), 380 rl.getTrainDirectionString(), train.getNumberCarsInTrain(rl), 381 train.getTrainLength(rl), Setup.getLengthUnit().toLowerCase(), 382 train.getTrainWeight(rl), train.getTrainTerminatesName(), 383 train.getSplitName()}); 384 } 385 newLine(fileOut, trainDeparts); 386 } 387 388 private void trainSummaryMessages(PrintWriter fileOut, Train train, Location location, boolean trainDone, 389 int stops) { 390 if (trainDone && !_pickupCars && !_dropCars) { 391 // Default message: Train ({0}) has serviced this location 392 newLine(fileOut, MessageFormat.format(messageFormatText = TrainSwitchListText.getStringTrainDone(), 393 new Object[]{train.getSplitName(), train.getDescription(), 394 location.getSplitName()})); 395 } else { 396 if (!_pickupCars) { 397 // Default message: No car pick ups for train ({0}) at this location 398 newLine(fileOut, 399 MessageFormat.format(messageFormatText = TrainSwitchListText.getStringNoCarPickUps(), 400 new Object[]{train.getSplitName(), train.getDescription(), 401 location.getSplitName()})); 402 } 403 if (!_dropCars) { 404 // Default message: No car set outs for train ({0}) at this location 405 newLine(fileOut, 406 MessageFormat.format(messageFormatText = TrainSwitchListText.getStringNoCarDrops(), 407 new Object[]{train.getSplitName(), train.getDescription(), 408 location.getSplitName()})); 409 } 410 } 411 } 412 413 private void reportByTrack(PrintWriter fileOut, Location location) { 414 if (Setup.isPrintTrackSummaryEnabled() && Setup.isSwitchListRealTime()) { 415 clearUtilityCarTypes(); // list utility cars by quantity 416 if (Setup.getSwitchListPageFormat().equals(Setup.PAGE_NORMAL)) { 417 newLine(fileOut); 418 newLine(fileOut); 419 } else { 420 fileOut.write(FORM_FEED); 421 } 422 newLine(fileOut, 423 MessageFormat.format(messageFormatText = TrainSwitchListText.getStringSwitchListByTrack(), 424 new Object[]{location.getSplitName()})); 425 426 // we only need the cars delivered to or at this location 427 List<Car> rsList = carManager.getByTrainList(); 428 List<Car> carList = new ArrayList<>(); 429 for (Car rs : rsList) { 430 if ((rs.getLocation() != null && 431 rs.getLocation().getSplitName().equals(location.getSplitName())) || 432 (rs.getDestination() != null && 433 rs.getSplitDestinationName().equals(location.getSplitName()))) 434 carList.add(rs); 435 } 436 // locations and tracks can have "similar" names, only list track names once 437 List<String> trackNames = new ArrayList<>(); 438 for (Location loc : locationManager.getLocationsByNameList()) { 439 if (!loc.getSplitName().equals(location.getSplitName())) 440 continue; 441 for (Track track : loc.getTracksByBlockingOrderList(null)) { 442 String trackName = track.getSplitName(); 443 if (trackNames.contains(trackName)) 444 continue; 445 trackNames.add(trackName); 446 447 String trainName = ""; // for printing train message once 448 newLine(fileOut); 449 newLine(fileOut, trackName); // print out just the track name 450 // now show the cars pickup and holds for this track 451 for (Car car : carList) { 452 if (!car.getSplitTrackName().equals(trackName) || car.isLocalMove()) { 453 continue; 454 } 455 // is the car scheduled for pickup? 456 if (car.getRouteLocation() != null) { 457 if (car.getRouteLocation().getLocation().getSplitName() 458 .equals(location.getSplitName())) { 459 // cars are sorted by train name, print train message once 460 if (!trainName.equals(car.getTrainName())) { 461 trainName = car.getTrainName(); 462 newLine(fileOut, MessageFormat.format( 463 messageFormatText = TrainSwitchListText.getStringScheduledWork(), 464 new Object[]{car.getTrainName(), car.getTrain().getDescription()})); 465 printPickupCarHeader(fileOut, !IS_MANIFEST, !IS_TWO_COLUMN_TRACK); 466 } 467 if (car.isUtility()) { 468 pickupUtilityCars(fileOut, carList, car, false, !IS_MANIFEST); 469 } else { 470 pickUpCar(fileOut, car, !IS_MANIFEST); 471 } 472 } 473 // car holds 474 } else if (car.isUtility()) { 475 String s = pickupUtilityCars(carList, car, !IS_MANIFEST, !IS_TWO_COLUMN_TRACK); 476 if (s != null) { 477 newLine(fileOut, TrainSwitchListText.getStringHoldCar().split("\\{")[0] + s.trim()); // NOI18N 478 } 479 } else { 480 newLine(fileOut, 481 MessageFormat.format(messageFormatText = TrainSwitchListText.getStringHoldCar(), 482 new Object[]{ 483 padAndTruncateIfNeeded(car.getRoadName(), 484 InstanceManager.getDefault(CarRoads.class) 485 .getMaxNameLength()), 486 padAndTruncateIfNeeded( 487 TrainCommon.splitString(car.getNumber()), 488 Control.max_len_string_print_road_number), 489 padAndTruncateIfNeeded( 490 car.getTypeName().split(TrainCommon.HYPHEN)[0], 491 InstanceManager.getDefault(CarTypes.class) 492 .getMaxNameLength()), 493 padAndTruncateIfNeeded( 494 car.getLength() + Setup.getLengthUnitAbv(), 495 Control.max_len_string_length_name), 496 padAndTruncateIfNeeded(car.getLoadName(), 497 InstanceManager.getDefault(CarLoads.class) 498 .getMaxNameLength()), 499 padAndTruncateIfNeeded(trackName, 500 locationManager.getMaxTrackNameLength()), 501 padAndTruncateIfNeeded(car.getColor(), InstanceManager 502 .getDefault(CarColors.class).getMaxNameLength())})); 503 } 504 } 505 // now do set outs at this location 506 trainName = ""; // for printing train message once 507 for (Car car : carList) { 508 if (!car.getSplitDestinationTrackName().equals(trackName) || car.isLocalMove()) { 509 continue; 510 } 511 if (car.getRouteDestination() != null && 512 car.getRouteDestination().getLocation().getSplitName() 513 .equals(location.getSplitName())) { 514 // cars are sorted by train name, print train message once 515 if (!trainName.equals(car.getTrainName())) { 516 trainName = car.getTrainName(); 517 newLine(fileOut, MessageFormat.format( 518 messageFormatText = TrainSwitchListText.getStringScheduledWork(), 519 new Object[]{car.getTrainName(), car.getTrain().getDescription()})); 520 printDropCarHeader(fileOut, !IS_MANIFEST, !IS_TWO_COLUMN_TRACK); 521 } 522 if (car.isUtility()) { 523 setoutUtilityCars(fileOut, carList, car, false, !IS_MANIFEST); 524 } else { 525 dropCar(fileOut, car, !IS_MANIFEST); 526 } 527 } 528 } 529 // only local moves 530 trainName = ""; // for printing train message once 531 for (Car car : carList) { 532 if (!car.getSplitDestinationTrackName().equals(trackName) || !car.isLocalMove()) { 533 continue; 534 } 535 if (car.getRouteDestination() != null && 536 car.getRouteDestination().getLocation().getSplitName() 537 .equals(location.getSplitName())) { 538 // cars are sorted by train name, print train message once 539 if (!trainName.equals(car.getTrainName())) { 540 trainName = car.getTrainName(); 541 newLine(fileOut, MessageFormat.format( 542 messageFormatText = TrainSwitchListText.getStringScheduledWork(), 543 new Object[]{car.getTrainName(), car.getTrain().getDescription()})); 544 printLocalCarMoveHeader(fileOut, !IS_MANIFEST); 545 } 546 if (car.isUtility()) { 547 setoutUtilityCars(fileOut, carList, car, false, !IS_MANIFEST); 548 } else { 549 dropCar(fileOut, car, !IS_MANIFEST); 550 } 551 } 552 } 553 } 554 } 555 } 556 } 557 558 public void printSwitchList(Location location, boolean isPreview) { 559 File switchListFile = InstanceManager.getDefault(TrainManagerXml.class).getSwitchListFile(location.getName()); 560 if (!switchListFile.exists()) { 561 log.warn("Switch list file missing for location ({})", location.getName()); 562 return; 563 } 564 if (isPreview && Setup.isManifestEditorEnabled()) { 565 TrainUtilities.openDesktop(switchListFile); 566 } else { 567 TrainPrintManifest.printReport(switchListFile, location.getName(), isPreview, Setup.getFontName(), 568 FileUtil.getExternalFilename(Setup.getManifestLogoURL()), location.getDefaultPrinterName(), 569 Setup.getSwitchListOrientation(), Setup.getManifestFontSize(), Setup.isPrintPageHeaderEnabled(), 570 Setup.getPrintDuplexSides()); 571 } 572 if (!isPreview) { 573 location.setStatus(Location.PRINTED); 574 location.setSwitchListState(Location.SW_PRINTED); 575 } 576 } 577 578 private static final Logger log = LoggerFactory.getLogger(TrainSwitchLists.class); 579}