org.htmlunit.activex.javascript.msxml.XMLDOMElement Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of xlt Show documentation
Show all versions of xlt Show documentation
XLT (Xceptance LoadTest) is an extensive load and performance test tool developed and maintained by Xceptance.
/*
* Copyright (c) 2002-2024 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
* https://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 org.htmlunit.activex.javascript.msxml;
import static org.htmlunit.html.DomElement.ATTRIBUTE_NOT_DEFINED;
import static org.htmlunit.javascript.configuration.SupportedBrowser.IE;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.htmlunit.html.DomAttr;
import org.htmlunit.html.DomElement;
import org.htmlunit.html.DomNode;
import org.htmlunit.html.DomText;
import org.htmlunit.javascript.HtmlUnitScriptable;
import org.htmlunit.javascript.JavaScriptEngine;
import org.htmlunit.javascript.configuration.JsxClass;
import org.htmlunit.javascript.configuration.JsxFunction;
import org.htmlunit.javascript.configuration.JsxGetter;
import org.htmlunit.javascript.host.dom.Node;
/**
* A JavaScript object for MSXML's (ActiveX) XMLDOMElement.
* Represents the element object.
* @see MSDN documentation
*
* @author Ahmed Ashour
* @author Marc Guillemot
* @author Sudhan Moghe
* @author Ronald Brill
* @author Frank Danek
*/
@JsxClass(domClass = DomElement.class, value = IE)
public class XMLDOMElement extends XMLDOMNode {
private XMLDOMNamedNodeMap attributes_;
private Map elementsByTagName_; // for performance and for equality (==)
/**
* Returns the list of attributes for this element.
* @return the list of attributes for this element
*/
@Override
public Object getAttributes() {
if (attributes_ == null) {
attributes_ = createAttributesObject();
}
return attributes_;
}
/**
* Creates the JS object for the property attributes. This object will the be cached.
* @return the JS object
*/
protected XMLDOMNamedNodeMap createAttributesObject() {
return new XMLDOMNamedNodeMap(getDomNodeOrDie());
}
/**
* Attempting to set the value of elements generates an error.
* @param newValue the new value to set
*/
@Override
public void setNodeValue(final String newValue) {
if (newValue == null || "null".equals(newValue)) {
throw JavaScriptEngine.reportRuntimeError("Type mismatch.");
}
throw JavaScriptEngine.reportRuntimeError("This operation cannot be performed with a node of type ELEMENT.");
}
/**
* Returns the element name.
* @return the element name
*/
@JsxGetter
public String getTagName() {
return getNodeName();
}
/**
* Returns a string that represents the element content. This will also include the text content from all child
* elements, concatenated in document order.
* @return a string that represents the element content
*/
@Override
public String getText() {
final StringBuilder builder = new StringBuilder();
toText(getDomNodeOrDie(), builder);
if (builder.length() > 0 && builder.charAt(builder.length() - 1) == '\n') {
return builder.substring(0, builder.length() - 1);
}
return builder.toString();
}
/**
* Replaces all children of this element with the supplied value.
* @param value the new value for the contents of this node
*/
@Override
public void setText(final Object value) {
if (value == null || "null".equals(value)) {
throw JavaScriptEngine.reportRuntimeError("Type mismatch.");
}
super.setText(value);
}
private void toText(final DomNode node, final StringBuilder builder) {
switch (node.getNodeType()) {
case Node.DOCUMENT_TYPE_NODE:
case Node.NOTATION_NODE:
return;
case Node.TEXT_NODE:
case Node.CDATA_SECTION_NODE:
case Node.COMMENT_NODE:
case Node.PROCESSING_INSTRUCTION_NODE:
builder.append(node.getNodeValue());
break;
default:
}
boolean lastWasElement = false;
for (final DomNode child : node.getChildren()) {
switch (child.getNodeType()) {
case Node.ELEMENT_NODE:
lastWasElement = true;
toText(child, builder);
break;
case Node.TEXT_NODE:
case Node.CDATA_SECTION_NODE:
if (StringUtils.isBlank(child.getNodeValue())) {
if (lastWasElement) {
builder.append(' ');
}
lastWasElement = false;
break;
}
lastWasElement = false;
builder.append(child.getNodeValue());
break;
default:
lastWasElement = false;
}
}
}
/**
* Returns the value of the attribute.
* @param name the name of the attribute to return
* @return the value of the specified attribute, {@code null} if the named attribute does not have a
* specified value
*/
@JsxFunction
public Object getAttribute(final String name) {
if (name == null || "null".equals(name)) {
throw JavaScriptEngine.reportRuntimeError("Type mismatch.");
}
if (StringUtils.isEmpty(name)) {
throw JavaScriptEngine.reportRuntimeError("The empty string '' is not a valid name.");
}
final String value = getDomNodeOrDie().getAttribute(name);
if (ATTRIBUTE_NOT_DEFINED == value) {
return null;
}
return value;
}
/**
* Returns the attribute node.
* @param name the name of the attribute to return
* @return the attribute node with the supplied name, {@code null} if the named attribute cannot be found
* on this element
*/
@JsxFunction
public HtmlUnitScriptable getAttributeNode(final String name) {
if (name == null || "null".equals(name)) {
throw JavaScriptEngine.reportRuntimeError("Type mismatch.");
}
if (StringUtils.isEmpty(name)) {
throw JavaScriptEngine.reportRuntimeError("The empty string '' is not a valid name.");
}
final Map attributes = getDomNodeOrDie().getAttributesMap();
for (final DomAttr attr : attributes.values()) {
if (attr.getName().equals(name)) {
return attr.getScriptableObject();
}
}
return null;
}
/**
* Removes the named attribute.
* @param name the name of the attribute to be removed
*/
@JsxFunction
public void removeAttribute(final String name) {
if (name == null || "null".equals(name)) {
throw JavaScriptEngine.reportRuntimeError("Type mismatch.");
}
if (StringUtils.isEmpty(name)) {
throw JavaScriptEngine.reportRuntimeError("The empty string '' is not a valid name.");
}
getDomNodeOrDie().removeAttribute(name);
delete(name);
}
/**
* Removes the specified attribute from this element.
* @param att the attribute to be removed from this element
* @return the removed attribute
*/
@JsxFunction
public XMLDOMAttribute removeAttributeNode(final XMLDOMAttribute att) {
if (att == null) {
throw JavaScriptEngine.reportRuntimeError("Type mismatch.");
}
final String name = att.getName();
final XMLDOMNamedNodeMap nodes = (XMLDOMNamedNodeMap) getAttributes();
final XMLDOMAttribute removedAtt = (XMLDOMAttribute) nodes.getNamedItemWithoutSyntheticClassAttr(name);
if (removedAtt != null) {
removedAtt.detachFromParent();
}
removeAttribute(name);
return removedAtt;
}
/**
* Sets the value of the named attribute.
*
* @param name the name of the attribute; if the attribute with that name already exists, its value is changed
* @param value the value for the named attribute
*/
@JsxFunction
public void setAttribute(final String name, final String value) {
if (name == null || "null".equals(name)) {
throw JavaScriptEngine.reportRuntimeError("Type mismatch.");
}
if (StringUtils.isEmpty(name)) {
throw JavaScriptEngine.reportRuntimeError("The empty string '' is not a valid name.");
}
if (value == null || "null".equals(value)) {
throw JavaScriptEngine.reportRuntimeError("Type mismatch.");
}
getDomNodeOrDie().setAttribute(name, value);
}
/**
* Sets or updates the supplied attribute node on this element.
* @param newAtt the attribute node to be associated with this element
* @return the replaced attribute node, if any, {@code null} otherwise
*/
@JsxFunction
public XMLDOMAttribute setAttributeNode(final XMLDOMAttribute newAtt) {
if (newAtt == null) {
throw JavaScriptEngine.reportRuntimeError("Type mismatch.");
}
final String name = newAtt.getBaseName();
final XMLDOMNamedNodeMap nodes = (XMLDOMNamedNodeMap) getAttributes();
final XMLDOMAttribute replacedAtt = (XMLDOMAttribute) nodes.getNamedItemWithoutSyntheticClassAttr(name);
if (replacedAtt != null) {
replacedAtt.detachFromParent();
}
final DomAttr newDomAttr = newAtt.getDomNodeOrDie();
getDomNodeOrDie().setAttributeNode(newDomAttr);
return replacedAtt;
}
/**
* Returns a list of all descendant elements that match the supplied name.
* @param tagName the name of the element to find; the tagName value '*' matches all descendant elements of this
* element
* @return a list containing all elements that match the supplied name
*/
@JsxFunction
public XMLDOMNodeList getElementsByTagName(final String tagName) {
if (tagName == null || "null".equals(tagName)) {
throw JavaScriptEngine.reportRuntimeError("Type mismatch.");
}
final String tagNameTrimmed = tagName.trim();
if (elementsByTagName_ == null) {
elementsByTagName_ = new HashMap<>();
}
XMLDOMNodeList collection = elementsByTagName_.get(tagNameTrimmed);
if (collection != null) {
return collection;
}
final DomNode node = getDomNodeOrDie();
final String description = "XMLDOMElement.getElementsByTagName('" + tagNameTrimmed + "')";
if ("*".equals(tagNameTrimmed)) {
collection = new XMLDOMNodeList(node, false, description) {
@Override
protected boolean isMatching(final DomNode domNode) {
return true;
}
};
}
else if ("".equals(tagNameTrimmed)) {
collection = new XMLDOMNodeList(node, false, description) {
@Override
protected List computeElements() {
final List response = new ArrayList<>();
final DomNode domNode = getDomNodeOrNull();
if (domNode == null) {
return response;
}
for (final DomNode candidate : getCandidates()) {
if (candidate instanceof DomText) {
final DomText domText = (DomText) candidate;
if (!StringUtils.isBlank(domText.getWholeText())) {
response.add(candidate);
}
}
else {
response.add(candidate);
}
}
return response;
}
};
}
else {
collection = new XMLDOMNodeList(node, false, description) {
@Override
protected boolean isMatching(final DomNode domNode) {
return tagNameTrimmed.equals(domNode.getNodeName());
}
};
}
elementsByTagName_.put(tagName, collection);
return collection;
}
/**
* Normalizes all descendant elements by combining two or more adjacent text nodes into one unified text node.
*/
@JsxFunction
public void normalize() {
final DomElement domElement = getDomNodeOrDie();
domElement.normalize();
// normalize all descendants
normalize(domElement);
}
private void normalize(final DomElement domElement) {
for (final DomNode domNode : domElement.getChildren()) {
if (domNode instanceof DomElement) {
domNode.normalize();
normalize((DomElement) domNode);
}
}
}
/**
* {@inheritDoc}
*/
@Override
public DomElement getDomNodeOrDie() {
return (DomElement) super.getDomNodeOrDie();
}
}