|
|
|
|
|
*/ |
|
/* |
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
|
* contributor license agreements. See the NOTICE file distributed with |
|
* this work for additional information regarding copyright ownership. |
|
* The ASF licenses this file to You under the Apache License, Version 2.0 |
|
* (the "License"); you may not use this file except in compliance with |
|
* the License. You may obtain a copy of the License at |
|
* |
|
* http://www.apache.org/licenses/LICENSE-2.0 |
|
* |
|
* Unless required by applicable law or agreed to in writing, software |
|
* distributed under the License is distributed on an "AS IS" BASIS, |
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
* See the License for the specific language governing permissions and |
|
* limitations under the License. |
|
*/ |
|
|
|
package com.sun.org.apache.xml.internal.dtm.ref.dom2dtm; |
|
|
|
import com.sun.org.apache.xml.internal.dtm.DTM; |
|
import com.sun.org.apache.xml.internal.dtm.DTMManager; |
|
import com.sun.org.apache.xml.internal.dtm.DTMWSFilter; |
|
import com.sun.org.apache.xml.internal.dtm.ref.DTMDefaultBaseIterators; |
|
import com.sun.org.apache.xml.internal.dtm.ref.DTMManagerDefault; |
|
import com.sun.org.apache.xml.internal.dtm.ref.ExpandedNameTable; |
|
import com.sun.org.apache.xml.internal.dtm.ref.IncrementalSAXSource; |
|
import com.sun.org.apache.xml.internal.res.XMLErrorResources; |
|
import com.sun.org.apache.xml.internal.res.XMLMessages; |
|
import com.sun.org.apache.xml.internal.utils.FastStringBuffer; |
|
import com.sun.org.apache.xml.internal.utils.QName; |
|
import com.sun.org.apache.xml.internal.utils.StringBufferPool; |
|
import com.sun.org.apache.xml.internal.utils.TreeWalker; |
|
import com.sun.org.apache.xml.internal.utils.XMLCharacterRecognizer; |
|
import com.sun.org.apache.xml.internal.utils.XMLString; |
|
import com.sun.org.apache.xml.internal.utils.XMLStringFactory; |
|
import java.util.ArrayList; |
|
import java.util.List; |
|
import javax.xml.transform.SourceLocator; |
|
import javax.xml.transform.dom.DOMSource; |
|
import org.w3c.dom.Attr; |
|
import org.w3c.dom.Document; |
|
import org.w3c.dom.DocumentType; |
|
import org.w3c.dom.Element; |
|
import org.w3c.dom.Entity; |
|
import org.w3c.dom.NamedNodeMap; |
|
import org.w3c.dom.Node; |
|
import org.xml.sax.ContentHandler; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public class DOM2DTM extends DTMDefaultBaseIterators |
|
{ |
|
static final boolean JJK_DEBUG=false; |
|
static final boolean JJK_NEWCODE=true; |
|
|
|
|
|
*/ |
|
static final String NAMESPACE_DECL_NS="http://www.w3.org/XML/1998/namespace"; |
|
|
|
|
|
* possible copying to DTM. */ |
|
transient private Node m_pos; |
|
|
|
private int m_last_parent=0; |
|
|
|
* previous sib. */ |
|
private int m_last_kid=NULL; |
|
|
|
|
|
|
|
* */ |
|
transient private Node m_root; |
|
|
|
|
|
synthesis of the implied xml: namespace declaration node. */ |
|
boolean m_processedFirstElement=false; |
|
|
|
|
|
|
|
* DOM tree. */ |
|
transient private boolean m_nodesAreProcessed; |
|
|
|
|
|
|
|
|
|
|
|
|
|
* that sequence. */ |
|
protected List<Node> m_nodes = new ArrayList<>(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public DOM2DTM(DTMManager mgr, DOMSource domSource, |
|
int dtmIdentity, DTMWSFilter whiteSpaceFilter, |
|
XMLStringFactory xstringfactory, |
|
boolean doIndexing) |
|
{ |
|
super(mgr, domSource, dtmIdentity, whiteSpaceFilter, |
|
xstringfactory, doIndexing); |
|
|
|
|
|
m_pos=m_root = domSource.getNode(); |
|
|
|
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? |
|
|
|
if(ELEMENT_NODE == m_root.getNodeType()) |
|
{ |
|
NamedNodeMap attrs=m_root.getAttributes(); |
|
int attrsize=(attrs==null) ? 0 : attrs.getLength(); |
|
if(attrsize>0) |
|
{ |
|
int attrIndex=NULL; |
|
for(int i=0;i<attrsize;++i) |
|
{ |
|
// No need to force nodetype in this case; |
|
// addNode() will take care of switching it from |
|
|
|
attrIndex=addNode(attrs.item(i),0,attrIndex,NULL); |
|
m_firstch.setElementAt(DTM.NULL,attrIndex); |
|
} |
|
// Terminate list of attrs, and make sure they aren't |
|
|
|
m_nextsib.setElementAt(DTM.NULL,attrIndex); |
|
|
|
// IMPORTANT: This does NOT change m_last_parent or m_last_kid! |
|
} // if attrs exist |
|
} //if(ELEMENT_NODE) |
|
|
|
|
|
m_nodesAreProcessed = false; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected int addNode(Node node, int parentIndex, |
|
int previousSibling, int forceNodeType) |
|
{ |
|
int nodeIndex = m_nodes.size(); |
|
|
|
|
|
if(m_dtmIdent.size() == (nodeIndex>>>DTMManager.IDENT_DTM_NODE_BITS)) |
|
{ |
|
try |
|
{ |
|
if(m_mgr==null) |
|
throw new ClassCastException(); |
|
|
|
|
|
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(XMLMessages.createXMLMessage(XMLErrorResources.ER_NO_DTMIDS_AVAIL, null)); |
|
} |
|
} |
|
|
|
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 |
|
|
|
if (Node.ATTRIBUTE_NODE == type) |
|
{ |
|
String name = node.getNodeName(); |
|
|
|
if (name.startsWith("xmlns:") || name.equals("xmlns")) |
|
{ |
|
type = DTM.NAMESPACE_NODE; |
|
} |
|
} |
|
|
|
m_nodes.add(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(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 |
|
|
|
String localName = (type == Node.PROCESSING_INSTRUCTION_NODE) ? |
|
node.getNodeName() : |
|
node.getLocalName(); |
|
|
|
|
|
if(((type == Node.ELEMENT_NODE) || (type == Node.ATTRIBUTE_NODE)) |
|
&& null == localName) |
|
localName = node.getNodeName(); |
|
|
|
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 |
|
|
|
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 |
|
|
|
if (type == DTM.NAMESPACE_NODE) |
|
declareNamespaceInContext(parentIndex,nodeIndex); |
|
|
|
return nodeIndex; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public int getNumberOfNodes() |
|
{ |
|
return m_nodes.size(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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 |
|
|
|
if (m_nodesAreProcessed) |
|
return false; |
|
|
|
// %REVIEW% Is this local copy Really Useful from a performance |
|
|
|
Node pos=m_pos; |
|
Node next=null; |
|
int nexttype=NULL; |
|
|
|
|
|
do |
|
{ |
|
|
|
if (pos.hasChildNodes()) |
|
{ |
|
next = pos.getFirstChild(); |
|
|
|
// %REVIEW% There's probably a more elegant way to skip |
|
|
|
if(next!=null && DOCUMENT_TYPE_NODE==next.getNodeType()) |
|
next=next.getNextSibling(); |
|
|
|
// Push DTM context -- except for children of Entity References, |
|
|
|
if(ENTITY_REFERENCE_NODE!=pos.getNodeType()) |
|
{ |
|
m_last_parent=m_last_kid; |
|
m_last_kid=NULL; |
|
|
|
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(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 |
|
|
|
next = pos.getNextSibling(); |
|
if(next!=null && DOCUMENT_TYPE_NODE==next.getNodeType()) |
|
next=next.getNextSibling(); |
|
|
|
if(next!=null) |
|
break; |
|
|
|
|
|
pos=pos.getParentNode(); |
|
if(pos==null) |
|
{ |
|
|
|
if(JJK_DEBUG) |
|
{ |
|
System.out.println("***** DOM2DTM Pop Control Flow problem"); |
|
for(;;); |
|
} |
|
} |
|
|
|
// The only parents in the DTM are Elements. However, |
|
// the DOM could contain EntityReferences. If we |
|
|
|
if(pos!=null && ENTITY_REFERENCE_NODE == pos.getNodeType()) |
|
{ |
|
|
|
if(JJK_DEBUG) |
|
System.out.println("***** DOM2DTM popping EntRef"); |
|
} |
|
else |
|
{ |
|
popShouldStripWhitespace(); |
|
|
|
if(m_last_kid==NULL) |
|
m_firstch.setElementAt(NULL,m_last_parent); |
|
else |
|
m_nextsib.setElementAt(NULL,m_last_kid); |
|
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 |
|
|
|
if (ENTITY_REFERENCE_NODE == nexttype) |
|
pos=next; |
|
} |
|
while (ENTITY_REFERENCE_NODE == nexttype); |
|
|
|
|
|
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(); |
|
|
|
|
|
if(TEXT_NODE == nexttype || CDATA_SECTION_NODE == nexttype) |
|
{ |
|
|
|
suppressNode=((null != m_wsfilter) && getShouldStripWhitespace()); |
|
|
|
// Scan logically contiguous text (siblings, plus "flattening" |
|
|
|
Node n=next; |
|
while(n!=null) |
|
{ |
|
lastTextNode=n; |
|
|
|
if(TEXT_NODE == n.getNodeType()) |
|
nexttype=TEXT_NODE; |
|
// Any non-whitespace in this sequence blocks whitespace |
|
|
|
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 |
|
|
|
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 |
|
|
|
int nextindex=addNode(next,m_last_parent,m_last_kid, |
|
nexttype); |
|
|
|
m_last_kid=nextindex; |
|
|
|
if(ELEMENT_NODE == nexttype) |
|
{ |
|
int attrIndex=NULL; |
|
// Process attributes _now_, rather than waiting. |
|
|
|
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 |
|
|
|
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 |
|
|
|
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(!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 |
|
|
|
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) |
|
|
|
|
|
if(TEXT_NODE == nexttype || CDATA_SECTION_NODE == nexttype) |
|
{ |
|
// %TBD% If nexttype was forced to TEXT, patch the DTM node |
|
|
|
next=lastTextNode; |
|
} |
|
|
|
|
|
m_pos=next; |
|
return true; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public Node getNode(int nodeHandle) |
|
{ |
|
|
|
int identity = makeNodeIdentity(nodeHandle); |
|
|
|
return m_nodes.get(identity); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected Node lookupNode(int nodeIdentity) |
|
{ |
|
return m_nodes.get(nodeIdentity); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected int getNextNodeIdentity(int identity) |
|
{ |
|
|
|
identity += 1; |
|
|
|
if (identity >= m_nodes.size()) |
|
{ |
|
if (!nextNode()) |
|
identity = DTM.NULL; |
|
} |
|
|
|
return identity; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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.get(i) == node) |
|
return makeNodeHandle(i); |
|
} |
|
|
|
isMore = nextNode(); |
|
|
|
len = m_nodes.size(); |
|
|
|
} |
|
while(isMore || i < len); |
|
} |
|
|
|
return DTM.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 |
|
|
|
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 |
|
|
|
for(Node cursor=node; |
|
cursor!=null; |
|
cursor= |
|
(cursor.getNodeType()!=ATTRIBUTE_NODE) |
|
? cursor.getParentNode() |
|
: ((org.w3c.dom.Attr)cursor).getOwnerElement()) |
|
{ |
|
if(cursor==m_root) |
|
|
|
return getHandleFromNode(node); |
|
} // for ancestors of node |
|
} // if node and m_root in same Document |
|
} // if node!=null |
|
|
|
return DTM.NULL; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public int getAttributeNode(int nodeHandle, String namespaceURI, |
|
String name) |
|
{ |
|
|
|
|
|
if (null == namespaceURI) |
|
namespaceURI = ""; |
|
|
|
int type = getNodeType(nodeHandle); |
|
|
|
if (DTM.ELEMENT_NODE == type) |
|
{ |
|
|
|
|
|
int identity = makeNodeIdentity(nodeHandle); |
|
|
|
while (DTM.NULL != (identity = getNextNodeIdentity(identity))) |
|
{ |
|
|
|
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 |
|
|
|
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 |
|
{ |
|
break; |
|
} |
|
} |
|
} |
|
|
|
return DTM.NULL; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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 |
|
|
|
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" |
|
|
|
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() ); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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" |
|
|
|
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; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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 : |
|
buf.append(node.getNodeValue()); |
|
break; |
|
case Node.PROCESSING_INSTRUCTION_NODE : |
|
|
|
break; |
|
default : |
|
|
|
break; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public String getNodeName(int nodeHandle) |
|
{ |
|
|
|
Node node = getNode(nodeHandle); |
|
|
|
|
|
return node.getNodeName(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public String getNodeNameX(int nodeHandle) |
|
{ |
|
|
|
String name; |
|
short type = getNodeType(nodeHandle); |
|
|
|
switch (type) |
|
{ |
|
case DTM.NAMESPACE_NODE : |
|
{ |
|
Node node = getNode(nodeHandle); |
|
|
|
|
|
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); |
|
|
|
|
|
name = node.getNodeName(); |
|
} |
|
break; |
|
default : |
|
name = ""; |
|
} |
|
|
|
return name; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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) |
|
{ |
|
|
|
String qname = newnode.getNodeName(); |
|
if('#'==qname.charAt(0)) |
|
{ |
|
// Match old default for this function |
|
|
|
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); |
|
|
|
|
|
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; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public String getPrefix(int nodeHandle) |
|
{ |
|
|
|
String prefix; |
|
short type = getNodeType(nodeHandle); |
|
|
|
switch (type) |
|
{ |
|
case DTM.NAMESPACE_NODE : |
|
{ |
|
Node node = getNode(nodeHandle); |
|
|
|
|
|
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); |
|
|
|
|
|
String qname = node.getNodeName(); |
|
int index = qname.indexOf(':'); |
|
|
|
prefix = (index < 0) ? "" : qname.substring(0, index); |
|
} |
|
break; |
|
default : |
|
prefix = ""; |
|
} |
|
|
|
return prefix; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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); |
|
|
|
|
|
nsuri = node.getNamespaceURI(); |
|
|
|
// %TBD% Handle DOM1? |
|
} |
|
break; |
|
default : |
|
nsuri = null; |
|
} |
|
|
|
return nsuri; |
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private Node logicalNextDOMTextNode(Node n) |
|
{ |
|
Node p=n.getNextSibling(); |
|
if(p==null) |
|
{ |
|
|
|
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()) |
|
{ |
|
|
|
if(n.hasChildNodes()) |
|
n=n.getFirstChild(); |
|
else |
|
n=n.getNextSibling(); |
|
} |
|
if(n!=null) |
|
{ |
|
|
|
int ntype=n.getNodeType(); |
|
if(TEXT_NODE != ntype && CDATA_SECTION_NODE != ntype) |
|
n=null; |
|
} |
|
return n; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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 |
|
|
|
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" |
|
|
|
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; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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) |
|
{ |
|
// 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]." |
|
|
|
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; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public boolean isAttributeSpecified(int attributeHandle) |
|
{ |
|
int type = getNodeType(attributeHandle); |
|
|
|
if (DTM.ATTRIBUTE_NODE == type) |
|
{ |
|
Attr attr = (Attr)getNode(attributeHandle); |
|
return attr.getSpecified(); |
|
} |
|
return false; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public void setIncrementalSAXSource(IncrementalSAXSource source) |
|
{ |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
* */ |
|
public org.xml.sax.ContentHandler getContentHandler() |
|
{ |
|
return null; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public org.xml.sax.ext.LexicalHandler getLexicalHandler() |
|
{ |
|
|
|
return null; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public org.xml.sax.EntityResolver getEntityResolver() |
|
{ |
|
|
|
return null; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public org.xml.sax.DTDHandler getDTDHandler() |
|
{ |
|
|
|
return null; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public org.xml.sax.ErrorHandler getErrorHandler() |
|
{ |
|
|
|
return null; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public org.xml.sax.ext.DeclHandler getDeclHandler() |
|
{ |
|
|
|
return null; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
* */ |
|
public boolean needsTwoThreads() |
|
{ |
|
return false; |
|
} |
|
|
|
// ========== Direct SAX Dispatch, for optimization purposes ======== |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private static boolean isSpace(char ch) |
|
{ |
|
return XMLCharacterRecognizer.isWhiteSpace(ch); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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 |
|
|
|
if(TEXT_NODE == type || CDATA_SECTION_NODE == type) |
|
{ |
|
while( null != (node=logicalNextDOMTextNode(node)) ) |
|
{ |
|
dispatchNodeData(node, ch, 0); |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@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 : |
|
case Node.COMMENT_NODE : |
|
if(0 != depth) |
|
break; |
|
// NOTE: Because this operation works in the DOM space, it does _not_ attempt |
|
|
|
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); |
|
|
|
default : |
|
|
|
break; |
|
} |
|
} |
|
|
|
TreeWalker m_walker = new TreeWalker(null); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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); |
|
} |
|
} |
|
|
|
public interface CharacterNodeHandler |
|
{ |
|
public void characters(Node node) |
|
throws org.xml.sax.SAXException; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public void setProperty(String property, Object value) |
|
{ |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public SourceLocator getSourceLocatorFor(int node) |
|
{ |
|
return null; |
|
} |
|
|
|
} |