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.html;
16  
17  import org.htmlunit.SgmlPage;
18  import org.htmlunit.WebAssert;
19  import org.htmlunit.html.xpath.XPathHelper;
20  import org.htmlunit.javascript.host.dom.Document;
21  import org.htmlunit.util.StringUtils;
22  
23  /**
24   * Intermediate base class for DOM Nodes that have namespaces. That includes HtmlElement and HtmlAttr.
25   *
26   * @author David K. Taylor
27   * @author Ahmed Ashour
28   * @author Ronald Brill
29   * @author Frank Danek
30   */
31  public abstract class DomNamespaceNode extends DomNode {
32  
33      private String namespaceURI_;
34      private String qualifiedName_;
35      private final String localName_;
36      private final String localNameLC_;
37      private String prefix_;
38  
39      /**
40       * Creates an instance of a DOM node that can have a namespace.
41       *
42       * @param namespaceURI the URI that identifies an XML namespace
43       * @param qualifiedName the qualified name of the element type to instantiate
44       * @param page the page that contains this element
45       */
46      protected DomNamespaceNode(final String namespaceURI, final String qualifiedName, final SgmlPage page) {
47          super(page);
48          WebAssert.notNull("qualifiedName", qualifiedName);
49          qualifiedName_ = qualifiedName;
50          namespaceURI_ = namespaceURI;
51  
52          final int colonPosition = qualifiedName_.indexOf(':');
53          if (colonPosition == -1) {
54              localName_ = qualifiedName_;
55              prefix_ = null;
56          }
57          else {
58              localName_ = qualifiedName_.substring(colonPosition + 1);
59              prefix_ = qualifiedName_.substring(0, colonPosition);
60          }
61  
62          localNameLC_ = StringUtils.toRootLowerCase(localName_);
63      }
64  
65      /**
66       * {@inheritDoc}
67       */
68      @Override
69      public String getNamespaceURI() {
70          final SgmlPage page = getPage();
71          if (page.isHtmlPage()
72              && !(page instanceof XHtmlPage)
73              && Html.XHTML_NAMESPACE.equals(namespaceURI_)
74              && XPathHelper.isProcessingXPath()) {
75              // for xpath processing we have to strip the 'default' XHTML namespace for HTML pages to be able to find
76              // the elements by XPath without needing to add the namespace to it
77              return null;
78          }
79          return namespaceURI_;
80      }
81  
82      /**
83       * {@inheritDoc}
84       */
85      @Override
86      public String getLocalName() {
87          final boolean caseSensitive = getPage().hasCaseSensitiveTagNames();
88          if (!caseSensitive
89                  && XPathHelper.isProcessingXPath()) { // and this method was called from xpath processor
90              return localNameLC_;
91          }
92          return localName_;
93      }
94  
95      /**
96       * <span style="color:red">INTERNAL API - SUBJECT TO CHANGE AT ANY TIME - USE AT YOUR OWN RISK.</span><br>
97       * @return the element name as lowercase
98       */
99      public String getLowercaseName() {
100         return localNameLC_;
101     }
102 
103     /**
104      * {@inheritDoc}
105      */
106     @Override
107     public String getPrefix() {
108         return prefix_;
109     }
110 
111     /**
112      * {@inheritDoc}
113      */
114     @Override
115     public void setPrefix(final String prefix) {
116         prefix_ = prefix;
117         if (localName_ != null) {
118             if (prefix != null && !prefix.isEmpty()) {
119                 qualifiedName_ = prefix + ":" + localName_;
120             }
121             else {
122                 qualifiedName_ = localName_;
123             }
124         }
125     }
126 
127     /**
128      * Returns this node's qualified name.
129      * @return this node's qualified name
130      */
131     public String getQualifiedName() {
132         return qualifiedName_;
133     }
134 
135     /**
136      * {@inheritDoc}
137      */
138     @Override
139     public void processImportNode(final Document doc) {
140         super.processImportNode(doc);
141 
142         // if we are importing from a namespace-aware source
143         // we have to drop the XHtmlNamespace because we did this already
144         // for the HTML document itself
145         if (doc.getDomNodeOrDie() instanceof SgmlPage page) {
146             if (page.isHtmlPage() && !(page instanceof XHtmlPage) && Html.XHTML_NAMESPACE.equals(namespaceURI_)) {
147                 namespaceURI_ = null;
148             }
149         }
150     }
151 }