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