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