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("Done"),
071                    Bundle.getMessage("Route"), Bundle.getMessage("Departs"), Bundle.getMessage("Terminates"),
072                    Bundle.getMessage("Status"), Bundle.getMessage("Comment"), Bundle.getMessage("LocoTypes"),
073                    Bundle.getMessage("CarTypes"), Bundle.getMessage("RoadOption"), Bundle.getMessage("RoadsCar"),
074                    Bundle.getMessage("RoadOption"), Bundle.getMessage("RoadsCaboose"), Bundle.getMessage("RoadOption"),
075                    Bundle.getMessage("RoadsLoco"),
076                    Bundle.getMessage("LoadOption"), Bundle.getMessage("Loads"), Bundle.getMessage("OwnerOption"),
077                    Bundle.getMessage("Owners"), Bundle.getMessage("Built"),
078                    Bundle.getMessage("NormalModeWhenBuilding"), Bundle.getMessage("AllowCarsToReturn"),
079                    Bundle.getMessage("AllowThroughCars"), Bundle.getMessage("SendCustomToStaging"),
080                    Bundle.getMessage("SendToTerminal", ""),
081                    Bundle.getMessage("AllowLocalMoves"), Bundle.getMessage("ServiceAllCars"),
082                    Bundle.getMessage("BuildConsist"));
083
084            int count = 0;
085
086            for (Train train : InstanceManager.getDefault(TrainManager.class).getTrainsByTimeList()) {
087                if (!train.isBuildEnabled()) {
088                    continue;
089                }
090                count++;
091                String routeName = "";
092                if (train.getRoute() != null) {
093                    routeName = train.getRoute().getName();
094                }
095                fileOut.printRecord(train.getName(), train.getDescription(), train.getDepartureTime(), 
096                        train.getExpectedDepartureTime(train.getTrainTerminatesRouteLocation(), true), routeName,
097                        train.getTrainDepartsName(), train.getTrainTerminatesName(), train.getStatus(),
098                        train.getComment(), TrainCommon.formatStringToCommaSeparated(train.getLocoTypeNames()),
099                        TrainCommon.formatStringToCommaSeparated(train.getCarTypeNames()), getCarRoadOption(train),
100                        getCarRoads(train), getCabooseRoadOption(train), getCabooseRoads(train),
101                        getLocoRoadOption(train), getLocoRoads(train), getLoadOption(train),
102                        getLoads(train), getOwnerOption(train), getOwners(train), getBuilt(train),
103                        train.isBuildTrainNormalEnabled() ? Bundle.getMessage("ButtonYes") : "",
104                        train.isAllowReturnToStagingEnabled() ? Bundle.getMessage("ButtonYes") : "",
105                        train.isAllowThroughCarsEnabled() ? Bundle.getMessage("ButtonYes") : "",
106                        train.isSendCarsWithCustomLoadsToStagingEnabled() ? Bundle.getMessage("ButtonYes") : "",
107                        train.isSendCarsToTerminalEnabled() ? Bundle.getMessage("ButtonYes") : "",
108                        train.isAllowLocalMovesEnabled() ? Bundle.getMessage("ButtonYes") : "",
109                        train.isServiceAllCarsWithFinalDestinationsEnabled() ? Bundle.getMessage("ButtonYes") : "",
110                        train.isBuildConsistEnabled() ? Bundle.getMessage("ButtonYes") : "");
111            }
112
113            fileOut.println();
114            // second create header for built trains
115            fileOut.printRecord(Bundle.getMessage("Name"), Bundle.getMessage("csvParameters"),
116                    Bundle.getMessage("Attributes"));
117
118            for (Train train : InstanceManager.getDefault(TrainManager.class).getTrainsByTimeList()) {
119                if (!train.isBuildEnabled()) {
120                    continue;
121                }
122
123                if (train.isBuilt() && train.getRoute() != null) {
124                    ArrayList<Object> line = new ArrayList<>();
125                    line.addAll(Arrays.asList(new Object[] { train.getName(), Bundle.getMessage("Route") }));
126                    train.getRoute().getLocationsBySequenceList().forEach(rl -> line.add(rl.getName()));
127                    fileOut.printRecord(line);
128
129                    line.clear();
130                    line.addAll(Arrays.asList(new Object[] { train.getName(), Bundle.getMessage("csvArrivalTime") }));
131                    train.getRoute().getLocationsBySequenceList()
132                            .forEach(rl -> line.add(train.getExpectedArrivalTime(rl)));
133                    fileOut.printRecord(line);
134
135                    line.clear();
136                    line.addAll(Arrays.asList(new Object[] { train.getName(), Bundle.getMessage("csvDepartureTime") }));
137                    train.getRoute().getLocationsBySequenceList()
138                            .forEach(rl -> line.add(train.getExpectedDepartureTime(rl)));
139                    fileOut.printRecord(line);
140
141                    line.clear();
142                    line.addAll(
143                            Arrays.asList(new Object[] { train.getName(), Bundle.getMessage("csvTrainDirection") }));
144                    train.getRoute().getLocationsBySequenceList().forEach(rl -> line.add(rl.getTrainDirectionString()));
145                    fileOut.printRecord(line);
146
147                    line.clear();
148                    line.addAll(Arrays.asList(new Object[] { train.getName(), Bundle.getMessage("csvTrainWeight") }));
149                    train.getRoute().getLocationsBySequenceList().forEach(rl -> line.add(train.getTrainWeight(rl)));
150                    fileOut.printRecord(line);
151
152                    line.clear();
153                    line.addAll(Arrays.asList(new Object[] { train.getName(), Bundle.getMessage("csvTrainLength") }));
154                    train.getRoute().getLocationsBySequenceList().forEach(rl -> line.add(train.getTrainLength(rl)));
155                    fileOut.printRecord(line);
156
157                    line.clear();
158                    line.addAll(Arrays.asList(new Object[] { train.getName(), Bundle.getMessage("Engine") }));
159                    train.getRoute().getLocationsBySequenceList().forEach(rl -> line.add(train.getLeadEngine(rl)));
160                    fileOut.printRecord(line);
161
162                    line.clear();
163                    line.addAll(Arrays.asList(new Object[] { train.getName(), Bundle.getMessage("Cars") }));
164                    train.getRoute().getLocationsBySequenceList()
165                            .forEach(rl -> line.add(train.getNumberCarsInTrain(rl)));
166                    fileOut.printRecord(line);
167
168                    line.clear();
169                    line.addAll(Arrays.asList(new Object[] { train.getName(), Bundle.getMessage("csvEmpties") }));
170                    train.getRoute().getLocationsBySequenceList()
171                            .forEach(rl -> line.add(train.getNumberEmptyCarsInTrain(rl)));
172                    fileOut.printRecord(line);
173
174                    line.clear();
175                    line.addAll(Arrays.asList(new Object[] { train.getName(), Bundle.getMessage("Loads") }));
176                    train.getRoute().getLocationsBySequenceList()
177                            .forEach(rl -> line.add(train.getNumberLoadedCarsInTrain(rl)));
178                    fileOut.printRecord(line);
179
180                    fileOut.println();
181                }
182            }
183
184            log.info("Exported {} trains to file {}", count, defaultOperationsFilename());
185            JmriJOptionPane.showMessageDialog(null,
186                    Bundle.getMessage("ExportedTrainsToFile",
187                            count, defaultOperationsFilename()),
188                    Bundle.getMessage("ExportComplete"), JmriJOptionPane.INFORMATION_MESSAGE);
189        } catch (IOException e) {
190            log.error("Can not open export trains CSV file: {}", e.getLocalizedMessage());
191            JmriJOptionPane.showMessageDialog(null,
192                    Bundle.getMessage("ExportedTrainsToFile",
193                            0, defaultOperationsFilename()),
194                    Bundle.getMessage("ExportFailed"), JmriJOptionPane.ERROR_MESSAGE);
195        }
196    }
197
198    private String getCarRoadOption(Train train) {
199        String roadOption = Bundle.getMessage("AcceptAll");
200        if (train.getCarRoadOption().equals(Train.INCLUDE_ROADS)) {
201            roadOption = Bundle.getMessage(
202                    "AcceptOnly") + " " + train.getCarRoadNames().length + " " + Bundle.getMessage("Roads");
203        } else if (train.getCarRoadOption().equals(Train.EXCLUDE_ROADS)) {
204            roadOption = Bundle.getMessage(
205                    "Exclude") + " " + train.getCarRoadNames().length + " " + Bundle.getMessage("Roads");
206        }
207        return roadOption;
208    }
209
210    private String getCarRoads(Train train) {
211        if (train.getCarRoadOption().equals(Train.ALL_ROADS)) {
212            return "";
213        } else {
214            return TrainCommon.formatStringToCommaSeparated(train.getCarRoadNames());
215        }
216    }
217    
218    private String getCabooseRoadOption(Train train) {
219        String roadOption = Bundle.getMessage("AcceptAll");
220        if (train.getCabooseRoadOption().equals(Train.INCLUDE_ROADS)) {
221            roadOption = Bundle.getMessage(
222                    "AcceptOnly") + " " + train.getCabooseRoadNames().length + " " + Bundle.getMessage("Roads");
223        } else if (train.getCabooseRoadOption().equals(Train.EXCLUDE_ROADS)) {
224            roadOption = Bundle.getMessage(
225                    "Exclude") + " " + train.getCabooseRoadNames().length + " " + Bundle.getMessage("Roads");
226        }
227        return roadOption;
228    }
229
230    private String getCabooseRoads(Train train) {
231        if (train.getCabooseRoadOption().equals(Train.ALL_ROADS)) {
232            return "";
233        } else {
234            return TrainCommon.formatStringToCommaSeparated(train.getCabooseRoadNames());
235        }
236    }
237
238    private String getLocoRoadOption(Train train) {
239        String roadOption = Bundle.getMessage("AcceptAll");
240        if (train.getLocoRoadOption().equals(Train.INCLUDE_ROADS)) {
241            roadOption = Bundle.getMessage(
242                    "AcceptOnly") + " " + train.getLocoRoadNames().length + " " + Bundle.getMessage("Roads");
243        } else if (train.getLocoRoadOption().equals(Train.EXCLUDE_ROADS)) {
244            roadOption = Bundle.getMessage(
245                    "Exclude") + " " + train.getLocoRoadNames().length + " " + Bundle.getMessage("Roads");
246        }
247        return roadOption;
248    }
249
250    private String getLocoRoads(Train train) {
251        if (train.getLocoRoadOption().equals(Train.ALL_ROADS)) {
252            return "";
253        } else {
254            return TrainCommon.formatStringToCommaSeparated(train.getLocoRoadNames());
255        }
256    }
257
258    private String getLoadOption(Train train) {
259        String loadOption = Bundle.getMessage("AcceptAll");
260        if (train.getLoadOption().equals(Train.INCLUDE_LOADS)) {
261            loadOption = Bundle.getMessage(
262                    "AcceptOnly") + " " + train.getLoadNames().length + " " + Bundle.getMessage("Loads");
263        } else if (train.getLoadOption().equals(Train.EXCLUDE_LOADS)) {
264            loadOption = Bundle.getMessage(
265                    "Exclude") + " " + train.getLoadNames().length + " " + Bundle.getMessage("Loads");
266        }
267        return loadOption;
268    }
269
270    private String getLoads(Train train) {
271        if (train.getLoadOption().equals(Train.ALL_LOADS)) {
272            return "";
273        } else {
274            return TrainCommon.formatStringToCommaSeparated(train.getLoadNames());
275        }
276    }
277
278    private String getOwnerOption(Train train) {
279        String ownerOption = Bundle.getMessage("AcceptAll");
280        if (train.getOwnerOption().equals(Train.INCLUDE_OWNERS)) {
281            ownerOption = Bundle.getMessage(
282                    "AcceptOnly") + " " + train.getOwnerNames().length + " " + Bundle.getMessage("Owners");
283        } else if (train.getOwnerOption().equals(Train.EXCLUDE_OWNERS)) {
284            ownerOption = Bundle.getMessage(
285                    "Exclude") + " " + train.getOwnerNames().length + " " + Bundle.getMessage("Owners");
286        }
287        return ownerOption;
288    }
289
290    private String getOwners(Train train) {
291        if (train.getOwnerOption().equals(Train.ALL_OWNERS)) {
292            return "";
293        } else {
294            return TrainCommon.formatStringToCommaSeparated(train.getOwnerNames());
295        }
296    }
297
298    private String getBuilt(Train train) {
299        if (!train.getBuiltStartYear().equals(Train.NONE) && train.getBuiltEndYear().equals(Train.NONE)) {
300            return Bundle.getMessage("After") + " " + train.getBuiltStartYear();
301        }
302        if (train.getBuiltStartYear().equals(Train.NONE) && !train.getBuiltEndYear().equals(Train.NONE)) {
303            return Bundle.getMessage("Before") + " " + train.getBuiltEndYear();
304        }
305        if (!train.getBuiltStartYear().equals(Train.NONE) && !train.getBuiltEndYear().equals(Train.NONE)) {
306            return Bundle.getMessage("Range") + " " + train.getBuiltStartYear() + ":" + train.getBuiltEndYear();
307        }
308        return "";
309    }
310
311    // Operation files always use the same directory
312    public static String defaultOperationsFilename() {
313        return OperationsSetupXml.getFileLocation() +
314                OperationsSetupXml.getOperationsDirectoryName() +
315                File.separator +
316                getOperationsFileName();
317    }
318
319    public static void setOperationsFileName(String name) {
320        operationsFileName = name;
321    }
322
323    public static String getOperationsFileName() {
324        return operationsFileName;
325    }
326
327    private static String operationsFileName = "ExportOperationsTrainRoster.csv"; // NOI18N
328
329    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ExportTrains.class);
330
331}