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