001package jmri.jmrit.display.layoutEditor; 002 003import static jmri.jmrit.XmlFile.newDocument; 004import static jmri.jmrit.XmlFile.xsltLocation; 005 006import java.io.File; 007import java.io.IOException; 008import java.util.HashMap; 009import java.util.List; 010import java.util.Map; 011import java.util.ArrayList; 012import jmri.BasicRosterEntry; 013import jmri.Block; 014import jmri.BlockManager; 015import jmri.Path; 016import jmri.jmrit.XmlFile; 017import jmri.jmrit.roster.Roster; 018import jmri.jmrit.roster.RosterEntry; 019import jmri.util.FileUtil; 020import jmri.PowerManager; 021import jmri.JmriException; 022import org.jdom2.Attribute; 023import org.jdom2.DataConversionException; 024import org.jdom2.Document; 025import org.jdom2.Element; 026import org.jdom2.JDOMException; 027import org.jdom2.ProcessingInstruction; 028import org.slf4j.Logger; 029import org.slf4j.LoggerFactory; 030 031/** 032 * Handle saving/restoring block value information to XML files. This class 033 * manipulates files conforming to the block_value DTD. 034 * 035 * @author Dave Duchamp Copyright (C) 2008 036 * @author George Warner Copyright (c) 2017-2018 037 */ 038public class BlockValueFile extends XmlFile { 039 040 public BlockValueFile() { 041 super(); 042 blockManager = jmri.InstanceManager.getDefault(jmri.BlockManager.class); 043 } 044 045 // operational variables 046 private BlockManager blockManager = null; 047 private final static String defaultFileName = FileUtil.getUserFilesPath() + "blockvalues.xml"; 048 private Element root = null; 049 050 /** 051 * Reads Block values from a file in the user's preferences directory. If 052 * the file containing block values does not exist this routine returns 053 * quietly. If a Block named in the file does not exist currently, that 054 * entry is quietly ignored. 055 * 056 * @throws JDOMException on rootFromName if all methods fail 057 * @throws IOException if an I/O error occurs while reading a file 058 */ 059 public void readBlockValues() throws JDOMException, IOException { 060 log.debug("entered readBlockValues"); 061 List<String> blocks = new ArrayList<>(blockManager.getNamedBeanSet().size()); 062 blockManager.getNamedBeanSet().forEach(bean -> { 063 blocks.add(bean.getSystemName()); 064 }); 065 // check if file exists 066 if (checkFile(defaultFileName)) { 067 // file is present, 068 root = rootFromName(defaultFileName); 069 if ((root != null) && (blocks.size() > 0)) { 070 // there is a file and there are Blocks defined 071 Element blockvalues = root.getChild("blockvalues"); 072 if (blockvalues != null) { 073 // there are values defined, read and set block values if Block exists. 074 List<Element> blockList = blockvalues.getChildren("block"); 075 // check if all powermanagers are turned on, if they are, we should expect 076 // blocks with values to be occupied 077 boolean allPoweredUp = true; 078 for (PowerManager pm : jmri.InstanceManager.getList(PowerManager.class)) { 079 if (pm.getPower() != jmri.PowerManager.ON) { 080 allPoweredUp = false; 081 } 082 } 083 List<String> passes = new ArrayList<>(); 084 passes.add("set"); 085 if (allPoweredUp) { 086 // perform two passes, one to check blocks with values are occupied, the second to 087 // set values 088 passes.add(0, "check"); 089 } 090 for (String pass : passes) { 091 for (Element bl : blockList) { 092 if (bl.getAttribute("systemname") == null) { 093 log.warn("unexpected null in systemName {} {}", bl, bl.getAttributes()); 094 break; 095 } 096 String sysName = bl.getAttribute("systemname").getValue(); 097 // get Block - ignore entry if block not found 098 Block b = blockManager.getBySystemName(sysName); 099 if (b != null) { 100 // Block was found 101 if (pass.equals("check") && b.getState() != Block.OCCUPIED) { 102 // we have a recorded value for an empty block, the blockvalues file 103 // must be out of date, bail out before we set any values 104 log.error("block {} is not occupied but has a saved value, not setting saved block values", 105 b.getDisplayName()); 106 return; 107 } 108 if (pass.equals("set")) { 109 Object v = bl.getAttribute("value").getValue(); 110 if (bl.getAttribute("valueClass") != null) { 111 if (bl.getAttribute("valueClass").getValue().equals("jmri.jmrit.roster.RosterEntry")) { 112 RosterEntry re = Roster.getDefault().getEntryForId(((String) v)); 113 if (re != null) { 114 v = re; 115 } 116 } 117 } 118 b.setValue(v); 119 } 120 if (pass.equals("set")) { 121 // set direction if there is one 122 int dd = Path.NONE; 123 Attribute a = bl.getAttribute("dir"); 124 if (a != null) { 125 try { 126 dd = a.getIntValue(); 127 } catch (DataConversionException e) { 128 log.error("failed to convert direction attribute"); 129 } 130 } 131 b.setDirection(dd); 132 } 133 } 134 } 135 } 136 } 137 } 138 } 139 } 140 141 142 /* 143 * Writes out block values to a file in the user's preferences directory 144 * If there are no defined Blocks, no file is written. 145 * If none of the defined Blocks have values, no file is written. 146 * 147 * @throws IOException 148 */ 149 public void writeBlockValues() throws IOException { 150 log.debug("entered writeBlockValues"); 151 if (blockManager.getNamedBeanSet().size() > 0) { 152 // there are blocks defined, create root element 153 root = new Element("block_values"); 154 Document doc = newDocument(root, dtdLocation + "block-values.dtd"); 155 boolean valuesFound = false; 156 157 // add XSLT processing instruction 158 // <?xml-stylesheet type="text/xsl" href="XSLT/block-values.xsl"?> 159 Map<String, String> m = new HashMap<>(); 160 m.put("type", "text/xsl"); 161 m.put("href", xsltLocation + "blockValues.xsl"); 162 ProcessingInstruction p = new ProcessingInstruction("xml-stylesheet", m); 163 doc.addContent(0, p); 164 165 // save block values in xml format 166 Element values = new Element("blockvalues"); 167 168 for (Block b : blockManager.getNamedBeanSet()) { 169 if (b != null) { 170 Object o = b.getValue(); 171 if (o != null) { 172 // block has value, save it 173 Element val = new Element("block"); 174 val.setAttribute("systemname", b.getSystemName()); 175 if (o instanceof RosterEntry) { 176 val.setAttribute("value", ((BasicRosterEntry) o).getId()); 177 val.setAttribute("valueClass", "jmri.jmrit.roster.RosterEntry"); 178 } else { 179 val.setAttribute("value", o.toString()); 180 } 181 int v = b.getDirection(); 182 if (v != Path.NONE) { 183 val.setAttribute("dir", "" + v); 184 } 185 values.addContent(val); 186 valuesFound = true; 187 } 188 } else { 189 log.error("Block null in blockManager.getNamedBeanSet()"); 190 } 191 } 192 root.addContent(values); 193 194 // write out the file if values were found 195 if (valuesFound) { 196 try { 197 if (!checkFile(defaultFileName)) { 198 // file does not exist, create it 199 File file = new File(defaultFileName); 200 if (!file.createNewFile()) // create and check result 201 { 202 log.error("createNewFile failed"); 203 } 204 } 205 // write content to file 206 writeXML(findFile(defaultFileName), doc); 207 } catch (IOException ioe) { 208 log.error("While writing block value file ", ioe); 209 throw (ioe); 210 } 211 } 212 } 213 } 214 215 // initialize logging 216 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(BlockValueFile.class); 217 218}