001/* 002 * This file is part of McIDAS-V 003 * 004 * Copyright 2007-2016 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 */ 028package edu.wisc.ssec.mcidasv.util; 029 030import static java.util.Objects.requireNonNull; 031 032import java.io.File; 033import java.io.InputStream; 034import java.util.ArrayList; 035import java.util.Iterator; 036import java.util.List; 037import java.util.Map; 038import java.util.concurrent.ConcurrentHashMap; 039 040import javax.xml.parsers.DocumentBuilder; 041import javax.xml.parsers.DocumentBuilderFactory; 042import javax.xml.xpath.XPathConstants; 043import javax.xml.xpath.XPathExpression; 044import javax.xml.xpath.XPathExpressionException; 045import javax.xml.xpath.XPathFactory; 046 047import org.w3c.dom.Document; 048import org.w3c.dom.Element; 049import org.w3c.dom.Node; 050import org.w3c.dom.NodeList; 051 052import ucar.unidata.idv.IntegratedDataViewer; 053import ucar.unidata.idv.IdvResourceManager.IdvResource; 054import ucar.unidata.idv.IdvResourceManager.XmlIdvResource; 055import ucar.unidata.util.ResourceCollection.Resource; 056import ucar.unidata.xml.XmlResourceCollection; 057 058/** 059 * Documentation is still forthcoming, but remember that <b>no methods accept 060 * {@code null} parameters!</b> 061 */ 062public final class XPathUtils { 063 064 /** 065 * Maps (and caches) the XPath {@link String} to its compiled 066 * {@link XPathExpression}. 067 */ 068 private static final Map<String, XPathExpression> pathMap = 069 new ConcurrentHashMap<>(); 070 071 /** 072 * Thou shalt not create an instantiation of this class! 073 */ 074 private XPathUtils() {} 075 076 public static XPathExpression expr(String xPath) { 077 requireNonNull(xPath, "Cannot compile a null string"); 078 079 XPathExpression expr = pathMap.get(xPath); 080 if (expr == null) { 081 try { 082 expr = XPathFactory.newInstance().newXPath().compile(xPath); 083 pathMap.put(xPath, expr); 084 } catch (XPathExpressionException e) { 085 throw new RuntimeException("Error compiling xpath", e); 086 } 087 } 088 return expr; 089 } 090 091 public static List<Node> eval(final XmlResourceCollection collection, final String xPath) { 092 requireNonNull(collection, "Cannot search a null resource collection"); 093 requireNonNull(xPath, "Cannot search using a null XPath query"); 094 095 try { 096 List<Node> nodeList = new ArrayList<>(); 097 XPathExpression expression = expr(xPath); 098 099 // Resources are the only things added to the list returned by 100 // getResources(). 101 @SuppressWarnings("unchecked") 102 List<Resource> files = collection.getResources(); 103 104 for (int i = 0; i < files.size(); i++) { 105 if (!collection.isValid(i)) { 106 continue; 107 } 108 109 InputStream in = XPathUtils.class.getResourceAsStream(files.get(i).toString()); 110 if (in == null) { 111 continue; 112 } 113 114 NodeList tmpList = (NodeList)expression.evaluate(loadXml(in), XPathConstants.NODESET); 115 for (int j = 0; j < tmpList.getLength(); j++) { 116 nodeList.add(tmpList.item(j)); 117 } 118 } 119 return nodeList; 120 } catch (XPathExpressionException e) { 121 throw new RuntimeException("Error evaluating xpath", e); 122 } 123 } 124 125 public static NodeList eval(final String xmlFile, final String xPath) { 126 requireNonNull(xmlFile, "Null path to a XML file"); 127 requireNonNull(xPath, "Cannot search using a null XPath query"); 128 129 try { 130 return (NodeList)expr(xPath).evaluate(loadXml(xmlFile), XPathConstants.NODESET); 131 } catch (XPathExpressionException e) { 132 throw new RuntimeException("Error evaluation xpath", e); 133 } 134 } 135 136 public static NodeList eval(final Node root, final String xPath) { 137 requireNonNull(root, "Cannot search a null root node"); 138 requireNonNull(xPath, "Cannot search using a null XPath query"); 139 140 try { 141 return (NodeList)expr(xPath).evaluate(root, XPathConstants.NODESET); 142 } catch (XPathExpressionException e) { 143 throw new RuntimeException("Error evaluation xpath", e); 144 } 145 } 146 147 public static List<Node> nodes(final IntegratedDataViewer idv, final IdvResource collectionId, final String xPath) { 148 requireNonNull(idv); 149 requireNonNull(collectionId); 150 requireNonNull(xPath); 151 152 XmlResourceCollection collection = idv.getResourceManager().getXmlResources(collectionId); 153 return nodes(collection, xPath); 154 } 155 156 public static List<Node> nodes(final XmlResourceCollection collection, final String xPath) { 157 requireNonNull(collection); 158 requireNonNull(xPath); 159 return eval(collection, xPath); 160 } 161 162 public static NodeListIterator nodes(final String xmlFile, final String xPath) { 163 requireNonNull(xmlFile); 164 requireNonNull(xPath); 165 return new NodeListIterator(eval(xmlFile, xPath)); 166 } 167 168 public static NodeListIterator nodes(final Node root, final String xPath) { 169 requireNonNull(root); 170 requireNonNull(xPath); 171 return new NodeListIterator(eval(root, xPath)); 172 } 173 174 public static NodeListIterator nodes(final Node root) { 175 requireNonNull(root); 176 return nodes(root, "//*"); 177 } 178 179 public static List<Element> elements(final IntegratedDataViewer idv, final IdvResource collectionId, final String xPath) { 180 requireNonNull(idv); 181 requireNonNull(collectionId); 182 requireNonNull(xPath); 183 184 XmlResourceCollection collection = idv.getResourceManager().getXmlResources(collectionId); 185 return elements(collection, xPath); 186 } 187 188 public static List<Element> elements(final XmlResourceCollection collection, final String xPath) { 189 requireNonNull(collection); 190 requireNonNull(xPath); 191 List<Element> elements = new ArrayList<>(); 192 for (Node n : eval(collection, xPath)) { 193 elements.add((Element)n); 194 } 195 return elements; 196 } 197 198 public static ElementListIterator elements(final String xmlFile, final String xPath) { 199 requireNonNull(xmlFile); 200 requireNonNull(xPath); 201 return new ElementListIterator(eval(xmlFile, xPath)); 202 } 203 204 public static ElementListIterator elements(final Node root) { 205 requireNonNull(root); 206 return elements(root, "//*"); 207 } 208 209 public static ElementListIterator elements(final Node root, final String xPath) { 210 requireNonNull(root); 211 requireNonNull(xPath); 212 return new ElementListIterator(eval(root, xPath)); 213 } 214 215 public static Document loadXml(final String xmlFile) { 216 requireNonNull(xmlFile); 217 218 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 219 factory.setNamespaceAware(false); 220 try { 221 DocumentBuilder builder = factory.newDocumentBuilder(); 222 return builder.parse(xmlFile); 223 } catch (Exception e) { 224 throw new RuntimeException("Error loading XML file: "+e.getMessage(), e); 225 } 226 } 227 228 public static Document loadXml(final File xmlFile) { 229 requireNonNull(xmlFile); 230 231 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 232 factory.setNamespaceAware(false); 233 try { 234 DocumentBuilder builder = factory.newDocumentBuilder(); 235 return builder.parse(xmlFile); 236 } catch (Exception e) { 237 throw new RuntimeException("Error loading XML file: "+e.getMessage(), e); 238 } 239 } 240 241 public static Document loadXml(final InputStream in) { 242 requireNonNull(in); 243 244 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 245 factory.setNamespaceAware(false); 246 try { 247 DocumentBuilder builder = factory.newDocumentBuilder(); 248 return builder.parse(in); 249 } catch (Exception e) { 250 throw new RuntimeException("Error loading XML from input stream: "+e.getMessage(), e); 251 } 252 } 253 254 public static class NodeListIterator implements Iterable<Node>, Iterator<Node> { 255 private final NodeList nodeList; 256 private int index = 0; 257 258 public NodeListIterator(final NodeList nodeList) { 259 requireNonNull(nodeList); 260 this.nodeList = nodeList; 261 } 262 263 public Iterator<Node> iterator() { 264 return this; 265 } 266 267 public boolean hasNext() { 268 return index < nodeList.getLength(); 269 } 270 271 public Node next() { 272 return nodeList.item(index++); 273 } 274 275 public void remove() { 276 throw new UnsupportedOperationException("not implemented"); 277 } 278 } 279 280 public static class ElementListIterator implements Iterable<Element>, Iterator<Element> { 281 private final NodeList nodeList; 282 private int index = 0; 283 284 public ElementListIterator(final NodeList nodeList) { 285 requireNonNull(nodeList); 286 this.nodeList = nodeList; 287 } 288 289 public Iterator<Element> iterator() { 290 return this; 291 } 292 293 public boolean hasNext() { 294 return index < nodeList.getLength(); 295 } 296 297 public Element next() { 298 return (Element)nodeList.item(index++); 299 } 300 301 public void remove() { 302 throw new UnsupportedOperationException("not implemented"); 303 } 304 } 305}