Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/apache/xml/dtm/ref/DTMManagerDefault.java


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