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