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