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