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