001package jmri.jmrit.dispatcher;
002
003import java.io.File;
004import java.util.ArrayList;
005import java.util.Arrays;
006import java.util.List;
007import java.util.regex.Matcher;
008import java.util.regex.Pattern;
009import jmri.util.FileUtil;
010import jmri.util.XmlFilenameFilter;
011import org.jdom2.Document;
012import org.jdom2.Element;
013import org.slf4j.Logger;
014import org.slf4j.LoggerFactory;
015import jmri.configurexml.AbstractXmlAdapter.EnumIO;
016import jmri.configurexml.AbstractXmlAdapter.EnumIoNamesNumbers;
017import jmri.jmrit.dispatcher.ActiveTrain.TrainDetection;
018
019/**
020 * Handles reading and writing of TrainInfo files to disk as an XML file to/from
021 * the dispatcher/traininfo/ directory in the user's preferences area
022 * <p>
023 * This class manipulates the files conforming to the dispatcher-traininfo DTD
024 * <p>
025 * The file is written when the user requests that train information be saved. A
026 * TrainInfo file is read when the user request it in the Activate New Train
027 * window
028 *
029 * <p>
030 * This file is part of JMRI.
031 * <p>
032 * JMRI is open source software; you can redistribute it and/or modify it under
033 * the terms of version 2 of the GNU General Public License as published by the
034 * Free Software Foundation. See the "COPYING" file for a copy of this license.
035 * <p>
036 * JMRI is distributed in the hope that it will be useful, but WITHOUT ANY
037 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
038 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
039 *
040 * @author Dave Duchamp Copyright (C) 2009
041 */
042public class TrainInfoFile extends jmri.jmrit.XmlFile {
043
044    public TrainInfoFile() {
045        super();
046    }
047    // operational variables
048    private String fileLocation = FileUtil.getUserFilesPath()
049            + "dispatcher" + File.separator + "traininfo" + File.separator;
050
051    public void setFileLocation(String testLocation) {
052        fileLocation = testLocation;
053    }
054    private Document doc = null;
055    private Element root = null;
056
057    static final EnumIO<ActiveTrain.TrainDetection> trainsdectionFromEnumMap = new EnumIoNamesNumbers<>(ActiveTrain.TrainDetection.class);
058
059    /*
060     *  Reads Dispatcher TrainInfo from a file in the user's preferences directory
061     *  If the file containing Dispatcher TrainInfo does not exist this routine returns quietly.
062     *  "name" is assumed to have the .xml or .XML extension already included
063     */
064    public TrainInfo readTrainInfo(String name) throws org.jdom2.JDOMException, java.io.IOException {
065        log.debug("entered readTrainInfo for {}", name);
066        TrainInfo tInfo = null;
067        int version  = 1;
068        // check if file exists
069        if (checkFile(fileLocation + name)) {
070            // file is present.
071            tInfo = new TrainInfo();
072            root = rootFromName(fileLocation + name);
073            if (root != null) {
074                // there is a file
075                Element traininfo = root.getChild("traininfo");
076                if (traininfo != null) {
077                    // get version so we dont look for missing fields
078                    if (traininfo.getAttribute("version") != null ) {
079                        try {
080                            version = traininfo.getAttribute("version").getIntValue();
081                        }
082                        catch(Exception ex) {
083                            log.error("Traininfo file version number not an integer: assuming version 1");
084                            version = 1;
085                        }
086                    } else {
087                        version = 1;
088                    }
089                    tInfo.setVersion(version);
090                    // there are train info options defined, read them
091                    if (traininfo.getAttribute("transitname") != null) {
092                        // there is a transit name selected
093                        tInfo.setTransitName(traininfo.getAttribute("transitname").getValue());
094                    } else {
095                        log.error("Transit name missing when reading TrainInfoFile {}", name);
096                    }
097                    if (version < 6) {
098                        if (traininfo.getAttribute("trainname") != null) {
099                            tInfo.setTrainName(traininfo.getAttribute("trainname").getValue());
100                            tInfo.setRosterId(traininfo.getAttribute("trainname").getValue());
101                            tInfo.setTrainUserName(traininfo.getAttribute("trainname").getValue());
102                        } else {
103                            log.error("Train name missing when reading TrainInfoFile {}", name);
104                        }
105                    } else {
106                        if (traininfo.getAttribute("trainname") != null) {
107                            tInfo.setRosterId(traininfo.getAttribute("trainname").getValue());
108                        }
109                        if (traininfo.getAttribute("rosterid") != null) {
110                            tInfo.setRosterId(traininfo.getAttribute("rosterid").getValue());
111                        }
112                        if (traininfo.getAttribute("trainusername") != null) {
113                            tInfo.setTrainUserName(traininfo.getAttribute("trainusername").getValue());
114                        }
115                    }
116                    if (traininfo.getAttribute("dccaddress") != null) {
117                        tInfo.setDccAddress(traininfo.getAttribute("dccaddress").getValue());
118                    } else {
119                        log.error("DCC Address missing when reading TrainInfoFile {}", name);
120                    }
121                    if (traininfo.getAttribute("trainintransit") != null) {
122                        tInfo.setTrainInTransit(true);
123                        if (traininfo.getAttribute("trainintransit").getValue().equals("no")) {
124                            tInfo.setTrainInTransit(false);
125                        }
126                    } else {
127                        log.error("Train in Transit check box missing  when reading TrainInfoFile {}", name);
128                    }
129                    if (traininfo.getAttribute("startblockname") != null) {
130                        // there is a transit name selected
131                        tInfo.setStartBlockName(traininfo.getAttribute("startblockname").getValue());
132                    } else {
133                        log.error("Start block name missing when reading TrainInfoFile {}", name);
134                    }
135                    if (traininfo.getAttribute("endblockname") != null) {
136                        // there is a transit name selected
137                        tInfo.setDestinationBlockName(traininfo.getAttribute("endblockname").getValue());
138                    } else {
139                        log.error("Destination block name missing when reading TrainInfoFile {}", name);
140                    }
141
142                    if (traininfo.getAttribute("trainfromroster") != null) {
143                        tInfo.setTrainFromRoster(true);
144                        if (traininfo.getAttribute("trainfromroster").getValue().equals("no")) {
145                            tInfo.setTrainFromRoster(false);
146                        }
147                    }
148                    if (traininfo.getAttribute("trainfromtrains") != null) {
149                        tInfo.setTrainFromTrains(true);
150                        if (traininfo.getAttribute("trainfromtrains").getValue().equals("no")) {
151                            tInfo.setTrainFromTrains(false);
152                        }
153                    }
154                    if (traininfo.getAttribute("trainfromuser") != null) {
155                        tInfo.setTrainFromUser(true);
156                        if (traininfo.getAttribute("trainfromuser").getValue().equals("no")) {
157                            tInfo.setTrainFromUser(false);
158                        }
159                    }
160                    if (traininfo.getAttribute("trainfromsetlater") != null) {
161                        tInfo.setTrainFromSetLater(true);
162                        if (traininfo.getAttribute("trainfromsetlater").getValue().equals("no")) {
163                            tInfo.setTrainFromSetLater(false);
164                        }
165                    }
166                    if (traininfo.getAttribute("priority") != null) {
167                        tInfo.setPriority(Integer.parseInt(traininfo.getAttribute("priority").getValue()));
168                    } else {
169                        log.error("Priority missing when reading TrainInfoFile {}", name);
170                    }
171                    if (traininfo.getAttribute("allocatealltheway") != null) {
172                        if (traininfo.getAttribute("allocatealltheway").getValue().equals("yes")) {
173                            tInfo.setAllocateAllTheWay(true);
174                        }
175                    }
176                    if (traininfo.getAttribute("allocationmethod") != null) {
177                        tInfo.setAllocationMethod(traininfo.getAttribute("allocationmethod").getIntValue());
178                    }
179                    if (traininfo.getAttribute("nexttrain") != null) {
180                        tInfo.setNextTrain(traininfo.getAttribute("nexttrain").getValue());
181                    }
182                    if (traininfo.getAttribute("resetwhendone") != null) {
183                        if (traininfo.getAttribute("resetwhendone").getValue().equals("yes")) {
184                            tInfo.setResetWhenDone(true);
185                        }
186                        if (traininfo.getAttribute("delayedrestart") != null) {
187                            // for older files that didnot have seperate restart details for to and fro
188                            // we default that data to this data.
189                            switch (traininfo.getAttribute("delayedrestart").getValue()) {
190                                case "no":
191                                    tInfo.setDelayedRestart(ActiveTrain.NODELAY);
192                                    tInfo.setReverseDelayedRestart(ActiveTrain.NODELAY);
193                                    break;
194                                case "sensor":
195                                    tInfo.setDelayedRestart(ActiveTrain.SENSORDELAY);
196                                    tInfo.setReverseDelayedRestart(ActiveTrain.SENSORDELAY);
197                                    if (traininfo.getAttribute("delayedrestartsensor") != null) {
198                                        tInfo.setRestartSensorName(traininfo.getAttribute("delayedrestartsensor").getValue());
199                                        tInfo.setReverseRestartSensorName(traininfo.getAttribute("delayedrestartsensor").getValue());
200                                    }
201                                    if (traininfo.getAttribute("resetrestartsensor") != null) {
202                                        tInfo.setResetRestartSensor(traininfo.getAttribute("resetrestartsensor").getValue().equals("yes"));
203                                        tInfo.setReverseResetRestartSensor(traininfo.getAttribute("resetrestartsensor").getValue().equals("yes"));
204                                    }
205                                    break;
206                                case "timed":
207                                    tInfo.setDelayedRestart(ActiveTrain.TIMEDDELAY);
208                                    tInfo.setReverseDelayedRestart(ActiveTrain.TIMEDDELAY);
209                                    if (traininfo.getAttribute("delayedrestarttime") != null) {
210                                        tInfo.setRestartDelayMin((int) traininfo.getAttribute("delayedrestarttime").getLongValue());
211                                        tInfo.setReverseRestartDelayMin((int) traininfo.getAttribute("delayedrestarttime").getLongValue());
212                                    }   break;
213                                default:
214                                    break;
215                            }
216                        }
217                    }
218                    if (traininfo.getAttribute("reverseatend") != null) {
219                        tInfo.setReverseAtEnd(true);
220                        if (traininfo.getAttribute("reverseatend").getValue().equals("no")) {
221                            tInfo.setReverseAtEnd(false);
222                        }
223                        if (version > 3) {
224                            // fro delays are independent from to delays
225                            if (traininfo.getAttribute("reversedelayedrestart") != null) {
226                                switch (traininfo.getAttribute("reversedelayedrestart").getValue()) {
227                                    case "no":
228                                        tInfo.setReverseDelayedRestart(ActiveTrain.NODELAY);
229                                        break;
230                                    case "sensor":
231                                        tInfo.setReverseDelayedRestart(ActiveTrain.SENSORDELAY);
232                                        if (traininfo.getAttribute("reversedelayedrestartsensor") != null) {
233                                            tInfo.setReverseRestartSensorName(
234                                                    traininfo.getAttribute("reversedelayedrestartsensor").getValue());
235                                        }
236                                        if (traininfo.getAttribute("reverseresetrestartsensor") != null) {
237                                            tInfo.setReverseResetRestartSensor(
238                                                    traininfo.getAttribute("reverseresetrestartsensor").getValue()
239                                                            .equals("yes"));
240                                        }
241                                        break;
242                                    case "timed":
243                                        tInfo.setReverseDelayedRestart(ActiveTrain.TIMEDDELAY);
244                                        if (traininfo.getAttribute("reversedelayedrestarttime") != null) {
245                                            tInfo.setReverseRestartDelayMin((int) traininfo
246                                                    .getAttribute("reversedelayedrestarttime").getLongValue());
247                                        }
248                                        break;
249                                    default:
250                                        break;
251                                }
252                            }
253                        }
254                    }
255                    if (traininfo.getAttribute("delayedstart") != null) {
256                        switch (traininfo.getAttribute("delayedstart").getValue()) {
257                            case "no":
258                                tInfo.setDelayedStart(ActiveTrain.NODELAY);
259                                break;
260                            case "sensor":
261                                tInfo.setDelayedStart(ActiveTrain.SENSORDELAY);
262                                break;
263                            default:
264                                //This covers the old versions of the file with "yes"
265                                tInfo.setDelayedStart(ActiveTrain.TIMEDDELAY);
266                                break;
267                        }
268                    }
269                    if (traininfo.getAttribute("departuretimehr") != null) {
270                        tInfo.setDepartureTimeHr(Integer.parseInt(traininfo.getAttribute("departuretimehr").getValue()));
271                    }
272                    if (traininfo.getAttribute("departuretimemin") != null) {
273                        tInfo.setDepartureTimeMin(Integer.parseInt(traininfo.getAttribute("departuretimemin").getValue()));
274                    }
275                    if (traininfo.getAttribute("delayedSensor") != null) {
276                        tInfo.setDelaySensorName(traininfo.getAttribute("delayedSensor").getValue());
277                    }
278                    if (traininfo.getAttribute("resetstartsensor") != null) {
279                        tInfo.setResetStartSensor(traininfo.getAttribute("resetstartsensor").getValue().equals("yes"));
280                    }
281                    if (traininfo.getAttribute("traintype") != null) {
282                        tInfo.setTrainType(traininfo.getAttribute("traintype").getValue());
283                    }
284                    if (traininfo.getAttribute("autorun") != null) {
285                        tInfo.setAutoRun(true);
286                        if (traininfo.getAttribute("autorun").getValue().equals("no")) {
287                            tInfo.setAutoRun(false);
288                        }
289                    }
290                    if (traininfo.getAttribute("loadatstartup") != null) {
291                        tInfo.setLoadAtStartup(true);
292                        if (traininfo.getAttribute("loadatstartup").getValue().equals("no")) {
293                            tInfo.setLoadAtStartup(false);
294                        }
295                    }
296                    // here retrieve items related only to automatically run trains if present
297                    if (traininfo.getAttribute("speedfactor") != null) {
298                        tInfo.setSpeedFactor(Float.parseFloat(traininfo.getAttribute("speedfactor").getValue()));
299                    }
300                    if (traininfo.getAttribute("maxspeed") != null) {
301                        tInfo.setMaxSpeed(Float.parseFloat(traininfo.getAttribute("maxspeed").getValue()));
302                    }
303                    if (traininfo.getAttribute("minreliableoperatingspeed") != null) {
304                        tInfo.setMinReliableOperatingSpeed(Float.parseFloat(traininfo.getAttribute("minreliableoperatingspeed").getValue()));
305                    }
306                    if (traininfo.getAttribute("ramprate") != null) {
307                        tInfo.setRampRate(traininfo.getAttribute("ramprate").getValue());
308                    }
309                    tInfo.setTrainDetection(TrainDetection.TRAINDETECTION_WHOLETRAIN);
310                    if (version > 4) {
311                        if (traininfo.getAttribute("traindetection") != null) {
312                            tInfo.setTrainDetection(trainsdectionFromEnumMap.inputFromAttribute(traininfo.getAttribute("traindetection")));
313                        }
314                    }
315                    else {
316                        if (traininfo.getAttribute("resistancewheels").getValue().equals("no")) {
317                            tInfo.setTrainDetection(TrainDetection.TRAINDETECTION_HEADONLY);
318                        }
319                    }
320                    if (traininfo.getAttribute("runinreverse") != null) {
321                        tInfo.setRunInReverse(true);
322                        if (traininfo.getAttribute("runinreverse").getValue().equals("no")) {
323                            tInfo.setRunInReverse(false);
324                        }
325                    }
326                    if (traininfo.getAttribute("sounddecoder") != null) {
327                        tInfo.setSoundDecoder(true);
328                        if (traininfo.getAttribute("sounddecoder").getValue().equals("no")) {
329                            tInfo.setSoundDecoder(false);
330                        }
331                    }
332                    if (traininfo.getAttribute("maxtrainlength") != null) {
333                        tInfo.setMaxTrainLength(Float.parseFloat(traininfo.getAttribute("maxtrainlength").getValue()));
334                    }
335                    if (traininfo.getAttribute("terminatewhendone") != null) {
336                        tInfo.setTerminateWhenDone(false);
337                        if (traininfo.getAttribute("terminatewhendone").getValue().equals("yes")) {
338                            tInfo.setTerminateWhenDone(true);
339                        }
340                    }
341                    if (traininfo.getAttribute("usespeedprofile") != null) {
342                        tInfo.setUseSpeedProfile(false);
343                        if (traininfo.getAttribute("usespeedprofile").getValue().equals("yes")) {
344                            tInfo.setUseSpeedProfile(true);
345                        }
346                    }
347                    if (traininfo.getAttribute("stopbyspeedprofile") != null) {
348                        tInfo.setStopBySpeedProfile(false);
349                        if (traininfo.getAttribute("stopbyspeedprofile").getValue().equals("yes")) {
350                            tInfo.setStopBySpeedProfile(true);
351                        }
352                    }
353                    if (traininfo.getAttribute("stopbyspeedprofileadjust") != null) {
354                        tInfo.setStopBySpeedProfileAdjust(traininfo.getAttribute("stopbyspeedprofileadjust").getFloatValue());
355                    }
356                    if (traininfo.getAttribute("waittime") != null) {
357                        tInfo.setWaitTime(traininfo.getAttribute("waittime").getFloatValue());
358                    }
359                    if (traininfo.getAttribute("blockname") != null) {
360                        tInfo.setBlockName(traininfo.getAttribute("blockname").getValue());
361                    }
362
363                    if (version == 1) {
364                        String parseArray[];
365                        // If you only have a systemname then its everything before the dash
366                        tInfo.setStartBlockId(tInfo.getStartBlockName().split("-")[0]);
367                        // If you have a systemname and username you want everything before the open bracket
368                        tInfo.setStartBlockId(tInfo.getStartBlockId().split("\\(")[0]);
369                        // to guard against a dash in the names, we need the last part, not just [1]
370                        parseArray = tInfo.getStartBlockName().split("-");
371                        tInfo.setStartBlockSeq(-1); // default value
372                        if (parseArray.length > 0) {
373                            try {
374                                tInfo.setStartBlockSeq(Integer.parseInt(parseArray[parseArray.length -1]));
375                            }
376                            catch (Exception Ex) {
377                                log.error("Invalid StartBlockSequence{}",parseArray[parseArray.length -1]);
378                            }
379                        }
380                        // repeat for destination
381                        tInfo.setDestinationBlockId(tInfo.getDestinationBlockName().split("-")[0]);
382                        tInfo.setDestinationBlockId(tInfo.getDestinationBlockId().split("\\(")[0]);
383                        parseArray = tInfo.getDestinationBlockName().split("-");
384                        tInfo.setDestinationBlockSeq(-1);
385                        if (parseArray.length > 0) {
386                            try {
387                                tInfo.setDestinationBlockSeq(Integer.parseInt(parseArray[parseArray.length -1]));
388                            }
389                            catch (Exception Ex) {
390                                log.error("Invalid StartBlockSequence{}",parseArray[parseArray.length -1]);
391                            }
392                        }
393                        // Transit we need the whole thing or the bit before the first open bracket
394                        tInfo.setTransitId(tInfo.getTransitName().split("\\(")[0]);
395                        log.debug("v1: t = {}, bs = {}, be = {}", tInfo.getTransitName(), tInfo.getStartBlockName(), tInfo.getDestinationBlockName());
396                    }
397                    if ( version > 1 ) {
398                        if (traininfo.getAttribute("transitid") != null) {
399                            // there is a transit name selected
400                            tInfo.setTransitId(traininfo.getAttribute("transitid").getValue());
401                        } else {
402                            log.error("Transit id missing when reading TrainInfoFile {}", name);
403                        }
404                        if (traininfo.getAttribute("startblockid") != null) {
405                            // there is a transit name selected
406                            tInfo.setStartBlockId(traininfo.getAttribute("startblockid").getValue());
407                        } else {
408                            log.error("Start block Id missing when reading TrainInfoFile {}", name);
409                        }
410                        if (traininfo.getAttribute("endblockid") != null) {
411                            // there is a transit name selected
412                            tInfo.setDestinationBlockId(traininfo.getAttribute("endblockid").getValue());
413                        } else {
414                            log.error("Destination block Id missing when reading TrainInfoFile {}", name);
415                        }
416                        if (traininfo.getAttribute("startblockseq") != null) {
417                            // there is a transit name selected
418                            try {
419                                tInfo.setStartBlockSeq(traininfo.getAttribute("startblockseq").getIntValue());
420                            }
421                            catch (Exception ex) {
422                                log.error("Start block sequence invalid when reading TrainInfoFile");
423                            }
424                        } else {
425                            log.error("Start block sequence missing when reading TrainInfoFile {}", name);
426                        }
427                        if (traininfo.getAttribute("endblockseq") != null) {
428                            // there is a transit name selected
429                            try {
430                                tInfo.setDestinationBlockSeq(traininfo.getAttribute("endblockseq").getIntValue());
431                            }
432                            catch (Exception ex) {
433                                log.error("Destination block sequence invalid when reading TrainInfoFile {}", name);
434                            }
435                        } else {
436                            log.error("Destination block sequence missing when reading TrainInfoFile {}", name);
437                        }
438                    }
439                    if ( version == 1 || version == 2) {
440                        // Change transit and block names from sysName(userName) to displayName
441                        tInfo.setTransitName(convertName(tInfo.getTransitName()));
442                        tInfo.setStartBlockName(convertName(tInfo.getStartBlockName()));
443                        tInfo.setDestinationBlockName(convertName(tInfo.getDestinationBlockName()));
444                    }
445               }
446            }
447        }
448        return tInfo;
449    }
450
451    public String convertName(String name) {
452        // transit: sys(user), block: sys(user)-n
453        String newName = name;
454
455        Pattern p = Pattern.compile(".+\\((.+)\\)(-\\d+)*");
456        Matcher m = p.matcher(name);
457        if (m.matches()) {
458            log.debug("regex: name = '{}', group 1 = '{}', group 2 = '{}'", name, m.group(1), m.group(2));
459            if (m.group(1) != null) {
460                newName = m.group(1).trim();
461                if (m.group(2) != null) {
462                    newName = newName + m.group(2).trim();
463                }
464            }
465        }
466
467        log.debug("convertName: old = '{}', new = '{}'", name, newName);
468        return newName;
469    }
470
471    /*
472     *  Writes out Dispatcher options to a file in the user's preferences directory
473     */
474    public void writeTrainInfo(TrainInfo tf, String name) throws java.io.IOException {
475        log.debug("entered writeTrainInfo");
476        root = new Element("traininfofile");
477        doc = newDocument(root, dtdLocation + "dispatcher-traininfo.dtd");
478        // add XSLT processing instruction
479        // <?xml-stylesheet type="text/xsl" href="XSLT/block-values.xsl"?>
480        java.util.Map<String, String> m = new java.util.HashMap<>();
481        m.put("type", "text/xsl");
482        m.put("href", xsltLocation + "dispatcher-traininfo.xsl");
483        org.jdom2.ProcessingInstruction p = new org.jdom2.ProcessingInstruction("xml-stylesheet", m);
484        doc.addContent(0, p);
485
486        // save Dispatcher TrainInfo in xml format
487        Element traininfo = new Element("traininfo");
488        // write version number
489        traininfo.setAttribute("version", "6");
490        traininfo.setAttribute("transitname", tf.getTransitName());
491        traininfo.setAttribute("transitid", tf.getTransitId());
492        traininfo.setAttribute("trainname", tf.getTrainName());
493        traininfo.setAttribute("trainusername", tf.getTrainUserName());
494        traininfo.setAttribute("rosterid", tf.getRosterId());
495        traininfo.setAttribute("dccaddress", tf.getDccAddress());
496        traininfo.setAttribute("trainintransit", "" + (tf.getTrainInTransit() ? "yes" : "no"));
497        traininfo.setAttribute("startblockname", tf.getStartBlockName());
498        traininfo.setAttribute("startblockid", tf.getStartBlockId());
499        traininfo.setAttribute("startblockseq", Integer.toString(tf.getStartBlockSeq()));
500        traininfo.setAttribute("endblockname", tf.getDestinationBlockName());
501        traininfo.setAttribute("endblockid", tf.getDestinationBlockId());
502        traininfo.setAttribute("endblockseq", Integer.toString(tf.getDestinationBlockSeq()));
503        traininfo.setAttribute("trainfromroster", "" + (tf.getTrainFromRoster() ? "yes" : "no"));
504        traininfo.setAttribute("trainfromtrains", "" + (tf.getTrainFromTrains() ? "yes" : "no"));
505        traininfo.setAttribute("trainfromuser", "" + (tf.getTrainFromUser() ? "yes" : "no"));
506        traininfo.setAttribute("trainfromsetlater", "" + (tf.getTrainFromSetLater() ? "yes" : "no"));
507        traininfo.setAttribute("priority", Integer.toString(tf.getPriority()));
508        traininfo.setAttribute("traindetection", trainsdectionFromEnumMap.outputFromEnum(tf.getTrainDetection()));
509        traininfo.setAttribute("resetwhendone", "" + (tf.getResetWhenDone() ? "yes" : "no"));
510        switch (tf.getDelayedRestart()) {
511            case ActiveTrain.SENSORDELAY:
512                traininfo.setAttribute("delayedrestart", "sensor");
513                traininfo.setAttribute("delayedrestartsensor", tf.getRestartSensorName());
514                traininfo.setAttribute("resetrestartsensor", "" + (tf.getResetRestartSensor() ? "yes" : "no"));
515                break;
516            case ActiveTrain.TIMEDDELAY:
517                traininfo.setAttribute("delayedrestart", "timed");
518                traininfo.setAttribute("delayedrestarttime", Integer.toString(tf.getRestartDelayMin()));
519                break;
520            default:
521                traininfo.setAttribute("delayedrestart", "no");
522                break;
523        }
524
525        traininfo.setAttribute("reverseatend", "" + (tf.getReverseAtEnd() ? "yes" : "no"));
526        switch (tf.getReverseDelayedRestart()) {
527            case ActiveTrain.SENSORDELAY:
528                traininfo.setAttribute("reversedelayedrestart", "sensor");
529                traininfo.setAttribute("reversedelayedrestartsensor", tf.getReverseRestartSensorName());
530                traininfo.setAttribute("reverseresetrestartsensor", "" + (tf.getReverseResetRestartSensor() ? "yes" : "no"));
531                break;
532            case ActiveTrain.TIMEDDELAY:
533                traininfo.setAttribute("reversedelayedrestart", "timed");
534                traininfo.setAttribute("reversedelayedrestarttime", Integer.toString(tf.getReverseRestartDelayMin()));
535                break;
536            default:
537                traininfo.setAttribute("reversedelayedrestart", "no");
538                break;
539        }
540        if (tf.getDelayedStart() == ActiveTrain.TIMEDDELAY) {
541            traininfo.setAttribute("delayedstart", "timed");
542        } else if (tf.getDelayedStart() == ActiveTrain.SENSORDELAY) {
543            traininfo.setAttribute("delayedstart", "sensor");
544            if (tf.getDelaySensorName() != null) {
545                traininfo.setAttribute("delayedSensor", tf.getDelaySensorName());
546                traininfo.setAttribute("resetstartsensor", "" + (tf.getResetStartSensor() ? "yes" : "no"));
547            }
548        }
549
550        traininfo.setAttribute("terminatewhendone", (tf.getTerminateWhenDone() ? "yes" : "no"));
551        traininfo.setAttribute("departuretimehr", Integer.toString(tf.getDepartureTimeHr()));
552        traininfo.setAttribute("departuretimemin", Integer.toString(tf.getDepartureTimeMin()));
553        traininfo.setAttribute("traintype", tf.getTrainType());
554        traininfo.setAttribute("autorun", "" + (tf.getAutoRun() ? "yes" : "no"));
555        traininfo.setAttribute("loadatstartup", "" + (tf.getLoadAtStartup() ? "yes" : "no"));
556        traininfo.setAttribute("allocatealltheway", "" + (tf.getAllocateAllTheWay() ? "yes" : "no"));
557        traininfo.setAttribute("allocationmethod", Integer.toString(tf.getAllocationMethod()));
558        traininfo.setAttribute("nexttrain", tf.getNextTrain());
559        // here save items related to automatically running active trains
560        traininfo.setAttribute("speedfactor", Float.toString(tf.getSpeedFactor()));
561        traininfo.setAttribute("maxspeed", Float.toString(tf.getMaxSpeed()));
562        traininfo.setAttribute("minreliableoperatingspeed", Float.toString(tf.getMinReliableOperatingSpeed()));
563        traininfo.setAttribute("ramprate", tf.getRampRate());
564        traininfo.setAttribute("runinreverse", "" + (tf.getRunInReverse() ? "yes" : "no"));
565        traininfo.setAttribute("sounddecoder", "" + (tf.getSoundDecoder() ? "yes" : "no"));
566        traininfo.setAttribute("maxtrainlength", Float.toString(tf.getMaxTrainLength()));
567        traininfo.setAttribute("usespeedprofile", "" + (tf.getUseSpeedProfile() ? "yes" : "no"));
568        traininfo.setAttribute("stopbyspeedprofile", "" + (tf.getStopBySpeedProfile() ? "yes" : "no"));
569        traininfo.setAttribute("stopbyspeedprofileadjust", Float.toString(tf.getStopBySpeedProfileAdjust()));
570        traininfo.setAttribute("waittime", Float.toString(tf.getWaitTime()));
571        traininfo.setAttribute("blockname", tf.getBlockName());
572
573        root.addContent(traininfo);
574
575        // write out the file
576        try {
577            if (!checkFile(fileLocation + name)) {
578                // file does not exist, create it
579                File file = new File(fileLocation + name);
580                if (!file.createNewFile()) // create file and check result
581                {
582                    log.error("createNewFile failed");
583                }
584            }
585            // write content to file
586            writeXML(findFile(fileLocation + name), doc);
587        } catch (java.io.IOException ioe) {
588            log.error("IO Exception writing", ioe);
589            throw (ioe);
590        }
591    }
592
593    /**
594     * Get the names of all current TrainInfo files. Returns names as an array
595     * of Strings. Returns an empty array if no files are present. Note: Fill
596     * names still end with .xml or .XML. (Modeled after a method in
597     * RecreateRosterAction.java by Bob Jacobsen)
598     *
599     * @return names as an array or an empty array if none present
600     */
601    public String[] getTrainInfoFileNames() {
602        // ensure preferences will be found for read
603        FileUtil.createDirectory(fileLocation);
604        // create an array of file names from roster dir in preferences, count entries
605        List<String> names = new ArrayList<>();
606        log.debug("directory of TrainInfoFiles is {}", fileLocation);
607        File fp = new File(fileLocation);
608        if (fp.exists()) {
609            String[] xmlList = fp.list(new XmlFilenameFilter());
610            if (xmlList!=null) {
611                names.addAll(Arrays.asList(xmlList));
612            }
613        }
614        // Sort the resulting array
615        names.sort((s1, s2) -> {
616            return s1.compareTo(s2);
617        });
618        return names.toArray(new String[names.size()]);
619    }
620
621    /**
622     * Delete a specified TrainInfo file.
623     *
624     * @param name the file to delete
625     */
626    public void deleteTrainInfoFile(String name) {
627        // locate the file and delete it if it exists
628        File f = new File(fileLocation + name);
629        if (!f.delete()) { // delete file and check success
630            log.error("failed to delete TrainInfo file - {}", name);
631        }
632    }
633
634    private final static Logger log = LoggerFactory.getLogger(TrainInfoFile.class);
635}