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 }