001package jmri.jmrit.operations.rollingstock.engines.tools;
002
003import java.io.*;
004
005import jmri.InstanceManager;
006import jmri.jmrit.operations.locations.*;
007import jmri.jmrit.operations.rollingstock.ImportRollingStock;
008import jmri.jmrit.operations.rollingstock.RollingStock;
009import jmri.jmrit.operations.rollingstock.engines.*;
010import jmri.jmrit.operations.setup.Control;
011import jmri.jmrit.operations.trains.TrainCommon;
012import jmri.util.swing.JmriJOptionPane;
013
014/**
015 * This routine will import engines into the operation database. Each field is
016 * space or comma delimited. Field order: Number Road Model Length Owner Built
017 * Location - Track Consist HP WeightTons Type
018 * 
019 * @author Dan Boudreau Copyright (C) 2008, 2013
020 */
021public class ImportEngines extends ImportRollingStock {
022
023    private static final String DEFAULT_ENGINE_TYPE = Bundle.getMessage("engineDefaultType");
024    private static final String DEFAULT_ENGINE_HP = Bundle.getMessage("engineDefaultHp");
025
026    private static final int ENG_NUMBER = 0;
027    private static final int ENG_ROAD = 1;
028    private static final int ENG_MODEL = 2;
029    private static final int ENG_LENGTH = 3;
030
031    private static final int ENG_OWNER = 4;
032    private static final int ENG_BUILT = 5;
033    private static final int ENG_LOCATION = 6;
034    private static final int ENG_LOCATION_TRACK_SEPARATOR = 7;
035    private static final int ENG_TRACK = 8;
036
037    // only for CSV files
038    private static final int ENG_CONSIST = 9;
039    // private static final int ENG_MOVES = 10;
040    // private static final int ENG_VALUE = 12;
041    private static final int ENG_HP = 13;
042    private static final int ENG_WEIGHT = 14;
043    private static final int ENG_TYPE = 15;
044    // private static final int ENG_COMMENT = 16;
045    // private static final int ENG_MISCELLANEOUS = 17;
046
047    // as of 7/23/2018 there were 18 attributes exported by operations
048
049    EngineManager engineManager = InstanceManager.getDefault(EngineManager.class);
050
051    // we use a thread so the status frame will work!
052    @Override
053    public void run() {
054        File file = getFile();
055        if (file == null) {
056            return;
057        }
058        BufferedReader in = getBufferedReader(file);
059        if (in == null) {
060            return;
061        }
062
063        createStatusFrame(Bundle.getMessage("ImportEngines"));
064
065        // Now read the input file
066        boolean importOkay = false;
067        boolean comma = false;
068        int lineNum = 0;
069        int enginesAdded = 0;
070        String line = " ";
071        String engineNumber;
072        String engineRoad;
073        String engineModel;
074        String engineLength;
075        String engineOwner = "";
076        String engineBuilt = "";
077        String engineLocationName = "";
078        String engineTrackName = "";
079        String engineConsistName = "";
080        String engineHp = "";
081        String engineWeightTons = "";
082        String engineType = "";
083        String[] inputLine;
084
085        // does the file name end with .csv?
086        if (file.getAbsolutePath().endsWith(".csv")) { // NOI18N
087            log.info("Using comma as delimiter for import engines");
088            comma = true;
089        }
090
091        while (true) {
092            lineNumber.setText(Bundle.getMessage("LineNumber") + " " + Integer.toString(++lineNum));
093            try {
094                line = in.readLine();
095            } catch (IOException e) {
096                break;
097            }
098
099            if (line == null) {
100                importOkay = true;
101                break;
102            }
103
104            // has user canceled import?
105            if (!fstatus.isShowing()) {
106                break;
107            }
108
109            line = line.trim();
110            log.debug("Import: {}", line);
111            importLine.setText(line);
112
113            if (line.startsWith(Bundle.getMessage("Number"))) {
114                continue; // skip header
115            }
116            if (line.equalsIgnoreCase("comma")) { // NOI18N
117                log.info("Using comma as delimiter for import engines");
118                comma = true;
119                continue;
120            }
121            // use comma as delimiter if found otherwise use spaces
122            if (comma) {
123                inputLine = parseCommaLine(line);
124            } else {
125                inputLine = line.split("\\s+"); // NOI18N
126            }
127            if (inputLine.length < 1 || line.isEmpty()) {
128                log.debug("Skipping blank line");
129                continue;
130            }
131            int base = 1;
132            if (comma || !inputLine[0].isEmpty()) {
133                base--; // skip over any spaces at start of line
134            }
135
136            if (inputLine.length > base + 3) {
137                engineNumber = inputLine[base + ENG_NUMBER].trim();
138                engineRoad = inputLine[base + ENG_ROAD].trim();
139                engineModel = inputLine[base + ENG_MODEL].trim();
140                engineLength = inputLine[base + ENG_LENGTH].trim();
141                engineOwner = "";
142                engineBuilt = "";
143                engineLocationName = "";
144                engineTrackName = "";
145                engineConsistName = "";
146                engineHp = "";
147                engineWeightTons = "";
148                engineType = "";
149
150                log.debug("Checking engine number ({}) road ({}) model ({}) length ({})", engineNumber, engineRoad,
151                        engineModel, engineLength); // NOI18N
152                if (engineNumber.isEmpty()) {
153                    log.info("Import line {} missing engine number", lineNum);
154                    JmriJOptionPane.showMessageDialog(null, 
155                            Bundle.getMessage("RoadNumberNotSpecified", lineNum),
156                            Bundle.getMessage("RoadNumberMissing"), JmriJOptionPane.ERROR_MESSAGE);
157                    break;
158                }
159                if (engineRoad.isEmpty()) {
160                    log.info("Import line {} missing engine road", lineNum);
161                    JmriJOptionPane.showMessageDialog(null, 
162                            Bundle.getMessage("RoadNameNotSpecified", lineNum),
163                            Bundle.getMessage("RoadNameMissing"), JmriJOptionPane.ERROR_MESSAGE);
164                    break;
165                }
166                if (engineModel.isEmpty()) {
167                    log.info("Import line {} missing engine model", lineNum);
168                    JmriJOptionPane.showMessageDialog(null, 
169                            Bundle.getMessage("EngineModelNotSpecified", lineNum),
170                            Bundle.getMessage("EngineModelMissing"), JmriJOptionPane.ERROR_MESSAGE);
171                    break;
172                }
173                if (engineLength.isEmpty()) {
174                    log.info("Import line {} missing engine length", lineNum);
175                    JmriJOptionPane.showMessageDialog(null, 
176                            Bundle.getMessage("EngineLengthNotSpecified", lineNum),
177                            Bundle.getMessage("EngineLengthMissing"), JmriJOptionPane.ERROR_MESSAGE);
178                    break;
179                }
180                if (TrainCommon.splitString(engineNumber).length() > Control.max_len_string_road_number) {
181                    JmriJOptionPane.showMessageDialog(
182                            null, Bundle.getMessage("EngineRoadNumberTooLong",
183                                    engineRoad, engineNumber, engineNumber),
184                            Bundle.getMessage("RoadNumMustBeLess"), JmriJOptionPane.ERROR_MESSAGE);
185                    break;
186                }
187                if (engineRoad.length() > Control.max_len_string_attibute) {
188                    JmriJOptionPane.showMessageDialog(null, 
189                            Bundle.getMessage("EngineRoadNameTooLong",
190                                    engineRoad, engineNumber, engineRoad),
191                            Bundle.getMessage("engineAttribute",
192                                    Control.max_len_string_attibute),
193                            JmriJOptionPane.ERROR_MESSAGE);
194                    break;
195                }
196                if (engineModel.length() > Control.max_len_string_attibute) {
197                    JmriJOptionPane.showMessageDialog(
198                            null, Bundle.getMessage("EngineModelNameTooLong",
199                                    engineRoad, engineNumber, engineModel),
200                            Bundle.getMessage("engineAttribute",
201                                    Control.max_len_string_attibute),
202                            JmriJOptionPane.ERROR_MESSAGE);
203                    break;
204                }
205                if (!InstanceManager.getDefault(EngineModels.class).containsName(engineModel)) {
206                    int results = JmriJOptionPane.showConfirmDialog(null, Bundle.getMessage("Engine") +
207                            " (" +
208                            engineRoad +
209                            " " +
210                            engineNumber +
211                            ")" +
212                            NEW_LINE +
213                            Bundle.getMessage("modelNameNotExist", engineModel),
214                            Bundle.getMessage("engineAddModel"), JmriJOptionPane.YES_NO_CANCEL_OPTION);
215                    if (results == JmriJOptionPane.YES_OPTION) {
216                        InstanceManager.getDefault(EngineModels.class).addName(engineModel);
217                    } else if (results == JmriJOptionPane.CANCEL_OPTION ) {
218                        break;
219                    }
220                }
221                if (engineLength.length() > Control.max_len_string_length_name) {
222                    JmriJOptionPane.showMessageDialog(
223                            null, Bundle.getMessage("EngineLengthNameTooLong",
224                                    engineRoad, engineNumber, engineLength),
225                            Bundle.getMessage("engineAttribute",
226                                    Control.max_len_string_length_name),
227                            JmriJOptionPane.ERROR_MESSAGE);
228                    break;
229                }
230                try {
231                    Integer.parseInt(engineLength);
232                } catch (NumberFormatException e) {
233                    JmriJOptionPane.showMessageDialog(
234                            null, Bundle.getMessage("EngineLengthNameNotNumber",
235                                    engineRoad, engineNumber, engineLength),
236                            Bundle.getMessage("EngineLengthMissing"), JmriJOptionPane.ERROR_MESSAGE);
237                    break;
238                }
239                Engine e = engineManager.getByRoadAndNumber(engineRoad, engineNumber);
240                if (e != null) {
241                    log.info("Can not add engine number ({}) road ({}) it already exists!", engineNumber, engineRoad); // NOI18N
242                    continue;
243                }
244
245                if (inputLine.length > base + ENG_OWNER) {
246                    engineOwner = inputLine[base + ENG_OWNER].trim();
247                    if (engineOwner.length() > Control.max_len_string_attibute) {
248                        JmriJOptionPane.showMessageDialog(null,
249                                Bundle.getMessage("EngineOwnerNameTooLong",
250                                        engineRoad, engineNumber, engineOwner),
251                                Bundle.getMessage("engineAttribute",
252                                        Control.max_len_string_attibute),
253                                JmriJOptionPane.ERROR_MESSAGE);
254                        break;
255                    }
256                }
257                if (inputLine.length > base + ENG_BUILT) {
258                    engineBuilt = inputLine[base + ENG_BUILT].trim();
259                    if (engineBuilt.length() > Control.max_len_string_built_name) {
260                        JmriJOptionPane.showMessageDialog(null,
261                                Bundle.getMessage("EngineBuiltDateTooLong",
262                                        engineRoad, engineNumber, engineBuilt),
263                                Bundle.getMessage("engineAttribute",
264                                        Control.max_len_string_built_name),
265                                JmriJOptionPane.ERROR_MESSAGE);
266                        break;
267                    }
268                }
269                if (inputLine.length > base + ENG_LOCATION) {
270                    engineLocationName = inputLine[base + ENG_LOCATION].trim();
271                }
272                if (comma && inputLine.length > base + ENG_TRACK) {
273                    engineTrackName = inputLine[base + ENG_TRACK].trim();
274                }
275                // Location and track name can be one or more words in a
276                // space delimited file
277                if (!comma) {
278                    int j = 0;
279                    StringBuffer name = new StringBuffer(engineLocationName);
280                    for (int i = base + ENG_LOCATION_TRACK_SEPARATOR; i < inputLine.length; i++) {
281                        if (inputLine[i].equals(LOCATION_TRACK_SEPARATOR)) {
282                            j = i + 1; // skip over separator
283                            break;
284                        } else {
285                            name.append(" " + inputLine[i]);
286                        }
287                    }
288                    engineLocationName = name.toString();
289                    log.debug("Engine ({} {}) has location ({})", engineRoad, engineNumber, engineLocationName);
290                    // now get the track name
291                    name = new StringBuffer();
292                    if (j != 0 && j < inputLine.length) {
293                        name.append(inputLine[j]);
294                        for (int i = j + 1; i < inputLine.length; i++) {
295                            name.append(" " + inputLine[i]);
296                        }
297                        log.debug("Engine ({} {}) has track ({})", engineRoad, engineNumber, engineTrackName);
298                    }
299                    engineTrackName = name.toString();
300                }
301
302                if (engineLocationName.length() > Control.max_len_string_location_name) {
303                    JmriJOptionPane.showMessageDialog(null,
304                            Bundle.getMessage("EngineLocationNameTooLong",
305                                    engineRoad, engineNumber, engineLocationName),
306                            Bundle.getMessage("engineAttribute",
307                                    Control.max_len_string_location_name),
308                            JmriJOptionPane.ERROR_MESSAGE);
309                    break;
310                }
311                if (engineTrackName.length() > Control.max_len_string_track_name) {
312                    JmriJOptionPane.showMessageDialog(null,
313                            Bundle.getMessage("EngineTrackNameTooLong",
314                                    engineRoad, engineNumber, engineTrackName),
315                            Bundle.getMessage("engineAttribute",
316                                    Control.max_len_string_track_name),
317                            JmriJOptionPane.ERROR_MESSAGE);
318                    break;
319                }
320                Location location =
321                        InstanceManager.getDefault(LocationManager.class).getLocationByName(engineLocationName);
322                Track track = null;
323                if (location == null && !engineLocationName.isEmpty()) {
324                    JmriJOptionPane.showMessageDialog(null,
325                            Bundle.getMessage("EngineLocationDoesNotExist",
326                                    engineRoad, engineNumber, engineLocationName),
327                            Bundle.getMessage("engineLocation"), JmriJOptionPane.ERROR_MESSAGE);
328                    int results = JmriJOptionPane.showConfirmDialog(null,
329                            Bundle.getMessage("DoYouWantToCreateLoc",
330                                    engineLocationName),
331                            Bundle.getMessage("engineLocation"), JmriJOptionPane.YES_NO_OPTION);
332                    if (results == JmriJOptionPane.YES_OPTION) {
333                        log.debug("Create location ({})", engineLocationName);
334                        location = InstanceManager.getDefault(LocationManager.class).newLocation(engineLocationName);
335                    } else {
336                        break;
337                    }
338                }
339                if (location != null && !engineTrackName.isEmpty()) {
340                    track = location.getTrackByName(engineTrackName, null);
341                    if (track == null) {
342                        JmriJOptionPane.showMessageDialog(
343                                null, Bundle.getMessage("EngineTrackDoesNotExist",
344                                        engineRoad, engineNumber, engineTrackName,
345                                                engineLocationName),
346                                Bundle.getMessage("engineTrack"), JmriJOptionPane.ERROR_MESSAGE);
347                        int results = JmriJOptionPane.showConfirmDialog(null,
348                                Bundle.getMessage("DoYouWantToCreateTrack",
349                                        engineTrackName, engineLocationName),
350                                Bundle.getMessage("engineTrack"), JmriJOptionPane.YES_NO_OPTION);
351                        if (results == JmriJOptionPane.YES_OPTION) {
352                            if (!location.isStaging()) {
353                                log.debug("Create 1000 foot yard track ({})", engineTrackName);
354                                track = location.addTrack(engineTrackName, Track.YARD);
355                            } else {
356                                log.debug("Create 1000 foot staging track ({})", engineTrackName);
357                                track = location.addTrack(engineTrackName, Track.STAGING);
358                            }
359                            track.setLength(1000);
360                        } else {
361                            break;
362                        }
363                    }
364                }
365                // check for consist name
366                if (comma && inputLine.length > base + ENG_CONSIST) {
367                    engineConsistName = inputLine[ENG_CONSIST].trim();
368                    log.debug("Consist name ({})", engineConsistName);
369                }
370                // check for HP
371                if (comma && inputLine.length > base + ENG_HP) {
372                    engineHp = inputLine[ENG_HP].trim();
373                    log.debug("Engine HP ({})", engineHp);
374                }
375                // check for engine weight tons
376                if (comma && inputLine.length > base + ENG_WEIGHT) {
377                    engineWeightTons = inputLine[ENG_WEIGHT].trim();
378                    log.debug("Engine weight tons ({})", engineWeightTons);
379                }
380                // check for engine type
381                if (comma && inputLine.length > base + ENG_TYPE) {
382                    engineType = inputLine[ENG_TYPE].trim();
383                    log.debug("Engine type ({})", engineType);
384                }
385                log.debug("Add engine ({} {}) owner ({}) built ({}) location ({}, {})", engineRoad, engineNumber,
386                        engineOwner, engineBuilt, engineLocationName, engineTrackName);
387                Engine engine = engineManager.newRS(engineRoad, engineNumber);
388                engine.setModel(engineModel);
389                engine.setLength(engineLength);
390                // does this model already have a type?
391                if (engine.getTypeName().equals(Engine.NONE)) {
392                    if (!engineType.isEmpty()) {
393                        engine.setTypeName(engineType);
394                    } else {
395                        engine.setTypeName(DEFAULT_ENGINE_TYPE);
396                    }
397                }
398                // does this model already have a HP?
399                if (engine.getHp().equals(Engine.NONE)) {
400                    if (!engineHp.isEmpty()) {
401                        engine.setHp(engineHp);
402                    } else {
403                        engine.setHp(DEFAULT_ENGINE_HP);
404                    }
405                }
406                // does this model already have a weight in tons?
407                if (engine.getWeightTons().equals(Engine.NONE)) {
408                    engine.setWeightTons(engineWeightTons);
409                }
410                engine.setOwnerName(engineOwner);
411                engine.setBuilt(engineBuilt);
412                // consist?
413                if (!engineConsistName.isEmpty()) {
414                    Consist consist = InstanceManager.getDefault(ConsistManager.class).newConsist(engineConsistName);
415                    engine.setConsist(consist);
416                }
417
418                enginesAdded++;
419
420                if (location != null && track != null) {
421                    String status = engine.setLocation(location, track);
422                    if (!status.equals(Track.OKAY)) {
423                        log.debug("Can't set engine's location because of {}", status);
424                        JmriJOptionPane.showMessageDialog(
425                                null, Bundle.getMessage("CanNotSetEngineAtLocation",
426                                        engineRoad, engineNumber, engineModel, engineLocationName,
427                                                engineTrackName, status),
428                                Bundle.getMessage("rsCanNotLoc"), JmriJOptionPane.ERROR_MESSAGE);
429                        if (status.startsWith(Track.TYPE)) {
430                            int results = JmriJOptionPane.showConfirmDialog(
431                                    null, Bundle.getMessage("DoYouWantToAllowService",
432                                            engineLocationName, engineTrackName,
433                                                    engineRoad, engineNumber, engine.getTypeName()),
434                                    Bundle.getMessage("ServiceEngineType"),
435                                    JmriJOptionPane.YES_NO_OPTION);
436                            if (results == JmriJOptionPane.YES_OPTION) {
437                                location.addTypeName(engine.getTypeName());
438                                track.addTypeName(engine.getTypeName());
439                                status = engine.setLocation(location, track);
440                            } else {
441                                break;
442                            }
443                        }
444                        if (status.startsWith(Track.LENGTH) || status.startsWith(Track.CAPACITY)) {
445                            int results = JmriJOptionPane.showConfirmDialog(null,
446                                    Bundle.getMessage("DoYouWantIncreaseLength",
447                                            engineTrackName),
448                                    Bundle.getMessage("TrackLength"), JmriJOptionPane.YES_NO_OPTION);
449                            if (results == JmriJOptionPane.YES_OPTION) {
450                                track.setLength(track.getLength() + 1000);
451                                status = engine.setLocation(location, track);
452                            } else {
453                                break;
454                            }
455                        }
456                        if (!status.equals(Track.OKAY)) {
457                            int results = JmriJOptionPane.showConfirmDialog(
458                                    null, Bundle.getMessage("DoYouWantToForceEngine",
459                                            engineRoad, engineNumber, engineLocationName,
460                                                    engineTrackName),
461                                    Bundle.getMessage("OverRide"), JmriJOptionPane.YES_NO_OPTION);
462                            if (results == JmriJOptionPane.YES_OPTION) {
463                                engine.setLocation(location, track, RollingStock.FORCE); // force
464                            } else {
465                                break;
466                            }
467                        }
468                    }
469                } else {
470                    // log.debug("No location for engine ("+engineRoad+"
471                    // "+engineNumber+")");
472                }
473            } else if (!line.isEmpty()) {
474                log.info("Engine import line {} missing attributes: {}", lineNum, line);
475                JmriJOptionPane.showMessageDialog(null, Bundle.getMessage("ImportMissingAttributes",
476                        lineNum), Bundle.getMessage("EngineAttributeMissing"),
477                        JmriJOptionPane.ERROR_MESSAGE);
478                break;
479            }
480        }
481        try {
482            in.close();
483        } catch (IOException e) {
484            log.error("Import Engines failed: {}", e.getLocalizedMessage());
485        }
486
487        if (importOkay) {
488            JmriJOptionPane.showMessageDialog(null, Bundle.getMessage("ImportEnginesAdded",
489                    enginesAdded), Bundle.getMessage("SuccessfulImport"),
490                    JmriJOptionPane.INFORMATION_MESSAGE);
491        } else {
492            JmriJOptionPane.showMessageDialog(null, Bundle.getMessage("ImportEnginesAdded",
493                    enginesAdded), Bundle.getMessage("ImportFailed"), JmriJOptionPane.ERROR_MESSAGE);
494        }
495
496        // kill status panel
497        fstatus.dispose();
498    }
499
500    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ImportEngines.class);
501}