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