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