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