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