001package jmri.jmrit.catalog.configurexml; 002 003import java.io.File; 004import java.io.IOException; 005import java.util.Enumeration; 006import java.util.List; 007import java.util.Set; 008import javax.swing.tree.*; 009import jmri.CatalogTree; 010import jmri.CatalogTreeManager; 011import jmri.InstanceManager; 012import jmri.jmrit.XmlFile; 013import jmri.CatalogTreeLeaf; 014import jmri.CatalogTreeNode; 015import jmri.util.FileUtil; 016import org.jdom2.Attribute; 017import org.jdom2.Document; 018import org.jdom2.Element; 019import org.slf4j.Logger; 020import org.slf4j.LoggerFactory; 021 022/** 023 * Provides the abstract base and store functionality for configuring the 024 * CatalogTreeManager. 025 * <p> 026 * Typically, a subclass will just implement the load(Element catalogTree) 027 * class, relying on implementation here to load the individual CatalogTree 028 * objects. 029 * 030 * @author Pete Cressman Copyright: Copyright (c) 2009 031 */ 032public class DefaultCatalogTreeManagerXml extends XmlFile { 033 034 private final static String DEFAULT_FILE_NAME = FileUtil.getUserFilesPath() + "catalogTrees.xml"; 035 036 public DefaultCatalogTreeManagerXml() { 037 } 038 039 /** 040 * Write out tree values to a file in the user's preferences directory. 041 * 042 * @throws IOException from any I/O issues during write; not handled locally 043 */ 044 public void writeCatalogTrees() throws IOException { 045 log.debug("entered writeCatalogTreeValues"); 046 CatalogTreeManager manager = InstanceManager.getDefault(jmri.CatalogTreeManager.class); 047 Set<CatalogTree> trees = manager.getNamedBeanSet(); 048 boolean found = false; 049 for (CatalogTree tree : manager.getNamedBeanSet()) { 050 String sname = tree.getSystemName(); 051 if (log.isDebugEnabled()) { 052 log.debug("Tree: sysName= {}, userName= {}", sname, tree.getUserName()); 053 CatalogTreeNode root = tree.getRoot(); 054 log.debug("enumerateTree called for root= {}, has {} children", root, root.getChildCount()); 055 056 Enumeration<TreeNode> e = root.depthFirstEnumeration(); 057 while (e.hasMoreElements()) { 058 CatalogTreeNode n = (CatalogTreeNode)e.nextElement(); 059 log.debug("nodeName= {} has {} leaves and {} subnodes.", n.getUserObject(), n.getLeaves().size(), n.getChildCount()); 060 } 061 } 062 if (sname.charAt(1) == CatalogTree.XML) { 063 found = true; 064 break; 065 } 066 } 067 if (found) { 068 // there are trees defined, create root element 069 Element root = new Element("catalogTrees"); 070 Document doc = newDocument(root, dtdLocation + "catalogTree.dtd"); 071 072 // add XSLT processing instruction 073 // <?xml-stylesheet type="text/xsl" href="XSLT/tree-values.xsl"?> 074 java.util.Map<String, String> m = new java.util.HashMap<>(); 075 m.put("type", "text/xsl"); 076 m.put("href", xsltLocation + "panelfile.xsl"); 077 org.jdom2.ProcessingInstruction p = new org.jdom2.ProcessingInstruction("xml-stylesheet", m); 078 doc.addContent(0, p); 079 080 store(root, trees); 081 082 try { 083 if (!checkFile(DEFAULT_FILE_NAME)) { 084 // file does not exist, create it 085 File file = new File(DEFAULT_FILE_NAME); 086 if (!file.createNewFile()) { 087 log.error("createNewFile failed"); 088 } 089 } 090 // write content to file 091 writeXML(findFile(DEFAULT_FILE_NAME), doc); 092 // memory consistent with file 093 InstanceManager.getDefault(CatalogTreeManager.class).indexChanged(false); 094 } catch (IOException ioe) { 095 log.error("IO Exception writing CatalogTrees", ioe); 096 throw (ioe); 097 } 098 } 099 } 100 101 /** 102 * Default implementation for storing the contents of a CatalogTreeManager. 103 * 104 * @param cat Element to load with contents 105 * @param trees List of contents 106 */ 107 public void store(Element cat, Set<CatalogTree> trees) { 108 cat.setAttribute("class", "jmri.jmrit.catalog.DefaultCatalogTreeManagerConfigXML"); 109 for (CatalogTree ct : trees) { 110 String sname = ct.getSystemName(); 111 log.debug("system name is {}", sname); 112 if (sname.charAt(1) != CatalogTree.XML) { 113 continue; 114 } 115 Element elem = new Element("catalogTree"); 116 elem.setAttribute("systemName", sname); 117 String uname = ct.getUserName(); 118 if (uname != null) { 119 elem.setAttribute("userName", uname); 120 } 121 122 storeNode(elem, ct.getRoot()); 123 124 log.debug("store CatalogTree {}", sname); 125 cat.addContent(elem); 126 } 127 } 128 129 /** 130 * Recursively store a CatalogTree. 131 * 132 * @param parent the element to store node in 133 * @param node the root node of the tree 134 */ 135 public void storeNode(Element parent, CatalogTreeNode node) { 136 log.debug("storeNode {}, has {} leaves.", node, node.getLeaves().size()); 137 Element element = new Element("node"); 138 element.setAttribute("nodeName", node.toString()); 139 List<CatalogTreeLeaf> leaves = node.getLeaves(); 140 for (CatalogTreeLeaf leaf : leaves) { 141 Element el = new Element("leaf"); 142 el.setAttribute("name", leaf.getName()); // prefer to store non-localized name 143 el.setAttribute("path", leaf.getPath()); 144 element.addContent(el); 145 } 146 parent.addContent(element); 147 Enumeration<TreeNode> e = node.children(); 148 while (e.hasMoreElements()) { 149 CatalogTreeNode n = (CatalogTreeNode) e.nextElement(); 150 storeNode(element, n); 151 } 152 } 153 154 /** 155 * This is invoked as part of the "store all" mechanism, which is not used 156 * for these objects. Hence this is implemented to do nothing. 157 * 158 * @param o the object to store 159 * @return null 160 */ 161 public Element store(Object o) { 162 return null; 163 } 164 165 /* 166 * Read CatalogTree values from a file in the user's preferences directory. 167 */ 168 public void readCatalogTrees() { 169 log.debug("entered readCatalogTrees"); 170 //CatalogTreeManager manager = InstanceManager.getDefault(jmri.CatalogTreeManager.class); 171 try { 172 // check if file exists 173 if (checkFile(DEFAULT_FILE_NAME)) { 174 Element root = rootFromName(DEFAULT_FILE_NAME); 175 if (root != null) { 176 load(root); 177 } 178 } else { 179 log.debug("File: {} not Found", DEFAULT_FILE_NAME); 180 } 181 } catch (org.jdom2.JDOMException | IOException jde) { 182 log.error("Exception reading CatalogTrees", jde); 183 } 184 } 185 186 /** 187 * Create a CatalogTreeManager object of the correct class, then register 188 * and fill it. 189 * 190 * @param catalogTrees top level Element to unpack 191 * @return true if successful 192 */ 193 public boolean load(Element catalogTrees) { 194 loadCatalogTrees(catalogTrees); 195 return true; 196 } 197 198 /** 199 * Utility method to load the individual CatalogTree objects. 200 * 201 * @param catalogTrees element containing trees 202 */ 203 public void loadCatalogTrees(Element catalogTrees) { 204 List<Element> catList = catalogTrees.getChildren("catalogTree"); 205 log.debug("loadCatalogTrees: found {} CatalogTree objects", catList.size()); 206 CatalogTreeManager mgr = InstanceManager.getDefault(jmri.CatalogTreeManager.class); 207 208 for (Element elem : catList) { 209 Attribute attr = elem.getAttribute("systemName"); 210 if (attr == null) { 211 log.warn("unexpected null systemName. elem= {}, attrs= {}", elem, elem.getAttributes()); 212 continue; 213 } 214 String sysName = attr.getValue(); 215 String userName; 216 attr = elem.getAttribute("userName"); 217 if (attr == null) { 218 log.warn("unexpected null userName. attrs= {}", elem.getAttributes()); 219 continue; 220 } else { 221 userName = attr.getValue(); 222 } 223 CatalogTree ct = mgr.getBySystemName(sysName); 224 if (ct != null) { 225 continue; // tree already registered 226 } 227 try { 228 ct = mgr.newCatalogTree(sysName, userName); 229 } 230 catch (IllegalArgumentException ex){ 231 log.error("Could not create CatalogTree: {}",ex.getMessage()); 232 continue; 233 } 234 if (ct instanceof DefaultTreeModel) { 235 log.debug("CatalogTree: sysName= {}, userName= {}", sysName, userName); 236 CatalogTreeNode root = ct.getRoot(); 237 elem = elem.getChild("node"); 238 loadNode(elem, root, (DefaultTreeModel) ct); 239 } 240 } 241 } 242 243 /** 244 * Recursively load a CatalogTree. 245 * 246 * @param element element containing the node to load 247 * @param parent the parent node of the node in element 248 * @param model the tree model containing the tree to add the node to 249 */ 250 public void loadNode(Element element, CatalogTreeNode parent, DefaultTreeModel model) { 251 List<Element> nodeList = element.getChildren("node"); 252 log.debug("Found {} CatalogTreeNode objects", nodeList.size()); 253 for (int i = 0; i < nodeList.size(); i++) { 254 Element elem = nodeList.get(i); 255 Attribute attr = elem.getAttribute("nodeName"); 256 if (attr == null) { 257 log.warn("unexpected null nodeName. elem= {}, attrs= {}", elem, elem.getAttributes()); 258 continue; 259 } 260 String nodeName = attr.getValue(); 261 CatalogTreeNode n = new CatalogTreeNode(nodeName); 262 addLeaves(elem, n); 263 model.insertNodeInto(n, parent, parent.getChildCount()); 264 loadNode(elem, n, model); 265 } 266 } 267 268 private void addLeaves(Element element, CatalogTreeNode node) { 269 List<Element> leafList = element.getChildren("leaf"); 270 for (Element elem : leafList) { 271 Attribute attr = elem.getAttribute("name"); 272 if (attr == null) { 273 log.error("unexpected null leaf name. elem= {}, attrs= {}", elem, elem.getAttributes()); 274 continue; 275 } 276 String name = attr.getValue(); 277 attr = elem.getAttribute("path"); 278 if (attr == null) { 279 log.error("unexpected null leaf path. elem= {}, attrs= {}", elem, elem.getAttributes()); 280 continue; 281 } 282 String path = attr.getValue(); 283 // use the method that maintains the same order 284 node.addLeaf(new CatalogTreeLeaf(name, path, 0)); 285 } 286 } 287 288 private final static Logger log = LoggerFactory.getLogger(DefaultCatalogTreeManagerXml.class); 289 290}