/* ***** 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 * * 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 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 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