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 signal mast type from/to xml 014 * 015 * @author Daniel Bergqvist (C) 2022 016 */ 017public class SignalMastTypeXml { 018 019 public SignalMastType load(SignalSystem signalSystem, File file) { 020 021// System.out.format("Load: %s%n", file.getAbsolutePath()); 022 023 Namespace namespace = Namespace.getNamespace("http://docbook.org/ns/docbook"); 024 025 SignalMastType signalMastType = new SignalMastType(file.getName()); 026 027 URL url = FileUtil.findURL(file.getAbsolutePath(), "resources", "xml"); 028 if (url == null) { 029 log.error("appearance file (xml/{}) doesn't exist", file); 030 throw new IllegalArgumentException("appearance file (xml/" + file + ") doesn't exist"); 031 } 032 033 jmri.jmrit.XmlFile xf = new jmri.jmrit.XmlFile(); 034 Element root; 035 try { 036 root = xf.rootFromURL(url); 037 038 signalMastType.setProcessingInstructionType(xf.getProcessingInstructionType()); 039 signalMastType.setProcessingInstructionHRef(xf.getProcessingInstructionHRef()); 040 041 Element aspecttable = root; 042 043 assert "appearancetable".equals(aspecttable.getName()); 044 045 Attribute attr = aspecttable.getAttribute("noNamespaceSchemaLocation"); 046 if (attr == null) { 047 for (Attribute a : aspecttable.getAttributes()) { 048 if ("noNamespaceSchemaLocation".equals(a.getName())) { 049 attr = a; 050 } 051 } 052 if (attr == null) { 053 throw new RuntimeException("Attribute 'noNamespaceSchemaLocation' is not found for element 'appearancetable'"); 054 } 055 } 056 057 signalMastType.setAppearanceSchema(attr.getValue()); 058 059 Element copyright = aspecttable.getChild("copyright", namespace); 060 signalMastType.getCopyright().getDates().clear(); 061 if (copyright != null) { 062 for (Element date : copyright.getChildren("year", namespace)) { 063 signalMastType.getCopyright().getDates().add(date.getTextTrim()); 064 } 065 signalMastType.getCopyright().setHolder(copyright.getChildText("holder", namespace)); 066 } 067 068 Element authors = aspecttable.getChild("authorgroup", namespace); 069 signalMastType.getAuthors().clear(); 070 if (authors != null) { 071 for (Element author : authors.getChildren("author", namespace)) { 072 Element personName = author.getChild("personname", namespace); 073 signalMastType.getAuthors().add( 074 new Author(personName.getChildText("firstname", namespace), 075 personName.getChildText("surname", namespace), 076 author.getChildText("email", namespace))); 077 } 078 } 079 080 Element revhistory = aspecttable.getChild("revhistory", namespace); 081 signalMastType.getRevisions().clear(); 082 if (revhistory != null) { 083 for (Element revision : revhistory.getChildren("revision", namespace)) { 084 signalMastType.getRevisions().add( 085 new Revision(revision.getChildText("revnumber", namespace), 086 revision.getChildText("date", namespace), 087 revision.getChildText("authorinitials", namespace), 088 revision.getChildText("revremark", namespace))); 089 } 090 } 091 092 093 signalMastType.setAspectTable(aspecttable.getChildText("aspecttable")); 094 095 signalMastType.setName(aspecttable.getChildText("name")); 096 097 signalMastType.getReferences().clear(); 098 for (Element referenceElement : aspecttable.getChildren("reference")) { 099 signalMastType.getReferences().add(StringWithLinksXml.load(referenceElement)); 100 } 101 102 signalMastType.getDescriptions().clear(); 103 for (Element descriptionElement : aspecttable.getChildren("description")) { 104 signalMastType.getDescriptions().add(StringWithLinksXml.load(descriptionElement)); 105 } 106 107 108 Element appearances = aspecttable.getChild("appearances"); 109 signalMastType.getAppearances().clear(); 110 if (appearances != null) { 111 for (Element appearanceElement : appearances.getChildren("appearance")) { 112 Appearance appearance = new Appearance( 113 appearanceElement.getChildText("aspectname")); 114 115 appearance.getShowList().clear(); 116 for (Element e : appearanceElement.getChildren("show")) { 117 appearance.getShowList().add(e.getText()); 118 } 119 appearance.getReferences().clear(); 120 for (Element e : appearanceElement.getChildren("reference")) { 121 appearance.getReferences().add(e.getText()); 122 } 123 appearance.getComments().clear(); 124 for (Element e : appearanceElement.getChildren("comment")) { 125 appearance.getComments().add(e.getText()); 126 } 127 if (appearanceElement.getChild("delay") != null) { 128 appearance.setDelay(appearanceElement.getChildText("delay")); 129 } 130 appearance.getImageLinks().clear(); 131 for (Element imageLinkElement : appearanceElement.getChildren("imagelink")) { 132 ImageType imageType = null; 133 if (imageLinkElement.getAttribute("type") != null) { 134 try { 135 imageType = signalSystem.getImageType(imageLinkElement.getAttributeValue("type")); 136 } catch (IllegalArgumentException ex) { 137 log.error("ERROR: image type {} does not exists, {}", imageLinkElement.getAttributeValue("type"), file.toString()); 138 } 139 } 140 appearance.getImageLinks().add(new ImageLink(imageLinkElement.getTextTrim(), imageType)); 141 } 142 signalMastType.getAppearances().add(appearance); 143 } 144 } 145 146 147 Element specificappearances = aspecttable.getChild("specificappearances"); 148 if (specificappearances != null) { 149 Element appearanceDanger = specificappearances.getChild("danger"); 150 if (appearanceDanger != null) { 151 signalMastType.getAppearanceDanger().setAspectName( 152 appearanceDanger.getChildText("aspect")); 153 for (Element imagelink : appearanceDanger.getChildren("imagelink")) { 154 ImageType imageType = null; 155 if (imagelink.getAttribute("type") != null) { 156 imageType = signalSystem.getImageType(imagelink.getAttributeValue("type")); 157 } 158 signalMastType.getAppearanceDanger().getImageLinks().add(new ImageLink( 159 imagelink.getTextTrim(), imageType)); 160 } 161 } 162 163 Element appearancePermissive = specificappearances.getChild("permissive"); 164 if (appearancePermissive != null) { 165 signalMastType.getAppearancePermissive().setAspectName( 166 appearancePermissive.getChildText("aspect")); 167 for (Element imagelink : appearancePermissive.getChildren("imagelink")) { 168 ImageType imageType = null; 169 if (imagelink.getAttribute("type") != null) { 170 imageType = signalSystem.getImageType(imagelink.getAttributeValue("type")); 171 } 172 signalMastType.getAppearancePermissive().getImageLinks().add(new ImageLink( 173 appearancePermissive.getChildText("imagelink"), imageType)); 174 } 175 } 176 177 Element appearanceHeld = specificappearances.getChild("held"); 178 if (appearanceHeld != null) { 179 signalMastType.getAppearanceHeld().setAspectName( 180 appearanceHeld.getChildText("aspect")); 181 for (Element imagelink : appearanceHeld.getChildren("imagelink")) { 182 ImageType imageType = null; 183 if (imagelink.getAttribute("type") != null) { 184 imageType = signalSystem.getImageType(imagelink.getAttributeValue("type")); 185 } 186 signalMastType.getAppearanceHeld().getImageLinks().add(new ImageLink( 187 imagelink.getTextTrim(), imageType)); 188 } 189 } 190 191 Element appearanceDark = specificappearances.getChild("dark"); 192 if (appearanceDark != null) { 193 signalMastType.getAppearanceDark().setAspectName( 194 appearanceDark.getChildText("aspect")); 195 for (Element imagelink : appearanceDark.getChildren("imagelink")) { 196 ImageType imageType = null; 197 if (imagelink.getAttribute("type") != null) { 198 imageType = signalSystem.getImageType(imagelink.getAttributeValue("type")); 199 } 200 signalMastType.getAppearanceDark().getImageLinks().add(new ImageLink( 201 imagelink.getTextTrim(), imageType)); 202 } 203 } 204 } 205 206 207 Element aspectMappings = aspecttable.getChild("aspectMappings"); 208 signalMastType.getAspectMappings().clear(); 209 if (aspectMappings != null) { 210 for (Element aspectMappingElement : aspectMappings.getChildren("aspectMapping")) { 211 AspectMapping aspectMapping = new AspectMapping( 212 aspectMappingElement.getChildText("advancedAspect")); 213 for (Element ourAspectElement : aspectMappingElement.getChildren("ourAspect")) { 214 aspectMapping.getOurAspects().add(ourAspectElement.getText()); 215 } 216 signalMastType.getAspectMappings().add(aspectMapping); 217 } 218 } 219 220 log.debug("loading complete"); 221 } catch (java.io.IOException | org.jdom2.JDOMException e) { 222 log.error("error reading file {}", url.getPath(), e); 223 return null; 224 } 225 226 227 return signalMastType; 228 } 229 230 231 public void save(SignalSystem signalSystem, SignalMastType signalMastType, boolean makeBackup) { 232 save(signalSystem, signalMastType, FileUtil.getProfilePath(), makeBackup); 233 } 234 235 public void save(SignalSystem signalSystem, SignalMastType signalMastType, String path, boolean makeBackup) { 236 String fileName = path + "xml/signals/" + signalSystem.getFolderName() + "/" + signalMastType.getFileName(); 237 238 XmlFile xmlFile = new XmlFile(); 239 if (makeBackup) { 240 xmlFile.makeBackupFile(fileName); 241 } 242 File file = new File(fileName); 243 244// System.out.format("Store: %s%n", file.getAbsolutePath()); 245 246 try { 247 File parentDir = file.getParentFile(); 248 if (!parentDir.exists()) { 249 if (!parentDir.mkdirs()) { 250 log.warn("Could not create parent directories for signal file :{}", fileName); 251 return; 252 } 253 } 254 if (file.createNewFile()) { 255 log.debug("Creating new signal file: {}", fileName); 256 } 257 } catch (IOException ea) { 258 log.error("Could not create signal file at {}.", fileName, ea); 259 } 260 261 try { 262 Element root = new Element("appearancetable"); 263 Document doc = new Document(root); 264 265 // add XSLT processing instruction 266 // <?xml-stylesheet type="text/xsl" href="XSLT/panelfile"+schemaVersion+".xsl"?> 267 java.util.Map<String, String> m = new java.util.HashMap<>(); 268 m.put("type", signalMastType.getProcessingInstructionType() != null 269 ? signalMastType.getProcessingInstructionType() : "text/xsl"); 270 m.put("href", signalMastType.getProcessingInstructionHRef() != null 271 ? signalMastType.getProcessingInstructionHRef() : "../../XSLT/appearancetable.xsl"); 272 ProcessingInstruction p = new ProcessingInstruction("xml-stylesheet", m); 273 doc.addContent(0, p); 274 275 root.setAttribute("noNamespaceSchemaLocation", 276 signalMastType.getAppearanceSchema() != null ? signalMastType.getAppearanceSchema() : "http://jmri.org/xml/schema/appearancetable.xsd", 277 Namespace.getNamespace("xsi", 278 "http://www.w3.org/2001/XMLSchema-instance")); 279 280 if (store(signalMastType, root)) { 281 xmlFile.writeXML(file, doc); 282 } 283 } catch (IOException eb) { 284 log.warn("Exception in storing signal xml", eb); 285 } 286 } 287 288 public boolean store(SignalMastType signalMastType, Element root) { 289 Namespace namespace = Namespace.getNamespace("http://docbook.org/ns/docbook"); 290 291 Element copyright = new Element("copyright", namespace); 292 for (String date : signalMastType.getCopyright().getDates()) { 293 copyright.addContent(new Element("year", namespace).setText(date)); 294 } 295 copyright.addContent(new Element("holder", namespace).setText(signalMastType.getCopyright().getHolder())); 296 root.addContent(copyright); 297 298 Element authorGroup = new Element("authorgroup", namespace); 299 for (Author author : signalMastType.getAuthors()) { 300 Element authorElement = new Element("author", namespace); 301 Element personName = new Element("personname", namespace); 302 personName.addContent(new Element("firstname", namespace).setText(author.getFirstName())); 303 personName.addContent(new Element("surname", namespace).setText(author.getSurName())); 304 authorElement.addContent(personName); 305 if (author.getEmail() != null && !author.getEmail().isBlank()) { 306 authorElement.addContent(new Element("email", namespace).addContent(author.getEmail())); 307 } 308 authorGroup.addContent(authorElement); 309 } 310 root.addContent(authorGroup); 311 312 Element revhistory = new Element("revhistory", namespace); 313 for (Revision revision : signalMastType.getRevisions()) { 314 Element revisionElement = new Element("revision", namespace); 315 revisionElement.addContent(new Element("revnumber", namespace).setText(revision.getRevNumber())); 316 revisionElement.addContent(new Element("date", namespace).setText(revision.getDate())); 317 revisionElement.addContent(new Element("authorinitials", namespace).setText(revision.getAuthorInitials())); 318 revisionElement.addContent(new Element("revremark", namespace).setText(revision.getRemark())); 319 revhistory.addContent(revisionElement); 320 } 321 root.addContent(revhistory); 322 323 324 root.addContent(new Element("aspecttable").setText(signalMastType.getAspectTable())); 325 326 root.addContent(new Element("name").setText(signalMastType.getName())); 327 328 for (StringWithLinks reference : signalMastType.getReferences()) { 329 Element e = StringWithLinksXml.store(reference, "reference"); 330 if (e != null) { 331 root.addContent(e); 332 } 333 } 334 335 for (StringWithLinks description : signalMastType.getDescriptions()) { 336 Element e = StringWithLinksXml.store(description, "description"); 337 if (e != null) { 338 root.addContent(e); 339 } 340 } 341 342 343 Element appearancesElement = new Element("appearances"); 344 for (Appearance appearance : signalMastType.getAppearances()) { 345 Element appearanceElement = new Element("appearance"); 346 appearanceElement.addContent(new Element("aspectname").setText(appearance.getAspectName())); 347 348 for (String show : appearance.getShowList()) { 349 appearanceElement.addContent(new Element("show").setText(show)); 350 } 351 for (String reference : appearance.getReferences()) { 352 appearanceElement.addContent(new Element("reference").setText(reference)); 353 } 354 for (String comment : appearance.getComments()) { 355 appearanceElement.addContent(new Element("comment").setText(comment)); 356 } 357 if (appearance.getDelay() != null && !appearance.getDelay().isBlank()) { 358 appearanceElement.addContent(new Element("delay").setText(appearance.getDelay())); 359 } 360 for (ImageLink imageLink : appearance.getImageLinks()) { 361 Element imageLinkElement = new Element("imagelink"); 362 imageLinkElement.setText(imageLink.getImageLink()); 363 if (imageLink.getType() != null) { 364 imageLinkElement.setAttribute("type", imageLink.getType().getType()); 365 } 366 appearanceElement.addContent(imageLinkElement); 367 } 368 appearancesElement.addContent(appearanceElement); 369 } 370 root.addContent(appearancesElement); 371 372 373 Element specificAppearancesElement = new Element("specificappearances"); 374 375 String dangerAspect = signalMastType.getAppearanceDanger().getAspectName(); 376 if (dangerAspect != null && !dangerAspect.isBlank()) { 377 Element specificAppearancesDanger = new Element("danger"); 378 specificAppearancesDanger.addContent(new Element("aspect").setText(dangerAspect)); 379 for (ImageLink imageLink : signalMastType.getAppearanceDanger().getImageLinks()) { 380 Element imageLinkElement = new Element("imagelink"); 381 imageLinkElement.setText(imageLink.getImageLink()); 382 if (imageLink.getType() != null) { 383 imageLinkElement.setAttribute("type", imageLink.getType().getType()); 384 } 385 specificAppearancesDanger.addContent(imageLinkElement); 386 } 387 specificAppearancesElement.addContent(specificAppearancesDanger); 388 } 389 390 String permissiveAspect = signalMastType.getAppearancePermissive().getAspectName(); 391 if (permissiveAspect != null && !permissiveAspect.isBlank()) { 392 Element specificAppearancesPermissive = new Element("permissive"); 393 specificAppearancesPermissive.addContent(new Element("aspect").setText(permissiveAspect)); 394 for (ImageLink imageLink : signalMastType.getAppearancePermissive().getImageLinks()) { 395 Element imageLinkElement = new Element("imagelink"); 396 imageLinkElement.setText(imageLink.getImageLink()); 397 if (imageLink.getType() != null) { 398 imageLinkElement.setAttribute("type", imageLink.getType().getType()); 399 } 400 specificAppearancesPermissive.addContent(imageLinkElement); 401 } 402 specificAppearancesElement.addContent(specificAppearancesPermissive); 403 } 404 405 String heldAspect = signalMastType.getAppearanceHeld().getAspectName(); 406 if (heldAspect != null && !heldAspect.isBlank()) { 407 Element specificAppearancesHeld = new Element("held"); 408 specificAppearancesHeld.addContent(new Element("aspect").setText(heldAspect)); 409 for (ImageLink imageLink : signalMastType.getAppearanceHeld().getImageLinks()) { 410 Element imageLinkElement = new Element("imagelink"); 411 imageLinkElement.setText(imageLink.getImageLink()); 412 if (imageLink.getType() != null) { 413 imageLinkElement.setAttribute("type", imageLink.getType().getType()); 414 } 415 specificAppearancesHeld.addContent(imageLinkElement); 416 } 417 specificAppearancesElement.addContent(specificAppearancesHeld); 418 } 419 420 String darkAspect = signalMastType.getAppearanceDark().getAspectName(); 421 if (darkAspect != null && !darkAspect.isBlank()) { 422 Element specificAppearancesDark = new Element("dark"); 423 specificAppearancesDark.addContent(new Element("aspect").setText(darkAspect)); 424 for (ImageLink imageLink : signalMastType.getAppearanceDark().getImageLinks()) { 425 Element imageLinkElement = new Element("imagelink"); 426 imageLinkElement.setText(imageLink.getImageLink()); 427 if (imageLink.getType() != null) { 428 imageLinkElement.setAttribute("type", imageLink.getType().getType()); 429 } 430 specificAppearancesDark.addContent(imageLinkElement); 431 } 432 specificAppearancesElement.addContent(specificAppearancesDark); 433 } 434 435 if (!specificAppearancesElement.getChildren().isEmpty()) { 436 root.addContent(specificAppearancesElement); 437 } 438 439 440 Element aspectMappingsElement = new Element("aspectMappings"); 441 for (AspectMapping aspectMapping : signalMastType.getAspectMappings()) { 442 Element aspectMappingElement = new Element("aspectMapping"); 443 aspectMappingElement.addContent(new Element("advancedAspect").setText(aspectMapping.getAdvancedAspect())); 444 for (String ourAspect : aspectMapping.getOurAspects()) { 445 aspectMappingElement.addContent(new Element("ourAspect").setText(ourAspect)); 446 } 447 aspectMappingsElement.addContent(aspectMappingElement); 448 } 449 if (!aspectMappingsElement.getChildren().isEmpty()) { 450 root.addContent(aspectMappingsElement); 451 } 452 453 454 return true; 455 } 456 457 458 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(SignalMastTypeXml.class); 459}