001package jmri.jmrit.operations.locations.tools; 002 003import java.io.*; 004import java.util.Locale; 005 006import jmri.InstanceManager; 007import jmri.jmrit.operations.locations.*; 008import jmri.jmrit.operations.locations.divisions.Division; 009import jmri.jmrit.operations.locations.divisions.DivisionManager; 010import jmri.jmrit.operations.rollingstock.ImportRollingStock; 011import jmri.jmrit.operations.setup.Setup; 012import jmri.util.swing.JmriJOptionPane; 013 014/** 015 * This routine will import Locations from a CSV file into the operations database. The field order is: Location, Track, 016 * Type, Length, Division, Serviced by Trains Traveling, Rolling Stock, Order, Road Option, Roads, Load Option, Loads, 017 * Ship Load Option, Ships, Set Out Restrictions, Restrictions, Pick up Restrictions, Restrictions, Schedule Name, Mode, 018 * Alternate Track, Pool name, Minimum, Track Blocking Order, Planned Pick Ups, Track Destinations, Destinations, Swap 019 * default loads and empties, Empty cars with default loads, Generate custom loads for spurs serviced by this train, 020 * Generate custom loads for any spur (multiple trains), Generate custom loads for any staging track, Block cars by pick 021 * up location, Comment, Comment when there is only pick ups, Comment when there is only set outs 022 */ 023public class ImportLocations extends ImportRollingStock { 024 025 LocationManager locationManager = InstanceManager.getDefault(LocationManager.class); 026 DivisionManager divisionManager = InstanceManager.getDefault(DivisionManager.class); 027 028 protected static final int FIELD_LOCATION = 0; 029 protected static final int FIELD_TRACK = 1; 030 protected static final int FIELD_TYPE = 2; 031 protected static final int FIELD_LENGTH = 3; 032 protected static final int FIELD_DIVISION = 4; 033 protected static final int FIELD_SERVICED_BY = 5; 034 protected static final int FIELD_ROLLING_STOCK = 6; 035 protected static final int FIELD_ORDER = 7; 036 protected static final int FIELD_ROAD_OPTION = 8; 037 protected static final int FIELD_ROADS = 9; 038 protected static final int FIELD_LOAD_OPTION = 10; 039 protected static final int FIELD_LOADS = 11; 040 protected static final int FIELD_SHIP_LOAD_OPTION = 12; 041 protected static final int FIELD_SHIPS = 13; 042 protected static final int FIELD_SET_OUT_RESTRICTIONS = 14; // not used 043 protected static final int FIELD_RESTRICTIONS_1 = 15; 044 protected static final int FIELD_PICK_UP_RESTRICTIONS = 16; // not used 045 protected static final int FIELD_RESTRICTIONS_2 = 17; 046 protected static final int FIELD_SCHEDULE_NAME = 18; 047 protected static final int FIELD_SCHEDULE_MODE = 19; 048 protected static final int FIELD_PERCENT_STAGING = 20; 049 protected static final int FIELD_ALTERNATE_TRACK = 21; 050 protected static final int FIELD_POOL_NAME = 22; 051 protected static final int FIELD_IGNORE_MINIMUM = 23; 052 protected static final int FIELD_TRACK_BLOCKING_ORDER = 24; 053 protected static final int FIELD_PLANNED_PICK_UPS = 25; 054 protected static final int FIELD_TRACK_DESTINATIONS = 26; 055 protected static final int FIELD_DESTINATIONS = 27; 056 protected static final int FIELD_HOLD_CARS_CUSTOM_LOADS = 28; 057 protected static final int FIELD_SWAP_DEFAULT = 29; 058 protected static final int FIELD_EMPTY_DEFAULT_LOADS = 30; 059 protected static final int FIELD_EMPTY_CUSTOM_LOADS = 31; 060 protected static final int FIELD_GENERATE_SPUR = 32; 061 protected static final int FIELD_GENERATE_ANY_SPUR = 33; 062 protected static final int FIELD_GENERATE_STAGING = 34; 063 protected static final int FIELD_BLOCK_CARS_BY_PICKUP = 35; 064 protected static final int FIELD_COMMENT = 36; 065 protected static final int FIELD_COMMENT_BOTH = 37; 066 protected static final int FIELD_COMMENT_PICKUPS = 38; 067 protected static final int FIELD_COMMENT_SETOUTS = 39; 068 069 @Override 070 public void run() { 071 File file = getFile(); 072 if (file == null) { 073 return; 074 } 075 BufferedReader rdr = getBufferedReader(file); 076 if (rdr == null) { 077 return; 078 } 079 createStatusFrame(Bundle.getMessage("ImportLocations")); 080 081 // read the import (CSV) file 082 int lineNum = 0; 083 int tracksAdded = 0; 084 String line = " "; 085 boolean importOkay = false; 086 String[] inputLine; 087 088 while (true) { 089 lineNumber.setText(Bundle.getMessage("LineNumber") + " " + Integer.toString(++lineNum)); 090 try { 091 line = rdr.readLine(); 092 } catch (IOException e) { 093 break; 094 } 095 if (line == null) { 096 importOkay = true; 097 break; 098 } 099 if (!fstatus.isShowing()) { 100 //user canceled input! 101 break; 102 } 103 line = line.trim(); 104 importLine.setText(line); 105 inputLine = parseCommaLine(line); 106 log.debug("Import line number {} has {} elements", lineNum, inputLine.length); 107 String fieldLocation = ""; 108 String fieldTrack = ""; 109 String fieldType = ""; 110 String fieldLength = ""; 111 if (line.startsWith(Bundle.getMessage("Location"))) { 112 int elementNum = 0; 113 for (String lineElement : inputLine) { 114 log.debug("Header {} is: {}", elementNum++, lineElement); 115 } 116 continue; // skip header 117 } 118 if (inputLine.length < 1) { 119 log.debug("Skipping blank line"); 120 continue; 121 } 122 if (inputLine.length < 4) { 123 log.info("Skipping row {} as we need at least 4 fields (Location, Track, Type and Length)", Integer.toString(lineNum)); 124 continue; 125 } 126 fieldLocation = inputLine[FIELD_LOCATION]; 127 Location location = locationManager.getLocationByName(fieldLocation); 128 if (location == null) { 129 log.debug("adding location - {}", fieldLocation); 130 location = locationManager.newLocation(fieldLocation); 131 } 132 fieldTrack = inputLine[FIELD_TRACK]; 133 fieldLength = inputLine[FIELD_LENGTH].trim(); 134 fieldType = inputLine[FIELD_TYPE].trim(); 135 String typeValue = null; 136 if (fieldType.length() > 0) { 137 if (fieldType.equals(Bundle.getMessage("Spur").toLowerCase(Locale.ROOT))) { 138 typeValue = Track.SPUR; 139 } else if (fieldType.equals(Bundle.getMessage("Yard").toLowerCase(Locale.ROOT))) { 140 typeValue = Track.YARD; 141 } else if (fieldType.equals(Bundle.getMessage("Class/Interchange"))) { 142 typeValue = Track.INTERCHANGE; 143 } else if (fieldType.equals(Bundle.getMessage("Staging").toLowerCase(Locale.ROOT))) { 144 typeValue = Track.STAGING; 145 } else { 146 typeValue = "unknown"; 147 } 148 } 149 Track thisTrack = location.getTrackByName(fieldTrack, null); 150 Integer trackLength = null; 151 try { 152 trackLength = Integer.valueOf(fieldLength); 153 } catch (NumberFormatException exception) { 154 log.info("Import caught an exception converting the length field of the new track - value was {} at line number {}", fieldLength, Integer.toString(lineNum)); 155 } 156 if (thisTrack != null) { 157 if (!thisTrack.getTrackType().equals(typeValue)) { 158 log.debug("Import is changing type of track for Location {} track {} to {}", location.getName(), thisTrack.getName(), typeValue); 159 thisTrack.setTrackType(typeValue); 160 } 161 } else { 162 log.debug("Import is adding location {} new track {} of type {}", location.getName(), fieldTrack, typeValue); 163 thisTrack = location.addTrack(fieldTrack, typeValue); 164 ++tracksAdded; 165 } 166 if (trackLength != null) { 167 thisTrack.setLength(trackLength); 168 } 169 //if (inputLine.length >= FIELD_DIVISION) { 170 // division was included in import 171 String fieldDivision = inputLine[FIELD_DIVISION].trim(); 172 if (fieldDivision.length() > 0) { 173 Division division = divisionManager.newDivision(fieldDivision); 174 location.setDivision(division); 175 log.debug("Setting this location to division {}", division); 176 } 177 if (inputLine.length >= FIELD_SERVICED_BY) { 178 // process direction string (a list of directions each ending with a semicolon) 179 String[] directions = inputLine[FIELD_SERVICED_BY].split("; "); 180 log.debug("this track is serviced by {} directions", directions.length); 181 int trackDir = 0; // no direction yet 182 for (String dir : directions) { 183 trackDir += Setup.getDirectionInt(dir); 184 } 185 thisTrack.setTrainDirections(trackDir); 186 log.debug("setting this location to directions {}", trackDir); 187 } 188 if (inputLine.length >= FIELD_ROLLING_STOCK) { 189 // process rolling stock accepted 190 if (inputLine[FIELD_ROLLING_STOCK].length() > 0) { 191 log.debug("Will be setting this location to accepting the following rolling stock: {}", inputLine[FIELD_ROLLING_STOCK]); 192 // first we need to remove all rolling stock types 193 for (String typeName : thisTrack.getTypeNames()) { 194 thisTrack.deleteTypeName(typeName); 195 } 196 String[] rollingStock = inputLine[FIELD_ROLLING_STOCK].split("; "); 197 for (String typeName : rollingStock) { 198 thisTrack.addTypeName(typeName); 199 } 200 } 201 } 202 if (inputLine.length >= FIELD_ORDER) { 203 // process service order (Normal, FIFO or LIFO - Track handles the bundling 204 String fieldServiceOrder = inputLine[FIELD_ORDER].trim(); 205 if (fieldServiceOrder.length() > 0) { 206 thisTrack.setServiceOrder(fieldServiceOrder); 207 log.debug("Setting the service order to {}", fieldServiceOrder); 208 } 209 } 210 211 if (inputLine.length >= FIELD_ROADS) { 212 log.debug("setting the road names to {}", inputLine[FIELD_ROADS]); 213 // note -- don't trim so the final semi-colon space remains on the last field 214 if (inputLine[FIELD_ROADS].length() > 0) { 215 String[] roads = inputLine[FIELD_ROADS].split("; "); 216 for (String road : roads) { 217 thisTrack.addRoadName(road); 218 } 219 } 220 } 221 if (inputLine.length >= FIELD_ROAD_OPTION) { 222 // process road option - again use the words imported 223 String roadOptions = inputLine[FIELD_ROAD_OPTION].trim(); 224 String optionValue = ""; 225 if (roadOptions.length() > 0) { 226 if (roadOptions.startsWith(Bundle.getMessage("AcceptsAllRoads"))) { 227 optionValue = Track.ALL_ROADS; 228 } else if (roadOptions.startsWith(Bundle.getMessage("AcceptOnly"))) { 229 optionValue = Track.INCLUDE_ROADS; 230 } else if (roadOptions.startsWith(Bundle.getMessage("Exclude"))) { 231 optionValue = Track.EXCLUDE_ROADS; 232 } 233 thisTrack.setRoadOption(optionValue); 234 log.debug("setting the road options to {}", optionValue); 235 } 236 } 237 if (inputLine.length >= FIELD_LOAD_OPTION) { 238 String loadOptions = inputLine[FIELD_LOAD_OPTION].trim(); 239 String optionValue = ""; 240 if (loadOptions.length() > 0) { 241 if (loadOptions.startsWith(Bundle.getMessage("AcceptsAllLoads"))) { 242 optionValue = Track.ALL_LOADS; 243 } else if (loadOptions.startsWith(Bundle.getMessage("AcceptOnly"))) { 244 optionValue = Track.INCLUDE_ROADS; 245 } else if (loadOptions.startsWith(Bundle.getMessage("Exclude"))) { 246 optionValue = Track.EXCLUDE_LOADS; 247 } else { 248 log.error("Locations Import load option was not recognized: {} ", loadOptions); 249 } 250 thisTrack.setLoadOption(optionValue); 251 } 252 } 253 if (inputLine.length >= FIELD_LOADS) { 254 // process names of loads, again, don't trim first 255 if (inputLine[FIELD_LOADS].length() > 0) { 256 String[] loads = inputLine[FIELD_LOADS].split("; "); 257 log.debug("This location is surviced by {} loads", loads.length); 258 for (String load : loads) { 259 thisTrack.addLoadName(load); 260 } 261 } 262 } 263 if (inputLine.length >= FIELD_SHIP_LOAD_OPTION) { 264 String loadOptions = inputLine[FIELD_SHIP_LOAD_OPTION].trim(); 265 String optionValue = ""; 266 if (loadOptions.length() > 0) { 267 if (loadOptions.startsWith(Bundle.getMessage("ShipsAllLoads"))) { 268 optionValue = Track.ALL_LOADS; 269 } else if (loadOptions.startsWith(Bundle.getMessage("ShipOnly"))) { 270 optionValue = Track.INCLUDE_ROADS; 271 } else if (loadOptions.startsWith(Bundle.getMessage("Exclude"))) { 272 optionValue = Track.EXCLUDE_LOADS; 273 } else { 274 log.error("Locations Import ship load option was not recognized: {} ", loadOptions); 275 } 276 thisTrack.setShipLoadOption(optionValue); 277 } 278 } 279 if (inputLine.length >= FIELD_SHIPS) { 280 // process names of loads, again, don't trim first 281 if (inputLine[FIELD_SHIPS].length() > 0) { 282 String[] loads = inputLine[FIELD_SHIPS].split("; "); 283 log.debug("This location ships {} loads", loads.length); 284 for (String load : loads) { 285 thisTrack.addShipLoadName(load); 286 } 287 } 288 } 289 290 // TODO import fields 14 through 22 291 292 if (inputLine.length >= FIELD_IGNORE_MINIMUM) { 293 String ignoreMin = inputLine[FIELD_IGNORE_MINIMUM].trim(); 294 if (ignoreMin.length() > 0) { 295 log.debug("setting the ignore minimum to {}", ignoreMin); 296 Integer ignoreValue = null; 297 try { 298 ignoreValue = Integer.valueOf(ignoreMin); 299 thisTrack.setBlockingOrder(ignoreValue); 300 } catch (NumberFormatException exception) { 301 log.debug("Exception converting the ignore minimum to a number - value was {}", ignoreMin); 302 } 303 } 304 } 305 if (inputLine.length >= FIELD_TRACK_BLOCKING_ORDER) { 306 String fieldTrackBlockingOrder = inputLine[FIELD_TRACK_BLOCKING_ORDER].trim(); 307 if (fieldTrackBlockingOrder.length() > 0) { 308 log.debug("setting the blocking order to {}", fieldTrackBlockingOrder); 309 Integer blockingOrder = null; 310 try { 311 blockingOrder = Integer.valueOf(fieldTrackBlockingOrder); 312 thisTrack.setBlockingOrder(blockingOrder); 313 } catch (NumberFormatException exception) { 314 log.debug("Exception converting the track blocking order to a number - value was {}", fieldTrackBlockingOrder); 315 } 316 } 317 } 318 if (inputLine.length >= FIELD_PLANNED_PICK_UPS) { 319 String ignoreUsedLength = inputLine[FIELD_PLANNED_PICK_UPS].trim(); 320 if (ignoreUsedLength.length() > 0) { 321 try { 322 Integer ignorePercentage = Integer.valueOf(ignoreUsedLength); 323 thisTrack.setIgnoreUsedLengthPercentage( ignorePercentage); 324 } catch (NumberFormatException exception) { 325 log.debug("Exception converting field Ignore Used track Percentage - value was {}", ignoreUsedLength); 326 } 327 } 328 } 329 // TODO import fields 26 though 35 330 331 if (inputLine.length >= FIELD_COMMENT) { 332 String fieldComment = inputLine[FIELD_COMMENT].trim(); 333 if (fieldComment.length() > 0) { 334 log.debug("setting the location comment to: {}", fieldComment); 335 thisTrack.setComment(fieldComment); 336 } 337 } 338 if (inputLine.length >= FIELD_COMMENT_BOTH) { 339 String commentBoth = inputLine[FIELD_COMMENT_BOTH].trim(); 340 thisTrack.setCommentBoth(commentBoth); 341 } 342 if (inputLine.length >= FIELD_COMMENT_PICKUPS) { 343 String commentPickups = inputLine[FIELD_COMMENT_PICKUPS].trim(); 344 thisTrack.setCommentPickup(commentPickups); 345 } 346 if (inputLine.length >= FIELD_COMMENT_SETOUTS) { 347 String commentSetouts = inputLine[FIELD_COMMENT_SETOUTS].trim(); 348 thisTrack.setCommentSetout(commentSetouts); 349 } 350 } 351 if (importOkay) { 352 JmriJOptionPane.showMessageDialog(null, Bundle.getMessage("ImportTracksAdded", tracksAdded), 353 Bundle.getMessage("SuccessfulImport"), JmriJOptionPane.INFORMATION_MESSAGE); 354 } else { 355 JmriJOptionPane.showMessageDialog(null, Bundle.getMessage("ImportTracksAdded", tracksAdded), 356 Bundle.getMessage("ImportFailed"), JmriJOptionPane.ERROR_MESSAGE); 357 } 358 fstatus.dispose(); 359 } 360 361 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ImportLocations.class); 362 363}