DOM2DTM
class serves up a DOM's contents via the
* DTM API.
*
* Note that it doesn't necessarily represent a full Document
* tree. You can wrap a DOM2DTM around a specific node and its subtree
* and the right things should happen. (I don't _think_ we currently
* support DocumentFrgment nodes as roots, though that might be worth
* considering.)
*
* Note too that we do not currently attempt to track document
* mutation. If you alter the DOM after wrapping DOM2DTM around it,
* all bets are off.
* */
public class DOM2DTM extends DTMDefaultBaseIterators
{
// static final boolean JJK_DEBUG=false;
// static final boolean JJK_NEWCODE=true;
/** Manefest constant
*/
static final String NAMESPACE_DECL_NS = "http://www.w3.org/XML/1998/namespace";
/** The current position in the DOM tree. Last node examined for
* possible copying to DTM. */
transient private Node m_pos;
/** The current position in the DTM tree. Who children get appended to. */
private int m_last_parent = 0;
/** The current position in the DTM tree. Who children reference as their
* previous sib. */
private int m_last_kid = NULL;
/** The top of the subtree.
* %REVIEW%: 'may not be the same as m_context if "//foo" pattern.'
* */
transient private Node m_root;
/** True iff the first element has been processed. This is used to control
synthesis of the implied xml: namespace declaration node. */
boolean m_processedFirstElement = false;
/** true if ALL the nodes in the m_root subtree have been processed;
* false if our incremental build has not yet finished scanning the
* DOM tree. */
transient private boolean m_nodesAreProcessed;
/** The node objects. The instance part of the handle indexes
* directly into this vector. Each DTM node may actually be
* composed of several DOM nodes (for example, if logically-adjacent
* Text/CDATASection nodes in the DOM have been coalesced into a
* single DTM Text node); this table points only to the first in
* that sequence. */
protected final java.util.List%OPT% This will be pretty slow.
* *%OPT% An XPath-like search (walk up DOM to root, tracking path; * walk down DTM reconstructing path) might be considerably faster * on later nodes in large documents. That might also imply improving * this call to handle nodes which would be in this DTM but * have not yet been built, which might or might not be a Good Thing.
* * %REVIEW% This relies on being able to test node-identity via * object-identity. DTM2DOM proxying is a great example of a case where * that doesn't work. DOM Level 3 will provide the isSameNode() method * to fix that, but until then this is going to be flaky. * * @param node A node, which may be null. * * @return The node handle orDTM.NULL
.
*/
public int
getHandleFromNode(Node node)
{
if (null != node) {
int len = m_nodes.size();
boolean isMore;
int i = 0;
do {
for (; i < len; i++) {
if (m_nodes.get(i) == node) {
return makeNodeHandle(i);
}
}
isMore = nextNode();
len = m_nodes.size();
} while (isMore || i < len);
}
return DTM.NULL;
}
/** Get the handle from a Node. This is a more robust version of
* getHandleFromNode, intended to be usable by the public.
*
* %OPT% This will be pretty slow.
* * %REVIEW% This relies on being able to test node-identity via * object-identity. DTM2DOM proxying is a great example of a case where * that doesn't work. DOM Level 3 will provide the isSameNode() method * to fix that, but until then this is going to be flaky. * * @param node A node, which may be null. * * @return The node handle orDTM.NULL
. */
public int
getHandleOfNode(Node node)
{
if (null != node) {
// Is Node actually within the same document? If not, don't search!
// This would be easier if m_root was always the Document node, but
// we decided to allow wrapping a DTM around a subtree.
if ((m_root == node) ||
(m_root.getNodeType() == DOCUMENT_NODE &&
m_root == node.getOwnerDocument()) ||
(m_root.getNodeType() != DOCUMENT_NODE &&
m_root.getOwnerDocument() == node.getOwnerDocument())
) {
// If node _is_ in m_root's tree, find its handle
//
// %OPT% This check may be improved significantly when DOM
// Level 3 nodeKey and relative-order tests become
// available!
for (Node cursor = node;
cursor != null;
cursor =
(cursor.getNodeType() != ATTRIBUTE_NODE)
? cursor.getParentNode()
: ((org.w3c.dom.Attr)cursor).getOwnerElement()) {
if (cursor == m_root)
// We know this node; find its handle.
{
return getHandleFromNode(node);
}
} // for ancestors of node
} // if node and m_root in same Document
} // if node!=null
return DTM.NULL;
}
/**
* Retrieves an attribute node by by qualified name and namespace URI.
*
* @param nodeHandle int Handle of the node upon which to look up this attribute..
* @param namespaceURI The namespace URI of the attribute to
* retrieve, or null.
* @param name The local name of the attribute to
* retrieve.
* @return The attribute node handle with the specified name (
* nodeName
) or DTM.NULL
if there is no such
* attribute.
*/
public int
getAttributeNode(int nodeHandle, String namespaceURI,
String name)
{
// %OPT% This is probably slower than it needs to be.
if (null == namespaceURI) {
namespaceURI = "";
}
int type = getNodeType(nodeHandle);
if (DTM.ELEMENT_NODE == type) {
// Assume that attributes immediately follow the element.
int identity = makeNodeIdentity(nodeHandle);
while (DTM.NULL != (identity = getNextNodeIdentity(identity))) {
// Assume this can not be null.
type = _type(identity);
// %REVIEW%
// Should namespace nodes be retrievable DOM-style as attrs?
// If not we need a separate function... which may be desirable
// architecturally, but which is ugly from a code point of view.
// (If we REALLY insist on it, this code should become a subroutine
// of both -- retrieve the node, then test if the type matches
// what you're looking for.)
if (type == DTM.ATTRIBUTE_NODE || type == DTM.NAMESPACE_NODE) {
Node node = lookupNode(identity);
String nodeuri = node.getNamespaceURI();
if (null == nodeuri) {
nodeuri = "";
}
String nodelocalname = node.getLocalName();
if (nodeuri.equals(namespaceURI) && name.equals(nodelocalname)) {
return makeNodeHandle(identity);
}
}
else { // if (DTM.NAMESPACE_NODE != type)
break;
}
}
}
return DTM.NULL;
}
/**
* Get the string-value of a node as a String object
* (see http://www.w3.org/TR/xpath#data-model
* for the definition of a node's string-value).
*
* @param nodeHandle The node ID.
*
* @return A string object that represents the string-value of the given node.
*/
public XMLString
getStringValue(int nodeHandle)
{
int type = getNodeType(nodeHandle);
Node node = getNode(nodeHandle);
// %TBD% If an element only has one text node, we should just use it
// directly.
if (DTM.ELEMENT_NODE == type || DTM.DOCUMENT_NODE == type
|| DTM.DOCUMENT_FRAGMENT_NODE == type) {
FastStringBuffer buf = StringBufferPool.get();
String s;
try {
getNodeData(node, buf);
s = (buf.length() > 0) ? buf.toString() : "";
} finally {
StringBufferPool.free(buf);
}
return m_xstrf.newstr(s);
} else if (TEXT_NODE == type || CDATA_SECTION_NODE == type) {
// If this is a DTM text node, it may be made of multiple DOM text
// nodes -- including navigating into Entity References. DOM2DTM
// records the first node in the sequence and requires that we
// pick up the others when we retrieve the DTM node's value.
//
// %REVIEW% DOM Level 3 is expected to add a "whole text"
// retrieval method which performs this function for us.
FastStringBuffer buf = StringBufferPool.get();
while (node != null) {
buf.append(node.getNodeValue());
node = logicalNextDOMTextNode(node);
}
String s = (buf.length() > 0) ? buf.toString() : "";
StringBufferPool.free(buf);
return m_xstrf.newstr(s);
} else {
return m_xstrf.newstr(node.getNodeValue());
}
}
/**
* Determine if the string-value of a node is whitespace
*
* @param nodeHandle The node Handle.
*
* @return Return true if the given node is whitespace.
*/
public boolean
isWhitespace(int nodeHandle)
{
int type = getNodeType(nodeHandle);
Node node = getNode(nodeHandle);
if (TEXT_NODE == type || CDATA_SECTION_NODE == type) {
// If this is a DTM text node, it may be made of multiple DOM text
// nodes -- including navigating into Entity References. DOM2DTM
// records the first node in the sequence and requires that we
// pick up the others when we retrieve the DTM node's value.
//
// %REVIEW% DOM Level 3 is expected to add a "whole text"
// retrieval method which performs this function for us.
FastStringBuffer buf = StringBufferPool.get();
while (node != null) {
buf.append(node.getNodeValue());
node = logicalNextDOMTextNode(node);
}
boolean b = buf.isWhitespace(0, buf.length());
StringBufferPool.free(buf);
return b;
}
return false;
}
/**
* Retrieve the text content of a DOM subtree, appending it into a
* user-supplied FastStringBuffer object. Note that attributes are
* not considered part of the content of an element.
* * There are open questions regarding whitespace stripping. * Currently we make no special effort in that regard, since the standard * DOM doesn't yet provide DTD-based information to distinguish * whitespace-in-element-context from genuine #PCDATA. Note that we * should probably also consider xml:space if/when we address this. * DOM Level 3 may solve the problem for us. *
* %REVIEW% Actually, since this method operates on the DOM side of the * fence rather than the DTM side, it SHOULDN'T do * any special handling. The DOM does what the DOM does; if you want * DTM-level abstractions, use DTM-level methods. * * @param node Node whose subtree is to be walked, gathering the * contents of all Text or CDATASection nodes. * @param buf FastStringBuffer into which the contents of the text * nodes are to be concatenated. */ protected static void getNodeData(Node node, FastStringBuffer buf) { switch (node.getNodeType()) { case Node.DOCUMENT_FRAGMENT_NODE : case Node.DOCUMENT_NODE : case Node.ELEMENT_NODE : { for (Node child = node.getFirstChild(); null != child; child = child.getNextSibling()) { getNodeData(child, buf); } } break; case Node.TEXT_NODE : case Node.CDATA_SECTION_NODE : case Node.ATTRIBUTE_NODE : // Never a child but might be our starting node buf.append(node.getNodeValue()); break; case Node.PROCESSING_INSTRUCTION_NODE : // warning(XPATHErrorResources.WG_PARSING_AND_PREPARING); break; default : // ignore break; } } /** * Given a node handle, return its DOM-style node name. This will * include names such as #text or #document. * * @param nodeHandle the id of the node. * @return String Name of this node, which may be an empty string. * %REVIEW% Document when empty string is possible... * %REVIEW-COMMENT% It should never be empty, should it? */ public String getNodeName(int nodeHandle) { Node node = getNode(nodeHandle); // Assume non-null. return node.getNodeName(); } /** * Given a node handle, return the XPath node name. This should be * the name as described by the XPath data model, NOT the DOM-style * name. * * @param nodeHandle the id of the node. * @return String Name of this node, which may be an empty string. */ public String getNodeNameX(int nodeHandle) { String name; short type = getNodeType(nodeHandle); switch (type) { case DTM.NAMESPACE_NODE : { Node node = getNode(nodeHandle); // assume not null. name = node.getNodeName(); if (name.startsWith("xmlns:")) { name = QName.getLocalPart(name); } else if (name.equals("xmlns")) { name = ""; } } break; case DTM.ATTRIBUTE_NODE : case DTM.ELEMENT_NODE : case DTM.ENTITY_REFERENCE_NODE : case DTM.PROCESSING_INSTRUCTION_NODE : { Node node = getNode(nodeHandle); // assume not null. name = node.getNodeName(); } break; default : name = ""; } return name; } /** * Given a node handle, return its XPath-style localname. * (As defined in Namespaces, this is the portion of the name after any * colon character). * * @param nodeHandle the id of the node. * @return String Local name of this node. */ public String getLocalName(int nodeHandle) { // if(JJK_NEWCODE) // { int id = makeNodeIdentity(nodeHandle); if (NULL == id) { return null; } Node newnode = m_nodes.get(id); String newname = newnode.getLocalName(); if (null == newname) { // XSLT treats PIs, and possibly other things, as having QNames. String qname = newnode.getNodeName(); if ('#' == qname.charAt(0)) { // Match old default for this function // This conversion may or may not be necessary newname = ""; } else { int index = qname.indexOf(':'); newname = (index < 0) ? qname : qname.substring(index + 1); } } return newname; // } // else // { // String name; // short type = getNodeType(nodeHandle); // switch (type) // { // case DTM.ATTRIBUTE_NODE : // case DTM.ELEMENT_NODE : // case DTM.ENTITY_REFERENCE_NODE : // case DTM.NAMESPACE_NODE : // case DTM.PROCESSING_INSTRUCTION_NODE : // { // Node node = getNode(nodeHandle); // // // assume not null. // name = node.getLocalName(); // // if (null == name) // { // String qname = node.getNodeName(); // int index = qname.indexOf(':'); // // name = (index < 0) ? qname : qname.substring(index + 1); // } // } // break; // default : // name = ""; // } // return name; // } } /** * Given a namespace handle, return the prefix that the namespace decl is * mapping. * Given a node handle, return the prefix used to map to the namespace. * *
%REVIEW% Are you sure you want "" for no prefix?
*%REVIEW-COMMENT% I think so... not totally sure. -sb
* * @param nodeHandle the id of the node. * @return String prefix of this node's name, or "" if no explicit * namespace prefix was given. */ public String getPrefix(int nodeHandle) { String prefix; short type = getNodeType(nodeHandle); switch (type) { case DTM.NAMESPACE_NODE : { Node node = getNode(nodeHandle); // assume not null. String qname = node.getNodeName(); int index = qname.indexOf(':'); prefix = (index < 0) ? "" : qname.substring(index + 1); } break; case DTM.ATTRIBUTE_NODE : case DTM.ELEMENT_NODE : { Node node = getNode(nodeHandle); // assume not null. String qname = node.getNodeName(); int index = qname.indexOf(':'); prefix = (index < 0) ? "" : qname.substring(0, index); } break; default : prefix = ""; } return prefix; } /** * Given a node handle, return its DOM-style namespace URI * (As defined in Namespaces, this is the declared URI which this node's * prefix -- or default in lieu thereof -- was mapped to.) * *%REVIEW% Null or ""? -sb
* * @param nodeHandle the id of the node. * @return String URI value of this node's namespace, or null if no * namespace was resolved. */ public String getNamespaceURI(int nodeHandle) { // if(JJK_NEWCODE) // { int id = makeNodeIdentity(nodeHandle); if (id == NULL) { return null; } Node node = m_nodes.get(id); return node.getNamespaceURI(); // } // else // { // String nsuri; // short type = getNodeType(nodeHandle); // // switch (type) // { // case DTM.ATTRIBUTE_NODE : // case DTM.ELEMENT_NODE : // case DTM.ENTITY_REFERENCE_NODE : // case DTM.NAMESPACE_NODE : // case DTM.PROCESSING_INSTRUCTION_NODE : // { // Node node = getNode(nodeHandle); // // // assume not null. // nsuri = node.getNamespaceURI(); // // // %TBD% Handle DOM1? // } // break; // default : // nsuri = null; // } // // return nsuri; // } } /** Utility function: Given a DOM Text node, determine whether it is * logically followed by another Text or CDATASection node. This may * involve traversing into Entity References. * * %REVIEW% DOM Level 3 is expected to add functionality which may * allow us to retire this. */ private Node logicalNextDOMTextNode(Node n) { Node p = n.getNextSibling(); if (p == null) { // Walk out of any EntityReferenceNodes that ended with text for (n = n.getParentNode(); n != null && ENTITY_REFERENCE_NODE == n.getNodeType(); n = n.getParentNode()) { p = n.getNextSibling(); if (p != null) { break; } } } n = p; while (n != null && ENTITY_REFERENCE_NODE == n.getNodeType()) { // Walk into any EntityReferenceNodes that start with text if (n.hasChildNodes()) { n = n.getFirstChild(); } else { n = n.getNextSibling(); } } if (n != null) { // Found a logical next sibling. Is it text? int ntype = n.getNodeType(); if (TEXT_NODE != ntype && CDATA_SECTION_NODE != ntype) { n = null; } } return n; } /** * Given a node handle, return its node value. This is mostly * as defined by the DOM, but may ignore some conveniences. *
*
* @param nodeHandle The node id.
* @return String Value of this node, or null if not
* meaningful for this node type.
*/
public String
getNodeValue(int nodeHandle)
{
// The _type(nodeHandle) call was taking the lion's share of our
// time, and was wrong anyway since it wasn't coverting handle to
// identity. Inlined it.
int type = _exptype(makeNodeIdentity(nodeHandle));
type = (NULL != type) ? getNodeType(nodeHandle) : NULL;
if (TEXT_NODE != type && CDATA_SECTION_NODE != type) {
return getNode(nodeHandle).getNodeValue();
}
// If this is a DTM text node, it may be made of multiple DOM text
// nodes -- including navigating into Entity References. DOM2DTM
// records the first node in the sequence and requires that we
// pick up the others when we retrieve the DTM node's value.
//
// %REVIEW% DOM Level 3 is expected to add a "whole text"
// retrieval method which performs this function for us.
Node node = getNode(nodeHandle);
Node n = logicalNextDOMTextNode(node);
if (n == null) {
return node.getNodeValue();
}
FastStringBuffer buf = StringBufferPool.get();
buf.append(node.getNodeValue());
while (n != null) {
buf.append(n.getNodeValue());
n = logicalNextDOMTextNode(n);
}
String s = (buf.length() > 0) ? buf.toString() : "";
StringBufferPool.free(buf);
return s;
}
/**
* A document type declaration information item has the following properties:
*
* 1. [system identifier] The system identifier of the external subset, if
* it exists. Otherwise this property has no value.
*
* @return the system identifier String object, or null if there is none.
*/
public String
getDocumentTypeDeclarationSystemIdentifier()
{
Document doc;
if (m_root.getNodeType() == Node.DOCUMENT_NODE) {
doc = (Document) m_root;
} else {
doc = m_root.getOwnerDocument();
}
if (null != doc) {
DocumentType dtd = doc.getDoctype();
if (null != dtd) {
return dtd.getSystemId();
}
}
return null;
}
/**
* Return the public identifier of the external subset,
* normalized as described in 4.2.2 External Entities [XML]. If there is
* no external subset or if it has no public identifier, this property
* has no value.
*
* @return the public identifier String object, or null if there is none.
*/
public String
getDocumentTypeDeclarationPublicIdentifier()
{
Document doc;
if (m_root.getNodeType() == Node.DOCUMENT_NODE) {
doc = (Document) m_root;
} else {
doc = m_root.getOwnerDocument();
}
if (null != doc) {
DocumentType dtd = doc.getDoctype();
if (null != dtd) {
return dtd.getPublicId();
}
}
return null;
}
/**
* Returns the Element
whose ID
is given by
* elementId
. If no such element exists, returns
* DTM.NULL
. Behavior is not defined if more than one element
* has this ID
. Attributes (including those
* with the name "ID") are not of type ID unless so defined by DTD/Schema
* information available to the DTM implementation.
* Implementations that do not know whether attributes are of type ID or
* not are expected to return DTM.NULL
.
*
*
%REVIEW% Presumably IDs are still scoped to a single document, * and this operation searches only within a single document, right? * Wouldn't want collisions between DTMs in the same process.
* * @param elementId The uniqueid
value for an element.
* @return The handle of the matching element.
*/
public int
getElementById(String elementId)
{
Document doc = (m_root.getNodeType() == Node.DOCUMENT_NODE)
? (Document) m_root : m_root.getOwnerDocument();
if (null != doc) {
Node elem = doc.getElementById(elementId);
if (null != elem) {
int elemHandle = getHandleFromNode(elem);
if (DTM.NULL == elemHandle) {
int identity = m_nodes.size() - 1;
while (DTM.NULL != (identity = getNextNodeIdentity(identity))) {
Node node = getNode(identity);
if (node == elem) {
elemHandle = getHandleFromNode(elem);
break;
}
}
}
return elemHandle;
}
}
return DTM.NULL;
}
/**
* The getUnparsedEntityURI function returns the URI of the unparsed
* entity with the specified name in the same document as the context
* node (see [3.3 Unparsed Entities]). It returns the empty string if
* there is no such entity.
* * XML processors may choose to use the System Identifier (if one * is provided) to resolve the entity, rather than the URI in the * Public Identifier. The details are dependent on the processor, and * we would have to support some form of plug-in resolver to handle * this properly. Currently, we simply return the System Identifier if * present, and hope that it a usable URI or that our caller can * map it to one. * TODO: Resolve Public Identifiers... or consider changing function name. *
* If we find a relative URI * reference, XML expects it to be resolved in terms of the base URI * of the document. The DOM doesn't do that for us, and it isn't * entirely clear whether that should be done here; currently that's * pushed up to a higher level of our application. (Note that DOM Level * 1 didn't store the document's base URI.) * TODO: Consider resolving Relative URIs. *
* (The DOM's statement that "An XML processor may choose to
* completely expand entities before the structure model is passed
* to the DOM" refers only to parsed entities, not unparsed, and hence
* doesn't affect this function.)
*
* @param name A string containing the Entity Name of the unparsed
* entity.
*
* @return String containing the URI of the Unparsed Entity, or an
* empty string if no such entity exists.
*/
public String
getUnparsedEntityURI(String name)
{
String url = "";
Document doc = (m_root.getNodeType() == Node.DOCUMENT_NODE)
? (Document) m_root : m_root.getOwnerDocument();
if (null != doc) {
DocumentType doctype = doc.getDoctype();
if (null != doctype) {
NamedNodeMap entities = doctype.getEntities();
if (null == entities) {
return url;
}
Entity entity = (Entity) entities.getNamedItem(name);
if (null == entity) {
return url;
}
String notationName = entity.getNotationName();
if (null != notationName) { // then it's unparsed
// The draft says: "The XSLT processor may use the public
// identifier to generate a URI for the entity instead of the URI
// specified in the system identifier. If the XSLT processor does
// not use the public identifier to generate the URI, it must use
// the system identifier; if the system identifier is a relative
// URI, it must be resolved into an absolute URI using the URI of
// the resource containing the entity declaration as the base
// URI [RFC2396]."
// So I'm falling a bit short here.
url = entity.getSystemId();
if (null == url) {
url = entity.getPublicId();
} else {
// This should be resolved to an absolute URL, but that's hard
// to do from here.
}
}
}
}
return url;
}
/**
* 5. [specified] A flag indicating whether this attribute was actually
* specified in the start-tag of its element, or was defaulted from the
* DTD.
*
* @param attributeHandle the attribute handle
* @return true
if the attribute was specified;
* false
if it was defaulted.
*/
public boolean
isAttributeSpecified(int attributeHandle)
{
int type = getNodeType(attributeHandle);
if (DTM.ATTRIBUTE_NODE == type) {
Attr attr = (Attr)getNode(attributeHandle);
return attr.getSpecified();
}
return false;
}
/** Bind an IncrementalSAXSource to this DTM. NOT RELEVANT for DOM2DTM, since
* we're wrapped around an existing DOM.
*
* @param source The IncrementalSAXSource that we want to recieve events from
* on demand.
*/
public void
setIncrementalSAXSource(IncrementalSAXSource source)
{
}
/** getContentHandler returns "our SAX builder" -- the thing that
* someone else should send SAX events to in order to extend this
* DTM model.
*
* @return null if this model doesn't respond to SAX events,
* "this" if the DTM object has a built-in SAX ContentHandler,
* the IncrmentalSAXSource if we're bound to one and should receive
* the SAX stream via it for incremental build purposes...
* */
public org.xml.sax.ContentHandler
getContentHandler()
{
return null;
}
/**
* Return this DTM's lexical handler.
*
* %REVIEW% Should this return null if constrution already done/begun?
*
* @return null if this model doesn't respond to lexical SAX events,
* "this" if the DTM object has a built-in SAX ContentHandler,
* the IncrementalSAXSource if we're bound to one and should receive
* the SAX stream via it for incremental build purposes...
*/
public org.xml.sax.ext.LexicalHandler
getLexicalHandler()
{
return null;
}
/**
* Return this DTM's EntityResolver.
*
* @return null if this model doesn't respond to SAX entity ref events.
*/
public org.xml.sax.EntityResolver
getEntityResolver()
{
return null;
}
/**
* Return this DTM's DTDHandler.
*
* @return null if this model doesn't respond to SAX dtd events.
*/
public org.xml.sax.DTDHandler
getDTDHandler()
{
return null;
}
/**
* Return this DTM's ErrorHandler.
*
* @return null if this model doesn't respond to SAX error events.
*/
public org.xml.sax.ErrorHandler
getErrorHandler()
{
return null;
}
/**
* Return this DTM's DeclHandler.
*
* @return null if this model doesn't respond to SAX Decl events.
*/
public org.xml.sax.ext.DeclHandler
getDeclHandler()
{
return null;
}
/** @return true iff we're building this model incrementally (eg
* we're partnered with a IncrementalSAXSource) and thus require that the
* transformation and the parse run simultaneously. Guidance to the
* DTMManager.
* */
public boolean
needsTwoThreads()
{
return false;
}
// ========== Direct SAX Dispatch, for optimization purposes ========
/**
* Returns whether the specified ch conforms to the XML 1.0 definition
* of whitespace. Refer to
* the definition of S
for details.
* @param ch Character to check as XML whitespace.
* @return =true if ch is XML whitespace; otherwise =false.
*/
private static boolean
isSpace(char ch)
{
return XMLCharacterRecognizer.isWhiteSpace(ch); // Take the easy way out for now.
}
/**
* Directly call the
* characters method on the passed ContentHandler for the
* string-value of the given node (see http://www.w3.org/TR/xpath#data-model
* for the definition of a node's string-value). Multiple calls to the
* ContentHandler's characters methods may well occur for a single call to
* this method.
*
* @param nodeHandle The node ID.
* @param ch A non-null reference to a ContentHandler.
*
* @throws org.xml.sax.SAXException
*/
public void
dispatchCharactersEvents(
int nodeHandle, org.xml.sax.ContentHandler ch,
boolean normalize)
throws org.xml.sax.SAXException
{
if (normalize) {
XMLString str = getStringValue(nodeHandle);
str = str.fixWhiteSpace(true, true, false);
str.dispatchCharactersEvents(ch);
} else {
int type = getNodeType(nodeHandle);
Node node = getNode(nodeHandle);
dispatchNodeData(node, ch, 0);
// Text coalition -- a DTM text node may represent multiple
// DOM nodes.
if (TEXT_NODE == type || CDATA_SECTION_NODE == type) {
while (null != (node = logicalNextDOMTextNode(node))) {
dispatchNodeData(node, ch, 0);
}
}
}
}
/**
* Retrieve the text content of a DOM subtree, appending it into a
* user-supplied FastStringBuffer object. Note that attributes are
* not considered part of the content of an element.
*
* There are open questions regarding whitespace stripping. * Currently we make no special effort in that regard, since the standard * DOM doesn't yet provide DTD-based information to distinguish * whitespace-in-element-context from genuine #PCDATA. Note that we * should probably also consider xml:space if/when we address this. * DOM Level 3 may solve the problem for us. *
* %REVIEW% Note that as a DOM-level operation, it can be argued that this
* routine _shouldn't_ perform any processing beyond what the DOM already
* does, and that whitespace stripping and so on belong at the DTM level.
* If you want a stripped DOM view, wrap DTM2DOM around DOM2DTM.
*
* @param node Node whose subtree is to be walked, gathering the
* contents of all Text or CDATASection nodes.
*/
@SuppressWarnings("fallthrough")
protected static void
dispatchNodeData(Node node,
org.xml.sax.ContentHandler ch,
int depth)
throws org.xml.sax.SAXException
{
switch (node.getNodeType()) {
case Node.DOCUMENT_FRAGMENT_NODE :
case Node.DOCUMENT_NODE :
case Node.ELEMENT_NODE : {
for (Node child = node.getFirstChild(); null != child;
child = child.getNextSibling()) {
dispatchNodeData(child, ch, depth + 1);
}
}
break;
case Node.PROCESSING_INSTRUCTION_NODE : // %REVIEW%
case Node.COMMENT_NODE :
if (0 != depth) {
break;
}
// NOTE: Because this operation works in the DOM space, it does _not_ attempt
// to perform Text Coalition. That should only be done in DTM space.
case Node.TEXT_NODE :
case Node.CDATA_SECTION_NODE :
case Node.ATTRIBUTE_NODE :
String str = node.getNodeValue();
if (ch instanceof org.apache.xml.dtm.ref.dom2dtm.DOM2DTM.CharacterNodeHandler) {
((org.apache.xml.dtm.ref.dom2dtm.DOM2DTM.CharacterNodeHandler)ch).characters(node);
} else {
ch.characters(str.toCharArray(), 0, str.length());
}
break;
// /* case Node.PROCESSING_INSTRUCTION_NODE :
// // warning(XPATHErrorResources.WG_PARSING_AND_PREPARING);
// break; */
default :
// ignore
break;
}
}
TreeWalker m_walker = new TreeWalker(null);
/**
* Directly create SAX parser events from a subtree.
*
* @param nodeHandle The node ID.
* @param ch A non-null reference to a ContentHandler.
*
* @throws org.xml.sax.SAXException
*/
public void
dispatchToEvents(int nodeHandle, org.xml.sax.ContentHandler ch)
throws org.xml.sax.SAXException
{
TreeWalker treeWalker = m_walker;
ContentHandler prevCH = treeWalker.getContentHandler();
if (null != prevCH) {
treeWalker = new TreeWalker(null);
}
treeWalker.setContentHandler(ch);
try {
Node node = getNode(nodeHandle);
treeWalker.traverseFragment(node);
} finally {
treeWalker.setContentHandler(null);
}
}
/**
* For the moment all the run time properties are ignored by this
* class.
*
* @param property a String
value
* @param value an Object
value
*/
public void
setProperty(String property, Object value)
{
}
/**
* No source information is available for DOM2DTM, so return
* null
here.
*
* @param node an int
value
* @return null
*/
public SourceLocator
getSourceLocatorFor(int node)
{
return null;
}
}