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.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          if (getPage().isHtmlPage()
71              && !(getPage() instanceof XHtmlPage)
72              && Html.XHTML_NAMESPACE.equals(namespaceURI_)
73              && XPathHelper.isProcessingXPath()) {
74              // for xpath processing we have to strip the 'default' XHTML namespace for HTML pages to be able to find
75              // the elements by XPath without needing to add the namespace to it
76              return null;
77          }
78          return namespaceURI_;
79      }
80  
81      /**
82       * {@inheritDoc}
83       */
84      @Override
85      public String getLocalName() {
86          final boolean caseSensitive = getPage().hasCaseSensitiveTagNames();
87          if (!caseSensitive
88                  && XPathHelper.isProcessingXPath()) { // and this method was called from xpath processor
89              return localNameLC_;
90          }
91          return localName_;
92      }
93  
94      /**
95       * <span style="color:red">INTERNAL API - SUBJECT TO CHANGE AT ANY TIME - USE AT YOUR OWN RISK.</span><br>
96       * @return the element name as lowercase
97       */
98      public String getLowercaseName() {
99          return localNameLC_;
100     }
101 
102     /**
103      * {@inheritDoc}
104      */
105     @Override
106     public String getPrefix() {
107         return prefix_;
108     }
109 
110     /**
111      * {@inheritDoc}
112      */
113     @Override
114     public void setPrefix(final String prefix) {
115         prefix_ = prefix;
116         if (prefix_ != null && localName_ != null) {
117             qualifiedName_ = prefix_ + ":" + localName_;
118         }
119     }
120 
121     /**
122      * Returns this node's qualified name.
123      * @return this node's qualified name
124      */
125     public String getQualifiedName() {
126         return qualifiedName_;
127     }
128 
129     /**
130      * {@inheritDoc}
131      */
132     @Override
133     public void processImportNode(final Document doc) {
134         super.processImportNode(doc);
135 
136         // if we are importing from a namespace-aware source
137         // we have to drop the XHtmlNamespace because we did this already
138         // for the HTML document itself
139         final SgmlPage page = (SgmlPage) doc.getDomNodeOrDie();
140         if (page.isHtmlPage() && !(page instanceof XHtmlPage) && Html.XHTML_NAMESPACE.equals(namespaceURI_)) {
141             namespaceURI_ = null;
142         }
143     }
144 }