| /* |
| |
| Licensed to the Apache Software Foundation (ASF) under one or more |
| contributor license agreements. See the NOTICE file distributed with |
| this work for additional information regarding copyright ownership. |
| The ASF licenses this file to You under the Apache License, Version 2.0 |
| (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.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| |
| */ |
| package org.apache.batik.dom; |
| |
| import java.io.Serializable; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.Map; |
| |
| import org.apache.batik.dom.events.DOMMutationEvent; |
| import org.apache.batik.dom.events.EventSupport; |
| import org.apache.batik.dom.events.NodeEventTarget; |
| import org.apache.batik.dom.util.DOMUtilities; |
| import org.apache.batik.dom.util.XMLSupport; |
| import org.apache.batik.dom.xbl.NodeXBL; |
| import org.apache.batik.dom.xbl.XBLManagerData; |
| import org.apache.batik.util.ParsedURL; |
| import org.apache.batik.constants.XMLConstants; |
| |
| import org.w3c.dom.Attr; |
| import org.w3c.dom.DOMException; |
| import org.w3c.dom.Document; |
| import org.w3c.dom.DocumentType; |
| import org.w3c.dom.Element; |
| import org.w3c.dom.NamedNodeMap; |
| import org.w3c.dom.Node; |
| import org.w3c.dom.NodeList; |
| import org.w3c.dom.UserDataHandler; |
| import org.w3c.dom.events.Event; |
| import org.w3c.dom.events.EventException; |
| import org.w3c.dom.events.EventListener; |
| import org.w3c.dom.events.MutationEvent; |
| |
| /** |
| * This class implements the {@link org.w3c.dom.Node} interface. |
| * |
| * @author <a href="mailto:stephane@hillion.org">Stephane Hillion</a> |
| * @version $Id$ |
| */ |
| public abstract class AbstractNode |
| implements ExtendedNode, |
| NodeXBL, |
| XBLManagerData, |
| Serializable { |
| |
| /** |
| * An empty instance of NodeList. |
| */ |
| public static final NodeList EMPTY_NODE_LIST = new NodeList() { |
| public Node item(int i) { return null; } |
| public int getLength() { return 0; } |
| }; |
| |
| /** |
| * The owner document. |
| */ |
| protected AbstractDocument ownerDocument; |
| |
| /** |
| * The event support. |
| */ |
| protected transient EventSupport eventSupport; |
| |
| /** |
| * User data. |
| */ |
| protected HashMap userData; |
| |
| /** |
| * User data handlers. |
| */ |
| protected HashMap userDataHandlers; |
| |
| /** |
| * The XBL manager data. |
| */ |
| protected Object managerData; |
| |
| /** |
| * Sets the name of this node. |
| * Do nothing. |
| */ |
| public void setNodeName(String v) { |
| } |
| |
| /** |
| * Sets the owner document of this node. |
| */ |
| public void setOwnerDocument(Document doc) { |
| ownerDocument = (AbstractDocument)doc; |
| } |
| |
| /** |
| * Sets the value of the specified attribute. This method only applies |
| * to Attr objects. |
| */ |
| public void setSpecified(boolean v) { |
| throw createDOMException(DOMException.INVALID_STATE_ERR, |
| "node.type", |
| new Object[] {(int) getNodeType(), |
| getNodeName()}); |
| } |
| |
| /** |
| * <b>DOM</b>: Implements {@link org.w3c.dom.Node#getNodeValue()}. |
| * @return null. |
| */ |
| public String getNodeValue() throws DOMException { |
| return null; |
| } |
| |
| /** |
| * <b>DOM</b>: Implements {@link org.w3c.dom.Node#setNodeValue(String)}. |
| * Do nothing. |
| */ |
| public void setNodeValue(String nodeValue) throws DOMException { |
| } |
| |
| /** |
| * <b>DOM</b>: Implements {@link org.w3c.dom.Node#getParentNode()}. |
| * @return null. |
| */ |
| public Node getParentNode() { |
| return null; |
| } |
| |
| /** |
| * Sets the parent node. |
| * Throws a HIERARCHY_REQUEST_ERR {@link org.w3c.dom.DOMException}. |
| */ |
| public void setParentNode(Node v) { |
| throw createDOMException(DOMException.HIERARCHY_REQUEST_ERR, |
| "parent.not.allowed", |
| new Object[] {(int) getNodeType(), |
| getNodeName() }); |
| } |
| |
| /** |
| * <b>DOM</b>: Implements {@link org.w3c.dom.Node#getChildNodes()}. |
| * @return {@link #EMPTY_NODE_LIST}. |
| */ |
| public NodeList getChildNodes() { |
| return EMPTY_NODE_LIST; |
| } |
| |
| /** |
| * <b>DOM</b>: Implements {@link org.w3c.dom.Node#getFirstChild()}. |
| * @return null. |
| */ |
| public Node getFirstChild() { |
| return null; |
| } |
| |
| /** |
| * <b>DOM</b>: Implements {@link org.w3c.dom.Node#getLastChild()}. |
| * @return null. |
| */ |
| public Node getLastChild() { |
| return null; |
| } |
| |
| /** |
| * Sets the node immediately preceding this node. |
| * Throws a HIERARCHY_REQUEST_ERR {@link org.w3c.dom.DOMException}. |
| */ |
| public void setPreviousSibling(Node n) { |
| throw createDOMException(DOMException.HIERARCHY_REQUEST_ERR, |
| "sibling.not.allowed", |
| new Object[] {(int) getNodeType(), |
| getNodeName() }); |
| } |
| |
| /** |
| * <b>DOM</b>: Implements {@link org.w3c.dom.Node#getPreviousSibling()}. |
| * @return null. |
| */ |
| public Node getPreviousSibling() { |
| return null; |
| } |
| |
| /** |
| * Sets the node immediately following this node. |
| * Throws a HIERARCHY_REQUEST_ERR {@link org.w3c.dom.DOMException}. |
| */ |
| public void setNextSibling(Node n) { |
| throw createDOMException(DOMException.HIERARCHY_REQUEST_ERR, |
| "sibling.not.allowed", |
| new Object[] {(int) getNodeType(), |
| getNodeName() }); |
| } |
| |
| /** |
| * <b>DOM</b>: Implements {@link org.w3c.dom.Node#getNextSibling()}. |
| * @return null. |
| */ |
| public Node getNextSibling() { |
| return null; |
| } |
| |
| /** |
| * <b>DOM</b>: Implements {@link org.w3c.dom.Node#hasAttributes()}. |
| * @return false. |
| */ |
| public boolean hasAttributes() { |
| return false; |
| } |
| |
| /** |
| * <b>DOM</b>: Implements {@link org.w3c.dom.Node#getAttributes()}. |
| * @return null. |
| */ |
| public NamedNodeMap getAttributes() { |
| return null; |
| } |
| |
| /** |
| * <b>DOM</b>: Implements {@link org.w3c.dom.Node#getOwnerDocument()}. |
| * @return {@link #ownerDocument}. |
| */ |
| public Document getOwnerDocument() { |
| return ownerDocument; |
| } |
| |
| /** |
| * <b>DOM</b>: Implements {@link org.w3c.dom.Node#getNamespaceURI()}. |
| * @return null. |
| */ |
| public String getNamespaceURI() { |
| return null; |
| } |
| |
| /** |
| * <b>DOM</b>: Implements {@link |
| * org.w3c.dom.Node#insertBefore(Node, Node)}. |
| * Throws a HIERARCHY_REQUEST_ERR {@link org.w3c.dom.DOMException}. |
| */ |
| public Node insertBefore(Node newChild, Node refChild) |
| throws DOMException { |
| throw createDOMException(DOMException.HIERARCHY_REQUEST_ERR, |
| "children.not.allowed", |
| new Object[] {(int) getNodeType(), |
| getNodeName() }); |
| } |
| |
| /** |
| * <b>DOM</b>: Implements {@link |
| * org.w3c.dom.Node#replaceChild(Node, Node)}. |
| * Throws a HIERARCHY_REQUEST_ERR {@link org.w3c.dom.DOMException}. |
| */ |
| public Node replaceChild(Node newChild, Node oldChild) |
| throws DOMException { |
| throw createDOMException(DOMException.HIERARCHY_REQUEST_ERR, |
| "children.not.allowed", |
| new Object[] {(int) getNodeType(), |
| getNodeName()}); |
| } |
| |
| /** |
| * <b>DOM</b>: Implements {@link org.w3c.dom.Node#removeChild(Node)}. |
| * Throws a HIERARCHY_REQUEST_ERR {@link org.w3c.dom.DOMException}. |
| */ |
| public Node removeChild(Node oldChild) throws DOMException { |
| throw createDOMException(DOMException.HIERARCHY_REQUEST_ERR, |
| "children.not.allowed", |
| new Object[] {(int) getNodeType(), |
| getNodeName() }); |
| } |
| |
| /** |
| * <b>DOM</b>: Implements {@link org.w3c.dom.Node#appendChild(Node)}. |
| * Throws a HIERARCHY_REQUEST_ERR {@link org.w3c.dom.DOMException}. |
| */ |
| public Node appendChild(Node newChild) throws DOMException { |
| throw createDOMException(DOMException.HIERARCHY_REQUEST_ERR, |
| "children.not.allowed", |
| new Object[] {(int) getNodeType(), |
| getNodeName() }); |
| } |
| |
| /** |
| * <b>DOM</b>: Implements {@link org.w3c.dom.Node#hasChildNodes()}. |
| * @return false. |
| */ |
| public boolean hasChildNodes() { |
| return false; |
| } |
| |
| /** |
| * <b>DOM</b>: Implements {@link org.w3c.dom.Node#cloneNode(boolean)}. |
| */ |
| public Node cloneNode(boolean deep) { |
| Node n = deep ? deepCopyInto(newNode()) : copyInto(newNode()); |
| fireUserDataHandlers(UserDataHandler.NODE_CLONED, this, n); |
| return n; |
| } |
| |
| /** |
| * <b>DOM</b>: Implements {@link org.w3c.dom.Node#normalize()}. |
| * Do nothing. |
| */ |
| public void normalize() { |
| } |
| |
| /** |
| * <b>DOM</b>: Implements {@link |
| * org.w3c.dom.Node#isSupported(String,String)}. |
| */ |
| public boolean isSupported(String feature, String version) { |
| return getCurrentDocument().getImplementation().hasFeature(feature, |
| version); |
| } |
| |
| /** |
| * <b>DOM</b>: Implements {@link org.w3c.dom.Node#getPrefix()}. |
| */ |
| public String getPrefix() { |
| return (getNamespaceURI() == null) |
| ? null |
| : DOMUtilities.getPrefix(getNodeName()); |
| } |
| |
| /** |
| * <b>DOM</b>: Implements {@link org.w3c.dom.Node#setPrefix(String)}. |
| */ |
| public void setPrefix(String prefix) throws DOMException { |
| if (isReadonly()) { |
| throw createDOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, |
| "readonly.node", |
| new Object[] {(int) getNodeType(), |
| getNodeName() }); |
| } |
| String uri = getNamespaceURI(); |
| if (uri == null) { |
| throw createDOMException(DOMException.NAMESPACE_ERR, |
| "namespace", |
| new Object[] {(int) getNodeType(), |
| getNodeName() }); |
| } |
| |
| String name = getLocalName(); |
| if (prefix == null) { |
| // prefix null is explicitly allowed by org.w3c.dom.Node#setPrefix(String) |
| setNodeName(name); |
| return; |
| } |
| |
| // prefix is guaranteed to be non-null here... |
| if (!prefix.equals("") && !DOMUtilities.isValidName(prefix)) { |
| throw createDOMException(DOMException.INVALID_CHARACTER_ERR, |
| "prefix", |
| new Object[] {(int) getNodeType(), |
| getNodeName(), |
| prefix }); |
| } |
| if (!DOMUtilities.isValidPrefix(prefix)) { |
| throw createDOMException(DOMException.NAMESPACE_ERR, |
| "prefix", |
| new Object[] {(int) getNodeType(), |
| getNodeName(), |
| prefix }); |
| } |
| if ((prefix.equals("xml") && |
| !XMLSupport.XML_NAMESPACE_URI.equals(uri)) || |
| (prefix.equals("xmlns") && |
| !XMLSupport.XMLNS_NAMESPACE_URI.equals(uri))) { |
| throw createDOMException(DOMException.NAMESPACE_ERR, |
| "namespace.uri", |
| new Object[] {(int) getNodeType(), |
| getNodeName(), |
| uri }); |
| } |
| setNodeName(prefix + ':' + name); |
| } |
| |
| /** |
| * <b>DOM</b>: Implements {@link org.w3c.dom.Node#getLocalName()}. |
| */ |
| public String getLocalName() { |
| return (getNamespaceURI() == null) |
| ? null |
| : DOMUtilities.getLocalName(getNodeName()); |
| } |
| |
| /** |
| * Creates an exception with the appropriate error message. |
| */ |
| public DOMException createDOMException(short type, |
| String key, |
| Object[] args) { |
| try { |
| return new DOMException |
| (type, getCurrentDocument().formatMessage(key, args)); |
| } catch (Exception e) { |
| return new DOMException(type, key); |
| } |
| } |
| |
| /** |
| * Returns the xml:base attribute value of the given element, |
| * resolving any dependency on parent bases if needed. |
| */ |
| protected String getCascadedXMLBase(Node node) { |
| String base = null; |
| Node n = node.getParentNode(); |
| while (n != null) { |
| if (n.getNodeType() == Node.ELEMENT_NODE) { |
| base = getCascadedXMLBase(n); |
| break; |
| } |
| n = n.getParentNode(); |
| } |
| if (base == null) { |
| AbstractDocument doc; |
| if (node.getNodeType() == Node.DOCUMENT_NODE) { |
| doc = (AbstractDocument) node; |
| } else { |
| doc = (AbstractDocument) node.getOwnerDocument(); |
| } |
| base = doc.getDocumentURI(); |
| } |
| while (node != null && node.getNodeType() != Node.ELEMENT_NODE) { |
| node = node.getParentNode(); |
| } |
| if (node == null) { |
| return base; |
| } |
| Element e = (Element) node; |
| Attr attr = e.getAttributeNodeNS(XMLConstants.XML_NAMESPACE_URI, |
| XMLConstants.XML_BASE_ATTRIBUTE); |
| if (attr != null) { |
| if (base == null) { |
| base = attr.getNodeValue(); |
| } else { |
| base = new ParsedURL(base, attr.getNodeValue()).toString(); |
| } |
| } |
| return base; |
| } |
| |
| /** |
| * <b>DOM</b>: Implements {@link org.w3c.dom.Node#getBaseURI()}. |
| */ |
| public String getBaseURI() { |
| return getCascadedXMLBase(this); |
| } |
| |
| public static String getBaseURI(Node n) { |
| return n.getBaseURI(); |
| } |
| |
| // DocumentPosition constants from DOM Level 3 Core org.w3c.dom.Node |
| // interface. |
| |
| public static final short DOCUMENT_POSITION_DISCONNECTED = 0x01; |
| public static final short DOCUMENT_POSITION_PRECEDING = 0x02; |
| public static final short DOCUMENT_POSITION_FOLLOWING = 0x04; |
| public static final short DOCUMENT_POSITION_CONTAINS = 0x08; |
| public static final short DOCUMENT_POSITION_CONTAINED_BY = 0x10; |
| public static final short DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC = 0x20; |
| |
| /** |
| * <b>DOM</b>: Implements |
| * {@link org.w3c.dom.Node#compareDocumentPosition(Node)}. |
| * XXX Doesn't handle notation or entity nodes. |
| */ |
| public short compareDocumentPosition(Node other) throws DOMException { |
| if (this == other) { |
| return 0; |
| } |
| ArrayList a1 = new ArrayList(10); |
| ArrayList a2 = new ArrayList(10); |
| int c1 = 0; |
| int c2 = 0; |
| Node n; |
| if (getNodeType() == ATTRIBUTE_NODE) { |
| a1.add(this); |
| c1++; |
| n = ((Attr) this).getOwnerElement(); |
| if (other.getNodeType() == ATTRIBUTE_NODE) { |
| Attr otherAttr = (Attr) other; |
| if (n == otherAttr.getOwnerElement()) { |
| if (hashCode() < other.hashCode()) { |
| return DOCUMENT_POSITION_PRECEDING |
| | DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC; |
| } else { |
| return DOCUMENT_POSITION_FOLLOWING |
| | DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC; |
| } |
| } |
| } |
| } else { |
| n = this; |
| } |
| while (n != null) { |
| if (n == other) { |
| return DOCUMENT_POSITION_CONTAINED_BY |
| | DOCUMENT_POSITION_FOLLOWING; |
| } |
| a1.add(n); |
| c1++; |
| n = n.getParentNode(); |
| } |
| if (other.getNodeType() == ATTRIBUTE_NODE) { |
| a2.add(other); |
| c2++; |
| n = ((Attr) other).getOwnerElement(); |
| } else { |
| n = other; |
| } |
| while (n != null) { |
| if (n == this) { |
| return DOCUMENT_POSITION_CONTAINS |
| | DOCUMENT_POSITION_PRECEDING; |
| } |
| a2.add(n); |
| c2++; |
| n = n.getParentNode(); |
| } |
| int i1 = c1 - 1; |
| int i2 = c2 - 1; |
| if (a1.get(i1) != a2.get(i2)) { |
| if (hashCode() < other.hashCode()) { |
| return DOCUMENT_POSITION_DISCONNECTED |
| | DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC |
| | DOCUMENT_POSITION_PRECEDING; |
| } else { |
| return DOCUMENT_POSITION_DISCONNECTED |
| | DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC |
| | DOCUMENT_POSITION_FOLLOWING; |
| } |
| } |
| Object n1 = a1.get(i1); |
| Object n2 = a2.get(i2); |
| while (n1 == n2) { |
| n = (Node) n1; |
| n1 = a1.get(--i1); |
| n2 = a2.get(--i2); |
| } |
| for (n = n.getFirstChild(); n != null; n = n.getNextSibling()) { |
| if (n == n1) { |
| return DOCUMENT_POSITION_PRECEDING; |
| } else if (n == n2) { |
| return DOCUMENT_POSITION_FOLLOWING; |
| } |
| } |
| return DOCUMENT_POSITION_DISCONNECTED; |
| } |
| |
| /** |
| * <b>DOM</b>: Implements {@link org.w3c.dom.Node#getTextContent()}. |
| */ |
| public String getTextContent() { |
| return null; |
| } |
| |
| /** |
| * <b>DOM</b>: Implements {@link org.w3c.dom.Node#setTextContent(String)}. |
| */ |
| public void setTextContent(String s) throws DOMException { |
| if (isReadonly()) { |
| throw createDOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, |
| "readonly.node", |
| new Object[] {(int) getNodeType(), |
| getNodeName() }); |
| } |
| if (getNodeType() != DOCUMENT_TYPE_NODE) { |
| while (getFirstChild() != null) { |
| removeChild(getFirstChild()); |
| } |
| appendChild(getOwnerDocument().createTextNode(s)); |
| } |
| } |
| |
| /** |
| * <b>DOM</b>: Implements {@link org.w3c.dom.Node#isSameNode(Node)}. |
| */ |
| public boolean isSameNode(Node other) { |
| return this == other; |
| } |
| |
| /** |
| * <b>DOM</b>: Implements {@link org.w3c.dom.Node#lookupPrefix(String)}. |
| */ |
| public String lookupPrefix(String namespaceURI) { |
| if (namespaceURI == null || namespaceURI.length() == 0) { |
| return null; |
| } |
| int type = getNodeType(); |
| switch (type) { |
| case Node.ELEMENT_NODE: |
| return lookupNamespacePrefix(namespaceURI, (Element) this); |
| case Node.DOCUMENT_NODE: |
| AbstractNode de |
| = (AbstractNode) ((Document) this).getDocumentElement(); |
| return de.lookupPrefix(namespaceURI); |
| case Node.ENTITY_NODE : |
| case Node.NOTATION_NODE: |
| case Node.DOCUMENT_FRAGMENT_NODE: |
| case Node.DOCUMENT_TYPE_NODE: |
| return null; |
| case Node.ATTRIBUTE_NODE: |
| AbstractNode ownerElement |
| = (AbstractNode) ((Attr) this).getOwnerElement(); |
| if (ownerElement != null) { |
| return ownerElement.lookupPrefix(namespaceURI); |
| } |
| return null; |
| default: |
| for (Node n = this.getParentNode(); |
| n != null; |
| n = n.getParentNode()) { |
| if (n.getNodeType() == ELEMENT_NODE) { |
| return n.lookupPrefix(namespaceURI); |
| } |
| } |
| return null; |
| } |
| } |
| |
| /** |
| * Helper function for {@link #lookupPrefix}. |
| */ |
| protected String lookupNamespacePrefix(String namespaceURI, |
| Element originalElement) { |
| String ns = originalElement.getNamespaceURI(); |
| String prefix = originalElement.getPrefix(); |
| if (ns != null |
| && ns.equals(namespaceURI) |
| && prefix != null) { |
| String pns = |
| originalElement.lookupNamespaceURI(prefix); |
| if (pns != null && pns.equals(namespaceURI)) { |
| return prefix; |
| } |
| } |
| NamedNodeMap nnm = originalElement.getAttributes(); |
| if (nnm != null) { |
| for (int i = 0; i < nnm.getLength(); i++) { |
| Node attr = nnm.item(i); |
| if (XMLConstants.XMLNS_PREFIX.equals(attr.getPrefix()) |
| && attr.getNodeValue().equals(namespaceURI)) { |
| String ln = attr.getLocalName(); |
| AbstractNode oe = (AbstractNode) originalElement; |
| String pns = oe.lookupNamespaceURI(ln); |
| if (pns != null && pns.equals(namespaceURI)) { |
| return ln; |
| } |
| } |
| } |
| } |
| for (Node n = getParentNode(); n != null; n = n.getParentNode()) { |
| if (n.getNodeType() == ELEMENT_NODE) { |
| return ((AbstractNode) n).lookupNamespacePrefix |
| (namespaceURI, originalElement); |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * <b>DOM</b>: Implements |
| * {@link org.w3c.dom.Node#isDefaultNamespace(String)}. |
| */ |
| public boolean isDefaultNamespace(String namespaceURI) { |
| switch (getNodeType()) { |
| case DOCUMENT_NODE: |
| AbstractNode de |
| = (AbstractNode) ((Document) this).getDocumentElement(); |
| return de.isDefaultNamespace(namespaceURI); |
| case ENTITY_NODE: |
| case NOTATION_NODE: |
| case DOCUMENT_TYPE_NODE: |
| case DOCUMENT_FRAGMENT_NODE: |
| return false; |
| case ATTRIBUTE_NODE: |
| AbstractNode owner |
| = (AbstractNode) ((Attr) this).getOwnerElement(); |
| if (owner != null) { |
| return owner.isDefaultNamespace(namespaceURI); |
| } |
| return false; |
| case ELEMENT_NODE: |
| if (getPrefix() == null) { |
| String ns = getNamespaceURI(); |
| return ns == null && namespaceURI == null |
| || ns != null && ns.equals(namespaceURI); |
| } |
| NamedNodeMap nnm = getAttributes(); |
| if (nnm != null) { |
| for (int i = 0; i < nnm.getLength(); i++) { |
| Node attr = nnm.item(i); |
| if (XMLConstants.XMLNS_PREFIX |
| .equals(attr.getLocalName())) { |
| return attr.getNodeValue().equals(namespaceURI); |
| } |
| } |
| } |
| // fall through |
| default: |
| for (Node n = this; n != null; n = n.getParentNode()) { |
| if (n.getNodeType() == ELEMENT_NODE) { |
| AbstractNode an = (AbstractNode) n; |
| return an.isDefaultNamespace(namespaceURI); |
| } |
| } |
| return false; |
| } |
| } |
| |
| /** |
| * <b>DOM</b>: Implements |
| * {@link org.w3c.dom.Node#lookupNamespaceURI(String)}. |
| */ |
| public String lookupNamespaceURI(String prefix) { |
| switch (getNodeType()) { |
| case DOCUMENT_NODE: |
| AbstractNode de = |
| (AbstractNode) ((Document) this).getDocumentElement(); |
| return de.lookupNamespaceURI(prefix); |
| case ENTITY_NODE: |
| case NOTATION_NODE: |
| case DOCUMENT_TYPE_NODE: |
| case DOCUMENT_FRAGMENT_NODE: |
| return null; |
| case ATTRIBUTE_NODE: |
| AbstractNode owner |
| = (AbstractNode) ((Attr) this).getOwnerElement(); |
| if (owner != null) { |
| return owner.lookupNamespaceURI(prefix); |
| } |
| return null; |
| case ELEMENT_NODE: |
| /*String ns = getNamespaceURI(); |
| if (ns != null && compareStrings(getPrefix(), prefix)) { |
| return getNamespaceURI(); |
| } */ |
| NamedNodeMap nnm = getAttributes(); |
| if (nnm != null) { |
| for (int i = 0; i < nnm.getLength(); i++) { |
| Node attr = nnm.item(i); |
| String attrPrefix = attr.getPrefix(); |
| String localName = attr.getLocalName(); |
| if (localName == null) { |
| localName = attr.getNodeName(); |
| } |
| if (XMLConstants.XMLNS_PREFIX.equals(attrPrefix) |
| && compareStrings(localName, prefix) |
| || XMLConstants.XMLNS_PREFIX.equals(localName) |
| && prefix == null) { |
| String value = attr.getNodeValue(); |
| if (value.length() > 0) { |
| return value; |
| } |
| return null; |
| } |
| } |
| } |
| // fall through |
| default: |
| for (Node n = this.getParentNode(); n != null; n = n.getParentNode()) { |
| if (n.getNodeType() == ELEMENT_NODE) { |
| AbstractNode an = (AbstractNode) n; |
| return an.lookupNamespaceURI(prefix); |
| } |
| } |
| return null; |
| } |
| } |
| |
| /** |
| * <b>DOM</b>: Implements {@link org.w3c.dom.Node#isEqualNode(Node)}. |
| */ |
| public boolean isEqualNode(Node other) { |
| if (other == null) { |
| return false; |
| } |
| int nt = other.getNodeType(); |
| if (nt != getNodeType() |
| || !compareStrings(getNodeName(), other.getNodeName()) |
| || !compareStrings(getLocalName(), other.getLocalName()) |
| || !compareStrings(getPrefix(), other.getPrefix()) |
| || !compareStrings(getNodeValue(), other.getNodeValue()) |
| || !compareStrings(getNodeValue(), other.getNodeValue()) |
| || !compareNamedNodeMaps(getAttributes(), |
| other.getAttributes())) { |
| return false; |
| } |
| if (nt == Node.DOCUMENT_TYPE_NODE) { |
| DocumentType dt1 = (DocumentType) this; |
| DocumentType dt2 = (DocumentType) other; |
| if (!compareStrings(dt1.getPublicId(), dt2.getPublicId()) |
| || !compareStrings(dt1.getSystemId(), dt2.getSystemId()) |
| || !compareStrings(dt1.getInternalSubset(), |
| dt2.getInternalSubset()) |
| || !compareNamedNodeMaps(dt1.getEntities(), |
| dt2.getEntities()) |
| || !compareNamedNodeMaps(dt1.getNotations(), |
| dt2.getNotations())) { |
| return false; |
| } |
| } |
| Node n = getFirstChild(); |
| Node m = other.getFirstChild(); |
| if (n != null && m != null) { |
| if (!n.isEqualNode(m)) { |
| return false; |
| } |
| } |
| return n == m; |
| } |
| |
| /** |
| * Compare two strings for equality. |
| */ |
| protected boolean compareStrings(String s1, String s2) { |
| return s1 != null && s1.equals(s2) || s1 == null && s2 == null; |
| } |
| |
| /** |
| * Compare two NamedNodeMaps for equality. |
| */ |
| protected boolean compareNamedNodeMaps(NamedNodeMap nnm1, |
| NamedNodeMap nnm2) { |
| if (nnm1 == null && nnm2 != null |
| || nnm1 != null && nnm2 == null) { |
| return false; |
| } |
| if (nnm1 != null) { |
| int len = nnm1.getLength(); |
| if (len != nnm2.getLength()) { |
| return false; |
| } |
| for (int i = 0; i < len; i++) { |
| Node n1 = nnm1.item(i); |
| String n1ln = n1.getLocalName(); |
| Node n2; |
| if (n1ln != null) { |
| n2 = nnm2.getNamedItemNS(n1.getNamespaceURI(), n1ln); |
| } else { |
| n2 = nnm2.getNamedItem(n1.getNodeName()); |
| } |
| if (!n1.isEqualNode(n2)) { |
| return false; |
| } |
| } |
| } |
| return true; |
| } |
| |
| /** |
| * <b>DOM</b>: Implements |
| * {@link org.w3c.dom.Node#getFeature(String,String)}. |
| */ |
| public Object getFeature(String feature, String version) { |
| return null; |
| } |
| |
| /** |
| * <b>DOM</b>: Implements {@link org.w3c.dom.Node#getUserData(String)}. |
| */ |
| public Object getUserData(String key) { |
| if (userData == null) { |
| return null; |
| } |
| return userData.get(key); |
| } |
| |
| /** |
| * <b>DOM</b>: Implements |
| * {@link org.w3c.dom.Node#setUserData(String,Object,UserDataHandler)}. |
| */ |
| public Object setUserData(String key, Object data, UserDataHandler handler) { |
| if (userData == null) { |
| userData = new HashMap(); |
| userDataHandlers = new HashMap(); |
| } |
| if (data == null) { |
| userData.remove(key); |
| return userDataHandlers.remove(key); |
| } |
| userDataHandlers.put(key, handler); |
| return userData.put(key, data); |
| } |
| |
| /** |
| * Fire any UserDataHandlers on the given oldNode. |
| */ |
| protected void fireUserDataHandlers(short type, |
| Node oldNode, |
| Node newNode) { |
| AbstractNode an = (AbstractNode) oldNode; |
| if (an.userData != null) { |
| for (Object o : an.userData.entrySet()) { |
| Map.Entry e = (Map.Entry) o; |
| UserDataHandler h |
| = (UserDataHandler) an.userDataHandlers.get(e.getKey()); |
| if (h != null) { |
| h.handle(type, |
| (String) e.getKey(), |
| e.getValue(), |
| oldNode, |
| newNode); |
| } |
| } |
| } |
| } |
| |
| // EventTarget //////////////////////////////////////////////////////////// |
| |
| /** |
| * <b>DOM</b>: Implements |
| * {@link |
| * org.w3c.dom.events.EventTarget#addEventListener(String,EventListener,boolean)}. |
| */ |
| public void addEventListener(String type, |
| EventListener listener, |
| boolean useCapture) { |
| if (eventSupport == null) { |
| initializeEventSupport(); |
| } |
| eventSupport.addEventListener(type, listener, useCapture); |
| } |
| |
| /** |
| * <b>DOM</b>: Implements |
| * {@link |
| * NodeEventTarget#addEventListenerNS(String,String,EventListener,boolean,Object)}. |
| */ |
| public void addEventListenerNS(String namespaceURI, |
| String type, |
| EventListener listener, |
| boolean useCapture, |
| Object evtGroup) { |
| if (eventSupport == null) { |
| initializeEventSupport(); |
| } |
| if (namespaceURI != null && namespaceURI.length() == 0) { |
| namespaceURI = null; |
| } |
| eventSupport.addEventListenerNS(namespaceURI, |
| type, |
| listener, |
| useCapture, |
| evtGroup); |
| } |
| |
| /** |
| * <b>DOM</b>: Implements |
| * {@link |
| * org.w3c.dom.events.EventTarget#removeEventListener(String,EventListener,boolean)}. |
| */ |
| public void removeEventListener(String type, |
| EventListener listener, |
| boolean useCapture) { |
| if (eventSupport != null) { |
| eventSupport.removeEventListener(type, listener, useCapture); |
| } |
| } |
| |
| /** |
| * <b>DOM</b>: Implements |
| * {@link |
| * NodeEventTarget#removeEventListenerNS(String,String,EventListener,boolean)}. |
| */ |
| public void removeEventListenerNS(String namespaceURI, |
| String type, |
| EventListener listener, |
| boolean useCapture) { |
| if (eventSupport != null) { |
| if (namespaceURI != null && namespaceURI.length() == 0) { |
| namespaceURI = null; |
| } |
| eventSupport.removeEventListenerNS(namespaceURI, |
| type, |
| listener, |
| useCapture); |
| } |
| } |
| |
| /** |
| * Implements {@link |
| * org.apache.batik.dom.events.NodeEventTarget#getParentNodeEventTarget()}. |
| */ |
| public NodeEventTarget getParentNodeEventTarget() { |
| return (NodeEventTarget) getXblParentNode(); |
| } |
| |
| /** |
| * <b>DOM</b>: Implements |
| * {@link org.w3c.dom.events.EventTarget#dispatchEvent(Event)}. |
| */ |
| public boolean dispatchEvent(Event evt) throws EventException { |
| if (eventSupport == null) { |
| initializeEventSupport(); |
| } |
| return eventSupport.dispatchEvent(this, evt); |
| } |
| |
| /** |
| * <b>DOM</b>: Implements |
| * <code>EventTarget#willTriggerNS(String,String)</code> from an old draft |
| * of DOM Level 3 Events. |
| */ |
| public boolean willTriggerNS(String namespaceURI, String type) { |
| return true; |
| } |
| |
| /** |
| * <b>DOM</b>: Implements |
| * <code>EventTarget.hasEventListenerNS(String,String)</code> from an old |
| * draft of DOM Level 3 Events. |
| */ |
| public boolean hasEventListenerNS(String namespaceURI, String type) { |
| if (eventSupport == null) { |
| return false; |
| } |
| if (namespaceURI != null && namespaceURI.length() == 0) { |
| namespaceURI = null; |
| } |
| return eventSupport.hasEventListenerNS(namespaceURI, type); |
| } |
| |
| /** |
| * Returns the event support instance for this node, or null if any. |
| */ |
| public EventSupport getEventSupport() { |
| return eventSupport; |
| } |
| |
| /** |
| * Initializes the event support instance for this node if it has not |
| * been already, and returns it. |
| */ |
| public EventSupport initializeEventSupport() { |
| if (eventSupport == null) { |
| AbstractDocument doc = getCurrentDocument(); |
| AbstractDOMImplementation di |
| = (AbstractDOMImplementation) doc.getImplementation(); |
| eventSupport = di.createEventSupport(this); |
| doc.setEventsEnabled(true); |
| } |
| return eventSupport; |
| } |
| |
| /** |
| * Recursively fires a DOMNodeInsertedIntoDocument event. |
| */ |
| public void fireDOMNodeInsertedIntoDocumentEvent() { |
| AbstractDocument doc = getCurrentDocument(); |
| if (doc.getEventsEnabled()) { |
| DOMMutationEvent ev = |
| (DOMMutationEvent)doc.createEvent("MutationEvents"); |
| ev.initMutationEventNS(XMLConstants.XML_EVENTS_NAMESPACE_URI, |
| "DOMNodeInsertedIntoDocument", |
| true, // canBubbleArg |
| false, // cancelableArg |
| null, // relatedNodeArg |
| null, // prevValueArg |
| null, // newValueArg |
| null, // attrNameArg |
| MutationEvent.ADDITION); |
| dispatchEvent(ev); |
| } |
| } |
| |
| /** |
| * Recursively fires a DOMNodeRemovedFromDocument event. |
| */ |
| public void fireDOMNodeRemovedFromDocumentEvent() { |
| AbstractDocument doc = getCurrentDocument(); |
| if (doc.getEventsEnabled()) { |
| DOMMutationEvent ev |
| = (DOMMutationEvent) doc.createEvent("MutationEvents"); |
| ev.initMutationEventNS(XMLConstants.XML_EVENTS_NAMESPACE_URI, |
| "DOMNodeRemovedFromDocument", |
| true, // canBubbleArg |
| false, // cancelableArg |
| null, // relatedNodeArg |
| null, // prevValueArg |
| null, // newValueArg |
| null, // attrNameArg |
| MutationEvent.REMOVAL); |
| dispatchEvent(ev); |
| } |
| } |
| |
| /** |
| * Fires a DOMCharacterDataModified event. |
| */ |
| protected void fireDOMCharacterDataModifiedEvent(String oldv, |
| String newv) { |
| AbstractDocument doc = getCurrentDocument(); |
| if (doc.getEventsEnabled()) { |
| DOMMutationEvent ev |
| = (DOMMutationEvent) doc.createEvent("MutationEvents"); |
| ev.initMutationEventNS(XMLConstants.XML_EVENTS_NAMESPACE_URI, |
| "DOMCharacterDataModified", |
| true, // canBubbleArg |
| false, // cancelableArg |
| null, // relatedNodeArg |
| oldv, // prevValueArg |
| newv, // newValueArg |
| null, // attrNameArg |
| MutationEvent.MODIFICATION); |
| dispatchEvent(ev); |
| } |
| } |
| |
| /** |
| * Returns the current document. |
| */ |
| protected AbstractDocument getCurrentDocument() { |
| return ownerDocument; |
| } |
| |
| /** |
| * Returns a new uninitialized instance of this object's class. |
| */ |
| protected abstract Node newNode(); |
| |
| /** |
| * Exports this node to the given document. |
| */ |
| protected Node export(Node n, AbstractDocument d) { |
| AbstractNode p = (AbstractNode)n; |
| p.ownerDocument = d; |
| p.setReadonly(false); |
| return n; |
| } |
| |
| /** |
| * Deeply exports this node to the given document. |
| */ |
| protected Node deepExport(Node n, AbstractDocument d) { |
| AbstractNode p = (AbstractNode)n; |
| p.ownerDocument = d; |
| p.setReadonly(false); |
| return n; |
| } |
| |
| /** |
| * Copy the fields of the current node into the given node. |
| * @param n a node of the type of this. |
| */ |
| protected Node copyInto(Node n) { |
| AbstractNode an = (AbstractNode)n; |
| an.ownerDocument = ownerDocument; |
| return n; |
| } |
| |
| /** |
| * Deeply copy the fields of the current node into the given node. |
| * @param n a node of the type of this. |
| */ |
| protected Node deepCopyInto(Node n) { |
| AbstractNode an = (AbstractNode)n; |
| an.ownerDocument = ownerDocument; |
| return n; |
| } |
| |
| /** |
| * Checks the validity of a node to be inserted. |
| */ |
| protected void checkChildType(Node n, boolean replace) { |
| throw createDOMException(DOMException.HIERARCHY_REQUEST_ERR, |
| "children.not.allowed", |
| new Object[] {(int) getNodeType(), |
| getNodeName() }); |
| } |
| |
| // NodeXBL ////////////////////////////////////////////////////////////// |
| |
| /** |
| * Get the parent of this node in the fully flattened tree. |
| */ |
| public Node getXblParentNode() { |
| return ownerDocument.getXBLManager().getXblParentNode(this); |
| } |
| |
| /** |
| * Get the list of child nodes of this node in the fully flattened tree. |
| */ |
| public NodeList getXblChildNodes() { |
| return ownerDocument.getXBLManager().getXblChildNodes(this); |
| } |
| |
| /** |
| * Get the list of child nodes of this node in the fully flattened tree |
| * that are within the same shadow scope. |
| */ |
| public NodeList getXblScopedChildNodes() { |
| return ownerDocument.getXBLManager().getXblScopedChildNodes(this); |
| } |
| |
| /** |
| * Get the first child node of this node in the fully flattened tree. |
| */ |
| public Node getXblFirstChild() { |
| return ownerDocument.getXBLManager().getXblFirstChild(this); |
| } |
| |
| /** |
| * Get the last child node of this node in the fully flattened tree. |
| */ |
| public Node getXblLastChild() { |
| return ownerDocument.getXBLManager().getXblLastChild(this); |
| } |
| |
| /** |
| * Get the node which directly precedes the current node in the |
| * xblParentNode's xblChildNodes list. |
| */ |
| public Node getXblPreviousSibling() { |
| return ownerDocument.getXBLManager().getXblPreviousSibling(this); |
| } |
| |
| /** |
| * Get the node which directly follows the current node in the |
| * xblParentNode's xblChildNodes list. |
| */ |
| public Node getXblNextSibling() { |
| return ownerDocument.getXBLManager().getXblNextSibling(this); |
| } |
| |
| /** |
| * Get the first element child of this node in the fully flattened tree. |
| */ |
| public Element getXblFirstElementChild() { |
| return ownerDocument.getXBLManager().getXblFirstElementChild(this); |
| } |
| |
| /** |
| * Get the last element child of this node in the fully flattened tree. |
| */ |
| public Element getXblLastElementChild() { |
| return ownerDocument.getXBLManager().getXblLastElementChild(this); |
| } |
| |
| /** |
| * Get the first element that precedes the current node in the |
| * xblParentNode's xblChildNodes list. |
| */ |
| public Element getXblPreviousElementSibling() { |
| return ownerDocument.getXBLManager().getXblPreviousElementSibling(this); |
| } |
| |
| /** |
| * Get the first element that follows the current node in the |
| * xblParentNode's xblChildNodes list. |
| */ |
| public Element getXblNextElementSibling() { |
| return ownerDocument.getXBLManager().getXblNextElementSibling(this); |
| } |
| |
| /** |
| * Get the bound element whose shadow tree this current node resides in. |
| */ |
| public Element getXblBoundElement() { |
| return ownerDocument.getXBLManager().getXblBoundElement(this); |
| } |
| |
| /** |
| * Get the shadow tree of this node. |
| */ |
| public Element getXblShadowTree() { |
| return ownerDocument.getXBLManager().getXblShadowTree(this); |
| } |
| |
| /** |
| * Get the xbl:definition elements currently binding this element. |
| */ |
| public NodeList getXblDefinitions() { |
| return ownerDocument.getXBLManager().getXblDefinitions(this); |
| } |
| |
| // XBLManagerData //////////////////////////////////////////////////////// |
| |
| /** |
| * Returns the XBL manager associated data for this node. |
| */ |
| public Object getManagerData() { |
| return managerData; |
| } |
| |
| /** |
| * Sets the XBL manager associated data for this node. |
| */ |
| public void setManagerData(Object data) { |
| managerData = data; |
| } |
| } |