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