org.mozilla.javascript.xmlimpl.XmlNode Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of rhino-runtime Show documentation
Show all versions of rhino-runtime Show documentation
Rhino JavaScript runtime jar, excludes tools & JSR-223 Script Engine wrapper.
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.javascript.xmlimpl;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.mozilla.javascript.Undefined;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.ProcessingInstruction;
import org.w3c.dom.UserDataHandler;
class XmlNode implements Serializable {
private static final long serialVersionUID = 1L;
private static final String XML_NAMESPACES_NAMESPACE_URI = "http://www.w3.org/2000/xmlns/";
private static final String USER_DATA_XMLNODE_KEY = XmlNode.class.getName();
private static final boolean DOM_LEVEL_3 = true;
private static XmlNode getUserData(Node node) {
if (DOM_LEVEL_3) {
return (XmlNode)node.getUserData(USER_DATA_XMLNODE_KEY);
}
return null;
}
private static void setUserData(Node node, XmlNode wrap) {
if (DOM_LEVEL_3) {
node.setUserData(USER_DATA_XMLNODE_KEY, wrap, wrap.events);
}
}
private static XmlNode createImpl(Node node) {
if (node instanceof Document) throw new IllegalArgumentException();
XmlNode rv = null;
if (getUserData(node) == null) {
rv = new XmlNode();
rv.dom = node;
setUserData(node, rv);
} else {
rv = getUserData(node);
}
return rv;
}
static XmlNode newElementWithText(XmlProcessor processor, XmlNode reference, XmlNode.QName qname, String value) {
if (reference instanceof org.w3c.dom.Document) throw new IllegalArgumentException("Cannot use Document node as reference");
Document document = null;
if (reference != null) {
document = reference.dom.getOwnerDocument();
} else {
document = processor.newDocument();
}
Node referenceDom = (reference != null) ? reference.dom : null;
Namespace ns = qname.getNamespace();
Element e = (ns == null || ns.getUri().length() == 0)
? document.createElementNS(null, qname.getLocalName())
: document.createElementNS(ns.getUri(),
qname.qualify(referenceDom));
if (value != null) {
e.appendChild(document.createTextNode(value));
}
return XmlNode.createImpl(e);
}
static XmlNode createText(XmlProcessor processor, String value) {
return createImpl( processor.newDocument().createTextNode(value) );
}
static XmlNode createElementFromNode(Node node) {
if (node instanceof Document)
node = ((Document) node).getDocumentElement();
return createImpl(node);
}
static XmlNode createElement(XmlProcessor processor, String namespaceUri, String xml) throws org.xml.sax.SAXException {
return createImpl( processor.toXml(namespaceUri, xml) );
}
static XmlNode createEmpty(XmlProcessor processor) {
return createText(processor, "");
}
private static XmlNode copy(XmlNode other) {
return createImpl( other.dom.cloneNode(true) );
}
private UserDataHandler events = new XmlNodeUserDataHandler();
private Node dom;
private XML xml;
private XmlNode() {
}
String debug() {
XmlProcessor raw = new XmlProcessor();
raw.setIgnoreComments(false);
raw.setIgnoreProcessingInstructions(false);
raw.setIgnoreWhitespace(false);
raw.setPrettyPrinting(false);
return raw.ecmaToXmlString(this.dom);
}
@Override
public String toString() {
return "XmlNode: type=" + dom.getNodeType() + " dom=" + dom.toString();
}
XML getXml() {
return xml;
}
void setXml(XML xml) {
this.xml = xml;
}
int getChildCount() {
return this.dom.getChildNodes().getLength();
}
XmlNode parent() {
Node domParent = dom.getParentNode();
if (domParent instanceof Document) return null;
if (domParent == null) return null;
return createImpl(domParent);
}
int getChildIndex() {
if (this.isAttributeType()) return -1;
if (parent() == null) return -1;
org.w3c.dom.NodeList siblings = this.dom.getParentNode().getChildNodes();
for (int i=0; i map = new HashMap();
private Map uriToPrefix = new HashMap();
Namespaces() {
}
void declare(Namespace n) {
if (map.get(n.prefix) == null) {
map.put(n.prefix, n.uri);
}
// TODO I think this is analogous to the other way, but have not really thought it through ... should local scope
// matter more than outer scope?
if (uriToPrefix.get(n.uri) == null) {
uriToPrefix.put(n.uri, n.prefix);
}
}
Namespace getNamespaceByUri(String uri) {
if (uriToPrefix.get(uri) == null) return null;
return Namespace.create(uri, uriToPrefix.get(uri));
}
Namespace getNamespace(String prefix) {
if (map.get(prefix) == null) return null;
return Namespace.create(prefix, map.get(prefix));
}
Namespace[] getNamespaces() {
ArrayList rv = new ArrayList();
for (Map.Entry e : map.entrySet()) {
Namespace n = Namespace.create(e.getKey(), e.getValue());
if (!n.isEmpty()) {
rv.add(n);
}
}
return rv.toArray(new Namespace[rv.size()]);
}
}
final XmlNode copy() {
return copy( this );
}
// Returns whether this node is capable of being a parent
final boolean isParentType() {
return isElementType();
}
final boolean isTextType() {
return dom.getNodeType() == Node.TEXT_NODE || dom.getNodeType() == Node.CDATA_SECTION_NODE;
}
final boolean isAttributeType() {
return dom.getNodeType() == Node.ATTRIBUTE_NODE;
}
final boolean isProcessingInstructionType() {
return dom.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE;
}
final boolean isCommentType() {
return dom.getNodeType() == Node.COMMENT_NODE;
}
final boolean isElementType() {
return dom.getNodeType() == Node.ELEMENT_NODE;
}
final void renameNode(QName qname) {
this.dom = dom.getOwnerDocument().renameNode(dom, qname.getNamespace().getUri(), qname.qualify(dom));
}
void invalidateNamespacePrefix() {
if (!(dom instanceof Element)) throw new IllegalStateException();
String prefix = this.dom.getPrefix();
QName after = QName.create(this.dom.getNamespaceURI(), this.dom.getLocalName(), null);
renameNode(after);
NamedNodeMap attrs = this.dom.getAttributes();
for (int i=0; i 0) {
e.setAttributeNS(XML_NAMESPACES_NAMESPACE_URI, "xmlns:" + prefix, uri);
} else {
e.setAttribute("xmlns", uri);
}
}
void declareNamespace(String prefix, String uri) {
if (!(dom instanceof Element)) throw new IllegalStateException();
if (dom.lookupNamespaceURI(uri) != null && dom.lookupNamespaceURI(uri).equals(prefix)) {
// do nothing
} else {
Element e = (Element)dom;
declareNamespace(e, prefix, uri);
}
}
private Namespace getDefaultNamespace() {
String prefix = "";
String uri = (dom.lookupNamespaceURI(null) == null) ? "" : dom.lookupNamespaceURI(null);
return Namespace.create(prefix, uri);
}
private String getExistingPrefixFor(Namespace namespace) {
if (getDefaultNamespace().getUri().equals(namespace.getUri())) {
return "";
}
return dom.lookupPrefix(namespace.getUri());
}
private Namespace getNodeNamespace() {
String uri = dom.getNamespaceURI();
String prefix = dom.getPrefix();
if (uri == null) uri = "";
if (prefix == null) prefix = "";
return Namespace.create(prefix, uri);
}
Namespace getNamespace() {
return getNodeNamespace();
}
void removeNamespace(Namespace namespace) {
Namespace current = getNodeNamespace();
// Do not remove in-use namespace
if (namespace.is(current)) return;
NamedNodeMap attrs = this.dom.getAttributes();
for (int i=0; i rv = new ArrayList();
NodeList nodes = this.dom.getChildNodes();
for (int i=0; i 0) return prefix + ":" + localName;
return localName;
}
private Namespace namespace;
private String localName;
private QName() {
}
@Override
public String toString() {
return "XmlNode.QName [" + localName + "," + namespace + "]";
}
private boolean equals(String one, String two) {
if (one == null && two == null) return true;
if (one == null || two == null) return false;
return one.equals(two);
}
private boolean namespacesEqual(Namespace one, Namespace two) {
if (one == null && two == null) return true;
if (one == null || two == null) return false;
return equals(one.getUri(), two.getUri());
}
final boolean equals(QName other) {
if (!namespacesEqual(this.namespace, other.namespace)) return false;
if (!equals(this.localName, other.localName)) return false;
return true;
}
@Override
public boolean equals(Object obj) {
if(!(obj instanceof QName)) {
return false;
}
return equals((QName)obj);
}
@Override
public int hashCode() {
return localName == null ? 0 : localName.hashCode();
}
void lookupPrefix(org.w3c.dom.Node node) {
if (node == null) throw new IllegalArgumentException("node must not be null");
String prefix = node.lookupPrefix(namespace.getUri());
if (prefix == null) {
// check to see if we match the default namespace
String defaultNamespace = node.lookupNamespaceURI(null);
if (defaultNamespace == null) defaultNamespace = "";
String nodeNamespace = namespace.getUri();
if (nodeNamespace.equals(defaultNamespace)) {
prefix = "";
}
}
int i = 0;
while(prefix == null) {
String generatedPrefix = "e4x_" + i++;
String generatedUri = node.lookupNamespaceURI(generatedPrefix);
if (generatedUri == null) {
prefix = generatedPrefix;
org.w3c.dom.Node top = node;
while(top.getParentNode() != null && top.getParentNode() instanceof org.w3c.dom.Element) {
top = top.getParentNode();
}
((org.w3c.dom.Element)top).setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:" + prefix, namespace.getUri());
}
}
namespace.setPrefix(prefix);
}
String qualify(org.w3c.dom.Node node) {
if (namespace.getPrefix() == null) {
if (node != null) {
lookupPrefix(node);
} else {
namespace.setPrefix("");
}
}
return qualify(namespace.getPrefix(), localName);
}
void setAttribute(org.w3c.dom.Element element, String value) {
if (namespace.getPrefix() == null) lookupPrefix(element);
element.setAttributeNS(namespace.getUri(), qualify(namespace.getPrefix(), localName), value);
}
Namespace getNamespace() {
return namespace;
}
String getLocalName() {
return localName;
}
}
static class InternalList implements Serializable {
private static final long serialVersionUID = -3633151157292048978L;
private List list;
InternalList() {
list = new ArrayList();
}
private void _add(XmlNode n) {
list.add(n);
}
XmlNode item(int index) {
return list.get(index);
}
void remove(int index) {
list.remove(index);
}
void add(InternalList other) {
for (int i=0; i