001 /* 002 * $Id: XPathUtils.java,v 1.9 2012/02/19 17:35:52 davep Exp $ 003 * 004 * This file is part of McIDAS-V 005 * 006 * Copyright 2007-2012 007 * Space Science and Engineering Center (SSEC) 008 * University of Wisconsin - Madison 009 * 1225 W. Dayton Street, Madison, WI 53706, USA 010 * https://www.ssec.wisc.edu/mcidas 011 * 012 * All Rights Reserved 013 * 014 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and 015 * some McIDAS-V source code is based on IDV and VisAD source code. 016 * 017 * McIDAS-V is free software; you can redistribute it and/or modify 018 * it under the terms of the GNU Lesser Public License as published by 019 * the Free Software Foundation; either version 3 of the License, or 020 * (at your option) any later version. 021 * 022 * McIDAS-V is distributed in the hope that it will be useful, 023 * but WITHOUT ANY WARRANTY; without even the implied warranty of 024 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 025 * GNU Lesser Public License for more details. 026 * 027 * You should have received a copy of the GNU Lesser Public License 028 * along with this program. If not, see http://www.gnu.org/licenses. 029 */ 030 package edu.wisc.ssec.mcidasv.util; 031 032 import java.io.File; 033 import java.io.InputStream; 034 import java.util.ArrayList; 035 import java.util.Iterator; 036 import java.util.List; 037 import java.util.Map; 038 import java.util.concurrent.ConcurrentHashMap; 039 040 import javax.xml.parsers.DocumentBuilder; 041 import javax.xml.parsers.DocumentBuilderFactory; 042 import javax.xml.xpath.XPathConstants; 043 import javax.xml.xpath.XPathExpression; 044 import javax.xml.xpath.XPathExpressionException; 045 import javax.xml.xpath.XPathFactory; 046 047 import org.w3c.dom.Document; 048 import org.w3c.dom.Element; 049 import org.w3c.dom.Node; 050 import org.w3c.dom.NodeList; 051 052 import ucar.unidata.idv.IntegratedDataViewer; 053 import ucar.unidata.idv.IdvResourceManager.IdvResource; 054 import ucar.unidata.idv.IdvResourceManager.XmlIdvResource; 055 import ucar.unidata.util.ResourceCollection.Resource; 056 import ucar.unidata.xml.XmlResourceCollection; 057 058 import edu.wisc.ssec.mcidasv.util.Contract; 059 060 /** 061 * Documentation is still forthcoming, but remember that <b>no methods accept 062 * {@code null} parameters!</b> 063 */ 064 public final class XPathUtils { 065 066 /** Maps (and caches) the XPath {@link String} to its compiled {@link XPathExpression}. */ 067 private static final Map<String, XPathExpression> pathMap = new ConcurrentHashMap<String, XPathExpression>(); 068 069 /** 070 * Thou shalt not create an instantiation of this class! 071 */ 072 private XPathUtils() {} 073 074 public static XPathExpression expr(String xPath) { 075 Contract.notNull(xPath, "Cannot compile a null string"); 076 077 XPathExpression expr = pathMap.get(xPath); 078 if (expr == null) { 079 try { 080 expr = XPathFactory.newInstance().newXPath().compile(xPath); 081 pathMap.put(xPath, expr); 082 } catch (XPathExpressionException e) { 083 throw new RuntimeException("Error compiling xpath", e); 084 } 085 } 086 return expr; 087 } 088 089 public static List<Node> eval(final XmlResourceCollection collection, final String xPath) { 090 Contract.notNull(collection, "Cannot search a null resource collection"); 091 Contract.notNull(xPath, "Cannot search using a null XPath query"); 092 093 try { 094 List<Node> nodeList = new ArrayList<Node>(); 095 XPathExpression expression = expr(xPath); 096 097 // Resources are the only things added to the list returned by 098 // getResources(). 099 @SuppressWarnings("unchecked") 100 List<Resource> files = collection.getResources(); 101 102 for (int i = 0; i < files.size(); i++) { 103 if (!collection.isValid(i)) 104 continue; 105 106 InputStream in = XPathUtils.class.getResourceAsStream(files.get(i).toString()); 107 if (in == null) 108 continue; 109 110 NodeList tmpList = (NodeList)expression.evaluate(loadXml(in), XPathConstants.NODESET); 111 for (int j = 0; j < tmpList.getLength(); j++) { 112 nodeList.add(tmpList.item(j)); 113 } 114 } 115 return nodeList; 116 } catch (XPathExpressionException e) { 117 throw new RuntimeException("Error evaluating xpath", e); 118 } 119 } 120 121 public static NodeList eval(final String xmlFile, final String xPath) { 122 Contract.notNull(xmlFile, "Null path to a XML file"); 123 Contract.notNull(xPath, "Cannot search using a null XPath query"); 124 125 try { 126 return (NodeList)expr(xPath).evaluate(loadXml(xmlFile), XPathConstants.NODESET); 127 } catch (XPathExpressionException e) { 128 throw new RuntimeException("Error evaluation xpath", e); 129 } 130 } 131 132 public static NodeList eval(final Node root, final String xPath) { 133 Contract.notNull(root, "Cannot search a null root node"); 134 Contract.notNull(xPath, "Cannot search using a null XPath query"); 135 136 try { 137 return (NodeList)expr(xPath).evaluate(root, XPathConstants.NODESET); 138 } catch (XPathExpressionException e) { 139 throw new RuntimeException("Error evaluation xpath", e); 140 } 141 } 142 143 public static List<Node> nodes(final IntegratedDataViewer idv, final IdvResource collectionId, final String xPath) { 144 Contract.notNull(idv); 145 Contract.notNull(collectionId); 146 Contract.notNull(xPath); 147 148 XmlResourceCollection collection = idv.getResourceManager().getXmlResources(collectionId); 149 return nodes(collection, xPath); 150 } 151 152 public static List<Node> nodes(final XmlResourceCollection collection, final String xPath) { 153 Contract.notNull(collection); 154 Contract.notNull(xPath); 155 return eval(collection, xPath); 156 } 157 158 public static NodeListIterator nodes(final String xmlFile, final String xPath) { 159 Contract.notNull(xmlFile); 160 Contract.notNull(xPath); 161 return new NodeListIterator(eval(xmlFile, xPath)); 162 } 163 164 public static NodeListIterator nodes(final Node root, final String xPath) { 165 Contract.notNull(root); 166 Contract.notNull(xPath); 167 return new NodeListIterator(eval(root, xPath)); 168 } 169 170 public static NodeListIterator nodes(final Node root) { 171 Contract.notNull(root); 172 return nodes(root, "//*"); 173 } 174 175 public static List<Element> elements(final IntegratedDataViewer idv, final IdvResource collectionId, final String xPath) { 176 Contract.notNull(idv); 177 Contract.notNull(collectionId); 178 Contract.notNull(xPath); 179 180 XmlResourceCollection collection = idv.getResourceManager().getXmlResources(collectionId); 181 return elements(collection, xPath); 182 } 183 184 public static List<Element> elements(final XmlResourceCollection collection, final String xPath) { 185 Contract.notNull(collection); 186 Contract.notNull(xPath); 187 List<Element> elements = new ArrayList<Element>(); 188 for (Node n : eval(collection, xPath)) 189 elements.add((Element)n); 190 return elements; 191 } 192 193 public static ElementListIterator elements(final String xmlFile, final String xPath) { 194 Contract.notNull(xmlFile); 195 Contract.notNull(xPath); 196 return new ElementListIterator(eval(xmlFile, xPath)); 197 } 198 199 public static ElementListIterator elements(final Node root) { 200 Contract.notNull(root); 201 return elements(root, "//*"); 202 } 203 204 public static ElementListIterator elements(final Node root, final String xPath) { 205 Contract.notNull(root); 206 Contract.notNull(xPath); 207 return new ElementListIterator(eval(root, xPath)); 208 } 209 210 public static Document loadXml(final String xmlFile) { 211 Contract.notNull(xmlFile); 212 213 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 214 factory.setNamespaceAware(false); 215 try { 216 DocumentBuilder builder = factory.newDocumentBuilder(); 217 return builder.parse(xmlFile); 218 } catch (Exception e) { 219 throw new RuntimeException("Error loading XML file: "+e.getMessage(), e); 220 } 221 } 222 223 public static Document loadXml(final File xmlFile) { 224 Contract.notNull(xmlFile); 225 226 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 227 factory.setNamespaceAware(false); 228 try { 229 DocumentBuilder builder = factory.newDocumentBuilder(); 230 return builder.parse(xmlFile); 231 } catch (Exception e) { 232 throw new RuntimeException("Error loading XML file: "+e.getMessage(), e); 233 } 234 } 235 236 public static Document loadXml(final InputStream in) { 237 Contract.notNull(in); 238 239 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 240 factory.setNamespaceAware(false); 241 try { 242 DocumentBuilder builder = factory.newDocumentBuilder(); 243 return builder.parse(in); 244 } catch (Exception e) { 245 throw new RuntimeException("Error loading XML from input stream: "+e.getMessage(), e); 246 } 247 } 248 249 public static class NodeListIterator implements Iterable<Node>, Iterator<Node> { 250 private final NodeList nodeList; 251 private int index = 0; 252 253 public NodeListIterator(final NodeList nodeList) { 254 Contract.notNull(nodeList); 255 this.nodeList = nodeList; 256 } 257 258 public Iterator<Node> iterator() { 259 return this; 260 } 261 262 public boolean hasNext() { 263 return (index < nodeList.getLength()); 264 } 265 266 public Node next() { 267 return nodeList.item(index++); 268 } 269 270 public void remove() { 271 throw new UnsupportedOperationException("not implemented"); 272 } 273 } 274 275 public static class ElementListIterator implements Iterable<Element>, Iterator<Element> { 276 private final NodeList nodeList; 277 private int index = 0; 278 279 public ElementListIterator(final NodeList nodeList) { 280 Contract.notNull(nodeList); 281 this.nodeList = nodeList; 282 } 283 284 public Iterator<Element> iterator() { 285 return this; 286 } 287 288 public boolean hasNext() { 289 return (index < nodeList.getLength()); 290 } 291 292 public Element next() { 293 return (Element)nodeList.item(index++); 294 } 295 296 public void remove() { 297 throw new UnsupportedOperationException("not implemented"); 298 } 299 } 300 }