001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements. See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership. The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the  "License");
007     * you may not use this file except in compliance with the License.
008     * You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing, software
013     * distributed under the License is distributed on an "AS IS" BASIS,
014     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    /*
019     * $Id: DTMManagerDefault.java 468653 2006-10-28 07:07:05Z minchau $
020     */
021    package org.apache.xml.dtm.ref;
022    
023    import javax.xml.parsers.DocumentBuilder;
024    import javax.xml.parsers.DocumentBuilderFactory;
025    import javax.xml.transform.Source;
026    import javax.xml.transform.dom.DOMSource;
027    import javax.xml.transform.sax.SAXSource;
028    import javax.xml.transform.stream.StreamSource;
029    
030    import org.apache.xml.dtm.DTM;
031    import org.apache.xml.dtm.DTMException;
032    import org.apache.xml.dtm.DTMFilter;
033    import org.apache.xml.dtm.DTMIterator;
034    import org.apache.xml.dtm.DTMManager;
035    import org.apache.xml.dtm.DTMWSFilter;
036    import org.apache.xml.dtm.ref.dom2dtm.DOM2DTM;
037    import org.apache.xml.dtm.ref.sax2dtm.SAX2DTM;
038    import org.apache.xml.dtm.ref.sax2dtm.SAX2RTFDTM;
039    import org.apache.xml.res.XMLErrorResources;
040    import org.apache.xml.res.XMLMessages;
041    import org.apache.xml.utils.PrefixResolver;
042    import org.apache.xml.utils.SystemIDResolver;
043    import org.apache.xml.utils.XMLReaderManager;
044    import org.apache.xml.utils.XMLStringFactory;
045    
046    import org.w3c.dom.Document;
047    import org.w3c.dom.Node;
048    
049    import org.xml.sax.InputSource;
050    import org.xml.sax.SAXException;
051    import org.xml.sax.SAXNotRecognizedException;
052    import org.xml.sax.SAXNotSupportedException;
053    import org.xml.sax.XMLReader;
054    import org.xml.sax.helpers.DefaultHandler;
055    
056    /**
057     * The default implementation for the DTMManager.
058     *
059     * %REVIEW% There is currently a reentrancy issue, since the finalizer
060     * for XRTreeFrag (which runs in the GC thread) wants to call
061     * DTMManager.release(), and may do so at the same time that the main
062     * transformation thread is accessing the manager. Our current solution is
063     * to make most of the manager's methods <code>synchronized</code>.
064     * Early tests suggest that doing so is not causing a significant
065     * performance hit in Xalan. However, it should be noted that there
066     * is a possible alternative solution: rewrite release() so it merely
067     * posts a request for release onto a threadsafe queue, and explicitly
068     * process that queue on an infrequent basis during main-thread
069     * activity (eg, when getDTM() is invoked). The downside of that solution
070     * would be a greater delay before the DTM's storage is actually released
071     * for reuse.
072     * */
073    public class DTMManagerDefault extends DTMManager
074    {
075      //static final boolean JKESS_XNI_EXPERIMENT=true;
076    
077      /** Set this to true if you want a dump of the DTM after creation. */
078      private static final boolean DUMPTREE = false;
079    
080      /** Set this to true if you want a basic diagnostics. */
081      private static final boolean DEBUG = false;
082    
083      /**
084       * Map from DTM identifier numbers to DTM objects that this manager manages.
085       * One DTM may have several prefix numbers, if extended node indexing
086       * is in use; in that case, m_dtm_offsets[] will used to control which
087       * prefix maps to which section of the DTM.
088       * 
089       * This array grows as necessary; see addDTM().
090       * 
091       * This array grows as necessary; see addDTM(). Growth is uncommon... but
092       * access needs to be blindingly fast since it's used in node addressing.
093       */
094      protected DTM m_dtms[] = new DTM[256];
095            
096      /** Map from DTM identifier numbers to offsets. For small DTMs with a 
097       * single identifier, this will always be 0. In overflow addressing, where
098       * additional identifiers are allocated to access nodes beyond the range of
099       * 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    }