001package jmri.jmrix.openlcb.swing.eventtable.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; 011 012import jmri.jmrit.XmlFile; 013//import jmri.jmrix.openlcb.OlcbEventNameStore; 014import jmri.jmrix.openlcb.swing.eventtable.EventTableDataModel; 015import jmri.util.FileUtil; 016 017import org.openlcb.EventID; 018import org.openlcb.NodeID; 019 020/** 021 * Persistence for the content of a EventTableDataModel 022 * <p> 023 * Note that this is explicitly invoked by 024 * {@link jmri.jmrix.openlcb.swing.eventtable.EventTableDataModel} 025 * and is not invoked by the general "store all" mechanism used to store layout files. 026 * 027 * @author Bob Jacobsen Copyright (C) 2025, 2026 028 * @since 5.17.3 029 */ 030public class EventTableDataModelXml extends XmlFile { // note final for testing 031 032 public EventTableDataModelXml(EventTableDataModel model) { 033 this.model = model; 034 } 035 036 EventTableDataModel model; 037 038 public void store() throws java.io.IOException { 039 log.debug("Storing using file: {}", getDefaultModelFileName()); 040 createFile(getDefaultModelFileName(), true); 041 try { 042 writeFile(getDefaultModelFileName()); 043 } catch (FileNotFoundException ex) { 044 log.error("File not found while writing Data Model file, may not be complete", ex); 045 } 046 } 047 048 public void load() { 049 log.debug("Loading..."); 050 try { 051 readFile(getDefaultModelFileName()); 052 } catch (JDOMException | IOException ex) { 053 log.error("Exception during Data Model file reading", ex); 054 } 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 // The file does not exist, create it before writing 066 file = new File(fileName); 067 File parentDir = file.getParentFile(); 068 if (!parentDir.exists()) { 069 if (!parentDir.mkdir()) { 070 log.error("Directory wasn't created"); 071 } 072 } 073 if (file.createNewFile()) { 074 log.debug("New file created"); 075 } 076 } else { 077 file = new File(fileName); 078 } 079 } catch (java.io.IOException ex) { 080 log.error("Exception while creating Event Name file, may not be complete", (Object) ex); 081 } 082 return file; 083 } 084 085 private void writeFile(String fileName) throws FileNotFoundException, java.io.IOException { 086 log.debug("writeFile {}", fileName); 087 // This is taken in large part from "Java and XML" page 368 088 File file = findFile(fileName); 089 if (file == null) { 090 file = new File(fileName); 091 } 092 // Create root element 093 Element root = new Element("eventTableStore"); // NOI18N 094 // root.setAttribute("noNamespaceSchemaLocation", // NOI18N 095 // "http://jmri.org/xml/schema/idtags.xsd", // NOI18N 096 // org.jdom2.Namespace.getNamespace("xsi", // NOI18N 097 // "http://www.w3.org/2001/XMLSchema-instance")); // NOI18N 098 Document doc = newDocument(root); 099 100 // add XSLT processing instruction 101 // java.util.Map<String, String> m = new java.util.HashMap<>(); 102 // m.put("type", "text/xsl"); // NOI18N 103 // m.put("href", xsltLocation + "idtags.xsl"); // NOI18N 104 // ProcessingInstruction p = new ProcessingInstruction("xml-stylesheet", m); // NOI18N 105 // doc.addContent(0, p); 106 107 Element values; 108 109 // Loop through node names 110 root.addContent(values = new Element("nodes")); // NOI18N 111 for (NodeID nid : model.getNodeIDMatches()) { 112 var name = model.getNodeName(nid); 113 log.debug("Writing node name: {} node {}", name, nid); 114 var element = new Element("node"); 115 var nameElement = new Element("name"); 116 nameElement.addContent(name); 117 var nodeIdElement = new Element("nodeID"); 118 nodeIdElement.addContent(nid.toString()); 119 element.addContent(nodeIdElement); 120 element.addContent(nameElement); 121 values.addContent(element); 122 } 123 124 // Loop through producer node <-> event ID associations 125 root.addContent(values = new Element("producers")); // NOI18N 126 for (var memo : EventTableDataModel.getMemos()) { 127 if (memo.eventID != null && memo.producer != null) { 128 var event = memo.eventID.toShortString(); 129 var range = memo.rangeSuffix; 130 var producer = memo.producer.toString(); 131 var pname = memo.producerName; 132 133 var element = new Element("producer"); 134 var eventElement = new Element("event"); 135 eventElement.addContent(event); 136 var rangeElement = new Element("range"); 137 rangeElement.addContent(range); 138 var producerElement = new Element("producer"); 139 producerElement.addContent(producer); 140 var pnameElement = new Element("pName"); 141 pnameElement.addContent(pname); 142 element.addContent(eventElement); 143 element.addContent(rangeElement); 144 element.addContent(producerElement); 145 element.addContent(pnameElement); 146 values.addContent(element); 147 148 } 149 } 150 151 // Loop through consumer node <-> event ID associations 152 root.addContent(values = new Element("consumers")); // NOI18N 153 for (var memo : EventTableDataModel.getMemos()) { 154 if (memo.eventID != null && memo.consumer != null) { 155 var event = memo.eventID.toShortString(); 156 var range = memo.rangeSuffix; 157 var consumer = memo.consumer.toString(); 158 var cname = memo.consumerName; 159 160 var element = new Element("consumer"); 161 var eventElement = new Element("event"); 162 eventElement.addContent(event); 163 var rangeElement = new Element("range"); 164 rangeElement.addContent(range); 165 var consumerElement = new Element("consumer"); 166 consumerElement.addContent(consumer); 167 var cnameElement = new Element("cName"); 168 cnameElement.addContent(cname); 169 element.addContent(eventElement); 170 element.addContent(rangeElement); 171 element.addContent(consumerElement); 172 element.addContent(cnameElement); 173 values.addContent(element); 174 175 } 176 } 177 178 // Store the event ID <-> Descriptions information 179 root.addContent(values = new Element("descriptions")); // NOI18N 180 for (var event : model.getAuxiliaryKnownEvents()) { 181 for (var description : model.getAuxiliaryInformation(event) ) { 182 log.trace("for event {} write {}", event, description); 183 var eventElement = new Element("event"); 184 eventElement.addContent(event.toShortString()); 185 var descriptionElement = new Element("description"); 186 descriptionElement.addContent(description); 187 var valueElement = new Element("value"); 188 values.addContent(valueElement); 189 valueElement.addContent(eventElement); 190 valueElement.addContent(descriptionElement); 191 } 192 } 193 194 writeXML(file, doc); 195 } 196 197 protected String getDefaultModelFileName() { // protected for testing 198 return getFileLocation() + getModelFileDirectoryName() + File.separator + getModelDataFileName(); 199 } 200 201 private String getFileLocation() { 202 return FileUtil.getUserFilesPath(); 203 } 204 205 private static final String MODEL_FILE_DIRECTORY_NAME = "eventnames"; // NOI18N 206 207 protected String getModelFileDirectoryName() { // protected for testing 208 return MODEL_FILE_DIRECTORY_NAME; 209 } 210 211 private String getModelDataFileName() { 212 return "eventTable.xml"; 213 } 214 215 private void readFile(String fileName) throws org.jdom2.JDOMException, java.io.IOException, IllegalArgumentException { 216 // Check file exists 217 if (findFile(fileName) == null) { 218 log.debug("{} file could not be found", fileName); 219 return; 220 } 221 222 // Find root 223 Element root = rootFromName(fileName); 224 if (root == null) { 225 log.warn("{} file could not be read", fileName); 226 return; 227 } 228 229 // Now read name-id mapping information 230 if (root.getChild("nodes") != null) { // NOI18N 231 List<Element> nodes = root.getChildren("nodes"); 232 log.debug("readFile sees {} nodes elements", nodes.size()); 233 for (Element n : nodes) { 234 List<Element> l = n.getChildren("node"); // NOI18N 235 log.debug(" readFile sees {} node names", l.size()); 236 for (Element e : l) { 237 String nid = e.getChild("nodeID").getText(); // NOI18N 238 String name = e.getChild("name").getText(); 239 log.trace(" read nodeID {} name {}", nid, name); 240 model.addMatch(new NodeID(nid), name); 241 } 242 } 243 } 244 245 // Now read producer information 246 if (root.getChild("producers") != null) { // NOI18N 247 List<Element> producers = root.getChildren("producers"); 248 log.debug("readFile sees {} producers elements", producers.size()); 249 for (Element n : producers) { 250 List<Element> l = n.getChildren("producer"); // NOI18N 251 for (Element e : l) { 252 String event = e.getChild("event").getText(); // NOI18N 253 String range = e.getChild("range").getText(); // NOI18N 254 String producer = e.getChild("producer").getText(); // NOI18N 255 String pName = e.getChild("pName").getText(); // NOI18N 256 257 // having those, we see if there's a node ID associated with the name 258 NodeID newNodeID = null; 259 if (! pName.isEmpty()) { 260 newNodeID = model.getNodeID(pName); 261 } 262 if (newNodeID == null) { 263 // no, use the ID from the file 264 newNodeID = new NodeID(producer); 265 } 266 267 model.recordProducer(new EventID(event), newNodeID, range, false); 268 } 269 } 270 } 271 272 // Now read consumer information 273 if (root.getChild("consumers") != null) { // NOI18N 274 List<Element> consumers = root.getChildren("consumers"); 275 log.debug("readFile sees {} consumers elements", consumers.size()); 276 for (Element n : consumers) { 277 List<Element> l = n.getChildren("consumer"); // NOI18N 278 for (Element e : l) { 279 String event = e.getChild("event").getText(); // NOI18N 280 String range = e.getChild("range").getText(); // NOI18N 281 String consumer = e.getChild("consumer").getText(); // NOI18N 282 String cName = e.getChild("cName").getText(); // NOI18N 283 284 // having those, we see if there's a node ID associated with the name 285 NodeID newNodeID = null; 286 if (! cName.isEmpty()) { 287 newNodeID = model.getNodeID(cName); 288 } 289 if (newNodeID == null) { 290 // no, use the ID from the file 291 newNodeID = new NodeID(consumer); 292 } 293 294 model.recordConsumer(new EventID(event), newNodeID, range); 295 } 296 } 297 } 298 299 // Now read descriptions information 300 if (root.getChild("descriptions") != null) { // NOI18N 301 List<Element> descriptions = root.getChildren("descriptions"); 302 log.debug("readFile sees {} description elements", descriptions.size()); 303 for (Element n : descriptions) { 304 List<Element> l = n.getChildren("value"); // NOI18N 305 for (Element e : l) { 306 String event = e.getChild("event").getText(); // NOI18N 307 String description = e.getChild("description").getText(); // NOI18N 308 309 model.addAuxiliaryInformation(new EventID(event), description); 310 } 311 } 312 } 313 314 } 315 316 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(EventTableDataModelXml.class); 317 318}