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(getSpurInfo(track));
713                writer.write(getSchedule(track));
714                writer.write(getStagingInfo(track));
715                writer.write(NEW_LINE);
716            } catch (IOException we) {
717                log.error("Error printing PrintLocationAction: {}", we.getLocalizedMessage());
718            }
719        }
720    }
721
722    private String getLocationTypes(Location location) {
723        StringBuffer buf = new StringBuffer(TAB + TAB + Bundle.getMessage("TypesServiced") + NEW_LINE + TAB + TAB);
724        int charCount = 0;
725        int typeCount = 0;
726
727        for (String type : cts.getNames()) {
728            if (location.acceptsTypeName(type)) {
729                typeCount++;
730                charCount += type.length() + 2;
731                if (charCount > charactersPerLine - 2 * TAB_LENGTH) {
732                    buf.append(NEW_LINE + TAB + TAB);
733                    charCount = type.length() + 2;
734                }
735                buf.append(type + ", ");
736            }
737        }
738
739        for (String type : InstanceManager.getDefault(EngineTypes.class).getNames()) {
740            if (location.acceptsTypeName(type)) {
741                typeCount++;
742                charCount += type.length() + 2;
743                if (charCount > charactersPerLine - 2 * TAB_LENGTH) {
744                    buf.append(NEW_LINE + TAB + TAB);
745                    charCount = type.length() + 2;
746                }
747                buf.append(type + ", ");
748            }
749        }
750        if (buf.length() > 2) {
751            buf.setLength(buf.length() - 2); // remove trailing separators
752        }
753        // does this location accept all types?
754        if (typeCount == cts.getNames().length + InstanceManager.getDefault(EngineTypes.class).getNames().length) {
755            buf = new StringBuffer(TAB + TAB + Bundle.getMessage("LocationAcceptsAllTypes"));
756        }
757        buf.append(NEW_LINE);
758        return buf.toString();
759    }
760
761    private String getTrackCarTypes(Track track) {
762        StringBuffer buf =
763                new StringBuffer(TAB + TAB + Bundle.getMessage("CarTypesServicedTrack") + NEW_LINE + TAB + TAB);
764        int charCount = 0;
765        int typeCount = 0;
766
767        for (String type : cts.getNames()) {
768            if (track.isTypeNameAccepted(type)) {
769                typeCount++;
770                charCount += type.length() + 2;
771                if (charCount > charactersPerLine - 2 * TAB_LENGTH) {
772                    buf.append(NEW_LINE + TAB + TAB);
773                    charCount = type.length() + 2;
774                }
775                buf.append(type + ", ");
776            }
777        }
778        if (buf.length() > 2) {
779            buf.setLength(buf.length() - 2); // remove trailing separators
780        }
781        // does this track accept all types?
782        if (typeCount == cts.getNames().length) {
783            buf = new StringBuffer(TAB + TAB + Bundle.getMessage("TrackAcceptsAllCarTypes"));
784        }
785        buf.append(NEW_LINE);
786        return buf.toString();
787    }
788
789    private String getTrackEngineTypes(Track track) {
790        StringBuffer buf =
791                new StringBuffer(TAB + TAB + Bundle.getMessage("EngineTypesServicedTrack") + NEW_LINE + TAB + TAB);
792        int charCount = 0;
793        int typeCount = 0;
794
795        for (String type : InstanceManager.getDefault(EngineTypes.class).getNames()) {
796            if (track.isTypeNameAccepted(type)) {
797                typeCount++;
798                charCount += type.length() + 2;
799                if (charCount > charactersPerLine - 2 * TAB_LENGTH) {
800                    buf.append(NEW_LINE + TAB + TAB);
801                    charCount = type.length() + 2;
802                }
803                buf.append(type + ", ");
804            }
805        }
806        if (buf.length() > 2) {
807            buf.setLength(buf.length() - 2); // remove trailing separators
808        }
809        // does this track accept all types?
810        if (typeCount == InstanceManager.getDefault(EngineTypes.class).getNames().length) {
811            buf = new StringBuffer(TAB + TAB + Bundle.getMessage("TrackAcceptsAllEngTypes"));
812        }
813        buf.append(NEW_LINE);
814        return buf.toString();
815    }
816
817    private String getTrackRoads(Track track) {
818        if (track.getRoadOption().equals(Track.ALL_ROADS)) {
819            return TAB + TAB + Bundle.getMessage("AcceptsAllRoads") + NEW_LINE;
820        }
821
822        String op = Bundle.getMessage("RoadsServicedTrack");
823        if (track.getRoadOption().equals(Track.EXCLUDE_ROADS)) {
824            op = Bundle.getMessage("ExcludeRoadsTrack");
825        }
826
827        StringBuffer buf = new StringBuffer(TAB + TAB + op + NEW_LINE + TAB + TAB);
828        int charCount = 0;
829
830        for (String road : track.getRoadNames()) {
831            charCount += road.length() + 2;
832            if (charCount > charactersPerLine - 2 * TAB_LENGTH) {
833                buf.append(NEW_LINE + TAB + TAB);
834                charCount = road.length() + 2;
835            }
836            buf.append(road + ", ");
837        }
838        if (buf.length() > 2) {
839            buf.setLength(buf.length() - 2); // remove trailing separators
840        }
841        buf.append(NEW_LINE);
842        return buf.toString();
843    }
844
845    private String getTrackLoads(Track track) {
846        if (track.getLoadOption().equals(Track.ALL_LOADS)) {
847            return TAB + TAB + Bundle.getMessage("AcceptsAllLoads") + NEW_LINE;
848        }
849
850        String op = Bundle.getMessage("LoadsServicedTrack");
851        if (track.getLoadOption().equals(Track.EXCLUDE_LOADS)) {
852            op = Bundle.getMessage("ExcludeLoadsTrack");
853        }
854
855        StringBuffer buf = new StringBuffer(TAB + TAB + op + NEW_LINE + TAB + TAB);
856        int charCount = 0;
857
858        for (String load : track.getLoadNames()) {
859            charCount += load.length() + 2;
860            if (charCount > charactersPerLine - 2 * TAB_LENGTH) {
861                buf.append(NEW_LINE + TAB + TAB);
862                charCount = load.length() + 2;
863            }
864            buf.append(load + ", ");
865        }
866        if (buf.length() > 2) {
867            buf.setLength(buf.length() - 2); // remove trailing separators
868        }
869        buf.append(NEW_LINE);
870        return buf.toString();
871    }
872
873    private String getTrackShipLoads(Track track) {
874        // only staging has the ship load control
875        if (!track.isStaging()) {
876            return "";
877        }
878        if (track.getShipLoadOption().equals(Track.ALL_LOADS)) {
879            return TAB + TAB + Bundle.getMessage("ShipsAllLoads") + NEW_LINE;
880        }
881        String op = Bundle.getMessage("LoadsShippedTrack");
882        if (track.getShipLoadOption().equals(Track.EXCLUDE_LOADS)) {
883            op = Bundle.getMessage("ExcludeLoadsShippedTrack");
884        }
885
886        StringBuffer buf = new StringBuffer(TAB + TAB + op + NEW_LINE + TAB + TAB);
887        int charCount = 0;
888
889        for (String load : track.getShipLoadNames()) {
890            charCount += load.length() + 2;
891            if (charCount > charactersPerLine - 2 * TAB_LENGTH) {
892                buf.append(NEW_LINE + TAB + TAB);
893                charCount = load.length() + 2;
894            }
895            buf.append(load + ", ");
896        }
897        if (buf.length() > 2) {
898            buf.setLength(buf.length() - 2); // remove trailing separators
899        }
900        buf.append(NEW_LINE);
901        return buf.toString();
902    }
903
904    private String getCarOrder(Track track) {
905        // only yards and interchanges have the car order option
906        if (track.isSpur() || track.isStaging() || track.getServiceOrder().equals(Track.NORMAL)) {
907            return "";
908        }
909        if (track.getServiceOrder().equals(Track.FIFO)) {
910            return TAB + TAB + Bundle.getMessage("TrackPickUpOrderFIFO") + NEW_LINE;
911        }
912        return TAB + TAB + Bundle.getMessage("TrackPickUpOrderLIFO") + NEW_LINE;
913    }
914
915    private String getSetOutTrains(Track track) {
916        if (track.getDropOption().equals(Track.ANY)) {
917            return TAB + TAB + Bundle.getMessage("SetOutAllTrains") + NEW_LINE;
918        }
919        StringBuffer buf;
920        int charCount = 0;
921        String[] ids = track.getDropIds();
922        if (track.getDropOption().equals(Track.TRAINS) || track.getDropOption().equals(Track.EXCLUDE_TRAINS)) {
923            String trainType = Bundle.getMessage("TrainsSetOutTrack");
924            if (track.getDropOption().equals(Track.EXCLUDE_TRAINS)) {
925                trainType = Bundle.getMessage("ExcludeTrainsSetOutTrack");
926            }
927            buf = new StringBuffer(TAB + TAB + trainType + NEW_LINE + TAB + TAB);
928            for (String id : ids) {
929                Train train = InstanceManager.getDefault(TrainManager.class).getTrainById(id);
930                if (train == null) {
931                    log.info("Could not find a train for id: {} track ({})", id, track.getName());
932                    continue;
933                }
934                charCount += train.getName().length() + 2;
935                if (charCount > charactersPerLine - 2 * TAB_LENGTH) {
936                    buf.append(NEW_LINE + TAB + TAB);
937                    charCount = train.getName().length() + 2;
938                }
939                buf.append(train.getName() + ", ");
940            }
941        } else {
942            String routeType = Bundle.getMessage("RoutesSetOutTrack");
943            if (track.getDropOption().equals(Track.EXCLUDE_ROUTES)) {
944                routeType = Bundle.getMessage("ExcludeRoutesSetOutTrack");
945            }
946            buf = new StringBuffer(TAB + TAB + routeType + NEW_LINE + TAB + TAB);
947            for (String id : ids) {
948                Route route = InstanceManager.getDefault(RouteManager.class).getRouteById(id);
949                if (route == null) {
950                    log.info("Could not find a route for id: {} location ({}) track ({})", id,
951                            track.getLocation().getName(), track.getName()); // NOI18N
952                    continue;
953                }
954                charCount += route.getName().length() + 2;
955                if (charCount > charactersPerLine - 2 * TAB_LENGTH) {
956                    buf.append(NEW_LINE + TAB + TAB);
957                    charCount = route.getName().length() + 2;
958                }
959                buf.append(route.getName() + ", ");
960            }
961        }
962        if (buf.length() > 2) {
963            buf.setLength(buf.length() - 2); // remove trailing separators
964        }
965        buf.append(NEW_LINE);
966        return buf.toString();
967    }
968
969    private String getPickUpTrains(Track track) {
970        if (track.getPickupOption().equals(Track.ANY)) {
971            return TAB + TAB + Bundle.getMessage("PickUpAllTrains") + NEW_LINE;
972        }
973        StringBuffer buf;
974        int charCount = 0;
975        String[] ids = track.getPickupIds();
976        if (track.getPickupOption().equals(Track.TRAINS) || track.getPickupOption().equals(Track.EXCLUDE_TRAINS)) {
977            String trainType = Bundle.getMessage("TrainsPickUpTrack");
978            if (track.getPickupOption().equals(Track.EXCLUDE_TRAINS)) {
979                trainType = Bundle.getMessage("ExcludeTrainsPickUpTrack");
980            }
981            buf = new StringBuffer(TAB + TAB + trainType + NEW_LINE + TAB + TAB);
982            for (String id : ids) {
983                Train train = InstanceManager.getDefault(TrainManager.class).getTrainById(id);
984                if (train == null) {
985                    log.info("Could not find a train for id: {} track ({})", id, track.getName());
986                    continue;
987                }
988                charCount += train.getName().length() + 2;
989                if (charCount > charactersPerLine - 2 * TAB_LENGTH) {
990                    buf.append(NEW_LINE + TAB + TAB);
991                    charCount = train.getName().length() + 2;
992                }
993                buf.append(train.getName() + ", ");
994            }
995        } else {
996            String routeType = Bundle.getMessage("RoutesPickUpTrack");
997            if (track.getPickupOption().equals(Track.EXCLUDE_ROUTES)) {
998                routeType = Bundle.getMessage("ExcludeRoutesPickUpTrack");
999            }
1000            buf = new StringBuffer(TAB + TAB + routeType + NEW_LINE + TAB + TAB);
1001            for (String id : ids) {
1002                Route route = InstanceManager.getDefault(RouteManager.class).getRouteById(id);
1003                if (route == null) {
1004                    log.info("Could not find a route for id: {} location ({}) track ({})", id,
1005                            track.getLocation().getName(), track.getName()); // NOI18N
1006                    continue;
1007                }
1008                charCount += route.getName().length() + 2;
1009                if (charCount > charactersPerLine - 2 * TAB_LENGTH) {
1010                    buf.append(NEW_LINE + TAB + TAB);
1011                    charCount = route.getName().length() + 2;
1012                }
1013                buf.append(route.getName() + ", ");
1014            }
1015        }
1016        if (buf.length() > 2) {
1017            buf.setLength(buf.length() - 2); // remove trailing separators
1018        }
1019        buf.append(NEW_LINE);
1020        return buf.toString();
1021    }
1022
1023    private String getDestinations(Track track) {
1024        StringBuffer buf = new StringBuffer();
1025        if (track.isOnlyCarsWithFinalDestinationEnabled()) {
1026            buf.append(TAB + TAB + Bundle.getMessage("OnlyCarsWithFD"));
1027            buf.append(NEW_LINE);
1028        }
1029        if (track.getDestinationOption().equals(Track.ALL_DESTINATIONS)) {
1030            return buf.toString();
1031        }
1032        String op = Bundle.getMessage(
1033                "AcceptOnly") + " " + track.getDestinationListSize() + " " + Bundle.getMessage("Destinations") + ":";
1034        if (track.getDestinationOption().equals(Track.EXCLUDE_DESTINATIONS)) {
1035            op = Bundle.getMessage("Exclude") +
1036                    " " +
1037                    (lmanager.getNumberOfLocations() - track.getDestinationListSize()) +
1038                    " " +
1039                    Bundle.getMessage("Destinations") +
1040                    ":";
1041        }
1042        buf.append(TAB + TAB + op + NEW_LINE + TAB + TAB);
1043        String[] destIds = track.getDestinationIds();
1044        int charCount = 0;
1045        for (String id : destIds) {
1046            Location location = lmanager.getLocationById(id);
1047            if (location == null) {
1048                continue;
1049            }
1050            charCount += location.getName().length() + 2;
1051            if (charCount > charactersPerLine - 2 * TAB_LENGTH) {
1052                buf.append(NEW_LINE + TAB + TAB);
1053                charCount = location.getName().length() + 2;
1054            }
1055            buf.append(location.getName() + ", ");
1056        }
1057        if (buf.length() > 2) {
1058            buf.setLength(buf.length() - 2); // remove trailing separators
1059        }
1060        buf.append(NEW_LINE);
1061        return buf.toString();
1062    }
1063
1064    private String getSchedule(Track track) {
1065        // only spurs have schedules
1066        if (!track.isSpur() || track.getSchedule() == null) {
1067            return "";
1068        }
1069        StringBuffer buf = new StringBuffer(TAB +
1070                TAB +
1071                Bundle.getMessage("TrackScheduleName", track.getScheduleName()) +
1072                NEW_LINE);
1073        if (track.getAlternateTrack() != null) {
1074            buf.append(TAB +
1075                    TAB +
1076                    Bundle.getMessage("AlternateTrackName",
1077                            track.getAlternateTrack().getName()) +
1078                    NEW_LINE);
1079        }
1080        if (track.getReservationFactor() != 100) {
1081            buf.append(TAB +
1082                    TAB +
1083                    Bundle.getMessage("PercentageStaging",
1084                            track.getReservationFactor()) +
1085                    NEW_LINE);
1086        }
1087        return buf.toString();
1088    }
1089
1090    private void isAlternate(Track track) throws IOException {
1091        if (track.isAlternate()) {
1092            writer.write(TAB + TAB + Bundle.getMessage("AlternateTrack") + NEW_LINE);
1093        }
1094    }
1095
1096    private String getSpurInfo(Track track) {
1097        if (!track.isSpur()) {
1098            return "";
1099        }
1100
1101        StringBuffer buf = new StringBuffer();
1102
1103        if (track.isHoldCarsWithCustomLoadsEnabled()) {
1104            buf.append(TAB + TAB + Bundle.getMessage("HoldCarsWithCustomLoads") + NEW_LINE);
1105        }
1106        if (track.isDisableLoadChangeEnabled()) {
1107            buf.append(TAB + TAB + Bundle.getMessage("DisableLoadChange") + NEW_LINE);
1108        }
1109        return buf.toString();
1110    }
1111
1112    private String getStagingInfo(Track track) {
1113        if (!track.isStaging()) {
1114            return "";
1115        }
1116
1117        StringBuffer buf = new StringBuffer();
1118
1119        if (track.isLoadSwapEnabled() || track.isLoadEmptyEnabled()) {
1120            buf.append(TAB + SPACES_3 + Bundle.getMessage("OptionalLoads") + NEW_LINE);
1121            if (track.isLoadSwapEnabled()) {
1122                buf.append(TAB + TAB + Bundle.getMessage("SwapCarLoads") + NEW_LINE);
1123            }
1124            if (track.isLoadEmptyEnabled()) {
1125                buf.append(TAB + TAB + Bundle.getMessage("EmptyDefaultCarLoads") + NEW_LINE);
1126            }
1127        }
1128
1129        if (track.isRemoveCustomLoadsEnabled() ||
1130                track.isAddCustomLoadsEnabled() ||
1131                track.isAddCustomLoadsAnySpurEnabled() ||
1132                track.isAddCustomLoadsAnyStagingTrackEnabled()) {
1133            buf.append(TAB + SPACES_3 + Bundle.getMessage("OptionalCustomLoads") + NEW_LINE);
1134            if (track.isRemoveCustomLoadsEnabled()) {
1135                buf.append(TAB + TAB + Bundle.getMessage("EmptyCarLoads") + NEW_LINE);
1136            }
1137            if (track.isAddCustomLoadsEnabled()) {
1138                buf.append(TAB + TAB + Bundle.getMessage("LoadCarLoads") + NEW_LINE);
1139            }
1140            if (track.isAddCustomLoadsAnySpurEnabled()) {
1141                buf.append(TAB + TAB + Bundle.getMessage("LoadAnyCarLoads") + NEW_LINE);
1142            }
1143            if (track.isAddCustomLoadsAnyStagingTrackEnabled()) {
1144                buf.append(TAB + TAB + Bundle.getMessage("LoadsStaging") + NEW_LINE);
1145            }
1146        }
1147
1148        if (track.isBlockCarsEnabled()) {
1149            buf.append(TAB + SPACES_3 + Bundle.getMessage("OptionalBlocking") + NEW_LINE);
1150            buf.append(TAB + TAB + Bundle.getMessage("BlockCars") + NEW_LINE);
1151        }
1152        return buf.toString();
1153    }
1154
1155    private String padOutString(String s, int length) {
1156        return TrainCommon.padAndTruncate(s, length);
1157    }
1158
1159    private final static Logger log = LoggerFactory.getLogger(PrintLocationsFrame.class);
1160}