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