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