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}