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