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