- /*
 - * The Apache Software License, Version 1.1
 - *
 - *
 - * Copyright (c) 1999 The Apache Software Foundation. All rights
 - * reserved.
 - *
 - * Redistribution and use in source and binary forms, with or without
 - * modification, are permitted provided that the following conditions
 - * are met:
 - *
 - * 1. Redistributions of source code must retain the above copyright
 - * notice, this list of conditions and the following disclaimer.
 - *
 - * 2. Redistributions in binary form must reproduce the above copyright
 - * notice, this list of conditions and the following disclaimer in
 - * the documentation and/or other materials provided with the
 - * distribution.
 - *
 - * 3. The end-user documentation included with the redistribution,
 - * if any, must include the following acknowledgment:
 - * "This product includes software developed by the
 - * Apache Software Foundation (http://www.apache.org/)."
 - * Alternately, this acknowledgment may appear in the software itself,
 - * if and wherever such third-party acknowledgments normally appear.
 - *
 - * 4. The names "Xalan" and "Apache Software Foundation" must
 - * not be used to endorse or promote products derived from this
 - * software without prior written permission. For written
 - * permission, please contact apache@apache.org.
 - *
 - * 5. Products derived from this software may not be called "Apache",
 - * nor may "Apache" appear in their name, without prior written
 - * permission of the Apache Software Foundation.
 - *
 - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 - * SUCH DAMAGE.
 - * ====================================================================
 - *
 - * This software consists of voluntary contributions made by many
 - * individuals on behalf of the Apache Software Foundation and was
 - * originally based on software copyright (c) 1999, Lotus
 - * Development Corporation., http://www.lotus.com. For more
 - * information on the Apache Software Foundation, please see
 - * <http://www.apache.org/>.
 - */
 - package org.apache.xml.dtm.ref.dom2dtm;
 - import org.apache.xml.dtm.ref.*;
 - import org.apache.xml.dtm.*;
 - import org.apache.xml.utils.SuballocatedIntVector;
 - import org.apache.xml.utils.IntStack;
 - import org.apache.xml.utils.BoolStack;
 - import org.apache.xml.utils.StringBufferPool;
 - import org.apache.xml.utils.FastStringBuffer;
 - import org.apache.xml.utils.TreeWalker;
 - import org.apache.xml.utils.QName;
 - import org.apache.xml.utils.XMLCharacterRecognizer;
 - import org.w3c.dom.*;
 - import java.util.Vector;
 - import javax.xml.transform.dom.DOMSource;
 - import javax.xml.transform.SourceLocator;
 - import org.xml.sax.ContentHandler;
 - import org.apache.xml.utils.NodeVector;
 - import org.apache.xml.utils.XMLString;
 - import org.apache.xml.utils.XMLStringFactory;
 - import org.apache.xalan.res.XSLTErrorResources;
 - import org.apache.xalan.res.XSLMessages;
 - /** The <code>DOM2DTM</code> 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 Vector m_nodes = new Vector();
 - /**
 - * Construct a DOM2DTM object from a DOM node.
 - *
 - * @param mgr The DTMManager who owns this DTM.
 - * @param domSource the DOM source that this DTM will wrap.
 - * @param dtmIdentity The DTM identity ID for this DTM.
 - * @param whiteSpaceFilter The white space filter for this DTM, which may
 - * be null.
 - * @param xstringfactory XMLString factory for creating character content.
 - * @param doIndexing true if the caller considers it worth it to use
 - * indexing schemes.
 - */
 - public DOM2DTM(DTMManager mgr, DOMSource domSource,
 - int dtmIdentity, DTMWSFilter whiteSpaceFilter,
 - XMLStringFactory xstringfactory,
 - boolean doIndexing)
 - {
 - super(mgr, domSource, dtmIdentity, whiteSpaceFilter,
 - xstringfactory, doIndexing);
 - // Initialize DOM navigation
 - m_pos=m_root = domSource.getNode();
 - // Initialize DTM navigation
 - m_last_parent=m_last_kid=NULL;
 - m_last_kid=addNode(m_root, m_last_parent,m_last_kid, NULL);
 - // Apparently the domSource root may not actually be the
 - // Document node. If it's an Element node, we need to immediately
 - // add its attributes. Adapted from nextNode().
 - // %REVIEW% Move this logic into addNode and recurse? Cleaner!
 - //
 - // (If it's an EntityReference node, we're probably scrod. For now
 - // I'm just hoping nobody is ever quite that foolish... %REVIEW%)
 - //
 - // %ISSUE% What about inherited namespaces in this case?
 - // Do we need to special-case initialize them into the DTM model?
 - if(ELEMENT_NODE == m_root.getNodeType())
 - {
 - NamedNodeMap attrs=m_root.getAttributes();
 - int attrsize=(attrs==null) ? 0 : attrs.getLength();
 - if(attrsize>0)
 - {
 - int attrIndex=NULL; // start with no previous sib
 - for(int i=0;i<attrsize;++i)
 - {
 - // No need to force nodetype in this case;
 - // addNode() will take care of switching it from
 - // Attr to Namespace if necessary.
 - attrIndex=addNode(attrs.item(i),0,attrIndex,NULL);
 - m_firstch.setElementAt(DTM.NULL,attrIndex);
 - }
 - // Terminate list of attrs, and make sure they aren't
 - // considered children of the element
 - m_nextsib.setElementAt(DTM.NULL,attrIndex);
 - // IMPORTANT: This does NOT change m_last_parent or m_last_kid!
 - } // if attrs exist
 - } //if(ELEMENT_NODE)
 - // Initialize DTM-completed status
 - m_nodesAreProcessed = false;
 - }
 - /**
 - * Construct the node map from the node.
 - *
 - * @param node The node that is to be added to the DTM.
 - * @param parentIndex The current parent index.
 - * @param previousSibling The previous sibling index.
 - * @param forceNodeType If not DTM.NULL, overrides the DOM node type.
 - * Used to force nodes to Text rather than CDATASection when their
 - * coalesced value includes ordinary Text nodes (current DTM behavior).
 - *
 - * @return The index identity of the node that was added.
 - */
 - protected int addNode(Node node, int parentIndex,
 - int previousSibling, int forceNodeType)
 - {
 - int nodeIndex = m_nodes.size();
 - // Have we overflowed a DTM Identity's addressing range?
 - if(m_dtmIdent.size() == (nodeIndex>>>DTMManager.IDENT_DTM_NODE_BITS))
 - {
 - try
 - {
 - if(m_mgr==null)
 - throw new ClassCastException();
 - // Handle as Extended Addressing
 - DTMManagerDefault mgrD=(DTMManagerDefault)m_mgr;
 - int id=mgrD.getFirstFreeDTMID();
 - mgrD.addDTM(this,id,nodeIndex);
 - m_dtmIdent.addElement(id<<DTMManager.IDENT_DTM_NODE_BITS);
 - }
 - catch(ClassCastException e)
 - {
 - // %REVIEW% Wrong error message, but I've been told we're trying
 - // not to add messages right not for I18N reasons.
 - // %REVIEW% Should this be a Fatal Error?
 - error(XSLMessages.createMessage(XSLTErrorResources.ER_NO_DTMIDS_AVAIL, null));//"No more DTM IDs are available";
 - }
 - }
 - m_size++;
 - // ensureSize(nodeIndex);
 - int type;
 - if(NULL==forceNodeType)
 - type = node.getNodeType();
 - else
 - type=forceNodeType;
 - // %REVIEW% The Namespace Spec currently says that Namespaces are
 - // processed in a non-namespace-aware manner, by matching the
 - // QName, even though there is in fact a namespace assigned to
 - // these nodes in the DOM. If and when that changes, we will have
 - // to consider whether we check the namespace-for-namespaces
 - // rather than the node name.
 - //
 - // %TBD% Note that the DOM does not necessarily explicitly declare
 - // all the namespaces it uses. DOM Level 3 will introduce a
 - // namespace-normalization operation which reconciles that, and we
 - // can request that users invoke it or otherwise ensure that the
 - // tree is namespace-well-formed before passing the DOM to Xalan.
 - // But if they don't, what should we do about it? We probably
 - // don't want to alter the source DOM (and may not be able to do
 - // so if it's read-only). The best available answer might be to
 - // synthesize additional DTM Namespace Nodes that don't correspond
 - // to DOM Attr Nodes.
 - if (Node.ATTRIBUTE_NODE == type)
 - {
 - String name = node.getNodeName();
 - if (name.startsWith("xmlns:") || name.equals("xmlns"))
 - {
 - type = DTM.NAMESPACE_NODE;
 - }
 - }
 - m_nodes.addElement(node);
 - m_firstch.setElementAt(NOTPROCESSED,nodeIndex);
 - m_nextsib.setElementAt(NOTPROCESSED,nodeIndex);
 - m_prevsib.setElementAt(previousSibling,nodeIndex);
 - m_parent.setElementAt(parentIndex,nodeIndex);
 - if(DTM.NULL != parentIndex &&
 - type != DTM.ATTRIBUTE_NODE &&
 - type != DTM.NAMESPACE_NODE)
 - {
 - // If the DTM parent had no children, this becomes its first child.
 - if(NOTPROCESSED == m_firstch.elementAt(parentIndex))
 - m_firstch.setElementAt(nodeIndex,parentIndex);
 - }
 - String nsURI = node.getNamespaceURI();
 - // Deal with the difference between Namespace spec and XSLT
 - // definitions of local name. (The former says PIs don't have
 - // localnames; the latter says they do.)
 - String localName = (type == Node.PROCESSING_INSTRUCTION_NODE) ?
 - node.getNodeName() :
 - node.getLocalName();
 - // Hack to make DOM1 sort of work...
 - if(((type == Node.ELEMENT_NODE) || (type == Node.ATTRIBUTE_NODE))
 - && null == localName)
 - localName = node.getNodeName(); // -sb
 - ExpandedNameTable exnt = m_expandedNameTable;
 - // %TBD% Nodes created with the old non-namespace-aware DOM
 - // calls createElement() and createAttribute() will never have a
 - // localname. That will cause their expandedNameID to be just the
 - // nodeType... which will keep them from being matched
 - // successfully by name. Since the DOM makes no promise that
 - // those will participate in namespace processing, this is
 - // officially accepted as Not Our Fault. But it might be nice to
 - // issue a diagnostic message!
 - if(node.getLocalName()==null &&
 - (type==Node.ELEMENT_NODE || type==Node.ATTRIBUTE_NODE))
 - {
 - // warning("DOM 'level 1' node "+node.getNodeName()+" won't be mapped properly in DOM2DTM.");
 - }
 - int expandedNameID = (null != localName)
 - ? exnt.getExpandedTypeID(nsURI, localName, type) :
 - exnt.getExpandedTypeID(type);
 - m_exptype.setElementAt(expandedNameID,nodeIndex);
 - indexNode(expandedNameID, nodeIndex);
 - if (DTM.NULL != previousSibling)
 - m_nextsib.setElementAt(nodeIndex,previousSibling);
 - // This should be done after m_exptype has been set, and probably should
 - // always be the last thing we do
 - if (type == DTM.NAMESPACE_NODE)
 - declareNamespaceInContext(parentIndex,nodeIndex);
 - return nodeIndex;
 - }
 - /**
 - * Get the number of nodes that have been added.
 - */
 - protected int getNumberOfNodes()
 - {
 - return m_nodes.size();
 - }
 - /**
 - * This method iterates to the next node that will be added to the table.
 - * Each call to this method adds a new node to the table, unless the end
 - * is reached, in which case it returns null.
 - *
 - * @return The true if a next node is found or false if
 - * there are no more nodes.
 - */
 - protected boolean nextNode()
 - {
 - // Non-recursive one-fetch-at-a-time depth-first traversal with
 - // attribute/namespace nodes and white-space stripping.
 - // Navigating the DOM is simple, navigating the DTM is simple;
 - // keeping track of both at once is a trifle baroque but at least
 - // we've avoided most of the special cases.
 - if (m_nodesAreProcessed)
 - return false;
 - // %REVIEW% Is this local copy Really Useful from a performance
 - // point of view? Or is this a false microoptimization?
 - Node pos=m_pos;
 - Node next=null;
 - int nexttype=NULL;
 - // Navigate DOM tree
 - do
 - {
 - // Look down to first child.
 - if (pos.hasChildNodes())
 - {
 - next = pos.getFirstChild();
 - // %REVIEW% There's probably a more elegant way to skip
 - // the doctype. (Just let it go and Suppress it?
 - if(next!=null && DOCUMENT_TYPE_NODE==next.getNodeType())
 - next=next.getNextSibling();
 - // Push DTM context -- except for children of Entity References,
 - // which have no DTM equivalent and cause no DTM navigation.
 - if(ENTITY_REFERENCE_NODE!=pos.getNodeType())
 - {
 - m_last_parent=m_last_kid;
 - m_last_kid=NULL;
 - // Whitespace-handler context stacking
 - if(null != m_wsfilter)
 - {
 - short wsv =
 - m_wsfilter.getShouldStripSpace(makeNodeHandle(m_last_parent),this);
 - boolean shouldStrip = (DTMWSFilter.INHERIT == wsv)
 - ? getShouldStripWhitespace()
 - : (DTMWSFilter.STRIP == wsv);
 - pushShouldStripWhitespace(shouldStrip);
 - } // if(m_wsfilter)
 - }
 - }
 - // If that fails, look up and right (but not past root!)
 - else
 - {
 - if(m_last_kid!=NULL)
 - {
 - // Last node posted at this level had no more children
 - // If it has _no_ children, we need to record that.
 - if(m_firstch.elementAt(m_last_kid)==NOTPROCESSED)
 - m_firstch.setElementAt(NULL,m_last_kid);
 - }
 - while(m_last_parent != NULL)
 - {
 - // %REVIEW% There's probably a more elegant way to
 - // skip the doctype. (Just let it go and Suppress it?
 - next = pos.getNextSibling();
 - if(next!=null && DOCUMENT_TYPE_NODE==next.getNodeType())
 - next=next.getNextSibling();
 - if(next!=null)
 - break; // Found it!
 - // No next-sibling found. Pop the DOM.
 - pos=pos.getParentNode();
 - if(pos==null)
 - {
 - // %TBD% Should never arise, but I want to be sure of that...
 - if(JJK_DEBUG)
 - {
 - System.out.println("***** DOM2DTM Pop Control Flow problem");
 - for(;;); // Freeze right here!
 - }
 - }
 - // The only parents in the DTM are Elements. However,
 - // the DOM could contain EntityReferences. If we
 - // encounter one, pop it _without_ popping DTM.
 - if(pos!=null && ENTITY_REFERENCE_NODE == pos.getNodeType())
 - {
 - // Nothing needs doing
 - if(JJK_DEBUG)
 - System.out.println("***** DOM2DTM popping EntRef");
 - }
 - else
 - {
 - popShouldStripWhitespace();
 - // Fix and pop DTM
 - if(m_last_kid==NULL)
 - m_firstch.setElementAt(NULL,m_last_parent); // Popping from an element
 - else
 - m_nextsib.setElementAt(NULL,m_last_kid); // Popping from anything else
 - m_last_parent=m_parent.elementAt(m_last_kid=m_last_parent);
 - }
 - }
 - if(m_last_parent==NULL)
 - next=null;
 - }
 - if(next!=null)
 - nexttype=next.getNodeType();
 - // If it's an entity ref, advance past it.
 - //
 - // %REVIEW% Should we let this out the door and just suppress it?
 - // More work, but simpler code, more likely to be correct, and
 - // it doesn't happen very often. We'd get rid of the loop too.
 - if (ENTITY_REFERENCE_NODE == nexttype)
 - pos=next;
 - }
 - while (ENTITY_REFERENCE_NODE == nexttype);
 - // Did we run out of the tree?
 - if(next==null)
 - {
 - m_nextsib.setElementAt(NULL,0);
 - m_nodesAreProcessed = true;
 - m_pos=null;
 - if(JJK_DEBUG)
 - {
 - System.out.println("***** DOM2DTM Crosscheck:");
 - for(int i=0;i<m_nodes.size();++i)
 - System.out.println(i+":\t"+m_firstch.elementAt(i)+"\t"+m_nextsib.elementAt(i));
 - }
 - return false;
 - }
 - // Text needs some special handling:
 - //
 - // DTM may skip whitespace. This is handled by the suppressNode flag, which
 - // when true will keep the DTM node from being created.
 - //
 - // DTM only directly records the first DOM node of any logically-contiguous
 - // sequence. The lastTextNode value will be set to the last node in the
 - // contiguous sequence, and -- AFTER the DTM addNode -- can be used to
 - // advance next over this whole block. Should be simpler than special-casing
 - // the above loop for "Was the logically-preceeding sibling a text node".
 - //
 - // Finally, a DTM node should be considered a CDATASection only if all the
 - // contiguous text it covers is CDATASections. The first Text should
 - // force DTM to Text.
 - boolean suppressNode=false;
 - Node lastTextNode=null;
 - nexttype=next.getNodeType();
 - // nexttype=pos.getNodeType();
 - if(TEXT_NODE == nexttype || CDATA_SECTION_NODE == nexttype)
 - {
 - // If filtering, initially assume we're going to suppress the node
 - suppressNode=((null != m_wsfilter) && getShouldStripWhitespace());
 - // Scan logically contiguous text (siblings, plus "flattening"
 - // of entity reference boundaries).
 - Node n=next;
 - while(n!=null)
 - {
 - lastTextNode=n;
 - // Any Text node means DTM considers it all Text
 - if(TEXT_NODE == n.getNodeType())
 - nexttype=TEXT_NODE;
 - // Any non-whitespace in this sequence blocks whitespace
 - // suppression
 - suppressNode &=
 - XMLCharacterRecognizer.isWhiteSpace(n.getNodeValue());
 - n=logicalNextDOMTextNode(n);
 - }
 - }
 - // Special handling for PIs: Some DOMs represent the XML
 - // Declaration as a PI. This is officially incorrect, per the DOM
 - // spec, but is considered a "wrong but tolerable" temporary
 - // workaround pending proper handling of these fields in DOM Level
 - // 3. We want to recognize and reject that case.
 - else if(PROCESSING_INSTRUCTION_NODE==nexttype)
 - {
 - suppressNode = (pos.getNodeName().toLowerCase().equals("xml"));
 - }
 - if(!suppressNode)
 - {
 - // Inserting next. NOTE that we force the node type; for
 - // coalesced Text, this records CDATASections adjacent to
 - // ordinary Text as Text.
 - int nextindex=addNode(next,m_last_parent,m_last_kid,
 - nexttype);
 - m_last_kid=nextindex;
 - if(ELEMENT_NODE == nexttype)
 - {
 - int attrIndex=NULL; // start with no previous sib
 - // Process attributes _now_, rather than waiting.
 - // Simpler control flow, makes NS cache available immediately.
 - NamedNodeMap attrs=next.getAttributes();
 - int attrsize=(attrs==null) ? 0 : attrs.getLength();
 - if(attrsize>0)
 - {
 - for(int i=0;i<attrsize;++i)
 - {
 - // No need to force nodetype in this case;
 - // addNode() will take care of switching it from
 - // Attr to Namespace if necessary.
 - attrIndex=addNode(attrs.item(i),
 - nextindex,attrIndex,NULL);
 - m_firstch.setElementAt(DTM.NULL,attrIndex);
 - // If the xml: prefix is explicitly declared
 - // we don't need to synthesize one.
 - //
 - // NOTE that XML Namespaces were not originally
 - // defined as being namespace-aware (grrr), and
 - // while the W3C is planning to fix this it's
 - // safer for now to test the QName and trust the
 - // parsers to prevent anyone from redefining the
 - // reserved xmlns: prefix
 - if(!m_processedFirstElement
 - && "xmlns:xml".equals(attrs.item(i).getNodeName()))
 - m_processedFirstElement=true;
 - }
 - // Terminate list of attrs, and make sure they aren't
 - // considered children of the element
 - } // if attrs exist
 - if(!m_processedFirstElement)
 - {
 - // The DOM might not have an explicit declaration for the
 - // implicit "xml:" prefix, but the XPath data model
 - // requires that this appear as a Namespace Node so we
 - // have to synthesize one. You can think of this as
 - // being a default attribute defined by the XML
 - // Namespaces spec rather than by the DTD.
 - attrIndex=addNode(new DOM2DTMdefaultNamespaceDeclarationNode(
 - (Element)next,"xml",NAMESPACE_DECL_NS,
 - makeNodeHandle(((attrIndex==NULL)?nextindex:attrIndex)+1)
 - ),
 - nextindex,attrIndex,NULL);
 - m_firstch.setElementAt(DTM.NULL,attrIndex);
 - m_processedFirstElement=true;
 - }
 - if(attrIndex!=NULL)
 - m_nextsib.setElementAt(DTM.NULL,attrIndex);
 - } //if(ELEMENT_NODE)
 - } // (if !suppressNode)
 - // Text postprocessing: Act on values stored above
 - if(TEXT_NODE == nexttype || CDATA_SECTION_NODE == nexttype)
 - {
 - // %TBD% If nexttype was forced to TEXT, patch the DTM node
 - next=lastTextNode; // Advance the DOM cursor over contiguous text
 - }
 - // Remember where we left off.
 - m_pos=next;
 - return true;
 - }
 - /**
 - * Return an DOM node for the given node.
 - *
 - * @param nodeHandle The node ID.
 - *
 - * @return A node representation of the DTM node.
 - */
 - public Node getNode(int nodeHandle)
 - {
 - int identity = makeNodeIdentity(nodeHandle);
 - return (Node) m_nodes.elementAt(identity);
 - }
 - /**
 - * Get a Node from an identity index.
 - *
 - * NEEDSDOC @param nodeIdentity
 - *
 - * NEEDSDOC ($objectName$) @return
 - */
 - protected Node lookupNode(int nodeIdentity)
 - {
 - return (Node) m_nodes.elementAt(nodeIdentity);
 - }
 - /**
 - * Get the next node identity value in the list, and call the iterator
 - * if it hasn't been added yet.
 - *
 - * @param identity The node identity (index).
 - * @return identity+1, or DTM.NULL.
 - */
 - protected int getNextNodeIdentity(int identity)
 - {
 - identity += 1;
 - if (identity >= m_nodes.size())
 - {
 - if (!nextNode())
 - identity = DTM.NULL;
 - }
 - return identity;
 - }
 - /**
 - * Get the handle from a Node.
 - * <p>%OPT% This will be pretty slow.</p>
 - *
 - * <p>%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.</p>
 - *
 - * %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 or <code>DTM.NULL</code>.
 - */
 - private 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.elementAt(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.
 - *
 - * <p>%OPT% This will be pretty slow.</p>
 - *
 - * %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 or <code>DTM.NULL</code>. */
 - 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 (
 - * <code>nodeName</code>) or <code>DTM.NULL</code> 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() );
 - }
 - /**
 - * 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.
 - * <p>
 - * 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.
 - * <p>
 - * %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=(Node)m_nodes.elementAt(id);
 - String newname=newnode.getLocalName();
 - if (null == newname)
 - {
 - // XSLT treats PIs, and possibly other things, as having QNames.
 - String qname = newnode.getNodeName();
 - if('#'==newnode.getNodeName().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.
 - *
 - * <p> %REVIEW% Are you sure you want "" for no prefix? </p>
 - * <p> %REVIEW-COMMENT% I think so... not totally sure. -sb </p>
 - *
 - * @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.)
 - *
 - * <p>%REVIEW% Null or ""? -sb</p>
 - *
 - * @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=(Node)m_nodes.elementAt(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.
 - * <p>
 - *
 - * @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.
 - *
 - * @param the document type declaration handle
 - *
 - * @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 <code>Element</code> whose <code>ID</code> is given by
 - * <code>elementId</code>. If no such element exists, returns
 - * <code>DTM.NULL</code>. Behavior is not defined if more than one element
 - * has this <code>ID</code>. 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 <code>DTM.NULL</code>.
 - *
 - * <p>%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.</p>
 - *
 - * @param elementId The unique <code>id</code> 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.
 - * <p>
 - * 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.
 - * <p>
 - * 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.
 - * <p>
 - * (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 the attribute handle
 - *
 - * NEEDSDOC @param attributeHandle
 - * @return <code>true</code> if the attribute was specified;
 - * <code>false</code> 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 <var>ch</var> conforms to the XML 1.0 definition
 - * of whitespace. Refer to <A href="http://www.w3.org/TR/1998/REC-xml-19980210#NT-S">
 - * the definition of <CODE>S</CODE></A> for details.
 - * @param ch Character to check as XML whitespace.
 - * @return =true if <var>ch</var> 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.
 - * <p>
 - * 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.
 - * <p>
 - * %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.
 - * @param buf FastStringBuffer into which the contents of the text
 - * nodes are to be concatenated.
 - */
 - 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 CharacterNodeHandler)
 - {
 - ((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.traverse(node);
 - }
 - finally
 - {
 - treeWalker.setContentHandler(null);
 - }
 - }
 - public interface CharacterNodeHandler
 - {
 - public void characters(Node node)
 - throws org.xml.sax.SAXException;
 - }
 - /**
 - * For the moment all the run time properties are ignored by this
 - * class.
 - *
 - * @param property a <code>String</code> value
 - * @param value an <code>Object</code> value
 - */
 - public void setProperty(String property, Object value)
 - {
 - }
 - /**
 - * No source information is available for DOM2DTM, so return
 - * <code>null</code> here.
 - *
 - * @param node an <code>int</code> value
 - * @return null
 - */
 - public SourceLocator getSourceLocatorFor(int node)
 - {
 - return null;
 - }
 - }