com.gargoylesoftware.htmlunit.javascript.NamedNodeMap Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of htmlunit Show documentation
Show all versions of htmlunit Show documentation
A headless browser intended for use in testing web-based applications.
/*
* Copyright (c) 2002-2010 Gargoyle Software Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.gargoylesoftware.htmlunit.javascript;
import net.sourceforge.htmlunit.corejs.javascript.Context;
import net.sourceforge.htmlunit.corejs.javascript.Scriptable;
import com.gargoylesoftware.htmlunit.BrowserVersionFeatures;
import com.gargoylesoftware.htmlunit.html.DomAttr;
import com.gargoylesoftware.htmlunit.html.DomElement;
import com.gargoylesoftware.htmlunit.html.DomNode;
import com.gargoylesoftware.htmlunit.html.HtmlElement;
import com.gargoylesoftware.htmlunit.javascript.host.Attr;
import com.gargoylesoftware.htmlunit.javascript.host.Node;
/**
* A collection of nodes that can be accessed by name. String comparisons in this class are case-insensitive when
* used with an {@link com.gargoylesoftware.htmlunit.html.HtmlElement},
* but case-sensitive when used with a {@link DomElement}.
*
* @version $Revision: 5864 $
* @author Daniel Gredler
* @author Ahmed Ashour
* @author Marc Guillemot
* @see DOM Level 2 Core Spec
* @see IXMLDOMNamedNodeMap
*/
public class NamedNodeMap extends SimpleScriptable implements ScriptableWithFallbackGetter {
private static final long serialVersionUID = -1910087049570242560L;
private final org.w3c.dom.NamedNodeMap attributes_;
/**
* We need default constructors to build the prototype instance.
*/
public NamedNodeMap() {
attributes_ = null;
}
/**
* Creates a new named node map for the specified element.
*
* @param element the owning element
*/
public NamedNodeMap(final DomElement element) {
setParentScope(element.getScriptObject());
setPrototype(getPrototype(getClass()));
attributes_ = element.getAttributes();
setDomNode(element, false);
}
/**
* Returns the element at the specified index, or NOT_FOUND if the index is invalid.
*
* {@inheritDoc}
*/
@Override
public final Object get(final int index, final Scriptable start) {
final NamedNodeMap startMap = (NamedNodeMap) start;
final Object response = startMap.jsxFunction_item(index);
if (response != null) {
return response;
}
return NOT_FOUND;
}
/**
* Returns the element with the specified name, or NOT_FOUND if the name is invalid.
*
* {@inheritDoc}
*/
public Object getWithFallback(final String name) {
final Object response = jsxFunction_getNamedItem(name);
if (response != null) {
return response;
}
if (useRecursiveAttributeForIE() && isRecursiveAttribute(name)) {
return getUnspecifiedAttributeNode(name);
}
return NOT_FOUND;
}
/**
* Gets the specified attribute.
* @param name attribute name
* @return the attribute node, null
if the attribute is not defined
*/
public Object jsxFunction_getNamedItem(final String name) {
final DomNode attr = (DomNode) attributes_.getNamedItem(name);
if (attr != null) {
return attr.getScriptObject();
}
if (!name.equals("className") && useRecursiveAttributeForIE() && isRecursiveAttribute(name)) {
return getUnspecifiedAttributeNode(name);
}
return null;
}
/**
* Sets the specified attribute.
* @param node the attribute
*/
public void jsxFunction_setNamedItem(final Node node) {
attributes_.setNamedItem(node.getDomNodeOrDie());
}
/**
* Returns the item at the specified index.
* @param index the index
* @return the item at the specified index
*/
public Object jsxFunction_item(int index) {
final DomNode attr = (DomNode) attributes_.item(index);
if (attr != null) {
return attr.getScriptObject();
}
if (useRecursiveAttributeForIE()) {
index -= attributes_.getLength();
final String name = getRecusiveAttributeNameAt(index);
if (name != null) {
return getUnspecifiedAttributeNode(name);
}
}
return null;
}
private boolean useRecursiveAttributeForIE() {
return getBrowserVersion().hasFeature(BrowserVersionFeatures.GENERATED_148)
&& this.getDomNodeOrDie() instanceof HtmlElement;
}
/**
* Creates a new unspecified attribute node.
* @return a new unspecified attribute node
*/
private Attr getUnspecifiedAttributeNode(final String attrName) {
final HtmlElement domNode = (HtmlElement) getDomNodeOrDie();
final DomAttr attr = domNode.getPage().createAttribute(attrName);
domNode.setAttributeNode(attr);
return (Attr) attr.getScriptObject();
}
/**
* Returns the number of attributes in this named node map.
* @return the number of attributes in this named node map
*/
public int jsxGet_length() {
int length = attributes_.getLength();
if (useRecursiveAttributeForIE()) {
length += getRecursiveAttributesLength();
}
return length;
}
private boolean isRecursiveAttribute(final String name) {
for (Scriptable object = this.getDomNodeOrDie().getScriptObject(); object != null;
object = object.getPrototype()) {
for (final Object id : object.getIds()) {
if (name.equals(Context.toString(id))) {
return true;
}
}
}
return false;
}
private int getRecursiveAttributesLength() {
int length = 0;
for (Scriptable object = this.getDomNodeOrDie().getScriptObject(); object != null;
object = object.getPrototype()) {
length += object.getIds().length;
}
return length;
}
private String getRecusiveAttributeNameAt(final int index) {
int i = 0;
for (Scriptable object = this.getDomNodeOrDie().getScriptObject(); object != null;
object = object.getPrototype()) {
for (final Object id : object.getIds()) {
if (i == index) {
return Context.toString(id);
}
i++;
}
}
return null;
}
}