001package jmri.jmrit.operations.locations.tools;
002
003import java.awt.*;
004import java.io.IOException;
005import java.text.MessageFormat;
006import java.util.List;
007
008import javax.swing.*;
009
010import org.slf4j.Logger;
011import org.slf4j.LoggerFactory;
012
013import jmri.InstanceManager;
014import jmri.jmrit.operations.OperationsFrame;
015import jmri.jmrit.operations.locations.*;
016import jmri.jmrit.operations.locations.schedules.*;
017import jmri.jmrit.operations.rollingstock.cars.*;
018import jmri.jmrit.operations.rollingstock.engines.EngineTypes;
019import jmri.jmrit.operations.routes.Route;
020import jmri.jmrit.operations.routes.RouteManager;
021import jmri.jmrit.operations.setup.Control;
022import jmri.jmrit.operations.setup.Setup;
023import jmri.jmrit.operations.trains.*;
024import jmri.jmrit.operations.trains.trainbuilder.TrainCommon;
025import jmri.util.davidflanagan.HardcopyWriter;
026
027/**
028 * Frame to print a summary of the Location Roster contents
029 * <p>
030 * This uses the older style printing, for compatibility with Java 1.1.8 in
031 * Macintosh MRJ
032 *
033 * @author Bob Jacobsen Copyright (C) 2003
034 * @author Dennis Miller Copyright (C) 2005
035 * @author Daniel Boudreau Copyright (C) 2008, 2011, 2012, 2014, 2022, 2023
036 */
037public class PrintLocationsFrame extends OperationsFrame {
038
039    static final String FORM_FEED = "\f"; // NOI18N
040    static final String TAB = "\t"; // NOI18N
041    static final int TAB_LENGTH = 10;
042    static final String SPACES_2 = "  ";
043    static final String SPACES_3 = "   ";
044    static final String SPACES_4 = "    ";
045
046    static final int MAX_NAME_LENGTH = Control.max_len_string_location_name;
047
048    JCheckBox printLocations = new JCheckBox(Bundle.getMessage("PrintLocations"));
049    JCheckBox printSchedules = new JCheckBox(Bundle.getMessage("PrintSchedules"));
050    JCheckBox printComments = new JCheckBox(Bundle.getMessage("PrintComments"));
051    JCheckBox printDetails = new JCheckBox(Bundle.getMessage("PrintDetails"));
052    JCheckBox printAnalysis = new JCheckBox(Bundle.getMessage("PrintAnalysis"));
053    JCheckBox printErrorAnalysis = new JCheckBox(Bundle.getMessage("PrintErrorAnalysis"));
054
055    JButton okayButton = new JButton(Bundle.getMessage("ButtonOK"));
056
057    LocationManager lmanager = InstanceManager.getDefault(LocationManager.class);
058    CarTypes cts = InstanceManager.getDefault(CarTypes.class);
059    CarLoads cls = InstanceManager.getDefault(CarLoads.class);
060    CarRoads crs = InstanceManager.getDefault(CarRoads.class);
061
062    boolean _isPreview;
063    Location _location;
064
065    private int charactersPerLine = 70;
066
067    HardcopyWriter writer;
068
069    public PrintLocationsFrame(boolean isPreview, Location location) {
070        super();
071        _isPreview = isPreview;
072        _location = location;
073
074        // create panel
075        JPanel pPanel = new JPanel();
076        pPanel.setLayout(new GridBagLayout());
077        pPanel.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("PrintOptions")));
078        addItemLeft(pPanel, printLocations, 0, 0);
079        addItemLeft(pPanel, printSchedules, 0, 3);
080        addItemLeft(pPanel, printComments, 0, 5);
081        addItemLeft(pPanel, printDetails, 0, 7);
082        addItemLeft(pPanel, printAnalysis, 0, 9);
083        addItemLeft(pPanel, printErrorAnalysis, 0, 11);
084
085        // set defaults
086        printLocations.setSelected(true);
087        printSchedules.setSelected(false);
088        printComments.setSelected(false);
089        printDetails.setSelected(false);
090        printAnalysis.setSelected(false);
091        printErrorAnalysis.setSelected(false);
092
093        // add tool tips
094        JPanel pButtons = new JPanel();
095        pButtons.setLayout(new GridBagLayout());
096        pButtons.add(okayButton);
097        addButtonAction(okayButton);
098
099        getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
100        getContentPane().add(pPanel);
101        getContentPane().add(pButtons);
102        setPreferredSize(null);
103        if (_isPreview) {
104            setTitle(Bundle.getMessage("MenuItemPreview"));
105        } else {
106            setTitle(Bundle.getMessage("MenuItemPrint"));
107        }
108        initMinimumSize(new Dimension(Control.panelWidth300, Control.panelHeight250));
109    }
110
111    @Override
112    public void buttonActionPerformed(java.awt.event.ActionEvent ae) {
113        setVisible(false);
114        printLocations();
115    }
116
117    private void printLocations() {
118        // prevent NPE on close
119        if (!printLocations.isSelected() &&
120                !printSchedules.isSelected() &&
121                !printComments.isSelected() &&
122                !printDetails.isSelected() &&
123                !printAnalysis.isSelected() &&
124                !printErrorAnalysis.isSelected()) {
125            return;
126        }
127        // obtain a HardcopyWriter
128        String title = Bundle.getMessage("TitleLocationsTable");
129        if (_location != null) {
130            title = _location.getName();
131        }
132        try (HardcopyWriter writer =
133                new HardcopyWriter(new Frame(), title, Control.reportFontSize, .5, .5, .5, .5, _isPreview)) {
134
135            this.writer = writer;
136
137            charactersPerLine = writer.getCharactersPerLine();
138
139            // print locations?
140            if (printLocations.isSelected()) {
141                printLocationsSelected();
142            }
143            // print schedules?
144            if (printSchedules.isSelected()) {
145                printSchedulesSelected();
146            }
147            if (printComments.isSelected()) {
148                printCommentsSelected();
149            }
150            // print detailed report?
151            if (printDetails.isSelected()) {
152                printDetailsSelected();
153            }
154            // print analysis?
155            if (printAnalysis.isSelected()) {
156                printAnalysisSelected();
157            }
158            if (printErrorAnalysis.isSelected()) {
159                printErrorAnalysisSelected();
160            }
161        } catch (HardcopyWriter.PrintCanceledException ex) {
162            log.debug("Print cancelled");
163        } catch (IOException we) {
164            log.error("Error printing PrintLocationAction: {}", we.getLocalizedMessage());
165        }
166    }
167
168    // Loop through the Roster, printing as needed
169    private void printLocationsSelected() throws IOException {
170        List<Location> locations = lmanager.getLocationsByNameList();
171        int totalLength = 0;
172        int usedLength = 0;
173        int numberRS = 0;
174        int numberCars = 0;
175        int numberEngines = 0;
176        // header
177        String s = Bundle.getMessage("Location") +
178                TAB +
179                TAB +
180                TAB +
181                Bundle.getMessage("Length") +
182                " " +
183                Bundle.getMessage("Used") +
184                TAB +
185                Bundle.getMessage("RS") +
186                TAB +
187                Bundle.getMessage("Cars") +
188                TAB +
189                Bundle.getMessage("Engines") +
190                TAB +
191                Bundle.getMessage("Pickups") +
192                " " +
193                Bundle.getMessage("Drop") +
194                NEW_LINE;
195        writer.write(s);
196        for (Location location : locations) {
197            if (_location != null && location != _location) {
198                continue;
199            }
200            // location name, track length, used, number of RS, scheduled pick
201            // ups and drops
202            s = padOutString(location.getName(), MAX_NAME_LENGTH) +
203                    TAB +
204                    "  " +
205                    Integer.toString(location.getLength()) +
206                    TAB +
207                    Integer.toString(location.getUsedLength()) +
208                    TAB +
209                    Integer.toString(location.getNumberRS()) +
210                    TAB +
211                    Integer.toString(location.getNumberCars()) +
212                    TAB +
213                    Integer.toString(location.getNumberEngines()) +
214                    TAB +
215                    Integer.toString(location.getPickupRS()) +
216                    TAB +
217                    Integer.toString(location.getDropRS()) +
218                    NEW_LINE;
219            writer.write(s);
220
221            if (location.getDivision() != null) {
222                writer.write(SPACES_3 + Bundle.getMessage("Division") + ": " + location.getDivisionName() + NEW_LINE);
223            }
224
225            totalLength += location.getLength();
226            usedLength += location.getUsedLength();
227            numberRS += location.getNumberRS();
228
229            List<Track> yards = location.getTracksByNameList(Track.YARD);
230            if (yards.size() > 0) {
231                // header
232                writer.write(SPACES_3 + Bundle.getMessage("YardName") + NEW_LINE);
233                for (Track yard : yards) {
234                    writer.write(getTrackString(yard));
235                    numberCars += yard.getNumberCars();
236                    numberEngines += yard.getNumberEngines();
237                }
238            }
239
240            List<Track> spurs = location.getTracksByNameList(Track.SPUR);
241            if (spurs.size() > 0) {
242                // header
243                writer.write(SPACES_3 + Bundle.getMessage("SpurName") + NEW_LINE);
244                for (Track spur : spurs) {
245                    writer.write(getTrackString(spur));
246                    numberCars += spur.getNumberCars();
247                    numberEngines += spur.getNumberEngines();
248                }
249            }
250
251            List<Track> interchanges = location.getTracksByNameList(Track.INTERCHANGE);
252            if (interchanges.size() > 0) {
253                // header
254                writer.write(SPACES_3 + Bundle.getMessage("InterchangeName") + NEW_LINE);
255                for (Track interchange : interchanges) {
256                    writer.write(getTrackString(interchange));
257                    numberCars += interchange.getNumberCars();
258                    numberEngines += interchange.getNumberEngines();
259                }
260            }
261
262            List<Track> stagingTracks = location.getTracksByNameList(Track.STAGING);
263            if (stagingTracks.size() > 0) {
264                // header
265                writer.write(SPACES_3 + Bundle.getMessage("StagingName") + NEW_LINE);
266                for (Track staging : stagingTracks) {
267                    writer.write(getTrackString(staging));
268                    numberCars += staging.getNumberCars();
269                    numberEngines += staging.getNumberEngines();
270                }
271            }
272            writer.write(NEW_LINE);
273        }
274
275        // summary
276        s = MessageFormat
277                .format(Bundle.getMessage("TotalLengthMsg"),
278                        new Object[]{Integer.toString(totalLength), Integer.toString(usedLength),
279                                totalLength > 0 ? Integer.toString(usedLength * 100 / totalLength) : 0}) +
280                NEW_LINE;
281        writer.write(s);
282        s = MessageFormat
283                .format(Bundle.getMessage("TotalRollingMsg"),
284                        new Object[]{Integer.toString(numberRS), Integer.toString(numberCars),
285                                Integer.toString(numberEngines)}) +
286                NEW_LINE;
287        writer.write(s);
288        // are there trains en route, then some cars and engines not counted!
289        if (numberRS != numberCars + numberEngines) {
290            s = Bundle.getMessage("NoteRSMsg", Integer.toString(numberRS - (numberCars + numberEngines))) + NEW_LINE;
291            writer.write(s);
292        }
293        if (printSchedules.isSelected() ||
294                printComments.isSelected() ||
295                printDetails.isSelected() ||
296                printAnalysis.isSelected() ||
297                printErrorAnalysis.isSelected()) {
298            writer.write(FORM_FEED);
299        }
300    }
301
302    private void printSchedulesSelected() throws IOException {
303        List<Location> locations = lmanager.getLocationsByNameList();
304        String s = padOutString(Bundle.getMessage("Schedules"), MAX_NAME_LENGTH) +
305                " " +
306                Bundle.getMessage("Location") +
307                " - " +
308                Bundle.getMessage("SpurName") +
309                NEW_LINE;
310        writer.write(s);
311        List<Schedule> schedules = InstanceManager.getDefault(ScheduleManager.class).getSchedulesByNameList();
312        for (Schedule schedule : schedules) {
313            for (Location location : locations) {
314                if (_location != null && location != _location) {
315                    continue;
316                }
317                List<Track> spurs = location.getTracksByNameList(Track.SPUR);
318                for (Track spur : spurs) {
319                    if (spur.getScheduleId().equals(schedule.getId())) {
320                        // pad out schedule name
321                        s = padOutString(schedule.getName(),
322                                MAX_NAME_LENGTH) + " " + location.getName() + " - " + spur.getName();
323                        String status = spur.checkScheduleValid();
324                        if (!status.equals(Schedule.SCHEDULE_OKAY)) {
325                            StringBuffer buf = new StringBuffer(s);
326                            for (int m = s.length(); m < 63; m++) {
327                                buf.append(" ");
328                            }
329                            s = buf.toString();
330                            if (s.length() > 63) {
331                                s = s.substring(0, 63);
332                            }
333                            s = s + TAB + status;
334                        }
335                        s = s + NEW_LINE;
336                        writer.write(s);
337                        // show the schedule's mode
338                        s = padOutString("", MAX_NAME_LENGTH) +
339                                SPACES_3 +
340                                Bundle.getMessage("ScheduleMode") +
341                                ": " +
342                                spur.getScheduleModeName() +
343                                NEW_LINE;
344                        writer.write(s);
345                        // show alternate track if there's one
346                        if (spur.getAlternateTrack() != null) {
347                            s = padOutString("", MAX_NAME_LENGTH) +
348                                    SPACES_3 +
349                                    Bundle.getMessage("AlternateTrackName", spur.getAlternateTrack().getName()) +
350                                    NEW_LINE;
351                            writer.write(s);
352                        }
353                        // show custom loads from staging if not 100%
354                        if (spur.getReservationFactor() != 100) {
355                            s = padOutString("", MAX_NAME_LENGTH) +
356                                    SPACES_3 +
357                                    Bundle.getMessage("PercentageStaging",
358                                            spur.getReservationFactor()) +
359                                    NEW_LINE;
360                            writer.write(s);
361                        }
362                    }
363                }
364            }
365        }
366        // now show the contents of each schedule
367        for (Schedule schedule : schedules) {
368            writer.write(FORM_FEED);
369            s = schedule.getName() + NEW_LINE;
370            writer.write(s);
371
372            for (ScheduleItem si : schedule.getItemsBySequenceList()) {
373                s = padOutString(Bundle.getMessage("Type"), cts.getMaxNameLength() + 1) +
374                        padOutString(Bundle.getMessage("Receive"), cls.getMaxNameLength() + 1) +
375                        padOutString(Bundle.getMessage("Ship"), cls.getMaxNameLength() + 1) +
376                        padOutString(Bundle.getMessage("Destination"), lmanager.getMaxLocationNameLength() + 1) +
377                        Bundle.getMessage("Track") +
378                        NEW_LINE;
379                writer.write(s);
380                s = padOutString(si.getTypeName(), cts.getMaxNameLength() + 1) +
381                        padOutString(si.getReceiveLoadName(), cls.getMaxNameLength() + 1) +
382                        padOutString(si.getShipLoadName(), cls.getMaxNameLength() + 1) +
383                        padOutString(si.getDestinationName(), lmanager.getMaxLocationNameLength() + 1) +
384                        si.getDestinationTrackName() +
385                        NEW_LINE;
386                writer.write(s);
387
388                s = padOutString("", cts.getMaxNameLength() + 1) +
389                        padOutString(Bundle.getMessage("Random"), Bundle.getMessage("Random").length() + 1) +
390                        padOutString(Bundle.getMessage("Delivery"), Bundle.getMessage("Delivery").length() + 1) +
391                        padOutString(Bundle.getMessage("Road"), crs.getMaxNameLength() + 1) +
392                        padOutString(Bundle.getMessage("Pickup"), Bundle.getMessage("Delivery").length() + 1) +
393                        Bundle.getMessage("Wait") +
394                        NEW_LINE;
395                writer.write(s);
396
397                s = padOutString("", cts.getMaxNameLength() + 1) +
398                        padOutString(si.getRandom(), Bundle.getMessage("Random").length() + 1) +
399                        padOutString(si.getSetoutTrainScheduleName(), Bundle.getMessage("Delivery").length() + 1) +
400                        padOutString(si.getRoadName(), crs.getMaxNameLength() + 1) +
401                        padOutString(si.getPickupTrainScheduleName(), Bundle.getMessage("Delivery").length() + 1) +
402                        si.getWait() +
403                        NEW_LINE;
404                writer.write(s);
405            }
406        }
407        if (printComments.isSelected() ||
408                printDetails.isSelected() ||
409                printAnalysis.isSelected() ||
410                printErrorAnalysis.isSelected()) {
411            writer.write(FORM_FEED);
412        }
413    }
414
415    private void printCommentsSelected() throws IOException {
416        String s = Bundle.getMessage("PrintComments") + NEW_LINE + NEW_LINE;
417        writer.write(s);
418        List<Location> locations = lmanager.getLocationsByNameList();
419        for (Location location : locations) {
420            if (_location != null && location != _location) {
421                continue;
422            }
423            s = location.getName() + NEW_LINE;
424            writer.write(s);
425            s = SPACES_3 + location.getComment() + NEW_LINE;
426            writer.write(s);
427            for (Track track : location.getTracksByNameList(null)) {
428                if (!track.getComment().equals(Track.NONE) ||
429                        !track.getCommentBoth().equals(Track.NONE) ||
430                        !track.getCommentPickup().equals(Track.NONE) ||
431                        !track.getCommentSetout().equals(Track.NONE)) {
432                    s = SPACES_2 + track.getName() + NEW_LINE;
433                    writer.write(s);
434                    if (!track.getComment().equals(Track.NONE)) {
435                        s = SPACES_4 + track.getComment() + NEW_LINE;
436                        writer.write(s);
437                    }
438                    if (!track.getCommentBoth().equals(Track.NONE)) {
439                        s = SPACES_3 + Bundle.getMessage("CommentBoth") + ":" + NEW_LINE;
440                        writer.write(s);
441                        s = SPACES_4 + track.getCommentBoth() + NEW_LINE;
442                        writer.write(s);
443                    }
444                    if (!track.getCommentPickup().equals(Track.NONE)) {
445                        s = SPACES_3 + Bundle.getMessage("CommentPickup") + ":" + NEW_LINE;
446                        writer.write(s);
447                        s = SPACES_4 + track.getCommentPickup() + NEW_LINE;
448                        writer.write(s);
449                    }
450                    if (!track.getCommentSetout().equals(Track.NONE)) {
451                        s = SPACES_3 + Bundle.getMessage("CommentSetout") + ":" + NEW_LINE;
452                        writer.write(s);
453                        s = SPACES_4 + track.getCommentSetout() + NEW_LINE;
454                        writer.write(s);
455                    }
456                }
457            }
458        }
459        if (printDetails.isSelected() || printAnalysis.isSelected() || printErrorAnalysis.isSelected()) {
460            writer.write(FORM_FEED);
461        }
462    }
463
464    private void printDetailsSelected() throws IOException {
465        List<Location> locations = lmanager.getLocationsByNameList();
466        String s = Bundle.getMessage("DetailedReport") + NEW_LINE;
467        writer.write(s);
468        for (Location location : locations) {
469            if (_location != null && location != _location) {
470                continue;
471            }
472            String name = location.getName();
473            // services train direction
474            int dir = location.getTrainDirections();
475            s = NEW_LINE + name + getDirection(dir);
476            writer.write(s);
477
478            // division
479            if (location.getDivision() != null) {
480                s = SPACES_3 + Bundle.getMessage("Division") + ": " + location.getDivisionName() + NEW_LINE;
481                writer.write(s);
482            }
483
484            // services car and engine types
485            s = getLocationTypes(location);
486            writer.write(s);
487
488            List<Track> spurs = location.getTracksByNameList(Track.SPUR);
489            if (spurs.size() > 0) {
490                s = SPACES_3 + Bundle.getMessage("SpurName") + NEW_LINE;
491                writer.write(s);
492                printTrackInfo(location, spurs);
493            }
494
495            List<Track> yards = location.getTracksByNameList(Track.YARD);
496            if (yards.size() > 0) {
497                s = SPACES_3 + Bundle.getMessage("YardName") + NEW_LINE;
498                writer.write(s);
499                printTrackInfo(location, yards);
500            }
501
502            List<Track> interchanges = location.getTracksByNameList(Track.INTERCHANGE);
503            if (interchanges.size() > 0) {
504                s = SPACES_3 + Bundle.getMessage("InterchangeName") + NEW_LINE;
505                writer.write(s);
506                printTrackInfo(location, interchanges);
507            }
508
509            List<Track> staging = location.getTracksByNameList(Track.STAGING);
510            if (staging.size() > 0) {
511                s = SPACES_3 + Bundle.getMessage("StagingName") + NEW_LINE;
512                writer.write(s);
513                printTrackInfo(location, staging);
514            }
515        }
516        if (printAnalysis.isSelected() || printErrorAnalysis.isSelected()) {
517            writer.write(FORM_FEED);
518        }
519    }
520
521    private final boolean showStaging = true;
522
523    private void printAnalysisSelected() throws IOException {
524        CarManager carManager = InstanceManager.getDefault(CarManager.class);
525        List<Location> locations = lmanager.getLocationsByNameList();
526        List<Car> cars = carManager.getByLocationList();
527        String[] carTypes = cts.getNames();
528
529        String s = Bundle.getMessage("TrackAnalysis") + NEW_LINE;
530        writer.write(s);
531
532        // print the car type being analyzed
533        for (String type : carTypes) {
534            // get the total length for a given car type
535            int numberOfCars = 0;
536            int totalTrackLength = 0;
537            for (Car car : cars) {
538                if (car.getTypeName().equals(type) && car.getLocation() != null) {
539                    numberOfCars++;
540                    totalTrackLength += car.getTotalLength();
541                }
542            }
543            writer.write(Bundle.getMessage("NumberTypeLength",
544                    numberOfCars, type, totalTrackLength, Setup.getLengthUnit().toLowerCase()) +
545                    NEW_LINE);
546            // don't bother reporting when the number of cars for a given type
547            // is zero. Round up percentage used by a car type.
548            if (numberOfCars > 0) {
549                // spurs
550                writer.write(SPACES_3 +
551                        Bundle.getMessage("SpurTrackThatAccept", type) +
552                        NEW_LINE);
553                int trackLength = getTrackLengthAcceptType(locations, type, Track.SPUR);
554                if (trackLength > 0) {
555                    writer.write(SPACES_3 +
556                            Bundle.getMessage("TotalLengthSpur", type, trackLength, Setup.getLengthUnit().toLowerCase(),
557                                    Math.ceil((double) 100 * totalTrackLength / trackLength)) +
558                            NEW_LINE);
559                } else {
560                    writer.write(SPACES_3 + Bundle.getMessage("None") + NEW_LINE);
561                }
562                // yards
563                writer.write(SPACES_3 +
564                        Bundle.getMessage("YardTrackThatAccept", type) +
565                        NEW_LINE);
566                trackLength = getTrackLengthAcceptType(locations, type, Track.YARD);
567                if (trackLength > 0) {
568                    writer.write(SPACES_3 +
569                            Bundle.getMessage("TotalLengthYard", type, trackLength, Setup.getLengthUnit().toLowerCase(),
570                                    Math.ceil((double) 100 * totalTrackLength / trackLength)) +
571                            NEW_LINE);
572                } else {
573                    writer.write(SPACES_3 + Bundle.getMessage("None") + NEW_LINE);
574                }
575                // interchanges
576                writer.write(SPACES_3 +
577                        Bundle.getMessage("InterchangesThatAccept", type) +
578                        NEW_LINE);
579                trackLength = getTrackLengthAcceptType(locations, type, Track.INTERCHANGE);
580                if (trackLength > 0) {
581                    writer.write(SPACES_3 +
582                            Bundle.getMessage("TotalLengthInterchange",
583                                    type, trackLength, Setup.getLengthUnit().toLowerCase(),
584                                    Math.ceil((double) 100 * totalTrackLength / trackLength)) +
585                            NEW_LINE);
586                } else {
587                    writer.write(SPACES_3 + Bundle.getMessage("None") + NEW_LINE);
588                }
589                // staging
590                if (showStaging) {
591                    writer.write(SPACES_3 +
592                            Bundle.getMessage("StageTrackThatAccept", type) +
593                            NEW_LINE);
594                    trackLength = getTrackLengthAcceptType(locations, type, Track.STAGING);
595                    if (trackLength > 0) {
596                        writer.write(SPACES_3 +
597                                Bundle.getMessage("TotalLengthStage",
598                                        type, trackLength, Setup.getLengthUnit().toLowerCase(),
599                                        Math.ceil((double) 100 * totalTrackLength / trackLength)) +
600                                NEW_LINE);
601                    } else {
602                        writer.write(SPACES_3 + Bundle.getMessage("None") + NEW_LINE);
603                    }
604                }
605            }
606        }
607        if (printErrorAnalysis.isSelected()) {
608            writer.write(FORM_FEED);
609        }
610    }
611
612    private void printErrorAnalysisSelected() throws IOException {
613        writer.write(Bundle.getMessage("TrackErrorAnalysis") + NEW_LINE);
614        boolean foundError = false;
615        for (Location location : lmanager.getLocationsByNameList()) {
616            if (_location != null && location != _location) {
617                continue;
618            }
619            writer.write(location.getName() + NEW_LINE);
620            for (Track track : location.getTracksByNameList(null)) {
621                if (!track.checkPickups().equals(Track.PICKUP_OKAY)) {
622                    writer.write(TAB + track.checkPickups() + NEW_LINE);
623                    foundError = true;
624                }
625            }
626        }
627        if (!foundError) {
628            writer.write(Bundle.getMessage("NoErrors"));
629        }
630    }
631
632    private int getTrackLengthAcceptType(List<Location> locations, String carType,
633            String trackType)
634            throws IOException {
635        int trackLength = 0;
636        for (Location location : locations) {
637            if (_location != null && location != _location) {
638                continue;
639            }
640            List<Track> tracks = location.getTracksByNameList(trackType);
641            for (Track track : tracks) {
642                if (track.isTypeNameAccepted(carType)) {
643                    trackLength = trackLength + track.getLength();
644                    writer.write(SPACES_3 +
645                            SPACES_3 +
646                            Bundle.getMessage("LocationTrackLength",
647                                    location.getName(), track.getName(), track.getLength(),
648                                    Setup.getLengthUnit().toLowerCase()) +
649                            NEW_LINE);
650                }
651            }
652        }
653        return trackLength;
654    }
655
656    private String getTrackString(Track track) {
657        String s = TAB +
658                padOutString(track.getName(), Control.max_len_string_track_name) +
659                " " +
660                Integer.toString(track.getLength()) +
661                TAB +
662                Integer.toString(track.getUsedLength()) +
663                TAB +
664                Integer.toString(track.getNumberRS()) +
665                TAB +
666                Integer.toString(track.getNumberCars()) +
667                TAB +
668                Integer.toString(track.getNumberEngines()) +
669                TAB +
670                Integer.toString(track.getPickupRS()) +
671                TAB +
672                Integer.toString(track.getDropRS()) +
673                NEW_LINE;
674        return s;
675    }
676
677    private String getDirection(int dir) {
678        if ((Setup.getTrainDirection() & dir) == 0) {
679            return " " + Bundle.getMessage("LocalOnly") + NEW_LINE;
680        }
681        StringBuffer direction = new StringBuffer(" " + Bundle.getMessage("ServicedByTrain") + " ");
682        if ((Setup.getTrainDirection() & dir & Location.NORTH) == Location.NORTH) {
683            direction.append(Bundle.getMessage("North") + " ");
684        }
685        if ((Setup.getTrainDirection() & dir & Location.SOUTH) == Location.SOUTH) {
686            direction.append(Bundle.getMessage("South") + " ");
687        }
688        if ((Setup.getTrainDirection() & dir & Location.EAST) == Location.EAST) {
689            direction.append(Bundle.getMessage("East") + " ");
690        }
691        if ((Setup.getTrainDirection() & dir & Location.WEST) == Location.WEST) {
692            direction.append(Bundle.getMessage("West") + " ");
693        }
694        direction.append(NEW_LINE);
695        return direction.toString();
696    }
697
698    private void printTrackInfo(Location location, List<Track> tracks) {
699        for (Track track : tracks) {
700            try {
701                String s = TAB +
702                        track.getName() +
703                        getDirection(location.getTrainDirections() & track.getTrainDirections());
704                writer.write(s);
705                isAlternate(track);
706                writer.write(getTrackCarTypes(track));
707                writer.write(getTrackEngineTypes(track));
708                writer.write(getTrackRoads(track));
709                writer.write(getTrackLoads(track));
710                writer.write(getTrackShipLoads(track));
711                writer.write(getCarOrder(track));
712                writer.write(getSetOutTrains(track));
713                writer.write(getPickUpTrains(track));
714                writer.write(getDestinations(track));
715                writer.write(getTrackInfo(track));
716                writer.write(getSpurInfo(track));
717                writer.write(getSchedule(track));
718                writer.write(getStagingInfo(track));
719                writer.write(NEW_LINE);
720            } catch (IOException we) {
721                log.error("Error printing PrintLocationAction: {}", we.getLocalizedMessage());
722            }
723        }
724    }
725
726    private String getLocationTypes(Location location) {
727        StringBuffer buf = new StringBuffer(TAB + TAB + Bundle.getMessage("TypesServiced") + NEW_LINE + TAB + TAB);
728        int charCount = 0;
729        int typeCount = 0;
730
731        for (String type : cts.getNames()) {
732            if (location.acceptsTypeName(type)) {
733                typeCount++;
734                charCount += type.length() + 2;
735                if (charCount > charactersPerLine - 2 * TAB_LENGTH) {
736                    buf.append(NEW_LINE + TAB + TAB);
737                    charCount = type.length() + 2;
738                }
739                buf.append(type + ", ");
740            }
741        }
742
743        for (String type : InstanceManager.getDefault(EngineTypes.class).getNames()) {
744            if (location.acceptsTypeName(type)) {
745                typeCount++;
746                charCount += type.length() + 2;
747                if (charCount > charactersPerLine - 2 * TAB_LENGTH) {
748                    buf.append(NEW_LINE + TAB + TAB);
749                    charCount = type.length() + 2;
750                }
751                buf.append(type + ", ");
752            }
753        }
754        if (buf.length() > 2) {
755            buf.setLength(buf.length() - 2); // remove trailing separators
756        }
757        // does this location accept all types?
758        if (typeCount == cts.getNames().length + InstanceManager.getDefault(EngineTypes.class).getNames().length) {
759            buf = new StringBuffer(TAB + TAB + Bundle.getMessage("LocationAcceptsAllTypes"));
760        }
761        buf.append(NEW_LINE);
762        return buf.toString();
763    }
764
765    private String getTrackCarTypes(Track track) {
766        StringBuffer buf =
767                new StringBuffer(TAB + TAB + Bundle.getMessage("CarTypesServicedTrack") + NEW_LINE + TAB + TAB);
768        int charCount = 0;
769        int typeCount = 0;
770
771        for (String type : cts.getNames()) {
772            if (track.isTypeNameAccepted(type)) {
773                typeCount++;
774                charCount += type.length() + 2;
775                if (charCount > charactersPerLine - 2 * TAB_LENGTH) {
776                    buf.append(NEW_LINE + TAB + TAB);
777                    charCount = type.length() + 2;
778                }
779                buf.append(type + ", ");
780            }
781        }
782        if (buf.length() > 2) {
783            buf.setLength(buf.length() - 2); // remove trailing separators
784        }
785        // does this track accept all types?
786        if (typeCount == cts.getNames().length) {
787            buf = new StringBuffer(TAB + TAB + Bundle.getMessage("TrackAcceptsAllCarTypes"));
788        }
789        buf.append(NEW_LINE);
790        return buf.toString();
791    }
792
793    private String getTrackEngineTypes(Track track) {
794        StringBuffer buf =
795                new StringBuffer(TAB + TAB + Bundle.getMessage("EngineTypesServicedTrack") + NEW_LINE + TAB + TAB);
796        int charCount = 0;
797        int typeCount = 0;
798
799        for (String type : InstanceManager.getDefault(EngineTypes.class).getNames()) {
800            if (track.isTypeNameAccepted(type)) {
801                typeCount++;
802                charCount += type.length() + 2;
803                if (charCount > charactersPerLine - 2 * TAB_LENGTH) {
804                    buf.append(NEW_LINE + TAB + TAB);
805                    charCount = type.length() + 2;
806                }
807                buf.append(type + ", ");
808            }
809        }
810        if (buf.length() > 2) {
811            buf.setLength(buf.length() - 2); // remove trailing separators
812        }
813        // does this track accept all types?
814        if (typeCount == InstanceManager.getDefault(EngineTypes.class).getNames().length) {
815            buf = new StringBuffer(TAB + TAB + Bundle.getMessage("TrackAcceptsAllEngTypes"));
816        }
817        buf.append(NEW_LINE);
818        return buf.toString();
819    }
820
821    private String getTrackRoads(Track track) {
822        if (track.getRoadOption().equals(Track.ALL_ROADS)) {
823            return TAB + TAB + Bundle.getMessage("AcceptsAllRoads") + NEW_LINE;
824        }
825
826        String op = Bundle.getMessage("RoadsServicedTrack");
827        if (track.getRoadOption().equals(Track.EXCLUDE_ROADS)) {
828            op = Bundle.getMessage("ExcludeRoadsTrack");
829        }
830
831        StringBuffer buf = new StringBuffer(TAB + TAB + op + NEW_LINE + TAB + TAB);
832        int charCount = 0;
833
834        for (String road : track.getRoadNames()) {
835            charCount += road.length() + 2;
836            if (charCount > charactersPerLine - 2 * TAB_LENGTH) {
837                buf.append(NEW_LINE + TAB + TAB);
838                charCount = road.length() + 2;
839            }
840            buf.append(road + ", ");
841        }
842        if (buf.length() > 2) {
843            buf.setLength(buf.length() - 2); // remove trailing separators
844        }
845        buf.append(NEW_LINE);
846        return buf.toString();
847    }
848
849    private String getTrackLoads(Track track) {
850        if (track.getLoadOption().equals(Track.ALL_LOADS)) {
851            return TAB + TAB + Bundle.getMessage("AcceptsAllLoads") + NEW_LINE;
852        }
853
854        String op = Bundle.getMessage("LoadsServicedTrack");
855        if (track.getLoadOption().equals(Track.EXCLUDE_LOADS)) {
856            op = Bundle.getMessage("ExcludeLoadsTrack");
857        }
858
859        StringBuffer buf = new StringBuffer(TAB + TAB + op + NEW_LINE + TAB + TAB);
860        int charCount = 0;
861
862        for (String load : track.getLoadNames()) {
863            charCount += load.length() + 2;
864            if (charCount > charactersPerLine - 2 * TAB_LENGTH) {
865                buf.append(NEW_LINE + TAB + TAB);
866                charCount = load.length() + 2;
867            }
868            buf.append(load + ", ");
869        }
870        if (buf.length() > 2) {
871            buf.setLength(buf.length() - 2); // remove trailing separators
872        }
873        buf.append(NEW_LINE);
874        return buf.toString();
875    }
876
877    private String getTrackShipLoads(Track track) {
878        // only staging has the ship load control
879        if (!track.isStaging()) {
880            return "";
881        }
882        if (track.getShipLoadOption().equals(Track.ALL_LOADS)) {
883            return TAB + TAB + Bundle.getMessage("ShipsAllLoads") + NEW_LINE;
884        }
885        String op = Bundle.getMessage("LoadsShippedTrack");
886        if (track.getShipLoadOption().equals(Track.EXCLUDE_LOADS)) {
887            op = Bundle.getMessage("ExcludeLoadsShippedTrack");
888        }
889
890        StringBuffer buf = new StringBuffer(TAB + TAB + op + NEW_LINE + TAB + TAB);
891        int charCount = 0;
892
893        for (String load : track.getShipLoadNames()) {
894            charCount += load.length() + 2;
895            if (charCount > charactersPerLine - 2 * TAB_LENGTH) {
896                buf.append(NEW_LINE + TAB + TAB);
897                charCount = load.length() + 2;
898            }
899            buf.append(load + ", ");
900        }
901        if (buf.length() > 2) {
902            buf.setLength(buf.length() - 2); // remove trailing separators
903        }
904        buf.append(NEW_LINE);
905        return buf.toString();
906    }
907
908    private String getCarOrder(Track track) {
909        // only yards and interchanges have the car order option
910        if (track.isSpur() || track.isStaging() || track.getServiceOrder().equals(Track.NORMAL)) {
911            return "";
912        }
913        if (track.getServiceOrder().equals(Track.FIFO)) {
914            return TAB + TAB + Bundle.getMessage("TrackPickUpOrderFIFO") + NEW_LINE;
915        }
916        return TAB + TAB + Bundle.getMessage("TrackPickUpOrderLIFO") + NEW_LINE;
917    }
918
919    private String getSetOutTrains(Track track) {
920        if (track.getDropOption().equals(Track.ANY)) {
921            return TAB + TAB + Bundle.getMessage("SetOutAllTrains") + NEW_LINE;
922        }
923        StringBuffer buf;
924        int charCount = 0;
925        String[] ids = track.getDropIds();
926        if (track.getDropOption().equals(Track.TRAINS) || track.getDropOption().equals(Track.EXCLUDE_TRAINS)) {
927            String trainType = Bundle.getMessage("TrainsSetOutTrack");
928            if (track.getDropOption().equals(Track.EXCLUDE_TRAINS)) {
929                trainType = Bundle.getMessage("ExcludeTrainsSetOutTrack");
930            }
931            buf = new StringBuffer(TAB + TAB + trainType + NEW_LINE + TAB + TAB);
932            for (String id : ids) {
933                Train train = InstanceManager.getDefault(TrainManager.class).getTrainById(id);
934                if (train == null) {
935                    log.info("Could not find a train for id: {} track ({})", id, track.getName());
936                    continue;
937                }
938                charCount += train.getName().length() + 2;
939                if (charCount > charactersPerLine - 2 * TAB_LENGTH) {
940                    buf.append(NEW_LINE + TAB + TAB);
941                    charCount = train.getName().length() + 2;
942                }
943                buf.append(train.getName() + ", ");
944            }
945        } else {
946            String routeType = Bundle.getMessage("RoutesSetOutTrack");
947            if (track.getDropOption().equals(Track.EXCLUDE_ROUTES)) {
948                routeType = Bundle.getMessage("ExcludeRoutesSetOutTrack");
949            }
950            buf = new StringBuffer(TAB + TAB + routeType + NEW_LINE + TAB + TAB);
951            for (String id : ids) {
952                Route route = InstanceManager.getDefault(RouteManager.class).getRouteById(id);
953                if (route == null) {
954                    log.info("Could not find a route for id: {} location ({}) track ({})", id,
955                            track.getLocation().getName(), track.getName()); // NOI18N
956                    continue;
957                }
958                charCount += route.getName().length() + 2;
959                if (charCount > charactersPerLine - 2 * TAB_LENGTH) {
960                    buf.append(NEW_LINE + TAB + TAB);
961                    charCount = route.getName().length() + 2;
962                }
963                buf.append(route.getName() + ", ");
964            }
965        }
966        if (buf.length() > 2) {
967            buf.setLength(buf.length() - 2); // remove trailing separators
968        }
969        buf.append(NEW_LINE);
970        return buf.toString();
971    }
972
973    private String getPickUpTrains(Track track) {
974        if (track.getPickupOption().equals(Track.ANY)) {
975            return TAB + TAB + Bundle.getMessage("PickUpAllTrains") + NEW_LINE;
976        }
977        StringBuffer buf;
978        int charCount = 0;
979        String[] ids = track.getPickupIds();
980        if (track.getPickupOption().equals(Track.TRAINS) || track.getPickupOption().equals(Track.EXCLUDE_TRAINS)) {
981            String trainType = Bundle.getMessage("TrainsPickUpTrack");
982            if (track.getPickupOption().equals(Track.EXCLUDE_TRAINS)) {
983                trainType = Bundle.getMessage("ExcludeTrainsPickUpTrack");
984            }
985            buf = new StringBuffer(TAB + TAB + trainType + NEW_LINE + TAB + TAB);
986            for (String id : ids) {
987                Train train = InstanceManager.getDefault(TrainManager.class).getTrainById(id);
988                if (train == null) {
989                    log.info("Could not find a train for id: {} track ({})", id, track.getName());
990                    continue;
991                }
992                charCount += train.getName().length() + 2;
993                if (charCount > charactersPerLine - 2 * TAB_LENGTH) {
994                    buf.append(NEW_LINE + TAB + TAB);
995                    charCount = train.getName().length() + 2;
996                }
997                buf.append(train.getName() + ", ");
998            }
999        } else {
1000            String routeType = Bundle.getMessage("RoutesPickUpTrack");
1001            if (track.getPickupOption().equals(Track.EXCLUDE_ROUTES)) {
1002                routeType = Bundle.getMessage("ExcludeRoutesPickUpTrack");
1003            }
1004            buf = new StringBuffer(TAB + TAB + routeType + NEW_LINE + TAB + TAB);
1005            for (String id : ids) {
1006                Route route = InstanceManager.getDefault(RouteManager.class).getRouteById(id);
1007                if (route == null) {
1008                    log.info("Could not find a route for id: {} location ({}) track ({})", id,
1009                            track.getLocation().getName(), track.getName()); // NOI18N
1010                    continue;
1011                }
1012                charCount += route.getName().length() + 2;
1013                if (charCount > charactersPerLine - 2 * TAB_LENGTH) {
1014                    buf.append(NEW_LINE + TAB + TAB);
1015                    charCount = route.getName().length() + 2;
1016                }
1017                buf.append(route.getName() + ", ");
1018            }
1019        }
1020        if (buf.length() > 2) {
1021            buf.setLength(buf.length() - 2); // remove trailing separators
1022        }
1023        buf.append(NEW_LINE);
1024        return buf.toString();
1025    }
1026
1027    private String getDestinations(Track track) {
1028        StringBuffer buf = new StringBuffer();
1029        if (track.isOnlyCarsWithFinalDestinationEnabled()) {
1030            buf.append(TAB + TAB + Bundle.getMessage("OnlyCarsWithFD"));
1031            buf.append(NEW_LINE);
1032        }
1033        if (track.getDestinationOption().equals(Track.ALL_DESTINATIONS)) {
1034            return buf.toString();
1035        }
1036        String op = Bundle.getMessage(
1037                "AcceptOnly") + " " + track.getDestinationListSize() + " " + Bundle.getMessage("Destinations") + ":";
1038        if (track.getDestinationOption().equals(Track.EXCLUDE_DESTINATIONS)) {
1039            op = Bundle.getMessage("Exclude") +
1040                    " " +
1041                    (lmanager.getNumberOfLocations() - track.getDestinationListSize()) +
1042                    " " +
1043                    Bundle.getMessage("Destinations") +
1044                    ":";
1045        }
1046        buf.append(TAB + TAB + op + NEW_LINE + TAB + TAB);
1047        String[] destIds = track.getDestinationIds();
1048        int charCount = 0;
1049        for (String id : destIds) {
1050            Location location = lmanager.getLocationById(id);
1051            if (location == null) {
1052                continue;
1053            }
1054            charCount += location.getName().length() + 2;
1055            if (charCount > charactersPerLine - 2 * TAB_LENGTH) {
1056                buf.append(NEW_LINE + TAB + TAB);
1057                charCount = location.getName().length() + 2;
1058            }
1059            buf.append(location.getName() + ", ");
1060        }
1061        if (buf.length() > 2) {
1062            buf.setLength(buf.length() - 2); // remove trailing separators
1063        }
1064        buf.append(NEW_LINE);
1065        return buf.toString();
1066    }
1067
1068    private String getTrackInfo(Track track) {
1069        if (track.getPool() != null) {
1070            StringBuffer buf =
1071                    new StringBuffer(TAB + TAB + Bundle.getMessage("Pool") + ": " + track.getPoolName() + NEW_LINE);
1072            return buf.toString();
1073        }
1074        return "";
1075    }
1076
1077    private String getSchedule(Track track) {
1078        // only spurs have schedules
1079        if (!track.isSpur() || track.getSchedule() == null) {
1080            return "";
1081        }
1082        StringBuffer buf = new StringBuffer(TAB +
1083                TAB +
1084                Bundle.getMessage("TrackScheduleName", track.getScheduleName()) +
1085                NEW_LINE);
1086        if (track.getAlternateTrack() != null) {
1087            buf.append(TAB +
1088                    TAB +
1089                    Bundle.getMessage("AlternateTrackName",
1090                            track.getAlternateTrack().getName()) +
1091                    NEW_LINE);
1092        }
1093        if (track.getReservationFactor() != 100) {
1094            buf.append(TAB +
1095                    TAB +
1096                    Bundle.getMessage("PercentageStaging",
1097                            track.getReservationFactor()) +
1098                    NEW_LINE);
1099        }
1100        return buf.toString();
1101    }
1102
1103    private void isAlternate(Track track) throws IOException {
1104        if (track.isAlternate()) {
1105            writer.write(TAB + TAB + Bundle.getMessage("AlternateTrack") + NEW_LINE);
1106        }
1107    }
1108
1109    private String getSpurInfo(Track track) {
1110        if (!track.isSpur()) {
1111            return "";
1112        }
1113
1114        StringBuffer buf = new StringBuffer();
1115
1116        if (track.isHoldCarsWithCustomLoadsEnabled()) {
1117            buf.append(TAB + TAB + Bundle.getMessage("HoldCarsWithCustomLoads") + NEW_LINE);
1118        }
1119        if (track.isDisableLoadChangeEnabled()) {
1120            buf.append(TAB + TAB + Bundle.getMessage("DisableLoadChange") + NEW_LINE);
1121        }
1122        return buf.toString();
1123    }
1124
1125    private String getStagingInfo(Track track) {
1126        if (!track.isStaging()) {
1127            return "";
1128        }
1129
1130        StringBuffer buf = new StringBuffer();
1131
1132        if (track.isLoadSwapEnabled() || track.isLoadEmptyEnabled()) {
1133            buf.append(TAB + SPACES_3 + Bundle.getMessage("OptionalLoads") + NEW_LINE);
1134            if (track.isLoadSwapEnabled()) {
1135                buf.append(TAB + TAB + Bundle.getMessage("SwapCarLoads") + NEW_LINE);
1136            }
1137            if (track.isLoadEmptyEnabled()) {
1138                buf.append(TAB + TAB + Bundle.getMessage("EmptyDefaultCarLoads") + NEW_LINE);
1139            }
1140        }
1141
1142        if (track.isRemoveCustomLoadsEnabled() ||
1143                track.isAddCustomLoadsEnabled() ||
1144                track.isAddCustomLoadsAnySpurEnabled() ||
1145                track.isAddCustomLoadsAnyStagingTrackEnabled()) {
1146            buf.append(TAB + SPACES_3 + Bundle.getMessage("OptionalCustomLoads") + NEW_LINE);
1147            if (track.isRemoveCustomLoadsEnabled()) {
1148                buf.append(TAB + TAB + Bundle.getMessage("EmptyCarLoads") + NEW_LINE);
1149            }
1150            if (track.isAddCustomLoadsEnabled()) {
1151                buf.append(TAB + TAB + Bundle.getMessage("LoadCarLoads") + NEW_LINE);
1152            }
1153            if (track.isAddCustomLoadsAnySpurEnabled()) {
1154                buf.append(TAB + TAB + Bundle.getMessage("LoadAnyCarLoads") + NEW_LINE);
1155            }
1156            if (track.isAddCustomLoadsAnyStagingTrackEnabled()) {
1157                buf.append(TAB + TAB + Bundle.getMessage("LoadsStaging") + NEW_LINE);
1158            }
1159        }
1160
1161        if (track.isBlockCarsEnabled()) {
1162            buf.append(TAB + SPACES_3 + Bundle.getMessage("OptionalBlocking") + NEW_LINE);
1163            buf.append(TAB + TAB + Bundle.getMessage("BlockCars") + NEW_LINE);
1164        }
1165        return buf.toString();
1166    }
1167
1168    private String padOutString(String s, int length) {
1169        return TrainCommon.padAndTruncate(s, length);
1170    }
1171
1172    private final static Logger log = LoggerFactory.getLogger(PrintLocationsFrame.class);
1173}