1 /*
   2  * reserved comment block
   3  * DO NOT REMOVE OR ALTER!
   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;
  23 
  24 import com.sun.org.apache.xalan.internal.utils.FactoryImpl;
  25 import javax.xml.parsers.DocumentBuilder;
  26 import javax.xml.parsers.DocumentBuilderFactory;
  27 import javax.xml.transform.Source;
  28 import javax.xml.transform.dom.DOMSource;
  29 import javax.xml.transform.sax.SAXSource;
  30 import javax.xml.transform.stream.StreamSource;
  31 
  32 import com.sun.org.apache.xml.internal.dtm.DTM;
  33 import com.sun.org.apache.xml.internal.dtm.DTMException;
  34 import com.sun.org.apache.xml.internal.dtm.DTMFilter;
  35 import com.sun.org.apache.xml.internal.dtm.DTMIterator;
  36 import com.sun.org.apache.xml.internal.dtm.DTMManager;
  37 import com.sun.org.apache.xml.internal.dtm.DTMWSFilter;
  38 import com.sun.org.apache.xml.internal.dtm.ref.dom2dtm.DOM2DTM;
  39 import com.sun.org.apache.xml.internal.dtm.ref.sax2dtm.SAX2DTM;
  40 import com.sun.org.apache.xml.internal.dtm.ref.sax2dtm.SAX2RTFDTM;
  41 import com.sun.org.apache.xml.internal.res.XMLErrorResources;
  42 import com.sun.org.apache.xml.internal.res.XMLMessages;
  43 import com.sun.org.apache.xml.internal.utils.PrefixResolver;
  44 import com.sun.org.apache.xml.internal.utils.SystemIDResolver;
  45 import com.sun.org.apache.xml.internal.utils.XMLReaderManager;
  46 import com.sun.org.apache.xml.internal.utils.XMLStringFactory;
  47 
  48 import org.w3c.dom.Document;
  49 import org.w3c.dom.Node;
  50 
  51 import org.xml.sax.InputSource;
  52 import org.xml.sax.SAXException;
  53 import org.xml.sax.SAXNotRecognizedException;
  54 import org.xml.sax.SAXNotSupportedException;
  55 import org.xml.sax.XMLReader;
  56 import org.xml.sax.helpers.DefaultHandler;
  57 
  58 /**
  59  * The default implementation for the DTMManager.
  60  *
  61  * %REVIEW% There is currently a reentrancy issue, since the finalizer
  62  * for XRTreeFrag (which runs in the GC thread) wants to call
  63  * DTMManager.release(), and may do so at the same time that the main
  64  * transformation thread is accessing the manager. Our current solution is
  65  * to make most of the manager's methods <code>synchronized</code>.
  66  * Early tests suggest that doing so is not causing a significant
  67  * performance hit in Xalan. However, it should be noted that there
  68  * is a possible alternative solution: rewrite release() so it merely
  69  * posts a request for release onto a threadsafe queue, and explicitly
  70  * process that queue on an infrequent basis during main-thread
  71  * activity (eg, when getDTM() is invoked). The downside of that solution
  72  * would be a greater delay before the DTM's storage is actually released
  73  * for reuse.
  74  * */
  75 public class DTMManagerDefault extends DTMManager
  76 {
  77   //static final boolean JKESS_XNI_EXPERIMENT=true;
  78 
  79   /** Set this to true if you want a dump of the DTM after creation. */
  80   private static final boolean DUMPTREE = false;
  81 
  82   /** Set this to true if you want a basic diagnostics. */
  83   private static final boolean DEBUG = false;
  84 
  85   /**
  86    * Map from DTM identifier numbers to DTM objects that this manager manages.
  87    * One DTM may have several prefix numbers, if extended node indexing
  88    * is in use; in that case, m_dtm_offsets[] will used to control which
  89    * prefix maps to which section of the DTM.
  90    *
  91    * This array grows as necessary; see addDTM().
  92    *
  93    * This array grows as necessary; see addDTM(). Growth is uncommon... but
  94    * access needs to be blindingly fast since it's used in node addressing.
  95    */
  96   protected DTM m_dtms[] = new DTM[256];
  97 
  98   /** Map from DTM identifier numbers to offsets. For small DTMs with a
  99    * single identifier, this will always be 0. In overflow addressing, where
 100    * additional identifiers are allocated to access nodes beyond the range of
 101    * a single Node Handle, this table is used to map the handle's node field
 102    * into the actual node identifier.
 103    *
 104    * This array grows as necessary; see addDTM().
 105    *
 106    * This array grows as necessary; see addDTM(). Growth is uncommon... but
 107    * access needs to be blindingly fast since it's used in node addressing.
 108    * (And at the moment, that includes accessing it from DTMDefaultBase,
 109    * which is why this is not Protected or Private.)
 110    */
 111   int m_dtm_offsets[] = new int[256];
 112 
 113   /**
 114    * The cache for XMLReader objects to be used if the user did not
 115    * supply an XMLReader for a SAXSource or supplied a StreamSource.
 116    */
 117   protected XMLReaderManager m_readerManager = null;
 118 
 119   /**
 120    * The default implementation of ContentHandler, DTDHandler and ErrorHandler.
 121    */
 122   protected DefaultHandler m_defaultHandler = new DefaultHandler();
 123 
 124   /**
 125    * Add a DTM to the DTM table. This convenience call adds it as the
 126    * "base DTM ID", with offset 0. The other version of addDTM should
 127    * be used if you want to add "extended" DTM IDs with nonzero offsets.
 128    *
 129    * @param dtm Should be a valid reference to a DTM.
 130    * @param id Integer DTM ID to be bound to this DTM
 131    */
 132   synchronized public void addDTM(DTM dtm, int id) {    addDTM(dtm,id,0); }
 133 
 134 
 135   /**
 136    * Add a DTM to the DTM table.
 137    *
 138    * @param dtm Should be a valid reference to a DTM.
 139    * @param id Integer DTM ID to be bound to this DTM.
 140    * @param offset Integer addressing offset. The internal DTM Node ID is
 141    * obtained by adding this offset to the node-number field of the
 142    * public DTM Handle. For the first DTM ID accessing each DTM, this is 0;
 143    * for overflow addressing it will be a multiple of 1<<IDENT_DTM_NODE_BITS.
 144    */
 145   synchronized public void addDTM(DTM dtm, int id, int offset)
 146   {
 147                 if(id>=IDENT_MAX_DTMS)
 148                 {
 149                         // TODO: %REVIEW% Not really the right error message.
 150             throw new DTMException(XMLMessages.createXMLMessage(XMLErrorResources.ER_NO_DTMIDS_AVAIL, null)); //"No more DTM IDs are available!");
 151                 }
 152 
 153                 // We used to just allocate the array size to IDENT_MAX_DTMS.
 154                 // But we expect to increase that to 16 bits, and I'm not willing
 155                 // to allocate that much space unless needed. We could use one of our
 156                 // handy-dandy Fast*Vectors, but this will do for now.
 157                 // %REVIEW%
 158                 int oldlen=m_dtms.length;
 159                 if(oldlen<=id)
 160                 {
 161                         // Various growth strategies are possible. I think we don't want
 162                         // to over-allocate excessively, and I'm willing to reallocate
 163                         // more often to get that. See also Fast*Vector classes.
 164                         //
 165                         // %REVIEW% Should throw a more diagnostic error if we go over the max...
 166                         int newlen=Math.min((id+256),IDENT_MAX_DTMS);
 167 
 168                         DTM new_m_dtms[] = new DTM[newlen];
 169                         System.arraycopy(m_dtms,0,new_m_dtms,0,oldlen);
 170                         m_dtms=new_m_dtms;
 171                         int new_m_dtm_offsets[] = new int[newlen];
 172                         System.arraycopy(m_dtm_offsets,0,new_m_dtm_offsets,0,oldlen);
 173                         m_dtm_offsets=new_m_dtm_offsets;
 174                 }
 175 
 176     m_dtms[id] = dtm;
 177                 m_dtm_offsets[id]=offset;
 178     dtm.documentRegistration();
 179                 // The DTM should have been told who its manager was when we created it.
 180                 // Do we need to allow for adopting DTMs _not_ created by this manager?
 181   }
 182 
 183   /**
 184    * Get the first free DTM ID available. %OPT% Linear search is inefficient!
 185    */
 186   synchronized public int getFirstFreeDTMID()
 187   {
 188     int n = m_dtms.length;
 189     for (int i = 1; i < n; i++)
 190     {
 191       if(null == m_dtms[i])
 192       {
 193         return i;
 194       }
 195     }
 196                 return n; // count on addDTM() to throw exception if out of range
 197   }
 198 
 199   /**
 200    * The default table for exandedNameID lookups.
 201    */
 202   private ExpandedNameTable m_expandedNameTable =
 203     new ExpandedNameTable();
 204 
 205   /**
 206    * Constructor DTMManagerDefault
 207    *
 208    */
 209   public DTMManagerDefault(){}
 210 
 211 
 212   /**
 213    * Get an instance of a DTM, loaded with the content from the
 214    * specified source.  If the unique flag is true, a new instance will
 215    * always be returned.  Otherwise it is up to the DTMManager to return a
 216    * new instance or an instance that it already created and may be being used
 217    * by someone else.
 218    *
 219    * A bit of magic in this implementation: If the source is null, unique is true,
 220    * and incremental and doIndexing are both false, we return an instance of
 221    * SAX2RTFDTM, which see.
 222    *
 223    * (I think more parameters will need to be added for error handling, and entity
 224    * resolution, and more explicit control of the RTF situation).
 225    *
 226    * @param source the specification of the source object.
 227    * @param unique true if the returned DTM must be unique, probably because it
 228    * is going to be mutated.
 229    * @param whiteSpaceFilter Enables filtering of whitespace nodes, and may
 230    *                         be null.
 231    * @param incremental true if the DTM should be built incrementally, if
 232    *                    possible.
 233    * @param doIndexing true if the caller considers it worth it to use
 234    *                   indexing schemes.
 235    *
 236    * @return a non-null DTM reference.
 237    */
 238   synchronized public DTM getDTM(Source source, boolean unique,
 239                                  DTMWSFilter whiteSpaceFilter,
 240                                  boolean incremental, boolean doIndexing)
 241   {
 242 
 243     if(DEBUG && null != source)
 244       System.out.println("Starting "+
 245                          (unique ? "UNIQUE" : "shared")+
 246                          " source: "+source.getSystemId()
 247                          );
 248 
 249     XMLStringFactory xstringFactory = m_xsf;
 250     int dtmPos = getFirstFreeDTMID();
 251     int documentID = dtmPos << IDENT_DTM_NODE_BITS;
 252 
 253     if ((null != source) && source instanceof DOMSource)
 254     {
 255       DOM2DTM dtm = new DOM2DTM(this, (DOMSource) source, documentID,
 256                                 whiteSpaceFilter, xstringFactory, doIndexing);
 257 
 258       addDTM(dtm, dtmPos, 0);
 259 
 260       //      if (DUMPTREE)
 261       //      {
 262       //        dtm.dumpDTM();
 263       //      }
 264 
 265       return dtm;
 266     }
 267     else
 268     {
 269       boolean isSAXSource = (null != source)
 270         ? (source instanceof SAXSource) : true;
 271       boolean isStreamSource = (null != source)
 272         ? (source instanceof StreamSource) : false;
 273 
 274       if (isSAXSource || isStreamSource) {
 275         XMLReader reader = null;
 276         SAX2DTM dtm;
 277 
 278         try {
 279           InputSource xmlSource;
 280 
 281           if (null == source) {
 282             xmlSource = null;
 283           } else {
 284             reader = getXMLReader(source);
 285             xmlSource = SAXSource.sourceToInputSource(source);
 286 
 287             String urlOfSource = xmlSource.getSystemId();
 288 
 289             if (null != urlOfSource) {
 290               try {
 291                 urlOfSource = SystemIDResolver.getAbsoluteURI(urlOfSource);
 292               } catch (Exception e) {
 293                 // %REVIEW% Is there a better way to send a warning?
 294                 System.err.println("Can not absolutize URL: " + urlOfSource);
 295               }
 296 
 297               xmlSource.setSystemId(urlOfSource);
 298             }
 299           }
 300 
 301           if (source==null && unique && !incremental && !doIndexing) {
 302             // Special case to support RTF construction into shared DTM.
 303             // It should actually still work for other uses,
 304             // but may be slightly deoptimized relative to the base
 305             // to allow it to deal with carrying multiple documents.
 306             //
 307             // %REVIEW% This is a sloppy way to request this mode;
 308             // we need to consider architectural improvements.
 309             dtm = new SAX2RTFDTM(this, source, documentID, whiteSpaceFilter,
 310                                  xstringFactory, doIndexing);
 311           }
 312           /**************************************************************
 313           // EXPERIMENTAL 3/22/02
 314           else if(JKESS_XNI_EXPERIMENT && m_incremental) {
 315             dtm = new XNI2DTM(this, source, documentID, whiteSpaceFilter,
 316                               xstringFactory, doIndexing);
 317           }
 318           **************************************************************/
 319           // Create the basic SAX2DTM.
 320           else {
 321             dtm = new SAX2DTM(this, source, documentID, whiteSpaceFilter,
 322                               xstringFactory, doIndexing);
 323           }
 324 
 325           // Go ahead and add the DTM to the lookup table.  This needs to be
 326           // done before any parsing occurs. Note offset 0, since we've just
 327           // created a new DTM.
 328           addDTM(dtm, dtmPos, 0);
 329 
 330 
 331           boolean haveXercesParser =
 332                      (null != reader)
 333                      && (reader.getClass()
 334                                .getName()
 335                                .equals("com.sun.org.apache.xerces.internal.parsers.SAXParser") );
 336 
 337           if (haveXercesParser) {
 338             incremental = true;  // No matter what.  %REVIEW%
 339           }
 340 
 341           // If the reader is null, but they still requested an incremental
 342           // build, then we still want to set up the IncrementalSAXSource stuff.
 343           if (m_incremental && incremental
 344                /* || ((null == reader) && incremental) */) {
 345             IncrementalSAXSource coParser=null;
 346 
 347             if (haveXercesParser) {
 348               // IncrementalSAXSource_Xerces to avoid threading.
 349               try {
 350                 coParser = new com.sun.org.apache.xml.internal.dtm.ref.IncrementalSAXSource_Xerces();
 351               }  catch( Exception ex ) {
 352                 ex.printStackTrace();
 353                 coParser=null;
 354               }
 355             }
 356 
 357             if (coParser==null ) {
 358               // Create a IncrementalSAXSource to run on the secondary thread.
 359               if (null == reader) {
 360                 coParser = new IncrementalSAXSource_Filter();
 361               } else {
 362                 IncrementalSAXSource_Filter filter =
 363                          new IncrementalSAXSource_Filter();
 364                 filter.setXMLReader(reader);
 365                 coParser=filter;
 366               }
 367             }
 368 
 369 
 370             /**************************************************************
 371             // EXPERIMENTAL 3/22/02
 372             if (JKESS_XNI_EXPERIMENT && m_incremental &&
 373                   dtm instanceof XNI2DTM &&
 374                   coParser instanceof IncrementalSAXSource_Xerces) {
 375                 com.sun.org.apache.xerces.internal.xni.parser.XMLPullParserConfiguration xpc=
 376                       ((IncrementalSAXSource_Xerces)coParser)
 377                                            .getXNIParserConfiguration();
 378               if (xpc!=null) {
 379                 // Bypass SAX; listen to the XNI stream
 380                 ((XNI2DTM)dtm).setIncrementalXNISource(xpc);
 381               } else {
 382                   // Listen to the SAX stream (will fail, diagnostically...)
 383                 dtm.setIncrementalSAXSource(coParser);
 384               }
 385             } else
 386             ***************************************************************/
 387 
 388             // Have the DTM set itself up as IncrementalSAXSource's listener.
 389             dtm.setIncrementalSAXSource(coParser);
 390 
 391             if (null == xmlSource) {
 392 
 393               // Then the user will construct it themselves.
 394               return dtm;
 395             }
 396 
 397             if (null == reader.getErrorHandler()) {
 398               reader.setErrorHandler(dtm);
 399             }
 400             reader.setDTDHandler(dtm);
 401 
 402             try {
 403               // Launch parsing coroutine.  Launches a second thread,
 404               // if we're using IncrementalSAXSource.filter().
 405 
 406               coParser.startParse(xmlSource);
 407             } catch (RuntimeException re) {
 408 
 409               dtm.clearCoRoutine();
 410 
 411               throw re;
 412             } catch (Exception e) {
 413 
 414               dtm.clearCoRoutine();
 415 
 416               throw new com.sun.org.apache.xml.internal.utils.WrappedRuntimeException(e);
 417             }
 418           } else {
 419             if (null == reader) {
 420 
 421               // Then the user will construct it themselves.
 422               return dtm;
 423             }
 424 
 425             // not incremental
 426             reader.setContentHandler(dtm);
 427             reader.setDTDHandler(dtm);
 428             if (null == reader.getErrorHandler()) {
 429               reader.setErrorHandler(dtm);
 430             }
 431 
 432             try {
 433               reader.setProperty(
 434                                "http://xml.org/sax/properties/lexical-handler",
 435                                dtm);
 436             } catch (SAXNotRecognizedException e){}
 437               catch (SAXNotSupportedException e){}
 438 
 439             try {
 440               reader.parse(xmlSource);
 441             } catch (RuntimeException re) {
 442               dtm.clearCoRoutine();
 443 
 444               throw re;
 445             } catch (Exception e) {
 446               dtm.clearCoRoutine();
 447 
 448               throw new com.sun.org.apache.xml.internal.utils.WrappedRuntimeException(e);
 449             }
 450           }
 451 
 452           if (DUMPTREE) {
 453             System.out.println("Dumping SAX2DOM");
 454             dtm.dumpDTM(System.err);
 455           }
 456 
 457           return dtm;
 458         } finally {
 459           // Reset the ContentHandler, DTDHandler, ErrorHandler to the DefaultHandler
 460           // after creating the DTM.
 461           if (reader != null && !(m_incremental && incremental)) {
 462             reader.setContentHandler(m_defaultHandler);
 463             reader.setDTDHandler(m_defaultHandler);
 464             reader.setErrorHandler(m_defaultHandler);
 465 
 466             // Reset the LexicalHandler to null after creating the DTM.
 467             try {
 468               reader.setProperty("http://xml.org/sax/properties/lexical-handler", null);
 469             }
 470             catch (Exception e) {}
 471           }
 472           releaseXMLReader(reader);
 473         }
 474       } else {
 475 
 476         // It should have been handled by a derived class or the caller
 477         // made a mistake.
 478         throw new DTMException(XMLMessages.createXMLMessage(XMLErrorResources.ER_NOT_SUPPORTED, new Object[]{source})); //"Not supported: " + source);
 479       }
 480     }
 481   }
 482 
 483   /**
 484    * Given a W3C DOM node, try and return a DTM handle.
 485    * Note: calling this may be non-optimal, and there is no guarantee that
 486    * the node will be found in any particular DTM.
 487    *
 488    * @param node Non-null reference to a DOM node.
 489    *
 490    * @return a valid DTM handle.
 491    */
 492   synchronized public int getDTMHandleFromNode(org.w3c.dom.Node node)
 493   {
 494     if(null == node)
 495       throw new IllegalArgumentException(XMLMessages.createXMLMessage(XMLErrorResources.ER_NODE_NON_NULL, null)); //"node must be non-null for getDTMHandleFromNode!");
 496 
 497     if (node instanceof com.sun.org.apache.xml.internal.dtm.ref.DTMNodeProxy)
 498       return ((com.sun.org.apache.xml.internal.dtm.ref.DTMNodeProxy) node).getDTMNodeNumber();
 499 
 500     else
 501     {
 502       // Find the DOM2DTMs wrapped around this Document (if any)
 503       // and check whether they contain the Node in question.
 504       //
 505       // NOTE that since a DOM2DTM may represent a subtree rather
 506       // than a full document, we have to be prepared to check more
 507       // than one -- and there is no guarantee that we will find
 508       // one that contains ancestors or siblings of the node we're
 509       // seeking.
 510       //
 511       // %REVIEW% We could search for the one which contains this
 512       // node at the deepest level, and thus covers the widest
 513       // subtree, but that's going to entail additional work
 514       // checking more DTMs... and getHandleOfNode is not a
 515       // cheap operation in most implementations.
 516                         //
 517                         // TODO: %REVIEW% If overflow addressing, we may recheck a DTM
 518                         // already examined. Ouch. But with the increased number of DTMs,
 519                         // scanning back to check this is painful.
 520                         // POSSIBLE SOLUTIONS:
 521                         //   Generate a list of _unique_ DTM objects?
 522                         //   Have each DTM cache last DOM node search?
 523                         int max = m_dtms.length;
 524       for(int i = 0; i < max; i++)
 525         {
 526           DTM thisDTM=m_dtms[i];
 527           if((null != thisDTM) && thisDTM instanceof DOM2DTM)
 528           {
 529             int handle=((DOM2DTM)thisDTM).getHandleOfNode(node);
 530             if(handle!=DTM.NULL) return handle;
 531           }
 532          }
 533 
 534                         // Not found; generate a new DTM.
 535                         //
 536                         // %REVIEW% Is this really desirable, or should we return null
 537                         // and make folks explicitly instantiate from a DOMSource? The
 538                         // latter is more work but gives the caller the opportunity to
 539                         // explicitly add the DTM to a DTMManager... and thus to know when
 540                         // it can be discarded again, which is something we need to pay much
 541                         // more attention to. (Especially since only DTMs which are assigned
 542                         // to a manager can use the overflow addressing scheme.)
 543                         //
 544                         // %BUG% If the source node was a DOM2DTM$defaultNamespaceDeclarationNode
 545                         // and the DTM wasn't registered with this DTMManager, we will create
 546                         // a new DTM and _still_ not be able to find the node (since it will
 547                         // be resynthesized). Another reason to push hard on making all DTMs
 548                         // be managed DTMs.
 549 
 550                         // Since the real root of our tree may be a DocumentFragment, we need to
 551       // use getParent to find the root, instead of getOwnerDocument.  Otherwise
 552       // DOM2DTM#getHandleOfNode will be very unhappy.
 553       Node root = node;
 554       Node p = (root.getNodeType() == Node.ATTRIBUTE_NODE) ? ((org.w3c.dom.Attr)root).getOwnerElement() : root.getParentNode();
 555       for (; p != null; p = p.getParentNode())
 556       {
 557         root = p;
 558       }
 559 
 560       DOM2DTM dtm = (DOM2DTM) getDTM(new javax.xml.transform.dom.DOMSource(root),
 561                                                                                                                                                  false, null, true, true);
 562 
 563       int handle;
 564 
 565       if(node instanceof com.sun.org.apache.xml.internal.dtm.ref.dom2dtm.DOM2DTMdefaultNamespaceDeclarationNode)
 566       {
 567                                 // Can't return the same node since it's unique to a specific DTM,
 568                                 // but can return the equivalent node -- find the corresponding
 569                                 // Document Element, then ask it for the xml: namespace decl.
 570                                 handle=dtm.getHandleOfNode(((org.w3c.dom.Attr)node).getOwnerElement());
 571                                 handle=dtm.getAttributeNode(handle,node.getNamespaceURI(),node.getLocalName());
 572       }
 573       else
 574                                 handle = ((DOM2DTM)dtm).getHandleOfNode(node);
 575 
 576       if(DTM.NULL == handle)
 577         throw new RuntimeException(XMLMessages.createXMLMessage(XMLErrorResources.ER_COULD_NOT_RESOLVE_NODE, null)); //"Could not resolve the node to a handle!");
 578 
 579       return handle;
 580     }
 581   }
 582 
 583   /**
 584    * This method returns the SAX2 parser to use with the InputSource
 585    * obtained from this URI.
 586    * It may return null if any SAX2-conformant XML parser can be used,
 587    * or if getInputSource() will also return null. The parser must
 588    * be free for use (i.e., not currently in use for another parse().
 589    * After use of the parser is completed, the releaseXMLReader(XMLReader)
 590    * must be called.
 591    *
 592    * @param inputSource The value returned from the URIResolver.
 593    * @return  a SAX2 XMLReader to use to resolve the inputSource argument.
 594    *
 595    * @return non-null XMLReader reference ready to parse.
 596    */
 597   synchronized public XMLReader getXMLReader(Source inputSource)
 598   {
 599 
 600     try
 601     {
 602       XMLReader reader = (inputSource instanceof SAXSource)
 603                          ? ((SAXSource) inputSource).getXMLReader() : null;
 604 
 605       // If user did not supply a reader, ask for one from the reader manager
 606       if (null == reader) {
 607         if (m_readerManager == null) {
 608             m_readerManager = XMLReaderManager.getInstance(super.useServicesMechnism());
 609         }
 610 
 611         reader = m_readerManager.getXMLReader();
 612       }
 613 
 614       return reader;
 615 
 616     } catch (SAXException se) {
 617       throw new DTMException(se.getMessage(), se);
 618     }
 619   }
 620 
 621   /**
 622    * Indicates that the XMLReader object is no longer in use for the transform.
 623    *
 624    * Note that the getXMLReader method may return an XMLReader that was
 625    * specified on the SAXSource object by the application code.  Such a
 626    * reader should still be passed to releaseXMLReader, but the reader manager
 627    * will only re-use XMLReaders that it created.
 628    *
 629    * @param reader The XMLReader to be released.
 630    */
 631   synchronized public void releaseXMLReader(XMLReader reader) {
 632     if (m_readerManager != null) {
 633       m_readerManager.releaseXMLReader(reader);
 634     }
 635   }
 636 
 637   /**
 638    * Return the DTM object containing a representation of this node.
 639    *
 640    * @param nodeHandle DTM Handle indicating which node to retrieve
 641    *
 642    * @return a reference to the DTM object containing this node.
 643    */
 644   synchronized public DTM getDTM(int nodeHandle)
 645   {
 646     try
 647     {
 648       // Performance critical function.
 649       return m_dtms[nodeHandle >>> IDENT_DTM_NODE_BITS];
 650     }
 651     catch(java.lang.ArrayIndexOutOfBoundsException e)
 652     {
 653       if(nodeHandle==DTM.NULL)
 654                                 return null;            // Accept as a special case.
 655       else
 656                                 throw e;                // Programming error; want to know about it.
 657     }
 658   }
 659 
 660   /**
 661    * Given a DTM, find the ID number in the DTM tables which addresses
 662    * the start of the document. If overflow addressing is in use, other
 663    * DTM IDs may also be assigned to this DTM.
 664    *
 665    * @param dtm The DTM which (hopefully) contains this node.
 666    *
 667    * @return The DTM ID (as the high bits of a NodeHandle, not as our
 668    * internal index), or -1 if the DTM doesn't belong to this manager.
 669    */
 670   synchronized public int getDTMIdentity(DTM dtm)
 671   {
 672         // Shortcut using DTMDefaultBase's extension hooks
 673         // %REVIEW% Should the lookup be part of the basic DTM API?
 674         if(dtm instanceof DTMDefaultBase)
 675         {
 676                 DTMDefaultBase dtmdb=(DTMDefaultBase)dtm;
 677                 if(dtmdb.getManager()==this)
 678                         return dtmdb.getDTMIDs().elementAt(0);
 679                 else
 680                         return -1;
 681         }
 682 
 683     int n = m_dtms.length;
 684 
 685     for (int i = 0; i < n; i++)
 686     {
 687       DTM tdtm = m_dtms[i];
 688 
 689       if (tdtm == dtm && m_dtm_offsets[i]==0)
 690         return i << IDENT_DTM_NODE_BITS;
 691     }
 692 
 693     return -1;
 694   }
 695 
 696   /**
 697    * Release the DTMManager's reference(s) to a DTM, making it unmanaged.
 698    * This is typically done as part of returning the DTM to the heap after
 699    * we're done with it.
 700    *
 701    * @param dtm the DTM to be released.
 702    *
 703    * @param shouldHardDelete If false, this call is a suggestion rather than an
 704    * order, and we may not actually release the DTM. This is intended to
 705    * support intelligent caching of documents... which is not implemented
 706    * in this version of the DTM manager.
 707    *
 708    * @return true if the DTM was released, false if shouldHardDelete was set
 709    * and we decided not to.
 710    */
 711   synchronized public boolean release(DTM dtm, boolean shouldHardDelete)
 712   {
 713     if(DEBUG)
 714     {
 715       System.out.println("Releasing "+
 716                          (shouldHardDelete ? "HARD" : "soft")+
 717                          " dtm="+
 718                          // Following shouldn't need a nodeHandle, but does...
 719                          // and doesn't seem to report the intended value
 720                          dtm.getDocumentBaseURI()
 721                          );
 722     }
 723 
 724     if (dtm instanceof SAX2DTM)
 725     {
 726       ((SAX2DTM) dtm).clearCoRoutine();
 727     }
 728 
 729                 // Multiple DTM IDs may be assigned to a single DTM.
 730                 // The Right Answer is to ask which (if it supports
 731                 // extension, the DTM will need a list anyway). The
 732                 // Wrong Answer, applied if the DTM can't help us,
 733                 // is to linearly search them all; this may be very
 734                 // painful.
 735                 //
 736                 // %REVIEW% Should the lookup move up into the basic DTM API?
 737                 if(dtm instanceof DTMDefaultBase)
 738                 {
 739                         com.sun.org.apache.xml.internal.utils.SuballocatedIntVector ids=((DTMDefaultBase)dtm).getDTMIDs();
 740                         for(int i=ids.size()-1;i>=0;--i)
 741                                 m_dtms[ids.elementAt(i)>>>DTMManager.IDENT_DTM_NODE_BITS]=null;
 742                 }
 743                 else
 744                 {
 745                         int i = getDTMIdentity(dtm);
 746                     if (i >= 0)
 747                         {
 748                                 m_dtms[i >>> DTMManager.IDENT_DTM_NODE_BITS] = null;
 749                         }
 750                 }
 751 
 752     dtm.documentRelease();
 753     return true;
 754   }
 755 
 756   /**
 757    * Method createDocumentFragment
 758    *
 759    *
 760    * NEEDSDOC (createDocumentFragment) @return
 761    */
 762   synchronized public DTM createDocumentFragment()
 763   {
 764 
 765     try
 766     {
 767       DocumentBuilderFactory dbf = FactoryImpl.getDOMFactory(super.useServicesMechnism());
 768       dbf.setNamespaceAware(true);
 769 
 770       DocumentBuilder db = dbf.newDocumentBuilder();
 771       Document doc = db.newDocument();
 772       Node df = doc.createDocumentFragment();
 773 
 774       return getDTM(new DOMSource(df), true, null, false, false);
 775     }
 776     catch (Exception e)
 777     {
 778       throw new DTMException(e);
 779     }
 780   }
 781 
 782   /**
 783    * NEEDSDOC Method createDTMIterator
 784    *
 785    *
 786    * NEEDSDOC @param whatToShow
 787    * NEEDSDOC @param filter
 788    * NEEDSDOC @param entityReferenceExpansion
 789    *
 790    * NEEDSDOC (createDTMIterator) @return
 791    */
 792   synchronized public DTMIterator createDTMIterator(int whatToShow, DTMFilter filter,
 793                                        boolean entityReferenceExpansion)
 794   {
 795 
 796     /** @todo: implement this com.sun.org.apache.xml.internal.dtm.DTMManager abstract method */
 797     return null;
 798   }
 799 
 800   /**
 801    * NEEDSDOC Method createDTMIterator
 802    *
 803    *
 804    * NEEDSDOC @param xpathString
 805    * NEEDSDOC @param presolver
 806    *
 807    * NEEDSDOC (createDTMIterator) @return
 808    */
 809   synchronized public DTMIterator createDTMIterator(String xpathString,
 810                                        PrefixResolver presolver)
 811   {
 812 
 813     /** @todo: implement this com.sun.org.apache.xml.internal.dtm.DTMManager abstract method */
 814     return null;
 815   }
 816 
 817   /**
 818    * NEEDSDOC Method createDTMIterator
 819    *
 820    *
 821    * NEEDSDOC @param node
 822    *
 823    * NEEDSDOC (createDTMIterator) @return
 824    */
 825   synchronized public DTMIterator createDTMIterator(int node)
 826   {
 827 
 828     /** @todo: implement this com.sun.org.apache.xml.internal.dtm.DTMManager abstract method */
 829     return null;
 830   }
 831 
 832   /**
 833    * NEEDSDOC Method createDTMIterator
 834    *
 835    *
 836    * NEEDSDOC @param xpathCompiler
 837    * NEEDSDOC @param pos
 838    *
 839    * NEEDSDOC (createDTMIterator) @return
 840    */
 841   synchronized public DTMIterator createDTMIterator(Object xpathCompiler, int pos)
 842   {
 843 
 844     /** @todo: implement this com.sun.org.apache.xml.internal.dtm.DTMManager abstract method */
 845     return null;
 846   }
 847 
 848   /**
 849    * return the expanded name table.
 850    *
 851    * NEEDSDOC @param dtm
 852    *
 853    * NEEDSDOC ($objectName$) @return
 854    */
 855   public ExpandedNameTable getExpandedNameTable(DTM dtm)
 856   {
 857     return m_expandedNameTable;
 858   }
 859 }