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: {}",
050                    e.getLocalizedMessage());
051        }
052    }
053
054    public void writeFile(String name) {
055        log.debug("writeFile {}", name);
056        // This is taken in large part from "Java and XML" page 368
057        File file = findFile(name);
058        if (file == null) {
059            file = new File(name);
060        }
061
062        try (CSVPrinter fileOut = new CSVPrinter(
063                new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8)),
064                CSVFormat.DEFAULT)) {
065
066            // create header
067            fileOut.printRecord(Bundle.getMessage("Name"), Bundle.getMessage("Description"), Bundle.getMessage("Time"),
068                    Bundle.getMessage("Route"), Bundle.getMessage("Departs"), Bundle.getMessage("Terminates"),
069                    Bundle.getMessage("Status"), Bundle.getMessage("Comment"), Bundle.getMessage("LocoTypes"),
070                    Bundle.getMessage("CarTypes"), Bundle.getMessage("RoadOption"), Bundle.getMessage("RoadsCar"),
071                    Bundle.getMessage("RoadOption"), Bundle.getMessage("RoadsCaboose"), Bundle.getMessage("RoadOption"),
072                    Bundle.getMessage("RoadsLoco"),
073                    Bundle.getMessage("LoadOption"), Bundle.getMessage("Loads"), Bundle.getMessage("OwnerOption"),
074                    Bundle.getMessage("Owners"), Bundle.getMessage("Built"),
075                    Bundle.getMessage("NormalModeWhenBuilding"), Bundle.getMessage("AllowCarsToReturn"),
076                    Bundle.getMessage("AllowThroughCars"), Bundle.getMessage("SendCustomToStaging"),
077                    Bundle.getMessage("SendToTerminal", ""),
078                    Bundle.getMessage("AllowLocalMoves"), Bundle.getMessage("ServiceAllCars"),
079                    Bundle.getMessage("BuildConsist"));
080
081            int count = 0;
082
083            for (Train train : InstanceManager.getDefault(TrainManager.class).getTrainsByTimeList()) {
084                if (!train.isBuildEnabled()) {
085                    continue;
086                }
087                count++;
088                String routeName = "";
089                if (train.getRoute() != null) {
090                    routeName = train.getRoute().getName();
091                }
092                fileOut.printRecord(train.getName(), train.getDescription(), train.getDepartureTime(), routeName,
093                        train.getTrainDepartsName(), train.getTrainTerminatesName(), train.getStatus(),
094                        train.getComment(), TrainCommon.formatStringToCommaSeparated(train.getLocoTypeNames()),
095                        TrainCommon.formatStringToCommaSeparated(train.getCarTypeNames()), getCarRoadOption(train),
096                        getCarRoads(train), getCabooseRoadOption(train), getCabooseRoads(train),
097                        getLocoRoadOption(train), getLocoRoads(train), getLoadOption(train),
098                        getLoads(train), getOwnerOption(train), getOwners(train), getBuilt(train),
099                        train.isBuildTrainNormalEnabled() ? Bundle.getMessage("ButtonYes") : "",
100                        train.isAllowReturnToStagingEnabled() ? Bundle.getMessage("ButtonYes") : "",
101                        train.isAllowThroughCarsEnabled() ? Bundle.getMessage("ButtonYes") : "",
102                        train.isSendCarsWithCustomLoadsToStagingEnabled() ? Bundle.getMessage("ButtonYes") : "",
103                        train.isSendCarsToTerminalEnabled() ? Bundle.getMessage("ButtonYes") : "",
104                        train.isAllowLocalMovesEnabled() ? Bundle.getMessage("ButtonYes") : "",
105                        train.isServiceAllCarsWithFinalDestinationsEnabled() ? Bundle.getMessage("ButtonYes") : "",
106                        train.isBuildConsistEnabled() ? Bundle.getMessage("ButtonYes") : "");
107            }
108
109            fileOut.println();
110            // second create header for built trains
111            fileOut.printRecord(Bundle.getMessage("Name"), Bundle.getMessage("csvParameters"),
112                    Bundle.getMessage("Attributes"));
113
114            for (Train train : InstanceManager.getDefault(TrainManager.class).getTrainsByTimeList()) {
115                if (!train.isBuildEnabled()) {
116                    continue;
117                }
118
119                if (train.isBuilt() && train.getRoute() != null) {
120                    ArrayList<Object> line = new ArrayList<>();
121                    line.addAll(Arrays.asList(new Object[] { train.getName(), Bundle.getMessage("Route") }));
122                    train.getRoute().getLocationsBySequenceList().forEach(rl -> line.add(rl.getName()));
123                    fileOut.printRecord(line);
124
125                    line.clear();
126                    line.addAll(Arrays.asList(new Object[] { train.getName(), Bundle.getMessage("csvArrivalTime") }));
127                    train.getRoute().getLocationsBySequenceList()
128                            .forEach(rl -> line.add(train.getExpectedArrivalTime(rl)));
129                    fileOut.printRecord(line);
130
131                    line.clear();
132                    line.addAll(Arrays.asList(new Object[] { train.getName(), Bundle.getMessage("csvDepartureTime") }));
133                    train.getRoute().getLocationsBySequenceList()
134                            .forEach(rl -> line.add(train.getExpectedDepartureTime(rl)));
135                    fileOut.printRecord(line);
136
137                    line.clear();
138                    line.addAll(
139                            Arrays.asList(new Object[] { train.getName(), Bundle.getMessage("csvTrainDirection") }));
140                    train.getRoute().getLocationsBySequenceList().forEach(rl -> line.add(rl.getTrainDirectionString()));
141                    fileOut.printRecord(line);
142
143                    line.clear();
144                    line.addAll(Arrays.asList(new Object[] { train.getName(), Bundle.getMessage("csvTrainWeight") }));
145                    train.getRoute().getLocationsBySequenceList().forEach(rl -> line.add(train.getTrainWeight(rl)));
146                    fileOut.printRecord(line);
147
148                    line.clear();
149                    line.addAll(Arrays.asList(new Object[] { train.getName(), Bundle.getMessage("csvTrainLength") }));
150                    train.getRoute().getLocationsBySequenceList().forEach(rl -> line.add(train.getTrainLength(rl)));
151                    fileOut.printRecord(line);
152
153                    line.clear();
154                    line.addAll(Arrays.asList(new Object[] { train.getName(), Bundle.getMessage("Engine") }));
155                    train.getRoute().getLocationsBySequenceList().forEach(rl -> line.add(train.getLeadEngine(rl)));
156                    fileOut.printRecord(line);
157
158                    line.clear();
159                    line.addAll(Arrays.asList(new Object[] { train.getName(), Bundle.getMessage("Cars") }));
160                    train.getRoute().getLocationsBySequenceList()
161                            .forEach(rl -> line.add(train.getNumberCarsInTrain(rl)));
162                    fileOut.printRecord(line);
163
164                    line.clear();
165                    line.addAll(Arrays.asList(new Object[] { train.getName(), Bundle.getMessage("csvEmpties") }));
166                    train.getRoute().getLocationsBySequenceList()
167                            .forEach(rl -> line.add(train.getNumberEmptyCarsInTrain(rl)));
168                    fileOut.printRecord(line);
169
170                    line.clear();
171                    line.addAll(Arrays.asList(new Object[] { train.getName(), Bundle.getMessage("Loads") }));
172                    train.getRoute().getLocationsBySequenceList()
173                            .forEach(rl -> line.add(train.getNumberLoadedCarsInTrain(rl)));
174                    fileOut.printRecord(line);
175
176                    fileOut.println();
177                }
178            }
179
180            fileOut.flush();
181            fileOut.close();
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}