1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements. See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership. The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the  "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 /*
19  * $Id: DTMManagerDefault.java 468653 2006-10-28 07:07:05Z minchau $
20  */
21 package org.apache.xml.dtm.ref;
22 
23 import javax.xml.parsers.DocumentBuilder;
24 import javax.xml.parsers.DocumentBuilderFactory;
25 import javax.xml.transform.Source;
26 import javax.xml.transform.dom.DOMSource;
27 import javax.xml.transform.sax.SAXSource;
28 import javax.xml.transform.stream.StreamSource;
29 
30 import org.apache.xml.dtm.DTM;
31 import org.apache.xml.dtm.DTMException;
32 import org.apache.xml.dtm.DTMFilter;
33 import org.apache.xml.dtm.DTMIterator;
34 import org.apache.xml.dtm.DTMManager;
35 import org.apache.xml.dtm.DTMWSFilter;
36 import org.apache.xml.dtm.ref.dom2dtm.DOM2DTM;
37 import org.apache.xml.dtm.ref.sax2dtm.SAX2DTM;
38 import org.apache.xml.dtm.ref.sax2dtm.SAX2RTFDTM;
39 import org.apache.xml.res.XMLErrorResources;
40 import org.apache.xml.res.XMLMessages;
41 import org.apache.xml.utils.PrefixResolver;
42 import org.apache.xml.utils.SystemIDResolver;
43 import org.apache.xml.utils.XMLReaderManager;
44 import org.apache.xml.utils.XMLStringFactory;
45 
46 import org.w3c.dom.Document;
47 import org.w3c.dom.Node;
48 
49 import org.xml.sax.InputSource;
50 import org.xml.sax.SAXException;
51 import org.xml.sax.SAXNotRecognizedException;
52 import org.xml.sax.SAXNotSupportedException;
53 import org.xml.sax.XMLReader;
54 import org.xml.sax.helpers.DefaultHandler;
55 
56 /**
57  * The default implementation for the DTMManager.
58  *
59  * %REVIEW% There is currently a reentrancy issue, since the finalizer
60  * for XRTreeFrag (which runs in the GC thread) wants to call
61  * DTMManager.release(), and may do so at the same time that the main
62  * transformation thread is accessing the manager. Our current solution is
63  * to make most of the manager's methods <code>synchronized</code>.
64  * Early tests suggest that doing so is not causing a significant
65  * performance hit in Xalan. However, it should be noted that there
66  * is a possible alternative solution: rewrite release() so it merely
67  * posts a request for release onto a threadsafe queue, and explicitly
68  * process that queue on an infrequent basis during main-thread
69  * activity (eg, when getDTM() is invoked). The downside of that solution
70  * would be a greater delay before the DTM's storage is actually released
71  * for reuse.
72  * */
73 public class DTMManagerDefault extends DTMManager
74 {
75   //static final boolean JKESS_XNI_EXPERIMENT=true;
76 
77   /** Set this to true if you want a dump of the DTM after creation. */
78   private static final boolean DUMPTREE = false;
79 
80   /** Set this to true if you want a basic diagnostics. */
81   private static final boolean DEBUG = false;
82 
83   /**
84    * Map from DTM identifier numbers to DTM objects that this manager manages.
85    * One DTM may have several prefix numbers, if extended node indexing
86    * is in use; in that case, m_dtm_offsets[] will used to control which
87    * prefix maps to which section of the DTM.
88    *
89    * This array grows as necessary; see addDTM().
90    *
91    * This array grows as necessary; see addDTM(). Growth is uncommon... but
92    * access needs to be blindingly fast since it's used in node addressing.
93    */
94   protected DTM m_dtms[] = new DTM[256];
95 
96   /** Map from DTM identifier numbers to offsets. For small DTMs with a
97    * single identifier, this will always be 0. In overflow addressing, where
98    * additional identifiers are allocated to access nodes beyond the range of
99    * a single Node Handle, this table is used to map the handle's node field
100    * into the actual node identifier.
101    *
102    * This array grows as necessary; see addDTM().
103    *
104    * This array grows as necessary; see addDTM(). Growth is uncommon... but
105    * access needs to be blindingly fast since it's used in node addressing.
106    * (And at the moment, that includes accessing it from DTMDefaultBase,
107    * which is why this is not Protected or Private.)
108    */
109   int m_dtm_offsets[] = new int[256];
110 
111   /**
112    * The cache for XMLReader objects to be used if the user did not
113    * supply an XMLReader for a SAXSource or supplied a StreamSource.
114    */
115   protected XMLReaderManager m_readerManager = null;
116 
117   /**
118    * The default implementation of ContentHandler, DTDHandler and ErrorHandler.
119    */
120   protected DefaultHandler m_defaultHandler = new DefaultHandler();
121 
122   /**
123    * Add a DTM to the DTM table. This convenience call adds it as the
124    * "base DTM ID", with offset 0. The other version of addDTM should
125    * be used if you want to add "extended" DTM IDs with nonzero offsets.
126    *
127    * @param dtm Should be a valid reference to a DTM.
128    * @param id Integer DTM ID to be bound to this DTM
129    */
addDTM(DTM dtm, int id)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    */
addDTM(DTM dtm, int id, int offset)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    */
getFirstFreeDTMID()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    */
DTMManagerDefault()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    */
getDTM(Source source, boolean unique, DTMWSFilter whiteSpaceFilter, boolean incremental, boolean doIndexing)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    */
getDTMHandleFromNode(org.w3c.dom.Node node)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    */
getXMLReader(Source inputSource)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    */
releaseXMLReader(XMLReader reader)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    */
getDTM(int nodeHandle)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    */
getDTMIdentity(DTM dtm)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    */
release(DTM dtm, boolean shouldHardDelete)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    */
createDocumentFragment()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    */
createDTMIterator(int whatToShow, DTMFilter filter, boolean entityReferenceExpansion)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    */
createDTMIterator(String xpathString, PrefixResolver presolver)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    */
createDTMIterator(int node)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    */
createDTMIterator(Object xpathCompiler, int pos)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    */
getExpandedNameTable(DTM dtm)855   public ExpandedNameTable getExpandedNameTable(DTM dtm)
856   {
857     return m_expandedNameTable;
858   }
859 }
860