1/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the  "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 *     http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18/*
19 * $Id: SAX2DTM.java 468653 2006-10-28 07:07:05Z minchau $
20 */
21package org.apache.xml.dtm.ref.sax2dtm;
22
23import java.util.Hashtable;
24import java.util.Vector;
25import javax.xml.transform.Source;
26import javax.xml.transform.SourceLocator;
27
28import org.apache.xml.dtm.*;
29import org.apache.xml.dtm.ref.*;
30import org.apache.xml.utils.StringVector;
31import org.apache.xml.utils.IntVector;
32import org.apache.xml.utils.FastStringBuffer;
33import org.apache.xml.utils.IntStack;
34import org.apache.xml.utils.SuballocatedIntVector;
35import org.apache.xml.utils.SystemIDResolver;
36import org.apache.xml.utils.WrappedRuntimeException;
37import org.apache.xml.utils.XMLString;
38import org.apache.xml.utils.XMLStringFactory;
39import org.apache.xml.res.XMLErrorResources;
40import org.apache.xml.res.XMLMessages;
41import org.xml.sax.*;
42import org.xml.sax.ext.*;
43
44/**
45 * This class implements a DTM that tends to be optimized more for speed than
46 * for compactness, that is constructed via SAX2 ContentHandler events.
47 */
48public class SAX2DTM extends DTMDefaultBaseIterators
49        implements EntityResolver, DTDHandler, ContentHandler, ErrorHandler,
50                   DeclHandler, LexicalHandler
51{
52  /** Set true to monitor SAX events and similar diagnostic info. */
53  private static final boolean DEBUG = false;
54
55  /**
56   * If we're building the model incrementally on demand, we need to
57   * be able to tell the source when to send us more data.
58   *
59   * Note that if this has not been set, and you attempt to read ahead
60   * of the current build point, we'll probably throw a null-pointer
61   * exception. We could try to wait-and-retry instead, as a very poor
62   * fallback, but that has all the known problems with multithreading
63   * on multiprocessors and we Don't Want to Go There.
64   *
65   * @see setIncrementalSAXSource
66   */
67  private IncrementalSAXSource m_incrementalSAXSource = null;
68
69  /**
70   * All the character content, including attribute values, are stored in
71   * this buffer.
72   *
73   * %REVIEW% Should this have an option of being shared across DTMs?
74   * Sequentially only; not threadsafe... Currently, I think not.
75   *
76   * %REVIEW% Initial size was pushed way down to reduce weight of RTFs.
77   * pending reduction in number of RTF DTMs. Now that we're sharing a DTM
78   * between RTFs, and tail-pruning... consider going back to the larger/faster.
79   *
80   * Made protected rather than private so SAX2RTFDTM can access it.
81   */
82  //private FastStringBuffer m_chars = new FastStringBuffer(13, 13);
83  protected FastStringBuffer m_chars;
84
85  /** This vector holds offset and length data.
86   */
87  protected SuballocatedIntVector m_data;
88
89  /** The parent stack, needed only for construction.
90   * Made protected rather than private so SAX2RTFDTM can access it.
91   */
92  transient protected IntStack m_parents;
93
94  /** The current previous node, needed only for construction time.
95   * Made protected rather than private so SAX2RTFDTM can access it.
96   */
97  transient protected int m_previous = 0;
98
99  /** 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}
2514