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