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 }