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