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