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