001package jmri.jmrit.operations;
002
003import java.io.*;
004
005import org.jdom2.JDOMException;
006import org.slf4j.Logger;
007import org.slf4j.LoggerFactory;
008
009import jmri.InstanceManager;
010import jmri.jmrit.XmlFile;
011import jmri.jmrit.operations.locations.LocationManagerXml;
012import jmri.jmrit.operations.rollingstock.cars.CarManagerXml;
013import jmri.jmrit.operations.rollingstock.engines.EngineManagerXml;
014import jmri.jmrit.operations.routes.RouteManagerXml;
015import jmri.jmrit.operations.setup.OperationsSetupXml;
016import jmri.jmrit.operations.trains.TrainManagerXml;
017import jmri.util.FileUtil;
018
019/**
020 * Loads and stores the operation setup using xml files.
021 *
022 * @author Daniel Boudreau Copyright (C) 2008
023 */
024public abstract class OperationsXml extends XmlFile {
025
026    /**
027     * Store the all of the operation train objects in the default place,
028     * including making a backup if needed
029     */
030    public void writeOperationsFile() {
031        createFile(getDefaultOperationsFilename(), true); // make backup
032        try {
033            writeFile(getDefaultOperationsFilename());
034        } catch (IOException e) {
035            log.error("Exception while writing operation file, may not be complete: {}", e.getLocalizedMessage());
036        }
037    }
038
039    protected void load() {
040        try {
041            readFile(getDefaultOperationsFilename());
042        } catch (IOException | JDOMException e) {
043            log.error("Exception during operations file reading: {}", e.getLocalizedMessage());
044        }
045    }
046    
047    protected File createFile(String fullPathName) {
048        return createFile(fullPathName, false); // no backup
049    }
050
051    protected File createFile(String fullPathName, boolean backupFile) {
052        if (backupFile) {
053            makeBackupFile(fullPathName);
054        }
055        File file = null;
056        try {
057            if (!checkFile(fullPathName)) {
058                // log.debug("File "+fullPathName+ " does not exist, creating it");
059                // The file does not exist, create it before writing
060                file = new File(fullPathName);
061                File parentDir = file.getParentFile();
062                if (!parentDir.exists()) {
063                    if (!parentDir.mkdir()) {
064                        log.error("Directory wasn't created");
065                    }
066                }
067                if (file.createNewFile()) {
068                    log.debug("File created {}", fullPathName);
069                }
070            } else {
071                file = new File(fullPathName);
072            }
073        } catch (IOException e) {
074            log.error("Exception while creating operations file, may not be complete: {}", e.getLocalizedMessage());
075        }
076        return file;
077    }
078    
079    protected void createDirectory(String fullPathName) {
080        if (!checkFile(fullPathName)) {
081            File file = new File(fullPathName);
082            File parentDir = file.getParentFile();
083            if (!parentDir.exists()) {
084                if (!parentDir.mkdir()) {
085                    log.error("Directory wasn't created");
086                }
087            }
088            if (file.mkdirs()) {
089                log.debug("Directory created {}", fullPathName);
090            }
091        }
092    }
093
094    protected void writeFile(String filename) throws FileNotFoundException, IOException {
095        log.error("writeFile not overridden");
096    }
097
098    /**
099     * @param filename The string file name.
100     * @throws org.jdom2.JDOMException Due to XML parsing error
101     * @throws java.io.IOException     Due to trouble accessing named file
102     */
103    abstract public void readFile(String filename) throws org.jdom2.JDOMException, java.io.IOException;
104
105    private boolean dirty = false;
106
107    public void setDirty(boolean b) {
108        dirty = b;
109    }
110
111    public boolean isDirty() {
112        return dirty;
113    }
114
115    public void writeFileIfDirty() {
116        if (isDirty()) {
117            writeOperationsFile();
118        }
119    }
120
121    public String getDefaultOperationsFilename() {
122        return getFileLocation() + getOperationsDirectoryName() + File.separator + getOperationsFileName();
123    }
124
125    public static void setOperationsDirectoryName(String name) {
126        operationsDirectoryName = name;
127    }
128
129    public static String getOperationsDirectoryName() {
130        return operationsDirectoryName;
131    }
132
133    private static String operationsDirectoryName = "operations"; // NOI18N
134
135    public void setOperationsFileName(String name) {
136        operationsFileName = name;
137    }
138
139    public String getOperationsFileName() {
140        return operationsFileName;
141    }
142
143    private String operationsFileName = "DefaultOperations.xml"; // should be overridden // NOI18N
144
145    /**
146     * Absolute path to location of Operations files.
147     * <p>
148     * Default is in the user's files path, but can be set to anything.
149     *
150     * @return The string path name.
151     *
152     * @see jmri.util.FileUtil#getUserFilesPath()
153     */
154    public static String getFileLocation() {
155        return fileLocation;
156    }
157
158    /**
159     * Set path to location of Operations files.
160     * <p>
161     * Default is in the user's files path, but can be set to anything.
162     *
163     * @param location path to file, including trailing file separator.
164     */
165    public static void setFileLocation(String location) {
166        fileLocation = location;
167    }
168
169    private static String fileLocation = FileUtil.getUserFilesPath();
170
171    /**
172     * Checks name for the file control characters:
173     *
174     * @param name The string to check for a valid file name.
175     * @return true if name is okay, false if name contains a control character.
176     */
177    public static boolean checkFileName(String name) {
178        if (name.contains(".") || name.contains("<") || name.contains(">") // NOI18N
179                || name.contains(":") || name.contains("\"") || name.contains("\\") // NOI18N
180                || name.contains("/") || name.contains("|") || name.contains("?") // NOI18N
181                || name.contains("*")) { // NOI18N
182            return false;
183        }
184        return true;
185    }
186
187    /**
188     * Saves operation files that have been modified.
189     */
190    public static void save() {
191        InstanceManager.getDefault(OperationsSetupXml.class).writeFileIfDirty();
192        InstanceManager.getDefault(LocationManagerXml.class).writeFileIfDirty(); // Need to save "moves" for track location
193        InstanceManager.getDefault(RouteManagerXml.class).writeFileIfDirty(); // Only if user used setX&Y
194        InstanceManager.getDefault(CarManagerXml.class).writeFileIfDirty(); // save train assignments
195        InstanceManager.getDefault(EngineManagerXml.class).writeFileIfDirty(); // save train assignments
196        InstanceManager.getDefault(TrainManagerXml.class).writeFileIfDirty(); // save train changes
197    }
198
199    /**
200     * Checks to see if any operations files are dirty
201     *
202     * @return True if any operations parameters have been modified.
203     */
204    public static boolean areFilesDirty() {
205        return InstanceManager.getDefault(OperationsSetupXml.class).isDirty()
206                || InstanceManager.getDefault(LocationManagerXml.class).isDirty()
207                || InstanceManager.getDefault(RouteManagerXml.class).isDirty()
208                || InstanceManager.getDefault(CarManagerXml.class).isDirty()
209                || InstanceManager.getDefault(EngineManagerXml.class).isDirty()
210                || InstanceManager.getDefault(TrainManagerXml.class).isDirty();
211    }
212
213    private final static Logger log = LoggerFactory.getLogger(OperationsXml.class);
214
215}