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