001package jmri.jmrix.openlcb.configurexml; 002 003import java.io.File; 004import java.io.FileNotFoundException; 005import java.io.IOException; 006import java.util.List; 007import java.util.HashSet; 008 009import org.jdom2.Document; 010import org.jdom2.Element; 011import org.jdom2.JDOMException; 012 013import jmri.IdTagManager; 014import jmri.InstanceManager; 015import jmri.jmrit.XmlFile; 016import jmri.jmrix.openlcb.OlcbConstants; 017import jmri.jmrix.openlcb.OlcbEventNameStore; 018import jmri.util.FileUtil; 019 020import org.openlcb.EventID; 021 022/** 023 * JMRI's implementation of part of the OpenLcb EventNameStore interface persistance 024 * 025 * @author Bob Jacobsen Copyright (C) 2025 026 */ 027public final class OlcbEventNameStoreXml extends XmlFile { 028 029 public OlcbEventNameStoreXml(OlcbEventNameStore nameStore, String baseFileName) { 030 this.nameStore = nameStore; 031 this.baseFileName = baseFileName; 032 033 migrateFromIdTagStore(); 034 } 035 036 OlcbEventNameStore nameStore; 037 String baseFileName; 038 /** 039 * The original implementation of this store 040 * was via the IdTagManager class. This is now 041 * viewed as a mistake. This method takes names 042 * stored in the IdTagManager and migrates them 043 * to the dedicated local store. 044 */ 045 private void migrateFromIdTagStore() { 046 // check for whether migration is already done 047 File file = findFile(getDefaultEventNameFileName()); 048 if (file != null) { 049 return; 050 } 051 052 IdTagManager tagmgr = InstanceManager.getDefault(IdTagManager.class); 053 log.debug("*** Starting event name migration"); 054 055 var tagSet = tagmgr.getNamedBeanSet(); 056 057 var localSet = new HashSet<>(tagSet); // avoid concurrent modifications 058 059 for (var tag : localSet) { 060 log.debug(" Process tag {}", tag); 061 if (tag.getSystemName().startsWith(OlcbConstants.tagPrefix)) { 062 var eid = tag.getSystemName().substring(OlcbConstants.tagPrefix.length()); 063 log.info(" Migrating event name '{}' event ID '{}' from IdTag table", 064 tag.getUserName(), eid); 065 066 // Add to this store 067 nameStore.addMatch(new EventID(eid), tag.getUserName()); 068 069 070 // Remove from ID tag store 071 tagmgr.deregister(tag); 072 tag.dispose(); 073 } 074 } 075 076 log.debug("*** Ending event name migration"); 077 } 078 079 public void store() throws java.io.IOException { 080 log.debug("Storing using file: {}", getDefaultEventNameFileName()); 081 createFile(getDefaultEventNameFileName(), true); 082 try { 083 writeFile(getDefaultEventNameFileName()); 084 } catch (FileNotFoundException ex) { 085 log.error("File not found while writing Event Name file, may not be complete", ex); 086 } 087 } 088 089 public void load() { 090 log.debug("Loading..."); 091 var wasDirty = nameStore.dirty; 092 try { 093 readFile(getDefaultEventNameFileName()); 094 } catch (JDOMException | IOException ex) { 095 log.error("Exception during IdTag file reading", ex); 096 } 097 nameStore.dirty = wasDirty; 098 } 099 100 private File createFile(String fileName, boolean backup) { 101 if (backup) { 102 makeBackupFile(fileName); 103 } 104 105 File file = null; 106 try { 107 if (!checkFile(fileName)) { 108 // The file does not exist, create it before writing 109 file = new File(fileName); 110 File parentDir = file.getParentFile(); 111 if (!parentDir.exists()) { 112 if (!parentDir.mkdir()) { 113 log.error("Directory wasn't created"); 114 } 115 } 116 if (file.createNewFile()) { 117 log.debug("New file created"); 118 } 119 } else { 120 file = new File(fileName); 121 } 122 } catch (java.io.IOException ex) { 123 log.error("Exception while creating Event Name file, may not be complete", (Object) ex); 124 } 125 return file; 126 } 127 128 private void writeFile(String fileName) throws FileNotFoundException, java.io.IOException { 129 log.debug("writeFile {}", fileName); 130 // This is taken in large part from "Java and XML" page 368 131 File file = findFile(fileName); 132 if (file == null) { 133 file = new File(fileName); 134 } 135 // Create root element 136 Element root = new Element("eventNameStore"); // NOI18N 137 // root.setAttribute("noNamespaceSchemaLocation", // NOI18N 138 // "http://jmri.org/xml/schema/idtags.xsd", // NOI18N 139 // org.jdom2.Namespace.getNamespace("xsi", // NOI18N 140 // "http://www.w3.org/2001/XMLSchema-instance")); // NOI18N 141 Document doc = newDocument(root); 142 143 // add XSLT processing instruction 144 // java.util.Map<String, String> m = new java.util.HashMap<>(); 145 // m.put("type", "text/xsl"); // NOI18N 146 // m.put("href", xsltLocation + "idtags.xsl"); // NOI18N 147 // ProcessingInstruction p = new ProcessingInstruction("xml-stylesheet", m); // NOI18N 148 // doc.addContent(0, p); 149 150 Element values; 151 152 // Loop through event names 153 root.addContent(values = new Element("names")); // NOI18N 154 for (EventID eid : nameStore.getMatches()) { 155 var name = nameStore.getEventName(eid); 156 log.debug("Writing event name: {} event {}", name, eid); 157 var element = new Element("entry"); 158 var nameElement = new Element("name"); 159 nameElement.addContent(name); 160 var eventIdElement = new Element("eventID"); 161 eventIdElement.addContent(eid.toShortString()); 162 element.addContent(eventIdElement); 163 element.addContent(nameElement); 164 values.addContent(element); 165 } 166 writeXML(file, doc); 167 } 168 169 private String getDefaultEventNameFileName() { 170 return getFileLocation() + getEventNameDirectoryName() + File.separator + getEventNameFileName(); 171 } 172 173 private String getFileLocation() { 174 return FileUtil.getUserFilesPath(); 175 } 176 177 private static final String EVENT_NAMES_DIRECTORY_NAME = "eventnames"; // NOI18N 178 179 private String getEventNameDirectoryName() { 180 return EVENT_NAMES_DIRECTORY_NAME; 181 } 182 183 private String getEventNameFileName() { 184 return "eventNames.xml"; 185 } 186 187 private void readFile(String fileName) throws org.jdom2.JDOMException, java.io.IOException, IllegalArgumentException { 188 // Check file exists 189 if (findFile(fileName) == null) { 190 log.debug("{} file could not be found", fileName); 191 return; 192 } 193 194 // Find root 195 Element root = rootFromName(fileName); 196 if (root == null) { 197 log.debug("{} file could not be read", fileName); 198 return; 199 } 200 201 // Now read name-id mapping information 202 if (root.getChild("names") != null) { // NOI18N 203 List<Element> l = root.getChild("names").getChildren("entry"); // NOI18N 204 log.debug("readFile sees {} event names", l.size()); 205 for (Element e : l) { 206 String eid = e.getChild("eventID").getText(); // NOI18N 207 String name = e.getChild("name").getText(); 208 log.debug("read EventID {}", eid); 209 nameStore.addMatch(new EventID(eid), name); 210 } 211 } 212 } 213 214 215 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(OlcbEventNameStoreXml.class); 216 217}