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