| /* |
| * 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.sax2dtm; |
| |
| import java.util.Hashtable; |
| import java.util.Vector; |
| import javax.xml.transform.Source; |
| import javax.xml.transform.SourceLocator; |
| import org.apache.xalan.transformer.XalanProperties; |
| import org.apache.xalan.res.XSLTErrorResources; |
| import org.apache.xalan.res.XSLMessages; |
| |
| import org.apache.xml.dtm.*; |
| import org.apache.xml.dtm.ref.*; |
| import org.apache.xml.utils.StringVector; |
| import org.apache.xml.utils.IntVector; |
| import org.apache.xml.utils.FastStringBuffer; |
| import org.apache.xml.utils.IntStack; |
| import org.apache.xml.utils.SuballocatedIntVector; |
| import org.apache.xml.utils.SystemIDResolver; |
| import org.apache.xml.utils.WrappedRuntimeException; |
| import org.apache.xml.utils.XMLCharacterRecognizer; |
| import org.apache.xml.utils.XMLString; |
| import org.apache.xml.utils.XMLStringFactory; |
| import org.xml.sax.*; |
| import org.xml.sax.ext.*; |
| |
| /** |
| * This is a subclass of SAX2DTM which has been modified to meet the needs of |
| * Result Tree Frameworks (RTFs). The differences are: |
| * |
| * 1) Multiple XML trees may be appended to the single DTM. This means |
| * that the root node of each document is _not_ node 0. Some code has |
| * had to be deoptimized to support this mode of operation, and an |
| * explicit mechanism for obtaining the Node Handle of the root node |
| * has been provided. |
| * |
| * 2) A stack of these documents is maintained, allowing us to "tail-prune" the |
| * most recently added trees off the end of the DTM as stylesheet elements |
| * (and thus variable contexts) are exited. |
| * |
| * PLEASE NOTE that this class may be _heavily_ dependent upon the |
| * internals of the SAX2DTM superclass, and must be maintained in |
| * parallel with that code. Arguably, they should be conditionals |
| * within a single class... but they have deen separated for |
| * performance reasons. (In fact, one could even argue about which is |
| * the superclass and which is the subclass; the current arrangement |
| * is as much about preserving stability of existing code during |
| * development as anything else.) |
| * |
| * %REVIEW% In fact, since the differences are so minor, I think it |
| * may be possible/practical to fold them back into the base |
| * SAX2DTM. Consider that as a future code-size optimization. |
| * */ |
| public class SAX2RTFDTM extends SAX2DTM |
| { |
| /** Set true to monitor SAX events and similar diagnostic info. */ |
| private static final boolean DEBUG = false; |
| |
| /** Most recently started Document, or null if the DTM is empty. */ |
| private int m_currentDocumentNode=NULL; |
| |
| /** Tail-pruning mark: Number of nodes in use */ |
| IntStack mark_size=new IntStack(); |
| /** Tail-pruning mark: Number of data items in use */ |
| IntStack mark_data_size=new IntStack(); |
| /** Tail-pruning mark: Number of size-of-data fields in use */ |
| IntStack mark_char_size=new IntStack(); |
| /** Tail-pruning mark: Number of dataOrQName slots in use */ |
| IntStack mark_doq_size=new IntStack(); |
| /** Tail-pruning mark: Number of namespace declaration sets in use |
| * %REVIEW% I don't think number of NS sets is ever different from number |
| * of NS elements. We can probabably reduce these to a single stack and save |
| * some storage. |
| * */ |
| IntStack mark_nsdeclset_size=new IntStack(); |
| /** Tail-pruning mark: Number of naespace declaration elements in use |
| * %REVIEW% I don't think number of NS sets is ever different from number |
| * of NS elements. We can probabably reduce these to a single stack and save |
| * some storage. |
| */ |
| IntStack mark_nsdeclelem_size=new IntStack(); |
| |
| public SAX2RTFDTM(DTMManager mgr, Source source, int dtmIdentity, |
| DTMWSFilter whiteSpaceFilter, |
| XMLStringFactory xstringfactory, |
| boolean doIndexing) |
| { |
| super(mgr, source, dtmIdentity, whiteSpaceFilter, |
| xstringfactory, doIndexing); |
| |
| // NEVER track source locators for RTFs; they aren't meaningful. I think. |
| // (If we did track them, we'd need to tail-prune these too.) |
| m_useSourceLocationProperty=false; //org.apache.xalan.processor.TransformerFactoryImpl.m_source_location; |
| m_sourceSystemId = (m_useSourceLocationProperty) ? new StringVector() : null; |
| m_sourceLine = (m_useSourceLocationProperty) ? new IntVector() : null; |
| m_sourceColumn = (m_useSourceLocationProperty) ? new IntVector() : null; |
| |
| } |
| |
| /** |
| * Given a DTM, find the owning document node. In the case of |
| * SAX2RTFDTM, which may contain multiple documents, this returns |
| * the <b>most recently started</b> document, or null if the DTM is |
| * empty or no document is currently under construction. |
| * |
| * %REVIEW% Should we continue to report the most recent after |
| * construction has ended? I think not, given that it may have been |
| * tail-pruned. |
| * |
| * @param nodeHandle the id of the node. |
| * @return int Node handle of Document node, or null if this DTM does not |
| * contain an "active" document. |
| * */ |
| public int getDocument() |
| { |
| return makeNodeHandle(m_currentDocumentNode); |
| } |
| |
| /** |
| * Given a node handle, find the owning document node, using DTM semantics |
| * (Document owns itself) rather than DOM semantics (Document has no owner). |
| * |
| * (I'm counting on the fact that getOwnerDocument() is implemented on top |
| * of this call, in the superclass, to avoid having to rewrite that one. |
| * Be careful if that code changes!) |
| * |
| * @param nodeHandle the id of the node. |
| * @return int Node handle of owning document |
| */ |
| public int getDocumentRoot(int nodeHandle) |
| { |
| for(int id=makeNodeIdentity(nodeHandle); |
| id!=NULL; |
| id=_parent(id)) |
| if(_type(id)==DTM.DOCUMENT_NODE) |
| return makeNodeHandle(id); |
| |
| return DTM.NULL; // Safety net; should never happen |
| } |
| |
| /** |
| * Given a node identifier, find the owning document node. Unlike the DOM, |
| * this considers the owningDocument of a Document to be itself. Note that |
| * in shared DTMs this may not be zero. |
| * |
| * @param nodeIdentifier the id of the starting node. |
| * @return int Node identifier of the root of this DTM tree |
| */ |
| protected int _documentRoot(int nodeIdentifier) |
| { |
| if(nodeIdentifier==NULL) return NULL; |
| |
| for(int parent=_parent(nodeIdentifier); |
| parent!=NULL; |
| nodeIdentifier=parent,parent=_parent(nodeIdentifier)) |
| ; |
| |
| return nodeIdentifier; |
| } |
| |
| /** |
| * Receive notification of the beginning of a new RTF document. |
| * |
| * %REVIEW% Y'know, this isn't all that much of a deoptimization. We |
| * might want to consider folding the start/endDocument changes back |
| * into the main SAX2DTM so we don't have to expose so many fields |
| * (even as Protected) and carry the additional code. |
| * |
| * @throws SAXException Any SAX exception, possibly |
| * wrapping another exception. |
| * @see org.xml.sax.ContentHandler#startDocument |
| * */ |
| public void startDocument() throws SAXException |
| { |
| // Re-initialize the tree append process |
| m_endDocumentOccured = false; |
| m_prefixMappings = new java.util.Vector(); |
| m_contextIndexes = new IntStack(); |
| m_parents = new IntStack(); |
| |
| m_currentDocumentNode=m_size; |
| super.startDocument(); |
| } |
| |
| /** |
| * Receive notification of the end of the document. |
| * |
| * %REVIEW% Y'know, this isn't all that much of a deoptimization. We |
| * might want to consider folding the start/endDocument changes back |
| * into the main SAX2DTM so we don't have to expose so many fields |
| * (even as Protected). |
| * |
| * @throws SAXException Any SAX exception, possibly |
| * wrapping another exception. |
| * @see org.xml.sax.ContentHandler#endDocument |
| * */ |
| public void endDocument() throws SAXException |
| { |
| charactersFlush(); |
| |
| m_nextsib.setElementAt(NULL,m_currentDocumentNode); |
| |
| if (m_firstch.elementAt(m_currentDocumentNode) == NOTPROCESSED) |
| m_firstch.setElementAt(NULL,m_currentDocumentNode); |
| |
| if (DTM.NULL != m_previous) |
| m_nextsib.setElementAt(DTM.NULL,m_previous); |
| |
| m_parents = null; |
| m_prefixMappings = null; |
| m_contextIndexes = null; |
| |
| m_currentDocumentNode= NULL; // no longer open |
| m_endDocumentOccured = true; |
| } |
| |
| |
| /** "Tail-pruning" support for RTFs. |
| * |
| * This function pushes information about the current size of the |
| * DTM's data structures onto a stack, for use by popRewindMark() |
| * (which see). |
| * |
| * %REVIEW% I have no idea how to rewind m_elemIndexes. However, |
| * RTFs will not be indexed, so I can simply panic if that case |
| * arises. Hey, it works... |
| * */ |
| public void pushRewindMark() |
| { |
| if(m_indexing || m_elemIndexes!=null) |
| throw new java.lang.NullPointerException("Coding error; Don't try to mark/rewind an indexed DTM"); |
| |
| // Values from DTMDefaultBase |
| // %REVIEW% Can the namespace stack sizes ever differ? If not, save space! |
| mark_size.push(m_size); |
| mark_nsdeclset_size.push( (m_namespaceDeclSets==null) ? 0 : m_namespaceDeclSets.size() ); |
| mark_nsdeclelem_size.push( (m_namespaceDeclSetElements==null) ? 0 : m_namespaceDeclSetElements.size() ); |
| |
| // Values from SAX2DTM |
| mark_data_size.push(m_data.size()); |
| mark_char_size.push(m_chars.size()); |
| mark_doq_size.push(m_dataOrQName.size()); |
| } |
| |
| /** "Tail-pruning" support for RTFs. |
| * |
| * This function pops the information previously saved by |
| * pushRewindMark (which see) and uses it to discard all nodes added |
| * to the DTM after that time. We expect that this will allow us to |
| * reuse storage more effectively. |
| * |
| * This is _not_ intended to be called while a document is still being |
| * constructed -- only between endDocument and the next startDocument |
| * |
| * %REVIEW% WARNING: This is the first use of some of the truncation |
| * methods. If Xalan blows up after this is called, that's a likely |
| * place to check. |
| * |
| * %REVIEW% Our original design for DTMs permitted them to share |
| * string pools. If there any risk that this might be happening, we |
| * can _not_ rewind and recover the string storage. One solution |
| * might to assert that DTMs used for RTFs Must Not take advantage |
| * of that feature, but this seems excessively fragile. Another, much |
| * less attractive, would be to just let them leak... Nah. |
| * |
| * @return true if and only if the pop completely emptied the |
| * RTF. That response is used when determining how to unspool |
| * RTF-started-while-RTF-open situations. |
| * */ |
| public boolean popRewindMark() |
| { |
| boolean top=mark_size.empty(); |
| |
| m_size=top ? 0 : mark_size.pop(); |
| m_exptype.setSize(m_size); |
| m_firstch.setSize(m_size); |
| m_nextsib.setSize(m_size); |
| m_prevsib.setSize(m_size); |
| m_parent.setSize(m_size); |
| |
| m_elemIndexes=null; |
| |
| int ds= top ? 0 : mark_nsdeclset_size.pop(); |
| if (m_namespaceDeclSets!=null) |
| m_namespaceDeclSets.setSize(ds); |
| |
| int ds1= top ? 0 : mark_nsdeclelem_size.pop(); |
| if (m_namespaceDeclSetElements!=null) |
| m_namespaceDeclSetElements.setSize(ds1); |
| |
| // Values from SAX2DTM |
| m_data.setSize(top ? 0 : mark_data_size.pop()); |
| m_chars.setLength(top ? 0 : mark_char_size.pop()); |
| m_dataOrQName.setSize(top ? 0 : mark_doq_size.pop()); |
| |
| // Return true iff DTM now empty |
| return m_size==0; |
| } |
| |
| /** @return true if a DTM tree is currently under construction. |
| * */ |
| public boolean isTreeIncomplete() |
| { |
| return !m_endDocumentOccured; |
| |
| } |
| } |