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