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