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("TitleTrackBlockingOrder"), 095 Bundle.getMessage("MenuItemPlannedPickups"), 096 Bundle.getMessage("MenuItemDestinations"), 097 Bundle.getMessage("Destinations"), 098 Bundle.getMessage("HoldCarsWithCustomLoads"), 099 Bundle.getMessage("DisableLoadChange"), 100 Bundle.getMessage("SwapCarLoads"), 101 Bundle.getMessage("EmptyDefaultCarLoads"), 102 Bundle.getMessage("EmptyCarLoads"), 103 Bundle.getMessage("LoadCarLoads"), 104 Bundle.getMessage("LoadAnyCarLoads"), 105 Bundle.getMessage("LoadsStaging"), 106 Bundle.getMessage("BlockCars"), 107 Bundle.getMessage("Comment"), 108 Bundle.getMessage("CommentBoth"), 109 Bundle.getMessage("CommentPickup"), 110 Bundle.getMessage("CommentSetout")); 111 112 List<Location> locations = locationManager.getLocationsByNameList(); 113 for (Location location : locations) { 114 for (Track track : location.getTracksByNameList(null)) { 115 116 StringBuilder trainDirections = new StringBuilder(); 117 String[] directions = Setup.getDirectionStrings( 118 Setup.getTrainDirection() & location.getTrainDirections() & track.getTrainDirections()); 119 for (String dir : directions) { 120 if (dir != null) { 121 trainDirections.append(dir).append("; "); 122 } 123 } 124 125 StringBuilder rollingStockNames = new StringBuilder(); 126 for (String rollingStockName : track.getTypeNames()) { 127 rollingStockNames.append(rollingStockName).append("; "); 128 } 129 130 StringBuilder roadNames = new StringBuilder(); 131 if (!track.getRoadOption().equals(Track.ALL_ROADS)) { 132 for (String roadName : track.getRoadNames()) { 133 roadNames.append(roadName).append("; "); 134 } 135 } 136 137 StringBuilder loadNames = new StringBuilder(); 138 if (!track.getLoadOption().equals(Track.ALL_LOADS)) { 139 for (String loadName : track.getLoadNames()) { 140 loadNames.append(loadName).append("; "); 141 } 142 } 143 144 StringBuilder shipNames = new StringBuilder(); 145 if (!track.getShipLoadOption().equals(Track.ALL_LOADS)) { 146 for (String shipName : track.getShipLoadNames()) { 147 shipNames.append(shipName).append("; "); 148 } 149 } 150 151 String setOutRestriction = Bundle.getMessage("None"); 152 switch (track.getDropOption()) { 153 case Track.TRAINS: 154 setOutRestriction = Bundle.getMessage("Trains"); 155 break; 156 case Track.ROUTES: 157 setOutRestriction = Bundle.getMessage("Routes"); 158 break; 159 case Track.EXCLUDE_TRAINS: 160 setOutRestriction = Bundle.getMessage("ExcludeTrains"); 161 break; 162 case Track.EXCLUDE_ROUTES: 163 setOutRestriction = Bundle.getMessage("ExcludeRoutes"); 164 break; 165 default: 166 break; 167 } 168 169 StringBuilder setOutRestrictions = new StringBuilder(); 170 if (track.getDropOption().equals(Track.TRAINS) || track.getDropOption().equals(Track.EXCLUDE_TRAINS)) { 171 for (String id : track.getDropIds()) { 172 Train train = trainManager.getTrainById(id); 173 if (train != null) { 174 setOutRestrictions.append(train.getName()).append("; "); 175 } 176 } 177 } 178 if (track.getDropOption().equals(Track.ROUTES) || track.getDropOption().equals(Track.EXCLUDE_ROUTES)) { 179 for (String id : track.getDropIds()) { 180 Route route = routeManager.getRouteById(id); 181 if (route != null) { 182 setOutRestrictions.append(route.getName()).append("; "); 183 } 184 } 185 } 186 187 String pickUpRestriction = Bundle.getMessage("None"); 188 switch (track.getPickupOption()) { 189 case Track.TRAINS: 190 pickUpRestriction = Bundle.getMessage("Trains"); 191 break; 192 case Track.ROUTES: 193 pickUpRestriction = Bundle.getMessage("Routes"); 194 break; 195 case Track.EXCLUDE_TRAINS: 196 pickUpRestriction = Bundle.getMessage("ExcludeTrains"); 197 break; 198 case Track.EXCLUDE_ROUTES: 199 pickUpRestriction = Bundle.getMessage("ExcludeRoutes"); 200 break; 201 default: 202 break; 203 } 204 205 StringBuilder pickUpRestrictions = new StringBuilder(); 206 if (track.getPickupOption().equals(Track.TRAINS) 207 || track.getPickupOption().equals(Track.EXCLUDE_TRAINS)) { 208 for (String id : track.getPickupIds()) { 209 Train train = trainManager.getTrainById(id); 210 if (train != null) { 211 pickUpRestrictions.append(train.getName()).append("; "); 212 } 213 } 214 } 215 if (track.getPickupOption().equals(Track.ROUTES) 216 || track.getPickupOption().equals(Track.EXCLUDE_ROUTES)) { 217 for (String id : track.getPickupIds()) { 218 Route route = routeManager.getRouteById(id); 219 if (route != null) { 220 pickUpRestrictions.append(route.getName()).append("; "); 221 } 222 } 223 } 224 225 String alternateTrackName = ""; 226 if (track.getAlternateTrack() != null) { 227 alternateTrackName = track.getAlternateTrack().getName(); 228 } 229 if (track.isAlternate()) { 230 alternateTrackName = Bundle.getMessage("ButtonYes"); 231 } 232 233 StringBuilder destinationNames = new StringBuilder(); 234 for (String id : track.getDestinationIds()) { 235 Location destination = locationManager.getLocationById(id); 236 if (destination != null) { 237 destinationNames.append(destination.getName()).append("; "); 238 } 239 } 240 241 fileOut.printRecord(location.getName(), 242 track.getName(), 243 track.getTrackTypeName(), 244 track.getLength(), 245 track.getUsedLength(), 246 track.getNumberCars(), 247 track.getNumberEngines(), 248 track.getMoves(), 249 track.getDivision(), 250 trainDirections.toString(), 251 rollingStockNames.toString(), 252 track.getServiceOrder(), 253 track.getRoadOptionString(), 254 roadNames.toString(), 255 track.getLoadOptionString(), 256 loadNames.toString(), 257 track.getShipLoadOptionString(), 258 shipNames.toString(), 259 setOutRestriction, 260 setOutRestrictions.toString(), 261 pickUpRestriction, 262 pickUpRestrictions.toString(), 263 track.getScheduleName(), 264 track.getScheduleModeName(), 265 track.getReservationFactor(), 266 alternateTrackName, 267 track.getPoolName(), 268 track.getMinimumLength(), 269 track.getBlockingOrder(), 270 track.getIgnoreUsedLengthPercentage(), 271 Bundle.getMessage(track.getDestinationOption().equals(Track.ALL_DESTINATIONS) ? "All" : "Include"), 272 destinationNames.toString(), 273 (track.isHoldCarsWithCustomLoadsEnabled() ? Bundle.getMessage("ButtonYes") : ""), 274 (track.isDisableLoadChangeEnabled() ? Bundle.getMessage("ButtonYes") : ""), 275 (track.isLoadSwapEnabled() ? Bundle.getMessage("ButtonYes") : ""), 276 (track.isLoadEmptyEnabled() ? Bundle.getMessage("ButtonYes") : ""), 277 (track.isRemoveCustomLoadsEnabled() ? Bundle.getMessage("ButtonYes") : ""), 278 (track.isAddCustomLoadsEnabled() ? Bundle.getMessage("ButtonYes") : ""), 279 (track.isAddCustomLoadsAnySpurEnabled() ? Bundle.getMessage("ButtonYes") : ""), 280 (track.isAddCustomLoadsAnyStagingTrackEnabled() ? Bundle.getMessage("ButtonYes") : ""), 281 (track.isBlockCarsEnabled() ? Bundle.getMessage("ButtonYes") : ""), 282 // strip line feeds, parse EOL error when importing 283 track.getComment().replace('\n', ' '), 284 track.getCommentBoth().replace('\n', ' '), 285 track.getCommentPickup().replace('\n', ' '), 286 track.getCommentSetout().replace('\n', ' ')); 287 } 288 } 289 fileOut.flush(); 290 fileOut.close(); 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}