001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements. See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership. The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the  "License");
007     * you may not use this file except in compliance with the License.
008     * You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing, software
013     * distributed under the License is distributed on an "AS IS" BASIS,
014     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    /*
019     * $Id: SAX2DTM.java 468653 2006-10-28 07:07:05Z minchau $
020     */
021    package org.apache.xml.dtm.ref.sax2dtm;
022    
023    import java.util.Hashtable;
024    import java.util.Vector;
025    import javax.xml.transform.Source;
026    import javax.xml.transform.SourceLocator;
027    
028    import org.apache.xml.dtm.*;
029    import org.apache.xml.dtm.ref.*;
030    import org.apache.xml.utils.StringVector;
031    import org.apache.xml.utils.IntVector;
032    import org.apache.xml.utils.FastStringBuffer;
033    import org.apache.xml.utils.IntStack;
034    import org.apache.xml.utils.SuballocatedIntVector;
035    import org.apache.xml.utils.SystemIDResolver;
036    import org.apache.xml.utils.WrappedRuntimeException;
037    import org.apache.xml.utils.XMLString;
038    import org.apache.xml.utils.XMLStringFactory;
039    import org.apache.xml.res.XMLErrorResources;
040    import org.apache.xml.res.XMLMessages;
041    import org.xml.sax.*;
042    import org.xml.sax.ext.*;
043    
044    /**
045     * This class implements a DTM that tends to be optimized more for speed than
046     * for compactness, that is constructed via SAX2 ContentHandler events.
047     */
048    public class SAX2DTM extends DTMDefaultBaseIterators
049            implements EntityResolver, DTDHandler, ContentHandler, ErrorHandler,
050                       DeclHandler, LexicalHandler
051    {
052      /** Set true to monitor SAX events and similar diagnostic info. */
053      private static final boolean DEBUG = false;
054    
055      /**
056       * If we're building the model incrementally on demand, we need to
057       * be able to tell the source when to send us more data.
058       *
059       * Note that if this has not been set, and you attempt to read ahead
060       * of the current build point, we'll probably throw a null-pointer
061       * exception. We could try to wait-and-retry instead, as a very poor
062       * fallback, but that has all the known problems with multithreading
063       * on multiprocessors and we Don't Want to Go There.
064       *
065       * @see setIncrementalSAXSource
066       */
067      private IncrementalSAXSource m_incrementalSAXSource = null;
068    
069      /**
070       * All the character content, including attribute values, are stored in
071       * this buffer.
072       *
073       * %REVIEW% Should this have an option of being shared across DTMs?
074       * Sequentially only; not threadsafe... Currently, I think not.
075       *
076       * %REVIEW% Initial size was pushed way down to reduce weight of RTFs.
077       * pending reduction in number of RTF DTMs. Now that we're sharing a DTM
078       * between RTFs, and tail-pruning... consider going back to the larger/faster.
079       *
080       * Made protected rather than private so SAX2RTFDTM can access it.
081       */
082      //private FastStringBuffer m_chars = new FastStringBuffer(13, 13);
083      protected FastStringBuffer m_chars;
084    
085      /** This vector holds offset and length data.
086       */
087      protected SuballocatedIntVector m_data;
088    
089      /** The parent stack, needed only for construction.
090       * Made protected rather than private so SAX2RTFDTM can access it.
091       */
092      transient protected IntStack m_parents;
093    
094      /** The current previous node, needed only for construction time.
095       * Made protected rather than private so SAX2RTFDTM can access it.
096       */
097      transient protected int m_previous = 0;
098    
099      /** Namespace support, only relevent at construction time.
100       * Made protected rather than private so SAX2RTFDTM can access it.
101       */
102      transient protected java.util.Vector m_prefixMappings =
103        new java.util.Vector();
104    
105      /** Namespace support, only relevent at construction time.
106       * Made protected rather than private so SAX2RTFDTM can access it.
107       */
108      transient protected IntStack m_contextIndexes;
109    
110      /** Type of next characters() event within text block in prgress. */
111      transient protected int m_textType = DTM.TEXT_NODE;
112    
113      /**
114       * Type of coalesced text block. See logic in the characters()
115       * method.
116       */
117      transient protected int m_coalescedTextType = DTM.TEXT_NODE;
118    
119      /** The SAX Document locator */
120      transient protected Locator m_locator = null;
121    
122      /** The SAX Document system-id */
123      transient private String m_systemId = null;
124    
125      /** We are inside the DTD.  This is used for ignoring comments.  */
126      transient protected boolean m_insideDTD = false;
127    
128      /** Tree Walker for dispatchToEvents. */
129      protected DTMTreeWalker m_walker = new DTMTreeWalker();
130    
131      /** pool of string values that come as strings. */
132      protected DTMStringPool m_valuesOrPrefixes;
133    
134      /** End document has been reached.
135       * Made protected rather than private so SAX2RTFDTM can access it.
136       */
137      protected boolean m_endDocumentOccured = false;
138    
139      /** Data or qualified name values, one array element for each node. */
140      protected SuballocatedIntVector m_dataOrQName;
141    
142      /**
143       * This table holds the ID string to node associations, for
144       * XML IDs.
145       */
146      protected Hashtable m_idAttributes = new Hashtable();
147    
148      /**
149       * fixed dom-style names.
150       */
151      private static final String[] m_fixednames = { null, 
152                        null,  // nothing, Element
153                        null, "#text",  // Attr, Text
154                        "#cdata_section", null,  // CDATA, EntityReference
155                        null, null,  // Entity, PI
156                        "#comment", "#document",  // Comment, Document
157                        null, "#document-fragment",  // Doctype, DocumentFragment
158                        null };  // Notation
159    
160      /**
161       * Vector of entities.  Each record is composed of four Strings:
162       *  publicId, systemID, notationName, and name.
163       */
164      private Vector m_entities = null;
165    
166      /** m_entities public ID offset. */
167      private static final int ENTITY_FIELD_PUBLICID = 0;
168    
169      /** m_entities system ID offset. */
170      private static final int ENTITY_FIELD_SYSTEMID = 1;
171    
172      /** m_entities notation name offset. */
173      private static final int ENTITY_FIELD_NOTATIONNAME = 2;
174    
175      /** m_entities name offset. */
176      private static final int ENTITY_FIELD_NAME = 3;
177    
178      /** Number of entries per record for m_entities. */
179      private static final int ENTITY_FIELDS_PER = 4;
180    
181      /**
182       * The starting offset within m_chars for the text or
183       * CDATA_SECTION node currently being acumulated,
184       * or -1 if there is no text node in progress
185       */
186      protected int m_textPendingStart = -1;
187    
188      /**
189       * Describes whether information about document source location
190       * should be maintained or not.
191       * 
192       * Made protected for access by SAX2RTFDTM.
193       */
194      protected boolean m_useSourceLocationProperty = false;
195    
196       /** Made protected for access by SAX2RTFDTM.
197       */
198      protected StringVector m_sourceSystemId;
199       /** Made protected for access by SAX2RTFDTM.
200       */
201      protected IntVector m_sourceLine;
202       /** Made protected for access by SAX2RTFDTM.
203       */
204      protected IntVector m_sourceColumn;
205      
206      /**
207       * Construct a SAX2DTM object using the default block size.
208       *
209       * @param mgr The DTMManager who owns this DTM.
210       * @param source the JAXP 1.1 Source object for this DTM.
211       * @param dtmIdentity The DTM identity ID for this DTM.
212       * @param whiteSpaceFilter The white space filter for this DTM, which may
213       *                         be null.
214       * @param xstringfactory XMLString factory for creating character content.
215       * @param doIndexing true if the caller considers it worth it to use
216       *                   indexing schemes.
217       */
218      public SAX2DTM(DTMManager mgr, Source source, int dtmIdentity,
219                     DTMWSFilter whiteSpaceFilter,
220                     XMLStringFactory xstringfactory,
221                     boolean doIndexing)
222      {
223    
224        this(mgr, source, dtmIdentity, whiteSpaceFilter,
225              xstringfactory, doIndexing, DEFAULT_BLOCKSIZE, true, false);
226      }
227      
228      /**
229       * Construct a SAX2DTM object ready to be constructed from SAX2
230       * ContentHandler events.
231       *
232       * @param mgr The DTMManager who owns this DTM.
233       * @param source the JAXP 1.1 Source object for this DTM.
234       * @param dtmIdentity The DTM identity ID for this DTM.
235       * @param whiteSpaceFilter The white space filter for this DTM, which may
236       *                         be null.
237       * @param xstringfactory XMLString factory for creating character content.
238       * @param doIndexing true if the caller considers it worth it to use
239       *                   indexing schemes.
240       * @param blocksize The block size of the DTM.
241       * @param usePrevsib true if we want to build the previous sibling node array.
242       * @param newNameTable true if we want to use a new ExpandedNameTable for this DTM.
243       */
244      public SAX2DTM(DTMManager mgr, Source source, int dtmIdentity,
245                     DTMWSFilter whiteSpaceFilter,
246                     XMLStringFactory xstringfactory,
247                     boolean doIndexing,
248                     int blocksize,
249                     boolean usePrevsib,
250                     boolean newNameTable)
251      {
252    
253        super(mgr, source, dtmIdentity, whiteSpaceFilter,
254              xstringfactory, doIndexing, blocksize, usePrevsib, newNameTable);
255    
256        // %OPT% Use smaller sizes for all internal storage units when
257        // the blocksize is small. This reduces the cost of creating an RTF.
258        if (blocksize <= 64) 
259        {
260          m_data = new SuballocatedIntVector(blocksize, DEFAULT_NUMBLOCKS_SMALL);
261          m_dataOrQName = new SuballocatedIntVector(blocksize, DEFAULT_NUMBLOCKS_SMALL);
262          m_valuesOrPrefixes = new DTMStringPool(16);
263          m_chars = new FastStringBuffer(7, 10);
264          m_contextIndexes = new IntStack(4);
265          m_parents = new IntStack(4);
266        }
267        else
268        {
269          m_data = new SuballocatedIntVector(blocksize, DEFAULT_NUMBLOCKS);
270          m_dataOrQName = new SuballocatedIntVector(blocksize, DEFAULT_NUMBLOCKS);
271          m_valuesOrPrefixes = new DTMStringPool();
272          m_chars = new FastStringBuffer(10, 13);
273          m_contextIndexes = new IntStack();
274          m_parents = new IntStack();
275        }
276             
277        // %REVIEW%  Initial size pushed way down to reduce weight of RTFs
278        // (I'm not entirely sure 0 would work, so I'm playing it safe for now.)
279        //m_data = new SuballocatedIntVector(doIndexing ? (1024*2) : 512, 1024);
280        //m_data = new SuballocatedIntVector(blocksize);
281    
282        m_data.addElement(0);   // Need placeholder in case index into here must be <0.
283    
284        //m_dataOrQName = new SuballocatedIntVector(blocksize);
285        
286        // m_useSourceLocationProperty=org.apache.xalan.processor.TransformerFactoryImpl.m_source_location;
287        m_useSourceLocationProperty = mgr.getSource_location();
288        m_sourceSystemId = (m_useSourceLocationProperty) ? new StringVector() : null;
289            m_sourceLine = (m_useSourceLocationProperty) ?  new IntVector() : null;
290        m_sourceColumn = (m_useSourceLocationProperty) ?  new IntVector() : null; 
291      }
292    
293      /**
294       * Set whether information about document source location
295       * should be maintained or not. 
296       */
297      public void setUseSourceLocation(boolean useSourceLocation)
298      {
299        m_useSourceLocationProperty = useSourceLocation;
300      }
301    
302      /**
303       * Get the data or qualified name for the given node identity.
304       *
305       * @param identity The node identity.
306       *
307       * @return The data or qualified name, or DTM.NULL.
308       */
309      protected int _dataOrQName(int identity)
310      {
311    
312        if (identity < m_size)
313          return m_dataOrQName.elementAt(identity);
314    
315        // Check to see if the information requested has been processed, and,
316        // if not, advance the iterator until we the information has been
317        // processed.
318        while (true)
319        {
320          boolean isMore = nextNode();
321    
322          if (!isMore)
323            return NULL;
324          else if (identity < m_size)
325            return m_dataOrQName.elementAt(identity);
326        }
327      }
328    
329      /**
330       * Ask the CoRoutine parser to doTerminate and clear the reference.
331       */
332      public void clearCoRoutine()
333      {
334        clearCoRoutine(true);
335      }
336    
337      /**
338       * Ask the CoRoutine parser to doTerminate and clear the reference. If
339       * the CoRoutine parser has already been cleared, this will have no effect.
340       *
341       * @param callDoTerminate true of doTerminate should be called on the
342       * coRoutine parser.
343       */
344      public void clearCoRoutine(boolean callDoTerminate)
345      {
346    
347        if (null != m_incrementalSAXSource)
348        {
349          if (callDoTerminate)
350            m_incrementalSAXSource.deliverMoreNodes(false);
351    
352          m_incrementalSAXSource = null;
353        }
354      }
355    
356      /**
357       * Bind a IncrementalSAXSource to this DTM. If we discover we need nodes
358       * that have not yet been built, we will ask this object to send us more
359       * events, and it will manage interactions with its data sources.
360       *
361       * Note that we do not actually build the IncrementalSAXSource, since we don't
362       * know what source it's reading from, what thread that source will run in,
363       * or when it will run.
364       *
365       * @param incrementalSAXSource The parser that we want to recieve events from
366       * on demand.
367       */
368      public void setIncrementalSAXSource(IncrementalSAXSource incrementalSAXSource)
369      {
370    
371        // Establish coroutine link so we can request more data
372        //
373        // Note: It's possible that some versions of IncrementalSAXSource may
374        // not actually use a CoroutineManager, and hence may not require
375        // that we obtain an Application Coroutine ID. (This relies on the
376        // coroutine transaction details having been encapsulated in the
377        // IncrementalSAXSource.do...() methods.)
378        m_incrementalSAXSource = incrementalSAXSource;
379    
380        // Establish SAX-stream link so we can receive the requested data
381        incrementalSAXSource.setContentHandler(this);
382        incrementalSAXSource.setLexicalHandler(this);
383        incrementalSAXSource.setDTDHandler(this);
384    
385        // Are the following really needed? incrementalSAXSource doesn't yet
386        // support them, and they're mostly no-ops here...
387        //incrementalSAXSource.setErrorHandler(this);
388        //incrementalSAXSource.setDeclHandler(this);
389      }
390    
391      /**
392       * getContentHandler returns "our SAX builder" -- the thing that
393       * someone else should send SAX events to in order to extend this
394       * DTM model.
395       *
396       * %REVIEW% Should this return null if constrution already done/begun?
397       *
398       * @return null if this model doesn't respond to SAX events,
399       * "this" if the DTM object has a built-in SAX ContentHandler,
400       * the IncrementalSAXSource if we're bound to one and should receive
401       * the SAX stream via it for incremental build purposes...
402       */
403      public ContentHandler getContentHandler()
404      {
405    
406        if (m_incrementalSAXSource instanceof IncrementalSAXSource_Filter)
407          return (ContentHandler) m_incrementalSAXSource;
408        else
409          return this;
410      }
411    
412      /**
413       * Return this DTM's lexical handler.
414       *
415       * %REVIEW% Should this return null if constrution already done/begun?
416       *
417       * @return null if this model doesn't respond to lexical SAX events,
418       * "this" if the DTM object has a built-in SAX ContentHandler,
419       * the IncrementalSAXSource if we're bound to one and should receive
420       * the SAX stream via it for incremental build purposes...
421       */
422      public LexicalHandler getLexicalHandler()
423      {
424    
425        if (m_incrementalSAXSource instanceof IncrementalSAXSource_Filter)
426          return (LexicalHandler) m_incrementalSAXSource;
427        else
428          return this;
429      }
430    
431      /**
432       * Return this DTM's EntityResolver.
433       *
434       * @return null if this model doesn't respond to SAX entity ref events.
435       */
436      public EntityResolver getEntityResolver()
437      {
438        return this;
439      }
440    
441      /**
442       * Return this DTM's DTDHandler.
443       *
444       * @return null if this model doesn't respond to SAX dtd events.
445       */
446      public DTDHandler getDTDHandler()
447      {
448        return this;
449      }
450    
451      /**
452       * Return this DTM's ErrorHandler.
453       *
454       * @return null if this model doesn't respond to SAX error events.
455       */
456      public ErrorHandler getErrorHandler()
457      {
458        return this;
459      }
460    
461      /**
462       * Return this DTM's DeclHandler.
463       *
464       * @return null if this model doesn't respond to SAX Decl events.
465       */
466      public DeclHandler getDeclHandler()
467      {
468        return this;
469      }
470    
471      /**
472       * @return true iff we're building this model incrementally (eg
473       * we're partnered with a IncrementalSAXSource) and thus require that the
474       * transformation and the parse run simultaneously. Guidance to the
475       * DTMManager.
476       */
477      public boolean needsTwoThreads()
478      {
479        return null != m_incrementalSAXSource;
480      }
481    
482      /**
483       * Directly call the
484       * characters method on the passed ContentHandler for the
485       * string-value of the given node (see http://www.w3.org/TR/xpath#data-model
486       * for the definition of a node's string-value). Multiple calls to the
487       * ContentHandler's characters methods may well occur for a single call to
488       * this method.
489       *
490       * @param nodeHandle The node ID.
491       * @param ch A non-null reference to a ContentHandler.
492       * @param normalize true if the content should be normalized according to
493       * the rules for the XPath
494       * <a href="http://www.w3.org/TR/xpath#function-normalize-space">normalize-space</a>
495       * function.
496       *
497       * @throws SAXException
498       */
499      public void dispatchCharactersEvents(int nodeHandle, ContentHandler ch,
500                                           boolean normalize)
501              throws SAXException
502      {
503    
504        int identity = makeNodeIdentity(nodeHandle);
505        
506        if (identity == DTM.NULL)
507          return;
508        
509        int type = _type(identity);
510    
511        if (isTextType(type))
512        {
513          int dataIndex = m_dataOrQName.elementAt(identity);
514          int offset = m_data.elementAt(dataIndex);
515          int length = m_data.elementAt(dataIndex + 1);
516    
517          if(normalize)
518            m_chars.sendNormalizedSAXcharacters(ch, offset, length);
519          else
520            m_chars.sendSAXcharacters(ch, offset, length);
521        }
522        else
523        {
524          int firstChild = _firstch(identity);
525    
526          if (DTM.NULL != firstChild)
527          {
528            int offset = -1;
529            int length = 0;
530            int startNode = identity;
531    
532            identity = firstChild;
533    
534            do {
535              type = _type(identity);
536    
537              if (isTextType(type))
538              {
539                int dataIndex = _dataOrQName(identity);
540    
541                if (-1 == offset)
542                {
543                  offset = m_data.elementAt(dataIndex);
544                }
545    
546                length += m_data.elementAt(dataIndex + 1);
547              }
548    
549              identity = getNextNodeIdentity(identity);
550            } while (DTM.NULL != identity && (_parent(identity) >= startNode));
551    
552            if (length > 0)
553            {
554              if(normalize)
555                m_chars.sendNormalizedSAXcharacters(ch, offset, length);
556              else
557                m_chars.sendSAXcharacters(ch, offset, length);
558            }
559          }
560          else if(type != DTM.ELEMENT_NODE)
561          {
562            int dataIndex = _dataOrQName(identity);
563    
564            if (dataIndex < 0)
565            {
566              dataIndex = -dataIndex;
567              dataIndex = m_data.elementAt(dataIndex + 1);
568            }
569    
570            String str = m_valuesOrPrefixes.indexToString(dataIndex);
571    
572              if(normalize)
573                FastStringBuffer.sendNormalizedSAXcharacters(str.toCharArray(),
574                                                             0, str.length(), ch);
575              else
576                ch.characters(str.toCharArray(), 0, str.length());
577          }
578        }
579      }
580    
581    
582      /**
583       * Given a node handle, return its DOM-style node name. This will
584       * include names such as #text or #document.
585       *
586       * @param nodeHandle the id of the node.
587       * @return String Name of this node, which may be an empty string.
588       * %REVIEW% Document when empty string is possible...
589       * %REVIEW-COMMENT% It should never be empty, should it?
590       */
591      public String getNodeName(int nodeHandle)
592      {
593    
594        int expandedTypeID = getExpandedTypeID(nodeHandle);
595        // If just testing nonzero, no need to shift...
596        int namespaceID = m_expandedNameTable.getNamespaceID(expandedTypeID);                     
597    
598        if (0 == namespaceID)
599        {
600          // Don't retrieve name until/unless needed
601          // String name = m_expandedNameTable.getLocalName(expandedTypeID);
602          int type = getNodeType(nodeHandle);
603    
604          if (type == DTM.NAMESPACE_NODE)
605          {
606            if (null == m_expandedNameTable.getLocalName(expandedTypeID))
607              return "xmlns";
608            else
609              return "xmlns:" + m_expandedNameTable.getLocalName(expandedTypeID);
610          }
611          else if (0 == m_expandedNameTable.getLocalNameID(expandedTypeID))
612          {
613            return m_fixednames[type];
614          }
615          else
616            return m_expandedNameTable.getLocalName(expandedTypeID);
617        }
618        else
619        {
620          int qnameIndex = m_dataOrQName.elementAt(makeNodeIdentity(nodeHandle));
621    
622          if (qnameIndex < 0)
623          {
624            qnameIndex = -qnameIndex;
625            qnameIndex = m_data.elementAt(qnameIndex);
626          }
627    
628          return m_valuesOrPrefixes.indexToString(qnameIndex);
629        }
630      }
631    
632      /**
633       * Given a node handle, return the XPath node name.  This should be
634       * the name as described by the XPath data model, NOT the DOM-style
635       * name.
636       *
637       * @param nodeHandle the id of the node.
638       * @return String Name of this node, which may be an empty string.
639       */
640      public String getNodeNameX(int nodeHandle)
641      {
642    
643        int expandedTypeID = getExpandedTypeID(nodeHandle);    
644        int namespaceID = m_expandedNameTable.getNamespaceID(expandedTypeID);                      
645    
646        if (0 == namespaceID)
647        {
648          String name = m_expandedNameTable.getLocalName(expandedTypeID);
649    
650          if (name == null)
651            return "";
652          else
653            return name;
654        }
655        else
656        {
657          int qnameIndex = m_dataOrQName.elementAt(makeNodeIdentity(nodeHandle));
658    
659          if (qnameIndex < 0)
660          {
661            qnameIndex = -qnameIndex;
662            qnameIndex = m_data.elementAt(qnameIndex);
663          }
664    
665          return m_valuesOrPrefixes.indexToString(qnameIndex);
666        }
667      }
668    
669      /**
670       *     5. [specified] A flag indicating whether this attribute was actually
671       *        specified in the start-tag of its element, or was defaulted from the
672       *        DTD.
673       *
674       * @param attributeHandle Must be a valid handle to an attribute node.
675       * @return <code>true</code> if the attribute was specified;
676       *         <code>false</code> if it was defaulted.
677       */
678      public boolean isAttributeSpecified(int attributeHandle)
679      {
680    
681        // I'm not sure if I want to do anything with this...
682        return true;  // ??
683      }
684    
685      /**
686       *   A document type declaration information item has the following properties:
687       *
688       *     1. [system identifier] The system identifier of the external subset, if
689       *        it exists. Otherwise this property has no value.
690       *
691       * @return the system identifier String object, or null if there is none.
692       */
693      public String getDocumentTypeDeclarationSystemIdentifier()
694      {
695    
696        /** @todo: implement this org.apache.xml.dtm.DTMDefaultBase abstract method */
697        error(XMLMessages.createXMLMessage(XMLErrorResources.ER_METHOD_NOT_SUPPORTED, null));//"Not yet supported!");
698    
699        return null;
700      }
701    
702      /**
703       * Get the next node identity value in the list, and call the iterator
704       * if it hasn't been added yet.
705       *
706       * @param identity The node identity (index).
707       * @return identity+1, or DTM.NULL.
708       */
709      protected int getNextNodeIdentity(int identity)
710      {
711    
712        identity += 1;
713    
714        while (identity >= m_size)
715        {
716          if (null == m_incrementalSAXSource)
717            return DTM.NULL;
718    
719          nextNode();
720        }
721    
722        return identity;
723      }
724    
725      /**
726       * Directly create SAX parser events from a subtree.
727       *
728       * @param nodeHandle The node ID.
729       * @param ch A non-null reference to a ContentHandler.
730       *
731       * @throws org.xml.sax.SAXException
732       */
733      public void dispatchToEvents(int nodeHandle, org.xml.sax.ContentHandler ch)
734              throws org.xml.sax.SAXException
735      {
736    
737        DTMTreeWalker treeWalker = m_walker;
738        ContentHandler prevCH = treeWalker.getcontentHandler();
739    
740        if (null != prevCH)
741        {
742          treeWalker = new DTMTreeWalker();
743        }
744    
745        treeWalker.setcontentHandler(ch);
746        treeWalker.setDTM(this);
747    
748        try
749        {
750          treeWalker.traverse(nodeHandle);
751        }
752        finally
753        {
754          treeWalker.setcontentHandler(null);
755        }
756      }
757    
758      /**
759       * Get the number of nodes that have been added.
760       *
761       * @return The number of that are currently in the tree.
762       */
763      public int getNumberOfNodes()
764      {
765        return m_size;
766      }
767    
768      /**
769       * This method should try and build one or more nodes in the table.
770       *
771       * @return The true if a next node is found or false if
772       *         there are no more nodes.
773       */
774      protected boolean nextNode()
775      {
776    
777        if (null == m_incrementalSAXSource)
778          return false;
779    
780        if (m_endDocumentOccured)
781        {
782          clearCoRoutine();
783    
784          return false;
785        }
786    
787        Object gotMore = m_incrementalSAXSource.deliverMoreNodes(true);
788    
789        // gotMore may be a Boolean (TRUE if still parsing, FALSE if
790        // EOF) or an exception if IncrementalSAXSource malfunctioned
791        // (code error rather than user error).
792        //
793        // %REVIEW% Currently the ErrorHandlers sketched herein are
794        // no-ops, so I'm going to initially leave this also as a
795        // no-op.
796        if (!(gotMore instanceof Boolean))
797        {
798          if(gotMore instanceof RuntimeException)
799          {
800            throw (RuntimeException)gotMore;
801          }
802          else if(gotMore instanceof Exception)
803          {
804            throw new WrappedRuntimeException((Exception)gotMore);
805          }
806          // for now...
807          clearCoRoutine();
808    
809          return false;
810    
811          // %TBD%
812        }
813    
814        if (gotMore != Boolean.TRUE)
815        {
816    
817          // EOF reached without satisfying the request
818          clearCoRoutine();  // Drop connection, stop trying
819    
820          // %TBD% deregister as its listener?
821        }
822    
823        return true;
824      }
825    
826      /**
827       * Bottleneck determination of text type.
828       *
829       * @param type oneof DTM.XXX_NODE.
830       *
831       * @return true if this is a text or cdata section.
832       */
833      private final boolean isTextType(int type)
834      {
835        return (DTM.TEXT_NODE == type || DTM.CDATA_SECTION_NODE == type);
836      }
837    
838    //    /**
839    //     * Ensure that the size of the information arrays can hold another entry
840    //     * at the given index.
841    //     *
842    //     * @param on exit from this function, the information arrays sizes must be
843    //     * at least index+1.
844    //     *
845    //     * NEEDSDOC @param index
846    //     */
847    //    protected void ensureSize(int index)
848    //    {
849    //          // dataOrQName is an SuballocatedIntVector and hence self-sizing.
850    //          // But DTMDefaultBase may need fixup.
851    //        super.ensureSize(index);
852    //    }
853    
854      /**
855       * Construct the node map from the node.
856       *
857       * @param type raw type ID, one of DTM.XXX_NODE.
858       * @param expandedTypeID The expended type ID.
859       * @param parentIndex The current parent index.
860       * @param previousSibling The previous sibling index.
861       * @param dataOrPrefix index into m_data table, or string handle.
862       * @param canHaveFirstChild true if the node can have a first child, false
863       *                          if it is atomic.
864       *
865       * @return The index identity of the node that was added.
866       */
867      protected int addNode(int type, int expandedTypeID,
868                            int parentIndex, int previousSibling,
869                            int dataOrPrefix, boolean canHaveFirstChild)
870      {
871        // Common to all nodes:
872        int nodeIndex = m_size++;
873    
874        // Have we overflowed a DTM Identity's addressing range?
875        if(m_dtmIdent.size() == (nodeIndex>>>DTMManager.IDENT_DTM_NODE_BITS))
876        {
877          addNewDTMID(nodeIndex);
878        }
879    
880        m_firstch.addElement(canHaveFirstChild ? NOTPROCESSED : DTM.NULL);
881        m_nextsib.addElement(NOTPROCESSED);
882        m_parent.addElement(parentIndex);
883        m_exptype.addElement(expandedTypeID);
884        m_dataOrQName.addElement(dataOrPrefix);
885    
886        if (m_prevsib != null) {
887          m_prevsib.addElement(previousSibling);
888        }
889    
890        if (DTM.NULL != previousSibling) {
891          m_nextsib.setElementAt(nodeIndex,previousSibling);
892        }
893    
894        if (m_locator != null && m_useSourceLocationProperty) {
895          setSourceLocation();
896        }
897    
898        // Note that nextSibling is not processed until charactersFlush()
899        // is called, to handle successive characters() events.
900    
901        // Special handling by type: Declare namespaces, attach first child
902        switch(type)
903        {
904        case DTM.NAMESPACE_NODE:
905          declareNamespaceInContext(parentIndex,nodeIndex);
906          break;
907        case DTM.ATTRIBUTE_NODE:
908          break;
909        default:
910          if (DTM.NULL == previousSibling && DTM.NULL != parentIndex) {
911            m_firstch.setElementAt(nodeIndex,parentIndex);
912          }
913          break;
914        }
915    
916        return nodeIndex;
917      }
918    
919      /**
920       * Get a new DTM ID beginning at the specified node index.
921       * @param  nodeIndex The node identity at which the new DTM ID will begin
922       * addressing.
923       */
924      protected void addNewDTMID(int nodeIndex) {
925        try
926        {
927          if(m_mgr==null)
928            throw new ClassCastException();
929                                  
930                                  // Handle as Extended Addressing
931          DTMManagerDefault mgrD=(DTMManagerDefault)m_mgr;
932          int id=mgrD.getFirstFreeDTMID();
933          mgrD.addDTM(this,id,nodeIndex);
934          m_dtmIdent.addElement(id<<DTMManager.IDENT_DTM_NODE_BITS);
935        }
936        catch(ClassCastException e)
937        {
938          // %REVIEW% Wrong error message, but I've been told we're trying
939          // not to add messages right not for I18N reasons.
940          // %REVIEW% Should this be a Fatal Error?
941          error(XMLMessages.createXMLMessage(XMLErrorResources.ER_NO_DTMIDS_AVAIL, null));//"No more DTM IDs are available";
942        }
943      }
944    
945      /**
946        * Migrate a DTM built with an old DTMManager to a new DTMManager.
947        * After the migration, the new DTMManager will treat the DTM as
948        * one that is built by itself.
949        * This is used to support DTM sharing between multiple transformations.
950        * @param manager the DTMManager
951        */
952      public void migrateTo(DTMManager manager) {
953        super.migrateTo(manager);
954        
955        // We have to reset the information in m_dtmIdent and
956        // register the DTM with the new manager. 
957        int numDTMs = m_dtmIdent.size();
958        int dtmId = m_mgrDefault.getFirstFreeDTMID();
959        int nodeIndex = 0;
960        for (int i = 0; i < numDTMs; i++)
961        {     
962          m_dtmIdent.setElementAt(dtmId << DTMManager.IDENT_DTM_NODE_BITS, i);
963          m_mgrDefault.addDTM(this, dtmId, nodeIndex);
964          dtmId++;
965          nodeIndex += (1 << DTMManager.IDENT_DTM_NODE_BITS);
966        }
967      }
968    
969      /**
970       * Store the source location of the current node.  This method must be called
971       * as every node is added to the DTM or for no node.
972       */
973      protected void setSourceLocation() {
974        m_sourceSystemId.addElement(m_locator.getSystemId());
975        m_sourceLine.addElement(m_locator.getLineNumber());
976        m_sourceColumn.addElement(m_locator.getColumnNumber());
977    
978        //%REVIEW% %BUG% Prevent this from arising in the first place
979        // by not allowing the enabling conditions to change after we start
980        // building the document.
981        if (m_sourceSystemId.size() != m_size) {
982            String msg = "CODING ERROR in Source Location: " + m_size + " != "
983                        + m_sourceSystemId.size();
984            System.err.println(msg);
985            throw new RuntimeException(msg);
986        }
987      }
988    
989      /**
990       * Given a node handle, return its node value. This is mostly
991       * as defined by the DOM, but may ignore some conveniences.
992       * <p>
993       *
994       * @param nodeHandle The node id.
995       * @return String Value of this node, or null if not
996       * meaningful for this node type.
997       */
998      public String getNodeValue(int nodeHandle)
999      {
1000    
1001        int identity = makeNodeIdentity(nodeHandle);
1002        int type = _type(identity);
1003    
1004        if (isTextType(type))
1005        {
1006          int dataIndex = _dataOrQName(identity);
1007          int offset = m_data.elementAt(dataIndex);
1008          int length = m_data.elementAt(dataIndex + 1);
1009    
1010          // %OPT% We should cache this, I guess.
1011          return m_chars.getString(offset, length);
1012        }
1013        else if (DTM.ELEMENT_NODE == type || DTM.DOCUMENT_FRAGMENT_NODE == type
1014                 || DTM.DOCUMENT_NODE == type)
1015        {
1016          return null;
1017        }
1018        else
1019        {
1020          int dataIndex = _dataOrQName(identity);
1021    
1022          if (dataIndex < 0)
1023          {
1024            dataIndex = -dataIndex;
1025            dataIndex = m_data.elementAt(dataIndex + 1);
1026          }
1027    
1028          return m_valuesOrPrefixes.indexToString(dataIndex);
1029        }
1030      }
1031    
1032      /**
1033       * Given a node handle, return its XPath-style localname.
1034       * (As defined in Namespaces, this is the portion of the name after any
1035       * colon character).
1036       *
1037       * @param nodeHandle the id of the node.
1038       * @return String Local name of this node.
1039       */
1040      public String getLocalName(int nodeHandle)
1041      {
1042        return m_expandedNameTable.getLocalName(_exptype(makeNodeIdentity(nodeHandle)));
1043      }
1044    
1045      /**
1046       * The getUnparsedEntityURI function returns the URI of the unparsed
1047       * entity with the specified name in the same document as the context
1048       * node (see [3.3 Unparsed Entities]). It returns the empty string if
1049       * there is no such entity.
1050       * <p>
1051       * XML processors may choose to use the System Identifier (if one
1052       * is provided) to resolve the entity, rather than the URI in the
1053       * Public Identifier. The details are dependent on the processor, and
1054       * we would have to support some form of plug-in resolver to handle
1055       * this properly. Currently, we simply return the System Identifier if
1056       * present, and hope that it a usable URI or that our caller can
1057       * map it to one.
1058       * TODO: Resolve Public Identifiers... or consider changing function name.
1059       * <p>
1060       * If we find a relative URI
1061       * reference, XML expects it to be resolved in terms of the base URI
1062       * of the document. The DOM doesn't do that for us, and it isn't
1063       * entirely clear whether that should be done here; currently that's
1064       * pushed up to a higher level of our application. (Note that DOM Level
1065       * 1 didn't store the document's base URI.)
1066       * TODO: Consider resolving Relative URIs.
1067       * <p>
1068       * (The DOM's statement that "An XML processor may choose to
1069       * completely expand entities before the structure model is passed
1070       * to the DOM" refers only to parsed entities, not unparsed, and hence
1071       * doesn't affect this function.)
1072       *
1073       * @param name A string containing the Entity Name of the unparsed
1074       * entity.
1075       *
1076       * @return String containing the URI of the Unparsed Entity, or an
1077       * empty string if no such entity exists.
1078       */
1079      public String getUnparsedEntityURI(String name)
1080      {
1081    
1082        String url = "";
1083    
1084        if (null == m_entities)
1085          return url;
1086    
1087        int n = m_entities.size();
1088    
1089        for (int i = 0; i < n; i += ENTITY_FIELDS_PER)
1090        {
1091          String ename = (String) m_entities.elementAt(i + ENTITY_FIELD_NAME);
1092    
1093          if (null != ename && ename.equals(name))
1094          {
1095            String nname = (String) m_entities.elementAt(i
1096                             + ENTITY_FIELD_NOTATIONNAME);
1097    
1098            if (null != nname)
1099            {
1100    
1101              // The draft says: "The XSLT processor may use the public
1102              // identifier to generate a URI for the entity instead of the URI
1103              // specified in the system identifier. If the XSLT processor does
1104              // not use the public identifier to generate the URI, it must use
1105              // the system identifier; if the system identifier is a relative
1106              // URI, it must be resolved into an absolute URI using the URI of
1107              // the resource containing the entity declaration as the base
1108              // URI [RFC2396]."
1109              // So I'm falling a bit short here.
1110              url = (String) m_entities.elementAt(i + ENTITY_FIELD_SYSTEMID);
1111    
1112              if (null == url)
1113              {
1114                url = (String) m_entities.elementAt(i + ENTITY_FIELD_PUBLICID);
1115              }
1116            }
1117    
1118            break;
1119          }
1120        }
1121    
1122        return url;
1123      }
1124    
1125      /**
1126       * Given a namespace handle, return the prefix that the namespace decl is
1127       * mapping.
1128       * Given a node handle, return the prefix used to map to the namespace.
1129       *
1130       * <p> %REVIEW% Are you sure you want "" for no prefix?  </p>
1131       * <p> %REVIEW-COMMENT% I think so... not totally sure. -sb  </p>
1132       *
1133       * @param nodeHandle the id of the node.
1134       * @return String prefix of this node's name, or "" if no explicit
1135       * namespace prefix was given.
1136       */
1137      public String getPrefix(int nodeHandle)
1138      {
1139    
1140        int identity = makeNodeIdentity(nodeHandle);
1141        int type = _type(identity);
1142    
1143        if (DTM.ELEMENT_NODE == type)
1144        {
1145          int prefixIndex = _dataOrQName(identity);
1146    
1147          if (0 == prefixIndex)
1148            return "";
1149          else
1150          {
1151            String qname = m_valuesOrPrefixes.indexToString(prefixIndex);
1152    
1153            return getPrefix(qname, null);
1154          }
1155        }
1156        else if (DTM.ATTRIBUTE_NODE == type)
1157        {
1158          int prefixIndex = _dataOrQName(identity);
1159    
1160          if (prefixIndex < 0)
1161          {
1162            prefixIndex = m_data.elementAt(-prefixIndex);
1163    
1164            String qname = m_valuesOrPrefixes.indexToString(prefixIndex);
1165    
1166            return getPrefix(qname, null);
1167          }
1168        }
1169    
1170        return "";
1171      }
1172    
1173      /**
1174       * Retrieves an attribute node by by qualified name and namespace URI.
1175       *
1176       * @param nodeHandle int Handle of the node upon which to look up this attribute..
1177       * @param namespaceURI The namespace URI of the attribute to
1178       *   retrieve, or null.
1179       * @param name The local name of the attribute to
1180       *   retrieve.
1181       * @return The attribute node handle with the specified name (
1182       *   <code>nodeName</code>) or <code>DTM.NULL</code> if there is no such
1183       *   attribute.
1184       */
1185      public int getAttributeNode(int nodeHandle, String namespaceURI,
1186                                  String name)
1187      {
1188    
1189        for (int attrH = getFirstAttribute(nodeHandle); DTM.NULL != attrH;
1190                attrH = getNextAttribute(attrH))
1191        {
1192          String attrNS = getNamespaceURI(attrH);
1193          String attrName = getLocalName(attrH);
1194          boolean nsMatch = namespaceURI == attrNS
1195                            || (namespaceURI != null
1196                                && namespaceURI.equals(attrNS));
1197    
1198          if (nsMatch && name.equals(attrName))
1199            return attrH;
1200        }
1201    
1202        return DTM.NULL;
1203      }
1204    
1205      /**
1206       * Return the public identifier of the external subset,
1207       * normalized as described in 4.2.2 External Entities [XML]. If there is
1208       * no external subset or if it has no public identifier, this property
1209       * has no value.
1210       *
1211       * @return the public identifier String object, or null if there is none.
1212       */
1213      public String getDocumentTypeDeclarationPublicIdentifier()
1214      {
1215    
1216        /** @todo: implement this org.apache.xml.dtm.DTMDefaultBase abstract method */
1217        error(XMLMessages.createXMLMessage(XMLErrorResources.ER_METHOD_NOT_SUPPORTED, null));//"Not yet supported!");
1218    
1219        return null;
1220      }
1221    
1222      /**
1223       * Given a node handle, return its DOM-style namespace URI
1224       * (As defined in Namespaces, this is the declared URI which this node's
1225       * prefix -- or default in lieu thereof -- was mapped to.)
1226       *
1227       * <p>%REVIEW% Null or ""? -sb</p>
1228       *
1229       * @param nodeHandle the id of the node.
1230       * @return String URI value of this node's namespace, or null if no
1231       * namespace was resolved.
1232       */
1233      public String getNamespaceURI(int nodeHandle)
1234      {
1235    
1236        return m_expandedNameTable.getNamespace(_exptype(makeNodeIdentity(nodeHandle)));
1237      }
1238    
1239      /**
1240       * Get the string-value of a node as a String object
1241       * (see http://www.w3.org/TR/xpath#data-model
1242       * for the definition of a node's string-value).
1243       *
1244       * @param nodeHandle The node ID.
1245       *
1246       * @return A string object that represents the string-value of the given node.
1247       */
1248      public XMLString getStringValue(int nodeHandle)
1249      {
1250        int identity = makeNodeIdentity(nodeHandle);
1251        int type;
1252        if(identity==DTM.NULL) // Separate lines because I wanted to breakpoint it
1253          type = DTM.NULL;
1254        else
1255          type= _type(identity);
1256    
1257        if (isTextType(type))
1258        {
1259          int dataIndex = _dataOrQName(identity);
1260          int offset = m_data.elementAt(dataIndex);
1261          int length = m_data.elementAt(dataIndex + 1);
1262    
1263          return m_xstrf.newstr(m_chars, offset, length);
1264        }
1265        else
1266        {
1267          int firstChild = _firstch(identity);
1268    
1269          if (DTM.NULL != firstChild)
1270          {
1271            int offset = -1;
1272            int length = 0;
1273            int startNode = identity;
1274    
1275            identity = firstChild;
1276    
1277            do {
1278              type = _type(identity);
1279    
1280              if (isTextType(type))
1281              {
1282                int dataIndex = _dataOrQName(identity);
1283    
1284                if (-1 == offset)
1285                {
1286                  offset = m_data.elementAt(dataIndex);
1287                }
1288    
1289                length += m_data.elementAt(dataIndex + 1);
1290              }
1291    
1292              identity = getNextNodeIdentity(identity);
1293            } while (DTM.NULL != identity && (_parent(identity) >= startNode));
1294    
1295            if (length > 0)
1296            {
1297              return m_xstrf.newstr(m_chars, offset, length);
1298            }
1299          }
1300          else if(type != DTM.ELEMENT_NODE)
1301          {
1302            int dataIndex = _dataOrQName(identity);
1303    
1304            if (dataIndex < 0)
1305            {
1306              dataIndex = -dataIndex;
1307              dataIndex = m_data.elementAt(dataIndex + 1);
1308            }
1309            return m_xstrf.newstr(m_valuesOrPrefixes.indexToString(dataIndex));
1310          }
1311        }
1312    
1313        return m_xstrf.emptystr();
1314      }
1315      
1316      /**
1317       * Determine if the string-value of a node is whitespace
1318       *
1319       * @param nodeHandle The node Handle.
1320       *
1321       * @return Return true if the given node is whitespace.
1322       */
1323      public boolean isWhitespace(int nodeHandle)
1324      {
1325        int identity = makeNodeIdentity(nodeHandle);
1326        int type;
1327        if(identity==DTM.NULL) // Separate lines because I wanted to breakpoint it
1328          type = DTM.NULL;
1329        else
1330          type= _type(identity);
1331    
1332        if (isTextType(type))
1333        {
1334          int dataIndex = _dataOrQName(identity);
1335          int offset = m_data.elementAt(dataIndex);
1336          int length = m_data.elementAt(dataIndex + 1);
1337    
1338          return m_chars.isWhitespace(offset, length);
1339        }
1340        return false;
1341      }
1342    
1343      /**
1344       * Returns the <code>Element</code> whose <code>ID</code> is given by
1345       * <code>elementId</code>. If no such element exists, returns
1346       * <code>DTM.NULL</code>. Behavior is not defined if more than one element
1347       * has this <code>ID</code>. Attributes (including those
1348       * with the name "ID") are not of type ID unless so defined by DTD/Schema
1349       * information available to the DTM implementation.
1350       * Implementations that do not know whether attributes are of type ID or
1351       * not are expected to return <code>DTM.NULL</code>.
1352       *
1353       * <p>%REVIEW% Presumably IDs are still scoped to a single document,
1354       * and this operation searches only within a single document, right?
1355       * Wouldn't want collisions between DTMs in the same process.</p>
1356       *
1357       * @param elementId The unique <code>id</code> value for an element.
1358       * @return The handle of the matching element.
1359       */
1360      public int getElementById(String elementId)
1361      {
1362    
1363        Integer intObj;
1364        boolean isMore = true;
1365    
1366        do
1367        {
1368          intObj = (Integer) m_idAttributes.get(elementId);
1369    
1370          if (null != intObj)
1371            return makeNodeHandle(intObj.intValue());
1372    
1373          if (!isMore || m_endDocumentOccured)
1374            break;
1375    
1376          isMore = nextNode();
1377        }
1378        while (null == intObj);
1379    
1380        return DTM.NULL;
1381      }
1382    
1383      /**
1384       * Get a prefix either from the qname or from the uri mapping, or just make
1385       * one up!
1386       *
1387       * @param qname The qualified name, which may be null.
1388       * @param uri The namespace URI, which may be null.
1389       *
1390       * @return The prefix if there is one, or null.
1391       */
1392      public String getPrefix(String qname, String uri)
1393      {
1394    
1395        String prefix;
1396        int uriIndex = -1;
1397    
1398        if (null != uri && uri.length() > 0)
1399        {
1400    
1401          do
1402          {
1403            uriIndex = m_prefixMappings.indexOf(uri, ++uriIndex);
1404          } while ( (uriIndex & 0x01) == 0);
1405    
1406          if (uriIndex >= 0)
1407          {
1408            prefix = (String) m_prefixMappings.elementAt(uriIndex - 1);
1409          }
1410          else if (null != qname)
1411          {
1412            int indexOfNSSep = qname.indexOf(':');
1413    
1414            if (qname.equals("xmlns"))
1415              prefix = "";
1416            else if (qname.startsWith("xmlns:"))
1417              prefix = qname.substring(indexOfNSSep + 1);
1418            else
1419              prefix = (indexOfNSSep > 0)
1420                       ? qname.substring(0, indexOfNSSep) : null;
1421          }
1422          else
1423          {
1424            prefix = null;
1425          }
1426        }
1427        else if (null != qname)
1428        {
1429          int indexOfNSSep = qname.indexOf(':');
1430    
1431          if (indexOfNSSep > 0)
1432          {
1433            if (qname.startsWith("xmlns:"))
1434              prefix = qname.substring(indexOfNSSep + 1);
1435            else
1436              prefix = qname.substring(0, indexOfNSSep);    
1437          }
1438          else
1439          {
1440            if (qname.equals("xmlns"))
1441              prefix = "";
1442            else
1443              prefix = null;
1444          }
1445        }
1446        else
1447        {
1448          prefix = null;
1449        }
1450    
1451        return prefix;
1452      }
1453      
1454      /**
1455       * Get a prefix either from the uri mapping, or just make
1456       * one up!
1457       *
1458       * @param uri The namespace URI, which may be null.
1459       *
1460       * @return The prefix if there is one, or null.
1461       */
1462      public int getIdForNamespace(String uri)
1463      {
1464    
1465         return m_valuesOrPrefixes.stringToIndex(uri);
1466        
1467      }
1468    
1469        /**
1470       * Get a prefix either from the qname or from the uri mapping, or just make
1471       * one up!
1472       *
1473       * @return The prefix if there is one, or null.
1474       */
1475      public String getNamespaceURI(String prefix)
1476      {
1477    
1478        String uri = "";
1479        int prefixIndex = m_contextIndexes.peek() - 1 ;
1480    
1481        if(null == prefix)
1482          prefix = "";
1483    
1484          do
1485          {
1486            prefixIndex = m_prefixMappings.indexOf(prefix, ++prefixIndex);
1487          } while ( (prefixIndex >= 0) && (prefixIndex & 0x01) == 0x01);
1488    
1489          if (prefixIndex > -1)
1490          {
1491            uri = (String) m_prefixMappings.elementAt(prefixIndex + 1);
1492          }
1493    
1494    
1495        return uri;
1496      }
1497    
1498      /**
1499       * Set an ID string to node association in the ID table.
1500       *
1501       * @param id The ID string.
1502       * @param elem The associated element handle.
1503       */
1504      public void setIDAttribute(String id, int elem)
1505      {
1506        m_idAttributes.put(id, new Integer(elem));
1507      }
1508    
1509      /**
1510       * Check whether accumulated text should be stripped; if not,
1511       * append the appropriate flavor of text/cdata node.
1512       */
1513      protected void charactersFlush()
1514      {
1515    
1516        if (m_textPendingStart >= 0)  // -1 indicates no-text-in-progress
1517        {
1518          int length = m_chars.size() - m_textPendingStart;
1519          boolean doStrip = false;
1520    
1521          if (getShouldStripWhitespace())
1522          {
1523            doStrip = m_chars.isWhitespace(m_textPendingStart, length);
1524          }
1525    
1526          if (doStrip) {
1527            m_chars.setLength(m_textPendingStart);  // Discard accumulated text
1528          } else {
1529            // Guard against characters/ignorableWhitespace events that
1530            // contained no characters.  They should not result in a node.
1531            if (length > 0) {
1532              int exName = m_expandedNameTable.getExpandedTypeID(DTM.TEXT_NODE);
1533              int dataIndex = m_data.size();
1534    
1535              m_previous = addNode(m_coalescedTextType, exName,
1536                                   m_parents.peek(), m_previous, dataIndex, false);
1537    
1538              m_data.addElement(m_textPendingStart);
1539              m_data.addElement(length);
1540            }
1541          }
1542    
1543          // Reset for next text block
1544          m_textPendingStart = -1;
1545          m_textType = m_coalescedTextType = DTM.TEXT_NODE;
1546        }
1547      }
1548    
1549      ////////////////////////////////////////////////////////////////////
1550      // Implementation of the EntityResolver interface.
1551      ////////////////////////////////////////////////////////////////////
1552    
1553      /**
1554       * Resolve an external entity.
1555       *
1556       * <p>Always return null, so that the parser will use the system
1557       * identifier provided in the XML document.  This method implements
1558       * the SAX default behaviour: application writers can override it
1559       * in a subclass to do special translations such as catalog lookups
1560       * or URI redirection.</p>
1561       *
1562       * @param publicId The public identifer, or null if none is
1563       *                 available.
1564       * @param systemId The system identifier provided in the XML
1565       *                 document.
1566       * @return The new input source, or null to require the
1567       *         default behaviour.
1568       * @throws SAXException Any SAX exception, possibly
1569       *            wrapping another exception.
1570       * @see org.xml.sax.EntityResolver#resolveEntity
1571       *
1572       * @throws SAXException
1573       */
1574      public InputSource resolveEntity(String publicId, String systemId)
1575              throws SAXException
1576      {
1577        return null;
1578      }
1579    
1580      ////////////////////////////////////////////////////////////////////
1581      // Implementation of DTDHandler interface.
1582      ////////////////////////////////////////////////////////////////////
1583    
1584      /**
1585       * Receive notification of a notation declaration.
1586       *
1587       * <p>By default, do nothing.  Application writers may override this
1588       * method in a subclass if they wish to keep track of the notations
1589       * declared in a document.</p>
1590       *
1591       * @param name The notation name.
1592       * @param publicId The notation public identifier, or null if not
1593       *                 available.
1594       * @param systemId The notation system identifier.
1595       * @throws SAXException Any SAX exception, possibly
1596       *            wrapping another exception.
1597       * @see org.xml.sax.DTDHandler#notationDecl
1598       *
1599       * @throws SAXException
1600       */
1601      public void notationDecl(String name, String publicId, String systemId)
1602              throws SAXException
1603      {
1604    
1605        // no op
1606      }
1607    
1608      /**
1609       * Receive notification of an unparsed entity declaration.
1610       *
1611       * <p>By default, do nothing.  Application writers may override this
1612       * method in a subclass to keep track of the unparsed entities
1613       * declared in a document.</p>
1614       *
1615       * @param name The entity name.
1616       * @param publicId The entity public identifier, or null if not
1617       *                 available.
1618       * @param systemId The entity system identifier.
1619       * @param notationName The name of the associated notation.
1620       * @throws SAXException Any SAX exception, possibly
1621       *            wrapping another exception.
1622       * @see org.xml.sax.DTDHandler#unparsedEntityDecl
1623       *
1624       * @throws SAXException
1625       */
1626      public void unparsedEntityDecl(
1627              String name, String publicId, String systemId, String notationName)
1628                throws SAXException
1629      {
1630    
1631        if (null == m_entities)
1632        {
1633          m_entities = new Vector();
1634        }
1635    
1636        try
1637        {
1638          systemId = SystemIDResolver.getAbsoluteURI(systemId,
1639                                                     getDocumentBaseURI());
1640        }
1641        catch (Exception e)
1642        {
1643          throw new org.xml.sax.SAXException(e);
1644        }
1645    
1646        //  private static final int ENTITY_FIELD_PUBLICID = 0;
1647        m_entities.addElement(publicId);
1648    
1649        //  private static final int ENTITY_FIELD_SYSTEMID = 1;
1650        m_entities.addElement(systemId);
1651    
1652        //  private static final int ENTITY_FIELD_NOTATIONNAME = 2;
1653        m_entities.addElement(notationName);
1654    
1655        //  private static final int ENTITY_FIELD_NAME = 3;
1656        m_entities.addElement(name);
1657      }
1658    
1659      ////////////////////////////////////////////////////////////////////
1660      // Implementation of ContentHandler interface.
1661      ////////////////////////////////////////////////////////////////////
1662    
1663      /**
1664       * Receive a Locator object for document events.
1665       *
1666       * <p>By default, do nothing.  Application writers may override this
1667       * method in a subclass if they wish to store the locator for use
1668       * with other document events.</p>
1669       *
1670       * @param locator A locator for all SAX document events.
1671       * @see org.xml.sax.ContentHandler#setDocumentLocator
1672       * @see org.xml.sax.Locator
1673       */
1674      public void setDocumentLocator(Locator locator)
1675      {
1676        m_locator = locator;
1677        m_systemId = locator.getSystemId();
1678      }
1679    
1680      /**
1681       * Receive notification of the beginning of the document.
1682       *
1683       * @throws SAXException Any SAX exception, possibly
1684       *            wrapping another exception.
1685       * @see org.xml.sax.ContentHandler#startDocument
1686       */
1687      public void startDocument() throws SAXException
1688      {
1689        if (DEBUG)
1690          System.out.println("startDocument");
1691    
1692                    
1693        int doc = addNode(DTM.DOCUMENT_NODE,
1694                          m_expandedNameTable.getExpandedTypeID(DTM.DOCUMENT_NODE),
1695                          DTM.NULL, DTM.NULL, 0, true);
1696    
1697        m_parents.push(doc);
1698        m_previous = DTM.NULL;
1699    
1700        m_contextIndexes.push(m_prefixMappings.size());  // for the next element.
1701      }
1702    
1703      /**
1704       * Receive notification of the end of the document.
1705       *
1706       * @throws SAXException Any SAX exception, possibly
1707       *            wrapping another exception.
1708       * @see org.xml.sax.ContentHandler#endDocument
1709       */
1710      public void endDocument() throws SAXException
1711      {
1712        if (DEBUG)
1713          System.out.println("endDocument");
1714    
1715                    charactersFlush();
1716    
1717        m_nextsib.setElementAt(NULL,0);
1718    
1719        if (m_firstch.elementAt(0) == NOTPROCESSED)
1720          m_firstch.setElementAt(NULL,0);
1721    
1722        if (DTM.NULL != m_previous)
1723          m_nextsib.setElementAt(DTM.NULL,m_previous);
1724    
1725        m_parents = null;
1726        m_prefixMappings = null;
1727        m_contextIndexes = null;
1728    
1729        m_endDocumentOccured = true;
1730        
1731        // Bugzilla 4858: throw away m_locator. we cache m_systemId
1732        m_locator = null;
1733      }
1734    
1735      /**
1736       * Receive notification of the start of a Namespace mapping.
1737       *
1738       * <p>By default, do nothing.  Application writers may override this
1739       * method in a subclass to take specific actions at the start of
1740       * each Namespace prefix scope (such as storing the prefix mapping).</p>
1741       *
1742       * @param prefix The Namespace prefix being declared.
1743       * @param uri The Namespace URI mapped to the prefix.
1744       * @throws SAXException Any SAX exception, possibly
1745       *            wrapping another exception.
1746       * @see org.xml.sax.ContentHandler#startPrefixMapping
1747       */
1748      public void startPrefixMapping(String prefix, String uri)
1749              throws SAXException
1750      {
1751    
1752        if (DEBUG)
1753          System.out.println("startPrefixMapping: prefix: " + prefix + ", uri: "
1754                             + uri);
1755    
1756        if(null == prefix)
1757          prefix = "";
1758        m_prefixMappings.addElement(prefix);  // JDK 1.1.x compat -sc
1759        m_prefixMappings.addElement(uri);  // JDK 1.1.x compat -sc
1760      }
1761    
1762      /**
1763       * Receive notification of the end of a Namespace mapping.
1764       *
1765       * <p>By default, do nothing.  Application writers may override this
1766       * method in a subclass to take specific actions at the end of
1767       * each prefix mapping.</p>
1768       *
1769       * @param prefix The Namespace prefix being declared.
1770       * @throws SAXException Any SAX exception, possibly
1771       *            wrapping another exception.
1772       * @see org.xml.sax.ContentHandler#endPrefixMapping
1773       */
1774      public void endPrefixMapping(String prefix) throws SAXException
1775      {
1776        if (DEBUG)
1777          System.out.println("endPrefixMapping: prefix: " + prefix);
1778    
1779        if(null == prefix)
1780          prefix = "";
1781    
1782        int index = m_contextIndexes.peek() - 1;
1783    
1784        do
1785        {
1786          index = m_prefixMappings.indexOf(prefix, ++index);
1787        } while ( (index >= 0) && ((index & 0x01) == 0x01) );
1788    
1789    
1790        if (index > -1)
1791        {
1792          m_prefixMappings.setElementAt("%@$#^@#", index);
1793          m_prefixMappings.setElementAt("%@$#^@#", index + 1);
1794        }
1795    
1796        // no op
1797      }
1798    
1799      /**
1800       * Check if a declaration has already been made for a given prefix.
1801       *
1802       * @param prefix non-null prefix string.
1803       *
1804       * @return true if the declaration has already been declared in the
1805       *         current context.
1806       */
1807      protected boolean declAlreadyDeclared(String prefix)
1808      {
1809    
1810        int startDecls = m_contextIndexes.peek();
1811        java.util.Vector prefixMappings = m_prefixMappings;
1812        int nDecls = prefixMappings.size();
1813    
1814        for (int i = startDecls; i < nDecls; i += 2)
1815        {
1816          String prefixDecl = (String) prefixMappings.elementAt(i);
1817    
1818          if (prefixDecl == null)
1819            continue;
1820    
1821          if (prefixDecl.equals(prefix))
1822            return true;
1823        }
1824    
1825        return false;
1826      }
1827    
1828            boolean m_pastFirstElement=false;
1829    
1830      /**
1831       * Receive notification of the start of an element.
1832       *
1833       * <p>By default, do nothing.  Application writers may override this
1834       * method in a subclass to take specific actions at the start of
1835       * each element (such as allocating a new tree node or writing
1836       * output to a file).</p>
1837       *
1838       * @param uri The Namespace URI, or the empty string if the
1839       *        element has no Namespace URI or if Namespace
1840       *        processing is not being performed.
1841       * @param localName The local name (without prefix), or the
1842       *        empty string if Namespace processing is not being
1843       *        performed.
1844       * @param qName The qualified name (with prefix), or the
1845       *        empty string if qualified names are not available.
1846       * @param attributes The specified or defaulted attributes.
1847       * @throws SAXException Any SAX exception, possibly
1848       *            wrapping another exception.
1849       * @see org.xml.sax.ContentHandler#startElement
1850       */
1851      public void startElement(
1852              String uri, String localName, String qName, Attributes attributes)
1853                throws SAXException
1854      {
1855       if (DEBUG)
1856             {
1857          System.out.println("startElement: uri: " + uri + ", localname: "
1858                                                                                                     + localName + ", qname: "+qName+", atts: " + attributes);
1859    
1860                            boolean DEBUG_ATTRS=true;
1861                            if(DEBUG_ATTRS & attributes!=null)
1862                            {
1863                                    int n = attributes.getLength();
1864                                    if(n==0)
1865                                            System.out.println("\tempty attribute list");
1866                                    else for (int i = 0; i < n; i++)
1867                                            System.out.println("\t attr: uri: " + attributes.getURI(i) +
1868                                                                                                                     ", localname: " + attributes.getLocalName(i) +
1869                                                                                                                     ", qname: " + attributes.getQName(i) +
1870                                                                                                                     ", type: " + attributes.getType(i) +
1871                                                                                                                     ", value: " + attributes.getValue(i)
1872                                                                                                                     );
1873                            }
1874             }
1875                    
1876        charactersFlush();
1877    
1878        int exName = m_expandedNameTable.getExpandedTypeID(uri, localName, DTM.ELEMENT_NODE);
1879        String prefix = getPrefix(qName, uri);
1880        int prefixIndex = (null != prefix)
1881                          ? m_valuesOrPrefixes.stringToIndex(qName) : 0;
1882    
1883        int elemNode = addNode(DTM.ELEMENT_NODE, exName,
1884                               m_parents.peek(), m_previous, prefixIndex, true);
1885    
1886        if(m_indexing)
1887          indexNode(exName, elemNode);
1888        
1889    
1890        m_parents.push(elemNode);
1891    
1892        int startDecls = m_contextIndexes.peek();
1893        int nDecls = m_prefixMappings.size();
1894        int prev = DTM.NULL;
1895    
1896        if(!m_pastFirstElement)
1897        {
1898          // SPECIAL CASE: Implied declaration at root element
1899          prefix="xml";
1900          String declURL = "http://www.w3.org/XML/1998/namespace";
1901          exName = m_expandedNameTable.getExpandedTypeID(null, prefix, DTM.NAMESPACE_NODE);
1902          int val = m_valuesOrPrefixes.stringToIndex(declURL);
1903          prev = addNode(DTM.NAMESPACE_NODE, exName, elemNode,
1904                         prev, val, false);
1905          m_pastFirstElement=true;
1906        }
1907    
1908        for (int i = startDecls; i < nDecls; i += 2)
1909        {
1910          prefix = (String) m_prefixMappings.elementAt(i);
1911    
1912          if (prefix == null)
1913            continue;
1914    
1915          String declURL = (String) m_prefixMappings.elementAt(i + 1);
1916    
1917          exName = m_expandedNameTable.getExpandedTypeID(null, prefix, DTM.NAMESPACE_NODE);
1918    
1919          int val = m_valuesOrPrefixes.stringToIndex(declURL);
1920    
1921          prev = addNode(DTM.NAMESPACE_NODE, exName, elemNode,
1922                         prev, val, false);
1923        }
1924    
1925        int n = attributes.getLength();
1926    
1927        for (int i = 0; i < n; i++)
1928        {
1929          String attrUri = attributes.getURI(i);
1930          String attrQName = attributes.getQName(i);
1931          String valString = attributes.getValue(i);
1932    
1933          prefix = getPrefix(attrQName, attrUri);
1934    
1935          int nodeType;
1936          
1937           String attrLocalName = attributes.getLocalName(i);
1938    
1939          if ((null != attrQName)
1940                  && (attrQName.equals("xmlns")
1941                      || attrQName.startsWith("xmlns:")))
1942          {
1943            if (declAlreadyDeclared(prefix))
1944              continue;  // go to the next attribute.
1945    
1946            nodeType = DTM.NAMESPACE_NODE;
1947          }
1948          else
1949          {
1950            nodeType = DTM.ATTRIBUTE_NODE;
1951    
1952            if (attributes.getType(i).equalsIgnoreCase("ID"))
1953              setIDAttribute(valString, elemNode);
1954          }
1955    
1956          // Bit of a hack... if somehow valString is null, stringToIndex will
1957          // return -1, which will make things very unhappy.
1958          if(null == valString)
1959            valString = "";
1960    
1961          int val = m_valuesOrPrefixes.stringToIndex(valString);
1962          //String attrLocalName = attributes.getLocalName(i);
1963    
1964          if (null != prefix)
1965          {
1966    
1967            prefixIndex = m_valuesOrPrefixes.stringToIndex(attrQName);
1968    
1969            int dataIndex = m_data.size();
1970    
1971            m_data.addElement(prefixIndex);
1972            m_data.addElement(val);
1973    
1974            val = -dataIndex;
1975          }
1976    
1977          exName = m_expandedNameTable.getExpandedTypeID(attrUri, attrLocalName, nodeType);
1978          prev = addNode(nodeType, exName, elemNode, prev, val,
1979                         false);
1980        }
1981    
1982        if (DTM.NULL != prev)
1983          m_nextsib.setElementAt(DTM.NULL,prev);
1984    
1985        if (null != m_wsfilter)
1986        {
1987          short wsv = m_wsfilter.getShouldStripSpace(makeNodeHandle(elemNode), this);
1988          boolean shouldStrip = (DTMWSFilter.INHERIT == wsv)
1989                                ? getShouldStripWhitespace()
1990                                : (DTMWSFilter.STRIP == wsv);
1991    
1992          pushShouldStripWhitespace(shouldStrip);
1993        }
1994    
1995        m_previous = DTM.NULL;
1996    
1997        m_contextIndexes.push(m_prefixMappings.size());  // for the children.
1998      }
1999    
2000      /**
2001       * Receive notification of the end of an element.
2002       *
2003       * <p>By default, do nothing.  Application writers may override this
2004       * method in a subclass to take specific actions at the end of
2005       * each element (such as finalising a tree node or writing
2006       * output to a file).</p>
2007       *
2008       * @param uri The Namespace URI, or the empty string if the
2009       *        element has no Namespace URI or if Namespace
2010       *        processing is not being performed.
2011       * @param localName The local name (without prefix), or the
2012       *        empty string if Namespace processing is not being
2013       *        performed.
2014       * @param qName The qualified XML 1.0 name (with prefix), or the
2015       *        empty string if qualified names are not available.
2016       * @throws SAXException Any SAX exception, possibly
2017       *            wrapping another exception.
2018       * @see org.xml.sax.ContentHandler#endElement
2019       */
2020      public void endElement(String uri, String localName, String qName)
2021              throws SAXException
2022      {
2023       if (DEBUG)
2024          System.out.println("endElement: uri: " + uri + ", localname: "
2025                                                                                                     + localName + ", qname: "+qName);
2026    
2027        charactersFlush();
2028    
2029        // If no one noticed, startPrefixMapping is a drag.
2030        // Pop the context for the last child (the one pushed by startElement)
2031        m_contextIndexes.quickPop(1);
2032    
2033        // Do it again for this one (the one pushed by the last endElement).
2034        int topContextIndex = m_contextIndexes.peek();
2035        if (topContextIndex != m_prefixMappings.size()) {
2036          m_prefixMappings.setSize(topContextIndex);
2037        }
2038    
2039        int lastNode = m_previous;
2040    
2041        m_previous = m_parents.pop();
2042    
2043        // If lastNode is still DTM.NULL, this element had no children
2044        if (DTM.NULL == lastNode)
2045          m_firstch.setElementAt(DTM.NULL,m_previous);
2046        else
2047          m_nextsib.setElementAt(DTM.NULL,lastNode);
2048    
2049        popShouldStripWhitespace();
2050      }
2051    
2052      /**
2053       * Receive notification of character data inside an element.
2054       *
2055       * <p>By default, do nothing.  Application writers may override this
2056       * method to take specific actions for each chunk of character data
2057       * (such as adding the data to a node or buffer, or printing it to
2058       * a file).</p>
2059       *
2060       * @param ch The characters.
2061       * @param start The start position in the character array.
2062       * @param length The number of characters to use from the
2063       *               character array.
2064       * @throws SAXException Any SAX exception, possibly
2065       *            wrapping another exception.
2066       * @see org.xml.sax.ContentHandler#characters
2067       */
2068      public void characters(char ch[], int start, int length) throws SAXException
2069      {
2070        if (m_textPendingStart == -1)  // First one in this block
2071        {
2072          m_textPendingStart = m_chars.size();
2073          m_coalescedTextType = m_textType;
2074        }
2075        // Type logic: If all adjacent text is CDATASections, the
2076        // concatentated text is treated as a single CDATASection (see
2077        // initialization above).  If any were ordinary Text, the whole
2078        // thing is treated as Text. This may be worth %REVIEW%ing.
2079        else if (m_textType == DTM.TEXT_NODE)
2080        {
2081          m_coalescedTextType = DTM.TEXT_NODE;
2082        }
2083    
2084        m_chars.append(ch, start, length);
2085      }
2086    
2087      /**
2088       * Receive notification of ignorable whitespace in element content.
2089       *
2090       * <p>By default, do nothing.  Application writers may override this
2091       * method to take specific actions for each chunk of ignorable
2092       * whitespace (such as adding data to a node or buffer, or printing
2093       * it to a file).</p>
2094       *
2095       * @param ch The whitespace characters.
2096       * @param start The start position in the character array.
2097       * @param length The number of characters to use from the
2098       *               character array.
2099       * @throws SAXException Any SAX exception, possibly
2100       *            wrapping another exception.
2101       * @see org.xml.sax.ContentHandler#ignorableWhitespace
2102       */
2103      public void ignorableWhitespace(char ch[], int start, int length)
2104              throws SAXException
2105      {
2106    
2107        // %OPT% We can probably take advantage of the fact that we know this 
2108        // is whitespace.
2109        characters(ch, start, length);
2110      }
2111    
2112      /**
2113       * Receive notification of a processing instruction.
2114       *
2115       * <p>By default, do nothing.  Application writers may override this
2116       * method in a subclass to take specific actions for each
2117       * processing instruction, such as setting status variables or
2118       * invoking other methods.</p>
2119       *
2120       * @param target The processing instruction target.
2121       * @param data The processing instruction data, or null if
2122       *             none is supplied.
2123       * @throws SAXException Any SAX exception, possibly
2124       *            wrapping another exception.
2125       * @see org.xml.sax.ContentHandler#processingInstruction
2126       */
2127      public void processingInstruction(String target, String data)
2128              throws SAXException
2129      {
2130        if (DEBUG)
2131                     System.out.println("processingInstruction: target: " + target +", data: "+data);
2132    
2133        charactersFlush();
2134    
2135        int exName = m_expandedNameTable.getExpandedTypeID(null, target,
2136                                             DTM.PROCESSING_INSTRUCTION_NODE);
2137        int dataIndex = m_valuesOrPrefixes.stringToIndex(data);
2138    
2139        m_previous = addNode(DTM.PROCESSING_INSTRUCTION_NODE, exName,
2140                             m_parents.peek(), m_previous,
2141                             dataIndex, false);
2142      }
2143    
2144      /**
2145       * Receive notification of a skipped entity.
2146       *
2147       * <p>By default, do nothing.  Application writers may override this
2148       * method in a subclass to take specific actions for each
2149       * processing instruction, such as setting status variables or
2150       * invoking other methods.</p>
2151       *
2152       * @param name The name of the skipped entity.
2153       * @throws SAXException Any SAX exception, possibly
2154       *            wrapping another exception.
2155       * @see org.xml.sax.ContentHandler#processingInstruction
2156       */
2157      public void skippedEntity(String name) throws SAXException
2158      {
2159    
2160        // %REVIEW% What should be done here?
2161        // no op
2162      }
2163    
2164      ////////////////////////////////////////////////////////////////////
2165      // Implementation of the ErrorHandler interface.
2166      ////////////////////////////////////////////////////////////////////
2167    
2168      /**
2169       * Receive notification of a parser warning.
2170       *
2171       * <p>The default implementation does nothing.  Application writers
2172       * may override this method in a subclass to take specific actions
2173       * for each warning, such as inserting the message in a log file or
2174       * printing it to the console.</p>
2175       *
2176       * @param e The warning information encoded as an exception.
2177       * @throws SAXException Any SAX exception, possibly
2178       *            wrapping another exception.
2179       * @see org.xml.sax.ErrorHandler#warning
2180       * @see org.xml.sax.SAXParseException
2181       */
2182      public void warning(SAXParseException e) throws SAXException
2183      {
2184    
2185        // %REVIEW% Is there anyway to get the JAXP error listener here?
2186        System.err.println(e.getMessage());
2187      }
2188    
2189      /**
2190       * Receive notification of a recoverable parser error.
2191       *
2192       * <p>The default implementation does nothing.  Application writers
2193       * may override this method in a subclass to take specific actions
2194       * for each error, such as inserting the message in a log file or
2195       * printing it to the console.</p>
2196       *
2197       * @param e The warning information encoded as an exception.
2198       * @throws SAXException Any SAX exception, possibly
2199       *            wrapping another exception.
2200       * @see org.xml.sax.ErrorHandler#warning
2201       * @see org.xml.sax.SAXParseException
2202       */
2203      public void error(SAXParseException e) throws SAXException
2204      {
2205        throw e;
2206      }
2207    
2208      /**
2209       * Report a fatal XML parsing error.
2210       *
2211       * <p>The default implementation throws a SAXParseException.
2212       * Application writers may override this method in a subclass if
2213       * they need to take specific actions for each fatal error (such as
2214       * collecting all of the errors into a single report): in any case,
2215       * the application must stop all regular processing when this
2216       * method is invoked, since the document is no longer reliable, and
2217       * the parser may no longer report parsing events.</p>
2218       *
2219       * @param e The error information encoded as an exception.
2220       * @throws SAXException Any SAX exception, possibly
2221       *            wrapping another exception.
2222       * @see org.xml.sax.ErrorHandler#fatalError
2223       * @see org.xml.sax.SAXParseException
2224       */
2225      public void fatalError(SAXParseException e) throws SAXException
2226      {
2227        throw e;
2228      }
2229    
2230      ////////////////////////////////////////////////////////////////////
2231      // Implementation of the DeclHandler interface.
2232      ////////////////////////////////////////////////////////////////////
2233    
2234      /**
2235       * Report an element type declaration.
2236       *
2237       * <p>The content model will consist of the string "EMPTY", the
2238       * string "ANY", or a parenthesised group, optionally followed
2239       * by an occurrence indicator.  The model will be normalized so
2240       * that all whitespace is removed,and will include the enclosing
2241       * parentheses.</p>
2242       *
2243       * @param name The element type name.
2244       * @param model The content model as a normalized string.
2245       * @throws SAXException The application may raise an exception.
2246       */
2247      public void elementDecl(String name, String model) throws SAXException
2248      {
2249    
2250        // no op
2251      }
2252    
2253      /**
2254       * Report an attribute type declaration.
2255       *
2256       * <p>Only the effective (first) declaration for an attribute will
2257       * be reported.  The type will be one of the strings "CDATA",
2258       * "ID", "IDREF", "IDREFS", "NMTOKEN", "NMTOKENS", "ENTITY",
2259       * "ENTITIES", or "NOTATION", or a parenthesized token group with
2260       * the separator "|" and all whitespace removed.</p>
2261       *
2262       * @param eName The name of the associated element.
2263       * @param aName The name of the attribute.
2264       * @param type A string representing the attribute type.
2265       * @param valueDefault A string representing the attribute default
2266       *        ("#IMPLIED", "#REQUIRED", or "#FIXED") or null if
2267       *        none of these applies.
2268       * @param value A string representing the attribute's default value,
2269       *        or null if there is none.
2270       * @throws SAXException The application may raise an exception.
2271       */
2272      public void attributeDecl(
2273              String eName, String aName, String type, String valueDefault, String value)
2274                throws SAXException
2275      {
2276    
2277        // no op
2278      }
2279    
2280      /**
2281       * Report an internal entity declaration.
2282       *
2283       * <p>Only the effective (first) declaration for each entity
2284       * will be reported.</p>
2285       *
2286       * @param name The name of the entity.  If it is a parameter
2287       *        entity, the name will begin with '%'.
2288       * @param value The replacement text of the entity.
2289       * @throws SAXException The application may raise an exception.
2290       * @see #externalEntityDecl
2291       * @see org.xml.sax.DTDHandler#unparsedEntityDecl
2292       */
2293      public void internalEntityDecl(String name, String value)
2294              throws SAXException
2295      {
2296    
2297        // no op
2298      }
2299    
2300      /**
2301       * Report a parsed external entity declaration.
2302       *
2303       * <p>Only the effective (first) declaration for each entity
2304       * will be reported.</p>
2305       *
2306       * @param name The name of the entity.  If it is a parameter
2307       *        entity, the name will begin with '%'.
2308       * @param publicId The declared public identifier of the entity, or
2309       *        null if none was declared.
2310       * @param systemId The declared system identifier of the entity.
2311       * @throws SAXException The application may raise an exception.
2312       * @see #internalEntityDecl
2313       * @see org.xml.sax.DTDHandler#unparsedEntityDecl
2314       */
2315      public void externalEntityDecl(
2316              String name, String publicId, String systemId) throws SAXException
2317      {
2318    
2319        // no op
2320      }
2321    
2322      ////////////////////////////////////////////////////////////////////
2323      // Implementation of the LexicalHandler interface.
2324      ////////////////////////////////////////////////////////////////////
2325    
2326      /**
2327       * Report the start of DTD declarations, if any.
2328       *
2329       * <p>Any declarations are assumed to be in the internal subset
2330       * unless otherwise indicated by a {@link #startEntity startEntity}
2331       * event.</p>
2332       *
2333       * <p>Note that the start/endDTD events will appear within
2334       * the start/endDocument events from ContentHandler and
2335       * before the first startElement event.</p>
2336       *
2337       * @param name The document type name.
2338       * @param publicId The declared public identifier for the
2339       *        external DTD subset, or null if none was declared.
2340       * @param systemId The declared system identifier for the
2341       *        external DTD subset, or null if none was declared.
2342       * @throws SAXException The application may raise an
2343       *            exception.
2344       * @see #endDTD
2345       * @see #startEntity
2346       */
2347      public void startDTD(String name, String publicId, String systemId)
2348              throws SAXException
2349      {
2350    
2351        m_insideDTD = true;
2352      }
2353    
2354      /**
2355       * Report the end of DTD declarations.
2356       *
2357       * @throws SAXException The application may raise an exception.
2358       * @see #startDTD
2359       */
2360      public void endDTD() throws SAXException
2361      {
2362    
2363        m_insideDTD = false;
2364      }
2365    
2366      /**
2367       * Report the beginning of an entity in content.
2368       *
2369       * <p><strong>NOTE:</entity> entity references in attribute
2370       * values -- and the start and end of the document entity --
2371       * are never reported.</p>
2372       *
2373       * <p>The start and end of the external DTD subset are reported
2374       * using the pseudo-name "[dtd]".  All other events must be
2375       * properly nested within start/end entity events.</p>
2376       *
2377       * <p>Note that skipped entities will be reported through the
2378       * {@link org.xml.sax.ContentHandler#skippedEntity skippedEntity}
2379       * event, which is part of the ContentHandler interface.</p>
2380       *
2381       * @param name The name of the entity.  If it is a parameter
2382       *        entity, the name will begin with '%'.
2383       * @throws SAXException The application may raise an exception.
2384       * @see #endEntity
2385       * @see org.xml.sax.ext.DeclHandler#internalEntityDecl
2386       * @see org.xml.sax.ext.DeclHandler#externalEntityDecl
2387       */
2388      public void startEntity(String name) throws SAXException
2389      {
2390    
2391        // no op
2392      }
2393    
2394      /**
2395       * Report the end of an entity.
2396       *
2397       * @param name The name of the entity that is ending.
2398       * @throws SAXException The application may raise an exception.
2399       * @see #startEntity
2400       */
2401      public void endEntity(String name) throws SAXException
2402      {
2403    
2404        // no op
2405      }
2406    
2407      /**
2408       * Report the start of a CDATA section.
2409       *
2410       * <p>The contents of the CDATA section will be reported through
2411       * the regular {@link org.xml.sax.ContentHandler#characters
2412       * characters} event.</p>
2413       *
2414       * @throws SAXException The application may raise an exception.
2415       * @see #endCDATA
2416       */
2417      public void startCDATA() throws SAXException
2418      {
2419        m_textType = DTM.CDATA_SECTION_NODE;
2420      }
2421    
2422      /**
2423       * Report the end of a CDATA section.
2424       *
2425       * @throws SAXException The application may raise an exception.
2426       * @see #startCDATA
2427       */
2428      public void endCDATA() throws SAXException
2429      {
2430        m_textType = DTM.TEXT_NODE;
2431      }
2432    
2433      /**
2434       * Report an XML comment anywhere in the document.
2435       *
2436       * <p>This callback will be used for comments inside or outside the
2437       * document element, including comments in the external DTD
2438       * subset (if read).</p>
2439       *
2440       * @param ch An array holding the characters in the comment.
2441       * @param start The starting position in the array.
2442       * @param length The number of characters to use from the array.
2443       * @throws SAXException The application may raise an exception.
2444       */
2445      public void comment(char ch[], int start, int length) throws SAXException
2446      {
2447    
2448        if (m_insideDTD)      // ignore comments if we're inside the DTD
2449          return;
2450    
2451        charactersFlush();
2452    
2453        int exName = m_expandedNameTable.getExpandedTypeID(DTM.COMMENT_NODE);
2454    
2455        // For now, treat comments as strings...  I guess we should do a
2456        // seperate FSB buffer instead.
2457        int dataIndex = m_valuesOrPrefixes.stringToIndex(new String(ch, start,
2458                          length));
2459    
2460    
2461        m_previous = addNode(DTM.COMMENT_NODE, exName,
2462                             m_parents.peek(), m_previous, dataIndex, false);
2463      }
2464    
2465      /**
2466       * Set a run time property for this DTM instance.
2467       * 
2468       * %REVIEW% Now that we no longer use this method to support
2469       * getSourceLocatorFor, can we remove it?
2470       *
2471       * @param property a <code>String</code> value
2472       * @param value an <code>Object</code> value
2473       */
2474      public void setProperty(String property, Object value)
2475      {
2476      }
2477    
2478      /** Retrieve the SourceLocator associated with a specific node.
2479       * This is only meaningful if the XalanProperties.SOURCE_LOCATION flag was
2480       * set True using setProperty; if it was never set, or was set false, we
2481       * will return null. 
2482       * 
2483       * (We _could_ return a locator with the document's base URI and bogus 
2484       * line/column information. Trying that; see the else clause.)
2485       * */
2486      public SourceLocator getSourceLocatorFor(int node)
2487      {
2488        if (m_useSourceLocationProperty)
2489        {
2490    
2491          node = makeNodeIdentity(node);
2492          
2493    
2494          return new NodeLocator(null,
2495                                 m_sourceSystemId.elementAt(node),
2496                                 m_sourceLine.elementAt(node),
2497                                 m_sourceColumn.elementAt(node));
2498        }
2499        else if(m_locator!=null)
2500        {
2501            return new NodeLocator(null,m_locator.getSystemId(),-1,-1);
2502        }
2503        else if(m_systemId!=null)
2504        {
2505            return new NodeLocator(null,m_systemId,-1,-1);
2506        }
2507        return null;
2508      }
2509      
2510      public String getFixedNames(int type){
2511        return m_fixednames[type];
2512      }
2513    }