001package jmri.jmrit.operations.trains.trainbuilder; 002 003import java.awt.*; 004import java.io.PrintWriter; 005import java.text.MessageFormat; 006import java.text.SimpleDateFormat; 007import java.util.*; 008import java.util.List; 009 010import javax.swing.JLabel; 011 012import org.slf4j.Logger; 013import org.slf4j.LoggerFactory; 014 015import com.fasterxml.jackson.databind.util.StdDateFormat; 016 017import jmri.InstanceManager; 018import jmri.jmrit.operations.locations.*; 019import jmri.jmrit.operations.locations.divisions.DivisionManager; 020import jmri.jmrit.operations.rollingstock.RollingStock; 021import jmri.jmrit.operations.rollingstock.cars.*; 022import jmri.jmrit.operations.rollingstock.engines.*; 023import jmri.jmrit.operations.routes.RouteLocation; 024import jmri.jmrit.operations.setup.Control; 025import jmri.jmrit.operations.setup.Setup; 026import jmri.jmrit.operations.trains.*; 027import jmri.util.ColorUtil; 028 029/** 030 * Common routines for trains 031 * 032 * @author Daniel Boudreau (C) Copyright 2008, 2009, 2010, 2011, 2012, 2013, 033 * 2021 034 */ 035public class TrainCommon { 036 037 protected static final String TAB = " "; // NOI18N 038 protected static final String NEW_LINE = "\n"; // NOI18N 039 public static final String SPACE = " "; 040 public static final String BLANK_LINE = " "; 041 protected static final char HORIZONTAL_LINE_CHAR = '-'; 042 protected static final String BUILD_REPORT_CHAR = "-"; 043 public static final String HYPHEN = "-"; 044 protected static final char VERTICAL_LINE_CHAR = '|'; 045 protected static final String TEXT_COLOR_START = "<FONT color=\""; 046 protected static final String TEXT_COLOR_DONE = "\">"; 047 protected static final String TEXT_COLOR_END = "</FONT>"; 048 049 // when true a pick up, when false a set out 050 protected static final boolean PICKUP = true; 051 // when true Manifest, when false switch list 052 protected static final boolean IS_MANIFEST = true; 053 // when true local car move 054 public static final boolean LOCAL = true; 055 // when true engine attribute, when false car 056 protected static final boolean ENGINE = true; 057 // when true, two column table is sorted by track names 058 public static final boolean IS_TWO_COLUMN_TRACK = true; 059 060 protected CarManager carManager = InstanceManager.getDefault(CarManager.class); 061 protected EngineManager engineManager = InstanceManager.getDefault(EngineManager.class); 062 protected LocationManager locationManager = InstanceManager.getDefault(LocationManager.class); 063 064 // for switch lists 065 protected boolean _pickupCars; // true when there are pickups 066 protected boolean _dropCars; // true when there are set outs 067 068 /** 069 * Used to generate "Two Column" format for engines. 070 * 071 * @param file Manifest or Switch List File 072 * @param engineList List of engines for this train. 073 * @param rl The RouteLocation being printed. 074 * @param isManifest True if manifest, false if switch list. 075 */ 076 protected void blockLocosTwoColumn(PrintWriter file, List<Engine> engineList, RouteLocation rl, 077 boolean isManifest) { 078 if (isThereWorkAtLocation(null, engineList, rl)) { 079 printEngineHeader(file, isManifest); 080 } 081 int lineLength = getLineLength(isManifest); 082 for (Engine engine : engineList) { 083 if (engine.getRouteLocation() == rl && !engine.getTrackName().equals(Engine.NONE)) { 084 String pullText = padAndTruncate(pickupEngine(engine).trim(), lineLength / 2); 085 pullText = formatColorString(pullText, Setup.getPickupEngineColor()); 086 String s = pullText + VERTICAL_LINE_CHAR + tabString("", lineLength / 2 - 1); 087 addLine(file, s); 088 } 089 if (engine.getRouteDestination() == rl) { 090 String dropText = padAndTruncate(dropEngine(engine).trim(), lineLength / 2 - 1); 091 dropText = formatColorString(dropText, Setup.getDropEngineColor()); 092 String s = tabString("", lineLength / 2) + VERTICAL_LINE_CHAR + dropText; 093 addLine(file, s); 094 } 095 } 096 } 097 098 /** 099 * Adds a list of locomotive pick ups for the route location to the output 100 * file. Used to generate "Standard" format. 101 * 102 * @param file Manifest or Switch List File 103 * @param engineList List of engines for this train. 104 * @param rl The RouteLocation being printed. 105 * @param isManifest True if manifest, false if switch list 106 */ 107 protected void pickupEngines(PrintWriter file, List<Engine> engineList, RouteLocation rl, boolean isManifest) { 108 boolean printHeader = Setup.isPrintHeadersEnabled(); 109 for (Engine engine : engineList) { 110 if (engine.getRouteLocation() == rl && !engine.getTrackName().equals(Engine.NONE)) { 111 if (printHeader) { 112 printPickupEngineHeader(file, isManifest); 113 printHeader = false; 114 } 115 pickupEngine(file, engine, isManifest); 116 } 117 } 118 } 119 120 private void pickupEngine(PrintWriter file, Engine engine, boolean isManifest) { 121 StringBuffer buf = new StringBuffer(padAndTruncateIfNeeded(Setup.getPickupEnginePrefix(), 122 isManifest ? Setup.getManifestPrefixLength() : Setup.getSwitchListPrefixLength())); 123 String[] format = Setup.getPickupEngineMessageFormat(); 124 for (String attribute : format) { 125 String s = getEngineAttribute(engine, attribute, PICKUP); 126 if (!checkStringLength(buf.toString() + s, isManifest)) { 127 addLine(file, buf, Setup.getPickupEngineColor()); 128 buf = new StringBuffer(TAB); // new line 129 } 130 buf.append(s); 131 } 132 addLine(file, buf, Setup.getPickupEngineColor()); 133 } 134 135 /** 136 * Adds a list of locomotive drops for the route location to the output 137 * file. Used to generate "Standard" format. 138 * 139 * @param file Manifest or Switch List File 140 * @param engineList List of engines for this train. 141 * @param rl The RouteLocation being printed. 142 * @param isManifest True if manifest, false if switch list 143 */ 144 protected void dropEngines(PrintWriter file, List<Engine> engineList, RouteLocation rl, boolean isManifest) { 145 boolean printHeader = Setup.isPrintHeadersEnabled(); 146 for (Engine engine : engineList) { 147 if (engine.getRouteDestination() == rl) { 148 if (printHeader) { 149 printDropEngineHeader(file, isManifest); 150 printHeader = false; 151 } 152 dropEngine(file, engine, isManifest); 153 } 154 } 155 } 156 157 private void dropEngine(PrintWriter file, Engine engine, boolean isManifest) { 158 StringBuffer buf = new StringBuffer(padAndTruncateIfNeeded(Setup.getDropEnginePrefix(), 159 isManifest ? Setup.getManifestPrefixLength() : Setup.getSwitchListPrefixLength())); 160 String[] format = Setup.getDropEngineMessageFormat(); 161 for (String attribute : format) { 162 String s = getEngineAttribute(engine, attribute, !PICKUP); 163 if (!checkStringLength(buf.toString() + s, isManifest)) { 164 addLine(file, buf, Setup.getDropEngineColor()); 165 buf = new StringBuffer(TAB); // new line 166 } 167 buf.append(s); 168 } 169 addLine(file, buf, Setup.getDropEngineColor()); 170 } 171 172 /** 173 * Returns the pick up string for a loco. Useful for frames like the train 174 * conductor and yardmaster. 175 * 176 * @param engine The Engine. 177 * @return engine pick up string 178 */ 179 public String pickupEngine(Engine engine) { 180 StringBuilder builder = new StringBuilder(); 181 for (String attribute : Setup.getPickupEngineMessageFormat()) { 182 builder.append(getEngineAttribute(engine, attribute, PICKUP)); 183 } 184 return builder.toString(); 185 } 186 187 /** 188 * Returns the drop string for a loco. Useful for frames like the train 189 * conductor and yardmaster. 190 * 191 * @param engine The Engine. 192 * @return engine drop string 193 */ 194 public String dropEngine(Engine engine) { 195 StringBuilder builder = new StringBuilder(); 196 for (String attribute : Setup.getDropEngineMessageFormat()) { 197 builder.append(getEngineAttribute(engine, attribute, !PICKUP)); 198 } 199 return builder.toString(); 200 } 201 202 // the next three booleans are used to limit the header to once per location 203 boolean _printPickupHeader = true; 204 boolean _printSetoutHeader = true; 205 boolean _printLocalMoveHeader = true; 206 207 /** 208 * Block cars by track, then pick up and set out for each location in a 209 * train's route. This routine is used for the "Standard" format. 210 * 211 * @param file Manifest or switch list File 212 * @param train The train being printed. 213 * @param carList List of cars for this train 214 * @param rl The RouteLocation being printed 215 * @param printHeader True if new location. 216 * @param isManifest True if manifest, false if switch list. 217 */ 218 protected void blockCarsByTrack(PrintWriter file, Train train, List<Car> carList, RouteLocation rl, 219 boolean printHeader, boolean isManifest) { 220 if (printHeader) { 221 _printPickupHeader = true; 222 _printSetoutHeader = true; 223 _printLocalMoveHeader = true; 224 } 225 List<Track> tracks = rl.getLocation().getTracksByNameList(null); 226 List<String> trackNames = new ArrayList<>(); 227 clearUtilityCarTypes(); // list utility cars by quantity 228 for (Track track : tracks) { 229 if (trackNames.contains(track.getSplitName())) { 230 continue; 231 } 232 trackNames.add(track.getSplitName()); // use a track name once 233 234 // car pick ups 235 blockCarsPickups(file, train, carList, rl, track, isManifest); 236 237 // now do car set outs and local moves 238 // group local moves first? 239 blockCarsSetoutsAndMoves(file, train, carList, rl, track, isManifest, false, 240 Setup.isGroupCarMovesEnabled()); 241 // set outs or both 242 blockCarsSetoutsAndMoves(file, train, carList, rl, track, isManifest, true, 243 !Setup.isGroupCarMovesEnabled()); 244 245 if (!Setup.isSortByTrackNameEnabled()) { 246 break; // done 247 } 248 } 249 } 250 251 private void blockCarsPickups(PrintWriter file, Train train, List<Car> carList, RouteLocation rl, 252 Track track, boolean isManifest) { 253 // block pick up cars, except for passenger cars 254 for (RouteLocation rld : train.getTrainBlockingOrder()) { 255 for (Car car : carList) { 256 if (Setup.isSortByTrackNameEnabled() && 257 !track.getSplitName().equals(car.getSplitTrackName())) { 258 continue; 259 } 260 // Block cars 261 // caboose or FRED is placed at end of the train 262 // passenger cars are already blocked in the car list 263 // passenger cars with negative block numbers are placed at 264 // the front of the train, positive numbers at the end of 265 // the train. 266 if (isNextCar(car, rl, rld)) { 267 // determine if pick up header is needed 268 printPickupCarHeader(file, car, isManifest, !IS_TWO_COLUMN_TRACK); 269 270 // use truncated format if there's a switch list 271 boolean isTruncate = Setup.isPrintTruncateManifestEnabled() && 272 rl.getLocation().isSwitchListEnabled(); 273 274 if (car.isUtility()) { 275 pickupUtilityCars(file, carList, car, isTruncate, isManifest); 276 } else if (isManifest && isTruncate) { 277 pickUpCarTruncated(file, car, isManifest); 278 } else { 279 pickUpCar(file, car, isManifest); 280 } 281 _pickupCars = true; 282 } 283 } 284 } 285 } 286 287 private void blockCarsSetoutsAndMoves(PrintWriter file, Train train, List<Car> carList, RouteLocation rl, 288 Track track, boolean isManifest, boolean isSetout, boolean isLocalMove) { 289 for (Car car : carList) { 290 if (!car.isLocalMove() && isSetout || car.isLocalMove() && isLocalMove) { 291 if (Setup.isSortByTrackNameEnabled() && 292 car.getRouteLocation() != null && 293 car.getRouteDestination() == rl) { 294 // must sort local moves by car's destination track name and not car's track name 295 // sorting by car's track name fails if there are "similar" location names. 296 if (!track.getSplitName().equals(car.getSplitDestinationTrackName())) { 297 continue; 298 } 299 } 300 if (car.getRouteDestination() == rl && car.getDestinationTrack() != null) { 301 // determine if drop or move header is needed 302 printDropOrMoveCarHeader(file, car, isManifest, !IS_TWO_COLUMN_TRACK); 303 304 // use truncated format if there's a switch list 305 boolean isTruncate = Setup.isPrintTruncateManifestEnabled() && 306 rl.getLocation().isSwitchListEnabled() && 307 !train.isLocalSwitcher(); 308 309 if (car.isUtility()) { 310 setoutUtilityCars(file, carList, car, isTruncate, isManifest); 311 } else if (isManifest && isTruncate) { 312 truncatedDropCar(file, car, isManifest); 313 } else { 314 dropCar(file, car, isManifest); 315 } 316 _dropCars = true; 317 } 318 } 319 } 320 } 321 322 /** 323 * Used to determine if car is the next to be processed when producing 324 * Manifests or Switch Lists. Caboose or FRED is placed at end of the train. 325 * Passenger cars are already blocked in the car list. Passenger cars with 326 * negative block numbers are placed at the front of the train, positive 327 * numbers at the end of the train. Note that a car in train doesn't have a 328 * track assignment. 329 * 330 * @param car the car being tested 331 * @param rl when in train's route the car is being pulled 332 * @param rld the destination being tested 333 * @return true if this car is the next one to be processed 334 */ 335 public static boolean isNextCar(Car car, RouteLocation rl, RouteLocation rld) { 336 return isNextCar(car, rl, rld, false); 337 } 338 339 public static boolean isNextCar(Car car, RouteLocation rl, RouteLocation rld, boolean isIgnoreTrack) { 340 Train train = car.getTrain(); 341 if (train != null && 342 (car.getTrack() != null || isIgnoreTrack) && 343 car.getRouteLocation() == rl && 344 (rld == car.getRouteDestination() && 345 !car.isCaboose() && 346 !car.hasFred() && 347 !car.isPassenger() || 348 rld == train.getTrainDepartsRouteLocation() && 349 car.isPassenger() && 350 car.getBlocking() < 0 || 351 rld == train.getTrainTerminatesRouteLocation() && 352 (car.isCaboose() || 353 car.hasFred() || 354 car.isPassenger() && car.getBlocking() >= 0))) { 355 return true; 356 } 357 return false; 358 } 359 360 private void printPickupCarHeader(PrintWriter file, Car car, boolean isManifest, boolean isTwoColumnTrack) { 361 if (_printPickupHeader && !car.isLocalMove()) { 362 printPickupCarHeader(file, isManifest, !IS_TWO_COLUMN_TRACK); 363 _printPickupHeader = false; 364 // check to see if the other headers are needed. If 365 // they are identical, not needed 366 if (getPickupCarHeader(isManifest, !IS_TWO_COLUMN_TRACK) 367 .equals(getDropCarHeader(isManifest, !IS_TWO_COLUMN_TRACK))) { 368 _printSetoutHeader = false; 369 } 370 if (getPickupCarHeader(isManifest, !IS_TWO_COLUMN_TRACK) 371 .equals(getLocalMoveHeader(isManifest))) { 372 _printLocalMoveHeader = false; 373 } 374 } 375 } 376 377 private void printDropOrMoveCarHeader(PrintWriter file, Car car, boolean isManifest, boolean isTwoColumnTrack) { 378 if (_printSetoutHeader && !car.isLocalMove()) { 379 printDropCarHeader(file, isManifest, !IS_TWO_COLUMN_TRACK); 380 _printSetoutHeader = false; 381 // check to see if the other headers are needed. If they 382 // are identical, not needed 383 if (getPickupCarHeader(isManifest, !IS_TWO_COLUMN_TRACK) 384 .equals(getDropCarHeader(isManifest, !IS_TWO_COLUMN_TRACK))) { 385 _printPickupHeader = false; 386 } 387 if (getDropCarHeader(isManifest, !IS_TWO_COLUMN_TRACK).equals(getLocalMoveHeader(isManifest))) { 388 _printLocalMoveHeader = false; 389 } 390 } 391 if (_printLocalMoveHeader && car.isLocalMove()) { 392 printLocalCarMoveHeader(file, isManifest); 393 _printLocalMoveHeader = false; 394 // check to see if the other headers are needed. If they 395 // are identical, not needed 396 if (getPickupCarHeader(isManifest, !IS_TWO_COLUMN_TRACK) 397 .equals(getLocalMoveHeader(isManifest))) { 398 _printPickupHeader = false; 399 } 400 if (getDropCarHeader(isManifest, !IS_TWO_COLUMN_TRACK).equals(getLocalMoveHeader(isManifest))) { 401 _printSetoutHeader = false; 402 } 403 } 404 } 405 406 /** 407 * Produces a two column format for car pick ups and set outs. Sorted by 408 * track and then by blocking order. This routine is used for the "Two 409 * Column" format. 410 * 411 * @param file Manifest or switch list File 412 * @param train The train 413 * @param carList List of cars for this train 414 * @param rl The RouteLocation being printed 415 * @param printHeader True if new location. 416 * @param isManifest True if manifest, false if switch list. 417 */ 418 protected void blockCarsTwoColumn(PrintWriter file, Train train, List<Car> carList, RouteLocation rl, 419 boolean printHeader, boolean isManifest) { 420 index = 0; 421 int lineLength = getLineLength(isManifest); 422 List<Track> tracks = rl.getLocation().getTracksByNameList(null); 423 List<String> trackNames = new ArrayList<>(); 424 clearUtilityCarTypes(); // list utility cars by quantity 425 if (printHeader) { 426 printCarHeader(file, isManifest, !IS_TWO_COLUMN_TRACK); 427 } 428 for (Track track : tracks) { 429 if (trackNames.contains(track.getSplitName())) { 430 continue; 431 } 432 trackNames.add(track.getSplitName()); // use a track name once 433 // block car pick ups 434 for (RouteLocation rld : train.getTrainBlockingOrder()) { 435 for (int k = 0; k < carList.size(); k++) { 436 Car car = carList.get(k); 437 // block cars 438 // caboose or FRED is placed at end of the train 439 // passenger cars are already blocked in the car list 440 // passenger cars with negative block numbers are placed at 441 // the front of the train, positive numbers at the end of 442 // the train. 443 if (isNextCar(car, rl, rld)) { 444 if (Setup.isSortByTrackNameEnabled() && 445 !track.getSplitName().equals(car.getSplitTrackName())) { 446 continue; 447 } 448 _pickupCars = true; 449 String s; 450 if (car.isUtility()) { 451 s = pickupUtilityCars(carList, car, isManifest, !IS_TWO_COLUMN_TRACK); 452 if (s == null) { 453 continue; 454 } 455 s = s.trim(); 456 } else { 457 s = pickupCar(car, isManifest, !IS_TWO_COLUMN_TRACK).trim(); 458 } 459 s = padAndTruncate(s, lineLength / 2); 460 if (car.isLocalMove()) { 461 s = formatColorString(s, Setup.getLocalColor()); 462 String sl = appendSetoutString(s, carList, car.getRouteDestination(), car, isManifest, 463 !IS_TWO_COLUMN_TRACK); 464 // check for utility car, and local route with two 465 // or more locations 466 if (!sl.equals(s)) { 467 s = sl; 468 carList.remove(car); // done with this car, remove from list 469 k--; 470 } else { 471 s = padAndTruncate(s + VERTICAL_LINE_CHAR, getLineLength(isManifest)); 472 } 473 } else { 474 s = formatColorString(s, Setup.getPickupColor()); 475 s = appendSetoutString(s, carList, rl, true, isManifest, !IS_TWO_COLUMN_TRACK); 476 } 477 addLine(file, s); 478 } 479 } 480 } 481 if (!Setup.isSortByTrackNameEnabled()) { 482 break; // done 483 } 484 } 485 while (index < carList.size()) { 486 String s = padString("", lineLength / 2); 487 s = appendSetoutString(s, carList, rl, false, isManifest, !IS_TWO_COLUMN_TRACK); 488 String test = s.trim(); 489 // null line contains | 490 if (test.length() > 1) { 491 addLine(file, s); 492 } 493 } 494 } 495 496 List<Car> doneCars = new ArrayList<>(); 497 498 /** 499 * Produces a two column format for car pick ups and set outs. Sorted by 500 * track and then by destination. Track name in header format, track name 501 * removed from format. This routine is used to generate the "Two Column by 502 * Track" format. 503 * 504 * @param file Manifest or switch list File 505 * @param train The train 506 * @param carList List of cars for this train 507 * @param rl The RouteLocation being printed 508 * @param printHeader True if new location. 509 * @param isManifest True if manifest, false if switch list. 510 */ 511 protected void blockCarsByTrackNameTwoColumn(PrintWriter file, Train train, List<Car> carList, RouteLocation rl, 512 boolean printHeader, boolean isManifest) { 513 index = 0; 514 List<Track> tracks = rl.getLocation().getTracksByNameList(null); 515 List<String> trackNames = new ArrayList<>(); 516 doneCars.clear(); 517 clearUtilityCarTypes(); // list utility cars by quantity 518 if (printHeader) { 519 printCarHeader(file, isManifest, IS_TWO_COLUMN_TRACK); 520 } 521 for (Track track : tracks) { 522 String trackName = track.getSplitName(); 523 if (trackNames.contains(trackName)) { 524 continue; 525 } 526 // block car pick ups 527 for (RouteLocation rld : train.getTrainBlockingOrder()) { 528 for (Car car : carList) { 529 if (car.getTrack() != null && 530 car.getRouteLocation() == rl && 531 trackName.equals(car.getSplitTrackName()) && 532 ((car.getRouteDestination() == rld && !car.isCaboose() && !car.hasFred()) || 533 (rld == train.getTrainTerminatesRouteLocation() && 534 (car.isCaboose() || car.hasFred())))) { 535 if (!trackNames.contains(trackName)) { 536 printTrackNameHeader(file, trackName, isManifest); 537 } 538 trackNames.add(trackName); // use a track name once 539 _pickupCars = true; 540 String s; 541 if (car.isUtility()) { 542 s = pickupUtilityCars(carList, car, isManifest, IS_TWO_COLUMN_TRACK); 543 if (s == null) { 544 continue; 545 } 546 s = s.trim(); 547 } else { 548 s = pickupCar(car, isManifest, IS_TWO_COLUMN_TRACK).trim(); 549 } 550 s = padAndTruncate(s, getLineLength(isManifest) / 2); 551 s = formatColorString(s, car.isLocalMove() ? Setup.getLocalColor() : Setup.getPickupColor()); 552 s = appendSetoutString(s, trackName, carList, rl, isManifest, IS_TWO_COLUMN_TRACK); 553 addLine(file, s); 554 } 555 } 556 } 557 for (Car car : carList) { 558 if (!doneCars.contains(car) && 559 car.getRouteDestination() == rl && 560 trackName.equals(car.getSplitDestinationTrackName())) { 561 if (!trackNames.contains(trackName)) { 562 printTrackNameHeader(file, trackName, isManifest); 563 } 564 trackNames.add(trackName); // use a track name once 565 String s = padString("", getLineLength(isManifest) / 2); 566 String so = appendSetoutString(s, carList, rl, car, isManifest, IS_TWO_COLUMN_TRACK); 567 // check for utility car 568 if (so.equals(s)) { 569 continue; 570 } 571 String test = so.trim(); 572 if (test.length() > 1) // null line contains | 573 { 574 addLine(file, so); 575 } 576 } 577 } 578 } 579 } 580 581 protected void printTrackComments(PrintWriter file, RouteLocation rl, List<Car> carList, boolean isManifest) { 582 Location location = rl.getLocation(); 583 if (location != null) { 584 List<Track> tracks = location.getTracksByNameList(null); 585 for (Track track : tracks) { 586 if (isManifest && !track.isPrintManifestCommentEnabled() || 587 !isManifest && !track.isPrintSwitchListCommentEnabled()) { 588 continue; 589 } 590 // any pick ups or set outs to this track? 591 boolean pickup = false; 592 boolean setout = false; 593 for (Car car : carList) { 594 if (car.getRouteLocation() == rl && car.getTrack() != null && car.getTrack() == track) { 595 pickup = true; 596 } 597 if (car.getRouteDestination() == rl && 598 car.getDestinationTrack() != null && 599 car.getDestinationTrack() == track) { 600 setout = true; 601 } 602 } 603 // print the appropriate comment if there's one 604 if (pickup && setout && !track.getCommentBothWithColor().equals(Track.NONE)) { 605 newLine(file, track.getCommentBothWithColor(), isManifest); 606 } else if (pickup && !setout && !track.getCommentPickupWithColor().equals(Track.NONE)) { 607 newLine(file, track.getCommentPickupWithColor(), isManifest); 608 } else if (!pickup && setout && !track.getCommentSetoutWithColor().equals(Track.NONE)) { 609 newLine(file, track.getCommentSetoutWithColor(), isManifest); 610 } 611 } 612 } 613 } 614 615 protected void setCarPickupTime(Train train, RouteLocation rl, List<Car> carList) { 616 String expectedDepartureTime = train.getExpectedDepartureTime(rl, true); 617 for (Car car : carList) { 618 if (car.getRouteLocation() == rl) { 619 car.setPickupTime(expectedDepartureTime); 620 } 621 } 622 623 } 624 625 @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "SLF4J_FORMAT_SHOULD_BE_CONST", 626 justification = "Only when exception") 627 public static String getTrainMessage(Train train, RouteLocation rl) { 628 String expectedArrivalTime = train.getExpectedArrivalTime(rl); 629 String routeLocationName = rl.getSplitName(); 630 String msg = ""; 631 String messageFormatText = ""; // the text being formated in case there's an exception 632 try { 633 // Scheduled work at {0} 634 msg = MessageFormat.format(messageFormatText = TrainManifestText 635 .getStringScheduledWork(), 636 new Object[]{routeLocationName, train.getName(), 637 train.getDescription(), rl.getLocation().getDivisionName()}); 638 if (train.isShowArrivalAndDepartureTimesEnabled()) { 639 if (rl == train.getTrainDepartsRouteLocation()) { 640 // Scheduled work at {0}, departure time {1} 641 msg = MessageFormat.format(messageFormatText = TrainManifestText 642 .getStringWorkDepartureTime(), 643 new Object[]{routeLocationName, 644 train.getFormatedDepartureTime(), train.getName(), 645 train.getDescription(), rl.getLocation().getDivisionName()}); 646 } else if (!rl.getDepartureTime().equals(RouteLocation.NONE) && 647 rl != train.getTrainTerminatesRouteLocation()) { 648 // Scheduled work at {0}, departure time {1} 649 msg = MessageFormat.format(messageFormatText = TrainManifestText 650 .getStringWorkDepartureTime(), 651 new Object[]{routeLocationName, 652 expectedArrivalTime.equals(Train.ALREADY_SERVICED) 653 ? rl.getFormatedDepartureTime() : train.getExpectedDepartureTime(rl), 654 train.getName(), train.getDescription(), 655 rl.getLocation().getDivisionName()}); 656 } else if (Setup.isUseDepartureTimeEnabled() && 657 rl != train.getTrainTerminatesRouteLocation() && 658 !train.getExpectedDepartureTime(rl).equals(Train.ALREADY_SERVICED)) { 659 // Scheduled work at {0}, departure time {1} 660 msg = MessageFormat.format(messageFormatText = TrainManifestText 661 .getStringWorkDepartureTime(), 662 new Object[]{routeLocationName, 663 train.getExpectedDepartureTime(rl), train.getName(), 664 train.getDescription(), rl.getLocation().getDivisionName()}); 665 } else if (!expectedArrivalTime.equals(Train.ALREADY_SERVICED)) { 666 // Scheduled work at {0}, arrival time {1} 667 msg = MessageFormat.format(messageFormatText = TrainManifestText 668 .getStringWorkArrivalTime(), 669 new Object[]{routeLocationName, expectedArrivalTime, 670 train.getName(), train.getDescription(), 671 rl.getLocation().getDivisionName()}); 672 } 673 } 674 return msg; 675 } catch (IllegalArgumentException e) { 676 msg = Bundle.getMessage("ErrorIllegalArgument", 677 Bundle.getMessage("TitleSwitchListText"), e.getLocalizedMessage()) + NEW_LINE + messageFormatText; 678 log.error(msg); 679 log.error("Illegal argument", e); 680 return msg; 681 } 682 } 683 684 @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "SLF4J_FORMAT_SHOULD_BE_CONST", 685 justification = "Only when exception") 686 public static String getSwitchListTrainStatus(Train train, RouteLocation rl) { 687 String expectedArrivalTime = train.getExpectedArrivalTime(rl); 688 String msg = ""; 689 String messageFormatText = ""; // the text being formated in case there's an exception 690 try { 691 if (train.isLocalSwitcher()) { 692 // Use Manifest text for local departure 693 // Scheduled work at {0}, departure time {1} 694 msg = MessageFormat.format(messageFormatText = TrainManifestText.getStringWorkDepartureTime(), 695 new Object[]{splitString(train.getTrainDepartsName()), train.getFormatedDepartureTime(), 696 train.getName(), train.getDescription(), 697 rl.getLocation().getDivisionName()}); 698 } else if (rl == train.getTrainDepartsRouteLocation()) { 699 // Departs {0} {1}bound at {2} 700 msg = MessageFormat.format(messageFormatText = TrainSwitchListText.getStringDepartsAt(), 701 new Object[]{splitString(train.getTrainDepartsName()), rl.getTrainDirectionString(), 702 train.getFormatedDepartureTime()}); 703 } else if (Setup.isUseSwitchListDepartureTimeEnabled() && 704 rl != train.getTrainTerminatesRouteLocation() && 705 !train.isTrainEnRoute()) { 706 // Departs {0} at {1} expected arrival {2}, arrives {3}bound 707 msg = MessageFormat.format( 708 messageFormatText = TrainSwitchListText.getStringDepartsAtExpectedArrival(), 709 new Object[]{splitString(rl.getName()), 710 train.getExpectedDepartureTime(rl), expectedArrivalTime, 711 rl.getTrainDirectionString()}); 712 } else if (Setup.isUseSwitchListDepartureTimeEnabled() && 713 rl == train.getCurrentRouteLocation() && 714 rl != train.getTrainTerminatesRouteLocation() && 715 !rl.getDepartureTime().equals(RouteLocation.NONE)) { 716 // Departs {0} {1}bound at {2} 717 msg = MessageFormat.format(messageFormatText = TrainSwitchListText.getStringDepartsAt(), 718 new Object[]{splitString(rl.getName()), rl.getTrainDirectionString(), 719 rl.getFormatedDepartureTime()}); 720 } else if (train.isTrainEnRoute()) { 721 if (!expectedArrivalTime.equals(Train.ALREADY_SERVICED)) { 722 // Departed {0}, expect to arrive in {1}, arrives {2}bound 723 msg = MessageFormat.format(messageFormatText = TrainSwitchListText.getStringDepartedExpected(), 724 new Object[]{splitString(train.getTrainDepartsName()), expectedArrivalTime, 725 rl.getTrainDirectionString(), train.getCurrentLocationName()}); 726 } 727 } else { 728 // Departs {0} at {1} expected arrival {2}, arrives {3}bound 729 msg = MessageFormat.format( 730 messageFormatText = TrainSwitchListText.getStringDepartsAtExpectedArrival(), 731 new Object[]{splitString(train.getTrainDepartsName()), 732 train.getFormatedDepartureTime(), expectedArrivalTime, 733 rl.getTrainDirectionString()}); 734 } 735 return msg; 736 } catch (IllegalArgumentException e) { 737 msg = Bundle.getMessage("ErrorIllegalArgument", 738 Bundle.getMessage("TitleSwitchListText"), e.getLocalizedMessage()) + NEW_LINE + messageFormatText; 739 log.error(msg); 740 log.error("Illegal argument", e); 741 return msg; 742 } 743 } 744 745 int index = 0; 746 747 /* 748 * Used by two column format. Local moves (pulls and spots) are lined up 749 * when using this format, 750 */ 751 private String appendSetoutString(String s, List<Car> carList, RouteLocation rl, boolean local, boolean isManifest, 752 boolean isTwoColumnTrack) { 753 while (index < carList.size()) { 754 Car car = carList.get(index++); 755 if (local && car.isLocalMove()) { 756 continue; // skip local moves 757 } 758 // car list is already sorted by destination track 759 if (car.getRouteDestination() == rl) { 760 String so = appendSetoutString(s, carList, rl, car, isManifest, isTwoColumnTrack); 761 // check for utility car 762 if (!so.equals(s)) { 763 return so; 764 } 765 } 766 } 767 // no set out for this line 768 return s + VERTICAL_LINE_CHAR + padAndTruncate("", getLineLength(isManifest) / 2 - 1); 769 } 770 771 /* 772 * Used by two column, track names shown in the columns. 773 */ 774 private String appendSetoutString(String s, String trackName, List<Car> carList, RouteLocation rl, 775 boolean isManifest, boolean isTwoColumnTrack) { 776 for (Car car : carList) { 777 if (!doneCars.contains(car) && 778 car.getRouteDestination() == rl && 779 trackName.equals(car.getSplitDestinationTrackName())) { 780 doneCars.add(car); 781 String so = appendSetoutString(s, carList, rl, car, isManifest, isTwoColumnTrack); 782 // check for utility car 783 if (!so.equals(s)) { 784 return so; 785 } 786 } 787 } 788 // no set out for this track 789 return s + VERTICAL_LINE_CHAR + padAndTruncate("", getLineLength(isManifest) / 2 - 1); 790 } 791 792 /* 793 * Appends to string the vertical line character, and the car set out 794 * string. Used in two column format. 795 */ 796 private String appendSetoutString(String s, List<Car> carList, RouteLocation rl, Car car, boolean isManifest, 797 boolean isTwoColumnTrack) { 798 _dropCars = true; 799 String dropText; 800 801 if (car.isUtility()) { 802 dropText = setoutUtilityCars(carList, car, !LOCAL, isManifest, isTwoColumnTrack); 803 if (dropText == null) { 804 return s; // no changes to the input string 805 } 806 } else { 807 dropText = dropCar(car, isManifest, isTwoColumnTrack).trim(); 808 } 809 810 dropText = padAndTruncate(dropText.trim(), getLineLength(isManifest) / 2 - 1); 811 dropText = formatColorString(dropText, car.isLocalMove() ? Setup.getLocalColor() : Setup.getDropColor()); 812 return s + VERTICAL_LINE_CHAR + dropText; 813 } 814 815 /** 816 * Adds the car's pick up string to the output file using the truncated 817 * manifest format 818 * 819 * @param file Manifest or switch list File 820 * @param car The car being printed. 821 * @param isManifest True if manifest, false if switch list. 822 */ 823 protected void pickUpCarTruncated(PrintWriter file, Car car, boolean isManifest) { 824 pickUpCar(file, car, 825 new StringBuffer(padAndTruncateIfNeeded(Setup.getPickupCarPrefix(), Setup.getManifestPrefixLength())), 826 Setup.getPickupTruncatedManifestMessageFormat(), isManifest); 827 } 828 829 /** 830 * Adds the car's pick up string to the output file using the manifest or 831 * switch list format 832 * 833 * @param file Manifest or switch list File 834 * @param car The car being printed. 835 * @param isManifest True if manifest, false if switch list. 836 */ 837 protected void pickUpCar(PrintWriter file, Car car, boolean isManifest) { 838 if (isManifest) { 839 pickUpCar(file, car, 840 new StringBuffer( 841 padAndTruncateIfNeeded(Setup.getPickupCarPrefix(), Setup.getManifestPrefixLength())), 842 Setup.getPickupManifestMessageFormat(), isManifest); 843 } else { 844 pickUpCar(file, car, new StringBuffer( 845 padAndTruncateIfNeeded(Setup.getSwitchListPickupCarPrefix(), Setup.getSwitchListPrefixLength())), 846 Setup.getPickupSwitchListMessageFormat(), isManifest); 847 } 848 } 849 850 private void pickUpCar(PrintWriter file, Car car, StringBuffer buf, String[] format, boolean isManifest) { 851 if (car.isLocalMove()) { 852 return; // print nothing local move, see dropCar 853 } 854 for (String attribute : format) { 855 String s = getCarAttribute(car, attribute, PICKUP, !LOCAL); 856 if (!checkStringLength(buf.toString() + s, isManifest)) { 857 addLine(file, buf, Setup.getPickupColor()); 858 buf = new StringBuffer(TAB); // new line 859 } 860 buf.append(s); 861 } 862 addLine(file, buf, Setup.getPickupColor()); 863 } 864 865 /** 866 * Returns the pick up car string. Useful for frames like train conductor 867 * and yardmaster. 868 * 869 * @param car The car being printed. 870 * @param isManifest when true use manifest format, when false use 871 * switch list format 872 * @param isTwoColumnTrack True if printing using two column format sorted 873 * by track name. 874 * @return pick up car string 875 */ 876 public String pickupCar(Car car, boolean isManifest, boolean isTwoColumnTrack) { 877 StringBuffer buf = new StringBuffer(); 878 String[] format; 879 if (isManifest && !isTwoColumnTrack) { 880 format = Setup.getPickupManifestMessageFormat(); 881 } else if (!isManifest && !isTwoColumnTrack) { 882 format = Setup.getPickupSwitchListMessageFormat(); 883 } else if (isManifest && isTwoColumnTrack) { 884 format = Setup.getPickupTwoColumnByTrackManifestMessageFormat(); 885 } else { 886 format = Setup.getPickupTwoColumnByTrackSwitchListMessageFormat(); 887 } 888 for (String attribute : format) { 889 buf.append(getCarAttribute(car, attribute, PICKUP, !LOCAL)); 890 } 891 return buf.toString(); 892 } 893 894 /** 895 * Adds the car's set out string to the output file using the truncated 896 * manifest format. Does not print out local moves. Local moves are only 897 * shown on the switch list for that location. 898 * 899 * @param file Manifest or switch list File 900 * @param car The car being printed. 901 * @param isManifest True if manifest, false if switch list. 902 */ 903 protected void truncatedDropCar(PrintWriter file, Car car, boolean isManifest) { 904 // local move? 905 if (car.isLocalMove()) { 906 return; // yes, don't print local moves on train manifest 907 } 908 dropCar(file, car, new StringBuffer(Setup.getDropCarPrefix()), Setup.getDropTruncatedManifestMessageFormat(), 909 false, isManifest); 910 } 911 912 /** 913 * Adds the car's set out string to the output file using the manifest or 914 * switch list format 915 * 916 * @param file Manifest or switch list File 917 * @param car The car being printed. 918 * @param isManifest True if manifest, false if switch list. 919 */ 920 protected void dropCar(PrintWriter file, Car car, boolean isManifest) { 921 boolean isLocal = car.isLocalMove(); 922 if (isManifest) { 923 StringBuffer buf = new StringBuffer( 924 padAndTruncateIfNeeded(Setup.getDropCarPrefix(), Setup.getManifestPrefixLength())); 925 String[] format = Setup.getDropManifestMessageFormat(); 926 if (isLocal) { 927 buf = new StringBuffer(padAndTruncateIfNeeded(Setup.getLocalPrefix(), Setup.getManifestPrefixLength())); 928 format = Setup.getLocalManifestMessageFormat(); 929 } 930 dropCar(file, car, buf, format, isLocal, isManifest); 931 } else { 932 StringBuffer buf = new StringBuffer( 933 padAndTruncateIfNeeded(Setup.getSwitchListDropCarPrefix(), Setup.getSwitchListPrefixLength())); 934 String[] format = Setup.getDropSwitchListMessageFormat(); 935 if (isLocal) { 936 buf = new StringBuffer( 937 padAndTruncateIfNeeded(Setup.getSwitchListLocalPrefix(), Setup.getSwitchListPrefixLength())); 938 format = Setup.getLocalSwitchListMessageFormat(); 939 } 940 dropCar(file, car, buf, format, isLocal, isManifest); 941 } 942 } 943 944 private void dropCar(PrintWriter file, Car car, StringBuffer buf, String[] format, boolean isLocal, 945 boolean isManifest) { 946 for (String attribute : format) { 947 String s = getCarAttribute(car, attribute, !PICKUP, isLocal); 948 if (!checkStringLength(buf.toString() + s, isManifest)) { 949 addLine(file, buf, isLocal ? Setup.getLocalColor() : Setup.getDropColor()); 950 buf = new StringBuffer(TAB); // new line 951 } 952 buf.append(s); 953 } 954 addLine(file, buf, isLocal ? Setup.getLocalColor() : Setup.getDropColor()); 955 } 956 957 /** 958 * Returns the drop car string. Useful for frames like train conductor and 959 * yardmaster. 960 * 961 * @param car The car being printed. 962 * @param isManifest when true use manifest format, when false use 963 * switch list format 964 * @param isTwoColumnTrack True if printing using two column format. 965 * @return drop car string 966 */ 967 public String dropCar(Car car, boolean isManifest, boolean isTwoColumnTrack) { 968 StringBuffer buf = new StringBuffer(); 969 String[] format; 970 if (isManifest && !isTwoColumnTrack) { 971 format = Setup.getDropManifestMessageFormat(); 972 } else if (!isManifest && !isTwoColumnTrack) { 973 format = Setup.getDropSwitchListMessageFormat(); 974 } else if (isManifest && isTwoColumnTrack) { 975 format = Setup.getDropTwoColumnByTrackManifestMessageFormat(); 976 } else { 977 format = Setup.getDropTwoColumnByTrackSwitchListMessageFormat(); 978 } 979 // TODO the Setup.Location doesn't work correctly for the conductor 980 // window due to the fact that the car can be in the train and not 981 // at its starting location. 982 // Therefore we use the local true to disable it. 983 boolean local = false; 984 if (car.getTrack() == null) { 985 local = true; 986 } 987 for (String attribute : format) { 988 buf.append(getCarAttribute(car, attribute, !PICKUP, local)); 989 } 990 return buf.toString(); 991 } 992 993 /** 994 * Returns the move car string. Useful for frames like train conductor and 995 * yardmaster. 996 * 997 * @param car The car being printed. 998 * @param isManifest when true use manifest format, when false use switch 999 * list format 1000 * @return move car string 1001 */ 1002 public String localMoveCar(Car car, boolean isManifest) { 1003 StringBuffer buf = new StringBuffer(); 1004 String[] format; 1005 if (isManifest) { 1006 format = Setup.getLocalManifestMessageFormat(); 1007 } else { 1008 format = Setup.getLocalSwitchListMessageFormat(); 1009 } 1010 for (String attribute : format) { 1011 buf.append(getCarAttribute(car, attribute, !PICKUP, LOCAL)); 1012 } 1013 return buf.toString(); 1014 } 1015 1016 List<String> utilityCarTypes = new ArrayList<>(); 1017 private static final int UTILITY_CAR_COUNT_FIELD_SIZE = 3; 1018 1019 /** 1020 * Add a list of utility cars scheduled for pick up from the route location 1021 * to the output file. The cars are blocked by destination. 1022 * 1023 * @param file Manifest or Switch List File. 1024 * @param carList List of cars for this train. 1025 * @param car The utility car. 1026 * @param isTruncate True if manifest is to be truncated 1027 * @param isManifest True if manifest, false if switch list. 1028 */ 1029 protected void pickupUtilityCars(PrintWriter file, List<Car> carList, Car car, boolean isTruncate, 1030 boolean isManifest) { 1031 // list utility cars by type, track, length, and load 1032 String[] format; 1033 if (isManifest) { 1034 format = Setup.getPickupUtilityManifestMessageFormat(); 1035 } else { 1036 format = Setup.getPickupUtilitySwitchListMessageFormat(); 1037 } 1038 if (isTruncate && isManifest) { 1039 format = Setup.createTruncatedManifestMessageFormat(format); 1040 } 1041 int count = countUtilityCars(format, carList, car, PICKUP); 1042 if (count == 0) { 1043 return; // already printed out this car type 1044 } 1045 pickUpCar(file, car, 1046 new StringBuffer(padAndTruncateIfNeeded(Setup.getPickupCarPrefix(), 1047 isManifest ? Setup.getManifestPrefixLength() : Setup.getSwitchListPrefixLength()) + 1048 SPACE + 1049 padString(Integer.toString(count), UTILITY_CAR_COUNT_FIELD_SIZE)), 1050 format, isManifest); 1051 } 1052 1053 /** 1054 * Add a list of utility cars scheduled for drop at the route location to 1055 * the output file. 1056 * 1057 * @param file Manifest or Switch List File. 1058 * @param carList List of cars for this train. 1059 * @param car The utility car. 1060 * @param isTruncate True if manifest is to be truncated 1061 * @param isManifest True if manifest, false if switch list. 1062 */ 1063 protected void setoutUtilityCars(PrintWriter file, List<Car> carList, Car car, boolean isTruncate, 1064 boolean isManifest) { 1065 boolean isLocal = car.isLocalMove(); 1066 StringBuffer buf; 1067 String[] format; 1068 if (isLocal && isManifest) { 1069 buf = new StringBuffer(padAndTruncateIfNeeded(Setup.getLocalPrefix(), Setup.getManifestPrefixLength())); 1070 format = Setup.getLocalUtilityManifestMessageFormat(); 1071 } else if (!isLocal && isManifest) { 1072 buf = new StringBuffer(padAndTruncateIfNeeded(Setup.getDropCarPrefix(), Setup.getManifestPrefixLength())); 1073 format = Setup.getDropUtilityManifestMessageFormat(); 1074 } else if (isLocal && !isManifest) { 1075 buf = new StringBuffer( 1076 padAndTruncateIfNeeded(Setup.getSwitchListLocalPrefix(), Setup.getSwitchListPrefixLength())); 1077 format = Setup.getLocalUtilitySwitchListMessageFormat(); 1078 } else { 1079 buf = new StringBuffer( 1080 padAndTruncateIfNeeded(Setup.getSwitchListDropCarPrefix(), Setup.getSwitchListPrefixLength())); 1081 format = Setup.getDropUtilitySwitchListMessageFormat(); 1082 } 1083 if (isTruncate && isManifest) { 1084 format = Setup.createTruncatedManifestMessageFormat(format); 1085 } 1086 1087 int count = countUtilityCars(format, carList, car, !PICKUP); 1088 if (count == 0) { 1089 return; // already printed out this car type 1090 } 1091 buf.append(SPACE + padString(Integer.toString(count), UTILITY_CAR_COUNT_FIELD_SIZE)); 1092 dropCar(file, car, buf, format, isLocal, isManifest); 1093 } 1094 1095 public String pickupUtilityCars(List<Car> carList, Car car, boolean isManifest, boolean isTwoColumnTrack) { 1096 int count = countPickupUtilityCars(carList, car, isManifest); 1097 if (count == 0) { 1098 return null; 1099 } 1100 String[] format; 1101 if (isManifest && !isTwoColumnTrack) { 1102 format = Setup.getPickupUtilityManifestMessageFormat(); 1103 } else if (!isManifest && !isTwoColumnTrack) { 1104 format = Setup.getPickupUtilitySwitchListMessageFormat(); 1105 } else if (isManifest && isTwoColumnTrack) { 1106 format = Setup.getPickupTwoColumnByTrackUtilityManifestMessageFormat(); 1107 } else { 1108 format = Setup.getPickupTwoColumnByTrackUtilitySwitchListMessageFormat(); 1109 } 1110 StringBuffer buf = new StringBuffer(SPACE + padString(Integer.toString(count), UTILITY_CAR_COUNT_FIELD_SIZE)); 1111 for (String attribute : format) { 1112 buf.append(getCarAttribute(car, attribute, PICKUP, !LOCAL)); 1113 } 1114 return buf.toString(); 1115 } 1116 1117 public int countPickupUtilityCars(List<Car> carList, Car car, boolean isManifest) { 1118 // list utility cars by type, track, length, and load 1119 String[] format; 1120 if (isManifest) { 1121 format = Setup.getPickupUtilityManifestMessageFormat(); 1122 } else { 1123 format = Setup.getPickupUtilitySwitchListMessageFormat(); 1124 } 1125 return countUtilityCars(format, carList, car, PICKUP); 1126 } 1127 1128 /** 1129 * For the Conductor and Yardmaster windows. 1130 * 1131 * @param carList List of cars for this train. 1132 * @param car The utility car. 1133 * @param isLocal True if local move. 1134 * @param isManifest True if manifest, false if switch list. 1135 * @return A string representing the work of identical utility cars. 1136 */ 1137 public String setoutUtilityCars(List<Car> carList, Car car, boolean isLocal, boolean isManifest) { 1138 return setoutUtilityCars(carList, car, isLocal, isManifest, !IS_TWO_COLUMN_TRACK); 1139 } 1140 1141 protected String setoutUtilityCars(List<Car> carList, Car car, boolean isLocal, boolean isManifest, 1142 boolean isTwoColumnTrack) { 1143 int count = countSetoutUtilityCars(carList, car, isLocal, isManifest); 1144 if (count == 0) { 1145 return null; 1146 } 1147 // list utility cars by type, track, length, and load 1148 String[] format; 1149 if (isLocal && isManifest && !isTwoColumnTrack) { 1150 format = Setup.getLocalUtilityManifestMessageFormat(); 1151 } else if (isLocal && !isManifest && !isTwoColumnTrack) { 1152 format = Setup.getLocalUtilitySwitchListMessageFormat(); 1153 } else if (!isLocal && !isManifest && !isTwoColumnTrack) { 1154 format = Setup.getDropUtilitySwitchListMessageFormat(); 1155 } else if (!isLocal && isManifest && !isTwoColumnTrack) { 1156 format = Setup.getDropUtilityManifestMessageFormat(); 1157 } else if (isManifest && isTwoColumnTrack) { 1158 format = Setup.getDropTwoColumnByTrackUtilityManifestMessageFormat(); 1159 } else { 1160 format = Setup.getDropTwoColumnByTrackUtilitySwitchListMessageFormat(); 1161 } 1162 StringBuffer buf = new StringBuffer(SPACE + padString(Integer.toString(count), UTILITY_CAR_COUNT_FIELD_SIZE)); 1163 // TODO the Setup.Location doesn't work correctly for the conductor 1164 // window due to the fact that the car can be in the train and not 1165 // at its starting location. 1166 // Therefore we use the local true to disable it. 1167 if (car.getTrack() == null) { 1168 isLocal = true; 1169 } 1170 for (String attribute : format) { 1171 buf.append(getCarAttribute(car, attribute, !PICKUP, isLocal)); 1172 } 1173 return buf.toString(); 1174 } 1175 1176 public int countSetoutUtilityCars(List<Car> carList, Car car, boolean isLocal, boolean isManifest) { 1177 // list utility cars by type, track, length, and load 1178 String[] format; 1179 if (isLocal && isManifest) { 1180 format = Setup.getLocalUtilityManifestMessageFormat(); 1181 } else if (isLocal && !isManifest) { 1182 format = Setup.getLocalUtilitySwitchListMessageFormat(); 1183 } else if (!isLocal && !isManifest) { 1184 format = Setup.getDropUtilitySwitchListMessageFormat(); 1185 } else { 1186 format = Setup.getDropUtilityManifestMessageFormat(); 1187 } 1188 return countUtilityCars(format, carList, car, !PICKUP); 1189 } 1190 1191 /** 1192 * Scans the car list for utility cars that have the same attributes as the 1193 * car provided. Returns 0 if this car type has already been processed, 1194 * otherwise the number of cars with the same attribute. 1195 * 1196 * @param format Message format. 1197 * @param carList List of cars for this train 1198 * @param car The utility car. 1199 * @param isPickup True if pick up, false if set out. 1200 * @return 0 if the car type has already been processed 1201 */ 1202 protected int countUtilityCars(String[] format, List<Car> carList, Car car, boolean isPickup) { 1203 int count = 0; 1204 // figure out if the user wants to show the car's length 1205 boolean showLength = showUtilityCarLength(format); 1206 // figure out if the user want to show the car's loads 1207 boolean showLoad = showUtilityCarLoad(format); 1208 boolean showLocation = false; 1209 boolean showDestination = false; 1210 String carType = car.getTypeName().split(HYPHEN)[0]; 1211 String carAttributes; 1212 // Note for car pick up: type, id, track name. For set out type, track 1213 // name, id (reversed). 1214 if (isPickup) { 1215 carAttributes = carType + car.getRouteLocationId() + car.getSplitTrackName(); 1216 showDestination = showUtilityCarDestination(format); 1217 if (showDestination) { 1218 carAttributes = carAttributes + car.getRouteDestinationId(); 1219 } 1220 } else { 1221 // set outs and local moves 1222 carAttributes = carType + car.getSplitDestinationTrackName() + car.getRouteDestinationId(); 1223 showLocation = showUtilityCarLocation(format); 1224 if (showLocation && car.getTrack() != null) { 1225 carAttributes = carAttributes + car.getRouteLocationId(); 1226 } 1227 } 1228 if (car.isLocalMove()) { 1229 carAttributes = carAttributes + car.getSplitTrackName(); 1230 } 1231 if (showLength) { 1232 carAttributes = carAttributes + car.getLength(); 1233 } 1234 if (showLoad) { 1235 carAttributes = carAttributes + car.getLoadName(); 1236 } 1237 // have we already done this car type? 1238 if (!utilityCarTypes.contains(carAttributes)) { 1239 utilityCarTypes.add(carAttributes); // don't do this type again 1240 // determine how many cars of this type 1241 for (Car c : carList) { 1242 if (!c.isUtility()) { 1243 continue; 1244 } 1245 String cType = c.getTypeName().split(HYPHEN)[0]; 1246 if (!cType.equals(carType)) { 1247 continue; 1248 } 1249 if (showLength && !c.getLength().equals(car.getLength())) { 1250 continue; 1251 } 1252 if (showLoad && !c.getLoadName().equals(car.getLoadName())) { 1253 continue; 1254 } 1255 if (showLocation && !c.getRouteLocationId().equals(car.getRouteLocationId())) { 1256 continue; 1257 } 1258 if (showDestination && !c.getRouteDestinationId().equals(car.getRouteDestinationId())) { 1259 continue; 1260 } 1261 if (car.isLocalMove() ^ c.isLocalMove()) { 1262 continue; 1263 } 1264 if (isPickup && 1265 c.getRouteLocation() == car.getRouteLocation() && 1266 c.getSplitTrackName().equals(car.getSplitTrackName())) { 1267 count++; 1268 } 1269 if (!isPickup && 1270 c.getRouteDestination() == car.getRouteDestination() && 1271 c.getSplitDestinationTrackName().equals(car.getSplitDestinationTrackName()) && 1272 (c.getSplitTrackName().equals(car.getSplitTrackName()) || !c.isLocalMove())) { 1273 count++; 1274 } 1275 } 1276 } 1277 return count; 1278 } 1279 1280 public void clearUtilityCarTypes() { 1281 utilityCarTypes.clear(); 1282 } 1283 1284 private boolean showUtilityCarLength(String[] mFormat) { 1285 return showUtilityCarAttribute(Setup.LENGTH, mFormat); 1286 } 1287 1288 private boolean showUtilityCarLoad(String[] mFormat) { 1289 return showUtilityCarAttribute(Setup.LOAD, mFormat); 1290 } 1291 1292 private boolean showUtilityCarLocation(String[] mFormat) { 1293 return showUtilityCarAttribute(Setup.LOCATION, mFormat); 1294 } 1295 1296 private boolean showUtilityCarDestination(String[] mFormat) { 1297 return showUtilityCarAttribute(Setup.DESTINATION, mFormat) || 1298 showUtilityCarAttribute(Setup.DEST_TRACK, mFormat); 1299 } 1300 1301 private boolean showUtilityCarAttribute(String string, String[] mFormat) { 1302 for (String s : mFormat) { 1303 if (s.equals(string)) { 1304 return true; 1305 } 1306 } 1307 return false; 1308 } 1309 1310 /** 1311 * Writes a line to the build report file 1312 * 1313 * @param file build report file 1314 * @param level print level 1315 * @param string string to write 1316 */ 1317 public static void addLine(PrintWriter file, String level, String string) { 1318 log.debug("addLine: {}", string); 1319 if (file != null) { 1320 String[] lines = string.split(NEW_LINE); 1321 for (String line : lines) { 1322 printLine(file, level, line); 1323 } 1324 } 1325 } 1326 1327 // only used by build report 1328 private static void printLine(PrintWriter file, String level, String string) { 1329 int lineLengthMax = getLineLength(Setup.PORTRAIT, Setup.MONOSPACED, Font.PLAIN, Setup.getBuildReportFontSize()); 1330 if (string.length() > lineLengthMax) { 1331 String[] words = string.split(SPACE); 1332 StringBuffer sb = new StringBuffer(); 1333 for (String word : words) { 1334 if (sb.length() + word.length() < lineLengthMax) { 1335 sb.append(word + SPACE); 1336 } else { 1337 file.println(level + BUILD_REPORT_CHAR + SPACE + sb.toString()); 1338 sb = new StringBuffer(word + SPACE); 1339 } 1340 } 1341 string = sb.toString(); 1342 } 1343 file.println(level + BUILD_REPORT_CHAR + SPACE + string); 1344 } 1345 1346 /** 1347 * Writes string to file. No line length wrap or protection. 1348 * 1349 * @param file The File to write to. 1350 * @param string The string to write. 1351 */ 1352 protected void addLine(PrintWriter file, String string) { 1353 log.debug("addLine: {}", string); 1354 if (file != null) { 1355 file.println(string); 1356 } 1357 } 1358 1359 /** 1360 * Writes a string to a file. Checks for string length, and will 1361 * automatically wrap lines. 1362 * 1363 * @param file The File to write to. 1364 * @param string The string to write. 1365 * @param isManifest set true for manifest page orientation, false for 1366 * switch list orientation 1367 */ 1368 protected void newLine(PrintWriter file, String string, boolean isManifest) { 1369 String[] lines = string.split(NEW_LINE); 1370 for (String line : lines) { 1371 String[] words = line.split(SPACE); 1372 StringBuffer sb = new StringBuffer(); 1373 for (String word : words) { 1374 if (checkStringLength(sb.toString() + word, isManifest)) { 1375 sb.append(word + SPACE); 1376 } else { 1377 sb.setLength(sb.length() - 1); // remove last space added to string 1378 addLine(file, sb.toString()); 1379 sb = new StringBuffer(word + SPACE); 1380 } 1381 } 1382 if (sb.length() > 0) { 1383 sb.setLength(sb.length() - 1); // remove last space added to string 1384 } 1385 addLine(file, sb.toString()); 1386 } 1387 } 1388 1389 /** 1390 * Adds a blank line to the file. 1391 * 1392 * @param file The File to write to. 1393 */ 1394 protected void newLine(PrintWriter file) { 1395 file.println(BLANK_LINE); 1396 } 1397 1398 /** 1399 * Splits a string (example-number) as long as the second part of the string 1400 * is an integer or if the first character after the hyphen is a left 1401 * parenthesis "(". 1402 * 1403 * @param name The string to split if necessary. 1404 * @return First half of the string. 1405 */ 1406 public static String splitString(String name) { 1407 String[] splitname = name.split(HYPHEN); 1408 // is the hyphen followed by a number or left parenthesis? 1409 if (splitname.length > 1 && !splitname[1].startsWith("(")) { 1410 try { 1411 Integer.parseInt(splitname[1]); 1412 } catch (NumberFormatException e) { 1413 // no return full name 1414 return name.trim(); 1415 } 1416 } 1417 return splitname[0].trim(); 1418 } 1419 1420 /** 1421 * Splits a string if there's a hyphen followed by a left parenthesis "-(". 1422 * 1423 * @return First half of the string. 1424 */ 1425 private static String splitStringLeftParenthesis(String name) { 1426 String[] splitname = name.split(HYPHEN); 1427 if (splitname.length > 1 && splitname[1].startsWith("(")) { 1428 return splitname[0].trim(); 1429 } 1430 return name.trim(); 1431 } 1432 1433 // returns true if there's work at location 1434 protected boolean isThereWorkAtLocation(List<Car> carList, List<Engine> engList, RouteLocation rl) { 1435 if (carList != null) { 1436 for (Car car : carList) { 1437 if (car.getRouteLocation() == rl || car.getRouteDestination() == rl) { 1438 return true; 1439 } 1440 } 1441 } 1442 if (engList != null) { 1443 for (Engine eng : engList) { 1444 if (eng.getRouteLocation() == rl || eng.getRouteDestination() == rl) { 1445 return true; 1446 } 1447 } 1448 } 1449 return false; 1450 } 1451 1452 /** 1453 * returns true if the train has work at the location 1454 * 1455 * @param train The Train. 1456 * @param location The Location. 1457 * @return true if the train has work at the location 1458 */ 1459 public static boolean isThereWorkAtLocation(Train train, Location location) { 1460 if (isThereWorkAtLocation(train, location, InstanceManager.getDefault(CarManager.class).getList(train))) { 1461 return true; 1462 } 1463 if (isThereWorkAtLocation(train, location, InstanceManager.getDefault(EngineManager.class).getList(train))) { 1464 return true; 1465 } 1466 return false; 1467 } 1468 1469 private static boolean isThereWorkAtLocation(Train train, Location location, List<? extends RollingStock> list) { 1470 for (RollingStock rs : list) { 1471 if ((rs.getRouteLocation() != null && 1472 rs.getTrack() != null && 1473 rs.getRouteLocation().getSplitName() 1474 .equals(location.getSplitName())) || 1475 (rs.getRouteDestination() != null && 1476 rs.getRouteDestination().getSplitName().equals(location.getSplitName()))) { 1477 return true; 1478 } 1479 } 1480 return false; 1481 } 1482 1483 protected void addCarsLocationUnknown(PrintWriter file, boolean isManifest) { 1484 List<Car> cars = carManager.getCarsLocationUnknown(); 1485 if (cars.size() == 0) { 1486 return; // no cars to search for! 1487 } 1488 newLine(file); 1489 newLine(file, Setup.getMiaComment(), isManifest); 1490 if (Setup.isPrintHeadersEnabled()) { 1491 printHorizontalLine(file, isManifest); 1492 newLine(file, SPACE + getHeader(Setup.getMissingCarMessageFormat(), false, false, false), isManifest); 1493 printHorizontalLine(file, isManifest); 1494 } 1495 for (Car car : cars) { 1496 addSearchForCar(file, car, isManifest); 1497 } 1498 } 1499 1500 private void addSearchForCar(PrintWriter file, Car car, boolean isManifest) { 1501 StringBuffer buf = new StringBuffer(); 1502 String[] format = Setup.getMissingCarMessageFormat(); 1503 for (String attribute : format) { 1504 buf.append(getCarAttribute(car, attribute, false, false)); 1505 } 1506 newLine(file, buf.toString(), isManifest); 1507 } 1508 1509 /* 1510 * Gets an engine's attribute String. Returns empty if there isn't an 1511 * attribute and not using the tabular feature. isPickup true when engine is 1512 * being picked up. 1513 */ 1514 private String getEngineAttribute(Engine engine, String attribute, boolean isPickup) { 1515 if (!attribute.equals(Setup.BLANK)) { 1516 String s = SPACE + getEngineAttrib(engine, attribute, isPickup); 1517 if (Setup.isTabEnabled() || !s.trim().isEmpty()) { 1518 return s; 1519 } 1520 } 1521 return ""; 1522 } 1523 1524 /* 1525 * Can not use String case statement since Setup.MODEL, etc, are not fixed 1526 * strings. 1527 */ 1528 private String getEngineAttrib(Engine engine, String attribute, boolean isPickup) { 1529 if (attribute.equals(Setup.MODEL)) { 1530 return padAndTruncateIfNeeded(splitStringLeftParenthesis(engine.getModel()), 1531 InstanceManager.getDefault(EngineModels.class).getMaxNameLength()); 1532 } else if (attribute.equals(Setup.HP)) { 1533 return padAndTruncateIfNeeded(engine.getHp(), 5) + 1534 (Setup.isPrintHeadersEnabled() ? "" : TrainManifestHeaderText.getStringHeader_Hp()); 1535 } else if (attribute.equals(Setup.CONSIST)) { 1536 return padAndTruncateIfNeeded(engine.getConsistName(), 1537 InstanceManager.getDefault(ConsistManager.class).getMaxNameLength()); 1538 } else if (attribute.equals(Setup.DCC_ADDRESS)) { 1539 return padAndTruncateIfNeeded(engine.getDccAddress(), 1540 TrainManifestHeaderText.getStringHeader_DCC_Address().length()); 1541 } else if (attribute.equals(Setup.COMMENT)) { 1542 return padAndTruncateIfNeeded(engine.getComment(), engineManager.getMaxCommentLength()); 1543 } 1544 return getRollingStockAttribute(engine, attribute, isPickup, false); 1545 } 1546 1547 /* 1548 * Gets a car's attribute String. Returns empty if there isn't an attribute 1549 * and not using the tabular feature. isPickup true when car is being picked 1550 * up. isLocal true when car is performing a local move. 1551 */ 1552 private String getCarAttribute(Car car, String attribute, boolean isPickup, boolean isLocal) { 1553 if (!attribute.equals(Setup.BLANK)) { 1554 String s = SPACE + getCarAttrib(car, attribute, isPickup, isLocal); 1555 if (Setup.isTabEnabled() || !s.trim().isEmpty()) { 1556 return s; 1557 } 1558 } 1559 return ""; 1560 } 1561 1562 private String getCarAttrib(Car car, String attribute, boolean isPickup, boolean isLocal) { 1563 if (attribute.equals(Setup.LOAD)) { 1564 return ((car.isCaboose() && !Setup.isPrintCabooseLoadEnabled()) || 1565 (car.isPassenger() && !Setup.isPrintPassengerLoadEnabled())) 1566 ? padAndTruncateIfNeeded("", 1567 InstanceManager.getDefault(CarLoads.class).getMaxNameLength()) 1568 : padAndTruncateIfNeeded(car.getLoadName().split(HYPHEN)[0], 1569 InstanceManager.getDefault(CarLoads.class).getMaxNameLength()); 1570 } else if (attribute.equals(Setup.LOAD_TYPE)) { 1571 return padAndTruncateIfNeeded(car.getLoadType(), 1572 TrainManifestHeaderText.getStringHeader_Load_Type().length()); 1573 } else if (attribute.equals(Setup.HAZARDOUS)) { 1574 return (car.isHazardous() ? Setup.getHazardousMsg() 1575 : padAndTruncateIfNeeded("", Setup.getHazardousMsg().length())); 1576 } else if (attribute.equals(Setup.DROP_COMMENT)) { 1577 return padAndTruncateIfNeeded(car.getDropComment(), 1578 InstanceManager.getDefault(CarLoads.class).getMaxLoadCommentLength()); 1579 } else if (attribute.equals(Setup.PICKUP_COMMENT)) { 1580 return padAndTruncateIfNeeded(car.getPickupComment(), 1581 InstanceManager.getDefault(CarLoads.class).getMaxLoadCommentLength()); 1582 } else if (attribute.equals(Setup.KERNEL)) { 1583 return padAndTruncateIfNeeded(car.getKernelName(), 1584 InstanceManager.getDefault(KernelManager.class).getMaxNameLength()); 1585 } else if (attribute.equals(Setup.KERNEL_SIZE)) { 1586 if (car.isLead()) { 1587 return padAndTruncateIfNeeded(Integer.toString(car.getKernel().getSize()), 2); 1588 } else { 1589 return SPACE + SPACE; // assumes that kernel size is 99 or less 1590 } 1591 } else if (attribute.equals(Setup.RWE)) { 1592 if (!car.getReturnWhenEmptyDestinationName().equals(Car.NONE)) { 1593 // format RWE destination and track name 1594 String rweAndTrackName = car.getSplitReturnWhenEmptyDestinationName(); 1595 if (!car.getReturnWhenEmptyDestTrackName().equals(Car.NONE)) { 1596 rweAndTrackName = rweAndTrackName + "," + SPACE + car.getSplitReturnWhenEmptyDestinationTrackName(); 1597 } 1598 return Setup.isPrintHeadersEnabled() 1599 ? padAndTruncateIfNeeded(rweAndTrackName, locationManager.getMaxLocationAndTrackNameLength()) 1600 : padAndTruncateIfNeeded( 1601 TrainManifestHeaderText.getStringHeader_RWE() + SPACE + rweAndTrackName, 1602 locationManager.getMaxLocationAndTrackNameLength() + 1603 TrainManifestHeaderText.getStringHeader_RWE().length() + 1604 3); 1605 } 1606 return padAndTruncateIfNeeded("", locationManager.getMaxLocationAndTrackNameLength()); 1607 } else if (attribute.equals(Setup.FINAL_DEST)) { 1608 return Setup.isPrintHeadersEnabled() 1609 ? padAndTruncateIfNeeded(car.getSplitFinalDestinationName(), 1610 locationManager.getMaxLocationNameLength()) 1611 : padAndTruncateIfNeeded( 1612 TrainManifestText.getStringFinalDestination() + 1613 SPACE + 1614 car.getSplitFinalDestinationName(), 1615 locationManager.getMaxLocationNameLength() + 1616 TrainManifestText.getStringFinalDestination().length() + 1617 1); 1618 } else if (attribute.equals(Setup.FINAL_DEST_TRACK)) { 1619 // format final destination and track name 1620 String FDAndTrackName = car.getSplitFinalDestinationName(); 1621 if (!car.getFinalDestinationTrackName().equals(Car.NONE)) { 1622 FDAndTrackName = FDAndTrackName + "," + SPACE + car.getSplitFinalDestinationTrackName(); 1623 } 1624 return Setup.isPrintHeadersEnabled() 1625 ? padAndTruncateIfNeeded(FDAndTrackName, locationManager.getMaxLocationAndTrackNameLength() + 2) 1626 : padAndTruncateIfNeeded(TrainManifestText.getStringFinalDestination() + SPACE + FDAndTrackName, 1627 locationManager.getMaxLocationAndTrackNameLength() + 1628 TrainManifestText.getStringFinalDestination().length() + 1629 3); 1630 } else if (attribute.equals(Setup.DIVISION)) { 1631 return padAndTruncateIfNeeded(car.getDivisionName(), 1632 InstanceManager.getDefault(DivisionManager.class).getMaxDivisionNameLength()); 1633 } else if (attribute.equals(Setup.COMMENT)) { 1634 return padAndTruncateIfNeeded(car.getComment(), carManager.getMaxCommentLength()); 1635 } 1636 return getRollingStockAttribute(car, attribute, isPickup, isLocal); 1637 } 1638 1639 private String getRollingStockAttribute(RollingStock rs, String attribute, boolean isPickup, boolean isLocal) { 1640 try { 1641 if (attribute.equals(Setup.NUMBER)) { 1642 return padAndTruncateIfNeeded(splitString(rs.getNumber()), Control.max_len_string_print_road_number); 1643 } else if (attribute.equals(Setup.ROAD)) { 1644 String road = rs.getRoadName().split(HYPHEN)[0]; 1645 return padAndTruncateIfNeeded(road, InstanceManager.getDefault(CarRoads.class).getMaxNameLength()); 1646 } else if (attribute.equals(Setup.TYPE)) { 1647 String type = rs.getTypeName().split(HYPHEN)[0]; 1648 return padAndTruncateIfNeeded(type, InstanceManager.getDefault(CarTypes.class).getMaxNameLength()); 1649 } else if (attribute.equals(Setup.LENGTH)) { 1650 return padAndTruncateIfNeeded(rs.getLength() + Setup.getLengthUnitAbv(), 1651 InstanceManager.getDefault(CarLengths.class).getMaxNameLength()); 1652 } else if (attribute.equals(Setup.WEIGHT)) { 1653 return padAndTruncateIfNeeded(Integer.toString(rs.getAdjustedWeightTons()), 1654 Control.max_len_string_weight_name) + 1655 (Setup.isPrintHeadersEnabled() ? "" : TrainManifestHeaderText.getStringHeader_Weight()); 1656 } else if (attribute.equals(Setup.COLOR)) { 1657 return padAndTruncateIfNeeded(rs.getColor(), 1658 InstanceManager.getDefault(CarColors.class).getMaxNameLength()); 1659 } else if (((attribute.equals(Setup.LOCATION)) && (isPickup || isLocal)) || 1660 (attribute.equals(Setup.TRACK) && isPickup)) { 1661 return Setup.isPrintHeadersEnabled() 1662 ? padAndTruncateIfNeeded(rs.getSplitTrackName(), 1663 locationManager.getMaxTrackNameLength()) 1664 : padAndTruncateIfNeeded( 1665 TrainManifestText.getStringFrom() + SPACE + rs.getSplitTrackName(), 1666 TrainManifestText.getStringFrom().length() + 1667 locationManager.getMaxTrackNameLength() + 1668 1); 1669 } else if (attribute.equals(Setup.LOCATION) && !isPickup && !isLocal) { 1670 return Setup.isPrintHeadersEnabled() 1671 ? padAndTruncateIfNeeded(rs.getSplitLocationName(), 1672 locationManager.getMaxLocationNameLength()) 1673 : padAndTruncateIfNeeded( 1674 TrainManifestText.getStringFrom() + SPACE + rs.getSplitLocationName(), 1675 locationManager.getMaxLocationNameLength() + 1676 TrainManifestText.getStringFrom().length() + 1677 1); 1678 } else if (attribute.equals(Setup.DESTINATION) && isPickup) { 1679 if (Setup.isPrintHeadersEnabled()) { 1680 return padAndTruncateIfNeeded(rs.getSplitDestinationName(), 1681 locationManager.getMaxLocationNameLength()); 1682 } 1683 if (Setup.isTabEnabled()) { 1684 return padAndTruncateIfNeeded( 1685 TrainManifestText.getStringDest() + SPACE + rs.getSplitDestinationName(), 1686 TrainManifestText.getStringDest().length() + 1687 locationManager.getMaxLocationNameLength() + 1688 1); 1689 } else { 1690 return TrainManifestText.getStringDestination() + 1691 SPACE + 1692 rs.getSplitDestinationName(); 1693 } 1694 } else if ((attribute.equals(Setup.DESTINATION) || attribute.equals(Setup.TRACK)) && !isPickup) { 1695 return Setup.isPrintHeadersEnabled() 1696 ? padAndTruncateIfNeeded(rs.getSplitDestinationTrackName(), 1697 locationManager.getMaxTrackNameLength()) 1698 : padAndTruncateIfNeeded( 1699 TrainManifestText.getStringTo() + 1700 SPACE + 1701 rs.getSplitDestinationTrackName(), 1702 locationManager.getMaxTrackNameLength() + 1703 TrainManifestText.getStringTo().length() + 1704 1); 1705 } else if (attribute.equals(Setup.DEST_TRACK)) { 1706 // format destination name and destination track name 1707 String destAndTrackName = 1708 rs.getSplitDestinationName() + "," + SPACE + rs.getSplitDestinationTrackName(); 1709 return Setup.isPrintHeadersEnabled() 1710 ? padAndTruncateIfNeeded(destAndTrackName, 1711 locationManager.getMaxLocationAndTrackNameLength() + 2) 1712 : padAndTruncateIfNeeded(TrainManifestText.getStringDest() + SPACE + destAndTrackName, 1713 locationManager.getMaxLocationAndTrackNameLength() + 1714 TrainManifestText.getStringDest().length() + 1715 3); 1716 } else if (attribute.equals(Setup.OWNER)) { 1717 return padAndTruncateIfNeeded(rs.getOwnerName(), 1718 InstanceManager.getDefault(CarOwners.class).getMaxNameLength()); 1719 } // the three utility attributes that don't get printed but need to 1720 // be tabbed out 1721 else if (attribute.equals(Setup.NO_NUMBER)) { 1722 return padAndTruncateIfNeeded("", 1723 Control.max_len_string_print_road_number - (UTILITY_CAR_COUNT_FIELD_SIZE + 1)); 1724 } else if (attribute.equals(Setup.NO_ROAD)) { 1725 return padAndTruncateIfNeeded("", InstanceManager.getDefault(CarRoads.class).getMaxNameLength()); 1726 } else if (attribute.equals(Setup.NO_COLOR)) { 1727 return padAndTruncateIfNeeded("", InstanceManager.getDefault(CarColors.class).getMaxNameLength()); 1728 } // there are four truncated manifest attributes 1729 else if (attribute.equals(Setup.NO_DEST_TRACK)) { 1730 return Setup.isPrintHeadersEnabled() 1731 ? padAndTruncateIfNeeded("", locationManager.getMaxLocationAndTrackNameLength() + 1) 1732 : ""; 1733 } else if ((attribute.equals(Setup.NO_LOCATION) && !isPickup) || 1734 (attribute.equals(Setup.NO_DESTINATION) && isPickup)) { 1735 return Setup.isPrintHeadersEnabled() 1736 ? padAndTruncateIfNeeded("", locationManager.getMaxLocationNameLength()) 1737 : ""; 1738 } else if (attribute.equals(Setup.NO_TRACK) || 1739 attribute.equals(Setup.NO_LOCATION) || 1740 attribute.equals(Setup.NO_DESTINATION)) { 1741 return Setup.isPrintHeadersEnabled() 1742 ? padAndTruncateIfNeeded("", locationManager.getMaxTrackNameLength()) 1743 : ""; 1744 } else if (attribute.equals(Setup.TAB)) { 1745 return createTabIfNeeded(Setup.getTab1Length() - 1); 1746 } else if (attribute.equals(Setup.TAB2)) { 1747 return createTabIfNeeded(Setup.getTab2Length() - 1); 1748 } else if (attribute.equals(Setup.TAB3)) { 1749 return createTabIfNeeded(Setup.getTab3Length() - 1); 1750 } 1751 // something isn't right! 1752 return Bundle.getMessage("ErrorPrintOptions", attribute); 1753 1754 } catch (ArrayIndexOutOfBoundsException e) { 1755 if (attribute.equals(Setup.ROAD)) { 1756 return padAndTruncateIfNeeded("", InstanceManager.getDefault(CarRoads.class).getMaxNameLength()); 1757 } else if (attribute.equals(Setup.TYPE)) { 1758 return padAndTruncateIfNeeded("", InstanceManager.getDefault(CarTypes.class).getMaxNameLength()); 1759 } 1760 // something isn't right! 1761 return Bundle.getMessage("ErrorPrintOptions", attribute); 1762 } 1763 } 1764 1765 /** 1766 * Two column header format. Left side pick ups, right side set outs 1767 * 1768 * @param file Manifest or switch list File. 1769 * @param isManifest True if manifest, false if switch list. 1770 */ 1771 public void printEngineHeader(PrintWriter file, boolean isManifest) { 1772 int lineLength = getLineLength(isManifest); 1773 printHorizontalLine(file, 0, lineLength); 1774 if (!Setup.isPrintHeadersEnabled()) { 1775 return; 1776 } 1777 if (!Setup.getPickupEnginePrefix().trim().isEmpty() || !Setup.getDropEnginePrefix().trim().isEmpty()) { 1778 // center engine pick up and set out text 1779 String s = padAndTruncate(tabString(Setup.getPickupEnginePrefix().trim(), 1780 lineLength / 4 - Setup.getPickupEnginePrefix().length() / 2), lineLength / 2) + 1781 VERTICAL_LINE_CHAR + 1782 tabString(Setup.getDropEnginePrefix(), lineLength / 4 - Setup.getDropEnginePrefix().length() / 2); 1783 s = padAndTruncate(s, lineLength); 1784 addLine(file, s); 1785 printHorizontalLine(file, 0, lineLength); 1786 } 1787 1788 String s = padAndTruncate(getPickupEngineHeader(), lineLength / 2); 1789 s = padAndTruncate(s + VERTICAL_LINE_CHAR + getDropEngineHeader(), lineLength); 1790 addLine(file, s); 1791 printHorizontalLine(file, 0, lineLength); 1792 } 1793 1794 public void printPickupEngineHeader(PrintWriter file, boolean isManifest) { 1795 int lineLength = getLineLength(isManifest); 1796 printHorizontalLine(file, 0, lineLength); 1797 String s = padAndTruncate(createTabIfNeeded(Setup.getManifestPrefixLength() + 1) + getPickupEngineHeader(), 1798 lineLength); 1799 addLine(file, s); 1800 printHorizontalLine(file, 0, lineLength); 1801 } 1802 1803 public void printDropEngineHeader(PrintWriter file, boolean isManifest) { 1804 int lineLength = getLineLength(isManifest); 1805 printHorizontalLine(file, 0, lineLength); 1806 String s = padAndTruncate(createTabIfNeeded(Setup.getManifestPrefixLength() + 1) + getDropEngineHeader(), 1807 lineLength); 1808 addLine(file, s); 1809 printHorizontalLine(file, 0, lineLength); 1810 } 1811 1812 /** 1813 * Prints the two column header for cars. Left side pick ups, right side set 1814 * outs. 1815 * 1816 * @param file Manifest or Switch List File 1817 * @param isManifest True if manifest, false if switch list. 1818 * @param isTwoColumnTrack True if two column format using track names. 1819 */ 1820 public void printCarHeader(PrintWriter file, boolean isManifest, boolean isTwoColumnTrack) { 1821 int lineLength = getLineLength(isManifest); 1822 printHorizontalLine(file, 0, lineLength); 1823 if (!Setup.isPrintHeadersEnabled()) { 1824 return; 1825 } 1826 // center pick up and set out text 1827 String s = padAndTruncate( 1828 tabString(Setup.getPickupCarPrefix(), lineLength / 4 - Setup.getPickupCarPrefix().length() / 2), 1829 lineLength / 2) + 1830 VERTICAL_LINE_CHAR + 1831 tabString(Setup.getDropCarPrefix(), lineLength / 4 - Setup.getDropCarPrefix().length() / 2); 1832 s = padAndTruncate(s, lineLength); 1833 addLine(file, s); 1834 printHorizontalLine(file, 0, lineLength); 1835 1836 s = padAndTruncate(getPickupCarHeader(isManifest, isTwoColumnTrack), lineLength / 2); 1837 s = padAndTruncate(s + VERTICAL_LINE_CHAR + getDropCarHeader(isManifest, isTwoColumnTrack), lineLength); 1838 addLine(file, s); 1839 printHorizontalLine(file, 0, lineLength); 1840 } 1841 1842 public void printPickupCarHeader(PrintWriter file, boolean isManifest, boolean isTwoColumnTrack) { 1843 if (!Setup.isPrintHeadersEnabled()) { 1844 return; 1845 } 1846 printHorizontalLine(file, isManifest); 1847 String s = padAndTruncate(createTabIfNeeded(Setup.getManifestPrefixLength() + 1) + 1848 getPickupCarHeader(isManifest, isTwoColumnTrack), getLineLength(isManifest)); 1849 addLine(file, s); 1850 printHorizontalLine(file, isManifest); 1851 } 1852 1853 public void printDropCarHeader(PrintWriter file, boolean isManifest, boolean isTwoColumnTrack) { 1854 if (!Setup.isPrintHeadersEnabled() || getDropCarHeader(isManifest, isTwoColumnTrack).trim().isEmpty()) { 1855 return; 1856 } 1857 printHorizontalLine(file, isManifest); 1858 String s = padAndTruncate( 1859 createTabIfNeeded(Setup.getManifestPrefixLength() + 1) + getDropCarHeader(isManifest, isTwoColumnTrack), 1860 getLineLength(isManifest)); 1861 addLine(file, s); 1862 printHorizontalLine(file, isManifest); 1863 } 1864 1865 public void printLocalCarMoveHeader(PrintWriter file, boolean isManifest) { 1866 if (!Setup.isPrintHeadersEnabled()) { 1867 return; 1868 } 1869 printHorizontalLine(file, isManifest); 1870 String s = padAndTruncate( 1871 createTabIfNeeded(Setup.getManifestPrefixLength() + 1) + getLocalMoveHeader(isManifest), 1872 getLineLength(isManifest)); 1873 addLine(file, s); 1874 printHorizontalLine(file, isManifest); 1875 } 1876 1877 public String getPickupEngineHeader() { 1878 return getHeader(Setup.getPickupEngineMessageFormat(), PICKUP, !LOCAL, ENGINE); 1879 } 1880 1881 public String getDropEngineHeader() { 1882 return getHeader(Setup.getDropEngineMessageFormat(), !PICKUP, !LOCAL, ENGINE); 1883 } 1884 1885 public String getPickupCarHeader(boolean isManifest, boolean isTwoColumnTrack) { 1886 if (isManifest && !isTwoColumnTrack) { 1887 return getHeader(Setup.getPickupManifestMessageFormat(), PICKUP, !LOCAL, !ENGINE); 1888 } else if (!isManifest && !isTwoColumnTrack) { 1889 return getHeader(Setup.getPickupSwitchListMessageFormat(), PICKUP, !LOCAL, !ENGINE); 1890 } else if (isManifest && isTwoColumnTrack) { 1891 return getHeader(Setup.getPickupTwoColumnByTrackManifestMessageFormat(), PICKUP, !LOCAL, !ENGINE); 1892 } else { 1893 return getHeader(Setup.getPickupTwoColumnByTrackSwitchListMessageFormat(), PICKUP, !LOCAL, !ENGINE); 1894 } 1895 } 1896 1897 public String getDropCarHeader(boolean isManifest, boolean isTwoColumnTrack) { 1898 if (isManifest && !isTwoColumnTrack) { 1899 return getHeader(Setup.getDropManifestMessageFormat(), !PICKUP, !LOCAL, !ENGINE); 1900 } else if (!isManifest && !isTwoColumnTrack) { 1901 return getHeader(Setup.getDropSwitchListMessageFormat(), !PICKUP, !LOCAL, !ENGINE); 1902 } else if (isManifest && isTwoColumnTrack) { 1903 return getHeader(Setup.getDropTwoColumnByTrackManifestMessageFormat(), !PICKUP, !LOCAL, !ENGINE); 1904 } else { 1905 return getHeader(Setup.getDropTwoColumnByTrackSwitchListMessageFormat(), !PICKUP, !LOCAL, !ENGINE); 1906 } 1907 } 1908 1909 public String getLocalMoveHeader(boolean isManifest) { 1910 if (isManifest) { 1911 return getHeader(Setup.getLocalManifestMessageFormat(), !PICKUP, LOCAL, !ENGINE); 1912 } else { 1913 return getHeader(Setup.getLocalSwitchListMessageFormat(), !PICKUP, LOCAL, !ENGINE); 1914 } 1915 } 1916 1917 private String getHeader(String[] format, boolean isPickup, boolean isLocal, boolean isEngine) { 1918 StringBuffer buf = new StringBuffer(); 1919 for (String attribute : format) { 1920 if (attribute.equals(Setup.BLANK)) { 1921 continue; 1922 } 1923 if (attribute.equals(Setup.ROAD)) { 1924 buf.append(padAndTruncateIfNeeded(TrainManifestHeaderText.getStringHeader_Road(), 1925 InstanceManager.getDefault(CarRoads.class).getMaxNameLength()) + SPACE); 1926 } else if (attribute.equals(Setup.NUMBER) && !isEngine) { 1927 buf.append(padAndTruncateIfNeeded(TrainManifestHeaderText.getStringHeader_Number(), 1928 Control.max_len_string_print_road_number) + SPACE); 1929 } else if (attribute.equals(Setup.NUMBER) && isEngine) { 1930 buf.append(padAndTruncateIfNeeded(TrainManifestHeaderText.getStringHeader_EngineNumber(), 1931 Control.max_len_string_print_road_number) + SPACE); 1932 } else if (attribute.equals(Setup.TYPE)) { 1933 buf.append(padAndTruncateIfNeeded(TrainManifestHeaderText.getStringHeader_Type(), 1934 InstanceManager.getDefault(CarTypes.class).getMaxNameLength()) + SPACE); 1935 } else if (attribute.equals(Setup.MODEL)) { 1936 buf.append(padAndTruncateIfNeeded(TrainManifestHeaderText.getStringHeader_Model(), 1937 InstanceManager.getDefault(EngineModels.class).getMaxNameLength()) + SPACE); 1938 } else if (attribute.equals(Setup.HP)) { 1939 buf.append(padAndTruncateIfNeeded(TrainManifestHeaderText.getStringHeader_Hp(), 1940 5) + SPACE); 1941 } else if (attribute.equals(Setup.CONSIST)) { 1942 buf.append(padAndTruncateIfNeeded(TrainManifestHeaderText.getStringHeader_Consist(), 1943 InstanceManager.getDefault(ConsistManager.class).getMaxNameLength()) + SPACE); 1944 } else if (attribute.equals(Setup.DCC_ADDRESS)) { 1945 buf.append(padAndTruncateIfNeeded(TrainManifestHeaderText.getStringHeader_DCC_Address(), 1946 TrainManifestHeaderText.getStringHeader_DCC_Address().length()) + SPACE); 1947 } else if (attribute.equals(Setup.KERNEL)) { 1948 buf.append(padAndTruncateIfNeeded(TrainManifestHeaderText.getStringHeader_Kernel(), 1949 InstanceManager.getDefault(KernelManager.class).getMaxNameLength()) + SPACE); 1950 } else if (attribute.equals(Setup.KERNEL_SIZE)) { 1951 buf.append(" "); // assume kernel size is 99 or less 1952 } else if (attribute.equals(Setup.LOAD)) { 1953 buf.append(padAndTruncateIfNeeded(TrainManifestHeaderText.getStringHeader_Load(), 1954 InstanceManager.getDefault(CarLoads.class).getMaxNameLength()) + SPACE); 1955 } else if (attribute.equals(Setup.LOAD_TYPE)) { 1956 buf.append(padAndTruncateIfNeeded(TrainManifestHeaderText.getStringHeader_Load_Type(), 1957 TrainManifestHeaderText.getStringHeader_Load_Type().length()) + SPACE); 1958 } else if (attribute.equals(Setup.COLOR)) { 1959 buf.append(padAndTruncateIfNeeded(TrainManifestHeaderText.getStringHeader_Color(), 1960 InstanceManager.getDefault(CarColors.class).getMaxNameLength()) + SPACE); 1961 } else if (attribute.equals(Setup.OWNER)) { 1962 buf.append(padAndTruncateIfNeeded(TrainManifestHeaderText.getStringHeader_Owner(), 1963 InstanceManager.getDefault(CarOwners.class).getMaxNameLength()) + SPACE); 1964 } else if (attribute.equals(Setup.LENGTH)) { 1965 buf.append(padAndTruncateIfNeeded(TrainManifestHeaderText.getStringHeader_Length(), 1966 InstanceManager.getDefault(CarLengths.class).getMaxNameLength()) + SPACE); 1967 } else if (attribute.equals(Setup.WEIGHT)) { 1968 buf.append(padAndTruncateIfNeeded(TrainManifestHeaderText.getStringHeader_Weight(), 1969 Control.max_len_string_weight_name) + SPACE); 1970 } else if (attribute.equals(Setup.TRACK)) { 1971 buf.append(padAndTruncateIfNeeded(TrainManifestHeaderText.getStringHeader_Track(), 1972 locationManager.getMaxTrackNameLength()) + SPACE); 1973 } else if (attribute.equals(Setup.LOCATION) && (isPickup || isLocal)) { 1974 buf.append(padAndTruncateIfNeeded(TrainManifestHeaderText.getStringHeader_Location(), 1975 locationManager.getMaxTrackNameLength()) + SPACE); 1976 } else if (attribute.equals(Setup.LOCATION) && !isPickup) { 1977 buf.append(padAndTruncateIfNeeded(TrainManifestHeaderText.getStringHeader_Location(), 1978 locationManager.getMaxLocationNameLength()) + SPACE); 1979 } else if (attribute.equals(Setup.DESTINATION) && !isPickup) { 1980 buf.append(padAndTruncateIfNeeded(TrainManifestHeaderText.getStringHeader_Destination(), 1981 locationManager.getMaxTrackNameLength()) + SPACE); 1982 } else if (attribute.equals(Setup.DESTINATION) && isPickup) { 1983 buf.append(padAndTruncateIfNeeded(TrainManifestHeaderText.getStringHeader_Destination(), 1984 locationManager.getMaxLocationNameLength()) + SPACE); 1985 } else if (attribute.equals(Setup.DEST_TRACK)) { 1986 buf.append(padAndTruncateIfNeeded(TrainManifestHeaderText.getStringHeader_Dest_Track(), 1987 locationManager.getMaxLocationAndTrackNameLength() + 2) + SPACE); 1988 } else if (attribute.equals(Setup.FINAL_DEST)) { 1989 buf.append(padAndTruncateIfNeeded(TrainManifestHeaderText.getStringHeader_Final_Dest(), 1990 locationManager.getMaxLocationNameLength()) + SPACE); 1991 } else if (attribute.equals(Setup.FINAL_DEST_TRACK)) { 1992 buf.append(padAndTruncateIfNeeded(TrainManifestHeaderText.getStringHeader_Final_Dest_Track(), 1993 locationManager.getMaxLocationAndTrackNameLength() + 2) + SPACE); 1994 } else if (attribute.equals(Setup.HAZARDOUS)) { 1995 buf.append(padAndTruncateIfNeeded(TrainManifestHeaderText.getStringHeader_Hazardous(), 1996 Setup.getHazardousMsg().length()) + SPACE); 1997 } else if (attribute.equals(Setup.RWE)) { 1998 buf.append(padAndTruncateIfNeeded(TrainManifestHeaderText.getStringHeader_RWE(), 1999 locationManager.getMaxLocationAndTrackNameLength()) + SPACE); 2000 } else if (attribute.equals(Setup.COMMENT)) { 2001 buf.append(padAndTruncateIfNeeded(TrainManifestHeaderText.getStringHeader_Comment(), 2002 isEngine ? engineManager.getMaxCommentLength() : carManager.getMaxCommentLength()) + SPACE); 2003 } else if (attribute.equals(Setup.DROP_COMMENT)) { 2004 buf.append(padAndTruncateIfNeeded(TrainManifestHeaderText.getStringHeader_Drop_Comment(), 2005 InstanceManager.getDefault(CarLoads.class).getMaxLoadCommentLength()) + SPACE); 2006 } else if (attribute.equals(Setup.PICKUP_COMMENT)) { 2007 buf.append(padAndTruncateIfNeeded(TrainManifestHeaderText.getStringHeader_Pickup_Comment(), 2008 InstanceManager.getDefault(CarLoads.class).getMaxLoadCommentLength()) + SPACE); 2009 } else if (attribute.equals(Setup.DIVISION)) { 2010 buf.append(padAndTruncateIfNeeded(TrainManifestHeaderText.getStringHeader_Division(), 2011 InstanceManager.getDefault(DivisionManager.class).getMaxDivisionNameLength()) + SPACE); 2012 } else if (attribute.equals(Setup.TAB)) { 2013 buf.append(createTabIfNeeded(Setup.getTab1Length())); 2014 } else if (attribute.equals(Setup.TAB2)) { 2015 buf.append(createTabIfNeeded(Setup.getTab2Length())); 2016 } else if (attribute.equals(Setup.TAB3)) { 2017 buf.append(createTabIfNeeded(Setup.getTab3Length())); 2018 } else { 2019 buf.append(attribute + SPACE); 2020 } 2021 } 2022 return buf.toString().stripTrailing(); 2023 } 2024 2025 protected void printTrackNameHeader(PrintWriter file, String trackName, boolean isManifest) { 2026 printHorizontalLine(file, isManifest); 2027 int lineLength = getLineLength(isManifest); 2028 String s = padAndTruncate(tabString(trackName.trim(), lineLength / 4 - trackName.trim().length() / 2), 2029 lineLength / 2) + 2030 VERTICAL_LINE_CHAR + 2031 tabString(trackName.trim(), lineLength / 4 - trackName.trim().length() / 2); 2032 s = padAndTruncate(s, lineLength); 2033 addLine(file, s); 2034 printHorizontalLine(file, isManifest); 2035 } 2036 2037 /** 2038 * Prints a line across the entire page. 2039 * 2040 * @param file The File to print to. 2041 * @param isManifest True if manifest, false if switch list. 2042 */ 2043 public void printHorizontalLine(PrintWriter file, boolean isManifest) { 2044 printHorizontalLine(file, 0, getLineLength(isManifest)); 2045 } 2046 2047 public void printHorizontalLine(PrintWriter file, int start, int end) { 2048 StringBuffer sb = new StringBuffer(); 2049 while (start-- > 0) { 2050 sb.append(SPACE); 2051 } 2052 while (end-- > 0) { 2053 sb.append(HORIZONTAL_LINE_CHAR); 2054 } 2055 addLine(file, sb.toString()); 2056 } 2057 2058 public static String getISO8601Date(boolean isModelYear) { 2059 Calendar calendar = Calendar.getInstance(); 2060 // use the JMRI Timebase (which may be a fast clock). 2061 calendar.setTime(jmri.InstanceManager.getDefault(jmri.Timebase.class).getTime()); 2062 if (isModelYear && !Setup.getYearModeled().isEmpty()) { 2063 try { 2064 calendar.set(Calendar.YEAR, Integer.parseInt(Setup.getYearModeled().trim())); 2065 } catch (NumberFormatException e) { 2066 return Setup.getYearModeled(); 2067 } 2068 } 2069 return (new StdDateFormat()).format(calendar.getTime()); 2070 } 2071 2072 public static String getDate(Date date) { 2073 SimpleDateFormat format = new SimpleDateFormat("M/dd/yyyy HH:mm"); // NOI18N 2074 if (Setup.is12hrFormatEnabled()) { 2075 format = new SimpleDateFormat("M/dd/yyyy hh:mm a"); // NOI18N 2076 } 2077 return format.format(date); 2078 } 2079 2080 public static String getDate(boolean isModelYear) { 2081 Calendar calendar = Calendar.getInstance(); 2082 // use the JMRI Timebase (which may be a fast clock). 2083 calendar.setTime(jmri.InstanceManager.getDefault(jmri.Timebase.class).getTime()); 2084 if (isModelYear && !Setup.getYearModeled().equals(Setup.NONE)) { 2085 try { 2086 calendar.set(Calendar.YEAR, Integer.parseInt(Setup.getYearModeled().trim())); 2087 } catch (NumberFormatException e) { 2088 return Setup.getYearModeled(); 2089 } 2090 } 2091 return TrainCommon.getDate(calendar.getTime()); 2092 } 2093 2094 public static Date convertStringToDate(String date) { 2095 if (!date.isBlank()) { 2096 // create a date object from the string. 2097 try { 2098 // try MM/dd/yyyy HH:mm:ss. 2099 SimpleDateFormat formatter = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss"); // NOI18N 2100 return formatter.parse(date); 2101 } catch (java.text.ParseException pe1) { 2102 // try the old 12 hour format (no seconds). 2103 try { 2104 SimpleDateFormat formatter = new SimpleDateFormat("MM/dd/yyyy hh:mmaa"); // NOI18N 2105 return formatter.parse(date); 2106 } catch (java.text.ParseException pe2) { 2107 try { 2108 // try 24hour clock. 2109 SimpleDateFormat formatter = new SimpleDateFormat("MM/dd/yyyy HH:mm"); // NOI18N 2110 return formatter.parse(date); 2111 } catch (java.text.ParseException pe3) { 2112 log.debug("Not able to parse date: {}", date); 2113 } 2114 } 2115 } 2116 } 2117 return null; // there was no date specified. 2118 } 2119 2120 /* 2121 * Converts String time DAYS:HH:MM and DAYS:HH:MM AM/PM to minutes from 2122 * midnight. 2123 */ 2124 protected int convertStringTime(String time) { 2125 int minutes = 0; 2126 boolean hrFormat = false; 2127 String[] splitTimePM = time.split(" "); 2128 if (splitTimePM.length > 1) { 2129 hrFormat = true; 2130 if (splitTimePM[1].equals(Bundle.getMessage("PM"))) { 2131 minutes = 12 * 60; 2132 } 2133 } 2134 String[] splitTime = splitTimePM[0].split(":"); 2135 2136 if (splitTime.length > 2) { 2137 // days:hrs:minutes 2138 if (hrFormat && splitTime[1].equals("12")) { 2139 splitTime[1] = "00"; 2140 } 2141 minutes += 24 * 60 * Integer.parseInt(splitTime[0]); 2142 minutes += 60 * Integer.parseInt(splitTime[1]); 2143 minutes += Integer.parseInt(splitTime[2]); 2144 } else { 2145 // hrs:minutes 2146 if (hrFormat && splitTime[0].equals("12")) { 2147 splitTime[0] = "00"; 2148 } 2149 minutes += 60 * Integer.parseInt(splitTime[0]); 2150 minutes += Integer.parseInt(splitTime[1]); 2151 } 2152 log.debug("convert time {} to minutes {}", time, minutes); 2153 return minutes; 2154 } 2155 2156 /** 2157 * Pads out a string by adding spaces to the end of the string, and will 2158 * remove characters from the end of the string if the string exceeds the 2159 * field size. 2160 * 2161 * @param s The string to pad. 2162 * @param fieldSize The maximum length of the string. 2163 * @return A String the specified length 2164 */ 2165 public static String padAndTruncateIfNeeded(String s, int fieldSize) { 2166 if (Setup.isTabEnabled()) { 2167 return padAndTruncate(s, fieldSize); 2168 } 2169 return s; 2170 } 2171 2172 public static String padAndTruncate(String s, int fieldSize) { 2173 s = padString(s, fieldSize); 2174 if (s.length() > fieldSize) { 2175 s = s.substring(0, fieldSize); 2176 } 2177 return s; 2178 } 2179 2180 /** 2181 * Adjusts string to be a certain number of characters by adding spaces to 2182 * the end of the string. 2183 * 2184 * @param s The string to pad 2185 * @param fieldSize The fixed length of the string. 2186 * @return A String the specified length 2187 */ 2188 public static String padString(String s, int fieldSize) { 2189 StringBuffer buf = new StringBuffer(s); 2190 while (buf.length() < fieldSize) { 2191 buf.append(SPACE); 2192 } 2193 return buf.toString(); 2194 } 2195 2196 /** 2197 * Creates a String of spaces to create a tab for text. Tabs must be 2198 * enabled. Setup.isTabEnabled() 2199 * 2200 * @param tabSize the length of tab 2201 * @return tab 2202 */ 2203 public static String createTabIfNeeded(int tabSize) { 2204 if (Setup.isTabEnabled()) { 2205 return tabString("", tabSize); 2206 } 2207 return ""; 2208 } 2209 2210 protected static String tabString(String s, int tabSize) { 2211 StringBuffer buf = new StringBuffer(); 2212 // TODO this doesn't consider the length of s string. 2213 while (buf.length() < tabSize) { 2214 buf.append(SPACE); 2215 } 2216 buf.append(s); 2217 return buf.toString(); 2218 } 2219 2220 /** 2221 * Returns the line length for manifest or switch list printout. Always an 2222 * even number. 2223 * 2224 * @param isManifest True if manifest. 2225 * @return line length for manifest or switch list. 2226 */ 2227 public static int getLineLength(boolean isManifest) { 2228 return getLineLength(isManifest ? Setup.getManifestOrientation() : Setup.getSwitchListOrientation(), 2229 Setup.getFontName(), Font.PLAIN, Setup.getManifestFontSize()); 2230 } 2231 2232 public static int getManifestHeaderLineLength() { 2233 return getLineLength(Setup.getManifestOrientation(), "SansSerif", Font.ITALIC, Setup.getManifestFontSize()); 2234 } 2235 2236 private static int getLineLength(String orientation, String fontName, int fontStyle, int fontSize) { 2237 Font font = new Font(fontName, fontStyle, fontSize); // NOI18N 2238 JLabel label = new JLabel(); 2239 FontMetrics metrics = label.getFontMetrics(font); 2240 int charwidth = metrics.charWidth('m'); 2241 if (charwidth == 0) { 2242 log.error("Line length charater width equal to zero. font size: {}, fontName: {}", fontSize, fontName); 2243 charwidth = fontSize / 2; // create a reasonable character width 2244 } 2245 // compute lines and columns within margins 2246 int charLength = getPageSize(orientation).width / charwidth; 2247 if (charLength % 2 != 0) { 2248 charLength--; // make it even 2249 } 2250 return charLength; 2251 } 2252 2253 private boolean checkStringLength(String string, boolean isManifest) { 2254 return checkStringLength(string, isManifest ? Setup.getManifestOrientation() : Setup.getSwitchListOrientation(), 2255 Setup.getFontName(), Setup.getManifestFontSize()); 2256 } 2257 2258 /** 2259 * Checks to see if the string fits on the page. 2260 * 2261 * @return false if string length is longer than page width. 2262 */ 2263 private boolean checkStringLength(String string, String orientation, String fontName, int fontSize) { 2264 // ignore text color controls when determining line length 2265 if (string.startsWith(TEXT_COLOR_START) && string.contains(TEXT_COLOR_DONE)) { 2266 string = string.substring(string.indexOf(TEXT_COLOR_DONE) + 2); 2267 } 2268 if (string.contains(TEXT_COLOR_END)) { 2269 string = string.substring(0, string.indexOf(TEXT_COLOR_END)); 2270 } 2271 Font font = new Font(fontName, Font.PLAIN, fontSize); // NOI18N 2272 JLabel label = new JLabel(); 2273 FontMetrics metrics = label.getFontMetrics(font); 2274 int stringWidth = metrics.stringWidth(string); 2275 return stringWidth <= getPageSize(orientation).width; 2276 } 2277 2278 protected static final Dimension PAPER_MARGINS = new Dimension(84, 72); 2279 2280 protected static Dimension getPageSize(String orientation) { 2281 // page size has been adjusted to account for margins of .5 2282 // Dimension(84, 72) 2283 Dimension pagesize = new Dimension(523, 720); // Portrait 8.5 x 11 2284 // landscape has .65 margins 2285 if (orientation.equals(Setup.LANDSCAPE)) { 2286 pagesize = new Dimension(702, 523); // 11 x 8.5 2287 } 2288 if (orientation.equals(Setup.HALFPAGE)) { 2289 pagesize = new Dimension(261, 720); // 4.25 x 11 2290 } 2291 if (orientation.equals(Setup.HANDHELD)) { 2292 pagesize = new Dimension(206, 720); // 3.25 x 11 2293 } 2294 return pagesize; 2295 } 2296 2297 /** 2298 * Produces a string using commas and spaces between the strings provided in 2299 * the array. Does not check for embedded commas in the string array. 2300 * 2301 * @param array The string array to be formated. 2302 * @return formated string using commas and spaces 2303 */ 2304 public static String formatStringToCommaSeparated(String[] array) { 2305 StringBuffer sbuf = new StringBuffer(""); 2306 for (String s : array) { 2307 if (s != null) { 2308 sbuf = sbuf.append(s + "," + SPACE); 2309 } 2310 } 2311 if (sbuf.length() > 2) { 2312 sbuf.setLength(sbuf.length() - 2); // remove trailing separators 2313 } 2314 return sbuf.toString(); 2315 } 2316 2317 private void addLine(PrintWriter file, StringBuffer buf, Color color) { 2318 String s = buf.toString(); 2319 if (!s.trim().isEmpty()) { 2320 addLine(file, formatColorString(s, color)); 2321 } 2322 } 2323 2324 /** 2325 * Adds HTML like color text control characters around a string. Note that 2326 * black is the standard text color, and if black is requested no control 2327 * characters are added. 2328 * 2329 * @param text the text to be modified 2330 * @param color the color the text is to be printed 2331 * @return formated text with color modifiers 2332 */ 2333 public static String formatColorString(String text, Color color) { 2334 String s = text; 2335 if (!color.equals(Color.black)) { 2336 s = TEXT_COLOR_START + ColorUtil.colorToColorName(color) + TEXT_COLOR_DONE + text + TEXT_COLOR_END; 2337 } 2338 return s; 2339 } 2340 2341 /** 2342 * Removes the color text control characters around the desired string 2343 * 2344 * @param string the string with control characters 2345 * @return pure text 2346 */ 2347 public static String getTextColorString(String string) { 2348 String text = string; 2349 if (string.contains(TEXT_COLOR_START)) { 2350 text = string.substring(0, string.indexOf(TEXT_COLOR_START)) + 2351 string.substring(string.indexOf(TEXT_COLOR_DONE) + 2); 2352 } 2353 if (text.contains(TEXT_COLOR_END)) { 2354 text = text.substring(0, text.indexOf(TEXT_COLOR_END)) + 2355 string.substring(string.indexOf(TEXT_COLOR_END) + TEXT_COLOR_END.length()); 2356 } 2357 return text; 2358 } 2359 2360 public static Color getTextColor(String string) { 2361 Color color = Color.black; 2362 if (string.contains(TEXT_COLOR_START)) { 2363 String c = string.substring(string.indexOf(TEXT_COLOR_START) + TEXT_COLOR_START.length()); 2364 c = c.substring(0, c.indexOf("\"")); 2365 color = ColorUtil.stringToColor(c); 2366 } 2367 return color; 2368 } 2369 2370 public static String getTextColorName(String string) { 2371 return ColorUtil.colorToColorName(getTextColor(string)); 2372 } 2373 2374 private static final Logger log = LoggerFactory.getLogger(TrainCommon.class); 2375}