jscover.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 Show documentation
Show all versions of rhino Show documentation
Rhino is an open-source implementation of JavaScript written entirely in
Java. It is typically embedded into Java applications to provide
scripting to end users.
/* 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 jscover.mozilla.javascript.xmlimpl;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import jscover.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 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 static final long serialVersionUID = 1L;
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 (String prefix: map.keySet()) {
String uri = map.get(prefix);
Namespace n = Namespace.create(prefix, uri);
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 {
if (namespace.getUri().equals("")) {
namespace.setPrefix("");
} else {
// TODO I am not sure this is right, but if we are creating a standalone node, I think we can set the
// default namespace on the node itself and not worry about setting a prefix for that namespace.
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