Home | History | Annotate | Download | only in util
      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 }