001package jmri.jmrix.openlcb.configurexml;
002
003import java.io.File;
004import java.io.FileNotFoundException;
005import java.io.IOException;
006import java.util.List;
007
008import org.jdom2.Document;
009import org.jdom2.Element;
010import org.jdom2.JDOMException;
011import org.jdom2.ProcessingInstruction;
012
013import org.openlcb.NodeID; 
014
015import jmri.Application;
016import jmri.jmrit.XmlFile;
017import jmri.jmrix.openlcb.OlcbNodeGroupStore;
018import jmri.util.FileUtil;
019
020/**
021 * Concrete implementation of abstract {@link jmri.jmrit.XmlFile} for
022 * the {@link jmri.jmrix.openlcb.OlcbNodeGroupStore}.
023 *
024 * @author Bob Jacobsen Copyright (C) 2024
025 * @since 5.11.1
026 */
027public class OlcbNodeGroupStoreXml extends XmlFile {
028
029    OlcbNodeGroupStore store; 
030    
031    public OlcbNodeGroupStoreXml(OlcbNodeGroupStore store, String baseFileName){
032        this.store = store;
033        BASE_FILENAME = baseFileName;
034    }
035
036    public void store() throws java.io.IOException {
037        log.debug("Storing using file: {}", getDefaultFileName());
038        createFile(getDefaultFileName(), true);
039        try {
040            log.debug("about to call writeFile");
041            writeFile(getDefaultFileName());
042        } catch (FileNotFoundException ex) {
043            log.error("File not found while writing node group file, may not be complete", ex);
044        }
045    }
046
047    public void load() {
048        log.debug("Loading...");
049        try {
050            readFile(getDefaultFileName());
051        } catch (JDOMException | IOException ex) {
052            log.error("Exception during node group file reading", ex);
053        }
054        log.debug("load complete...");
055    }
056
057    private File createFile(String fileName, boolean backup) {
058        if (backup) {
059            makeBackupFile(fileName);
060        }
061
062        File file = null;
063        try {
064            if (!checkFile(fileName)) {
065                log.debug("file check done");
066                // The file does not exist, create it before writing
067                file = new File(fileName);
068                File parentDir = file.getParentFile();
069                log.debug("before exists {}", parentDir);
070                if (!parentDir.exists()) {
071                    if (!parentDir.mkdir()) {
072                        log.error("Directory wasn't created");
073                    }
074                }
075                if (file.createNewFile()) {
076                    log.debug("New file created");
077                }
078           } else {
079              file = new File(fileName);
080           }
081       } catch (java.io.IOException ex) {
082          log.error("Exception while creating IdTag file, may not be complete", (Object) ex);
083       }
084       return file;
085   }
086
087   private void writeFile(String fileName) throws FileNotFoundException, java.io.IOException {
088        log.debug("writeFile {}", fileName);
089        // This is taken in large part from "Java and XML" page 368
090        File file = findFile(fileName);
091        if (file == null) {
092           file = new File(fileName);
093        }
094        // Create root element
095        Element root = new Element("nodegroupassociations");              // NOI18N
096        root.setAttribute("noNamespaceSchemaLocation", // NOI18N
097        "http://jmri.org/xml/schema/nodegroupassociations.xsd", // NOI18N
098        org.jdom2.Namespace.getNamespace("xsi", // NOI18N
099        "http://www.w3.org/2001/XMLSchema-instance")); // NOI18N
100        Document doc = newDocument(root);
101
102        // add XSLT processing instruction
103        java.util.Map<String, String> m = new java.util.HashMap<>();
104        m.put("type", "text/xsl"); // NOI18N
105        m.put("href", xsltLocation + "nodegroupassociations.xsl"); // NOI18N
106        ProcessingInstruction p = new ProcessingInstruction("xml-stylesheet", m); // NOI18N
107        doc.addContent(0, p);
108
109        Element values;
110
111        // Loop through associations
112        for (String group : store.getGroupNames()) {
113            for (NodeID node : store.getGroupNodes(group)) {
114                root.addContent(values = new Element("association"));                                              // NOI18N
115                values.addContent(new Element("node").addContent(node.toString()) );     // NOI18N
116                values.addContent(new Element("group").addContent(group)); // NOI18N
117           }
118        }
119        log.debug("write to {}", file);
120        writeXML(file, doc);
121    }
122
123    private void readFile(String fileName) throws org.jdom2.JDOMException, java.io.IOException, IllegalArgumentException {
124        // Check file exists
125        if (findFile(fileName) == null) {
126            log.warn("{} file could not be found", fileName);
127            return;
128        }
129
130        // Find root
131        Element root = rootFromName(fileName);
132        if (root == null) {
133            log.warn("{} file could not be read", fileName);
134            return;
135        }
136
137        // Node association information
138        List<Element> l = root.getChildren("association"); // NOI18N
139        for (Element e : l) {
140            NodeID node = new NodeID(e.getChild("node").getText()); // NOI18N
141            String group = e.getChild("group").getText(); // NOI18N
142            log.trace("load entry {} {}", node, group);
143            store.addNodeToGroup(node, group);
144        }
145    }
146
147    public String getDefaultFileName() {
148        return getFileLocation() + getDirectoryName() + File.separator + getFileName();
149    }
150
151    private static final String DIRECTORY_NAME = "idtags"; // NOI18N
152
153    public String getDirectoryName() {
154        return DIRECTORY_NAME;
155    }
156
157    private String BASE_FILENAME = "NodeGroupAssociations.xml"; // NOI18N
158
159    public String getFileName() {
160        String appName = Application.getApplicationName();
161        if (appName.equals("LccPro")) appName = "PanelPro";
162        String retval = appName + BASE_FILENAME;
163        // as a special case, the LccPro application uses
164        // the PanelPro file
165        jmri.util.LoggingUtil.infoOnce(log, "Using "+retval+" for LCC node group storage");
166        return retval;
167    }
168
169    /**
170     * Absolute path to location of IdTag files.
171     *
172     * @return path to location
173     */
174    public String getFileLocation() {
175        return FILE_LOCATION;
176    }
177
178    private final String FILE_LOCATION = FileUtil.getUserFilesPath();
179
180    private final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(OlcbNodeGroupStoreXml.class);
181
182}