XPathParser.java

  1. /*
  2.  *    Copyright 2009-2021 the original author or authors.
  3.  *
  4.  *    Licensed under the Apache License, Version 2.0 (the "License");
  5.  *    you may not use this file except in compliance with the License.
  6.  *    You may obtain a copy of the License at
  7.  *
  8.  *       http://www.apache.org/licenses/LICENSE-2.0
  9.  *
  10.  *    Unless required by applicable law or agreed to in writing, software
  11.  *    distributed under the License is distributed on an "AS IS" BASIS,
  12.  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13.  *    See the License for the specific language governing permissions and
  14.  *    limitations under the License.
  15.  */
  16. package org.apache.ibatis.parsing;

  17. import java.io.InputStream;
  18. import java.io.Reader;
  19. import java.io.StringReader;
  20. import java.util.ArrayList;
  21. import java.util.List;
  22. import java.util.Properties;

  23. import javax.xml.XMLConstants;
  24. import javax.xml.namespace.QName;
  25. import javax.xml.parsers.DocumentBuilder;
  26. import javax.xml.parsers.DocumentBuilderFactory;
  27. import javax.xml.xpath.XPath;
  28. import javax.xml.xpath.XPathConstants;
  29. import javax.xml.xpath.XPathFactory;

  30. import org.apache.ibatis.builder.BuilderException;
  31. import org.w3c.dom.Document;
  32. import org.w3c.dom.Node;
  33. import org.w3c.dom.NodeList;
  34. import org.xml.sax.EntityResolver;
  35. import org.xml.sax.ErrorHandler;
  36. import org.xml.sax.InputSource;
  37. import org.xml.sax.SAXException;
  38. import org.xml.sax.SAXParseException;

  39. /**
  40.  * @author Clinton Begin
  41.  * @author Kazuki Shimizu
  42.  */
  43. public class XPathParser {

  44.   private final Document document;
  45.   private boolean validation;
  46.   private EntityResolver entityResolver;
  47.   private Properties variables;
  48.   private XPath xpath;

  49.   public XPathParser(String xml) {
  50.     commonConstructor(false, null, null);
  51.     this.document = createDocument(new InputSource(new StringReader(xml)));
  52.   }

  53.   public XPathParser(Reader reader) {
  54.     commonConstructor(false, null, null);
  55.     this.document = createDocument(new InputSource(reader));
  56.   }

  57.   public XPathParser(InputStream inputStream) {
  58.     commonConstructor(false, null, null);
  59.     this.document = createDocument(new InputSource(inputStream));
  60.   }

  61.   public XPathParser(Document document) {
  62.     commonConstructor(false, null, null);
  63.     this.document = document;
  64.   }

  65.   public XPathParser(String xml, boolean validation) {
  66.     commonConstructor(validation, null, null);
  67.     this.document = createDocument(new InputSource(new StringReader(xml)));
  68.   }

  69.   public XPathParser(Reader reader, boolean validation) {
  70.     commonConstructor(validation, null, null);
  71.     this.document = createDocument(new InputSource(reader));
  72.   }

  73.   public XPathParser(InputStream inputStream, boolean validation) {
  74.     commonConstructor(validation, null, null);
  75.     this.document = createDocument(new InputSource(inputStream));
  76.   }

  77.   public XPathParser(Document document, boolean validation) {
  78.     commonConstructor(validation, null, null);
  79.     this.document = document;
  80.   }

  81.   public XPathParser(String xml, boolean validation, Properties variables) {
  82.     commonConstructor(validation, variables, null);
  83.     this.document = createDocument(new InputSource(new StringReader(xml)));
  84.   }

  85.   public XPathParser(Reader reader, boolean validation, Properties variables) {
  86.     commonConstructor(validation, variables, null);
  87.     this.document = createDocument(new InputSource(reader));
  88.   }

  89.   public XPathParser(InputStream inputStream, boolean validation, Properties variables) {
  90.     commonConstructor(validation, variables, null);
  91.     this.document = createDocument(new InputSource(inputStream));
  92.   }

  93.   public XPathParser(Document document, boolean validation, Properties variables) {
  94.     commonConstructor(validation, variables, null);
  95.     this.document = document;
  96.   }

  97.   public XPathParser(String xml, boolean validation, Properties variables, EntityResolver entityResolver) {
  98.     commonConstructor(validation, variables, entityResolver);
  99.     this.document = createDocument(new InputSource(new StringReader(xml)));
  100.   }

  101.   public XPathParser(Reader reader, boolean validation, Properties variables, EntityResolver entityResolver) {
  102.     commonConstructor(validation, variables, entityResolver);
  103.     this.document = createDocument(new InputSource(reader));
  104.   }

  105.   public XPathParser(InputStream inputStream, boolean validation, Properties variables, EntityResolver entityResolver) {
  106.     commonConstructor(validation, variables, entityResolver);
  107.     this.document = createDocument(new InputSource(inputStream));
  108.   }

  109.   public XPathParser(Document document, boolean validation, Properties variables, EntityResolver entityResolver) {
  110.     commonConstructor(validation, variables, entityResolver);
  111.     this.document = document;
  112.   }

  113.   public void setVariables(Properties variables) {
  114.     this.variables = variables;
  115.   }

  116.   public String evalString(String expression) {
  117.     return evalString(document, expression);
  118.   }

  119.   public String evalString(Object root, String expression) {
  120.     String result = (String) evaluate(expression, root, XPathConstants.STRING);
  121.     result = PropertyParser.parse(result, variables);
  122.     return result;
  123.   }

  124.   public Boolean evalBoolean(String expression) {
  125.     return evalBoolean(document, expression);
  126.   }

  127.   public Boolean evalBoolean(Object root, String expression) {
  128.     return (Boolean) evaluate(expression, root, XPathConstants.BOOLEAN);
  129.   }

  130.   public Short evalShort(String expression) {
  131.     return evalShort(document, expression);
  132.   }

  133.   public Short evalShort(Object root, String expression) {
  134.     return Short.valueOf(evalString(root, expression));
  135.   }

  136.   public Integer evalInteger(String expression) {
  137.     return evalInteger(document, expression);
  138.   }

  139.   public Integer evalInteger(Object root, String expression) {
  140.     return Integer.valueOf(evalString(root, expression));
  141.   }

  142.   public Long evalLong(String expression) {
  143.     return evalLong(document, expression);
  144.   }

  145.   public Long evalLong(Object root, String expression) {
  146.     return Long.valueOf(evalString(root, expression));
  147.   }

  148.   public Float evalFloat(String expression) {
  149.     return evalFloat(document, expression);
  150.   }

  151.   public Float evalFloat(Object root, String expression) {
  152.     return Float.valueOf(evalString(root, expression));
  153.   }

  154.   public Double evalDouble(String expression) {
  155.     return evalDouble(document, expression);
  156.   }

  157.   public Double evalDouble(Object root, String expression) {
  158.     return (Double) evaluate(expression, root, XPathConstants.NUMBER);
  159.   }

  160.   public List<XNode> evalNodes(String expression) {
  161.     return evalNodes(document, expression);
  162.   }

  163.   public List<XNode> evalNodes(Object root, String expression) {
  164.     List<XNode> xnodes = new ArrayList<>();
  165.     NodeList nodes = (NodeList) evaluate(expression, root, XPathConstants.NODESET);
  166.     for (int i = 0; i < nodes.getLength(); i++) {
  167.       xnodes.add(new XNode(this, nodes.item(i), variables));
  168.     }
  169.     return xnodes;
  170.   }

  171.   public XNode evalNode(String expression) {
  172.     return evalNode(document, expression);
  173.   }

  174.   public XNode evalNode(Object root, String expression) {
  175.     Node node = (Node) evaluate(expression, root, XPathConstants.NODE);
  176.     if (node == null) {
  177.       return null;
  178.     }
  179.     return new XNode(this, node, variables);
  180.   }

  181.   private Object evaluate(String expression, Object root, QName returnType) {
  182.     try {
  183.       return xpath.evaluate(expression, root, returnType);
  184.     } catch (Exception e) {
  185.       throw new BuilderException("Error evaluating XPath.  Cause: " + e, e);
  186.     }
  187.   }

  188.   private Document createDocument(InputSource inputSource) {
  189.     // important: this must only be called AFTER common constructor
  190.     try {
  191.       DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
  192.       factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
  193.       factory.setValidating(validation);

  194.       factory.setNamespaceAware(false);
  195.       factory.setIgnoringComments(true);
  196.       factory.setIgnoringElementContentWhitespace(false);
  197.       factory.setCoalescing(false);
  198.       factory.setExpandEntityReferences(true);

  199.       DocumentBuilder builder = factory.newDocumentBuilder();
  200.       builder.setEntityResolver(entityResolver);
  201.       builder.setErrorHandler(new ErrorHandler() {
  202.         @Override
  203.         public void error(SAXParseException exception) throws SAXException {
  204.           throw exception;
  205.         }

  206.         @Override
  207.         public void fatalError(SAXParseException exception) throws SAXException {
  208.           throw exception;
  209.         }

  210.         @Override
  211.         public void warning(SAXParseException exception) throws SAXException {
  212.           // NOP
  213.         }
  214.       });
  215.       return builder.parse(inputSource);
  216.     } catch (Exception e) {
  217.       throw new BuilderException("Error creating document instance.  Cause: " + e, e);
  218.     }
  219.   }

  220.   private void commonConstructor(boolean validation, Properties variables, EntityResolver entityResolver) {
  221.     this.validation = validation;
  222.     this.entityResolver = entityResolver;
  223.     this.variables = variables;
  224.     XPathFactory factory = XPathFactory.newInstance();
  225.     this.xpath = factory.newXPath();
  226.   }

  227. }