/*
* Copyright 1999-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.
*/
/*
* $Id: DOM2DTMdefaultNamespaceDeclarationNode.java,v 1.2.4.1 2005/09/15 08:15:11 suresh_emailid Exp $
*/
// This is copied directly out of the original
// com.sun.org.apache.xml.internal.dtm.ref.dom2dtm path to allow modifying
// DOM2DTM and working around package private constructors. Other than the
// package declaration, this class is unmodified
package nokogiri.internals.dom2dtm;
import org.apache.xml.dtm.DTMException;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.TypeInfo;
import org.w3c.dom.UserDataHandler;
import org.w3c.dom.DOMException;
/** This is a kluge to let us shove a declaration for xml: into the
* DOM2DTM model. Basically, it creates a proxy node in DOM space to
* carry the additional information. This is _NOT_ a full DOM
* implementation, and shouldn't be one since it sits alongside the
* DOM rather than becoming part of the DOM model.
*
* (This used to be an internal class within DOM2DTM. Moved out because
* I need to perform an instanceof operation on it to support a temporary
* workaround in DTMManagerDefault.)
*
* %REVIEW% What if the DOM2DTM was built around a DocumentFragment and
* there isn't a single root element? I think this fails that case...
*
* %REVIEW% An alternative solution would be to create the node _only_
* in DTM space, but given how DOM2DTM is currently written I think
* this is simplest.
* */
public class DOM2DTMdefaultNamespaceDeclarationNode implements Attr, TypeInfo
{
final String NOT_SUPPORTED_ERR = "Unsupported operation on pseudonode";
Element pseudoparent;
String prefix, uri, nodename;
int handle;
DOM2DTMdefaultNamespaceDeclarationNode(Element pseudoparent, String prefix, String uri, int handle)
{
this.pseudoparent = pseudoparent;
this.prefix = prefix;
this.uri = uri;
this.handle = handle;
this.nodename = "xmlns:" + prefix;
}
public String
getNodeName() {return nodename;}
public String
getName() {return nodename;}
public String
getNamespaceURI() {return "http://www.w3.org/2000/xmlns/";}
public String
getPrefix() {return prefix;}
public String
getLocalName() {return prefix;}
public String
getNodeValue() {return uri;}
public String
getValue() {return uri;}
public Element
getOwnerElement() {return pseudoparent;}
public boolean
isSupported(String feature, String version) {return false;}
public boolean
hasChildNodes() {return false;}
public boolean
hasAttributes() {return false;}
public Node
getParentNode() {return null;}
public Node
getFirstChild() {return null;}
public Node
getLastChild() {return null;}
public Node
getPreviousSibling() {return null;}
public Node
getNextSibling() {return null;}
public boolean
getSpecified() {return false;}
public void
normalize() {return;}
public NodeList
getChildNodes() {return null;}
public NamedNodeMap
getAttributes() {return null;}
public short
getNodeType() {return Node.ATTRIBUTE_NODE;}
public void
setNodeValue(String value) {throw new DTMException(NOT_SUPPORTED_ERR);}
public void
setValue(String value) {throw new DTMException(NOT_SUPPORTED_ERR);}
public void
setPrefix(String value) {throw new DTMException(NOT_SUPPORTED_ERR);}
public Node
insertBefore(Node a, Node b) {throw new DTMException(NOT_SUPPORTED_ERR);}
public Node
replaceChild(Node a, Node b) {throw new DTMException(NOT_SUPPORTED_ERR);}
public Node
appendChild(Node a) {throw new DTMException(NOT_SUPPORTED_ERR);}
public Node
removeChild(Node a) {throw new DTMException(NOT_SUPPORTED_ERR);}
public Document
getOwnerDocument() {return pseudoparent.getOwnerDocument();}
public Node
cloneNode(boolean deep) {throw new DTMException(NOT_SUPPORTED_ERR);}
/** Non-DOM method, part of the temporary kluge
* %REVIEW% This would be a pruning problem, but since it will always be
* added to the root element and we prune on elements, we shouldn't have
* to worry.
*/
public int
getHandleOfNode()
{
return handle;
}
//RAMESH: PENDING=> Add proper implementation for the below DOM L3 additions
/**
* @see org.w3c.dom.TypeInfo#getTypeName()
*/
public String
getTypeName() {return null; }
/**
* @see org.w3c.dom.TypeInfo#getTypeNamespace()
*/
public String
getTypeNamespace() { return null;}
/**
* @see or.gw3c.dom.TypeInfo#isDerivedFrom(String,String,int)
*/
public boolean
isDerivedFrom(String ns, String localName, int derivationMethod)
{
return false;
}
public TypeInfo
getSchemaTypeInfo() { return this; }
public boolean
isId() { return false; }
/**
* Associate an object to a key on this node. The object can later be
* retrieved from this node by calling getUserData
with the
* same key.
* @param key The key to associate the object to.
* @param data The object to associate to the given key, or
* null
to remove any existing association to that key.
* @param handler The handler to associate to that key, or
* null
.
* @return Returns the DOMObject
previously associated to
* the given key on this node, or null
if there was none.
* @since DOM Level 3
*/
public Object
setUserData(String key,
Object data,
UserDataHandler handler)
{
return getOwnerDocument().setUserData(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
* setUserData
with the same key.
* @param key The key the object is associated to.
* @return Returns the DOMObject
associated to the given key
* on this node, or null
if there was none.
* @since DOM Level 3
*/
public Object
getUserData(String key)
{
return getOwnerDocument().getUserData(key);
}
/**
* This method returns a specialized object which implements the
* specialized APIs of the specified feature and version. The
* specialized object may also be obtained by using binding-specific
* casting methods but is not necessarily expected to, as discussed in Mixed DOM implementations.
* @param feature The name of the feature requested (case-insensitive).
* @param version This is the version number of the feature to test. If
* the version is null
or the empty string, supporting
* any version of the feature will cause the method to return an
* object that supports at least one version of the feature.
* @return Returns an object which implements the specialized APIs of
* the specified feature and version, if any, or null
if
* there is no object which implements interfaces associated with that
* feature. If the DOMObject
returned by this method
* implements the Node
interface, it must delegate to the
* primary core Node
and not return results inconsistent
* with the primary core Node
such as attributes,
* childNodes, etc.
* @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;
}
/**
* Tests whether two nodes are equal.
*
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 Node.isSameNode
. All nodes that are the same
* will also be equal, though the reverse may not be true.
*
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: nodeName
, localName
,
* namespaceURI
, prefix
, nodeValue
* , baseURI
. This is: they are both null
, or
* they have the same length and are character for character identical.
* The attributes
NamedNodeMaps
are equal.
* This is: they are both null
, 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 childNodes
NodeLists
are
* equal. This is: they are both null
, or they have the
* same length and contain equal nodes at the same index. This is true
* for Attr
nodes as for any other type of node. Note that
* normalization can affect equality; to avoid this, nodes should be
* normalized before being compared.
*
For two DocumentType
nodes to be equal, the following
* conditions must also be satisfied: The following string attributes
* are equal: publicId
, systemId
,
* internalSubset
.The entities
* NamedNodeMaps
are equal.The notations
* NamedNodeMaps
are equal.
*
On the other hand, the following do not affect equality: the
* ownerDocument
attribute, the specified
* attribute for Attr
nodes, the
* isWhitespaceInElementContent
attribute for
* Text
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 true
, recursively compare the subtrees; if
* false
, compare only the nodes themselves (and its
* attributes, if it is an Element
).
* @return If the nodes, and possibly subtrees are equal,
* true
otherwise false
.
* @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;
}
/*
if (getBaseURI() == null) {
if (((NodeImpl) arg).getBaseURI() != null) {
return false;
}
}
else if (!getBaseURI().equals(((NodeImpl) arg).getBaseURI())) {
return false;
}
*/
return true;
}
/**
* 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.getOwnerElement().getNodeType() == Node.ELEMENT_NODE) {
return getOwnerElement().lookupNamespaceURI(specifiedPrefix);
}
return null;
}
default: {
/*
NodeImpl ancestor = (NodeImpl)getElementAncestor(this);
if (ancestor != null) {
return ancestor.lookupNamespaceURI(specifiedPrefix);
}
*/
return null;
}
}
}
/**
* DOM Level 3: Experimental
* This method checks if the specified namespaceURI
is the
* default namespace or not.
* @param namespaceURI The namespace URI to look for.
* @return true
if the specified namespaceURI
* is the default namespace, false
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;
}
}
*/
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.getOwnerElement().getNodeType() == Node.ELEMENT_NODE) {
return getOwnerElement().lookupPrefix(namespaceURI);
}
return null;
}
default: {
/*
NodeImpl ancestor = (NodeImpl)getElementAncestor(this);
if (ancestor != null) {
return ancestor.lookupPrefix(namespaceURI);
}
*/
return null;
}
}
}
/**
* Returns whether this node is the same node as the given one.
*
This method provides a way to determine whether two
* Node
references returned by the implementation reference
* the same object. When two Node
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 true
if the nodes are the same,
* false
otherwise.
* @since DOM Level 3
*/
public boolean
isSameNode(Node other)
{
// we do not use any wrapper so the answer is obvious
return this == other;
}
/**
* 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 Text
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.
*
The string returned is made of the text content of this node
* depending on its type, as defined below:
*
*
* Node type |
* Content |
*
*
*
* ELEMENT_NODE, ENTITY_NODE, ENTITY_REFERENCE_NODE,
* DOCUMENT_FRAGMENT_NODE |
* concatenation of the textContent
* attribute value of every child node, excluding COMMENT_NODE and
* PROCESSING_INSTRUCTION_NODE nodes |
*
*
* ATTRIBUTE_NODE, TEXT_NODE,
* CDATA_SECTION_NODE, COMMENT_NODE, PROCESSING_INSTRUCTION_NODE |
*
* nodeValue |
*
*
* DOCUMENT_NODE, DOCUMENT_TYPE_NODE, NOTATION_NODE |
*
* null |
*
*
* @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 DOMString
variable on the implementation
* platform.
* @since DOM Level 3
*/
public void
setTextContent(String textContent)
throws DOMException
{
setNodeValue(textContent);
}
/**
* 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 Text
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.
*
The string returned is made of the text content of this node
* depending on its type, as defined below:
*
*
* Node type |
* Content |
*
*
*
* ELEMENT_NODE, ENTITY_NODE, ENTITY_REFERENCE_NODE,
* DOCUMENT_FRAGMENT_NODE |
* concatenation of the textContent
* attribute value of every child node, excluding COMMENT_NODE and
* PROCESSING_INSTRUCTION_NODE nodes |
*
*
* ATTRIBUTE_NODE, TEXT_NODE,
* CDATA_SECTION_NODE, COMMENT_NODE, PROCESSING_INSTRUCTION_NODE |
*
* nodeValue |
*
*
* DOCUMENT_NODE, DOCUMENT_TYPE_NODE, NOTATION_NODE |
*
* null |
*
*
* @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 DOMString
variable on the implementation
* platform.
* @since DOM Level 3
*/
public String
getTextContent() throws DOMException
{
return getNodeValue(); // overriden in some subclasses
}
/**
* 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
{
return 0;
}
/**
* The absolute base URI of this node or null
if undefined.
* This value is computed according to . However, when the
* Document
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 documentURI
* attribute from the Document
interface otherwise.
*
When the node is an Element
, a Document
* or a a ProcessingInstruction
, this attribute represents
* the properties [base URI] defined in . When the node is a
* Notation
, an Entity
, or an
* EntityReference
, 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;
}
}