001package jmri.jmrit.operations.trains.tools;
002
003import java.io.*;
004import java.nio.charset.StandardCharsets;
005import java.util.ArrayList;
006import java.util.Arrays;
007
008import org.apache.commons.csv.CSVFormat;
009import org.apache.commons.csv.CSVPrinter;
010
011import jmri.InstanceManager;
012import jmri.jmrit.XmlFile;
013import jmri.jmrit.operations.setup.OperationsSetupXml;
014import jmri.jmrit.operations.trains.*;
015import jmri.util.swing.JmriJOptionPane;
016
017/**
018 * Exports the train roster into a comma delimited file (CSV). Only trains that
019 * have the "Build" checkbox selected are exported. If a train is built, a
020 * summary of the train's route and work is provided.
021 *
022 * @author Daniel Boudreau Copyright (C) 2010, 2011, 2019
023 *
024 */
025public class ExportTrains extends XmlFile {
026
027    public ExportTrains(){
028        // nothing to do
029    }
030
031    public void writeOperationsTrainsFile() {
032        makeBackupFile(defaultOperationsFilename());
033        try {
034            if (!checkFile(defaultOperationsFilename())) {
035                // The file does not exist, create it before writing
036                java.io.File file = new java.io.File(defaultOperationsFilename());
037                java.io.File parentDir = file.getParentFile();
038                if (!parentDir.exists()) {
039                    if (!parentDir.mkdir()) {
040                        log.error("Directory wasn't created");
041                    }
042                }
043                if (file.createNewFile()) {
044                    log.debug("File created");
045                }
046            }
047            writeFile(defaultOperationsFilename());
048        } catch (IOException e) {
049            log.error("Exception while writing the new CSV operations file, may not be complete", e);
050        }
051    }
052
053    public void writeFile(String name) {
054        log.debug("writeFile {}", name);
055        // This is taken in large part from "Java and XML" page 368
056        File file = findFile(name);
057        if (file == null) {
058            file = new File(name);
059        }
060
061        try (CSVPrinter fileOut = new CSVPrinter(
062                new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8)),
063                CSVFormat.DEFAULT)) {
064
065            // create header
066            fileOut.printRecord(Bundle.getMessage("Name"), Bundle.getMessage("Description"), Bundle.getMessage("Time"),
067                    Bundle.getMessage("Route"), Bundle.getMessage("Departs"), Bundle.getMessage("Terminates"),
068                    Bundle.getMessage("Status"), Bundle.getMessage("Comment"), Bundle.getMessage("LocoTypes"),
069                    Bundle.getMessage("CarTypes"), Bundle.getMessage("RoadOption"), Bundle.getMessage("Roads"),
070                    Bundle.getMessage("RoadOption"), Bundle.getMessage("Roads"),
071                    Bundle.getMessage("LoadOption"), Bundle.getMessage("Loads"), Bundle.getMessage("OwnerOption"),
072                    Bundle.getMessage("Owners"), Bundle.getMessage("Built"),
073                    Bundle.getMessage("NormalModeWhenBuilding"), Bundle.getMessage("AllowCarsToReturn"),
074                    Bundle.getMessage("AllowThroughCars"), Bundle.getMessage("SendCustomToStaging"),
075                    Bundle.getMessage("SendToTerminal", ""),
076                    Bundle.getMessage("AllowLocalMoves"), Bundle.getMessage("ServiceAllCars"),
077                    Bundle.getMessage("BuildConsist"));
078
079            int count = 0;
080
081            for (Train train : InstanceManager.getDefault(TrainManager.class).getTrainsByTimeList()) {
082                if (!train.isBuildEnabled()) {
083                    continue;
084                }
085                count++;
086                String routeName = "";
087                if (train.getRoute() != null) {
088                    routeName = train.getRoute().getName();
089                }
090                fileOut.printRecord(train.getName(), train.getDescription(), train.getDepartureTime(), routeName,
091                        train.getTrainDepartsName(), train.getTrainTerminatesName(), train.getStatus(),
092                        train.getComment(), TrainCommon.formatStringToCommaSeparated(train.getLocoTypeNames()),
093                        TrainCommon.formatStringToCommaSeparated(train.getCarTypeNames()), getCarRoadOption(train),
094                        getCarRoads(train), getLocoRoadOption(train), getLocoRoads(train), getLoadOption(train),
095                        getLoads(train), getOwnerOption(train), getOwners(train), getBuilt(train),
096                        train.isBuildTrainNormalEnabled() ? Bundle.getMessage("ButtonYes") : "",
097                        train.isAllowReturnToStagingEnabled() ? Bundle.getMessage("ButtonYes") : "",
098                        train.isAllowThroughCarsEnabled() ? Bundle.getMessage("ButtonYes") : "",
099                        train.isSendCarsWithCustomLoadsToStagingEnabled() ? Bundle.getMessage("ButtonYes") : "",
100                        train.isSendCarsToTerminalEnabled() ? Bundle.getMessage("ButtonYes") : "",
101                        train.isAllowLocalMovesEnabled() ? Bundle.getMessage("ButtonYes") : "",
102                        train.isServiceAllCarsWithFinalDestinationsEnabled() ? Bundle.getMessage("ButtonYes") : "",
103                        train.isBuildConsistEnabled() ? Bundle.getMessage("ButtonYes") : "");
104            }
105
106            fileOut.println();
107            // second create header for built trains
108            fileOut.printRecord(Bundle.getMessage("Name"), Bundle.getMessage("csvParameters"),
109                    Bundle.getMessage("Attributes"));
110
111            for (Train train : InstanceManager.getDefault(TrainManager.class).getTrainsByTimeList()) {
112                if (!train.isBuildEnabled()) {
113                    continue;
114                }
115
116                if (train.isBuilt() && train.getRoute() != null) {
117                    ArrayList<Object> line = new ArrayList<>();
118                    line.addAll(Arrays.asList(new Object[] { train.getName(), Bundle.getMessage("Route") }));
119                    train.getRoute().getLocationsBySequenceList().forEach(rl -> line.add(rl.getName()));
120                    fileOut.printRecord(line);
121
122                    line.clear();
123                    line.addAll(Arrays.asList(new Object[] { train.getName(), Bundle.getMessage("csvArrivalTime") }));
124                    train.getRoute().getLocationsBySequenceList()
125                            .forEach(rl -> line.add(train.getExpectedArrivalTime(rl)));
126                    fileOut.printRecord(line);
127
128                    line.clear();
129                    line.addAll(Arrays.asList(new Object[] { train.getName(), Bundle.getMessage("csvDepartureTime") }));
130                    train.getRoute().getLocationsBySequenceList()
131                            .forEach(rl -> line.add(train.getExpectedDepartureTime(rl)));
132                    fileOut.printRecord(line);
133
134                    line.clear();
135                    line.addAll(
136                            Arrays.asList(new Object[] { train.getName(), Bundle.getMessage("csvTrainDirection") }));
137                    train.getRoute().getLocationsBySequenceList().forEach(rl -> line.add(rl.getTrainDirectionString()));
138                    fileOut.printRecord(line);
139
140                    line.clear();
141                    line.addAll(Arrays.asList(new Object[] { train.getName(), Bundle.getMessage("csvTrainWeight") }));
142                    train.getRoute().getLocationsBySequenceList().forEach(rl -> line.add(train.getTrainWeight(rl)));
143                    fileOut.printRecord(line);
144
145                    line.clear();
146                    line.addAll(Arrays.asList(new Object[] { train.getName(), Bundle.getMessage("csvTrainLength") }));
147                    train.getRoute().getLocationsBySequenceList().forEach(rl -> line.add(train.getTrainLength(rl)));
148                    fileOut.printRecord(line);
149
150                    line.clear();
151                    line.addAll(Arrays.asList(new Object[] { train.getName(), Bundle.getMessage("Engine") }));
152                    train.getRoute().getLocationsBySequenceList().forEach(rl -> line.add(train.getLeadEngine(rl)));
153                    fileOut.printRecord(line);
154
155                    line.clear();
156                    line.addAll(Arrays.asList(new Object[] { train.getName(), Bundle.getMessage("Cars") }));
157                    train.getRoute().getLocationsBySequenceList()
158                            .forEach(rl -> line.add(train.getNumberCarsInTrain(rl)));
159                    fileOut.printRecord(line);
160
161                    line.clear();
162                    line.addAll(Arrays.asList(new Object[] { train.getName(), Bundle.getMessage("csvEmpties") }));
163                    train.getRoute().getLocationsBySequenceList()
164                            .forEach(rl -> line.add(train.getNumberEmptyCarsInTrain(rl)));
165                    fileOut.printRecord(line);
166
167                    line.clear();
168                    line.addAll(Arrays.asList(new Object[] { train.getName(), Bundle.getMessage("Loads") }));
169                    train.getRoute().getLocationsBySequenceList()
170                            .forEach(rl -> line.add(train.getNumberLoadedCarsInTrain(rl)));
171                    fileOut.printRecord(line);
172
173                    fileOut.println();
174                }
175            }
176
177            fileOut.flush();
178            fileOut.close();
179            log.info("Exported {} trains to file {}", count, defaultOperationsFilename());
180            JmriJOptionPane.showMessageDialog(null,
181                    Bundle.getMessage("ExportedTrainsToFile",
182                            count, defaultOperationsFilename()),
183                    Bundle.getMessage("ExportComplete"), JmriJOptionPane.INFORMATION_MESSAGE);
184        } catch (IOException e) {
185            log.error("Can not open export trains CSV file: {}", file.getName());
186            JmriJOptionPane.showMessageDialog(null,
187                    Bundle.getMessage("ExportedTrainsToFile",
188                            0, defaultOperationsFilename()),
189                    Bundle.getMessage("ExportFailed"), JmriJOptionPane.ERROR_MESSAGE);
190        }
191    }
192
193    private String getCarRoadOption(Train train) {
194        String roadOption = Bundle.getMessage("AcceptAll");
195        if (train.getCarRoadOption().equals(Train.INCLUDE_ROADS)) {
196            roadOption = Bundle.getMessage(
197                    "AcceptOnly") + " " + train.getCarRoadNames().length + " " + Bundle.getMessage("Roads");
198        } else if (train.getCarRoadOption().equals(Train.EXCLUDE_ROADS)) {
199            roadOption = Bundle.getMessage(
200                    "Exclude") + " " + train.getCarRoadNames().length + " " + Bundle.getMessage("Roads");
201        }
202        return roadOption;
203    }
204
205    private String getCarRoads(Train train) {
206        if (train.getCarRoadOption().equals(Train.ALL_ROADS)) {
207            return "";
208        } else {
209            return TrainCommon.formatStringToCommaSeparated(train.getCarRoadNames());
210        }
211    }
212    
213    private String getLocoRoadOption(Train train) {
214        String roadOption = Bundle.getMessage("AcceptAll");
215        if (train.getLocoRoadOption().equals(Train.INCLUDE_ROADS)) {
216            roadOption = Bundle.getMessage(
217                    "AcceptOnly") + " " + train.getLocoRoadNames().length + " " + Bundle.getMessage("Roads");
218        } else if (train.getLocoRoadOption().equals(Train.EXCLUDE_ROADS)) {
219            roadOption = Bundle.getMessage(
220                    "Exclude") + " " + train.getLocoRoadNames().length + " " + Bundle.getMessage("Roads");
221        }
222        return roadOption;
223    }
224
225    private String getLocoRoads(Train train) {
226        if (train.getLocoRoadOption().equals(Train.ALL_ROADS)) {
227            return "";
228        } else {
229            return TrainCommon.formatStringToCommaSeparated(train.getLocoRoadNames());
230        }
231    }
232
233    private String getLoadOption(Train train) {
234        String loadOption = Bundle.getMessage("AcceptAll");
235        if (train.getLoadOption().equals(Train.INCLUDE_LOADS)) {
236            loadOption = Bundle.getMessage(
237                    "AcceptOnly") + " " + train.getLoadNames().length + " " + Bundle.getMessage("Loads");
238        } else if (train.getLoadOption().equals(Train.EXCLUDE_LOADS)) {
239            loadOption = Bundle.getMessage(
240                    "Exclude") + " " + train.getLoadNames().length + " " + Bundle.getMessage("Loads");
241        }
242        return loadOption;
243    }
244
245    private String getLoads(Train train) {
246        if (train.getLoadOption().equals(Train.ALL_LOADS)) {
247            return "";
248        } else {
249            return TrainCommon.formatStringToCommaSeparated(train.getLoadNames());
250        }
251    }
252
253    private String getOwnerOption(Train train) {
254        String ownerOption = Bundle.getMessage("AcceptAll");
255        if (train.getOwnerOption().equals(Train.INCLUDE_OWNERS)) {
256            ownerOption = Bundle.getMessage(
257                    "AcceptOnly") + " " + train.getOwnerNames().length + " " + Bundle.getMessage("Owners");
258        } else if (train.getOwnerOption().equals(Train.EXCLUDE_OWNERS)) {
259            ownerOption = Bundle.getMessage(
260                    "Exclude") + " " + train.getOwnerNames().length + " " + Bundle.getMessage("Owners");
261        }
262        return ownerOption;
263    }
264
265    private String getOwners(Train train) {
266        if (train.getOwnerOption().equals(Train.ALL_OWNERS)) {
267            return "";
268        } else {
269            return TrainCommon.formatStringToCommaSeparated(train.getOwnerNames());
270        }
271    }
272
273    private String getBuilt(Train train) {
274        if (!train.getBuiltStartYear().equals(Train.NONE) && train.getBuiltEndYear().equals(Train.NONE)) {
275            return Bundle.getMessage("After") + " " + train.getBuiltStartYear();
276        }
277        if (train.getBuiltStartYear().equals(Train.NONE) && !train.getBuiltEndYear().equals(Train.NONE)) {
278            return Bundle.getMessage("Before") + " " + train.getBuiltEndYear();
279        }
280        if (!train.getBuiltStartYear().equals(Train.NONE) && !train.getBuiltEndYear().equals(Train.NONE)) {
281            return Bundle.getMessage("Range") + " " + train.getBuiltStartYear() + ":" + train.getBuiltEndYear();
282        }
283        return "";
284    }
285
286    // Operation files always use the same directory
287    public static String defaultOperationsFilename() {
288        return OperationsSetupXml.getFileLocation() +
289                OperationsSetupXml.getOperationsDirectoryName() +
290                File.separator +
291                getOperationsFileName();
292    }
293
294    public static void setOperationsFileName(String name) {
295        operationsFileName = name;
296    }
297
298    public static String getOperationsFileName() {
299        return operationsFileName;
300    }
301
302    private static String operationsFileName = "ExportOperationsTrainRoster.csv"; // NOI18N
303
304    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ExportTrains.class);
305
306}