001package jmri.jmrit.signalsystemeditor.configurexml; 002 003import java.io.*; 004import java.net.URL; 005 006import jmri.jmrit.XmlFile; 007import jmri.jmrit.signalsystemeditor.*; 008import jmri.util.FileUtil; 009 010import org.jdom2.*; 011 012/** 013 * Load and store a SignalSystem from/to xml 014 * 015 * @author Daniel Bergqvist (C) 2022 016 */ 017public class SignalSystemXml { 018 019 public SignalSystem load(File file) { 020 Namespace namespace = Namespace.getNamespace("http://docbook.org/ns/docbook"); 021 022 SignalSystem signalSystem = new SignalSystem(file.getParentFile().getName()); 023 024 URL url = FileUtil.findURL(file.getAbsolutePath(), "resources", "xml"); 025 if (url == null) { 026 log.error("appearance file (xml/{}) doesn't exist", file); 027 throw new IllegalArgumentException("appearance file (xml/" + file + ") doesn't exist"); 028 } 029 jmri.jmrit.XmlFile xf = new jmri.jmrit.XmlFile(); 030 Element root; 031 try { 032 root = xf.rootFromURL(url); 033 034 signalSystem.setProcessingInstructionType(xf.getProcessingInstructionType()); 035 signalSystem.setProcessingInstructionHRef(xf.getProcessingInstructionHRef()); 036 037 Element aspecttable = root; 038 039 assert "aspecttable".equals(aspecttable.getName()); 040 041 Attribute attr = aspecttable.getAttribute("noNamespaceSchemaLocation"); 042 if (attr == null) { 043 for (Attribute a : aspecttable.getAttributes()) { 044 if ("noNamespaceSchemaLocation".equals(a.getName())) { 045 attr = a; 046 } 047 } 048 if (attr == null) { 049 throw new RuntimeException("Attribute 'noNamespaceSchemaLocation' is not found for element 'aspecttable'"); 050 } 051 } 052 053 signalSystem.setAspectSchema(attr.getValue()); 054 055 signalSystem.setName(aspecttable.getChildText("name")); 056 if (aspecttable.getChild("date") != null) { 057 signalSystem.setDate(aspecttable.getChildText("date")); 058 } 059 060 signalSystem.getReferences().clear(); 061 for (Element e : aspecttable.getChildren("reference")) { 062 signalSystem.getReferences().add(e.getText()); 063 } 064 065 Element copyright = aspecttable.getChild("copyright", namespace); 066 signalSystem.getCopyright().getDates().clear(); 067 if (copyright != null) { 068 for (Element date : copyright.getChildren("year", namespace)) { 069 signalSystem.getCopyright().getDates().add(date.getTextTrim()); 070 } 071 signalSystem.getCopyright().setHolder(copyright.getChildText("holder", namespace)); 072 } else { 073 log.debug("ERROR: No copyright"); 074 } 075 076 Element authors = aspecttable.getChild("authorgroup", namespace); 077 signalSystem.getAuthors().clear(); 078 if (authors != null) { 079 for (Element author : authors.getChildren("author", namespace)) { 080 Element personName = author.getChild("personname", namespace); 081 signalSystem.getAuthors().add( 082 new Author(personName.getChildText("firstname", namespace), 083 personName.getChildText("surname", namespace), 084 author.getChildText("email", namespace))); 085 } 086 } else { 087 log.debug("ERROR: No authors"); 088 } 089 090 Element revhistory = aspecttable.getChild("revhistory", namespace); 091 signalSystem.getRevisions().clear(); 092 if (revhistory != null) { 093 for (Element revision : revhistory.getChildren("revision", namespace)) { 094 signalSystem.getRevisions().add( 095 new Revision(revision.getChildText("revnumber", namespace), 096 revision.getChildText("date", namespace), 097 revision.getChildText("authorinitials", namespace), 098 revision.getChildText("revremark", namespace))); 099 } 100 } else { 101 log.debug("ERROR: No authors"); 102 } 103 104 105 Element aspectsElement = aspecttable.getChild("aspects"); 106 signalSystem.getAspects().clear(); 107 if (aspectsElement != null) { 108 for (Element aspectElement : aspectsElement.getChildren("aspect")) { 109 Aspect aspect = new Aspect(StringWithCommentXml.load(aspectElement.getChild("name")), 110 aspectElement.getChildText("title"), 111 aspectElement.getChildText("rule"), 112 aspectElement.getChildText("indication"), 113 aspectElement.getChildText("route"), 114 aspectElement.getChildText("dccAspect")); 115 116 for (Element e : aspectElement.getChildren()) { 117 switch (e.getName()) { 118 case "description": 119 aspect.getDescriptions().add(e.getText()); 120 break; 121 case "reference": 122 aspect.getReferences().add(e.getText()); 123 break; 124 case "comment": 125 aspect.getComments().add(e.getText()); 126 break; 127 case "speed": 128 aspect.getSpeedList().add(e.getText()); 129 break; 130 case "speed2": 131 aspect.getSpeed2List().add(e.getText()); 132 break; 133 default: 134 // Ignore 135 } 136 } 137 signalSystem.getAspects().add(aspect); 138 } 139 } else { 140 log.debug("ERROR: No aspects"); 141 } 142 143 144 Element imagetypes = aspecttable.getChild("imagetypes"); 145 signalSystem.getImageTypes().clear(); 146 if (imagetypes != null) { 147 for (Element imagetype : imagetypes.getChildren("imagetype")) { 148 signalSystem.getImageTypes().add(new ImageType(imagetype.getAttributeValue("type"))); 149 } 150 } 151 152 153 Element appearancefiles = aspecttable.getChild("appearancefiles"); 154 signalSystem.getSignalMastTypes().clear(); 155 if (appearancefiles != null) { 156 for (Element appearancefile : appearancefiles.getChildren("appearancefile")) { 157 signalSystem.getSignalMastTypes().add(new SignalMastTypeXml() 158 .load(signalSystem, new File( 159 "xml/signals/"+file.getParentFile().getName() 160 +"/"+appearancefile.getAttributeValue("href")))); 161 } 162 } 163 164 log.debug("loading complete"); 165 } catch (java.io.IOException | org.jdom2.JDOMException e) { 166 log.error("error reading file {}", url.getPath(), e); 167 return null; 168 } 169 170 171 return signalSystem; 172 } 173 174 175 public void save(SignalSystem signalSystem) { 176 save(signalSystem, FileUtil.getProfilePath() + "xml/signals/"); 177 } 178 179 public void save(SignalSystem signalSystem, String path) { 180 String fileName = path + signalSystem.getFolderName() + "/aspects.xml"; 181 182 XmlFile xmlFile = new XmlFile(); 183 xmlFile.makeBackupFile(fileName); 184 File file = new File(fileName); 185 try { 186 File parentDir = file.getParentFile(); 187 if (!parentDir.exists()) { 188 if (!parentDir.mkdirs()) { 189 log.warn("Could not create parent directories for signal file :{}", fileName); 190 return; 191 } 192 } 193 if (file.createNewFile()) { 194 log.debug("Creating new signal file: {}", fileName); 195 } 196 } catch (IOException ea) { 197 log.error("Could not create signal file at {}.", fileName, ea); 198 } 199 200 try { 201 Element root = new Element("aspecttable"); 202 Document doc = new Document(root); 203 204 // add XSLT processing instruction 205 // <?xml-stylesheet type="text/xsl" href="XSLT/panelfile"+schemaVersion+".xsl"?> 206 java.util.Map<String, String> m = new java.util.HashMap<>(); 207 m.put("type", signalSystem.getProcessingInstructionType() != null 208 ? signalSystem.getProcessingInstructionType() : "text/xsl"); 209 m.put("href", signalSystem.getProcessingInstructionHRef() != null 210 ? signalSystem.getProcessingInstructionHRef() : "../../XSLT/aspecttable.xsl"); 211 ProcessingInstruction p = new ProcessingInstruction("xml-stylesheet", m); 212 doc.addContent(0, p); 213 214 root.setAttribute("noNamespaceSchemaLocation", 215 signalSystem.getAspectSchema() != null ? signalSystem.getAspectSchema() : "http://jmri.org/xml/schema/aspecttable.xsd", 216 Namespace.getNamespace("xsi", 217 "http://www.w3.org/2001/XMLSchema-instance")); 218 219 if (store(signalSystem, root)) { 220 xmlFile.writeXML(file, doc); 221 } 222 } catch (IOException eb) { 223 log.warn("Exception in storing signal xml", eb); 224 } 225 } 226 227 public boolean store(SignalSystem signalSystem, Element root) { 228 Namespace namespace = Namespace.getNamespace("http://docbook.org/ns/docbook"); 229 230 root.addContent(new Element("name").setText(signalSystem.getName())); 231 if (signalSystem.getDate() != null) { 232 root.addContent(new Element("date").setText(signalSystem.getDate())); 233 } 234 for (String ref : signalSystem.getReferences()) { 235 root.addContent(new Element("reference").setText(ref)); 236 } 237 238 Element copyright = new Element("copyright", namespace); 239 for (String date : signalSystem.getCopyright().getDates()) { 240 copyright.addContent(new Element("year", namespace).setText(date)); 241 } 242 copyright.addContent(new Element("holder", namespace).setText(signalSystem.getCopyright().getHolder())); 243 root.addContent(copyright); 244 245 Element authorGroup = new Element("authorgroup", namespace); 246 for (Author author : signalSystem.getAuthors()) { 247 Element authorElement = new Element("author", namespace); 248 Element personName = new Element("personname", namespace); 249 personName.addContent(new Element("firstname", namespace).setText(author.getFirstName())); 250 personName.addContent(new Element("surname", namespace).setText(author.getSurName())); 251 authorElement.addContent(personName); 252 if (author.getEmail() != null && !author.getEmail().isBlank()) { 253 authorElement.addContent(new Element("email", namespace).addContent(author.getEmail())); 254 } 255 authorGroup.addContent(authorElement); 256 } 257 root.addContent(authorGroup); 258 259 Element revhistory = new Element("revhistory", namespace); 260 for (Revision revision : signalSystem.getRevisions()) { 261 Element revisionElement = new Element("revision", namespace); 262 revisionElement.addContent(new Element("revnumber", namespace).setText(revision.getRevNumber())); 263 revisionElement.addContent(new Element("date", namespace).setText(revision.getDate())); 264 revisionElement.addContent(new Element("authorinitials", namespace).setText(revision.getAuthorInitials())); 265 revisionElement.addContent(new Element("revremark", namespace).setText(revision.getRemark())); 266 revhistory.addContent(revisionElement); 267 } 268 root.addContent(revhistory); 269 270 271 Element aspects = new Element("aspects"); 272 for (Aspect aspect : signalSystem.getAspects()) { 273 Element aspectElement = new Element("aspect"); 274 aspectElement.addContent(StringWithCommentXml.store(aspect.getName(), "name")); 275 if (aspect.getTitle() != null && !aspect.getTitle().isBlank()) { 276 aspectElement.addContent(new Element("title").setText(aspect.getTitle())); 277 } 278 if (aspect.getRule() != null && !aspect.getRule().isBlank()) { 279 aspectElement.addContent(new Element("rule").setText(aspect.getRule())); 280 } 281 aspectElement.addContent(new Element("indication").setText(aspect.getIndication())); 282 for (String description : aspect.getDescriptions()) { 283 aspectElement.addContent(new Element("description").setText(description)); 284 } 285 for (String reference : aspect.getReferences()) { 286 aspectElement.addContent(new Element("reference").setText(reference)); 287 } 288 for (String comment : aspect.getComments()) { 289 aspectElement.addContent(new Element("comment").setText(comment)); 290 } 291 for (String speed : aspect.getSpeedList()) { 292 aspectElement.addContent(new Element("speed").setText(speed)); 293 } 294 for (String speed2 : aspect.getSpeed2List()) { 295 aspectElement.addContent(new Element("speed2").setText(speed2)); 296 } 297 if (aspect.getRoute() != null && !aspect.getRoute().isBlank()) { 298 aspectElement.addContent(new Element("route").setText(aspect.getRoute())); 299 } 300 if (aspect.getDccAspect() != null && !aspect.getDccAspect().isBlank()) { 301 aspectElement.addContent(new Element("dccAspect").setText(aspect.getDccAspect())); 302 } 303 aspects.addContent(aspectElement); 304 } 305 root.addContent(aspects); 306 307 308 if (!signalSystem.getImageTypes().isEmpty()) { 309 Element imageTypes = new Element("imagetypes"); 310 for (ImageType imageType : signalSystem.getImageTypes()) { 311 Element imageTypeElement = new Element("imagetype"); 312 imageTypeElement.setAttribute("type", imageType.getType()); 313 imageTypes.addContent(imageTypeElement); 314 } 315 root.addContent(imageTypes); 316 } 317 318 319 Element appearanceFiles = new Element("appearancefiles"); 320 for (SignalMastType signalMastType : signalSystem.getSignalMastTypes()) { 321 Element appearanceFileElement = new Element("appearancefile"); 322 appearanceFileElement.setAttribute("href", signalMastType.getFileName()); 323 appearanceFiles.addContent(appearanceFileElement); 324 } 325 root.addContent(appearanceFiles); 326 327 return true; 328 } 329 330 331 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(SignalSystemXml.class); 332}