001package jmri.jmrit.operations.locations.tools;
002
003import java.io.*;
004import java.nio.charset.StandardCharsets;
005import java.util.List;
006
007import org.apache.commons.csv.CSVFormat;
008import org.apache.commons.csv.CSVPrinter;
009
010import jmri.InstanceManager;
011import jmri.jmrit.XmlFile;
012import jmri.jmrit.operations.locations.*;
013import jmri.jmrit.operations.routes.Route;
014import jmri.jmrit.operations.routes.RouteManager;
015import jmri.jmrit.operations.setup.OperationsSetupXml;
016import jmri.jmrit.operations.setup.Setup;
017import jmri.jmrit.operations.trains.Train;
018import jmri.jmrit.operations.trains.TrainManager;
019import jmri.util.swing.JmriJOptionPane;
020
021/**
022 * Exports the location roster into a comma delimited file (CSV).
023 * Keep ImportLocations.java in sync with export
024 *
025 * @author Daniel Boudreau Copyright (C) 2018, 2023
026 *
027 */
028public class ExportLocations extends XmlFile {
029
030    TrainManager trainManager = InstanceManager.getDefault(TrainManager.class);
031    RouteManager routeManager = InstanceManager.getDefault(RouteManager.class);
032    LocationManager locationManager = InstanceManager.getDefault(LocationManager.class);
033
034    public void writeOperationsLocationFile() {
035        makeBackupFile(defaultOperationsFilename());
036        try {
037            if (!checkFile(defaultOperationsFilename())) {
038                // The file does not exist, create it before writing
039                java.io.File file = new java.io.File(defaultOperationsFilename());
040                java.io.File parentDir = file.getParentFile();
041                if (!parentDir.exists()) {
042                    if (!parentDir.mkdir()) {
043                        log.error("Directory wasn't created");
044                    }
045                }
046                if (file.createNewFile()) {
047                    log.debug("File created");
048                }
049            }
050            writeFile(defaultOperationsFilename());
051        } catch (IOException e) {
052            log.error("Exception while writing the new CSV operations file, may not be complete: {}",
053                    e.getLocalizedMessage());
054        }
055    }
056
057    public void writeFile(String name) {
058        log.debug("writeFile {}", name);
059        File file = findFile(name);
060        if (file == null) {
061            file = new File(name);
062        }
063
064        try (CSVPrinter fileOut = new CSVPrinter(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8)),
065                CSVFormat.DEFAULT)) {
066            // create header
067            fileOut.printRecord(Bundle.getMessage("Location"),
068                    Bundle.getMessage("Track"),
069                    Bundle.getMessage("Type"),
070                    Bundle.getMessage("Length"),
071                    Bundle.getMessage("Division"),
072                    Bundle.getMessage("ServicedByTrains"),
073                    Bundle.getMessage("RollingStock"),
074                    Bundle.getMessage("ServiceOrder"),
075                    Bundle.getMessage("RoadOption"),
076                    Bundle.getMessage("Roads"),
077                    Bundle.getMessage("LoadOption"),
078                    Bundle.getMessage("Loads"),
079                    Bundle.getMessage("ShipLoadOption"),
080                    Bundle.getMessage("Ships"),
081                    Bundle.getMessage("SetOutRestrictions"),
082                    Bundle.getMessage("Restrictions"),
083                    Bundle.getMessage("PickUpRestrictions"),
084                    Bundle.getMessage("Restrictions"),
085                    Bundle.getMessage("ScheduleName"),
086                    Bundle.getMessage("ScheduleMode"),
087                    Bundle.getMessage("PercentStaging"),
088                    Bundle.getMessage("AlternateTrack"),
089                    Bundle.getMessage("PoolName"),
090                    Bundle.getMessage("Minimum"),
091                    Bundle.getMessage("TitleTrackBlockingOrder"),
092                    Bundle.getMessage("MenuItemPlannedPickups"),
093                    Bundle.getMessage("MenuItemDestinations"),
094                    Bundle.getMessage("Destinations"),
095                    Bundle.getMessage("HoldCarsWithCustomLoads"),
096                    Bundle.getMessage("DisableLoadChange"),
097                    Bundle.getMessage("SwapCarLoads"),
098                    Bundle.getMessage("EmptyDefaultCarLoads"),
099                    Bundle.getMessage("EmptyCarLoads"),
100                    Bundle.getMessage("LoadCarLoads"),
101                    Bundle.getMessage("LoadAnyCarLoads"),
102                    Bundle.getMessage("LoadsStaging"),
103                    Bundle.getMessage("BlockCars"),
104                    Bundle.getMessage("Comment"),
105                    Bundle.getMessage("CommentBoth"),
106                    Bundle.getMessage("CommentPickup"),
107                    Bundle.getMessage("CommentSetout"));
108
109            List<Location> locations = locationManager.getLocationsByNameList();
110            for (Location location : locations) {
111                for (Track track : location.getTracksByNameList(null)) {
112
113                    StringBuilder trainDirections = new StringBuilder();
114                    String[] directions = Setup.getDirectionStrings(
115                            Setup.getTrainDirection() & location.getTrainDirections() & track.getTrainDirections());
116                    for (String dir : directions) {
117                        if (dir != null) {
118                            trainDirections.append(dir).append("; ");
119                        }
120                    }
121
122                    StringBuilder rollingStockNames = new StringBuilder();
123                    for (String rollingStockName : track.getTypeNames()) {
124                        rollingStockNames.append(rollingStockName).append("; ");
125                    }
126
127                    StringBuilder roadNames = new StringBuilder();
128                    if (!track.getRoadOption().equals(Track.ALL_ROADS)) {
129                        for (String roadName : track.getRoadNames()) {
130                            roadNames.append(roadName).append("; ");
131                        }
132                    }
133
134                    StringBuilder loadNames = new StringBuilder();
135                    if (!track.getLoadOption().equals(Track.ALL_LOADS)) {
136                        for (String loadName : track.getLoadNames()) {
137                            loadNames.append(loadName).append("; ");
138                        }
139                    }
140
141                    StringBuilder shipNames = new StringBuilder();
142                    if (!track.getShipLoadOption().equals(Track.ALL_LOADS)) {
143                        for (String shipName : track.getShipLoadNames()) {
144                            shipNames.append(shipName).append("; ");
145                        }
146                    }
147
148                    String setOutRestriction = Bundle.getMessage("None");
149                    switch (track.getDropOption()) {
150                        case Track.TRAINS:
151                            setOutRestriction = Bundle.getMessage("Trains");
152                            break;
153                        case Track.ROUTES:
154                            setOutRestriction = Bundle.getMessage("Routes");
155                            break;
156                        case Track.EXCLUDE_TRAINS:
157                            setOutRestriction = Bundle.getMessage("ExcludeTrains");
158                            break;
159                        case Track.EXCLUDE_ROUTES:
160                            setOutRestriction = Bundle.getMessage("ExcludeRoutes");
161                            break;
162                        default:
163                            break;
164                    }
165
166                    StringBuilder setOutRestrictions = new StringBuilder();
167                    if (track.getDropOption().equals(Track.TRAINS) || track.getDropOption().equals(Track.EXCLUDE_TRAINS)) {
168                        for (String id : track.getDropIds()) {
169                            Train train = trainManager.getTrainById(id);
170                            if (train != null) {
171                                setOutRestrictions.append(train.getName()).append("; ");
172                            }
173                        }
174                    }
175                    if (track.getDropOption().equals(Track.ROUTES) || track.getDropOption().equals(Track.EXCLUDE_ROUTES)) {
176                        for (String id : track.getDropIds()) {
177                            Route route = routeManager.getRouteById(id);
178                            if (route != null) {
179                                setOutRestrictions.append(route.getName()).append("; ");
180                            }
181                        }
182                    }
183
184                    String pickUpRestriction = Bundle.getMessage("None");
185                    switch (track.getPickupOption()) {
186                        case Track.TRAINS:
187                            pickUpRestriction = Bundle.getMessage("Trains");
188                            break;
189                        case Track.ROUTES:
190                            pickUpRestriction = Bundle.getMessage("Routes");
191                            break;
192                        case Track.EXCLUDE_TRAINS:
193                            pickUpRestriction = Bundle.getMessage("ExcludeTrains");
194                            break;
195                        case Track.EXCLUDE_ROUTES:
196                            pickUpRestriction = Bundle.getMessage("ExcludeRoutes");
197                            break;
198                        default:
199                            break;
200                    }
201
202                    StringBuilder pickUpRestrictions = new StringBuilder();
203                    if (track.getPickupOption().equals(Track.TRAINS)
204                            || track.getPickupOption().equals(Track.EXCLUDE_TRAINS)) {
205                        for (String id : track.getPickupIds()) {
206                            Train train = trainManager.getTrainById(id);
207                            if (train != null) {
208                                pickUpRestrictions.append(train.getName()).append("; ");
209                            }
210                        }
211                    }
212                    if (track.getPickupOption().equals(Track.ROUTES)
213                            || track.getPickupOption().equals(Track.EXCLUDE_ROUTES)) {
214                        for (String id : track.getPickupIds()) {
215                            Route route = routeManager.getRouteById(id);
216                            if (route != null) {
217                                pickUpRestrictions.append(route.getName()).append("; ");
218                            }
219                        }
220                    }
221
222                    String alternateTrackName = "";
223                    if (track.getAlternateTrack() != null) {
224                        alternateTrackName = track.getAlternateTrack().getName();
225                    }
226                    if (track.isAlternate()) {
227                        alternateTrackName = Bundle.getMessage("ButtonYes");
228                    }
229
230                    StringBuilder destinationNames = new StringBuilder();
231                    for (String id : track.getDestinationIds()) {
232                        Location destination = locationManager.getLocationById(id);
233                        if (destination != null) {
234                            destinationNames.append(destination.getName()).append("; ");
235                        }
236                    }
237
238                    fileOut.printRecord(location.getName(),
239                            track.getName(),
240                            track.getTrackTypeName(),
241                            track.getLength(),
242                            track.getDivision(),
243                            trainDirections.toString(),
244                            rollingStockNames.toString(),
245                            track.getServiceOrder(),
246                            track.getRoadOptionString(),
247                            roadNames.toString(),
248                            track.getLoadOptionString(),
249                            loadNames.toString(),
250                            track.getShipLoadOptionString(),
251                            shipNames.toString(),
252                            setOutRestriction,
253                            setOutRestrictions.toString(),
254                            pickUpRestriction,
255                            pickUpRestrictions.toString(),
256                            track.getScheduleName(),
257                            track.getScheduleModeName(),
258                            track.getReservationFactor(),
259                            alternateTrackName,
260                            track.getPoolName(),
261                            track.getMinimumLength(),
262                            track.getBlockingOrder(),
263                            track.getIgnoreUsedLengthPercentage(),
264                            Bundle.getMessage(track.getDestinationOption().equals(Track.ALL_DESTINATIONS) ? "All" : "Include"),
265                            destinationNames.toString(),
266                            (track.isHoldCarsWithCustomLoadsEnabled() ? Bundle.getMessage("ButtonYes") : ""),
267                            (track.isDisableLoadChangeEnabled() ? Bundle.getMessage("ButtonYes") : ""),
268                            (track.isLoadSwapEnabled() ? Bundle.getMessage("ButtonYes") : ""),
269                            (track.isLoadEmptyEnabled() ? Bundle.getMessage("ButtonYes") : ""),
270                            (track.isRemoveCustomLoadsEnabled() ? Bundle.getMessage("ButtonYes") : ""),
271                            (track.isAddCustomLoadsEnabled() ? Bundle.getMessage("ButtonYes") : ""),
272                            (track.isAddCustomLoadsAnySpurEnabled() ? Bundle.getMessage("ButtonYes") : ""),
273                            (track.isAddCustomLoadsAnyStagingTrackEnabled() ? Bundle.getMessage("ButtonYes") : ""),
274                            (track.isBlockCarsEnabled() ? Bundle.getMessage("ButtonYes") : ""),
275                            // strip line feeds, parse EOL error when importing
276                            track.getComment().replace('\n', ' '),
277                            track.getCommentBoth().replace('\n', ' '),
278                            track.getCommentPickup().replace('\n', ' '),
279                            track.getCommentSetout().replace('\n', ' '));
280                }
281            }
282            fileOut.flush();
283            fileOut.close();
284            log.info("Exported {} locations to file {}", locations.size(), defaultOperationsFilename());
285            JmriJOptionPane.showMessageDialog(null,
286                    Bundle.getMessage("ExportedLocationsToFile", locations.size(), defaultOperationsFilename()),
287                    Bundle.getMessage("ExportComplete"), JmriJOptionPane.INFORMATION_MESSAGE);
288        } catch (IOException e) {
289            log.error("Can not open export locations CSV file: {}", e.getLocalizedMessage());
290            JmriJOptionPane.showMessageDialog(null,
291                    Bundle.getMessage("ExportedLocationsToFile", 0, defaultOperationsFilename()),
292                    Bundle.getMessage("ExportFailed"), JmriJOptionPane.ERROR_MESSAGE);
293        }
294    }
295
296    // Operation files always use the same directory
297    public static String defaultOperationsFilename() {
298        return OperationsSetupXml.getFileLocation()
299                + OperationsSetupXml.getOperationsDirectoryName()
300                + File.separator
301                + getOperationsFileName();
302    }
303
304    public static void setOperationsFileName(String name) {
305        operationsFileName = name;
306    }
307
308    public static String getOperationsFileName() {
309        return operationsFileName;
310    }
311
312    private static String operationsFileName = "ExportOperationsLocationRoster.csv"; // NOI18N
313
314    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ExportLocations.class);
315
316}