/*
* reserved comment block
* DO NOT REMOVE OR ALTER!
*/
/*
* Copyright 1999-2002,2004 The Apache Software Foundation.
*
* Licensed 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 com.sun.org.apache.xerces.internal.dom;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Hashtable;
import org.w3c.dom.UserDataHandler;
import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.DocumentType;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.events.Event;
import org.w3c.dom.events.EventListener;
import org.w3c.dom.events.EventTarget;
/**
* NodeImpl provides the basic structure of a DOM tree. It is never used
* directly, but instead is subclassed to add type and data
* information, and additional methods, appropriate to each node of
* the tree. Only its subclasses should be instantiated -- and those,
* with the exception of Document itself, only through a specific
* Document's factory methods.
* <P>
* The Node interface provides shared behaviors such as siblings and
* children, both for consistancy and so that the most common tree
* operations may be performed without constantly having to downcast
* to specific node types. When there is no obvious mapping for one of
* these queries, it will respond with null.
* Note that the default behavior is that children are forbidden. To
* permit them, the subclass ParentNode overrides several methods.
* <P>
* NodeImpl also implements NodeList, so it can return itself in
* response to the getChildNodes() query. This eliminiates the need
* for a separate ChildNodeList object. Note that this is an
* IMPLEMENTATION DETAIL; applications should _never_ assume that
* this identity exists.
* <P>
* All nodes in a single document must originate
* in that document. (Note that this is much tighter than "must be
* same implementation") Nodes are all aware of their ownerDocument,
* and attempts to mismatch will throw WRONG_DOCUMENT_ERR.
* <P>
* However, to save memory not all nodes always have a direct reference
* to their ownerDocument. When a node is owned by another node it relies
* on its owner to store its ownerDocument. Parent nodes always store it
* though, so there is never more than one level of indirection.
* And when a node doesn't have an owner, ownerNode refers to its
* ownerDocument.
* <p>
* This class doesn't directly support mutation events, however, it still
* implements the EventTarget interface and forward all related calls to the
* document so that the document class do so.
*
* @xerces.internal
*
* @author Arnaud Le Hors, IBM
* @author Joe Kesselman, IBM
* @since PR-DOM-Level-1-19980818.
*/
public abstract class NodeImpl
implements Node, NodeList, EventTarget, Cloneable, Serializable{
//
// Constants
//
// TreePosition Constants.
// Taken from DOM L3 Node interface.
/**
* The node precedes the reference node.
*/
public static final short TREE_POSITION_PRECEDING = 0x01;
/**
* The node follows the reference node.
*/
public static final short TREE_POSITION_FOLLOWING = 0x02;
/**
* The node is an ancestor of the reference node.
*/
public static final short TREE_POSITION_ANCESTOR = 0x04;
/**
* The node is a descendant of the reference node.
*/
public static final short TREE_POSITION_DESCENDANT = 0x08;
/**
* The two nodes have an equivalent position. This is the case of two
* attributes that have the same <code>ownerElement</code>, and two
* nodes that are the same.
*/
public static final short TREE_POSITION_EQUIVALENT = 0x10;
/**
* The two nodes are the same. Two nodes that are the same have an
* equivalent position, though the reverse may not be true.
*/
public static final short TREE_POSITION_SAME_NODE = 0x20;
/**
* The two nodes are disconnected, they do not have any common ancestor.
* This is the case of two nodes that are not in the same document.
*/
public static final short TREE_POSITION_DISCONNECTED = 0x00;
// DocumentPosition
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_IS_CONTAINED = 0x10;
public static final short DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC = 0x20;
/** Serialization version. */
static final long serialVersionUID = -6316591992167219696L;
// public
/** Element definition node type. */
public static final short ELEMENT_DEFINITION_NODE = 21;
//
// Data
//
// links
protected NodeImpl ownerNode; // typically the parent but not always!
// data
protected short flags;
protected final static short READONLY = 0x1<<0;
protected final static short SYNCDATA = 0x1<<1;
protected final static short SYNCCHILDREN = 0x1<<2;
protected final static short OWNED = 0x1<<3;
protected final static short FIRSTCHILD = 0x1<<4;
protected final static short SPECIFIED = 0x1<<5;
protected final static short IGNORABLEWS = 0x1<<6;
protected final static short HASSTRING = 0x1<<7;
protected final static short NORMALIZED = 0x1<<8;
protected final static short ID = 0x1<<9;
//
// Constructors
//
/**
* No public constructor; only subclasses of Node should be
* instantiated, and those normally via a Document's factory methods
* <p>
* Every Node knows what Document it belongs to.
*/
protected NodeImpl(CoreDocumentImpl ownerDocument) {
// as long as we do not have any owner, ownerNode is our ownerDocument
ownerNode = ownerDocument;
} // <init>(CoreDocumentImpl)
/** Constructor for serialization. */
public NodeImpl() {}
//
// Node methods
//
/**
* A short integer indicating what type of node this is. The named
* constants for this value are defined in the org.w3c.dom.Node interface.
*/
public abstract short getNodeType();
/**
* the name of this node.
*/
public abstract String getNodeName();
/**
* Returns the node value.
* @throws DOMException(DOMSTRING_SIZE_ERR)
*/
public String getNodeValue()
throws DOMException {
return null; // overridden in some subclasses
}
/**
* Sets the node value.
* @throws DOMException(NO_MODIFICATION_ALLOWED_ERR)
*/
public void setNodeValue(String x)
throws DOMException {
// Default behavior is to do nothing, overridden in some subclasses
}
/**
* Adds a child node to the end of the list of children for this node.
* Convenience shorthand for insertBefore(newChild,null).
* @see #insertBefore(Node, Node)
* <P>
* By default we do not accept any children, ParentNode overrides this.
* @see ParentNode
*
* @return newChild, in its new state (relocated, or emptied in the case of
* DocumentNode.)
*
* @throws DOMException(HIERARCHY_REQUEST_ERR) if newChild is of a
* type that shouldn't be a child of this node.
*
* @throws DOMException(WRONG_DOCUMENT_ERR) if newChild has a
* different owner document than we do.
*
* @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if this node is
* read-only.
*/
public Node appendChild(Node newChild) throws DOMException {
return insertBefore(newChild, null);
}
/**
* Returns a duplicate of a given node. You can consider this a
* generic "copy constructor" for nodes. The newly returned object should
* be completely independent of the source object's subtree, so changes
* in one after the clone has been made will not affect the other.
* <P>
* Note: since we never have any children deep is meaningless here,
* ParentNode overrides this behavior.
* @see ParentNode
*
* <p>
* Example: Cloning a Text node will copy both the node and the text it
* contains.
* <p>
* Example: Cloning something that has children -- Element or Attr, for
* example -- will _not_ clone those children unless a "deep clone"
* has been requested. A shallow clone of an Attr node will yield an
* empty Attr of the same name.
* <p>
* NOTE: Clones will always be read/write, even if the node being cloned
* is read-only, to permit applications using only the DOM API to obtain
* editable copies of locked portions of the tree.
*/
public Node cloneNode(boolean deep) {
if (needsSyncData()) {
synchronizeData();
}
NodeImpl newnode;
try {
newnode = (NodeImpl)clone();
}
catch (CloneNotSupportedException e) {
// if we get here we have an error in our program we may as well
// be vocal about it, so that people can take appropriate action.
throw new RuntimeException("**Internal Error**" + e);
}
// Need to break the association w/ original kids
newnode.ownerNode = ownerDocument();
newnode.isOwned(false);
// By default we make all clones readwrite,
// this is overriden in readonly subclasses
newnode.isReadOnly(false);
ownerDocument().callUserDataHandlers(this, newnode,
UserDataHandler.NODE_CLONED);
return newnode;
} // cloneNode(boolean):Node
/**
* Find the Document that this Node belongs to (the document in
* whose context the Node was created). The Node may or may not
* currently be part of that Document's actual contents.
*/
public Document getOwnerDocument() {
// if we have an owner simply forward the request
// otherwise ownerNode is our ownerDocument
if (isOwned()) {
return ownerNode.ownerDocument();
} else {
return (Document) ownerNode;
}
}
/**
* same as above but returns internal type and this one is not overridden
* by CoreDocumentImpl to return null
*/
CoreDocumentImpl ownerDocument() {
// if we have an owner simply forward the request
// otherwise ownerNode is our ownerDocument
if (isOwned()) {
return ownerNode.ownerDocument();
} else {
return (CoreDocumentImpl) ownerNode;
}
}
/**
* NON-DOM
* set the ownerDocument of this node
*/
void setOwnerDocument(CoreDocumentImpl doc) {
if (needsSyncData()) {
synchronizeData();
}
// if we have an owner we rely on it to have it right
// otherwise ownerNode is our ownerDocument
if (!isOwned()) {
ownerNode = doc;
}
}
/**
* Returns the node number
*/
protected int getNodeNumber() {
int nodeNumber;
CoreDocumentImpl cd = (CoreDocumentImpl)(this.getOwnerDocument());
nodeNumber = cd.getNodeNumber(this);
return nodeNumber;
}
/**
* Obtain the DOM-tree parent of this node, or null if it is not
* currently active in the DOM tree (perhaps because it has just been
* created or removed). Note that Document, DocumentFragment, and
* Attribute will never have parents.
*/
public Node getParentNode() {
return null; // overriden by ChildNode
}
/*
* same as above but returns internal type
*/
NodeImpl parentNode() {
return null;
}
/** The next child of this node's parent, or null if none */
public Node getNextSibling() {
return null; // default behavior, overriden in ChildNode
}
/** The previous child of this node's parent, or null if none */
public Node getPreviousSibling() {
return null; // default behavior, overriden in ChildNode
}
ChildNode previousSibling() {
return null; // default behavior, overriden in ChildNode
}
/**
* Return the collection of attributes associated with this node,
* or null if none. At this writing, Element is the only type of node
* which will ever have attributes.
*
* @see ElementImpl
*/
public NamedNodeMap getAttributes() {
return null; // overridden in ElementImpl
}
/**
* Returns whether this node (if it is an element) has any attributes.
* @return <code>true</code> if this node has any attributes,
* <code>false</code> otherwise.
* @since DOM Level 2
* @see ElementImpl
*/
public boolean hasAttributes() {
return false; // overridden in ElementImpl
}
/**
* Test whether this node has any children. Convenience shorthand
* for (Node.getFirstChild()!=null)
* <P>
* By default we do not have any children, ParentNode overrides this.
* @see ParentNode
*/
public boolean hasChildNodes() {
return false;
}
/**
* Obtain a NodeList enumerating all children of this node. If there
* are none, an (initially) empty NodeList is returned.
* <p>
* NodeLists are "live"; as children are added/removed the NodeList
* will immediately reflect those changes. Also, the NodeList refers
* to the actual nodes, so changes to those nodes made via the DOM tree
* will be reflected in the NodeList and vice versa.
* <p>
* In this implementation, Nodes implement the NodeList interface and
* provide their own getChildNodes() support. Other DOMs may solve this
* differently.
*/
public NodeList getChildNodes() {
return this;
}
/** The first child of this Node, or null if none.
* <P>
* By default we do not have any children, ParentNode overrides this.
* @see ParentNode
*/
public Node getFirstChild() {
return null;
}
/** The first child of this Node, or null if none.
* <P>
* By default we do not have any children, ParentNode overrides this.
* @see ParentNode
*/
public Node getLastChild() {
return null;
}
/**
* Move one or more node(s) to our list of children. Note that this
* implicitly removes them from their previous parent.
* <P>
* By default we do not accept any children, ParentNode overrides this.
* @see ParentNode
*
* @param newChild The Node to be moved to our subtree. As a
* convenience feature, inserting a DocumentNode will instead insert
* all its children.
*
* @param refChild Current child which newChild should be placed
* immediately before. If refChild is null, the insertion occurs
* after all existing Nodes, like appendChild().
*
* @return newChild, in its new state (relocated, or emptied in the case of
* DocumentNode.)
*
* @throws DOMException(HIERARCHY_REQUEST_ERR) if newChild is of a
* type that shouldn't be a child of this node, or if newChild is an
* ancestor of this node.
*
* @throws DOMException(WRONG_DOCUMENT_ERR) if newChild has a
* different owner document than we do.
*
* @throws DOMException(NOT_FOUND_ERR) if refChild is not a child of
* this node.
*
* @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if this node is
* read-only.
*/
public Node insertBefore(Node newChild, Node refChild)
throws DOMException {
throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR,
DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN,
"HIERARCHY_REQUEST_ERR", null));
}
/**
* Remove a child from this Node. The removed child's subtree
* remains intact so it may be re-inserted elsewhere.
* <P>
* By default we do not have any children, ParentNode overrides this.
* @see ParentNode
*
* @return oldChild, in its new state (removed).
*
* @throws DOMException(NOT_FOUND_ERR) if oldChild is not a child of
* this node.
*
* @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if this node is
* read-only.
*/
public Node removeChild(Node oldChild)
throws DOMException {
throw new DOMException(DOMException.NOT_FOUND_ERR,
DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN,
"NOT_FOUND_ERR", null));
}
/**
* Make newChild occupy the location that oldChild used to
* have. Note that newChild will first be removed from its previous
* parent, if any. Equivalent to inserting newChild before oldChild,
* then removing oldChild.
* <P>
* By default we do not have any children, ParentNode overrides this.
* @see ParentNode
*
* @return oldChild, in its new state (removed).
*
* @throws DOMException(HIERARCHY_REQUEST_ERR) if newChild is of a
* type that shouldn't be a child of this node, or if newChild is
* one of our ancestors.
*
* @throws DOMException(WRONG_DOCUMENT_ERR) if newChild has a
* different owner document than we do.
*
* @throws DOMException(NOT_FOUND_ERR) if oldChild is not a child of
* this node.
*
* @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if this node is
* read-only.
*/
public Node replaceChild(Node newChild, Node oldChild)
throws DOMException {
throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR,
DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN,
"HIERARCHY_REQUEST_ERR", null));
}
//
// NodeList methods
//
/**
* NodeList method: Count the immediate children of this node
* <P>
* By default we do not have any children, ParentNode overrides this.
* @see ParentNode
*
* @return int
*/
public int getLength() {
return 0;
}
/**
* NodeList method: Return the Nth immediate child of this node, or
* null if the index is out of bounds.
* <P>
* By default we do not have any children, ParentNode overrides this.
* @see ParentNode
*
* @return org.w3c.dom.Node
* @param Index int
*/
public Node item(int index) {
return null;
}
//
// DOM2: methods, getters, setters
//
/**
* Puts all <code>Text</code> nodes in the full depth of the sub-tree
* underneath this <code>Node</code>, including attribute nodes, into a
* "normal" form where only markup (e.g., tags, comments, processing
* instructions, CDATA sections, and entity references) separates
* <code>Text</code> nodes, i.e., there are no adjacent <code>Text</code>
* nodes. This can be used to ensure that the DOM view of a document is
* the same as if it were saved and re-loaded, and is useful when
* operations (such as XPointer lookups) that depend on a particular
* document tree structure are to be used.In cases where the document
* contains <code>CDATASections</code>, the normalize operation alone may
* not be sufficient, since XPointers do not differentiate between
* <code>Text</code> nodes and <code>CDATASection</code> nodes.
* <p>
* Note that this implementation simply calls normalize() on this Node's
* children. It is up to implementors or Node to override normalize()
* to take action.
*/
public void normalize() {
/* by default we do not have any children,
ParentNode overrides this behavior */
}
/**
* Introduced in DOM Level 2. <p>
* Tests whether the DOM implementation implements a specific feature and
* that feature is supported by this node.
* @param feature The package name of the feature to test. This is the same
* name as what can be passed to the method hasFeature on
* DOMImplementation.
* @param version This is the version number of the package name to
* test. In Level 2, version 1, this is the string "2.0". If the version is
* not specified, supporting any version of the feature will cause the
* method to return true.
* @return boolean Returns true if this node defines a subtree within which
* the specified feature is supported, false otherwise.
* @since WD-DOM-Level-2-19990923
*/
public boolean isSupported(String feature, String version)
{
return ownerDocument().getImplementation().hasFeature(feature,
version);
}
/**
* Introduced in DOM Level 2. <p>
*
* The namespace URI of this node, or null if it is unspecified. When this
* node is of any type other than ELEMENT_NODE and ATTRIBUTE_NODE, this is
* always null and setting it has no effect. <p>
*
* This is not a computed value that is the result of a namespace lookup
* based on an examination of the namespace declarations in scope. It is
* merely the namespace URI given at creation time.<p>
*
* For nodes created with a DOM Level 1 method, such as createElement
* from the Document interface, this is null.
* @since WD-DOM-Level-2-19990923
* @see AttrNSImpl
* @see ElementNSImpl
*/
public String getNamespaceURI()
{
return null;
}
/**
* Introduced in DOM Level 2. <p>
*
* The namespace prefix of this node, or null if it is unspecified. When
* this node is of any type other than ELEMENT_NODE and ATTRIBUTE_NODE this
* is always null and setting it has no effect.<p>
*
* For nodes created with a DOM Level 1 method, such as createElement
* from the Document interface, this is null. <p>
*
* @since WD-DOM-Level-2-19990923
* @see AttrNSImpl
* @see ElementNSImpl
*/
public String getPrefix()
{
return null;
}
/**
* Introduced in DOM Level 2. <p>
*
* The namespace prefix of this node, or null if it is unspecified. When
* this node is of any type other than ELEMENT_NODE and ATTRIBUTE_NODE
* this is always null and setting it has no effect.<p>
*
* For nodes created with a DOM Level 1 method, such as createElement from
* the Document interface, this is null.<p>
*
* Note that setting this attribute changes the nodeName attribute, which
* holds the qualified name, as well as the tagName and name attributes of
* the Element and Attr interfaces, when applicable.<p>
*
* @throws INVALID_CHARACTER_ERR Raised if the specified
* prefix contains an invalid character.
*
* @since WD-DOM-Level-2-19990923
* @see AttrNSImpl
* @see ElementNSImpl
*/
public void setPrefix(String prefix)
throws DOMException
{
throw new DOMException(DOMException.NAMESPACE_ERR,
DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN,
"NAMESPACE_ERR", null));
}
/**
* Introduced in DOM Level 2. <p>
*
* Returns the local part of the qualified name of this node.
* For nodes created with a DOM Level 1 method, such as createElement
* from the Document interface, and for nodes of any type other than
* ELEMENT_NODE and ATTRIBUTE_NODE this is the same as the nodeName
* attribute.
* @since WD-DOM-Level-2-19990923
* @see AttrNSImpl
* @see ElementNSImpl
*/
public String getLocalName()
{
return null;
}
//
// EventTarget support
//
public void addEventListener(String type, EventListener listener,
boolean useCapture) {
// simply forward to Document
ownerDocument().addEventListener(this, type, listener, useCapture);
}
public void removeEventListener(String type, EventListener listener,
boolean useCapture) {
// simply forward to Document
ownerDocument().removeEventListener(this, type, listener, useCapture);
}
public boolean dispatchEvent(Event event) {
// simply forward to Document
return ownerDocument().dispatchEvent(this, event);
}
//
// Public DOM Level 3 methods
//
/**
* The absolute base URI of this node or <code>null</code> if undefined.
* This value is computed according to . However, when the
* <code>Document</code> supports the feature "HTML" , the base URI is
* computed using first the value of the href attribute of the HTML BASE
* element if any, and the value of the <code>documentURI</code>
* attribute from the <code>Document</code> interface otherwise.
* <br> When the node is an <code>Element</code>, a <code>Document</code>
* or a a <code>ProcessingInstruction</code>, this attribute represents
* the properties [base URI] defined in . When the node is a
* <code>Notation</code>, an <code>Entity</code>, or an
* <code>EntityReference</code>, this attribute represents the
* properties [declaration base URI] in the . How will this be affected
* by resolution of relative namespace URIs issue?It's not.Should this
* only be on Document, Element, ProcessingInstruction, Entity, and
* Notation nodes, according to the infoset? If not, what is it equal to
* on other nodes? Null? An empty string? I think it should be the
* parent's.No.Should this be read-only and computed or and actual
* read-write attribute?Read-only and computed (F2F 19 Jun 2000 and
* teleconference 30 May 2001).If the base HTML element is not yet
* attached to a document, does the insert change the Document.baseURI?
* Yes. (F2F 26 Sep 2001)
* @since DOM Level 3
*/
public String getBaseURI() {
return null;
}
/**
* Compares a node with this node with regard to their position in the
* tree and according to the document order. This order can be extended
* by module that define additional types of nodes.
* @param other The node to compare against this node.
* @return Returns how the given node is positioned relatively to this
* node.
* @since DOM Level 3
* @deprecated
*/
public short compareTreePosition(Node other) {
// Questions of clarification for this method - to be answered by the
// DOM WG. Current assumptions listed - LM
//
// 1. How do ENTITY nodes compare?
// Current assumption: TREE_POSITION_DISCONNECTED, as ENTITY nodes
// aren't really 'in the tree'
//
// 2. How do NOTATION nodes compare?
// Current assumption: TREE_POSITION_DISCONNECTED, as NOTATION nodes
// aren't really 'in the tree'
//
// 3. Are TREE_POSITION_ANCESTOR and TREE_POSITION_DESCENDANT
// only relevant for nodes that are "part of the document tree"?
// <outer>
// <inner myattr="true"/>
// </outer>
// Is the element node "outer" considered an ancestor of "myattr"?
// Current assumption: No.
//
// 4. How do children of ATTRIBUTE nodes compare (with eachother, or
// with children of other attribute nodes with the same element)
// Current assumption: Children of ATTRIBUTE nodes are treated as if
// they they are the attribute node itself, unless the 2 nodes
// are both children of the same attribute.
//
// 5. How does an ENTITY_REFERENCE node compare with it's children?
// Given the DOM, it should precede its children as an ancestor.
// Given "document order", does it represent the same position?
// Current assumption: An ENTITY_REFERENCE node is an ancestor of its
// children.
//
// 6. How do children of a DocumentFragment compare?
// Current assumption: If both nodes are part of the same document
// fragment, there are compared as if they were part of a document.
// If the nodes are the same...
if (this==other)
return (TREE_POSITION_SAME_NODE | TREE_POSITION_EQUIVALENT);
// If either node is of type ENTITY or NOTATION, compare as disconnected
short thisType = this.getNodeType();
short otherType = other.getNodeType();
// If either node is of type ENTITY or NOTATION, compare as disconnected
if (thisType == Node.ENTITY_NODE ||
thisType == Node.NOTATION_NODE ||
otherType == Node.ENTITY_NODE ||
otherType == Node.NOTATION_NODE ) {
return TREE_POSITION_DISCONNECTED;
}
// Find the ancestor of each node, and the distance each node is from
// its ancestor.
// During this traversal, look for ancestor/descendent relationships
// between the 2 nodes in question.
// We do this now, so that we get this info correct for attribute nodes
// and their children.
Node node;
Node thisAncestor = this;
Node otherAncestor = other;
int thisDepth=0;
int otherDepth=0;
for (node=this; node != null; node = node.getParentNode()) {
thisDepth +=1;
if (node == other)
// The other node is an ancestor of this one.
return (TREE_POSITION_ANCESTOR | TREE_POSITION_PRECEDING);
thisAncestor = node;
}
for (node=other; node!=null; node=node.getParentNode()) {
otherDepth +=1;
if (node == this)
// The other node is a descendent of the reference node.
return (TREE_POSITION_DESCENDANT | TREE_POSITION_FOLLOWING);
otherAncestor = node;
}
Node thisNode = this;
Node otherNode = other;
int thisAncestorType = thisAncestor.getNodeType();
int otherAncestorType = otherAncestor.getNodeType();
// if the ancestor is an attribute, get owning element.
// we are now interested in the owner to determine position.
if (thisAncestorType == Node.ATTRIBUTE_NODE) {
thisNode = ((AttrImpl)thisAncestor).getOwnerElement();
}
if (otherAncestorType == Node.ATTRIBUTE_NODE) {
otherNode = ((AttrImpl)otherAncestor).getOwnerElement();
}
// Before proceeding, we should check if both ancestor nodes turned
// out to be attributes for the same element
if (thisAncestorType == Node.ATTRIBUTE_NODE &&
otherAncestorType == Node.ATTRIBUTE_NODE &&
thisNode==otherNode)
return TREE_POSITION_EQUIVALENT;
// Now, find the ancestor of the owning element, if the original
// ancestor was an attribute
// Note: the following 2 loops are quite close to the ones above.
// May want to common them up. LM.
if (thisAncestorType == Node.ATTRIBUTE_NODE) {
thisDepth=0;
for (node=thisNode; node != null; node=node.getParentNode()) {
thisDepth +=1;
if (node == otherNode)
// The other node is an ancestor of the owning element
{
return TREE_POSITION_PRECEDING;
}
thisAncestor = node;
}
}
// Now, find the ancestor of the owning element, if the original
// ancestor was an attribute
if (otherAncestorType == Node.ATTRIBUTE_NODE) {
otherDepth=0;
for (node=otherNode; node != null; node=node.getParentNode()) {
otherDepth +=1;
if (node == thisNode)
// The other node is a descendent of the reference
// node's element
return TREE_POSITION_FOLLOWING;
otherAncestor = node;
}
}
// thisAncestor and otherAncestor must be the same at this point,
// otherwise, we are not in the same tree or document fragment
if (thisAncestor != otherAncestor)
return TREE_POSITION_DISCONNECTED;
// Go up the parent chain of the deeper node, until we find a node
// with the same depth as the shallower node
if (thisDepth > otherDepth) {
for (int i=0; i<thisDepth - otherDepth; i++)
thisNode = thisNode.getParentNode();
// Check if the node we have reached is in fact "otherNode". This can
// happen in the case of attributes. In this case, otherNode
// "precedes" this.
if (thisNode == otherNode)
return TREE_POSITION_PRECEDING;
}
else {
for (int i=0; i<otherDepth - thisDepth; i++)
otherNode = otherNode.getParentNode();
// Check if the node we have reached is in fact "thisNode". This can
// happen in the case of attributes. In this case, otherNode
// "follows" this.
if (otherNode == thisNode)
return TREE_POSITION_FOLLOWING;
}
// We now have nodes at the same depth in the tree. Find a common
// ancestor.
Node thisNodeP, otherNodeP;
for (thisNodeP=thisNode.getParentNode(),
otherNodeP=otherNode.getParentNode();
thisNodeP!=otherNodeP;) {
thisNode = thisNodeP;
otherNode = otherNodeP;
thisNodeP = thisNodeP.getParentNode();
otherNodeP = otherNodeP.getParentNode();
}
// At this point, thisNode and otherNode are direct children of
// the common ancestor.
// See whether thisNode or otherNode is the leftmost
for (Node current=thisNodeP.getFirstChild();
current!=null;
current=current.getNextSibling()) {
if (current==otherNode) {
return TREE_POSITION_PRECEDING;
}
else if (current==thisNode) {
return TREE_POSITION_FOLLOWING;
}
}
// REVISIT: shouldn't get here. Should probably throw an
// exception
return 0;
}
/**
* Compares a node with this node with regard to their position in the
* document.
* @param other The node to compare against this node.
* @return Returns how the given node is positioned relatively to this
* node.
* @since DOM Level 3
*/
public short compareDocumentPosition(Node other) throws DOMException {
// If the nodes are the same, no flags should be set
if (this==other)
return 0;
// check if other is from a different implementation
try {
NodeImpl node = (NodeImpl) other;
} catch (ClassCastException e) {
// other comes from a different implementation
String msg = DOMMessageFormatter.formatMessage(
DOMMessageFormatter.DOM_DOMAIN, "NOT_SUPPORTED_ERR", null);
throw new DOMException(DOMException.NOT_SUPPORTED_ERR, msg);
}
Document thisOwnerDoc, otherOwnerDoc;
// get the respective Document owners.
if (this.getNodeType() == Node.DOCUMENT_NODE)
thisOwnerDoc = (Document)this;
else
thisOwnerDoc = this.getOwnerDocument();
if (other.getNodeType() == Node.DOCUMENT_NODE)
otherOwnerDoc = (Document)other;
else
otherOwnerDoc = other.getOwnerDocument();
// If from different documents, we know they are disconnected.
// and have an implementation dependent order
if (thisOwnerDoc != otherOwnerDoc &&
thisOwnerDoc !=null &&
otherOwnerDoc !=null)
{
int otherDocNum = ((CoreDocumentImpl)otherOwnerDoc).getNodeNumber();
int thisDocNum = ((CoreDocumentImpl)thisOwnerDoc).getNodeNumber();
if (otherDocNum > thisDocNum)
return DOCUMENT_POSITION_DISCONNECTED |
DOCUMENT_POSITION_FOLLOWING |
DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC;
else
return DOCUMENT_POSITION_DISCONNECTED |
DOCUMENT_POSITION_PRECEDING |
DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC;
}
// Find the ancestor of each node, and the distance each node is from
// its ancestor.
// During this traversal, look for ancestor/descendent relationships
// between the 2 nodes in question.
// We do this now, so that we get this info correct for attribute nodes
// and their children.
Node node;
Node thisAncestor = this;
Node otherAncestor = other;
int thisDepth=0;
int otherDepth=0;
for (node=this; node != null; node = node.getParentNode()) {
thisDepth +=1;
if (node == other)
// The other node is an ancestor of this one.
return (DOCUMENT_POSITION_CONTAINS |
DOCUMENT_POSITION_PRECEDING);
thisAncestor = node;
}
for (node=other; node!=null; node=node.getParentNode()) {
otherDepth +=1;
if (node == this)
// The other node is a descendent of the reference node.
return (DOCUMENT_POSITION_IS_CONTAINED |
DOCUMENT_POSITION_FOLLOWING);
otherAncestor = node;
}
int thisAncestorType = thisAncestor.getNodeType();
int otherAncestorType = otherAncestor.getNodeType();
Node thisNode = this;
Node otherNode = other;
// Special casing for ENTITY, NOTATION, DOCTYPE and ATTRIBUTES
// LM: should rewrite this.
switch (thisAncestorType) {
case Node.NOTATION_NODE:
case Node.ENTITY_NODE: {
DocumentType container = thisOwnerDoc.getDoctype();
if (container == otherAncestor) return
(DOCUMENT_POSITION_CONTAINS | DOCUMENT_POSITION_PRECEDING);
switch (otherAncestorType) {
case Node.NOTATION_NODE:
case Node.ENTITY_NODE: {
if (thisAncestorType != otherAncestorType)
// the nodes are of different types
return ((thisAncestorType>otherAncestorType) ?
DOCUMENT_POSITION_PRECEDING:DOCUMENT_POSITION_FOLLOWING);
else {
// the nodes are of the same type. Find order.
if (thisAncestorType == Node.NOTATION_NODE)
if (((NamedNodeMapImpl)container.getNotations()).precedes(otherAncestor,thisAncestor))
return (DOCUMENT_POSITION_PRECEDING |
DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC);
else
return (DOCUMENT_POSITION_FOLLOWING |
DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC);
else
if (((NamedNodeMapImpl)container.getEntities()).precedes(otherAncestor,thisAncestor))
return (DOCUMENT_POSITION_PRECEDING |
DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC);
else
return (DOCUMENT_POSITION_FOLLOWING |
DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC);
}
}
}
thisNode = thisAncestor = thisOwnerDoc;
break;
}
case Node.DOCUMENT_TYPE_NODE: {
if (otherNode == thisOwnerDoc)
return (DOCUMENT_POSITION_PRECEDING |
DOCUMENT_POSITION_CONTAINS);
else if (thisOwnerDoc!=null && thisOwnerDoc==otherOwnerDoc)
return (DOCUMENT_POSITION_FOLLOWING);
break;
}
case Node.ATTRIBUTE_NODE: {
thisNode = ((AttrImpl)thisAncestor).getOwnerElement();
if (otherAncestorType==Node.ATTRIBUTE_NODE) {
otherNode = ((AttrImpl)otherAncestor).getOwnerElement();
if (otherNode == thisNode) {
if (((NamedNodeMapImpl)thisNode.getAttributes()).precedes(other,this))
return (DOCUMENT_POSITION_PRECEDING |
DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC);
else
return (DOCUMENT_POSITION_FOLLOWING |
DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC);
}
}
// Now, find the ancestor of the element
thisDepth=0;
for (node=thisNode; node != null; node=node.getParentNode()) {
thisDepth +=1;
if (node == otherNode)
{
// The other node is an ancestor of the owning element
return (DOCUMENT_POSITION_CONTAINS |
DOCUMENT_POSITION_PRECEDING);
}
thisAncestor = node;
}
}
}
switch (otherAncestorType) {
case Node.NOTATION_NODE:
case Node.ENTITY_NODE: {
DocumentType container = thisOwnerDoc.getDoctype();
if (container == this) return (DOCUMENT_POSITION_IS_CONTAINED |
DOCUMENT_POSITION_FOLLOWING);
otherNode = otherAncestor = thisOwnerDoc;
break;
}
case Node.DOCUMENT_TYPE_NODE: {
if (thisNode == otherOwnerDoc)
return (DOCUMENT_POSITION_FOLLOWING |
DOCUMENT_POSITION_IS_CONTAINED);
else if (otherOwnerDoc!=null && thisOwnerDoc==otherOwnerDoc)
return (DOCUMENT_POSITION_PRECEDING);
break;
}
case Node.ATTRIBUTE_NODE: {
otherDepth=0;
otherNode = ((AttrImpl)otherAncestor).getOwnerElement();
for (node=otherNode; node != null; node=node.getParentNode()) {
otherDepth +=1;
if (node == thisNode)
// The other node is a descendent of the reference
// node's element
return DOCUMENT_POSITION_FOLLOWING |
DOCUMENT_POSITION_IS_CONTAINED;
otherAncestor = node;
}
}
}
// thisAncestor and otherAncestor must be the same at this point,
// otherwise, the original nodes are disconnected
if (thisAncestor != otherAncestor) {
int thisAncestorNum, otherAncestorNum;
thisAncestorNum = ((NodeImpl)thisAncestor).getNodeNumber();
otherAncestorNum = ((NodeImpl)otherAncestor).getNodeNumber();
if (thisAncestorNum > otherAncestorNum)
return DOCUMENT_POSITION_DISCONNECTED |
DOCUMENT_POSITION_FOLLOWING |
DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC;
else
return DOCUMENT_POSITION_DISCONNECTED |
DOCUMENT_POSITION_PRECEDING |
DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC;
}
// Go up the parent chain of the deeper node, until we find a node
// with the same depth as the shallower node
if (thisDepth > otherDepth) {
for (int i=0; i<thisDepth - otherDepth; i++)
thisNode = thisNode.getParentNode();
// Check if the node we have reached is in fact "otherNode". This can
// happen in the case of attributes. In this case, otherNode
// "precedes" this.
if (thisNode == otherNode)
{
return DOCUMENT_POSITION_PRECEDING;
}
}
else {
for (int i=0; i<otherDepth - thisDepth; i++)
otherNode = otherNode.getParentNode();
// Check if the node we have reached is in fact "thisNode". This can
// happen in the case of attributes. In this case, otherNode
// "follows" this.
if (otherNode == thisNode)
return DOCUMENT_POSITION_FOLLOWING;
}
// We now have nodes at the same depth in the tree. Find a common
// ancestor.
Node thisNodeP, otherNodeP;
for (thisNodeP=thisNode.getParentNode(),
otherNodeP=otherNode.getParentNode();
thisNodeP!=otherNodeP;) {
thisNode = thisNodeP;
otherNode = otherNodeP;
thisNodeP = thisNodeP.getParentNode();
otherNodeP = otherNodeP.getParentNode();
}
// At this point, thisNode and otherNode are direct children of
// the common ancestor.
// See whether thisNode or otherNode is the leftmost
for (Node current=thisNodeP.getFirstChild();
current!=null;
current=current.getNextSibling()) {
if (current==otherNode) {
return DOCUMENT_POSITION_PRECEDING;
}
else if (current==thisNode) {
return DOCUMENT_POSITION_FOLLOWING;
}
}
// REVISIT: shouldn't get here. Should probably throw an
// exception
return 0;
}
/**
* This attribute returns the text content of this node and its
* descendants. When it is defined to be null, setting it has no effect.
* When set, any possible children this node may have are removed and
* replaced by a single <code>Text</code> node containing the string
* this attribute is set to. On getting, no serialization is performed,
* the returned string does not contain any markup. No whitespace
* normalization is performed, the returned string does not contain the
* element content whitespaces . Similarly, on setting, no parsing is
* performed either, the input string is taken as pure textual content.
* <br>The string returned is made of the text content of this node
* depending on its type, as defined below:
* <table border='1'>
* <tr>
* <th>Node type</th>
* <th>Content</th>
* </tr>
/**
* This attribute returns the text content of this node and its
* descendants. When it is defined to be null, setting it has no effect.
* When set, any possible children this node may have are removed and
* replaced by a single <code>Text</code> node containing the string
* this attribute is set to. On getting, no serialization is performed,
* the returned string does not contain any markup. No whitespace
* normalization is performed, the returned string does not contain the
* element content whitespaces . Similarly, on setting, no parsing is
* performed either, the input string is taken as pure textual content.
* <br>The string returned is made of the text content of this node
* depending on its type, as defined below:
* <table border='1'>
* <tr>
* <th>Node type</th>
* <th>Content</th>
* </tr>
* <tr>
* <td valign='top' rowspan='1' colspan='1'>
* ELEMENT_NODE, ENTITY_NODE, ENTITY_REFERENCE_NODE,
* DOCUMENT_FRAGMENT_NODE</td>
* <td valign='top' rowspan='1' colspan='1'>concatenation of the <code>textContent</code>
* attribute value of every child node, excluding COMMENT_NODE and
* PROCESSING_INSTRUCTION_NODE nodes</td>
* </tr>
* <tr>
* <td valign='top' rowspan='1' colspan='1'>ATTRIBUTE_NODE, TEXT_NODE,
* CDATA_SECTION_NODE, COMMENT_NODE, PROCESSING_INSTRUCTION_NODE</td>
* <td valign='top' rowspan='1' colspan='1'>
* <code>nodeValue</code></td>
* </tr>
* <tr>
* <td valign='top' rowspan='1' colspan='1'>DOCUMENT_NODE, DOCUMENT_TYPE_NODE, NOTATION_NODE</td>
* <td valign='top' rowspan='1' colspan='1'>
* null</td>
* </tr>
* </table>
* @exception DOMException
* NO_MODIFICATION_ALLOWED_ERR: Raised when the node is readonly.
* @exception DOMException
* DOMSTRING_SIZE_ERR: Raised when it would return more characters than
* fit in a <code>DOMString</code> variable on the implementation
* platform.
* @since DOM Level 3
*/
public String getTextContent() throws DOMException {
return getNodeValue(); // overriden in some subclasses
}
// internal method taking a StringBuffer in parameter
void getTextContent(StringBuffer buf) throws DOMException {
String content = getNodeValue();
if (content != null) {
buf.append(content);
}
}
/**
* This attribute returns the text content of this node and its
* descendants. When it is defined to be null, setting it has no effect.
* When set, any possible children this node may have are removed and
* replaced by a single <code>Text</code> node containing the string
* this attribute is set to. On getting, no serialization is performed,
* the returned string does not contain any markup. No whitespace
* normalization is performed, the returned string does not contain the
* element content whitespaces . Similarly, on setting, no parsing is
* performed either, the input string is taken as pure textual content.
* <br>The string returned is made of the text content of this node
* depending on its type, as defined below:
* <table border='1'>
* <tr>
* <th>Node type</th>
* <th>Content</th>
* </tr>
* <tr>
* <td valign='top' rowspan='1' colspan='1'>
* ELEMENT_NODE, ENTITY_NODE, ENTITY_REFERENCE_NODE,
* DOCUMENT_FRAGMENT_NODE</td>
* <td valign='top' rowspan='1' colspan='1'>concatenation of the <code>textContent</code>
* attribute value of every child node, excluding COMMENT_NODE and
* PROCESSING_INSTRUCTION_NODE nodes</td>
* </tr>
* <tr>
* <td valign='top' rowspan='1' colspan='1'>ATTRIBUTE_NODE, TEXT_NODE,
* CDATA_SECTION_NODE, COMMENT_NODE, PROCESSING_INSTRUCTION_NODE</td>
* <td valign='top' rowspan='1' colspan='1'>
* <code>nodeValue</code></td>
* </tr>
* <tr>
* <td valign='top' rowspan='1' colspan='1'>DOCUMENT_NODE, DOCUMENT_TYPE_NODE, NOTATION_NODE</td>
* <td valign='top' rowspan='1' colspan='1'>
* null</td>
* </tr>
* </table>
* @exception DOMException
* NO_MODIFICATION_ALLOWED_ERR: Raised when the node is readonly.
* @exception DOMException
* DOMSTRING_SIZE_ERR: Raised when it would return more characters than
* fit in a <code>DOMString</code> variable on the implementation
* platform.
* @since DOM Level 3
*/
public void setTextContent(String textContent)
throws DOMException {
setNodeValue(textContent);
}
/**
* Returns whether this node is the same node as the given one.
* <br>This method provides a way to determine whether two
* <code>Node</code> references returned by the implementation reference
* the same object. When two <code>Node</code> references are references
* to the same object, even if through a proxy, the references may be
* used completely interchangably, such that all attributes have the
* same values and calling the same DOM method on either reference
* always has exactly the same effect.
* @param other The node to test against.
* @return Returns <code>true</code> if the nodes are the same,
* <code>false</code> otherwise.
* @since DOM Level 3
*/
public boolean isSameNode(Node other) {
// we do not use any wrapper so the answer is obvious
return this == other;
}
/**
* DOM Level 3: Experimental
* This method checks if the specified <code>namespaceURI</code> is the
* default namespace or not.
* @param namespaceURI The namespace URI to look for.
* @return <code>true</code> if the specified <code>namespaceURI</code>
* is the default namespace, <code>false</code> otherwise.
* @since DOM Level 3
*/
public boolean isDefaultNamespace(String namespaceURI){
// REVISIT: remove casts when DOM L3 becomes REC.
short type = this.getNodeType();
switch (type) {
case Node.ELEMENT_NODE: {
String namespace = this.getNamespaceURI();
String prefix = this.getPrefix();
// REVISIT: is it possible that prefix is empty string?
if (prefix == null || prefix.length() == 0) {
if (namespaceURI == null) {
return (namespace == namespaceURI);
}
return namespaceURI.equals(namespace);
}
if (this.hasAttributes()) {
ElementImpl elem = (ElementImpl)this;
NodeImpl attr = (NodeImpl)elem.getAttributeNodeNS("http://www.w3.org/2000/xmlns/", "xmlns");
if (attr != null) {
String value = attr.getNodeValue();
if (namespaceURI == null) {
return (namespace == value);
}
return namespaceURI.equals(value);
}
}
NodeImpl ancestor = (NodeImpl)getElementAncestor(this);
if (ancestor != null) {
return ancestor.isDefaultNamespace(namespaceURI);
}
return false;
}
case Node.DOCUMENT_NODE:{
return((NodeImpl)((Document)this).getDocumentElement()).isDefaultNamespace(namespaceURI);
}
case Node.ENTITY_NODE :
case Node.NOTATION_NODE:
case Node.DOCUMENT_FRAGMENT_NODE:
case Node.DOCUMENT_TYPE_NODE:
// type is unknown
return false;
case Node.ATTRIBUTE_NODE:{
if (this.ownerNode.getNodeType() == Node.ELEMENT_NODE) {
return ownerNode.isDefaultNamespace(namespaceURI);
}
return false;
}
default:{
NodeImpl ancestor = (NodeImpl)getElementAncestor(this);
if (ancestor != null) {
return ancestor.isDefaultNamespace(namespaceURI);
}
return false;
}
}
}
/**
*
* DOM Level 3 - Experimental:
* Look up the prefix associated to the given namespace URI, starting from this node.
*
* @param namespaceURI
* @return the prefix for the namespace
*/
public String lookupPrefix(String namespaceURI){
// REVISIT: When Namespaces 1.1 comes out this may not be true
// Prefix can't be bound to null namespace
if (namespaceURI == null) {
return null;
}
short type = this.getNodeType();
switch (type) {
case Node.ELEMENT_NODE: {
String namespace = this.getNamespaceURI(); // to flip out children
return lookupNamespacePrefix(namespaceURI, (ElementImpl)this);
}
case Node.DOCUMENT_NODE:{
return((NodeImpl)((Document)this).getDocumentElement()).lookupPrefix(namespaceURI);
}
case Node.ENTITY_NODE :
case Node.NOTATION_NODE:
case Node.DOCUMENT_FRAGMENT_NODE:
case Node.DOCUMENT_TYPE_NODE:
// type is unknown
return null;
case Node.ATTRIBUTE_NODE:{
if (this.ownerNode.getNodeType() == Node.ELEMENT_NODE) {
return ownerNode.lookupPrefix(namespaceURI);
}
return null;
}
default:{
NodeImpl ancestor = (NodeImpl)getElementAncestor(this);
if (ancestor != null) {
return ancestor.lookupPrefix(namespaceURI);
}
return null;
}
}
}
/**
* DOM Level 3 - Experimental:
* Look up the namespace URI associated to the given prefix, starting from this node.
* Use lookupNamespaceURI(null) to lookup the default namespace
*
* @param namespaceURI
* @return th URI for the namespace
* @since DOM Level 3
*/
public String lookupNamespaceURI(String specifiedPrefix) {
short type = this.getNodeType();
switch (type) {
case Node.ELEMENT_NODE : {
String namespace = this.getNamespaceURI();
String prefix = this.getPrefix();
if (namespace !=null) {
// REVISIT: is it possible that prefix is empty string?
if (specifiedPrefix== null && prefix==specifiedPrefix) {
// looking for default namespace
return namespace;
} else if (prefix != null && prefix.equals(specifiedPrefix)) {
// non default namespace
return namespace;
}
}
if (this.hasAttributes()) {
NamedNodeMap map = this.getAttributes();
int length = map.getLength();
for (int i=0;i<length;i++) {
Node attr = map.item(i);
String attrPrefix = attr.getPrefix();
String value = attr.getNodeValue();
namespace = attr.getNamespaceURI();
if (namespace !=null && namespace.equals("http://www.w3.org/2000/xmlns/")) {
// at this point we are dealing with DOM Level 2 nodes only
if (specifiedPrefix == null &&
attr.getNodeName().equals("xmlns")) {
// default namespace
return value;
} else if (attrPrefix !=null &&
attrPrefix.equals("xmlns") &&
attr.getLocalName().equals(specifiedPrefix)) {
// non default namespace
return value;
}
}
}
}
NodeImpl ancestor = (NodeImpl)getElementAncestor(this);
if (ancestor != null) {
return ancestor.lookupNamespaceURI(specifiedPrefix);
}
return null;
}
case Node.DOCUMENT_NODE : {
return((NodeImpl)((Document)this).getDocumentElement()).lookupNamespaceURI(specifiedPrefix);
}
case Node.ENTITY_NODE :
case Node.NOTATION_NODE:
case Node.DOCUMENT_FRAGMENT_NODE:
case Node.DOCUMENT_TYPE_NODE:
// type is unknown
return null;
case Node.ATTRIBUTE_NODE:{
if (this.ownerNode.getNodeType() == Node.ELEMENT_NODE) {
return ownerNode.lookupNamespaceURI(specifiedPrefix);
}
return null;
}
default:{
NodeImpl ancestor = (NodeImpl)getElementAncestor(this);
if (ancestor != null) {
return ancestor.lookupNamespaceURI(specifiedPrefix);
}
return null;
}
}
}
Node getElementAncestor (Node currentNode){
Node parent = currentNode.getParentNode();
if (parent != null) {
short type = parent.getNodeType();
if (type == Node.ELEMENT_NODE) {
return parent;
}
return getElementAncestor(parent);
}
return null;
}
String lookupNamespacePrefix(String namespaceURI, ElementImpl el){
String namespace = this.getNamespaceURI();
// REVISIT: if no prefix is available is it null or empty string, or
// could be both?
String prefix = this.getPrefix();
if (namespace!=null && namespace.equals(namespaceURI)) {
if (prefix != null) {
String foundNamespace = el.lookupNamespaceURI(prefix);
if (foundNamespace !=null && foundNamespace.equals(namespaceURI)) {
return prefix;
}
}
}
if (this.hasAttributes()) {
NamedNodeMap map = this.getAttributes();
int length = map.getLength();
for (int i=0;i<length;i++) {
Node attr = map.item(i);
String attrPrefix = attr.getPrefix();
String value = attr.getNodeValue();
namespace = attr.getNamespaceURI();
if (namespace !=null && namespace.equals("http://www.w3.org/2000/xmlns/")) {
// DOM Level 2 nodes
if (((attr.getNodeName().equals("xmlns")) ||
(attrPrefix !=null && attrPrefix.equals("xmlns")) &&
value.equals(namespaceURI))) {
String localname= attr.getLocalName();
String foundNamespace = el.lookupNamespaceURI(localname);
if (foundNamespace !=null && foundNamespace.equals(namespaceURI)) {
return localname;
}
}
}
}
}
NodeImpl ancestor = (NodeImpl)getElementAncestor(this);
if (ancestor != null) {
return ancestor.lookupNamespacePrefix(namespaceURI, el);
}
return null;
}
/**
* Tests whether two nodes are equal.
* <br>This method tests for equality of nodes, not sameness (i.e.,
* whether the two nodes are references to the same object) which can be
* tested with <code>Node.isSameNode</code>. All nodes that are the same
* will also be equal, though the reverse may not be true.
* <br>Two nodes are equal if and only if the following conditions are
* satisfied: The two nodes are of the same type.The following string
* attributes are equal: <code>nodeName</code>, <code>localName</code>,
* <code>namespaceURI</code>, <code>prefix</code>, <code>nodeValue</code>
* , <code>baseURI</code>. This is: they are both <code>null</code>, or
* they have the same length and are character for character identical.
* The <code>attributes</code> <code>NamedNodeMaps</code> are equal.
* This is: they are both <code>null</code>, or they have the same
* length and for each node that exists in one map there is a node that
* exists in the other map and is equal, although not necessarily at the
* same index.The <code>childNodes</code> <code>NodeLists</code> are
* equal. This is: they are both <code>null</code>, or they have the
* same length and contain equal nodes at the same index. This is true
* for <code>Attr</code> nodes as for any other type of node. Note that
* normalization can affect equality; to avoid this, nodes should be
* normalized before being compared.
* <br>For two <code>DocumentType</code> nodes to be equal, the following
* conditions must also be satisfied: The following string attributes
* are equal: <code>publicId</code>, <code>systemId</code>,
* <code>internalSubset</code>.The <code>entities</code>
* <code>NamedNodeMaps</code> are equal.The <code>notations</code>
* <code>NamedNodeMaps</code> are equal.
* <br>On the other hand, the following do not affect equality: the
* <code>ownerDocument</code> attribute, the <code>specified</code>
* attribute for <code>Attr</code> nodes, the
* <code>isWhitespaceInElementContent</code> attribute for
* <code>Text</code> nodes, as well as any user data or event listeners
* registered on the nodes.
* @param arg The node to compare equality with.
* @param deep If <code>true</code>, recursively compare the subtrees; if
* <code>false</code>, compare only the nodes themselves (and its
* attributes, if it is an <code>Element</code>).
* @return If the nodes, and possibly subtrees are equal,
* <code>true</code> otherwise <code>false</code>.
* @since DOM Level 3
*/
public boolean isEqualNode(Node arg) {
if (arg == this) {
return true;
}
if (arg.getNodeType() != getNodeType()) {
return false;
}
// in theory nodeName can't be null but better be careful
// who knows what other implementations may be doing?...
if (getNodeName() == null) {
if (arg.getNodeName() != null) {
return false;
}
}
else if (!getNodeName().equals(arg.getNodeName())) {
return false;
}
if (getLocalName() == null) {
if (arg.getLocalName() != null) {
return false;
}
}
else if (!getLocalName().equals(arg.getLocalName())) {
return false;
}
if (getNamespaceURI() == null) {
if (arg.getNamespaceURI() != null) {
return false;
}
}
else if (!getNamespaceURI().equals(arg.getNamespaceURI())) {
return false;
}
if (getPrefix() == null) {
if (arg.getPrefix() != null) {
return false;
}
}
else if (!getPrefix().equals(arg.getPrefix())) {
return false;
}
if (getNodeValue() == null) {
if (arg.getNodeValue() != null) {
return false;
}
}
else if (!getNodeValue().equals(arg.getNodeValue())) {
return false;
}
return true;
}
/**
* @since DOM Level 3
*/
public Object getFeature(String feature, String version) {
// we don't have any alternate node, either this node does the job
// or we don't have anything that does
return isSupported(feature, version) ? this : null;
}
/**
* Associate an object to a key on this node. The object can later be
* retrieved from this node by calling <code>getUserData</code> with the
* same key.
* @param key The key to associate the object to.
* @param data The object to associate to the given key, or
* <code>null</code> to remove any existing association to that key.
* @param handler The handler to associate to that key, or
* <code>null</code>.
* @return Returns the <code>DOMObject</code> previously associated to
* the given key on this node, or <code>null</code> if there was none.
* @since DOM Level 3
*/
public Object setUserData(String key,
Object data,
UserDataHandler handler) {
return ownerDocument().setUserData(this, key, data, handler);
}
/**
* Retrieves the object associated to a key on a this node. The object
* must first have been set to this node by calling
* <code>setUserData</code> with the same key.
* @param key The key the object is associated to.
* @return Returns the <code>DOMObject</code> associated to the given key
* on this node, or <code>null</code> if there was none.
* @since DOM Level 3
*/
public Object getUserData(String key) {
return ownerDocument().getUserData(this, key);
}
protected Hashtable getUserDataRecord(){
return ownerDocument().getUserDataRecord(this);
}
//
// Public methods
//
/**
* NON-DOM: PR-DOM-Level-1-19980818 mentions readonly nodes in conjunction
* with Entities, but provides no API to support this.
* <P>
* Most DOM users should not touch this method. Its anticpated use
* is during construction of EntityRefernces, where it will be used to
* lock the contents replicated from Entity so they can't be casually
* altered. It _could_ be published as a DOM extension, if desired.
* <P>
* Note: since we never have any children deep is meaningless here,
* ParentNode overrides this behavior.
* @see ParentNode
*
* @param readOnly True or false as desired.
* @param deep If true, children are also toggled. Note that this will
* not change the state of an EntityReference or its children,
* which are always read-only.
*/
public void setReadOnly(boolean readOnly, boolean deep) {
if (needsSyncData()) {
synchronizeData();
}
isReadOnly(readOnly);
} // setReadOnly(boolean,boolean)
/**
* NON-DOM: Returns true if this node is read-only. This is a
* shallow check.
*/
public boolean getReadOnly() {
if (needsSyncData()) {
synchronizeData();
}
return isReadOnly();
} // getReadOnly():boolean
/**
* NON-DOM: As an alternative to subclassing the DOM, this implementation
* has been extended with the ability to attach an object to each node.
* (If you need multiple objects, you can attach a collection such as a
* vector or hashtable, then attach your application information to that.)
* <p><b>Important Note:</b> You are responsible for removing references
* to your data on nodes that are no longer used. Failure to do so will
* prevent the nodes, your data is attached to, to be garbage collected
* until the whole document is.
*
* @param data the object to store or null to remove any existing reference
*/
public void setUserData(Object data) {
ownerDocument().setUserData(this, data);
}
/**
* NON-DOM:
* Returns the user data associated to this node.
*/
public Object getUserData() {
return ownerDocument().getUserData(this);
}
//
// Protected methods
//
/**
* Denotes that this node has changed.
*/
protected void changed() {
// we do not actually store this information on every node, we only
// have a global indicator on the Document. Doing otherwise cost us too
// much for little gain.
ownerDocument().changed();
}
/**
* Returns the number of changes to this node.
*/
protected int changes() {
// we do not actually store this information on every node, we only
// have a global indicator on the Document. Doing otherwise cost us too
// much for little gain.
return ownerDocument().changes();
}
/**
* Override this method in subclass to hook in efficient
* internal data structure.
*/
protected void synchronizeData() {
// By default just change the flag to avoid calling this method again
needsSyncData(false);
}
/**
* For non-child nodes, the node which "points" to this node.
* For example, the owning element for an attribute
*/
protected Node getContainer() {
return null;
}
/*
* Flags setters and getters
*/
final boolean isReadOnly() {
return (flags & READONLY) != 0;
}
final void isReadOnly(boolean value) {
flags = (short) (value ? flags | READONLY : flags & ~READONLY);
}
final boolean needsSyncData() {
return (flags & SYNCDATA) != 0;
}
final void needsSyncData(boolean value) {
flags = (short) (value ? flags | SYNCDATA : flags & ~SYNCDATA);
}
final boolean needsSyncChildren() {
return (flags & SYNCCHILDREN) != 0;
}
public final void needsSyncChildren(boolean value) {
flags = (short) (value ? flags | SYNCCHILDREN : flags & ~SYNCCHILDREN);
}
final boolean isOwned() {
return (flags & OWNED) != 0;
}
final void isOwned(boolean value) {
flags = (short) (value ? flags | OWNED : flags & ~OWNED);
}
final boolean isFirstChild() {
return (flags & FIRSTCHILD) != 0;
}
final void isFirstChild(boolean value) {
flags = (short) (value ? flags | FIRSTCHILD : flags & ~FIRSTCHILD);
}
final boolean isSpecified() {
return (flags & SPECIFIED) != 0;
}
final void isSpecified(boolean value) {
flags = (short) (value ? flags | SPECIFIED : flags & ~SPECIFIED);
}
// inconsistent name to avoid clash with public method on TextImpl
final boolean internalIsIgnorableWhitespace() {
return (flags & IGNORABLEWS) != 0;
}
final void isIgnorableWhitespace(boolean value) {
flags = (short) (value ? flags | IGNORABLEWS : flags & ~IGNORABLEWS);
}
final boolean hasStringValue() {
return (flags & HASSTRING) != 0;
}
final void hasStringValue(boolean value) {
flags = (short) (value ? flags | HASSTRING : flags & ~HASSTRING);
}
final boolean isNormalized() {
return (flags & NORMALIZED) != 0;
}
final void isNormalized(boolean value) {
// See if flag should propagate to parent.
if (!value && isNormalized() && ownerNode != null) {
ownerNode.isNormalized(false);
}
flags = (short) (value ? flags | NORMALIZED : flags & ~NORMALIZED);
}
final boolean isIdAttribute() {
return (flags & ID) != 0;
}
final void isIdAttribute(boolean value) {
flags = (short) (value ? flags | ID : flags & ~ID);
}
//
// Object methods
//
/** NON-DOM method for debugging convenience. */
public String toString() {
return "["+getNodeName()+": "+getNodeValue()+"]";
}
//
// Serialization methods
//
/** Serialize object. */
private void writeObject(ObjectOutputStream out) throws IOException {
// synchronize data
if (needsSyncData()) {
synchronizeData();
}
// write object
out.defaultWriteObject();
} // writeObject(ObjectOutputStream)
} // class NodeImpl