1 package org.unicode.cldr.util; 2 3 import java.io.BufferedInputStream; 4 import java.io.BufferedReader; 5 import java.io.ByteArrayInputStream; 6 import java.io.File; 7 import java.io.IOException; 8 import java.io.InputStream; 9 import java.io.Reader; 10 import java.nio.file.Files; 11 import java.util.Iterator; 12 13 import javax.xml.namespace.QName; 14 import javax.xml.parsers.DocumentBuilderFactory; 15 import javax.xml.parsers.ParserConfigurationException; 16 import javax.xml.xpath.XPath; 17 import javax.xml.xpath.XPathConstants; 18 import javax.xml.xpath.XPathException; 19 import javax.xml.xpath.XPathExpressionException; 20 import javax.xml.xpath.XPathFactory; 21 22 import org.w3c.dom.Document; 23 import org.w3c.dom.Node; 24 import org.w3c.dom.NodeList; 25 import org.xml.sax.SAXException; 26 27 /** 28 * Class that offers different methods of "evaluating" an XPath expression against a document provided and of 29 * iterating through the result of the evaluation 30 * 31 * @author ribnitz 32 * 33 * @param <E> 34 * @param <F> 35 */ 36 public class XPathExpressionParser { 37 38 /** 39 * Buffer which holds the contents to work on, initialized once, when it is read from file. Preferred over 40 * private final File f, to prevent re-reading the file on every request 41 */ 42 private final byte[] buf; 43 44 /** 45 * Interface for handling 'simple' content types, that are not Nodes, or for processing Nodes/NodeSets oneself 46 * @author ribnitz 47 * 48 * @param <G> 49 */ 50 public static interface SimpleContentHandlingInterface<G> { 51 52 void handle(G result); 53 } 54 55 /** 56 * Interface for handling Nodes/NodeSets; in the case of NodeSets call will be made for each node separately 57 * @author ribnitz 58 * 59 */ 60 61 public static interface NodeHandlingInterface extends SimpleContentHandlingInterface<Node> { 62 63 } 64 65 private Document getDocument(InputStream is) throws SAXException, IOException { 66 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 67 try { 68 return dbf.newDocumentBuilder().parse(is); 69 } catch (ParserConfigurationException e) { 70 // TODO Auto-generated catch block 71 e.printStackTrace(); 72 } 73 return null; 74 } 75 76 /** 77 * Initialize by reading the file specified 78 * @param f 79 * @throws IOException 80 */ 81 public XPathExpressionParser(File f) throws IOException { 82 buf = Files.readAllBytes(f.toPath()); 83 } 84 85 /** 86 * Create an expression parser using the Reader given 87 * @param rdr 88 * @throws IOException 89 */ 90 public XPathExpressionParser(Reader rdr) throws IOException { 91 StringBuilder sb = new StringBuilder(); 92 try (BufferedReader br = new BufferedReader(rdr)) { 93 String s = null; 94 while ((s = br.readLine()) != null) { 95 sb.append(s); 96 } 97 } 98 buf = sb.toString().getBytes(); 99 } 100 101 @SuppressWarnings({ "rawtypes", "unchecked" }) 102 private void evaluateWithXPathFixture(String xPathString, QName expectedResult, boolean iterate, SimpleContentHandlingInterface handler) 103 throws XPathExpressionException { 104 if (handler != null) { 105 try (InputStream is = new BufferedInputStream(new ByteArrayInputStream(buf))) { 106 Document doc = getDocument(is); 107 XPathFactory xpFact = XPathFactory.newInstance(); 108 XPath xp = xpFact.newXPath(); 109 Object result = xp.compile(xPathString).evaluate(doc, expectedResult); 110 if (expectedResult == XPathConstants.NODESET && iterate) { 111 if (result instanceof NodeList) { 112 NodeList nl = (NodeList) result; 113 Iterator<Node> nlIter = new NodeListIterator(nl); 114 while (nlIter.hasNext()) { 115 handler.handle(nlIter.next()); 116 } 117 } 118 } else { 119 handler.handle(result); 120 } 121 } catch (IOException e) { 122 // TODO Auto-generated catch block 123 e.printStackTrace(); 124 } catch (SAXException e) { 125 // TODO Auto-generated catch block 126 e.printStackTrace(); 127 } 128 } 129 } 130 131 /** 132 * Evaluate the xPathString with the expected result type, and pass the result to handler. 133 * @param xPathString 134 * @param expectedResult 135 * @param handler 136 * @throws XPathException 137 */ 138 public void evaluate(String xPathString, QName expectedResult, SimpleContentHandlingInterface<?> handler) throws XPathException { 139 evaluateWithXPathFixture(xPathString, expectedResult, false, handler); 140 } 141 142 /** 143 * Evaluate this xPathString, and feed the result to the handler. The result is assumed to be a Node. 144 * @param xPathString 145 * @param handler 146 * @throws XPathException 147 */ 148 public void evaluateToNode(String xPathString, NodeHandlingInterface handler) throws XPathException { 149 iterate(xPathString, XPathConstants.NODE, handler); 150 } 151 152 /** 153 * Internal method that gets the ResultSet identified by the xPathString, and that calls the 154 * handler for each node. 155 * @param xPathString 156 * @param handler 157 * @throws XPathException 158 */ 159 public void iterate(String xPathString, QName expectedReturnType, NodeHandlingInterface handler) throws XPathException { 160 evaluateWithXPathFixture(xPathString, expectedReturnType, true, handler); 161 } 162 163 /** 164 * Evaluate the expression which is expected to return a NodeSet and iterate through the result 165 * the handler will be called for each Node encountered. 166 * 167 * @param xPathExpression 168 * @param handler 169 * @throws XPathException 170 */ 171 public void iterateThroughNodeSet(String xPathExpression, NodeHandlingInterface handler) throws XPathException { 172 iterate(xPathExpression, XPathConstants.NODESET, handler); 173 } 174 }