Home | History | Annotate | Download | only in sax2dtm
      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  */
     21 package org.apache.xml.dtm.ref.sax2dtm;
     22 
     23 import java.util.Hashtable;
     24 import java.util.Vector;
     25 import javax.xml.transform.Source;
     26 import javax.xml.transform.SourceLocator;
     27 
     28 import org.apache.xml.dtm.*;
     29 import org.apache.xml.dtm.ref.*;
     30 import org.apache.xml.utils.StringVector;
     31 import org.apache.xml.utils.IntVector;
     32 import org.apache.xml.utils.FastStringBuffer;
     33 import org.apache.xml.utils.IntStack;
     34 import org.apache.xml.utils.SuballocatedIntVector;
     35 import org.apache.xml.utils.SystemIDResolver;
     36 import org.apache.xml.utils.WrappedRuntimeException;
     37 import org.apache.xml.utils.XMLString;
     38 import org.apache.xml.utils.XMLStringFactory;
     39 import org.apache.xml.res.XMLErrorResources;
     40 import org.apache.xml.res.XMLMessages;
     41 import org.xml.sax.*;
     42 import 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  */
     48 public 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