View Javadoc
1   /*
2    * Copyright (c) 2002-2025 Gargoyle Software Inc.
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    * https://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software
10   * distributed under the License is distributed on an "AS IS" BASIS,
11   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12   * See the License for the specific language governing permissions and
13   * limitations under the License.
14   */
15  package org.htmlunit.javascript.host.dom;
16  
17  import javax.xml.transform.TransformerException;
18  
19  import org.htmlunit.corejs.javascript.Context;
20  import org.htmlunit.corejs.javascript.Function;
21  import org.htmlunit.corejs.javascript.Scriptable;
22  import org.htmlunit.html.DomNode;
23  import org.htmlunit.html.xpath.HtmlUnitPrefixResolver;
24  import org.htmlunit.html.xpath.XPathAdapter;
25  import org.htmlunit.html.xpath.XPathHelper;
26  import org.htmlunit.javascript.HtmlUnitScriptable;
27  import org.htmlunit.javascript.JavaScriptEngine;
28  import org.htmlunit.javascript.configuration.JsxClass;
29  import org.htmlunit.javascript.configuration.JsxConstructor;
30  import org.htmlunit.javascript.configuration.JsxFunction;
31  import org.htmlunit.xpath.xml.utils.PrefixResolver;
32  import org.w3c.dom.Node;
33  
34  /**
35   * A JavaScript object for {@code XPathExpression}.
36   *
37   * @author Ahmed Ashour
38   * @author Ronald Brill
39   */
40  @JsxClass
41  public class XPathExpression extends HtmlUnitScriptable {
42  
43      private final XPathAdapter xpath_;
44      private final PrefixResolver prefixResolver_;
45  
46      /**
47       * Default constructor.
48       */
49      public XPathExpression() {
50          super();
51          xpath_ = null;
52          prefixResolver_ = null;
53      }
54  
55      /**
56       * JavaScript constructor.
57       */
58      @JsxConstructor
59      public void jsConstructor() {
60          // nothing to do
61      }
62  
63      XPathExpression(final String expression, final PrefixResolver prefixResolver,
64              final DomNode node) throws TransformerException {
65          super();
66          PrefixResolver resolver = prefixResolver;
67          if (resolver == null) {
68              resolver = new HtmlUnitPrefixResolver(node);
69          }
70          prefixResolver_ = resolver;
71          final boolean caseSensitive = node.getPage().hasCaseSensitiveTagNames();
72          xpath_ = new XPathAdapter(expression, prefixResolver, caseSensitive);
73      }
74  
75      /**
76       * Executes an XPath expression on the given node or document and returns an XPathResult.
77       * @param context the context
78       * @param scope the scope
79       * @param thisObj this object
80       * @param args the arguments
81       * @param function the function
82       * @return the result of the evaluation of the XPath expression
83       */
84      @JsxFunction
85      public static XPathResult evaluate(final Context context, final Scriptable scope,
86              final Scriptable thisObj, final Object[] args, final Function function) {
87          if (args.length < 1) {
88              throw JavaScriptEngine.reportRuntimeError("Missing 'contextNode' parameter");
89          }
90  
91          int type = 0; // ANY
92          if (args.length > 1) {
93              type = (int) JavaScriptEngine.toInteger(args[1]);
94          }
95  
96          Object result = null;
97          if (args.length > 2) {
98              result = args[2];
99          }
100 
101         try {
102             final Object contextNodeObj = args[0];
103             if (!(contextNodeObj instanceof org.htmlunit.javascript.host.dom.Node)) {
104                 throw JavaScriptEngine.reportRuntimeError("Illegal value for parameter 'context'");
105             }
106 
107             final Node contextNode = ((org.htmlunit.javascript.host.dom.Node) contextNodeObj).getDomNodeOrDie();
108             final XPathExpression expression = (XPathExpression) thisObj;
109 
110             final XPathResult xPathResult;
111             if (result instanceof XPathResult) {
112                 xPathResult = (XPathResult) result;
113             }
114             else {
115                 xPathResult = new XPathResult();
116                 xPathResult.setParentScope(expression.getParentScope());
117                 xPathResult.setPrototype(expression.getPrototype(xPathResult.getClass()));
118             }
119 
120             xPathResult.init(XPathHelper.getByXPath(contextNode, expression.xpath_, expression.prefixResolver_), type);
121             return xPathResult;
122         }
123         catch (final Exception e) {
124             throw JavaScriptEngine.reportRuntimeError("Failed to execute 'evaluate': " + e.getMessage());
125         }
126     }
127 }