| /* |
| * 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. |
| */ |
| /* |
| * $Id: SAX2DTM.java 468653 2006-10-28 07:07:05Z minchau $ |
| */ |
| 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.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.XMLString; |
| import org.apache.xml.utils.XMLStringFactory; |
| import org.apache.xml.res.XMLErrorResources; |
| import org.apache.xml.res.XMLMessages; |
| import org.xml.sax.*; |
| import org.xml.sax.ext.*; |
| |
| /** |
| * This class implements a DTM that tends to be optimized more for speed than |
| * for compactness, that is constructed via SAX2 ContentHandler events. |
| */ |
| public class SAX2DTM extends DTMDefaultBaseIterators |
| implements EntityResolver, DTDHandler, ContentHandler, ErrorHandler, |
| DeclHandler, LexicalHandler |
| { |
| /** Set true to monitor SAX events and similar diagnostic info. */ |
| private static final boolean DEBUG = false; |
| |
| /** |
| * If we're building the model incrementally on demand, we need to |
| * be able to tell the source when to send us more data. |
| * |
| * Note that if this has not been set, and you attempt to read ahead |
| * of the current build point, we'll probably throw a null-pointer |
| * exception. We could try to wait-and-retry instead, as a very poor |
| * fallback, but that has all the known problems with multithreading |
| * on multiprocessors and we Don't Want to Go There. |
| * |
| * @see setIncrementalSAXSource |
| */ |
| private IncrementalSAXSource m_incrementalSAXSource = null; |
| |
| /** |
| * All the character content, including attribute values, are stored in |
| * this buffer. |
| * |
| * %REVIEW% Should this have an option of being shared across DTMs? |
| * Sequentially only; not threadsafe... Currently, I think not. |
| * |
| * %REVIEW% Initial size was pushed way down to reduce weight of RTFs. |
| * pending reduction in number of RTF DTMs. Now that we're sharing a DTM |
| * between RTFs, and tail-pruning... consider going back to the larger/faster. |
| * |
| * Made protected rather than private so SAX2RTFDTM can access it. |
| */ |
| //private FastStringBuffer m_chars = new FastStringBuffer(13, 13); |
| protected FastStringBuffer m_chars; |
| |
| /** This vector holds offset and length data. |
| */ |
| protected SuballocatedIntVector m_data; |
| |
| /** The parent stack, needed only for construction. |
| * Made protected rather than private so SAX2RTFDTM can access it. |
| */ |
| transient protected IntStack m_parents; |
| |
| /** The current previous node, needed only for construction time. |
| * Made protected rather than private so SAX2RTFDTM can access it. |
| */ |
| transient protected int m_previous = 0; |
| |
| /** Namespace support, only relevent at construction time. |
| * Made protected rather than private so SAX2RTFDTM can access it. |
| */ |
| transient protected java.util.Vector m_prefixMappings = |
| new java.util.Vector(); |
| |
| /** Namespace support, only relevent at construction time. |
| * Made protected rather than private so SAX2RTFDTM can access it. |
| */ |
| transient protected IntStack m_contextIndexes; |
| |
| /** Type of next characters() event within text block in prgress. */ |
| transient protected int m_textType = DTM.TEXT_NODE; |
| |
| /** |
| * Type of coalesced text block. See logic in the characters() |
| * method. |
| */ |
| transient protected int m_coalescedTextType = DTM.TEXT_NODE; |
| |
| /** The SAX Document locator */ |
| transient protected Locator m_locator = null; |
| |
| /** The SAX Document system-id */ |
| transient private String m_systemId = null; |
| |
| /** We are inside the DTD. This is used for ignoring comments. */ |
| transient protected boolean m_insideDTD = false; |
| |
| /** Tree Walker for dispatchToEvents. */ |
| protected DTMTreeWalker m_walker = new DTMTreeWalker(); |
| |
| /** pool of string values that come as strings. */ |
| protected DTMStringPool m_valuesOrPrefixes; |
| |
| /** End document has been reached. |
| * Made protected rather than private so SAX2RTFDTM can access it. |
| */ |
| protected boolean m_endDocumentOccured = false; |
| |
| /** Data or qualified name values, one array element for each node. */ |
| protected SuballocatedIntVector m_dataOrQName; |
| |
| /** |
| * This table holds the ID string to node associations, for |
| * XML IDs. |
| */ |
| protected Hashtable m_idAttributes = new Hashtable(); |
| |
| /** |
| * fixed dom-style names. |
| */ |
| private static final String[] m_fixednames = { null, |
| null, // nothing, Element |
| null, "#text", // Attr, Text |
| "#cdata_section", null, // CDATA, EntityReference |
| null, null, // Entity, PI |
| "#comment", "#document", // Comment, Document |
| null, "#document-fragment", // Doctype, DocumentFragment |
| null }; // Notation |
| |
| /** |
| * Vector of entities. Each record is composed of four Strings: |
| * publicId, systemID, notationName, and name. |
| */ |
| private Vector m_entities = null; |
| |
| /** m_entities public ID offset. */ |
| private static final int ENTITY_FIELD_PUBLICID = 0; |
| |
| /** m_entities system ID offset. */ |
| private static final int ENTITY_FIELD_SYSTEMID = 1; |
| |
| /** m_entities notation name offset. */ |
| private static final int ENTITY_FIELD_NOTATIONNAME = 2; |
| |
| /** m_entities name offset. */ |
| private static final int ENTITY_FIELD_NAME = 3; |
| |
| /** Number of entries per record for m_entities. */ |
| private static final int ENTITY_FIELDS_PER = 4; |
| |
| /** |
| * The starting offset within m_chars for the text or |
| * CDATA_SECTION node currently being acumulated, |
| * or -1 if there is no text node in progress |
| */ |
| protected int m_textPendingStart = -1; |
| |
| /** |
| * Describes whether information about document source location |
| * should be maintained or not. |
| * |
| * Made protected for access by SAX2RTFDTM. |
| */ |
| protected boolean m_useSourceLocationProperty = false; |
| |
| /** Made protected for access by SAX2RTFDTM. |
| */ |
| protected StringVector m_sourceSystemId; |
| /** Made protected for access by SAX2RTFDTM. |
| */ |
| protected IntVector m_sourceLine; |
| /** Made protected for access by SAX2RTFDTM. |
| */ |
| protected IntVector m_sourceColumn; |
| |
| /** |
| * Construct a SAX2DTM object using the default block size. |
| * |
| * @param mgr The DTMManager who owns this DTM. |
| * @param source the JAXP 1.1 Source object for this DTM. |
| * @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 SAX2DTM(DTMManager mgr, Source source, int dtmIdentity, |
| DTMWSFilter whiteSpaceFilter, |
| XMLStringFactory xstringfactory, |
| boolean doIndexing) |
| { |
| |
| this(mgr, source, dtmIdentity, whiteSpaceFilter, |
| xstringfactory, doIndexing, DEFAULT_BLOCKSIZE, true, false); |
| } |
| |
| /** |
| * Construct a SAX2DTM object ready to be constructed from SAX2 |
| * ContentHandler events. |
| * |
| * @param mgr The DTMManager who owns this DTM. |
| * @param source the JAXP 1.1 Source object for this DTM. |
| * @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. |
| * @param blocksize The block size of the DTM. |
| * @param usePrevsib true if we want to build the previous sibling node array. |
| * @param newNameTable true if we want to use a new ExpandedNameTable for this DTM. |
| */ |
| public SAX2DTM(DTMManager mgr, Source source, int dtmIdentity, |
| DTMWSFilter whiteSpaceFilter, |
| XMLStringFactory xstringfactory, |
| boolean doIndexing, |
| int blocksize, |
| boolean usePrevsib, |
| boolean newNameTable) |
| { |
| |
| super(mgr, source, dtmIdentity, whiteSpaceFilter, |
| xstringfactory, doIndexing, blocksize, usePrevsib, newNameTable); |
| |
| // %OPT% Use smaller sizes for all internal storage units when |
| // the blocksize is small. This reduces the cost of creating an RTF. |
| if (blocksize <= 64) |
| { |
| m_data = new SuballocatedIntVector(blocksize, DEFAULT_NUMBLOCKS_SMALL); |
| m_dataOrQName = new SuballocatedIntVector(blocksize, DEFAULT_NUMBLOCKS_SMALL); |
| m_valuesOrPrefixes = new DTMStringPool(16); |
| m_chars = new FastStringBuffer(7, 10); |
| m_contextIndexes = new IntStack(4); |
| m_parents = new IntStack(4); |
| } |
| else |
| { |
| m_data = new SuballocatedIntVector(blocksize, DEFAULT_NUMBLOCKS); |
| m_dataOrQName = new SuballocatedIntVector(blocksize, DEFAULT_NUMBLOCKS); |
| m_valuesOrPrefixes = new DTMStringPool(); |
| m_chars = new FastStringBuffer(10, 13); |
| m_contextIndexes = new IntStack(); |
| m_parents = new IntStack(); |
| } |
| |
| // %REVIEW% Initial size pushed way down to reduce weight of RTFs |
| // (I'm not entirely sure 0 would work, so I'm playing it safe for now.) |
| //m_data = new SuballocatedIntVector(doIndexing ? (1024*2) : 512, 1024); |
| //m_data = new SuballocatedIntVector(blocksize); |
| |
| m_data.addElement(0); // Need placeholder in case index into here must be <0. |
| |
| //m_dataOrQName = new SuballocatedIntVector(blocksize); |
| |
| // m_useSourceLocationProperty=org.apache.xalan.processor.TransformerFactoryImpl.m_source_location; |
| m_useSourceLocationProperty = mgr.getSource_location(); |
| m_sourceSystemId = (m_useSourceLocationProperty) ? new StringVector() : null; |
| m_sourceLine = (m_useSourceLocationProperty) ? new IntVector() : null; |
| m_sourceColumn = (m_useSourceLocationProperty) ? new IntVector() : null; |
| } |
| |
| /** |
| * Set whether information about document source location |
| * should be maintained or not. |
| */ |
| public void setUseSourceLocation(boolean useSourceLocation) |
| { |
| m_useSourceLocationProperty = useSourceLocation; |
| } |
| |
| /** |
| * Get the data or qualified name for the given node identity. |
| * |
| * @param identity The node identity. |
| * |
| * @return The data or qualified name, or DTM.NULL. |
| */ |
| protected int _dataOrQName(int identity) |
| { |
| |
| if (identity < m_size) |
| return m_dataOrQName.elementAt(identity); |
| |
| // Check to see if the information requested has been processed, and, |
| // if not, advance the iterator until we the information has been |
| // processed. |
| while (true) |
| { |
| boolean isMore = nextNode(); |
| |
| if (!isMore) |
| return NULL; |
| else if (identity < m_size) |
| return m_dataOrQName.elementAt(identity); |
| } |
| } |
| |
| /** |
| * Ask the CoRoutine parser to doTerminate and clear the reference. |
| */ |
| public void clearCoRoutine() |
| { |
| clearCoRoutine(true); |
| } |
| |
| /** |
| * Ask the CoRoutine parser to doTerminate and clear the reference. If |
| * the CoRoutine parser has already been cleared, this will have no effect. |
| * |
| * @param callDoTerminate true of doTerminate should be called on the |
| * coRoutine parser. |
| */ |
| public void clearCoRoutine(boolean callDoTerminate) |
| { |
| |
| if (null != m_incrementalSAXSource) |
| { |
| if (callDoTerminate) |
| m_incrementalSAXSource.deliverMoreNodes(false); |
| |
| m_incrementalSAXSource = null; |
| } |
| } |
| |
| /** |
| * Bind a IncrementalSAXSource to this DTM. If we discover we need nodes |
| * that have not yet been built, we will ask this object to send us more |
| * events, and it will manage interactions with its data sources. |
| * |
| * Note that we do not actually build the IncrementalSAXSource, since we don't |
| * know what source it's reading from, what thread that source will run in, |
| * or when it will run. |
| * |
| * @param incrementalSAXSource The parser that we want to recieve events from |
| * on demand. |
| */ |
| public void setIncrementalSAXSource(IncrementalSAXSource incrementalSAXSource) |
| { |
| |
| // Establish coroutine link so we can request more data |
| // |
| // Note: It's possible that some versions of IncrementalSAXSource may |
| // not actually use a CoroutineManager, and hence may not require |
| // that we obtain an Application Coroutine ID. (This relies on the |
| // coroutine transaction details having been encapsulated in the |
| // IncrementalSAXSource.do...() methods.) |
| m_incrementalSAXSource = incrementalSAXSource; |
| |
| // Establish SAX-stream link so we can receive the requested data |
| incrementalSAXSource.setContentHandler(this); |
| incrementalSAXSource.setLexicalHandler(this); |
| incrementalSAXSource.setDTDHandler(this); |
| |
| // Are the following really needed? incrementalSAXSource doesn't yet |
| // support them, and they're mostly no-ops here... |
| //incrementalSAXSource.setErrorHandler(this); |
| //incrementalSAXSource.setDeclHandler(this); |
| } |
| |
| /** |
| * getContentHandler returns "our SAX builder" -- the thing that |
| * someone else should send SAX events to in order to extend this |
| * DTM model. |
| * |
| * %REVIEW% Should this return null if constrution already done/begun? |
| * |
| * @return null if this model doesn't respond to 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 ContentHandler getContentHandler() |
| { |
| |
| if (m_incrementalSAXSource instanceof IncrementalSAXSource_Filter) |
| return (ContentHandler) m_incrementalSAXSource; |
| else |
| return this; |
| } |
| |
| /** |
| * 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 LexicalHandler getLexicalHandler() |
| { |
| |
| if (m_incrementalSAXSource instanceof IncrementalSAXSource_Filter) |
| return (LexicalHandler) m_incrementalSAXSource; |
| else |
| return this; |
| } |
| |
| /** |
| * Return this DTM's EntityResolver. |
| * |
| * @return null if this model doesn't respond to SAX entity ref events. |
| */ |
| public EntityResolver getEntityResolver() |
| { |
| return this; |
| } |
| |
| /** |
| * Return this DTM's DTDHandler. |
| * |
| * @return null if this model doesn't respond to SAX dtd events. |
| */ |
| public DTDHandler getDTDHandler() |
| { |
| return this; |
| } |
| |
| /** |
| * Return this DTM's ErrorHandler. |
| * |
| * @return null if this model doesn't respond to SAX error events. |
| */ |
| public ErrorHandler getErrorHandler() |
| { |
| return this; |
| } |
| |
| /** |
| * Return this DTM's DeclHandler. |
| * |
| * @return null if this model doesn't respond to SAX Decl events. |
| */ |
| public DeclHandler getDeclHandler() |
| { |
| return this; |
| } |
| |
| /** |
| * @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 null != m_incrementalSAXSource; |
| } |
| |
| /** |
| * 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. |
| * @param normalize true if the content should be normalized according to |
| * the rules for the XPath |
| * <a href="http://www.w3.org/TR/xpath#function-normalize-space">normalize-space</a> |
| * function. |
| * |
| * @throws SAXException |
| */ |
| public void dispatchCharactersEvents(int nodeHandle, ContentHandler ch, |
| boolean normalize) |
| throws SAXException |
| { |
| |
| int identity = makeNodeIdentity(nodeHandle); |
| |
| if (identity == DTM.NULL) |
| return; |
| |
| int type = _type(identity); |
| |
| if (isTextType(type)) |
| { |
| int dataIndex = m_dataOrQName.elementAt(identity); |
| int offset = m_data.elementAt(dataIndex); |
| int length = m_data.elementAt(dataIndex + 1); |
| |
| if(normalize) |
| m_chars.sendNormalizedSAXcharacters(ch, offset, length); |
| else |
| m_chars.sendSAXcharacters(ch, offset, length); |
| } |
| else |
| { |
| int firstChild = _firstch(identity); |
| |
| if (DTM.NULL != firstChild) |
| { |
| int offset = -1; |
| int length = 0; |
| int startNode = identity; |
| |
| identity = firstChild; |
| |
| do { |
| type = _type(identity); |
| |
| if (isTextType(type)) |
| { |
| int dataIndex = _dataOrQName(identity); |
| |
| if (-1 == offset) |
| { |
| offset = m_data.elementAt(dataIndex); |
| } |
| |
| length += m_data.elementAt(dataIndex + 1); |
| } |
| |
| identity = getNextNodeIdentity(identity); |
| } while (DTM.NULL != identity && (_parent(identity) >= startNode)); |
| |
| if (length > 0) |
| { |
| if(normalize) |
| m_chars.sendNormalizedSAXcharacters(ch, offset, length); |
| else |
| m_chars.sendSAXcharacters(ch, offset, length); |
| } |
| } |
| else if(type != DTM.ELEMENT_NODE) |
| { |
| int dataIndex = _dataOrQName(identity); |
| |
| if (dataIndex < 0) |
| { |
| dataIndex = -dataIndex; |
| dataIndex = m_data.elementAt(dataIndex + 1); |
| } |
| |
| String str = m_valuesOrPrefixes.indexToString(dataIndex); |
| |
| if(normalize) |
| FastStringBuffer.sendNormalizedSAXcharacters(str.toCharArray(), |
| 0, str.length(), ch); |
| else |
| ch.characters(str.toCharArray(), 0, str.length()); |
| } |
| } |
| } |
| |
| |
| /** |
| * 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) |
| { |
| |
| int expandedTypeID = getExpandedTypeID(nodeHandle); |
| // If just testing nonzero, no need to shift... |
| int namespaceID = m_expandedNameTable.getNamespaceID(expandedTypeID); |
| |
| if (0 == namespaceID) |
| { |
| // Don't retrieve name until/unless needed |
| // String name = m_expandedNameTable.getLocalName(expandedTypeID); |
| int type = getNodeType(nodeHandle); |
| |
| if (type == DTM.NAMESPACE_NODE) |
| { |
| if (null == m_expandedNameTable.getLocalName(expandedTypeID)) |
| return "xmlns"; |
| else |
| return "xmlns:" + m_expandedNameTable.getLocalName(expandedTypeID); |
| } |
| else if (0 == m_expandedNameTable.getLocalNameID(expandedTypeID)) |
| { |
| return m_fixednames[type]; |
| } |
| else |
| return m_expandedNameTable.getLocalName(expandedTypeID); |
| } |
| else |
| { |
| int qnameIndex = m_dataOrQName.elementAt(makeNodeIdentity(nodeHandle)); |
| |
| if (qnameIndex < 0) |
| { |
| qnameIndex = -qnameIndex; |
| qnameIndex = m_data.elementAt(qnameIndex); |
| } |
| |
| return m_valuesOrPrefixes.indexToString(qnameIndex); |
| } |
| } |
| |
| /** |
| * 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) |
| { |
| |
| int expandedTypeID = getExpandedTypeID(nodeHandle); |
| int namespaceID = m_expandedNameTable.getNamespaceID(expandedTypeID); |
| |
| if (0 == namespaceID) |
| { |
| String name = m_expandedNameTable.getLocalName(expandedTypeID); |
| |
| if (name == null) |
| return ""; |
| else |
| return name; |
| } |
| else |
| { |
| int qnameIndex = m_dataOrQName.elementAt(makeNodeIdentity(nodeHandle)); |
| |
| if (qnameIndex < 0) |
| { |
| qnameIndex = -qnameIndex; |
| qnameIndex = m_data.elementAt(qnameIndex); |
| } |
| |
| return m_valuesOrPrefixes.indexToString(qnameIndex); |
| } |
| } |
| |
| /** |
| * 5. [specified] A flag indicating whether this attribute was actually |
| * specified in the start-tag of its element, or was defaulted from the |
| * DTD. |
| * |
| * @param attributeHandle Must be a valid handle to an attribute node. |
| * @return <code>true</code> if the attribute was specified; |
| * <code>false</code> if it was defaulted. |
| */ |
| public boolean isAttributeSpecified(int attributeHandle) |
| { |
| |
| // I'm not sure if I want to do anything with this... |
| return true; // ?? |
| } |
| |
| /** |
| * 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() |
| { |
| |
| /** @todo: implement this org.apache.xml.dtm.DTMDefaultBase abstract method */ |
| error(XMLMessages.createXMLMessage(XMLErrorResources.ER_METHOD_NOT_SUPPORTED, null));//"Not yet supported!"); |
| |
| return null; |
| } |
| |
| /** |
| * 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; |
| |
| while (identity >= m_size) |
| { |
| if (null == m_incrementalSAXSource) |
| return DTM.NULL; |
| |
| nextNode(); |
| } |
| |
| return identity; |
| } |
| |
| /** |
| * 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 |
| { |
| |
| DTMTreeWalker treeWalker = m_walker; |
| ContentHandler prevCH = treeWalker.getcontentHandler(); |
| |
| if (null != prevCH) |
| { |
| treeWalker = new DTMTreeWalker(); |
| } |
| |
| treeWalker.setcontentHandler(ch); |
| treeWalker.setDTM(this); |
| |
| try |
| { |
| treeWalker.traverse(nodeHandle); |
| } |
| finally |
| { |
| treeWalker.setcontentHandler(null); |
| } |
| } |
| |
| /** |
| * Get the number of nodes that have been added. |
| * |
| * @return The number of that are currently in the tree. |
| */ |
| public int getNumberOfNodes() |
| { |
| return m_size; |
| } |
| |
| /** |
| * This method should try and build one or more nodes in the table. |
| * |
| * @return The true if a next node is found or false if |
| * there are no more nodes. |
| */ |
| protected boolean nextNode() |
| { |
| |
| if (null == m_incrementalSAXSource) |
| return false; |
| |
| if (m_endDocumentOccured) |
| { |
| clearCoRoutine(); |
| |
| return false; |
| } |
| |
| Object gotMore = m_incrementalSAXSource.deliverMoreNodes(true); |
| |
| // gotMore may be a Boolean (TRUE if still parsing, FALSE if |
| // EOF) or an exception if IncrementalSAXSource malfunctioned |
| // (code error rather than user error). |
| // |
| // %REVIEW% Currently the ErrorHandlers sketched herein are |
| // no-ops, so I'm going to initially leave this also as a |
| // no-op. |
| if (!(gotMore instanceof Boolean)) |
| { |
| if(gotMore instanceof RuntimeException) |
| { |
| throw (RuntimeException)gotMore; |
| } |
| else if(gotMore instanceof Exception) |
| { |
| throw new WrappedRuntimeException((Exception)gotMore); |
| } |
| // for now... |
| clearCoRoutine(); |
| |
| return false; |
| |
| // %TBD% |
| } |
| |
| if (gotMore != Boolean.TRUE) |
| { |
| |
| // EOF reached without satisfying the request |
| clearCoRoutine(); // Drop connection, stop trying |
| |
| // %TBD% deregister as its listener? |
| } |
| |
| return true; |
| } |
| |
| /** |
| * Bottleneck determination of text type. |
| * |
| * @param type oneof DTM.XXX_NODE. |
| * |
| * @return true if this is a text or cdata section. |
| */ |
| private final boolean isTextType(int type) |
| { |
| return (DTM.TEXT_NODE == type || DTM.CDATA_SECTION_NODE == type); |
| } |
| |
| // /** |
| // * Ensure that the size of the information arrays can hold another entry |
| // * at the given index. |
| // * |
| // * @param on exit from this function, the information arrays sizes must be |
| // * at least index+1. |
| // * |
| // * NEEDSDOC @param index |
| // */ |
| // protected void ensureSize(int index) |
| // { |
| // // dataOrQName is an SuballocatedIntVector and hence self-sizing. |
| // // But DTMDefaultBase may need fixup. |
| // super.ensureSize(index); |
| // } |
| |
| /** |
| * Construct the node map from the node. |
| * |
| * @param type raw type ID, one of DTM.XXX_NODE. |
| * @param expandedTypeID The expended type ID. |
| * @param parentIndex The current parent index. |
| * @param previousSibling The previous sibling index. |
| * @param dataOrPrefix index into m_data table, or string handle. |
| * @param canHaveFirstChild true if the node can have a first child, false |
| * if it is atomic. |
| * |
| * @return The index identity of the node that was added. |
| */ |
| protected int addNode(int type, int expandedTypeID, |
| int parentIndex, int previousSibling, |
| int dataOrPrefix, boolean canHaveFirstChild) |
| { |
| // Common to all nodes: |
| int nodeIndex = m_size++; |
| |
| // Have we overflowed a DTM Identity's addressing range? |
| if(m_dtmIdent.size() == (nodeIndex>>>DTMManager.IDENT_DTM_NODE_BITS)) |
| { |
| addNewDTMID(nodeIndex); |
| } |
| |
| m_firstch.addElement(canHaveFirstChild ? NOTPROCESSED : DTM.NULL); |
| m_nextsib.addElement(NOTPROCESSED); |
| m_parent.addElement(parentIndex); |
| m_exptype.addElement(expandedTypeID); |
| m_dataOrQName.addElement(dataOrPrefix); |
| |
| if (m_prevsib != null) { |
| m_prevsib.addElement(previousSibling); |
| } |
| |
| if (DTM.NULL != previousSibling) { |
| m_nextsib.setElementAt(nodeIndex,previousSibling); |
| } |
| |
| if (m_locator != null && m_useSourceLocationProperty) { |
| setSourceLocation(); |
| } |
| |
| // Note that nextSibling is not processed until charactersFlush() |
| // is called, to handle successive characters() events. |
| |
| // Special handling by type: Declare namespaces, attach first child |
| switch(type) |
| { |
| case DTM.NAMESPACE_NODE: |
| declareNamespaceInContext(parentIndex,nodeIndex); |
| break; |
| case DTM.ATTRIBUTE_NODE: |
| break; |
| default: |
| if (DTM.NULL == previousSibling && DTM.NULL != parentIndex) { |
| m_firstch.setElementAt(nodeIndex,parentIndex); |
| } |
| break; |
| } |
| |
| return nodeIndex; |
| } |
| |
| /** |
| * Get a new DTM ID beginning at the specified node index. |
| * @param nodeIndex The node identity at which the new DTM ID will begin |
| * addressing. |
| */ |
| protected void addNewDTMID(int nodeIndex) { |
| 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(XMLMessages.createXMLMessage(XMLErrorResources.ER_NO_DTMIDS_AVAIL, null));//"No more DTM IDs are available"; |
| } |
| } |
| |
| /** |
| * Migrate a DTM built with an old DTMManager to a new DTMManager. |
| * After the migration, the new DTMManager will treat the DTM as |
| * one that is built by itself. |
| * This is used to support DTM sharing between multiple transformations. |
| * @param manager the DTMManager |
| */ |
| public void migrateTo(DTMManager manager) { |
| super.migrateTo(manager); |
| |
| // We have to reset the information in m_dtmIdent and |
| // register the DTM with the new manager. |
| int numDTMs = m_dtmIdent.size(); |
| int dtmId = m_mgrDefault.getFirstFreeDTMID(); |
| int nodeIndex = 0; |
| for (int i = 0; i < numDTMs; i++) |
| { |
| m_dtmIdent.setElementAt(dtmId << DTMManager.IDENT_DTM_NODE_BITS, i); |
| m_mgrDefault.addDTM(this, dtmId, nodeIndex); |
| dtmId++; |
| nodeIndex += (1 << DTMManager.IDENT_DTM_NODE_BITS); |
| } |
| } |
| |
| /** |
| * Store the source location of the current node. This method must be called |
| * as every node is added to the DTM or for no node. |
| */ |
| protected void setSourceLocation() { |
| m_sourceSystemId.addElement(m_locator.getSystemId()); |
| m_sourceLine.addElement(m_locator.getLineNumber()); |
| m_sourceColumn.addElement(m_locator.getColumnNumber()); |
| |
| //%REVIEW% %BUG% Prevent this from arising in the first place |
| // by not allowing the enabling conditions to change after we start |
| // building the document. |
| if (m_sourceSystemId.size() != m_size) { |
| String msg = "CODING ERROR in Source Location: " + m_size + " != " |
| + m_sourceSystemId.size(); |
| System.err.println(msg); |
| throw new RuntimeException(msg); |
| } |
| } |
| |
| /** |
| * 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) |
| { |
| |
| int identity = makeNodeIdentity(nodeHandle); |
| int type = _type(identity); |
| |
| if (isTextType(type)) |
| { |
| int dataIndex = _dataOrQName(identity); |
| int offset = m_data.elementAt(dataIndex); |
| int length = m_data.elementAt(dataIndex + 1); |
| |
| // %OPT% We should cache this, I guess. |
| return m_chars.getString(offset, length); |
| } |
| else if (DTM.ELEMENT_NODE == type || DTM.DOCUMENT_FRAGMENT_NODE == type |
| || DTM.DOCUMENT_NODE == type) |
| { |
| return null; |
| } |
| else |
| { |
| int dataIndex = _dataOrQName(identity); |
| |
| if (dataIndex < 0) |
| { |
| dataIndex = -dataIndex; |
| dataIndex = m_data.elementAt(dataIndex + 1); |
| } |
| |
| return m_valuesOrPrefixes.indexToString(dataIndex); |
| } |
| } |
| |
| /** |
| * 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) |
| { |
| return m_expandedNameTable.getLocalName(_exptype(makeNodeIdentity(nodeHandle))); |
| } |
| |
| /** |
| * 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 = ""; |
| |
| if (null == m_entities) |
| return url; |
| |
| int n = m_entities.size(); |
| |
| for (int i = 0; i < n; i += ENTITY_FIELDS_PER) |
| { |
| String ename = (String) m_entities.elementAt(i + ENTITY_FIELD_NAME); |
| |
| if (null != ename && ename.equals(name)) |
| { |
| String nname = (String) m_entities.elementAt(i |
| + ENTITY_FIELD_NOTATIONNAME); |
| |
| if (null != nname) |
| { |
| |
| // 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 = (String) m_entities.elementAt(i + ENTITY_FIELD_SYSTEMID); |
| |
| if (null == url) |
| { |
| url = (String) m_entities.elementAt(i + ENTITY_FIELD_PUBLICID); |
| } |
| } |
| |
| break; |
| } |
| } |
| |
| return url; |
| } |
| |
| /** |
| * 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) |
| { |
| |
| int identity = makeNodeIdentity(nodeHandle); |
| int type = _type(identity); |
| |
| if (DTM.ELEMENT_NODE == type) |
| { |
| int prefixIndex = _dataOrQName(identity); |
| |
| if (0 == prefixIndex) |
| return ""; |
| else |
| { |
| String qname = m_valuesOrPrefixes.indexToString(prefixIndex); |
| |
| return getPrefix(qname, null); |
| } |
| } |
| else if (DTM.ATTRIBUTE_NODE == type) |
| { |
| int prefixIndex = _dataOrQName(identity); |
| |
| if (prefixIndex < 0) |
| { |
| prefixIndex = m_data.elementAt(-prefixIndex); |
| |
| String qname = m_valuesOrPrefixes.indexToString(prefixIndex); |
| |
| return getPrefix(qname, null); |
| } |
| } |
| |
| return ""; |
| } |
| |
| /** |
| * 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) |
| { |
| |
| for (int attrH = getFirstAttribute(nodeHandle); DTM.NULL != attrH; |
| attrH = getNextAttribute(attrH)) |
| { |
| String attrNS = getNamespaceURI(attrH); |
| String attrName = getLocalName(attrH); |
| boolean nsMatch = namespaceURI == attrNS |
| || (namespaceURI != null |
| && namespaceURI.equals(attrNS)); |
| |
| if (nsMatch && name.equals(attrName)) |
| return attrH; |
| } |
| |
| return DTM.NULL; |
| } |
| |
| /** |
| * Return the public identifier of the external subset, |
| * normalized as described in 4.2.2 External Entities [XML]. If there is |
| * no external subset or if it has no public identifier, this property |
| * has no value. |
| * |
| * @return the public identifier String object, or null if there is none. |
| */ |
| public String getDocumentTypeDeclarationPublicIdentifier() |
| { |
| |
| /** @todo: implement this org.apache.xml.dtm.DTMDefaultBase abstract method */ |
| error(XMLMessages.createXMLMessage(XMLErrorResources.ER_METHOD_NOT_SUPPORTED, null));//"Not yet supported!"); |
| |
| return null; |
| } |
| |
| /** |
| * 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) |
| { |
| |
| return m_expandedNameTable.getNamespace(_exptype(makeNodeIdentity(nodeHandle))); |
| } |
| |
| /** |
| * 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 identity = makeNodeIdentity(nodeHandle); |
| int type; |
| if(identity==DTM.NULL) // Separate lines because I wanted to breakpoint it |
| type = DTM.NULL; |
| else |
| type= _type(identity); |
| |
| if (isTextType(type)) |
| { |
| int dataIndex = _dataOrQName(identity); |
| int offset = m_data.elementAt(dataIndex); |
| int length = m_data.elementAt(dataIndex + 1); |
| |
| return m_xstrf.newstr(m_chars, offset, length); |
| } |
| else |
| { |
| int firstChild = _firstch(identity); |
| |
| if (DTM.NULL != firstChild) |
| { |
| int offset = -1; |
| int length = 0; |
| int startNode = identity; |
| |
| identity = firstChild; |
| |
| do { |
| type = _type(identity); |
| |
| if (isTextType(type)) |
| { |
| int dataIndex = _dataOrQName(identity); |
| |
| if (-1 == offset) |
| { |
| offset = m_data.elementAt(dataIndex); |
| } |
| |
| length += m_data.elementAt(dataIndex + 1); |
| } |
| |
| identity = getNextNodeIdentity(identity); |
| } while (DTM.NULL != identity && (_parent(identity) >= startNode)); |
| |
| if (length > 0) |
| { |
| return m_xstrf.newstr(m_chars, offset, length); |
| } |
| } |
| else if(type != DTM.ELEMENT_NODE) |
| { |
| int dataIndex = _dataOrQName(identity); |
| |
| if (dataIndex < 0) |
| { |
| dataIndex = -dataIndex; |
| dataIndex = m_data.elementAt(dataIndex + 1); |
| } |
| return m_xstrf.newstr(m_valuesOrPrefixes.indexToString(dataIndex)); |
| } |
| } |
| |
| return m_xstrf.emptystr(); |
| } |
| |
| /** |
| * Determine if the string-value of a node is whitespace |
| * |
| * @param nodeHandle The node Handle. |
| * |
| * @return Return true if the given node is whitespace. |
| */ |
| public boolean isWhitespace(int nodeHandle) |
| { |
| int identity = makeNodeIdentity(nodeHandle); |
| int type; |
| if(identity==DTM.NULL) // Separate lines because I wanted to breakpoint it |
| type = DTM.NULL; |
| else |
| type= _type(identity); |
| |
| if (isTextType(type)) |
| { |
| int dataIndex = _dataOrQName(identity); |
| int offset = m_data.elementAt(dataIndex); |
| int length = m_data.elementAt(dataIndex + 1); |
| |
| return m_chars.isWhitespace(offset, length); |
| } |
| return false; |
| } |
| |
| /** |
| * 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) |
| { |
| |
| Integer intObj; |
| boolean isMore = true; |
| |
| do |
| { |
| intObj = (Integer) m_idAttributes.get(elementId); |
| |
| if (null != intObj) |
| return makeNodeHandle(intObj.intValue()); |
| |
| if (!isMore || m_endDocumentOccured) |
| break; |
| |
| isMore = nextNode(); |
| } |
| while (null == intObj); |
| |
| return DTM.NULL; |
| } |
| |
| /** |
| * Get a prefix either from the qname or from the uri mapping, or just make |
| * one up! |
| * |
| * @param qname The qualified name, which may be null. |
| * @param uri The namespace URI, which may be null. |
| * |
| * @return The prefix if there is one, or null. |
| */ |
| public String getPrefix(String qname, String uri) |
| { |
| |
| String prefix; |
| int uriIndex = -1; |
| |
| if (null != uri && uri.length() > 0) |
| { |
| |
| do |
| { |
| uriIndex = m_prefixMappings.indexOf(uri, ++uriIndex); |
| } while ( (uriIndex & 0x01) == 0); |
| |
| if (uriIndex >= 0) |
| { |
| prefix = (String) m_prefixMappings.elementAt(uriIndex - 1); |
| } |
| else if (null != qname) |
| { |
| int indexOfNSSep = qname.indexOf(':'); |
| |
| if (qname.equals("xmlns")) |
| prefix = ""; |
| else if (qname.startsWith("xmlns:")) |
| prefix = qname.substring(indexOfNSSep + 1); |
| else |
| prefix = (indexOfNSSep > 0) |
| ? qname.substring(0, indexOfNSSep) : null; |
| } |
| else |
| { |
| prefix = null; |
| } |
| } |
| else if (null != qname) |
| { |
| int indexOfNSSep = qname.indexOf(':'); |
| |
| if (indexOfNSSep > 0) |
| { |
| if (qname.startsWith("xmlns:")) |
| prefix = qname.substring(indexOfNSSep + 1); |
| else |
| prefix = qname.substring(0, indexOfNSSep); |
| } |
| else |
| { |
| if (qname.equals("xmlns")) |
| prefix = ""; |
| else |
| prefix = null; |
| } |
| } |
| else |
| { |
| prefix = null; |
| } |
| |
| return prefix; |
| } |
| |
| /** |
| * Get a prefix either from the uri mapping, or just make |
| * one up! |
| * |
| * @param uri The namespace URI, which may be null. |
| * |
| * @return The prefix if there is one, or null. |
| */ |
| public int getIdForNamespace(String uri) |
| { |
| |
| return m_valuesOrPrefixes.stringToIndex(uri); |
| |
| } |
| |
| /** |
| * Get a prefix either from the qname or from the uri mapping, or just make |
| * one up! |
| * |
| * @return The prefix if there is one, or null. |
| */ |
| public String getNamespaceURI(String prefix) |
| { |
| |
| String uri = ""; |
| int prefixIndex = m_contextIndexes.peek() - 1 ; |
| |
| if(null == prefix) |
| prefix = ""; |
| |
| do |
| { |
| prefixIndex = m_prefixMappings.indexOf(prefix, ++prefixIndex); |
| } while ( (prefixIndex >= 0) && (prefixIndex & 0x01) == 0x01); |
| |
| if (prefixIndex > -1) |
| { |
| uri = (String) m_prefixMappings.elementAt(prefixIndex + 1); |
| } |
| |
| |
| return uri; |
| } |
| |
| /** |
| * Set an ID string to node association in the ID table. |
| * |
| * @param id The ID string. |
| * @param elem The associated element handle. |
| */ |
| public void setIDAttribute(String id, int elem) |
| { |
| m_idAttributes.put(id, new Integer(elem)); |
| } |
| |
| /** |
| * Check whether accumulated text should be stripped; if not, |
| * append the appropriate flavor of text/cdata node. |
| */ |
| protected void charactersFlush() |
| { |
| |
| if (m_textPendingStart >= 0) // -1 indicates no-text-in-progress |
| { |
| int length = m_chars.size() - m_textPendingStart; |
| boolean doStrip = false; |
| |
| if (getShouldStripWhitespace()) |
| { |
| doStrip = m_chars.isWhitespace(m_textPendingStart, length); |
| } |
| |
| if (doStrip) { |
| m_chars.setLength(m_textPendingStart); // Discard accumulated text |
| } else { |
| // Guard against characters/ignorableWhitespace events that |
| // contained no characters. They should not result in a node. |
| if (length > 0) { |
| int exName = m_expandedNameTable.getExpandedTypeID(DTM.TEXT_NODE); |
| int dataIndex = m_data.size(); |
| |
| m_previous = addNode(m_coalescedTextType, exName, |
| m_parents.peek(), m_previous, dataIndex, false); |
| |
| m_data.addElement(m_textPendingStart); |
| m_data.addElement(length); |
| } |
| } |
| |
| // Reset for next text block |
| m_textPendingStart = -1; |
| m_textType = m_coalescedTextType = DTM.TEXT_NODE; |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////// |
| // Implementation of the EntityResolver interface. |
| //////////////////////////////////////////////////////////////////// |
| |
| /** |
| * Resolve an external entity. |
| * |
| * <p>Always return null, so that the parser will use the system |
| * identifier provided in the XML document. This method implements |
| * the SAX default behaviour: application writers can override it |
| * in a subclass to do special translations such as catalog lookups |
| * or URI redirection.</p> |
| * |
| * @param publicId The public identifer, or null if none is |
| * available. |
| * @param systemId The system identifier provided in the XML |
| * document. |
| * @return The new input source, or null to require the |
| * default behaviour. |
| * @throws SAXException Any SAX exception, possibly |
| * wrapping another exception. |
| * @see org.xml.sax.EntityResolver#resolveEntity |
| * |
| * @throws SAXException |
| */ |
| public InputSource resolveEntity(String publicId, String systemId) |
| throws SAXException |
| { |
| return null; |
| } |
| |
| //////////////////////////////////////////////////////////////////// |
| // Implementation of DTDHandler interface. |
| //////////////////////////////////////////////////////////////////// |
| |
| /** |
| * Receive notification of a notation declaration. |
| * |
| * <p>By default, do nothing. Application writers may override this |
| * method in a subclass if they wish to keep track of the notations |
| * declared in a document.</p> |
| * |
| * @param name The notation name. |
| * @param publicId The notation public identifier, or null if not |
| * available. |
| * @param systemId The notation system identifier. |
| * @throws SAXException Any SAX exception, possibly |
| * wrapping another exception. |
| * @see org.xml.sax.DTDHandler#notationDecl |
| * |
| * @throws SAXException |
| */ |
| public void notationDecl(String name, String publicId, String systemId) |
| throws SAXException |
| { |
| |
| // no op |
| } |
| |
| /** |
| * Receive notification of an unparsed entity declaration. |
| * |
| * <p>By default, do nothing. Application writers may override this |
| * method in a subclass to keep track of the unparsed entities |
| * declared in a document.</p> |
| * |
| * @param name The entity name. |
| * @param publicId The entity public identifier, or null if not |
| * available. |
| * @param systemId The entity system identifier. |
| * @param notationName The name of the associated notation. |
| * @throws SAXException Any SAX exception, possibly |
| * wrapping another exception. |
| * @see org.xml.sax.DTDHandler#unparsedEntityDecl |
| * |
| * @throws SAXException |
| */ |
| public void unparsedEntityDecl( |
| String name, String publicId, String systemId, String notationName) |
| throws SAXException |
| { |
| |
| if (null == m_entities) |
| { |
| m_entities = new Vector(); |
| } |
| |
| try |
| { |
| systemId = SystemIDResolver.getAbsoluteURI(systemId, |
| getDocumentBaseURI()); |
| } |
| catch (Exception e) |
| { |
| throw new org.xml.sax.SAXException(e); |
| } |
| |
| // private static final int ENTITY_FIELD_PUBLICID = 0; |
| m_entities.addElement(publicId); |
| |
| // private static final int ENTITY_FIELD_SYSTEMID = 1; |
| m_entities.addElement(systemId); |
| |
| // private static final int ENTITY_FIELD_NOTATIONNAME = 2; |
| m_entities.addElement(notationName); |
| |
| // private static final int ENTITY_FIELD_NAME = 3; |
| m_entities.addElement(name); |
| } |
| |
| //////////////////////////////////////////////////////////////////// |
| // Implementation of ContentHandler interface. |
| //////////////////////////////////////////////////////////////////// |
| |
| /** |
| * Receive a Locator object for document events. |
| * |
| * <p>By default, do nothing. Application writers may override this |
| * method in a subclass if they wish to store the locator for use |
| * with other document events.</p> |
| * |
| * @param locator A locator for all SAX document events. |
| * @see org.xml.sax.ContentHandler#setDocumentLocator |
| * @see org.xml.sax.Locator |
| */ |
| public void setDocumentLocator(Locator locator) |
| { |
| m_locator = locator; |
| m_systemId = locator.getSystemId(); |
| } |
| |
| /** |
| * Receive notification of the beginning of the document. |
| * |
| * @throws SAXException Any SAX exception, possibly |
| * wrapping another exception. |
| * @see org.xml.sax.ContentHandler#startDocument |
| */ |
| public void startDocument() throws SAXException |
| { |
| if (DEBUG) |
| System.out.println("startDocument"); |
| |
| |
| int doc = addNode(DTM.DOCUMENT_NODE, |
| m_expandedNameTable.getExpandedTypeID(DTM.DOCUMENT_NODE), |
| DTM.NULL, DTM.NULL, 0, true); |
| |
| m_parents.push(doc); |
| m_previous = DTM.NULL; |
| |
| m_contextIndexes.push(m_prefixMappings.size()); // for the next element. |
| } |
| |
| /** |
| * Receive notification of the end of the document. |
| * |
| * @throws SAXException Any SAX exception, possibly |
| * wrapping another exception. |
| * @see org.xml.sax.ContentHandler#endDocument |
| */ |
| public void endDocument() throws SAXException |
| { |
| if (DEBUG) |
| System.out.println("endDocument"); |
| |
| charactersFlush(); |
| |
| m_nextsib.setElementAt(NULL,0); |
| |
| if (m_firstch.elementAt(0) == NOTPROCESSED) |
| m_firstch.setElementAt(NULL,0); |
| |
| if (DTM.NULL != m_previous) |
| m_nextsib.setElementAt(DTM.NULL,m_previous); |
| |
| m_parents = null; |
| m_prefixMappings = null; |
| m_contextIndexes = null; |
| |
| m_endDocumentOccured = true; |
| |
| // Bugzilla 4858: throw away m_locator. we cache m_systemId |
| m_locator = null; |
| } |
| |
| /** |
| * Receive notification of the start of a Namespace mapping. |
| * |
| * <p>By default, do nothing. Application writers may override this |
| * method in a subclass to take specific actions at the start of |
| * each Namespace prefix scope (such as storing the prefix mapping).</p> |
| * |
| * @param prefix The Namespace prefix being declared. |
| * @param uri The Namespace URI mapped to the prefix. |
| * @throws SAXException Any SAX exception, possibly |
| * wrapping another exception. |
| * @see org.xml.sax.ContentHandler#startPrefixMapping |
| */ |
| public void startPrefixMapping(String prefix, String uri) |
| throws SAXException |
| { |
| |
| if (DEBUG) |
| System.out.println("startPrefixMapping: prefix: " + prefix + ", uri: " |
| + uri); |
| |
| if(null == prefix) |
| prefix = ""; |
| m_prefixMappings.addElement(prefix); // JDK 1.1.x compat -sc |
| m_prefixMappings.addElement(uri); // JDK 1.1.x compat -sc |
| } |
| |
| /** |
| * Receive notification of the end of a Namespace mapping. |
| * |
| * <p>By default, do nothing. Application writers may override this |
| * method in a subclass to take specific actions at the end of |
| * each prefix mapping.</p> |
| * |
| * @param prefix The Namespace prefix being declared. |
| * @throws SAXException Any SAX exception, possibly |
| * wrapping another exception. |
| * @see org.xml.sax.ContentHandler#endPrefixMapping |
| */ |
| public void endPrefixMapping(String prefix) throws SAXException |
| { |
| if (DEBUG) |
| System.out.println("endPrefixMapping: prefix: " + prefix); |
| |
| if(null == prefix) |
| prefix = ""; |
| |
| int index = m_contextIndexes.peek() - 1; |
| |
| do |
| { |
| index = m_prefixMappings.indexOf(prefix, ++index); |
| } while ( (index >= 0) && ((index & 0x01) == 0x01) ); |
| |
| |
| if (index > -1) |
| { |
| m_prefixMappings.setElementAt("%@$#^@#", index); |
| m_prefixMappings.setElementAt("%@$#^@#", index + 1); |
| } |
| |
| // no op |
| } |
| |
| /** |
| * Check if a declaration has already been made for a given prefix. |
| * |
| * @param prefix non-null prefix string. |
| * |
| * @return true if the declaration has already been declared in the |
| * current context. |
| */ |
| protected boolean declAlreadyDeclared(String prefix) |
| { |
| |
| int startDecls = m_contextIndexes.peek(); |
| java.util.Vector prefixMappings = m_prefixMappings; |
| int nDecls = prefixMappings.size(); |
| |
| for (int i = startDecls; i < nDecls; i += 2) |
| { |
| String prefixDecl = (String) prefixMappings.elementAt(i); |
| |
| if (prefixDecl == null) |
| continue; |
| |
| if (prefixDecl.equals(prefix)) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| boolean m_pastFirstElement=false; |
| |
| /** |
| * Receive notification of the start of an element. |
| * |
| * <p>By default, do nothing. Application writers may override this |
| * method in a subclass to take specific actions at the start of |
| * each element (such as allocating a new tree node or writing |
| * output to a file).</p> |
| * |
| * @param uri The Namespace URI, or the empty string if the |
| * element has no Namespace URI or if Namespace |
| * processing is not being performed. |
| * @param localName The local name (without prefix), or the |
| * empty string if Namespace processing is not being |
| * performed. |
| * @param qName The qualified name (with prefix), or the |
| * empty string if qualified names are not available. |
| * @param attributes The specified or defaulted attributes. |
| * @throws SAXException Any SAX exception, possibly |
| * wrapping another exception. |
| * @see org.xml.sax.ContentHandler#startElement |
| */ |
| public void startElement( |
| String uri, String localName, String qName, Attributes attributes) |
| throws SAXException |
| { |
| if (DEBUG) |
| { |
| System.out.println("startElement: uri: " + uri + ", localname: " |
| + localName + ", qname: "+qName+", atts: " + attributes); |
| |
| boolean DEBUG_ATTRS=true; |
| if(DEBUG_ATTRS & attributes!=null) |
| { |
| int n = attributes.getLength(); |
| if(n==0) |
| System.out.println("\tempty attribute list"); |
| else for (int i = 0; i < n; i++) |
| System.out.println("\t attr: uri: " + attributes.getURI(i) + |
| ", localname: " + attributes.getLocalName(i) + |
| ", qname: " + attributes.getQName(i) + |
| ", type: " + attributes.getType(i) + |
| ", value: " + attributes.getValue(i) |
| ); |
| } |
| } |
| |
| charactersFlush(); |
| |
| int exName = m_expandedNameTable.getExpandedTypeID(uri, localName, DTM.ELEMENT_NODE); |
| String prefix = getPrefix(qName, uri); |
| int prefixIndex = (null != prefix) |
| ? m_valuesOrPrefixes.stringToIndex(qName) : 0; |
| |
| int elemNode = addNode(DTM.ELEMENT_NODE, exName, |
| m_parents.peek(), m_previous, prefixIndex, true); |
| |
| if(m_indexing) |
| indexNode(exName, elemNode); |
| |
| |
| m_parents.push(elemNode); |
| |
| int startDecls = m_contextIndexes.peek(); |
| int nDecls = m_prefixMappings.size(); |
| int prev = DTM.NULL; |
| |
| if(!m_pastFirstElement) |
| { |
| // SPECIAL CASE: Implied declaration at root element |
| prefix="xml"; |
| String declURL = "http://www.w3.org/XML/1998/namespace"; |
| exName = m_expandedNameTable.getExpandedTypeID(null, prefix, DTM.NAMESPACE_NODE); |
| int val = m_valuesOrPrefixes.stringToIndex(declURL); |
| prev = addNode(DTM.NAMESPACE_NODE, exName, elemNode, |
| prev, val, false); |
| m_pastFirstElement=true; |
| } |
| |
| for (int i = startDecls; i < nDecls; i += 2) |
| { |
| prefix = (String) m_prefixMappings.elementAt(i); |
| |
| if (prefix == null) |
| continue; |
| |
| String declURL = (String) m_prefixMappings.elementAt(i + 1); |
| |
| exName = m_expandedNameTable.getExpandedTypeID(null, prefix, DTM.NAMESPACE_NODE); |
| |
| int val = m_valuesOrPrefixes.stringToIndex(declURL); |
| |
| prev = addNode(DTM.NAMESPACE_NODE, exName, elemNode, |
| prev, val, false); |
| } |
| |
| int n = attributes.getLength(); |
| |
| for (int i = 0; i < n; i++) |
| { |
| String attrUri = attributes.getURI(i); |
| String attrQName = attributes.getQName(i); |
| String valString = attributes.getValue(i); |
| |
| prefix = getPrefix(attrQName, attrUri); |
| |
| int nodeType; |
| |
| String attrLocalName = attributes.getLocalName(i); |
| |
| if ((null != attrQName) |
| && (attrQName.equals("xmlns") |
| || attrQName.startsWith("xmlns:"))) |
| { |
| if (declAlreadyDeclared(prefix)) |
| continue; // go to the next attribute. |
| |
| nodeType = DTM.NAMESPACE_NODE; |
| } |
| else |
| { |
| nodeType = DTM.ATTRIBUTE_NODE; |
| |
| if (attributes.getType(i).equalsIgnoreCase("ID")) |
| setIDAttribute(valString, elemNode); |
| } |
| |
| // Bit of a hack... if somehow valString is null, stringToIndex will |
| // return -1, which will make things very unhappy. |
| if(null == valString) |
| valString = ""; |
| |
| int val = m_valuesOrPrefixes.stringToIndex(valString); |
| //String attrLocalName = attributes.getLocalName(i); |
| |
| if (null != prefix) |
| { |
| |
| prefixIndex = m_valuesOrPrefixes.stringToIndex(attrQName); |
| |
| int dataIndex = m_data.size(); |
| |
| m_data.addElement(prefixIndex); |
| m_data.addElement(val); |
| |
| val = -dataIndex; |
| } |
| |
| exName = m_expandedNameTable.getExpandedTypeID(attrUri, attrLocalName, nodeType); |
| prev = addNode(nodeType, exName, elemNode, prev, val, |
| false); |
| } |
| |
| if (DTM.NULL != prev) |
| m_nextsib.setElementAt(DTM.NULL,prev); |
| |
| if (null != m_wsfilter) |
| { |
| short wsv = m_wsfilter.getShouldStripSpace(makeNodeHandle(elemNode), this); |
| boolean shouldStrip = (DTMWSFilter.INHERIT == wsv) |
| ? getShouldStripWhitespace() |
| : (DTMWSFilter.STRIP == wsv); |
| |
| pushShouldStripWhitespace(shouldStrip); |
| } |
| |
| m_previous = DTM.NULL; |
| |
| m_contextIndexes.push(m_prefixMappings.size()); // for the children. |
| } |
| |
| /** |
| * Receive notification of the end of an element. |
| * |
| * <p>By default, do nothing. Application writers may override this |
| * method in a subclass to take specific actions at the end of |
| * each element (such as finalising a tree node or writing |
| * output to a file).</p> |
| * |
| * @param uri The Namespace URI, or the empty string if the |
| * element has no Namespace URI or if Namespace |
| * processing is not being performed. |
| * @param localName The local name (without prefix), or the |
| * empty string if Namespace processing is not being |
| * performed. |
| * @param qName The qualified XML 1.0 name (with prefix), or the |
| * empty string if qualified names are not available. |
| * @throws SAXException Any SAX exception, possibly |
| * wrapping another exception. |
| * @see org.xml.sax.ContentHandler#endElement |
| */ |
| public void endElement(String uri, String localName, String qName) |
| throws SAXException |
| { |
| if (DEBUG) |
| System.out.println("endElement: uri: " + uri + ", localname: " |
| + localName + ", qname: "+qName); |
| |
| charactersFlush(); |
| |
| // If no one noticed, startPrefixMapping is a drag. |
| // Pop the context for the last child (the one pushed by startElement) |
| m_contextIndexes.quickPop(1); |
| |
| // Do it again for this one (the one pushed by the last endElement). |
| int topContextIndex = m_contextIndexes.peek(); |
| if (topContextIndex != m_prefixMappings.size()) { |
| m_prefixMappings.setSize(topContextIndex); |
| } |
| |
| int lastNode = m_previous; |
| |
| m_previous = m_parents.pop(); |
| |
| // If lastNode is still DTM.NULL, this element had no children |
| if (DTM.NULL == lastNode) |
| m_firstch.setElementAt(DTM.NULL,m_previous); |
| else |
| m_nextsib.setElementAt(DTM.NULL,lastNode); |
| |
| popShouldStripWhitespace(); |
| } |
| |
| /** |
| * Receive notification of character data inside an element. |
| * |
| * <p>By default, do nothing. Application writers may override this |
| * method to take specific actions for each chunk of character data |
| * (such as adding the data to a node or buffer, or printing it to |
| * a file).</p> |
| * |
| * @param ch The characters. |
| * @param start The start position in the character array. |
| * @param length The number of characters to use from the |
| * character array. |
| * @throws SAXException Any SAX exception, possibly |
| * wrapping another exception. |
| * @see org.xml.sax.ContentHandler#characters |
| */ |
| public void characters(char ch[], int start, int length) throws SAXException |
| { |
| if (m_textPendingStart == -1) // First one in this block |
| { |
| m_textPendingStart = m_chars.size(); |
| m_coalescedTextType = m_textType; |
| } |
| // Type logic: If all adjacent text is CDATASections, the |
| // concatentated text is treated as a single CDATASection (see |
| // initialization above). If any were ordinary Text, the whole |
| // thing is treated as Text. This may be worth %REVIEW%ing. |
| else if (m_textType == DTM.TEXT_NODE) |
| { |
| m_coalescedTextType = DTM.TEXT_NODE; |
| } |
| |
| m_chars.append(ch, start, length); |
| } |
| |
| /** |
| * Receive notification of ignorable whitespace in element content. |
| * |
| * <p>By default, do nothing. Application writers may override this |
| * method to take specific actions for each chunk of ignorable |
| * whitespace (such as adding data to a node or buffer, or printing |
| * it to a file).</p> |
| * |
| * @param ch The whitespace characters. |
| * @param start The start position in the character array. |
| * @param length The number of characters to use from the |
| * character array. |
| * @throws SAXException Any SAX exception, possibly |
| * wrapping another exception. |
| * @see org.xml.sax.ContentHandler#ignorableWhitespace |
| */ |
| public void ignorableWhitespace(char ch[], int start, int length) |
| throws SAXException |
| { |
| |
| // %OPT% We can probably take advantage of the fact that we know this |
| // is whitespace. |
| characters(ch, start, length); |
| } |
| |
| /** |
| * Receive notification of a processing instruction. |
| * |
| * <p>By default, do nothing. Application writers may override this |
| * method in a subclass to take specific actions for each |
| * processing instruction, such as setting status variables or |
| * invoking other methods.</p> |
| * |
| * @param target The processing instruction target. |
| * @param data The processing instruction data, or null if |
| * none is supplied. |
| * @throws SAXException Any SAX exception, possibly |
| * wrapping another exception. |
| * @see org.xml.sax.ContentHandler#processingInstruction |
| */ |
| public void processingInstruction(String target, String data) |
| throws SAXException |
| { |
| if (DEBUG) |
| System.out.println("processingInstruction: target: " + target +", data: "+data); |
| |
| charactersFlush(); |
| |
| int exName = m_expandedNameTable.getExpandedTypeID(null, target, |
| DTM.PROCESSING_INSTRUCTION_NODE); |
| int dataIndex = m_valuesOrPrefixes.stringToIndex(data); |
| |
| m_previous = addNode(DTM.PROCESSING_INSTRUCTION_NODE, exName, |
| m_parents.peek(), m_previous, |
| dataIndex, false); |
| } |
| |
| /** |
| * Receive notification of a skipped entity. |
| * |
| * <p>By default, do nothing. Application writers may override this |
| * method in a subclass to take specific actions for each |
| * processing instruction, such as setting status variables or |
| * invoking other methods.</p> |
| * |
| * @param name The name of the skipped entity. |
| * @throws SAXException Any SAX exception, possibly |
| * wrapping another exception. |
| * @see org.xml.sax.ContentHandler#processingInstruction |
| */ |
| public void skippedEntity(String name) throws SAXException |
| { |
| |
| // %REVIEW% What should be done here? |
| // no op |
| } |
| |
| //////////////////////////////////////////////////////////////////// |
| // Implementation of the ErrorHandler interface. |
| //////////////////////////////////////////////////////////////////// |
| |
| /** |
| * Receive notification of a parser warning. |
| * |
| * <p>The default implementation does nothing. Application writers |
| * may override this method in a subclass to take specific actions |
| * for each warning, such as inserting the message in a log file or |
| * printing it to the console.</p> |
| * |
| * @param e The warning information encoded as an exception. |
| * @throws SAXException Any SAX exception, possibly |
| * wrapping another exception. |
| * @see org.xml.sax.ErrorHandler#warning |
| * @see org.xml.sax.SAXParseException |
| */ |
| public void warning(SAXParseException e) throws SAXException |
| { |
| |
| // %REVIEW% Is there anyway to get the JAXP error listener here? |
| System.err.println(e.getMessage()); |
| } |
| |
| /** |
| * Receive notification of a recoverable parser error. |
| * |
| * <p>The default implementation does nothing. Application writers |
| * may override this method in a subclass to take specific actions |
| * for each error, such as inserting the message in a log file or |
| * printing it to the console.</p> |
| * |
| * @param e The warning information encoded as an exception. |
| * @throws SAXException Any SAX exception, possibly |
| * wrapping another exception. |
| * @see org.xml.sax.ErrorHandler#warning |
| * @see org.xml.sax.SAXParseException |
| */ |
| public void error(SAXParseException e) throws SAXException |
| { |
| throw e; |
| } |
| |
| /** |
| * Report a fatal XML parsing error. |
| * |
| * <p>The default implementation throws a SAXParseException. |
| * Application writers may override this method in a subclass if |
| * they need to take specific actions for each fatal error (such as |
| * collecting all of the errors into a single report): in any case, |
| * the application must stop all regular processing when this |
| * method is invoked, since the document is no longer reliable, and |
| * the parser may no longer report parsing events.</p> |
| * |
| * @param e The error information encoded as an exception. |
| * @throws SAXException Any SAX exception, possibly |
| * wrapping another exception. |
| * @see org.xml.sax.ErrorHandler#fatalError |
| * @see org.xml.sax.SAXParseException |
| */ |
| public void fatalError(SAXParseException e) throws SAXException |
| { |
| throw e; |
| } |
| |
| //////////////////////////////////////////////////////////////////// |
| // Implementation of the DeclHandler interface. |
| //////////////////////////////////////////////////////////////////// |
| |
| /** |
| * Report an element type declaration. |
| * |
| * <p>The content model will consist of the string "EMPTY", the |
| * string "ANY", or a parenthesised group, optionally followed |
| * by an occurrence indicator. The model will be normalized so |
| * that all whitespace is removed,and will include the enclosing |
| * parentheses.</p> |
| * |
| * @param name The element type name. |
| * @param model The content model as a normalized string. |
| * @throws SAXException The application may raise an exception. |
| */ |
| public void elementDecl(String name, String model) throws SAXException |
| { |
| |
| // no op |
| } |
| |
| /** |
| * Report an attribute type declaration. |
| * |
| * <p>Only the effective (first) declaration for an attribute will |
| * be reported. The type will be one of the strings "CDATA", |
| * "ID", "IDREF", "IDREFS", "NMTOKEN", "NMTOKENS", "ENTITY", |
| * "ENTITIES", or "NOTATION", or a parenthesized token group with |
| * the separator "|" and all whitespace removed.</p> |
| * |
| * @param eName The name of the associated element. |
| * @param aName The name of the attribute. |
| * @param type A string representing the attribute type. |
| * @param valueDefault A string representing the attribute default |
| * ("#IMPLIED", "#REQUIRED", or "#FIXED") or null if |
| * none of these applies. |
| * @param value A string representing the attribute's default value, |
| * or null if there is none. |
| * @throws SAXException The application may raise an exception. |
| */ |
| public void attributeDecl( |
| String eName, String aName, String type, String valueDefault, String value) |
| throws SAXException |
| { |
| |
| // no op |
| } |
| |
| /** |
| * Report an internal entity declaration. |
| * |
| * <p>Only the effective (first) declaration for each entity |
| * will be reported.</p> |
| * |
| * @param name The name of the entity. If it is a parameter |
| * entity, the name will begin with '%'. |
| * @param value The replacement text of the entity. |
| * @throws SAXException The application may raise an exception. |
| * @see #externalEntityDecl |
| * @see org.xml.sax.DTDHandler#unparsedEntityDecl |
| */ |
| public void internalEntityDecl(String name, String value) |
| throws SAXException |
| { |
| |
| // no op |
| } |
| |
| /** |
| * Report a parsed external entity declaration. |
| * |
| * <p>Only the effective (first) declaration for each entity |
| * will be reported.</p> |
| * |
| * @param name The name of the entity. If it is a parameter |
| * entity, the name will begin with '%'. |
| * @param publicId The declared public identifier of the entity, or |
| * null if none was declared. |
| * @param systemId The declared system identifier of the entity. |
| * @throws SAXException The application may raise an exception. |
| * @see #internalEntityDecl |
| * @see org.xml.sax.DTDHandler#unparsedEntityDecl |
| */ |
| public void externalEntityDecl( |
| String name, String publicId, String systemId) throws SAXException |
| { |
| |
| // no op |
| } |
| |
| //////////////////////////////////////////////////////////////////// |
| // Implementation of the LexicalHandler interface. |
| //////////////////////////////////////////////////////////////////// |
| |
| /** |
| * Report the start of DTD declarations, if any. |
| * |
| * <p>Any declarations are assumed to be in the internal subset |
| * unless otherwise indicated by a {@link #startEntity startEntity} |
| * event.</p> |
| * |
| * <p>Note that the start/endDTD events will appear within |
| * the start/endDocument events from ContentHandler and |
| * before the first startElement event.</p> |
| * |
| * @param name The document type name. |
| * @param publicId The declared public identifier for the |
| * external DTD subset, or null if none was declared. |
| * @param systemId The declared system identifier for the |
| * external DTD subset, or null if none was declared. |
| * @throws SAXException The application may raise an |
| * exception. |
| * @see #endDTD |
| * @see #startEntity |
| */ |
| public void startDTD(String name, String publicId, String systemId) |
| throws SAXException |
| { |
| |
| m_insideDTD = true; |
| } |
| |
| /** |
| * Report the end of DTD declarations. |
| * |
| * @throws SAXException The application may raise an exception. |
| * @see #startDTD |
| */ |
| public void endDTD() throws SAXException |
| { |
| |
| m_insideDTD = false; |
| } |
| |
| /** |
| * Report the beginning of an entity in content. |
| * |
| * <p><strong>NOTE:</entity> entity references in attribute |
| * values -- and the start and end of the document entity -- |
| * are never reported.</p> |
| * |
| * <p>The start and end of the external DTD subset are reported |
| * using the pseudo-name "[dtd]". All other events must be |
| * properly nested within start/end entity events.</p> |
| * |
| * <p>Note that skipped entities will be reported through the |
| * {@link org.xml.sax.ContentHandler#skippedEntity skippedEntity} |
| * event, which is part of the ContentHandler interface.</p> |
| * |
| * @param name The name of the entity. If it is a parameter |
| * entity, the name will begin with '%'. |
| * @throws SAXException The application may raise an exception. |
| * @see #endEntity |
| * @see org.xml.sax.ext.DeclHandler#internalEntityDecl |
| * @see org.xml.sax.ext.DeclHandler#externalEntityDecl |
| */ |
| public void startEntity(String name) throws SAXException |
| { |
| |
| // no op |
| } |
| |
| /** |
| * Report the end of an entity. |
| * |
| * @param name The name of the entity that is ending. |
| * @throws SAXException The application may raise an exception. |
| * @see #startEntity |
| */ |
| public void endEntity(String name) throws SAXException |
| { |
| |
| // no op |
| } |
| |
| /** |
| * Report the start of a CDATA section. |
| * |
| * <p>The contents of the CDATA section will be reported through |
| * the regular {@link org.xml.sax.ContentHandler#characters |
| * characters} event.</p> |
| * |
| * @throws SAXException The application may raise an exception. |
| * @see #endCDATA |
| */ |
| public void startCDATA() throws SAXException |
| { |
| m_textType = DTM.CDATA_SECTION_NODE; |
| } |
| |
| /** |
| * Report the end of a CDATA section. |
| * |
| * @throws SAXException The application may raise an exception. |
| * @see #startCDATA |
| */ |
| public void endCDATA() throws SAXException |
| { |
| m_textType = DTM.TEXT_NODE; |
| } |
| |
| /** |
| * Report an XML comment anywhere in the document. |
| * |
| * <p>This callback will be used for comments inside or outside the |
| * document element, including comments in the external DTD |
| * subset (if read).</p> |
| * |
| * @param ch An array holding the characters in the comment. |
| * @param start The starting position in the array. |
| * @param length The number of characters to use from the array. |
| * @throws SAXException The application may raise an exception. |
| */ |
| public void comment(char ch[], int start, int length) throws SAXException |
| { |
| |
| if (m_insideDTD) // ignore comments if we're inside the DTD |
| return; |
| |
| charactersFlush(); |
| |
| int exName = m_expandedNameTable.getExpandedTypeID(DTM.COMMENT_NODE); |
| |
| // For now, treat comments as strings... I guess we should do a |
| // seperate FSB buffer instead. |
| int dataIndex = m_valuesOrPrefixes.stringToIndex(new String(ch, start, |
| length)); |
| |
| |
| m_previous = addNode(DTM.COMMENT_NODE, exName, |
| m_parents.peek(), m_previous, dataIndex, false); |
| } |
| |
| /** |
| * Set a run time property for this DTM instance. |
| * |
| * %REVIEW% Now that we no longer use this method to support |
| * getSourceLocatorFor, can we remove it? |
| * |
| * @param property a <code>String</code> value |
| * @param value an <code>Object</code> value |
| */ |
| public void setProperty(String property, Object value) |
| { |
| } |
| |
| /** Retrieve the SourceLocator associated with a specific node. |
| * This is only meaningful if the XalanProperties.SOURCE_LOCATION flag was |
| * set True using setProperty; if it was never set, or was set false, we |
| * will return null. |
| * |
| * (We _could_ return a locator with the document's base URI and bogus |
| * line/column information. Trying that; see the else clause.) |
| * */ |
| public SourceLocator getSourceLocatorFor(int node) |
| { |
| if (m_useSourceLocationProperty) |
| { |
| |
| node = makeNodeIdentity(node); |
| |
| |
| return new NodeLocator(null, |
| m_sourceSystemId.elementAt(node), |
| m_sourceLine.elementAt(node), |
| m_sourceColumn.elementAt(node)); |
| } |
| else if(m_locator!=null) |
| { |
| return new NodeLocator(null,m_locator.getSystemId(),-1,-1); |
| } |
| else if(m_systemId!=null) |
| { |
| return new NodeLocator(null,m_systemId,-1,-1); |
| } |
| return null; |
| } |
| |
| public String getFixedNames(int type){ |
| return m_fixednames[type]; |
| } |
| } |