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;
16  
17  import org.htmlunit.corejs.javascript.Scriptable;
18  import org.htmlunit.html.DomElement;
19  import org.htmlunit.html.DomNode;
20  import org.htmlunit.javascript.HtmlUnitScriptable;
21  import org.htmlunit.javascript.JavaScriptEngine;
22  import org.htmlunit.javascript.configuration.JsxClass;
23  import org.htmlunit.javascript.configuration.JsxConstructor;
24  import org.htmlunit.javascript.configuration.JsxFunction;
25  import org.htmlunit.javascript.configuration.JsxGetter;
26  import org.htmlunit.javascript.configuration.JsxSymbol;
27  import org.htmlunit.javascript.host.dom.Attr;
28  import org.htmlunit.javascript.host.dom.Node;
29  
30  /**
31   * A collection of nodes that can be accessed by name. String comparisons in this class are case-insensitive when
32   * used with an {@link org.htmlunit.html.HtmlElement},
33   * but case-sensitive when used with a {@link org.htmlunit.html.DomElement}.
34   *
35   * @author Daniel Gredler
36   * @author Ahmed Ashour
37   * @author Marc Guillemot
38   * @author Ronald Brill
39   * @author Frank Danek
40   *
41   * @see <a href="https://developer.mozilla.org/en-US/docs/Web/API/NamedNodeMap">MDN Documentation</a>
42   */
43  @JsxClass
44  public class NamedNodeMap extends HtmlUnitScriptable {
45  
46      private final org.w3c.dom.NamedNodeMap attributes_;
47  
48      /**
49       * Default constructor for prototype instantiation.
50       */
51      public NamedNodeMap() {
52          super();
53          attributes_ = null;
54      }
55  
56      /**
57       * Creates an instance of this object.
58       */
59      @JsxConstructor
60      public void jsConstructor() {
61          // nothing to do
62      }
63  
64      /**
65       * Creates a new named node map for the specified element.
66       *
67       * @param element the owning element
68       */
69      public NamedNodeMap(final DomElement element) {
70          super();
71          setParentScope(element.getScriptableObject().getParentScope());
72          setPrototype(getPrototype(getClass()));
73  
74          attributes_ = element.getAttributes();
75          setDomNode(element, false);
76      }
77  
78      /**
79       * {@inheritDoc}
80       */
81      @Override
82      public final Object get(final int index, final Scriptable start) {
83          final NamedNodeMap startMap = (NamedNodeMap) start;
84          final Object response = startMap.item(index);
85          if (response != null) {
86              return response;
87          }
88          return NOT_FOUND;
89      }
90  
91      /**
92       * {@inheritDoc}
93       */
94      @Override
95      public Object get(final String name, final Scriptable start) {
96          Object response = super.get(name, start);
97          if (response != NOT_FOUND) {
98              return response;
99          }
100 
101         response = getNamedItem(name);
102         if (response != null) {
103             return response;
104         }
105 
106         return NOT_FOUND;
107     }
108 
109     /**
110      * <span style="color:red">INTERNAL API - SUBJECT TO CHANGE AT ANY TIME - USE AT YOUR OWN RISK.</span><br>
111      *
112      * Gets the specified attribute without handling the synthetic class attribute.
113      *
114      * @param name the attribute name
115      * @return the attribute node, or {@code null} if not found
116      * @see #getNamedItem(String)
117      */
118     public HtmlUnitScriptable getNamedItemWithoutSytheticClassAttr(final String name) {
119         if (attributes_ != null) {
120             final DomNode attr = (DomNode) attributes_.getNamedItem(name);
121             if (attr != null) {
122                 return attr.getScriptableObject();
123             }
124         }
125 
126         return null;
127     }
128 
129     /**
130      * Returns the attribute node with the specified name.
131      *
132      * @param name the attribute name
133      * @return the attribute node, or {@code null} if not defined
134      */
135     @JsxFunction
136     public HtmlUnitScriptable getNamedItem(final String name) {
137         return getNamedItemWithoutSytheticClassAttr(name);
138     }
139 
140     /**
141      * Returns the attribute node with the given namespace URI and local name.
142      *
143      * @param namespaceURI the namespace URI of the node to retrieve
144      * @param localName the local name of the node to retrieve
145      * @return the attribute node, or {@code null} if not found
146      */
147     @JsxFunction
148     public Node getNamedItemNS(final String namespaceURI, final String localName) {
149         if (attributes_ != null) {
150             final DomNode attr = (DomNode) attributes_.getNamedItemNS(namespaceURI, localName);
151             if (attr != null) {
152                 return attr.getScriptableObject();
153             }
154         }
155 
156         return null;
157     }
158 
159     /**
160      * Sets the specified attribute node.
161      *
162      * @param node the attribute node to set
163      */
164     @JsxFunction
165     public void setNamedItem(final Node node) {
166         attributes_.setNamedItem(node.getDomNodeOrDie());
167     }
168 
169     /**
170      * Sets the specified attribute node using its namespace URI and local name.
171      *
172      * @param node the attribute node to set
173      */
174     @JsxFunction
175     public void setNamedItemNS(final Node node) {
176         attributes_.setNamedItemNS(node.getDomNodeOrDie());
177     }
178 
179     /**
180      * Removes the attribute with the specified name.
181      *
182      * @param name the name of the attribute to remove
183      */
184     @JsxFunction
185     public void removeNamedItem(final String name) {
186         attributes_.removeNamedItem(name);
187     }
188 
189     /**
190      * Removes the attribute with the given namespace URI and local name.
191      *
192      * @param namespaceURI the namespace URI of the attribute to remove
193      * @param localName the local name of the attribute to remove
194      * @return the removed attribute node, or {@code null} if not found
195      */
196     @JsxFunction
197     public Attr removeNamedItemNS(final String namespaceURI, final String localName) {
198         return (Attr) attributes_.removeNamedItemNS(namespaceURI, localName);
199     }
200 
201     /**
202      * Returns the attribute node at the specified index.
203      *
204      * @param index the index
205      * @return the attribute node at the given index, or {@code null} if out of range
206      */
207     @JsxFunction
208     public HtmlUnitScriptable item(final int index) {
209         final DomNode attr = (DomNode) attributes_.item(index);
210         if (attr != null) {
211             return attr.getScriptableObject();
212         }
213         return null;
214     }
215 
216     /**
217      * Returns the number of attributes in this map.
218      *
219      * @return the number of attributes
220      */
221     @JsxGetter
222     public int getLength() {
223         return attributes_.getLength();
224     }
225 
226     /**
227      * {@inheritDoc}
228      */
229     @Override
230     public boolean has(final int index, final Scriptable start) {
231         return index >= 0 && index < getLength();
232     }
233 
234     /**
235      * Returns an iterator over the values in this map.
236      *
237      * @return the iterator
238      */
239     @JsxSymbol
240     public Scriptable iterator() {
241         return JavaScriptEngine.newArrayIteratorTypeValues(getParentScope(), this);
242     }
243 }