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