XPathParser.java

  1. /*
  2.  *    Copyright 2009-2024 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.  *       https://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.     return PropertyParser.parse(result, variables);
  122.   }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  226. }