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