summaryrefslogtreecommitdiffstats
path: root/infrastructure/rhino1_7R1/xmlimplsrc/org/mozilla/javascript/xmlimpl/XmlNode.java
diff options
context:
space:
mode:
Diffstat (limited to 'infrastructure/rhino1_7R1/xmlimplsrc/org/mozilla/javascript/xmlimpl/XmlNode.java')
-rw-r--r--infrastructure/rhino1_7R1/xmlimplsrc/org/mozilla/javascript/xmlimpl/XmlNode.java869
1 files changed, 869 insertions, 0 deletions
diff --git a/infrastructure/rhino1_7R1/xmlimplsrc/org/mozilla/javascript/xmlimpl/XmlNode.java b/infrastructure/rhino1_7R1/xmlimplsrc/org/mozilla/javascript/xmlimpl/XmlNode.java
new file mode 100644
index 0000000..06955d0
--- /dev/null
+++ b/infrastructure/rhino1_7R1/xmlimplsrc/org/mozilla/javascript/xmlimpl/XmlNode.java
@@ -0,0 +1,869 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (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.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino DOM-only E4X implementation.
+ *
+ * The Initial Developer of the Original Code is
+ * David P. Caldwell.
+ * Portions created by David P. Caldwell are Copyright (C)
+ * 2007 David P. Caldwell. All Rights Reserved.
+ *
+ *
+ * Contributor(s):
+ * David P. Caldwell <inonit@inonit.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript.xmlimpl;
+
+import java.util.*;
+
+import org.w3c.dom.*;
+
+import org.mozilla.javascript.*;
+
+// Disambiguate with org.mozilla.javascript
+import org.w3c.dom.Node;
+
+class XmlNode {
+ 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;
+ Element e = document.createElementNS(qname.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 UserDataHandler() {
+ public void handle(short operation, String key, Object data, Node src, Node dest) {
+ }
+ };
+
+ 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);
+ }
+
+ 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<siblings.getLength(); i++) {
+ if (siblings.item(i) == dom) {
+ return i;
+ }
+ }
+ // Either the parent is -1 or one of the this node's parent's children is this node.
+ throw new RuntimeException("Unreachable.");
+ }
+
+ void removeChild(int index) {
+ this.dom.removeChild( this.dom.getChildNodes().item(index) );
+ }
+
+ String toXmlString(XmlProcessor processor) {
+ return processor.ecmaToXmlString(this.dom);
+ }
+
+ String ecmaValue() {
+ // TODO See ECMA 357 Section 9.1
+ if (isTextType()) {
+ return ((org.w3c.dom.Text)dom).getData();
+ } else if (isAttributeType()) {
+ return ((org.w3c.dom.Attr)dom).getValue();
+ } else if (isProcessingInstructionType()) {
+ return ((org.w3c.dom.ProcessingInstruction)dom).getData();
+ } else if (isCommentType()) {
+ return ((org.w3c.dom.Comment)dom).getNodeValue();
+ } else if (isElementType()) {
+ throw new RuntimeException("Unimplemented ecmaValue() for elements.");
+ } else {
+ throw new RuntimeException("Unimplemented for node " + dom);
+ }
+ }
+
+ void deleteMe() {
+ if (dom instanceof Attr) {
+ Attr attr = (Attr)this.dom;
+ attr.getOwnerElement().getAttributes().removeNamedItemNS(attr.getNamespaceURI(), attr.getLocalName());
+ } else {
+ if (this.dom.getParentNode() != null) {
+ this.dom.getParentNode().removeChild(this.dom);
+ } else {
+ // This case can be exercised at least when executing the regression
+ // tests under https://bugzilla.mozilla.org/show_bug.cgi?id=354145
+ }
+ }
+ }
+
+ void normalize() {
+ this.dom.normalize();
+ }
+
+ void insertChildAt(int index, XmlNode node) {
+ Node parent = this.dom;
+ Node child = parent.getOwnerDocument().importNode( node.dom, true );
+ if (parent.getChildNodes().getLength() < index) {
+ // TODO Check ECMA for what happens here
+ throw new IllegalArgumentException("index=" + index + " length=" + parent.getChildNodes().getLength());
+ }
+ if (parent.getChildNodes().getLength() == index) {
+ parent.appendChild(child);
+ } else {
+ parent.insertBefore(child, parent.getChildNodes().item(index));
+ }
+ }
+
+ void insertChildrenAt(int index, XmlNode[] nodes) {
+ for (int i=0; i<nodes.length; i++) {
+ insertChildAt(index+i, nodes[i]);
+ }
+ }
+
+ XmlNode getChild(int index) {
+ Node child = dom.getChildNodes().item(index);
+ return createImpl(child);
+ }
+
+ // Helper method for XML.hasSimpleContent()
+ boolean hasChildElement() {
+ org.w3c.dom.NodeList nodes = this.dom.getChildNodes();
+ for (int i=0; i<nodes.getLength(); i++) {
+ if (nodes.item(i).getNodeType() == org.w3c.dom.Node.ELEMENT_NODE) return true;
+ }
+ return false;
+ }
+
+ boolean isSameNode(XmlNode other) {
+ // TODO May need to be changed if we allow XmlNode to refer to several Node objects
+ return this.dom == other.dom;
+ }
+
+ private String toUri(String ns) {
+ return (ns == null) ? "" : ns;
+ }
+
+ private void addNamespaces(Namespaces rv, Element element) {
+ if (element == null) throw new RuntimeException("element must not be null");
+ String myDefaultNamespace = toUri(element.lookupNamespaceURI(null));
+ String parentDefaultNamespace = "";
+ if (element.getParentNode() != null) {
+ parentDefaultNamespace = toUri(element.getParentNode().lookupNamespaceURI(null));
+ }
+ if (!myDefaultNamespace.equals(parentDefaultNamespace) || !(element.getParentNode() instanceof Element) ) {
+ rv.declare(Namespace.create("", myDefaultNamespace));
+ }
+ NamedNodeMap attributes = element.getAttributes();
+ for (int i=0; i<attributes.getLength(); i++) {
+ Attr attr = (Attr)attributes.item(i);
+ if (attr.getPrefix() != null && attr.getPrefix().equals("xmlns")) {
+ rv.declare(Namespace.create(attr.getLocalName(), attr.getValue()));
+ }
+ }
+ }
+
+ private Namespaces getAllNamespaces() {
+ Namespaces rv = new Namespaces();
+
+ Node target = this.dom;
+ if (target instanceof Attr) {
+ target = ((Attr)target).getOwnerElement();
+ }
+ while(target != null) {
+ if (target instanceof Element) {
+ addNamespaces(rv, (Element)target);
+ }
+ target = target.getParentNode();
+ }
+ // Fallback in case no namespace was declared
+ rv.declare(Namespace.create("", ""));
+ return rv;
+ }
+
+ Namespace[] getInScopeNamespaces() {
+ Namespaces rv = getAllNamespaces();
+ return rv.getNamespaces();
+ }
+
+ Namespace[] getNamespaceDeclarations() {
+ // ECMA357 13.4.4.24
+ if (this.dom instanceof Element) {
+ Namespaces rv = new Namespaces();
+ addNamespaces( rv, (Element)this.dom );
+ return rv.getNamespaces();
+ } else {
+ return new Namespace[0];
+ }
+ }
+
+ Namespace getNamespaceDeclaration(String prefix) {
+ if (prefix.equals("") && dom instanceof Attr) {
+ // Default namespaces do not apply to attributes; see XML Namespaces section 5.2
+ return Namespace.create("", "");
+ }
+ Namespaces rv = getAllNamespaces();
+ return rv.getNamespace(prefix);
+ }
+
+ Namespace getNamespaceDeclaration() {
+ if (dom.getPrefix() == null) return getNamespaceDeclaration("");
+ return getNamespaceDeclaration(dom.getPrefix());
+ }
+
+ private static class Namespaces {
+ private HashMap map = new HashMap();
+ private HashMap 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, (String)uriToPrefix.get(uri));
+ }
+
+ Namespace getNamespace(String prefix) {
+ if (map.get(prefix) == null) return null;
+ return Namespace.create(prefix, (String)map.get(prefix));
+ }
+
+ Namespace[] getNamespaces() {
+ Iterator i = map.keySet().iterator();
+ ArrayList rv = new ArrayList();
+ while(i.hasNext()) {
+ String prefix = (String)i.next();
+ String uri = (String)map.get(prefix);
+ Namespace n = Namespace.create(prefix, uri);
+ if (!n.isEmpty()) {
+ rv.add( n );
+ }
+ }
+ return (Namespace[])rv.toArray(new Namespace[0]);
+ }
+ }
+
+ 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.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<attrs.getLength(); i++) {
+ if (attrs.item(i).getPrefix().equals(prefix)) {
+ createImpl( attrs.item(i) ).renameNode( QName.create(attrs.item(i).getNamespaceURI(), attrs.item(i).getLocalName(), null) );
+ }
+ }
+ }
+
+ private void declareNamespace(Element e, String prefix, String uri) {
+ if (prefix.length() > 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<attrs.getLength(); i++) {
+ XmlNode attr = XmlNode.createImpl(attrs.item(i));
+ if (namespace.is(attr.getNodeNamespace())) return;
+ }
+
+ // TODO I must confess I am not sure I understand the spec fully. See ECMA357 13.4.4.31
+ String existingPrefix = getExistingPrefixFor(namespace);
+ if (existingPrefix != null) {
+ if (namespace.isUnspecifiedPrefix()) {
+ // we should remove any namespace with this URI from scope; we do this by declaring a namespace with the same
+ // prefix as the existing prefix and setting its URI to the default namespace
+ declareNamespace(existingPrefix, getDefaultNamespace().getUri());
+ } else {
+ if (existingPrefix.equals(namespace.getPrefix())) {
+ declareNamespace(existingPrefix, getDefaultNamespace().getUri());
+ }
+ }
+ } else {
+ // the argument namespace is not declared in this scope, so do nothing.
+ }
+ }
+
+ private void setProcessingInstructionName(String localName) {
+ org.w3c.dom.ProcessingInstruction pi = (ProcessingInstruction)this.dom;
+ // We cannot set the node name; Document.renameNode() only supports elements and attributes. So we replace it
+ pi.getParentNode().replaceChild(
+ pi,
+ pi.getOwnerDocument().createProcessingInstruction(localName, pi.getData())
+ );
+ }
+
+ final void setLocalName(String localName) {
+ if (dom instanceof ProcessingInstruction) {
+ setProcessingInstructionName(localName);
+ } else {
+ String prefix = dom.getPrefix();
+ if (prefix == null) prefix = "";
+ this.dom = dom.getOwnerDocument().renameNode(dom, dom.getNamespaceURI(), QName.qualify(prefix, localName));
+ }
+ }
+
+ final QName getQname() {
+ String uri = (dom.getNamespaceURI()) == null ? "" : dom.getNamespaceURI();
+ String prefix = (dom.getPrefix() == null) ? "" : dom.getPrefix();
+ return QName.create( uri, dom.getLocalName(), prefix );
+ }
+
+ void addMatchingChildren(XMLList result, XmlNode.Filter filter) {
+ Node node = this.dom;
+ NodeList children = node.getChildNodes();
+ for(int i=0; i<children.getLength(); i++) {
+ Node childnode = children.item(i);
+ XmlNode child = XmlNode.createImpl(childnode);
+ if (filter.accept(childnode)) {
+ result.addToList(child);
+ }
+ }
+ }
+
+ XmlNode[] getMatchingChildren(Filter filter) {
+ ArrayList rv = new ArrayList();
+ NodeList nodes = this.dom.getChildNodes();
+ for (int i=0; i<nodes.getLength(); i++) {
+ Node node = nodes.item(i);
+ if (filter.accept(node)) {
+ rv.add( createImpl(node) );
+ }
+ }
+ return (XmlNode[])rv.toArray(new XmlNode[0]);
+ }
+
+ XmlNode[] getAttributes() {
+ NamedNodeMap attrs = this.dom.getAttributes();
+ // TODO Or could make callers handle null?
+ if (attrs == null) throw new IllegalStateException("Must be element.");
+ XmlNode[] rv = new XmlNode[attrs.getLength()];
+ for (int i=0; i<attrs.getLength(); i++) {
+ rv[i] = createImpl( attrs.item(i) );
+ }
+ return rv;
+ }
+
+ String getAttributeValue() {
+ return ((Attr)dom).getValue();
+ }
+
+ void setAttribute(QName name, String value) {
+ if (!(dom instanceof Element)) throw new IllegalStateException("Can only set attribute on elements.");
+ name.setAttribute( (Element)dom, value );
+ }
+
+ void replaceWith(XmlNode other) {
+ Node replacement = other.dom;
+ if (replacement.getOwnerDocument() != this.dom.getOwnerDocument()) {
+ replacement = this.dom.getOwnerDocument().importNode(replacement, true);
+ }
+ this.dom.getParentNode().replaceChild(replacement, this.dom);
+ }
+
+ String ecmaToXMLString(XmlProcessor processor) {
+ if (this.isElementType()) {
+ Element copy = (Element)this.dom.cloneNode(true);
+ Namespace[] inScope = this.getInScopeNamespaces();
+ for (int i=0; i<inScope.length; i++) {
+ declareNamespace(copy, inScope[i].getPrefix(), inScope[i].getUri());
+ }
+ return processor.ecmaToXmlString(copy);
+ } else {
+ return processor.ecmaToXmlString(dom);
+ }
+ }
+
+ static class Namespace {
+ static Namespace create(String prefix, String uri) {
+ if (prefix == null) throw new IllegalArgumentException("Empty string represents default namespace prefix");
+ if (uri == null) throw new IllegalArgumentException("Namespace may not lack a URI");
+ Namespace rv = new Namespace();
+ rv.prefix = prefix;
+ rv.uri = uri;
+ return rv;
+ }
+
+ static Namespace create(String uri) {
+ Namespace rv = new Namespace();
+ rv.uri = uri;
+ return rv;
+ }
+
+ static final Namespace GLOBAL = create("", "");
+
+ private String prefix;
+ private String uri;
+
+ private Namespace() {
+ }
+
+ public String toString() {
+ if (prefix == null) return "XmlNode.Namespace [" + uri + "]";
+ return "XmlNode.Namespace [" + prefix + "{" + uri + "}]";
+ }
+
+ boolean isUnspecifiedPrefix() {
+ return prefix == null;
+ }
+
+ boolean is(Namespace other) {
+ return this.prefix != null && other.prefix != null && this.prefix.equals(other.prefix) && this.uri.equals(other.uri);
+ }
+
+ boolean isEmpty() {
+ return prefix != null && prefix.equals("") && uri.equals("");
+ }
+
+ boolean isDefault() {
+ return prefix != null && prefix.equals("");
+ }
+
+ boolean isGlobal() {
+ return uri != null && uri.equals("");
+ }
+
+ // Called by QName
+ // TODO Move functionality from QName lookupPrefix to here
+ private void setPrefix(String prefix) {
+ if (prefix == null) throw new IllegalArgumentException();
+ this.prefix = prefix;
+ }
+
+ String getPrefix() {
+ return prefix;
+ }
+
+ String getUri() {
+ return uri;
+ }
+ }
+
+ // TODO Where is this class used? No longer using it in QName implementation
+ static class QName {
+ static QName create(Namespace namespace, String localName) {
+ // A null namespace indicates a wild-card match for any namespace
+ // A null localName indicates "*" from the point of view of ECMA357
+ if (localName != null && localName.equals("*")) throw new RuntimeException("* is not valid localName");
+ QName rv = new QName();
+ rv.namespace = namespace;
+ rv.localName = localName;
+ return rv;
+ }
+
+ /** @deprecated */
+ static QName create(String uri, String localName, String prefix) {
+ return create(Namespace.create(prefix, uri), localName);
+ }
+
+ static String qualify(String prefix, String localName) {
+ if (prefix == null) throw new IllegalArgumentException("prefix must not be null");
+ if (prefix.length() > 0) return prefix + ":" + localName;
+ return localName;
+ }
+
+ private Namespace namespace;
+ private String localName;
+
+ private QName() {
+ }
+
+ 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 isEqualTo(QName other) {
+ if (!namespacesEqual(this.namespace, other.namespace)) return false;
+ if (!equals(this.localName, other.localName)) return false;
+ return true;
+ }
+
+ 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);
+ }
+
+ /** @deprecated Use getNamespace() */
+ String getUri() {
+ return namespace.getUri();
+ }
+
+ /** @deprecated Use getNamespace() */
+ String getPrefix() {
+ return namespace.getPrefix();
+ }
+
+ Namespace getNamespace() {
+ return namespace;
+ }
+
+ String getLocalName() {
+ return localName;
+ }
+ }
+
+ static class List {
+ private java.util.Vector v;
+
+ List() {
+ v = new java.util.Vector();
+ }
+
+ private void _add(XmlNode n) {
+ v.add(n);
+ }
+
+ XmlNode item(int index) {
+ return (XmlNode)(v.get(index));
+ }
+
+ void remove(int index) {
+ v.remove(index);
+ }
+
+ void add(List other) {
+ for (int i=0; i<other.length(); i++) {
+ _add(other.item(i));
+ }
+ }
+
+ void add(List from, int startInclusive, int endExclusive) {
+ for (int i=startInclusive; i<endExclusive; i++) {
+ _add(from.item(i));
+ }
+ }
+
+ void add(XmlNode node) {
+ _add(node);
+ }
+
+ /** @deprecated */
+ void add(XML xml) {
+ _add(xml.getAnnotation());
+ }
+
+ /** @deprecated */
+ void addToList(Object toAdd) {
+ if (toAdd instanceof Undefined) {
+ // Missing argument do nothing...
+ return;
+ }
+
+ if (toAdd instanceof XMLList) {
+ XMLList xmlSrc = (XMLList)toAdd;
+ for (int i = 0; i < xmlSrc.length(); i++) {
+ this._add((xmlSrc.item(i)).getAnnotation());
+ }
+ } else if (toAdd instanceof XML) {
+ this._add(((XML)(toAdd)).getAnnotation());
+ } else if (toAdd instanceof XmlNode) {
+ this._add((XmlNode)toAdd);
+ }
+ }
+
+ int length() {
+ return v.size();
+ }
+ }
+
+ static abstract class Filter {
+ static final Filter COMMENT = new Filter() {
+ boolean accept(Node node) {
+ return node.getNodeType() == Node.COMMENT_NODE;
+ }
+ };
+ static final Filter TEXT = new Filter() {
+ boolean accept(Node node) {
+ return node.getNodeType() == Node.TEXT_NODE;
+ }
+ };
+ static Filter PROCESSING_INSTRUCTION(final XMLName name) {
+ return new Filter() {
+ boolean accept(Node node) {
+ if (node.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE) {
+ ProcessingInstruction pi = (ProcessingInstruction)node;
+ return name.matchesLocalName(pi.getTarget());
+ }
+ return false;
+ }
+ };
+ }
+ static Filter ELEMENT = new Filter() {
+ boolean accept(Node node) {
+ return node.getNodeType() == Node.ELEMENT_NODE;
+ }
+ };
+ static Filter TRUE = new Filter() {
+ boolean accept(Node node) {
+ return true;
+ }
+ };
+ abstract boolean accept(Node node);
+ }
+
+ // Support experimental Java interface
+ org.w3c.dom.Node toDomNode() {
+ return this.dom;
+ }
+}