001package jmri.jmrit.dispatcher;
002
003import java.io.File;
004import java.util.Set;
005import jmri.InstanceManager;
006import jmri.InstanceManagerAutoDefault;
007import jmri.ScaleManager;
008import jmri.configurexml.AbstractXmlAdapter.EnumIO;
009import jmri.configurexml.AbstractXmlAdapter.EnumIoNamesNumbers;
010import jmri.jmrit.dispatcher.DispatcherFrame.TrainsFrom;
011import jmri.jmrit.display.EditorManager;
012import jmri.jmrit.display.layoutEditor.LayoutEditor;
013import jmri.util.FileUtil;
014import org.jdom2.Document;
015import org.jdom2.Element;
016import org.slf4j.Logger;
017import org.slf4j.LoggerFactory;
018
019/**
020 * Handles reading and writing of Dispatcher options to disk as an XML file
021 * called "dispatcher-options.xml" in the user's preferences area.
022 * <p>
023 * This class manipulates the files conforming to the dispatcher-options DTD
024 * <p>
025 * The file is written when the user requests that options be saved. If the
026 * dispatcheroptions.xml file is present when Dispatcher is started, it is read
027 * and options set accordingly
028 * <p>
029 * This file is part of JMRI.
030 * <p>
031 * JMRI is open source software; you can redistribute it and/or modify it under
032 * the terms of version 2 of the GNU General Public License as published by the
033 * Free Software Foundation. See the "COPYING" file for a copy of this license.
034 * <p>
035 * JMRI is distributed in the hope that it will be useful, but WITHOUT ANY
036 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
037 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
038 *
039 * @author Dave Duchamp Copyright (C) 2008
040 */
041public class OptionsFile extends jmri.jmrit.XmlFile implements InstanceManagerAutoDefault {
042
043    public OptionsFile() {
044        super();
045    }
046
047    static final EnumIO<DispatcherFrame.TrainsFrom> trainsFromEnumMap = new EnumIoNamesNumbers<>(DispatcherFrame.TrainsFrom.class);
048
049    // operational variables
050    protected DispatcherFrame dispatcher = null;
051    private static String defaultFileName = FileUtil.getUserFilesPath() + "dispatcheroptions.xml";
052
053    public static void setDefaultFileName(String testLocation) {
054        defaultFileName = testLocation;
055    }
056    private Document doc = null;
057    private Element root = null;
058
059    /**
060     * Read Dispatcher Options from a file in the user's preferences directory.
061     * If the file containing Dispatcher Options does not exist, this routine returns quietly.
062     * <p>The lename attribute is deprecated at 5.1.3. The current value will be retained.
063     *
064     * @param f   The dispatcher instance.
065     * @throws org.jdom2.JDOMException  if dispatcher parameter logically incorrect
066     * @throws java.io.IOException    if dispatcher parameter not found
067     */
068    public void readDispatcherOptions(DispatcherFrame f) throws org.jdom2.JDOMException, java.io.IOException {
069        // check if file exists
070        if (checkFile(defaultFileName)) {
071            // file is present,
072            log.debug("Reading Dispatcher options from file {}", defaultFileName);
073            root = rootFromName(defaultFileName);
074            dispatcher = f;
075            if (root != null) {
076                // there is a file
077                Element options = root.getChild("options");
078                if (options != null) {
079                    // there are options defined, read and set Dispatcher options
080                    if (options.getAttribute("lename") != null) {
081                        // there is a layout editor name selected
082                        String leName = options.getAttribute("lename").getValue();
083
084                        // get list of Layout Editor panels
085                        // Note: While editor is deprecated, retain the value for backward compatibility.
086                        Set<LayoutEditor> layoutEditorList = InstanceManager.getDefault(EditorManager.class).getAll(LayoutEditor.class);
087                        if (layoutEditorList.isEmpty()) {
088                            log.warn("Dispatcher options specify a Layout Editor panel that is not present.");
089                        } else {
090                            boolean found = false;
091                            for (LayoutEditor editor : layoutEditorList) {
092                                if (leName.equals(editor.getTitle())) {
093                                    found = true;
094                                    dispatcher.setLayoutEditor(editor);
095                                }
096                            }
097                            if (!found) {
098                                log.warn("Layout Editor panel - {} - not found.", leName);
099                            }
100                        }
101                    }
102                    if (options.getAttribute("usesignaltype") != null) {
103                        switch (options.getAttribute("usesignaltype").getValue()) {
104                            case "signalmast":
105                                dispatcher.setSignalType(DispatcherFrame.SIGNALMAST);
106                                break;
107                            case "sectionsallocated":
108                                dispatcher.setSignalType(DispatcherFrame.SECTIONSALLOCATED);
109                                break;
110                            default:
111                                dispatcher.setSignalType(DispatcherFrame.SIGNALHEAD);
112                        }
113                    }
114                    if (options.getAttribute("useconnectivity") != null) {
115                        dispatcher.setUseConnectivity(true);
116                        if (options.getAttribute("useconnectivity").getValue().equals("no")) {
117                            dispatcher.setUseConnectivity(false);
118                        }
119                    }
120                    if (options.getAttribute("trainsfrom") != null) {
121                        dispatcher.setTrainsFrom(trainsFromEnumMap.inputFromAttribute(options.getAttribute("trainsfrom")));
122                    } else {
123                        log.warn("Old Style dispatcheroptions file found - will be converted when saved");
124                    if (options.getAttribute("trainsfromroster") != null &&
125                            options.getAttribute("trainsfromroster").getValue().equals("yes")) {
126                        dispatcher.setTrainsFrom(TrainsFrom.TRAINSFROMROSTER);
127                    } else if (options.getAttribute("trainsfromtrains") != null &&
128                            options.getAttribute("trainsfromtrains").getValue().equals("no")) {
129                        dispatcher.setTrainsFrom(TrainsFrom.TRAINSFROMOPS);
130                    } else if (options.getAttribute("trainsfromuser") != null &&
131                            options.getAttribute("trainsfromuser").getValue().equals("no")) {
132                        dispatcher.setTrainsFrom(TrainsFrom.TRAINSFROMUSER);
133                    }
134                    }
135                    if (options.getAttribute("autoallocate") != null) {
136                        dispatcher.setAutoAllocate(false);
137                        if (options.getAttribute("autoallocate").getValue().equals("yes")) {
138                            dispatcher.setAutoAllocate(true);
139                        }
140                    }
141                    if (options.getAttribute("autorelease") != null) {
142                        dispatcher.setAutoRelease(false);
143                        if (options.getAttribute("autorelease").getValue().equals("yes")) {
144                            dispatcher.setAutoRelease(true);
145                        }
146                    }
147                    if (options.getAttribute("autoturnouts") != null) {
148                        dispatcher.setAutoTurnouts(true);
149                        if (options.getAttribute("autoturnouts").getValue().equals("no")) {
150                            dispatcher.setAutoTurnouts(false);
151                        }
152                    }
153                    if (options.getAttribute("trustknownturnouts") != null) {
154                        dispatcher.setTrustKnownTurnouts(false);
155                        if (options.getAttribute("trustknownturnouts").getValue().equals("yes")) {
156                            dispatcher.setTrustKnownTurnouts(true);
157                        }
158                    }
159                    if (options.getAttribute("useturnoutconnectiondelay") != null) {
160                        dispatcher.setUseTurnoutConnectionDelay(false);
161                        if (options.getAttribute("useturnoutconnectiondelay").getValue().equals("yes")) {
162                            dispatcher.setUseTurnoutConnectionDelay(true);
163                        }
164                    }
165                    if (options.getAttribute("minthrottleinterval") != null) {
166                        String s = (options.getAttribute("minthrottleinterval")).getValue();
167                        dispatcher.setMinThrottleInterval(Integer.parseInt(s));
168                    }
169                    if (options.getAttribute("fullramptime") != null) {
170                        String s = (options.getAttribute("fullramptime")).getValue();
171                        dispatcher.setFullRampTime(Integer.parseInt(s));
172                    }
173                    if (options.getAttribute("hasoccupancydetection") != null) {
174                        dispatcher.setHasOccupancyDetection(true);
175                        if (options.getAttribute("hasoccupancydetection").getValue().equals("no")) {
176                            dispatcher.setHasOccupancyDetection(false);
177                        }
178                    }
179                    if (options.getAttribute("sslcheckdirectionsensors") != null) {
180                        dispatcher.setSetSSLDirectionalSensors(true);
181                        if (options.getAttribute("sslcheckdirectionsensors").getValue().equals("no")) {
182                            dispatcher.setSetSSLDirectionalSensors(false);
183                        }
184                    }
185                    if (options.getAttribute("shortactivetrainnames") != null) {
186                        dispatcher.setShortActiveTrainNames(true);
187                        if (options.getAttribute("shortactivetrainnames").getValue().equals("no")) {
188                            dispatcher.setShortActiveTrainNames(false);
189                        }
190                    }
191                    if (options.getAttribute("shortnameinblock") != null) {
192                        dispatcher.setShortNameInBlock(true);
193                        if (options.getAttribute("shortnameinblock").getValue().equals("no")) {
194                            dispatcher.setShortNameInBlock(false);
195                        }
196                    }
197                    if (options.getAttribute("extracolorforallocated") != null) {
198                        dispatcher.setExtraColorForAllocated(true);
199                        if (options.getAttribute("extracolorforallocated").getValue().equals("no")) {
200                            dispatcher.setExtraColorForAllocated(false);
201                        }
202                    }
203                    if (options.getAttribute("nameinallocatedblock") != null) {
204                        dispatcher.setNameInAllocatedBlock(true);
205                        if (options.getAttribute("nameinallocatedblock").getValue().equals("no")) {
206                            dispatcher.setNameInAllocatedBlock(false);
207                        }
208                    }
209                    if (options.getAttribute("supportvsdecoder") != null) {
210                        dispatcher.setSupportVSDecoder(true);
211                        if (options.getAttribute("supportvsdecoder").getValue().equals("no")) {
212                            dispatcher.setSupportVSDecoder(false);
213                        }
214                    }
215                    if (options.getAttribute("layoutscale") != null) {
216                        String s = (options.getAttribute("layoutscale")).getValue();
217                        dispatcher.setScale(ScaleManager.getScale(s));
218                    }
219                    if (options.getAttribute("usescalemeters") != null) {
220                        dispatcher.setUseScaleMeters(true);
221                        if (options.getAttribute("usescalemeters").getValue().equals("no")) {
222                            dispatcher.setUseScaleMeters(false);
223                        }
224                    }
225                    if (options.getAttribute("userosterentryinblock") != null) {
226                        dispatcher.setRosterEntryInBlock(false);
227                        if (options.getAttribute("userosterentryinblock").getValue().equals("yes")) {
228                            dispatcher.setRosterEntryInBlock(true);
229                        }
230                    }
231                    if (options.getAttribute("stoppingspeedname") != null) {
232                        dispatcher.setStoppingSpeedName((options.getAttribute("stoppingspeedname")).getValue());
233                    }
234
235                    log.debug("  Options: {}, Detection={}, AutoAllocate={}, AutoTurnouts={}, SetSSLDirectionSensors={}",
236                            (dispatcher.getSignalTypeString()),
237                            (dispatcher.getAutoAllocate()?"yes":"no"),
238                            (dispatcher.getAutoTurnouts()?"yes":"no"),
239                            (dispatcher.getSetSSLDirectionalSensors()?"yes":"no"));
240                }
241            }
242        } else {
243            log.debug("No Dispatcher options file found at {}, using defaults", defaultFileName);
244        }
245    }
246
247    /**
248     * Write out Dispatcher options to a file in the user's preferences directory.
249     * <p>The lename attribute is deprecated at 5.1.3.  The current value will be retained.
250     * @param f Dispatcher instance.
251     * @throws java.io.IOException Thrown if dispatcher option file not found
252     */
253    public void writeDispatcherOptions(DispatcherFrame f) throws java.io.IOException {
254        log.debug("Saving Dispatcher options to file {}", defaultFileName);
255        dispatcher = f;
256        root = new Element("dispatcheroptions");
257        doc = newDocument(root, dtdLocation + "dispatcher-options.dtd");
258        // add XSLT processing instruction
259        // <?xml-stylesheet type="text/xsl" href="XSLT/block-values.xsl"?>
260        java.util.Map<String, String> m = new java.util.HashMap<>();
261        m.put("type", "text/xsl");
262        m.put("href", xsltLocation + "dispatcheroptions.xsl");
263        org.jdom2.ProcessingInstruction p = new org.jdom2.ProcessingInstruction("xml-stylesheet", m);
264        doc.addContent(0, p);
265
266        // save Dispatcher Options in xml format
267        Element options = new Element("options");
268        LayoutEditor le = dispatcher.getLayoutEditor();
269        if (le != null) {
270            options.setAttribute("lename", le.getTitle());
271        }
272        options.setAttribute("useconnectivity", "" + (dispatcher.getUseConnectivity() ? "yes" : "no"));
273        options.setAttribute("trainsfrom", trainsFromEnumMap.outputFromEnum(dispatcher.getTrainsFrom()));
274        options.setAttribute("autoallocate", "" + (dispatcher.getAutoAllocate() ? "yes" : "no"));
275        options.setAttribute("autorelease", "" + (dispatcher.getAutoRelease() ? "yes" : "no"));
276        options.setAttribute("autoturnouts", "" + (dispatcher.getAutoTurnouts() ? "yes" : "no"));
277        options.setAttribute("trustknownturnouts", "" + (dispatcher.getTrustKnownTurnouts() ? "yes" : "no"));
278        options.setAttribute("useturnoutconnectiondelay", "" + (dispatcher.getUseTurnoutConnectionDelay() ? "yes" : "no"));
279        options.setAttribute("minthrottleinterval", "" + (dispatcher.getMinThrottleInterval()));
280        options.setAttribute("fullramptime", "" + (dispatcher.getFullRampTime()));
281        options.setAttribute("hasoccupancydetection", "" + (dispatcher.getHasOccupancyDetection() ? "yes" : "no"));
282        options.setAttribute("sslcheckdirectionsensors", "" + (dispatcher.getSetSSLDirectionalSensors() ? "yes" : "no"));
283        options.setAttribute("shortactivetrainnames", "" + (dispatcher.getShortActiveTrainNames() ? "yes" : "no"));
284        options.setAttribute("shortnameinblock", "" + (dispatcher.getShortNameInBlock() ? "yes" : "no"));
285        options.setAttribute("extracolorforallocated", "" + (dispatcher.getExtraColorForAllocated() ? "yes" : "no"));
286        options.setAttribute("nameinallocatedblock", "" + (dispatcher.getNameInAllocatedBlock() ? "yes" : "no"));
287        options.setAttribute("supportvsdecoder", "" + (dispatcher.getSupportVSDecoder() ? "yes" : "no"));
288        options.setAttribute("layoutscale", dispatcher.getScale().getScaleName());
289        options.setAttribute("usescalemeters", "" + (dispatcher.getUseScaleMeters() ? "yes" : "no"));
290        options.setAttribute("userosterentryinblock", "" + (dispatcher.getRosterEntryInBlock() ? "yes" : "no"));
291        options.setAttribute("stoppingspeedname", dispatcher.getStoppingSpeedName());
292        switch (dispatcher.getSignalType()) {
293            case DispatcherFrame.SIGNALMAST:
294                options.setAttribute("usesignaltype", "signalmast");
295                break;
296            case DispatcherFrame.SECTIONSALLOCATED:
297                options.setAttribute("usesignaltype", "sectionsallocated");
298                break;
299            default:
300                options.setAttribute("usesignaltype", "signalhead");
301        }
302        root.addContent(options);
303
304        // write out the file
305        try {
306            if (!checkFile(defaultFileName)) {
307                // file does not exist, create it
308                File file = new File(defaultFileName);
309                if (!file.createNewFile()) // create new file and check result
310                {
311                    log.error("createNewFile failed");
312                }
313            }
314            // write content to file
315            writeXML(findFile(defaultFileName), doc);
316        } catch (java.io.IOException ioe) {
317            log.error("IO Exception {}", ioe.getMessage());
318            throw (ioe);
319        }
320    }
321
322    private final static Logger log = LoggerFactory.getLogger(OptionsFile.class);
323}