📄 xmlnode.java
字号:
/* ***** 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.javascriptimport 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); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -