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