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