001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the "License"); 007 * you may not use this file except in compliance with the License. 008 * You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 /* 019 * $Id: TransformerImpl.java 475979 2006-11-16 23:32:48Z minchau $ 020 */ 021 package org.apache.xalan.transformer; 022 023 import java.io.IOException; 024 import java.io.StringWriter; 025 import java.util.Enumeration; 026 import java.util.Properties; 027 import java.util.Stack; 028 import java.util.StringTokenizer; 029 import java.util.Vector; 030 031 import javax.xml.parsers.DocumentBuilder; 032 import javax.xml.parsers.DocumentBuilderFactory; 033 import javax.xml.parsers.ParserConfigurationException; 034 import javax.xml.transform.ErrorListener; 035 import javax.xml.transform.OutputKeys; 036 import javax.xml.transform.Result; 037 import javax.xml.transform.Source; 038 import javax.xml.transform.SourceLocator; 039 import javax.xml.transform.Transformer; 040 import javax.xml.transform.TransformerException; 041 import javax.xml.transform.URIResolver; 042 import javax.xml.transform.dom.DOMResult; 043 import javax.xml.transform.dom.DOMSource; 044 import javax.xml.transform.sax.SAXResult; 045 import javax.xml.transform.sax.SAXSource; 046 import javax.xml.transform.stream.StreamResult; 047 import javax.xml.transform.stream.StreamSource; 048 049 import org.apache.xalan.extensions.ExtensionsTable; 050 import org.apache.xalan.res.XSLMessages; 051 import org.apache.xalan.res.XSLTErrorResources; 052 import org.apache.xml.serializer.Method; 053 import org.apache.xml.serializer.Serializer; 054 import org.apache.xml.serializer.SerializerFactory; 055 import org.apache.xalan.templates.AVT; 056 import org.apache.xalan.templates.Constants; 057 import org.apache.xalan.templates.ElemAttributeSet; 058 import org.apache.xalan.templates.ElemForEach; 059 import org.apache.xalan.templates.ElemSort; 060 import org.apache.xalan.templates.ElemTemplate; 061 import org.apache.xalan.templates.ElemTemplateElement; 062 import org.apache.xalan.templates.ElemTextLiteral; 063 import org.apache.xalan.templates.ElemVariable; 064 import org.apache.xalan.templates.OutputProperties; 065 import org.apache.xalan.templates.Stylesheet; 066 import org.apache.xalan.templates.StylesheetComposed; 067 import org.apache.xalan.templates.StylesheetRoot; 068 import org.apache.xalan.templates.XUnresolvedVariable; 069 import org.apache.xalan.trace.GenerateEvent; 070 import org.apache.xalan.trace.TraceManager; 071 import org.apache.xml.dtm.DTM; 072 import org.apache.xml.dtm.DTMIterator; 073 import org.apache.xml.dtm.DTMManager; 074 import org.apache.xml.dtm.DTMWSFilter; 075 import org.apache.xml.serializer.ToSAXHandler; 076 import org.apache.xml.serializer.ToTextStream; 077 import org.apache.xml.serializer.ToXMLSAXHandler; 078 import org.apache.xml.serializer.SerializationHandler; 079 import org.apache.xml.utils.BoolStack; 080 import org.apache.xml.utils.DOMBuilder; 081 import org.apache.xml.utils.NodeVector; 082 import org.apache.xml.utils.ObjectPool; 083 import org.apache.xml.utils.ObjectStack; 084 import org.apache.xml.utils.QName; 085 import org.apache.xml.utils.SAXSourceLocator; 086 import org.apache.xml.utils.ThreadControllerWrapper; 087 import org.apache.xpath.Arg; 088 import org.apache.xpath.ExtensionsProvider; 089 import org.apache.xpath.VariableStack; 090 import org.apache.xpath.XPathContext; 091 import org.apache.xpath.functions.FuncExtFunction; 092 import org.apache.xpath.objects.XObject; 093 import org.xml.sax.Attributes; 094 import org.xml.sax.ContentHandler; 095 import org.xml.sax.SAXException; 096 import org.xml.sax.SAXNotRecognizedException; 097 import org.xml.sax.SAXNotSupportedException; 098 import org.xml.sax.ext.DeclHandler; 099 import org.xml.sax.ext.LexicalHandler; 100 101 /** 102 * This class implements the 103 * {@link javax.xml.transform.Transformer} interface, and is the core 104 * representation of the transformation execution.</p> 105 * @xsl.usage advanced 106 */ 107 public class TransformerImpl extends Transformer 108 implements Runnable, DTMWSFilter, ExtensionsProvider, org.apache.xml.serializer.SerializerTrace 109 { 110 111 // Synch object to gaurd against setting values from the TrAX interface 112 // or reentry while the transform is going on. 113 114 /** NEEDSDOC Field m_reentryGuard */ 115 private Boolean m_reentryGuard = new Boolean(true); 116 117 /** 118 * This is null unless we own the stream. 119 */ 120 private java.io.FileOutputStream m_outputStream = null; 121 122 /** 123 * True if the parser events should be on the main thread, 124 * false if not. Experemental. Can not be set right now. 125 */ 126 private boolean m_parserEventsOnMain = true; 127 128 /** The thread that the transformer is running on. */ 129 private Thread m_transformThread; 130 131 /** The base URL of the source tree. */ 132 private String m_urlOfSource = null; 133 134 /** The Result object at the start of the transform, if any. */ 135 private Result m_outputTarget = null; 136 137 /** 138 * The output format object set by the user. May be null. 139 */ 140 private OutputProperties m_outputFormat; 141 142 143 /** 144 * The content handler for the source input tree. 145 */ 146 ContentHandler m_inputContentHandler; 147 148 /** 149 * The content handler for the result tree. 150 */ 151 private ContentHandler m_outputContentHandler = null; 152 153 // /* 154 // * Use member variable to store param variables as they're 155 // * being created, use member variable so we don't 156 // * have to create a new vector every time. 157 // */ 158 // private Vector m_newVars = new Vector(); 159 160 /** The JAXP Document Builder, mainly to create Result Tree Fragments. */ 161 DocumentBuilder m_docBuilder = null; 162 163 /** 164 * A pool of ResultTreeHandlers, for serialization of a subtree to text. 165 * Please note that each of these also holds onto a Text Serializer. 166 */ 167 private ObjectPool m_textResultHandlerObjectPool = 168 new ObjectPool(ToTextStream.class); 169 170 /** 171 * Related to m_textResultHandlerObjectPool, this is a pool of 172 * StringWriters, which are passed to the Text Serializers. 173 * (I'm not sure if this is really needed any more. -sb) 174 */ 175 private ObjectPool m_stringWriterObjectPool = 176 new ObjectPool(StringWriter.class); 177 178 /** 179 * A static text format object, which can be used over and 180 * over to create the text serializers. 181 */ 182 private OutputProperties m_textformat = new OutputProperties(Method.TEXT); 183 184 // Commenteded out in response to problem reported by 185 // Nicola Brown <Nicola.Brown@jacobsrimell.com> 186 // /** 187 // * Flag to let us know if an exception should be reported inside the 188 // * postExceptionFromThread method. This is needed if the transform is 189 // * being generated from SAX events, and thus there is no central place 190 // * to report the exception from. (An exception is usually picked up in 191 // * the main thread from the transform thread in {@link #transform(Source source)} 192 // * from {@link #getExceptionThrown()}. ) 193 // */ 194 // private boolean m_reportInPostExceptionFromThread = false; 195 196 /** 197 * A node vector used as a stack to track the current 198 * ElemTemplateElement. Needed for the 199 * org.apache.xalan.transformer.TransformState interface, 200 * so a tool can discover the calling template. Note the use of an array 201 * for this limits the recursion depth to 4K. 202 */ 203 ObjectStack m_currentTemplateElements 204 = new ObjectStack(XPathContext.RECURSIONLIMIT); 205 206 /** The top of the currentTemplateElements stack. */ 207 //int m_currentTemplateElementsTop = 0; 208 209 /** 210 * A node vector used as a stack to track the current 211 * ElemTemplate that was matched. 212 * Needed for the 213 * org.apache.xalan.transformer.TransformState interface, 214 * so a tool can discover the matched template 215 */ 216 Stack m_currentMatchTemplates = new Stack(); 217 218 /** 219 * A node vector used as a stack to track the current 220 * node that was matched. 221 * Needed for the 222 * org.apache.xalan.transformer.TransformState interface, 223 * so a tool can discover the matched 224 * node. 225 */ 226 NodeVector m_currentMatchedNodes = new NodeVector(); 227 228 /** 229 * The root of a linked set of stylesheets. 230 */ 231 private StylesheetRoot m_stylesheetRoot = null; 232 233 /** 234 * If this is set to true, do not warn about pattern 235 * match conflicts. 236 */ 237 private boolean m_quietConflictWarnings = true; 238 239 /** 240 * The liason to the XML parser, so the XSL processor 241 * can handle included files, and the like, and do the 242 * initial parse of the XSL document. 243 */ 244 private XPathContext m_xcontext; 245 246 /** 247 * Object to guard agains infinite recursion when 248 * doing queries. 249 */ 250 private StackGuard m_stackGuard; 251 252 /** 253 * Output handler to bottleneck SAX events. 254 */ 255 private SerializationHandler m_serializationHandler; 256 257 /** The key manager, which manages xsl:keys. */ 258 private KeyManager m_keyManager = new KeyManager(); 259 260 /** 261 * Stack for the purposes of flagging infinite recursion with 262 * attribute sets. 263 */ 264 Stack m_attrSetStack = null; 265 266 /** 267 * The table of counters for xsl:number support. 268 * @see ElemNumber 269 */ 270 CountersTable m_countersTable = null; 271 272 /** 273 * Is > 0 when we're processing a for-each. 274 */ 275 BoolStack m_currentTemplateRuleIsNull = new BoolStack(); 276 277 /** 278 * Keeps track of the result delivered by any EXSLT <code>func:result</code> 279 * instruction that has been executed for the currently active EXSLT 280 * <code>func:function</code> 281 */ 282 ObjectStack m_currentFuncResult = new ObjectStack(); 283 284 /** 285 * The message manager, which manages error messages, warning 286 * messages, and other types of message events. 287 */ 288 private MsgMgr m_msgMgr; 289 290 /** 291 * The flag for the setting of the optimize feature; 292 * This flag should have the same value as the FEATURE_OPTIMIZE feature 293 * which is set by the TransformerFactory.setAttribut() method before a 294 * Transformer is created 295 */ 296 private boolean m_optimizer = true; 297 298 /** 299 * The flag for the setting of the incremental feature; 300 * This flag should have the same value as the FEATURE_INCREMENTAL feature 301 * which is set by the TransformerFactory.setAttribut() method before a 302 * Transformer is created 303 */ 304 private boolean m_incremental = false; 305 306 /** 307 * The flag for the setting of the source_location feature; 308 * This flag should have the same value as the FEATURE_SOURCE_LOCATION feature 309 * which is set by the TransformerFactory.setAttribut() method before a 310 * Transformer is created 311 */ 312 private boolean m_source_location = false; 313 314 /** 315 * This is a compile-time flag to turn off calling 316 * of trace listeners. Set this to false for optimization purposes. 317 */ 318 private boolean m_debug = false; 319 320 /** 321 * The SAX error handler, where errors and warnings are sent. 322 */ 323 private ErrorListener m_errorHandler = 324 new org.apache.xml.utils.DefaultErrorHandler(false); 325 326 /** 327 * The trace manager. 328 */ 329 private TraceManager m_traceManager = new TraceManager(this); 330 331 /** 332 * If the transform thread throws an exception, the exception needs to 333 * be stashed away so that the main thread can pass it on to the 334 * client. 335 */ 336 private Exception m_exceptionThrown = null; 337 338 /** 339 * The InputSource for the source tree, which is needed if the 340 * parse thread is not the main thread, in order for the parse 341 * thread's run method to get to the input source. 342 * (Delete this if reversing threads is outlawed. -sb) 343 */ 344 private Source m_xmlSource; 345 346 /** 347 * This is needed for support of setSourceTreeDocForThread(Node doc), 348 * which must be called in order for the transform thread's run 349 * method to obtain the root of the source tree to be transformed. 350 */ 351 private int m_doc; 352 353 /** 354 * If the the transform is on the secondary thread, we 355 * need to know when it is done, so we can return. 356 */ 357 private boolean m_isTransformDone = false; 358 359 /** Flag to to tell if the tranformer needs to be reset. */ 360 private boolean m_hasBeenReset = false; 361 362 /** NEEDSDOC Field m_shouldReset */ 363 private boolean m_shouldReset = true; 364 365 /** 366 * NEEDSDOC Method setShouldReset 367 * 368 * 369 * NEEDSDOC @param shouldReset 370 */ 371 public void setShouldReset(boolean shouldReset) 372 { 373 m_shouldReset = shouldReset; 374 } 375 376 /** 377 * A stack of current template modes. 378 */ 379 private Stack m_modes = new Stack(); 380 381 //========================================================== 382 // SECTION: Constructor 383 //========================================================== 384 385 /** 386 * Construct a TransformerImpl. 387 * 388 * @param stylesheet The root of the stylesheet tree. 389 */ 390 public TransformerImpl(StylesheetRoot stylesheet) 391 // throws javax.xml.transform.TransformerException 392 { 393 m_optimizer = stylesheet.getOptimizer(); 394 m_incremental = stylesheet.getIncremental(); 395 m_source_location = stylesheet.getSource_location(); 396 setStylesheet(stylesheet); 397 XPathContext xPath = new XPathContext(this); 398 xPath.setIncremental(m_incremental); 399 xPath.getDTMManager().setIncremental(m_incremental); 400 xPath.setSource_location(m_source_location); 401 xPath.getDTMManager().setSource_location(m_source_location); 402 403 if (stylesheet.isSecureProcessing()) 404 xPath.setSecureProcessing(true); 405 406 setXPathContext(xPath); 407 getXPathContext().setNamespaceContext(stylesheet); 408 m_stackGuard = new StackGuard(this); 409 } 410 411 // ================ ExtensionsTable =================== 412 413 /** 414 * The table of ExtensionHandlers. 415 */ 416 private ExtensionsTable m_extensionsTable = null; 417 418 /** 419 * Get the extensions table object. 420 * 421 * @return The extensions table. 422 */ 423 public ExtensionsTable getExtensionsTable() 424 { 425 return m_extensionsTable; 426 } 427 428 /** 429 * If the stylesheet contains extensions, set the extensions table object. 430 * 431 * 432 * @param sroot The stylesheet. 433 * @throws javax.xml.transform.TransformerException 434 */ 435 void setExtensionsTable(StylesheetRoot sroot) 436 throws javax.xml.transform.TransformerException 437 { 438 try 439 { 440 if (sroot.getExtensions() != null) 441 m_extensionsTable = new ExtensionsTable(sroot); 442 } 443 catch (javax.xml.transform.TransformerException te) 444 {te.printStackTrace();} 445 } 446 447 //== Implementation of the XPath ExtensionsProvider interface. 448 449 public boolean functionAvailable(String ns, String funcName) 450 throws javax.xml.transform.TransformerException 451 { 452 return getExtensionsTable().functionAvailable(ns, funcName); 453 } 454 455 public boolean elementAvailable(String ns, String elemName) 456 throws javax.xml.transform.TransformerException 457 { 458 return getExtensionsTable().elementAvailable(ns, elemName); 459 } 460 461 public Object extFunction(String ns, String funcName, 462 Vector argVec, Object methodKey) 463 throws javax.xml.transform.TransformerException 464 {//System.out.println("TransImpl.extFunction() " + ns + " " + funcName +" " + getExtensionsTable()); 465 return getExtensionsTable().extFunction(ns, funcName, 466 argVec, methodKey, 467 getXPathContext().getExpressionContext()); 468 } 469 470 public Object extFunction(FuncExtFunction extFunction, Vector argVec) 471 throws javax.xml.transform.TransformerException 472 { 473 return getExtensionsTable().extFunction(extFunction, argVec, 474 getXPathContext().getExpressionContext()); 475 } 476 477 //========================= 478 479 /** 480 * Reset the state. This needs to be called after a process() call 481 * is invoked, if the processor is to be used again. 482 */ 483 public void reset() 484 { 485 486 if (!m_hasBeenReset && m_shouldReset) 487 { 488 m_hasBeenReset = true; 489 490 if (this.m_outputStream != null) 491 { 492 try 493 { 494 m_outputStream.close(); 495 } 496 catch (java.io.IOException ioe){} 497 } 498 499 m_outputStream = null; 500 501 // I need to look more carefully at which of these really 502 // needs to be reset. 503 m_countersTable = null; 504 505 m_xcontext.reset(); 506 507 m_xcontext.getVarStack().reset(); 508 resetUserParameters(); 509 510 511 m_currentTemplateElements.removeAllElements(); 512 m_currentMatchTemplates.removeAllElements(); 513 m_currentMatchedNodes.removeAllElements(); 514 515 m_serializationHandler = null; 516 m_outputTarget = null; 517 m_keyManager = new KeyManager(); 518 m_attrSetStack = null; 519 m_countersTable = null; 520 m_currentTemplateRuleIsNull = new BoolStack(); 521 m_xmlSource = null; 522 m_doc = DTM.NULL; 523 m_isTransformDone = false; 524 m_transformThread = null; 525 526 // m_inputContentHandler = null; 527 // For now, reset the document cache each time. 528 m_xcontext.getSourceTreeManager().reset(); 529 } 530 531 // m_reportInPostExceptionFromThread = false; 532 } 533 534 /** 535 * <code>getProperty</code> returns the current setting of the 536 * property described by the <code>property</code> argument. 537 * 538 * %REVIEW% Obsolete now that source_location is handled in the TransformerFactory? 539 * 540 * @param property a <code>String</code> value 541 * @return a <code>boolean</code> value 542 */ 543 public boolean getProperty(String property) 544 { 545 return false; 546 } 547 548 /** 549 * Set a runtime property for this <code>TransformerImpl</code>. 550 * 551 * %REVIEW% Obsolete now that source_location is handled in the TransformerFactory? 552 * 553 * @param property a <code>String</code> value 554 * @param value an <code>Object</code> value 555 */ 556 public void setProperty(String property, Object value) 557 { 558 } 559 560 // ========= Transformer Interface Implementation ========== 561 562 /** 563 * Get true if the parser events should be on the main thread, 564 * false if not. Experimental. Can not be set right now. 565 * 566 * @return true if the parser events should be on the main thread, 567 * false if not. 568 * @xsl.usage experimental 569 */ 570 public boolean isParserEventsOnMain() 571 { 572 return m_parserEventsOnMain; 573 } 574 575 /** 576 * Get the thread that the transform process is on. 577 * 578 * @return The thread that the transform process is on, or null. 579 * @xsl.usage internal 580 */ 581 public Thread getTransformThread() 582 { 583 return m_transformThread; 584 } 585 586 /** 587 * Get the thread that the transform process is on. 588 * 589 * @param t The transform thread, may be null. 590 * @xsl.usage internal 591 */ 592 public void setTransformThread(Thread t) 593 { 594 m_transformThread = t; 595 } 596 597 /** NEEDSDOC Field m_hasTransformThreadErrorCatcher */ 598 private boolean m_hasTransformThreadErrorCatcher = false; 599 600 /** 601 * Return true if the transform was initiated from the transform method, 602 * otherwise it was probably done from a pure parse events. 603 * 604 * NEEDSDOC ($objectName$) @return 605 */ 606 public boolean hasTransformThreadErrorCatcher() 607 { 608 return m_hasTransformThreadErrorCatcher; 609 } 610 611 /** 612 * Process the source tree to SAX parse events. 613 * @param source The input for the source tree. 614 * 615 * @throws TransformerException 616 */ 617 public void transform(Source source) throws TransformerException 618 { 619 transform(source, true); 620 } 621 622 /** 623 * Process the source tree to SAX parse events. 624 * @param source The input for the source tree. 625 * @param shouldRelease Flag indicating whether to release DTMManager. 626 * 627 * @throws TransformerException 628 */ 629 public void transform(Source source, boolean shouldRelease) throws TransformerException 630 { 631 632 try 633 { 634 635 // Patch for bugzilla #13863. If we don't reset the namespaceContext 636 // then we will get a NullPointerException if transformer is reused 637 // (for stylesheets that use xsl:key). Not sure if this should go 638 // here or in reset(). -is 639 if(getXPathContext().getNamespaceContext() == null){ 640 getXPathContext().setNamespaceContext(getStylesheet()); 641 } 642 String base = source.getSystemId(); 643 644 // If no systemID of the source, use the base of the stylesheet. 645 if(null == base) 646 { 647 base = m_stylesheetRoot.getBaseIdentifier(); 648 } 649 650 // As a last resort, use the current user dir. 651 if(null == base) 652 { 653 String currentDir = ""; 654 try { 655 currentDir = System.getProperty("user.dir"); 656 } 657 catch (SecurityException se) {}// user.dir not accessible from applet 658 659 if (currentDir.startsWith(java.io.File.separator)) 660 base = "file://" + currentDir; 661 else 662 base = "file:///" + currentDir; 663 664 base = base + java.io.File.separatorChar 665 + source.getClass().getName(); 666 } 667 setBaseURLOfSource(base); 668 DTMManager mgr = m_xcontext.getDTMManager(); 669 /* 670 * According to JAXP1.2, new SAXSource()/StreamSource() 671 * should create an empty input tree, with a default root node. 672 * new DOMSource()creates an empty document using DocumentBuilder. 673 * newDocument(); Use DocumentBuilder.newDocument() for all 3 situations, 674 * since there is no clear spec. how to create an empty tree when 675 * both SAXSource() and StreamSource() are used. 676 */ 677 if ((source instanceof StreamSource && source.getSystemId()==null && 678 ((StreamSource)source).getInputStream()==null && 679 ((StreamSource)source).getReader()==null)|| 680 (source instanceof SAXSource && 681 ((SAXSource)source).getInputSource()==null && 682 ((SAXSource)source).getXMLReader()==null )|| 683 (source instanceof DOMSource && ((DOMSource)source).getNode()==null)){ 684 try { 685 DocumentBuilderFactory builderF = 686 DocumentBuilderFactory.newInstance(); 687 DocumentBuilder builder = builderF.newDocumentBuilder(); 688 String systemID = source.getSystemId(); 689 source = new DOMSource(builder.newDocument()); 690 691 // Copy system ID from original, empty Source to new Source 692 if (systemID != null) { 693 source.setSystemId(systemID); 694 } 695 } catch (ParserConfigurationException e) { 696 fatalError(e); 697 } 698 } 699 DTM dtm = mgr.getDTM(source, false, this, true, true); 700 dtm.setDocumentBaseURI(base); 701 702 boolean hardDelete = true; // %REVIEW% I have to think about this. -sb 703 704 try 705 { 706 // NOTE: This will work because this is _NOT_ a shared DTM, and thus has 707 // only a single Document node. If it could ever be an RTF or other 708 // shared DTM, look at dtm.getDocumentRoot(nodeHandle). 709 this.transformNode(dtm.getDocument()); 710 } 711 finally 712 { 713 if (shouldRelease) 714 mgr.release(dtm, hardDelete); 715 } 716 717 // Kick off the parse. When the ContentHandler gets 718 // the startDocument event, it will call transformNode( node ). 719 // reader.parse( xmlSource ); 720 // This has to be done to catch exceptions thrown from 721 // the transform thread spawned by the STree handler. 722 Exception e = getExceptionThrown(); 723 724 if (null != e) 725 { 726 if (e instanceof javax.xml.transform.TransformerException) 727 { 728 throw (javax.xml.transform.TransformerException) e; 729 } 730 else if (e instanceof org.apache.xml.utils.WrappedRuntimeException) 731 { 732 fatalError( 733 ((org.apache.xml.utils.WrappedRuntimeException) e).getException()); 734 } 735 else 736 { 737 throw new javax.xml.transform.TransformerException(e); 738 } 739 } 740 else if (null != m_serializationHandler) 741 { 742 m_serializationHandler.endDocument(); 743 } 744 } 745 catch (org.apache.xml.utils.WrappedRuntimeException wre) 746 { 747 Throwable throwable = wre.getException(); 748 749 while (throwable 750 instanceof org.apache.xml.utils.WrappedRuntimeException) 751 { 752 throwable = 753 ((org.apache.xml.utils.WrappedRuntimeException) throwable).getException(); 754 } 755 756 fatalError(throwable); 757 } 758 759 // Patch attributed to David Eisenberg <david@catcode.com> 760 catch (org.xml.sax.SAXParseException spe) 761 { 762 fatalError(spe); 763 } 764 catch (org.xml.sax.SAXException se) 765 { 766 m_errorHandler.fatalError(new TransformerException(se)); 767 } 768 finally 769 { 770 m_hasTransformThreadErrorCatcher = false; 771 772 // This looks to be redundent to the one done in TransformNode. 773 reset(); 774 } 775 } 776 777 private void fatalError(Throwable throwable) throws TransformerException 778 { 779 if (throwable instanceof org.xml.sax.SAXParseException) 780 m_errorHandler.fatalError(new TransformerException(throwable.getMessage(),new SAXSourceLocator((org.xml.sax.SAXParseException)throwable))); 781 else 782 m_errorHandler.fatalError(new TransformerException(throwable)); 783 784 } 785 786 /** 787 * Get the base URL of the source. 788 * 789 * @return The base URL of the source tree, or null. 790 */ 791 public String getBaseURLOfSource() 792 { 793 return m_urlOfSource; 794 } 795 796 /** 797 * Get the base URL of the source. 798 * 799 * 800 * NEEDSDOC @param base 801 * @return The base URL of the source tree, or null. 802 */ 803 public void setBaseURLOfSource(String base) 804 { 805 m_urlOfSource = base; 806 } 807 808 /** 809 * Get the original output target. 810 * 811 * @return The Result object used to kick of the transform or null. 812 */ 813 public Result getOutputTarget() 814 { 815 return m_outputTarget; 816 } 817 818 /** 819 * Set the original output target. This is useful when using a SAX transform and 820 * supplying a ContentHandler or when the URI of the output target should 821 * not be the same as the systemID of the original output target. 822 * 823 * 824 * NEEDSDOC @param outputTarget 825 */ 826 public void setOutputTarget(Result outputTarget) 827 { 828 m_outputTarget = outputTarget; 829 } 830 831 /** 832 * Get an output property that is in effect for the 833 * transformation. The property specified may be a property 834 * that was set with setOutputProperty, or it may be a 835 * property specified in the stylesheet. 836 * 837 * NEEDSDOC @param qnameString 838 * 839 * @return The string value of the output property, or null 840 * if no property was found. 841 * 842 * @throws IllegalArgumentException If the property is not supported. 843 * 844 * @see javax.xml.transform.OutputKeys 845 */ 846 public String getOutputProperty(String qnameString) 847 throws IllegalArgumentException 848 { 849 850 String value = null; 851 OutputProperties props = getOutputFormat(); 852 853 value = props.getProperty(qnameString); 854 855 if (null == value) 856 { 857 if (!OutputProperties.isLegalPropertyKey(qnameString)) 858 throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_OUTPUT_PROPERTY_NOT_RECOGNIZED, new Object[]{qnameString})); //"output property not recognized: " 859 //+ qnameString); 860 } 861 862 return value; 863 } 864 865 /** 866 * Get the value of a property, without using the default properties. This 867 * can be used to test if a property has been explicitly set by the stylesheet 868 * or user. 869 * 870 * NEEDSDOC @param qnameString 871 * 872 * @return The value of the property, or null if not found. 873 * 874 * @throws IllegalArgumentException If the property is not supported, 875 * and is not namespaced. 876 */ 877 public String getOutputPropertyNoDefault(String qnameString) 878 throws IllegalArgumentException 879 { 880 881 String value = null; 882 OutputProperties props = getOutputFormat(); 883 884 value = (String) props.getProperties().get(qnameString); 885 886 if (null == value) 887 { 888 if (!OutputProperties.isLegalPropertyKey(qnameString)) 889 throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_OUTPUT_PROPERTY_NOT_RECOGNIZED, new Object[]{qnameString})); //"output property not recognized: " 890 // + qnameString); 891 } 892 893 return value; 894 } 895 896 /** 897 * This method is used to set or override the value 898 * of the effective xsl:output attribute values 899 * specified in the stylesheet. 900 * <p> 901 * The recognized standard output properties are: 902 * <ul> 903 * <li>cdata-section-elements 904 * <li>doctype-system 905 * <li>doctype-public 906 * <li>indent 907 * <li>media-type 908 * <li>method 909 * <li>omit-xml-declaration 910 * <li>standalone 911 * <li>version 912 * </ul> 913 * <p> 914 * For example: 915 * <pre> 916 * tran.setOutputProperty("standalone", "yes"); 917 * </pre> 918 * <p> 919 * In the case of the cdata-section-elements property, 920 * the value should be a whitespace separated list of 921 * element names. The element name is the local name 922 * of the element, if it is in no namespace, or, the URI 923 * in braces followed immediately by the local name 924 * if the element is in that namespace. For example: 925 * <pre> 926 * tran.setOutputProperty( 927 * "cdata-section-elements", 928 * "elem1 {http://example.uri}elem2 elem3"); 929 * </pre> 930 * <p> 931 * The recognized Xalan extension elements are: 932 * <ul> 933 * <li>content-handler 934 * <li>entities 935 * <li>indent-amount 936 * <li>line-separator 937 * <li>omit-meta-tag 938 * <li>use-url-escaping 939 * </ul> 940 * <p> 941 * These must be in the extension namespace of 942 * "http://xml.apache.org/xalan". This is accomplished 943 * by putting the namespace URI in braces before the 944 * property name, for example: 945 * <pre> 946 * tran.setOutputProperty( 947 * "{http://xml.apache.org/xalan}line-separator" , 948 * "\n"); 949 * </pre> 950 * 951 * @param name The property name. 952 * @param value The requested value for the property. 953 * @throws IllegalArgumentException if the property name is not legal. 954 */ 955 public void setOutputProperty(String name, String value) 956 throws IllegalArgumentException 957 { 958 959 synchronized (m_reentryGuard) 960 { 961 962 // Get the output format that was set by the user, otherwise get the 963 // output format from the stylesheet. 964 if (null == m_outputFormat) 965 { 966 m_outputFormat = 967 (OutputProperties) getStylesheet().getOutputComposed().clone(); 968 } 969 970 if (!OutputProperties.isLegalPropertyKey(name)) 971 throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_OUTPUT_PROPERTY_NOT_RECOGNIZED, new Object[]{name})); //"output property not recognized: " 972 //+ name); 973 974 m_outputFormat.setProperty(name, value); 975 } 976 } 977 978 /** 979 * Set the output properties for the transformation. These 980 * properties will override properties set in the templates 981 * with xsl:output. 982 * 983 * <p>If argument to this function is null, any properties 984 * previously set will be removed.</p> 985 * 986 * @param oformat A set of output properties that will be 987 * used to override any of the same properties in effect 988 * for the transformation. 989 * 990 * @see javax.xml.transform.OutputKeys 991 * @see java.util.Properties 992 * 993 * @throws IllegalArgumentException if any of the argument keys are not 994 * recognized and are not namespace qualified. 995 */ 996 public void setOutputProperties(Properties oformat) 997 throws IllegalArgumentException 998 { 999 1000 synchronized (m_reentryGuard) 1001 { 1002 if (null != oformat) 1003 { 1004 1005 // See if an *explicit* method was set. 1006 String method = (String) oformat.get(OutputKeys.METHOD); 1007 1008 if (null != method) 1009 m_outputFormat = new OutputProperties(method); 1010 else if(m_outputFormat==null) 1011 m_outputFormat = new OutputProperties(); 1012 1013 m_outputFormat.copyFrom(oformat); 1014 // copyFrom does not set properties that have been already set, so 1015 // this must be called after, which is a bit in the reverse from 1016 // what one might think. 1017 m_outputFormat.copyFrom(m_stylesheetRoot.getOutputProperties()); 1018 } 1019 else { 1020 // if oformat is null JAXP says that any props previously set are removed 1021 // and we are to revert back to those in the templates object (i.e. Stylesheet). 1022 m_outputFormat = null; 1023 } 1024 } 1025 } 1026 1027 /** 1028 * Get a copy of the output properties for the transformation. These 1029 * properties will override properties set in the templates 1030 * with xsl:output. 1031 * 1032 * <p>Note that mutation of the Properties object returned will not 1033 * effect the properties that the transformation contains.</p> 1034 * 1035 * @return A copy of the set of output properties in effect 1036 * for the next transformation. 1037 * 1038 * NEEDSDOC ($objectName$) @return 1039 */ 1040 public Properties getOutputProperties() 1041 { 1042 return (Properties) getOutputFormat().getProperties().clone(); 1043 } 1044 1045 /** 1046 * Create a result ContentHandler from a Result object, based 1047 * on the current OutputProperties. 1048 * 1049 * @param outputTarget Where the transform result should go, 1050 * should not be null. 1051 * 1052 * @return A valid ContentHandler that will create the 1053 * result tree when it is fed SAX events. 1054 * 1055 * @throws TransformerException 1056 */ 1057 public SerializationHandler createSerializationHandler(Result outputTarget) 1058 throws TransformerException 1059 { 1060 SerializationHandler xoh = 1061 createSerializationHandler(outputTarget, getOutputFormat()); 1062 return xoh; 1063 } 1064 1065 /** 1066 * Create a ContentHandler from a Result object and an OutputProperties. 1067 * 1068 * @param outputTarget Where the transform result should go, 1069 * should not be null. 1070 * @param format The OutputProperties object that will contain 1071 * instructions on how to serialize the output. 1072 * 1073 * @return A valid ContentHandler that will create the 1074 * result tree when it is fed SAX events. 1075 * 1076 * @throws TransformerException 1077 */ 1078 public SerializationHandler createSerializationHandler( 1079 Result outputTarget, OutputProperties format) 1080 throws TransformerException 1081 { 1082 1083 SerializationHandler xoh; 1084 1085 // If the Result object contains a Node, then create 1086 // a ContentHandler that will add nodes to the input node. 1087 org.w3c.dom.Node outputNode = null; 1088 1089 if (outputTarget instanceof DOMResult) 1090 { 1091 outputNode = ((DOMResult) outputTarget).getNode(); 1092 org.w3c.dom.Node nextSibling = ((DOMResult)outputTarget).getNextSibling(); 1093 1094 org.w3c.dom.Document doc; 1095 short type; 1096 1097 if (null != outputNode) 1098 { 1099 type = outputNode.getNodeType(); 1100 doc = (org.w3c.dom.Node.DOCUMENT_NODE == type) 1101 ? (org.w3c.dom.Document) outputNode 1102 : outputNode.getOwnerDocument(); 1103 } 1104 else 1105 { 1106 boolean isSecureProcessing = m_stylesheetRoot.isSecureProcessing(); 1107 doc = org.apache.xml.utils.DOMHelper.createDocument(isSecureProcessing); 1108 outputNode = doc; 1109 type = outputNode.getNodeType(); 1110 1111 ((DOMResult) outputTarget).setNode(outputNode); 1112 } 1113 1114 DOMBuilder handler = 1115 (org.w3c.dom.Node.DOCUMENT_FRAGMENT_NODE == type) 1116 ? new DOMBuilder(doc, (org.w3c.dom.DocumentFragment) outputNode) 1117 : new DOMBuilder(doc, outputNode); 1118 1119 if (nextSibling != null) 1120 handler.setNextSibling(nextSibling); 1121 1122 String encoding = format.getProperty(OutputKeys.ENCODING); 1123 xoh = new ToXMLSAXHandler(handler, (LexicalHandler)handler, encoding); 1124 } 1125 else if (outputTarget instanceof SAXResult) 1126 { 1127 ContentHandler handler = ((SAXResult) outputTarget).getHandler(); 1128 1129 if (null == handler) 1130 throw new IllegalArgumentException( 1131 "handler can not be null for a SAXResult"); 1132 1133 LexicalHandler lexHandler; 1134 if (handler instanceof LexicalHandler) 1135 lexHandler = (LexicalHandler) handler; 1136 else 1137 lexHandler = null; 1138 1139 String encoding = format.getProperty(OutputKeys.ENCODING); 1140 String method = format.getProperty(OutputKeys.METHOD); 1141 1142 ToXMLSAXHandler toXMLSAXHandler = new ToXMLSAXHandler(handler, lexHandler, encoding); 1143 toXMLSAXHandler.setShouldOutputNSAttr(false); 1144 xoh = toXMLSAXHandler; 1145 1146 1147 String publicID = format.getProperty(OutputKeys.DOCTYPE_PUBLIC); 1148 String systemID = format.getProperty(OutputKeys.DOCTYPE_SYSTEM); 1149 if (systemID != null) 1150 xoh.setDoctypeSystem(systemID); 1151 if (publicID != null) 1152 xoh.setDoctypePublic(publicID); 1153 1154 if (handler instanceof TransformerClient) { 1155 XalanTransformState state = new XalanTransformState(); 1156 ((TransformerClient)handler).setTransformState(state); 1157 ((ToSAXHandler)xoh).setTransformState(state); 1158 } 1159 1160 1161 } 1162 1163 // Otherwise, create a ContentHandler that will serialize the 1164 // result tree to either a stream or a writer. 1165 else if (outputTarget instanceof StreamResult) 1166 { 1167 StreamResult sresult = (StreamResult) outputTarget; 1168 1169 try 1170 { 1171 SerializationHandler serializer = 1172 (SerializationHandler) SerializerFactory.getSerializer(format.getProperties()); 1173 1174 if (null != sresult.getWriter()) 1175 serializer.setWriter(sresult.getWriter()); 1176 else if (null != sresult.getOutputStream()) 1177 serializer.setOutputStream(sresult.getOutputStream()); 1178 else if (null != sresult.getSystemId()) 1179 { 1180 String fileURL = sresult.getSystemId(); 1181 1182 if (fileURL.startsWith("file:///")) 1183 { 1184 if (fileURL.substring(8).indexOf(":") >0) 1185 fileURL = fileURL.substring(8); 1186 else 1187 fileURL = fileURL.substring(7); 1188 } 1189 else if (fileURL.startsWith("file:/")) 1190 { 1191 if (fileURL.substring(6).indexOf(":") >0) 1192 fileURL = fileURL.substring(6); 1193 else 1194 fileURL = fileURL.substring(5); 1195 } 1196 1197 m_outputStream = new java.io.FileOutputStream(fileURL); 1198 1199 serializer.setOutputStream(m_outputStream); 1200 1201 xoh = serializer; 1202 } 1203 else 1204 throw new TransformerException(XSLMessages.createMessage(XSLTErrorResources.ER_NO_OUTPUT_SPECIFIED, null)); //"No output specified!"); 1205 1206 // handler = serializer.asContentHandler(); 1207 1208 // this.setSerializer(serializer); 1209 1210 xoh = serializer; 1211 } 1212 // catch (UnsupportedEncodingException uee) 1213 // { 1214 // throw new TransformerException(uee); 1215 // } 1216 catch (IOException ioe) 1217 { 1218 throw new TransformerException(ioe); 1219 } 1220 } 1221 else 1222 { 1223 throw new TransformerException(XSLMessages.createMessage(XSLTErrorResources.ER_CANNOT_TRANSFORM_TO_RESULT_TYPE, new Object[]{outputTarget.getClass().getName()})); //"Can't transform to a Result of type " 1224 //+ outputTarget.getClass().getName() 1225 //+ "!"); 1226 } 1227 1228 // before we forget, lets make the created handler hold a reference 1229 // to the current TransformImpl object 1230 xoh.setTransformer(this); 1231 1232 SourceLocator srcLocator = getStylesheet(); 1233 xoh.setSourceLocator(srcLocator); 1234 1235 1236 return xoh; 1237 1238 1239 } 1240 1241 /** 1242 * Process the source tree to the output result. 1243 * @param xmlSource The input for the source tree. 1244 * @param outputTarget The output source target. 1245 * 1246 * @throws TransformerException 1247 */ 1248 public void transform(Source xmlSource, Result outputTarget) 1249 throws TransformerException 1250 { 1251 transform(xmlSource, outputTarget, true); 1252 } 1253 1254 /** 1255 * Process the source tree to the output result. 1256 * @param xmlSource The input for the source tree. 1257 * @param outputTarget The output source target. 1258 * @param shouldRelease Flag indicating whether to release DTMManager. 1259 * 1260 * @throws TransformerException 1261 */ 1262 public void transform(Source xmlSource, Result outputTarget, boolean shouldRelease) 1263 throws TransformerException 1264 { 1265 1266 synchronized (m_reentryGuard) 1267 { 1268 SerializationHandler xoh = createSerializationHandler(outputTarget); 1269 this.setSerializationHandler(xoh); 1270 1271 m_outputTarget = outputTarget; 1272 1273 transform(xmlSource, shouldRelease); 1274 } 1275 } 1276 1277 /** 1278 * Process the source node to the output result, if the 1279 * processor supports the "http://xml.org/trax/features/dom/input" 1280 * feature. 1281 * %REVIEW% Do we need a Node version of this? 1282 * @param node The input source node, which can be any valid DTM node. 1283 * @param outputTarget The output source target. 1284 * 1285 * @throws TransformerException 1286 */ 1287 public void transformNode(int node, Result outputTarget) 1288 throws TransformerException 1289 { 1290 1291 1292 SerializationHandler xoh = createSerializationHandler(outputTarget); 1293 this.setSerializationHandler(xoh); 1294 1295 m_outputTarget = outputTarget; 1296 1297 transformNode(node); 1298 } 1299 1300 /** 1301 * Process the source node to the output result, if the 1302 * processor supports the "http://xml.org/trax/features/dom/input" 1303 * feature. 1304 * %REVIEW% Do we need a Node version of this? 1305 * @param node The input source node, which can be any valid DTM node. 1306 * 1307 * @throws TransformerException 1308 */ 1309 public void transformNode(int node) throws TransformerException 1310 { 1311 //dml 1312 setExtensionsTable(getStylesheet()); 1313 // Make sure we're not writing to the same output content handler. 1314 synchronized (m_serializationHandler) 1315 { 1316 m_hasBeenReset = false; 1317 1318 XPathContext xctxt = getXPathContext(); 1319 DTM dtm = xctxt.getDTM(node); 1320 1321 try 1322 { 1323 pushGlobalVars(node); 1324 1325 // ========== 1326 // Give the top-level templates a chance to pass information into 1327 // the context (this is mainly for setting up tables for extensions). 1328 StylesheetRoot stylesheet = this.getStylesheet(); 1329 int n = stylesheet.getGlobalImportCount(); 1330 1331 for (int i = 0; i < n; i++) 1332 { 1333 StylesheetComposed imported = stylesheet.getGlobalImport(i); 1334 int includedCount = imported.getIncludeCountComposed(); 1335 1336 for (int j = -1; j < includedCount; j++) 1337 { 1338 Stylesheet included = imported.getIncludeComposed(j); 1339 1340 included.runtimeInit(this); 1341 1342 for (ElemTemplateElement child = included.getFirstChildElem(); 1343 child != null; child = child.getNextSiblingElem()) 1344 { 1345 child.runtimeInit(this); 1346 } 1347 } 1348 } 1349 // =========== 1350 // System.out.println("Calling applyTemplateToNode - "+Thread.currentThread().getName()); 1351 DTMIterator dtmIter = new org.apache.xpath.axes.SelfIteratorNoPredicate(); 1352 dtmIter.setRoot(node, xctxt); 1353 xctxt.pushContextNodeList(dtmIter); 1354 try 1355 { 1356 this.applyTemplateToNode(null, null, node); 1357 } 1358 finally 1359 { 1360 xctxt.popContextNodeList(); 1361 } 1362 // m_stylesheetRoot.getStartRule().execute(this); 1363 1364 // System.out.println("Done with applyTemplateToNode - "+Thread.currentThread().getName()); 1365 if (null != m_serializationHandler) 1366 { 1367 m_serializationHandler.endDocument(); 1368 } 1369 } 1370 catch (Exception se) 1371 { 1372 1373 // System.out.println(Thread.currentThread().getName()+" threw an exception! " 1374 // +se.getMessage()); 1375 // If an exception was thrown, we need to make sure that any waiting 1376 // handlers can terminate, which I guess is best done by sending 1377 // an endDocument. 1378 1379 // SAXSourceLocator 1380 while(se instanceof org.apache.xml.utils.WrappedRuntimeException) 1381 { 1382 Exception e = ((org.apache.xml.utils.WrappedRuntimeException)se).getException(); 1383 if(null != e) 1384 se = e; 1385 } 1386 1387 if (null != m_serializationHandler) 1388 { 1389 try 1390 { 1391 if(se instanceof org.xml.sax.SAXParseException) 1392 m_serializationHandler.fatalError((org.xml.sax.SAXParseException)se); 1393 else if(se instanceof TransformerException) 1394 { 1395 TransformerException te = ((TransformerException)se); 1396 SAXSourceLocator sl = new SAXSourceLocator( te.getLocator() ); 1397 m_serializationHandler.fatalError(new org.xml.sax.SAXParseException(te.getMessage(), sl, te)); 1398 } 1399 else 1400 { 1401 m_serializationHandler.fatalError(new org.xml.sax.SAXParseException(se.getMessage(), new SAXSourceLocator(), se)); 1402 } 1403 } 1404 catch (Exception e){} 1405 } 1406 1407 if(se instanceof TransformerException) 1408 { 1409 m_errorHandler.fatalError((TransformerException)se); 1410 } 1411 else if(se instanceof org.xml.sax.SAXParseException) 1412 { 1413 m_errorHandler.fatalError(new TransformerException(se.getMessage(), 1414 new SAXSourceLocator((org.xml.sax.SAXParseException)se), 1415 se)); 1416 } 1417 else 1418 { 1419 m_errorHandler.fatalError(new TransformerException(se)); 1420 } 1421 1422 } 1423 finally 1424 { 1425 this.reset(); 1426 } 1427 } 1428 } 1429 1430 /** 1431 * Get a SAX2 ContentHandler for the input. 1432 * 1433 * @return A valid ContentHandler, which should never be null, as 1434 * long as getFeature("http://xml.org/trax/features/sax/input") 1435 * returns true. 1436 */ 1437 public ContentHandler getInputContentHandler() 1438 { 1439 return getInputContentHandler(false); 1440 } 1441 1442 /** 1443 * Get a SAX2 ContentHandler for the input. 1444 * 1445 * @param doDocFrag true if a DocumentFragment should be created as 1446 * the root, rather than a Document. 1447 * 1448 * @return A valid ContentHandler, which should never be null, as 1449 * long as getFeature("http://xml.org/trax/features/sax/input") 1450 * returns true. 1451 */ 1452 public ContentHandler getInputContentHandler(boolean doDocFrag) 1453 { 1454 1455 if (null == m_inputContentHandler) 1456 { 1457 1458 // if(null == m_urlOfSource && null != m_stylesheetRoot) 1459 // m_urlOfSource = m_stylesheetRoot.getBaseIdentifier(); 1460 m_inputContentHandler = new TransformerHandlerImpl(this, doDocFrag, 1461 m_urlOfSource); 1462 } 1463 1464 return m_inputContentHandler; 1465 } 1466 1467 /** 1468 * Get a SAX2 DeclHandler for the input. 1469 * @return A valid DeclHandler, which should never be null, as 1470 * long as getFeature("http://xml.org/trax/features/sax/input") 1471 * returns true. 1472 */ 1473 public DeclHandler getInputDeclHandler() 1474 { 1475 1476 if (m_inputContentHandler instanceof DeclHandler) 1477 return (DeclHandler) m_inputContentHandler; 1478 else 1479 return null; 1480 } 1481 1482 /** 1483 * Get a SAX2 LexicalHandler for the input. 1484 * @return A valid LexicalHandler, which should never be null, as 1485 * long as getFeature("http://xml.org/trax/features/sax/input") 1486 * returns true. 1487 */ 1488 public LexicalHandler getInputLexicalHandler() 1489 { 1490 1491 if (m_inputContentHandler instanceof LexicalHandler) 1492 return (LexicalHandler) m_inputContentHandler; 1493 else 1494 return null; 1495 } 1496 1497 /** 1498 * Set the output properties for the transformation. These 1499 * properties will override properties set in the templates 1500 * with xsl:output. 1501 * 1502 * @param oformat A valid OutputProperties object (which will 1503 * not be mutated), or null. 1504 */ 1505 public void setOutputFormat(OutputProperties oformat) 1506 { 1507 m_outputFormat = oformat; 1508 } 1509 1510 /** 1511 * Get the output properties used for the transformation. 1512 * 1513 * @return the output format that was set by the user, 1514 * otherwise the output format from the stylesheet. 1515 */ 1516 public OutputProperties getOutputFormat() 1517 { 1518 1519 // Get the output format that was set by the user, otherwise get the 1520 // output format from the stylesheet. 1521 OutputProperties format = (null == m_outputFormat) 1522 ? getStylesheet().getOutputComposed() 1523 : m_outputFormat; 1524 1525 return format; 1526 } 1527 1528 /** 1529 * Set a parameter for the templates. 1530 * 1531 * @param name The name of the parameter. 1532 * @param namespace The namespace of the parameter. 1533 * @param value The value object. This can be any valid Java object 1534 * -- it's up to the processor to provide the proper 1535 * coersion to the object, or simply pass it on for use 1536 * in extensions. 1537 */ 1538 public void setParameter(String name, String namespace, Object value) 1539 { 1540 1541 VariableStack varstack = getXPathContext().getVarStack(); 1542 QName qname = new QName(namespace, name); 1543 XObject xobject = XObject.create(value, getXPathContext()); 1544 1545 StylesheetRoot sroot = m_stylesheetRoot; 1546 Vector vars = sroot.getVariablesAndParamsComposed(); 1547 int i = vars.size(); 1548 while (--i >= 0) 1549 { 1550 ElemVariable variable = (ElemVariable)vars.elementAt(i); 1551 if(variable.getXSLToken() == Constants.ELEMNAME_PARAMVARIABLE && 1552 variable.getName().equals(qname)) 1553 { 1554 varstack.setGlobalVariable(i, xobject); 1555 } 1556 } 1557 } 1558 1559 /** NEEDSDOC Field m_userParams */ 1560 Vector m_userParams; 1561 1562 /** 1563 * Set a parameter for the transformation. 1564 * 1565 * @param name The name of the parameter, 1566 * which may have a namespace URI. 1567 * @param value The value object. This can be any valid Java object 1568 * -- it's up to the processor to provide the proper 1569 * coersion to the object, or simply pass it on for use 1570 * in extensions. 1571 */ 1572 public void setParameter(String name, Object value) 1573 { 1574 1575 if (value == null) { 1576 throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_INVALID_SET_PARAM_VALUE, new Object[]{name})); 1577 } 1578 1579 StringTokenizer tokenizer = new StringTokenizer(name, "{}", false); 1580 1581 try 1582 { 1583 1584 // The first string might be the namespace, or it might be 1585 // the local name, if the namespace is null. 1586 String s1 = tokenizer.nextToken(); 1587 String s2 = tokenizer.hasMoreTokens() ? tokenizer.nextToken() : null; 1588 1589 if (null == m_userParams) 1590 m_userParams = new Vector(); 1591 1592 if (null == s2) 1593 { 1594 replaceOrPushUserParam(new QName(s1), XObject.create(value, getXPathContext())); 1595 setParameter(s1, null, value); 1596 } 1597 else 1598 { 1599 replaceOrPushUserParam(new QName(s1, s2), XObject.create(value, getXPathContext())); 1600 setParameter(s2, s1, value); 1601 } 1602 } 1603 catch (java.util.NoSuchElementException nsee) 1604 { 1605 1606 // Should throw some sort of an error. 1607 } 1608 } 1609 1610 /** 1611 * NEEDSDOC Method replaceOrPushUserParam 1612 * 1613 * 1614 * NEEDSDOC @param qname 1615 * NEEDSDOC @param xval 1616 */ 1617 private void replaceOrPushUserParam(QName qname, XObject xval) 1618 { 1619 1620 int n = m_userParams.size(); 1621 1622 for (int i = n - 1; i >= 0; i--) 1623 { 1624 Arg arg = (Arg) m_userParams.elementAt(i); 1625 1626 if (arg.getQName().equals(qname)) 1627 { 1628 m_userParams.setElementAt(new Arg(qname, xval, true), i); 1629 1630 return; 1631 } 1632 } 1633 1634 m_userParams.addElement(new Arg(qname, xval, true)); 1635 } 1636 1637 /** 1638 * Get a parameter that was explicitly set with setParameter 1639 * or setParameters. 1640 * 1641 * 1642 * NEEDSDOC @param name 1643 * @return A parameter that has been set with setParameter 1644 * or setParameters, 1645 * *not* all the xsl:params on the stylesheet (which require 1646 * a transformation Source to be evaluated). 1647 */ 1648 public Object getParameter(String name) 1649 { 1650 1651 try 1652 { 1653 1654 // VariableStack varstack = getXPathContext().getVarStack(); 1655 // The first string might be the namespace, or it might be 1656 // the local name, if the namespace is null. 1657 QName qname = QName.getQNameFromString(name); 1658 1659 if (null == m_userParams) 1660 return null; 1661 1662 int n = m_userParams.size(); 1663 1664 for (int i = n - 1; i >= 0; i--) 1665 { 1666 Arg arg = (Arg) m_userParams.elementAt(i); 1667 1668 if (arg.getQName().equals(qname)) 1669 { 1670 return arg.getVal().object(); 1671 } 1672 } 1673 1674 return null; 1675 } 1676 catch (java.util.NoSuchElementException nsee) 1677 { 1678 1679 // Should throw some sort of an error. 1680 return null; 1681 } 1682 } 1683 1684 /** 1685 * Reset parameters that the user specified for the transformation. 1686 * Called during transformer.reset() after we have cleared the 1687 * variable stack. We need to make sure that user params are 1688 * reset so that the transformer object can be reused. 1689 */ 1690 private void resetUserParameters() 1691 { 1692 1693 try 1694 { 1695 1696 if (null == m_userParams) 1697 return; 1698 1699 int n = m_userParams.size(); 1700 for (int i = n - 1; i >= 0; i--) 1701 { 1702 Arg arg = (Arg) m_userParams.elementAt(i); 1703 QName name = arg.getQName(); 1704 // The first string might be the namespace, or it might be 1705 // the local name, if the namespace is null. 1706 String s1 = name.getNamespace(); 1707 String s2 = name.getLocalPart(); 1708 1709 setParameter(s2, s1, arg.getVal().object()); 1710 1711 } 1712 1713 } 1714 catch (java.util.NoSuchElementException nsee) 1715 { 1716 // Should throw some sort of an error. 1717 1718 } 1719 } 1720 1721 /** 1722 * Set a bag of parameters for the transformation. Note that 1723 * these will not be additive, they will replace the existing 1724 * set of parameters. 1725 * 1726 * NEEDSDOC @param params 1727 */ 1728 public void setParameters(Properties params) 1729 { 1730 1731 clearParameters(); 1732 1733 Enumeration names = params.propertyNames(); 1734 1735 while (names.hasMoreElements()) 1736 { 1737 String name = params.getProperty((String) names.nextElement()); 1738 StringTokenizer tokenizer = new StringTokenizer(name, "{}", false); 1739 1740 try 1741 { 1742 1743 // The first string might be the namespace, or it might be 1744 // the local name, if the namespace is null. 1745 String s1 = tokenizer.nextToken(); 1746 String s2 = tokenizer.hasMoreTokens() ? tokenizer.nextToken() : null; 1747 1748 if (null == s2) 1749 setParameter(s1, null, params.getProperty(name)); 1750 else 1751 setParameter(s2, s1, params.getProperty(name)); 1752 } 1753 catch (java.util.NoSuchElementException nsee) 1754 { 1755 1756 // Should throw some sort of an error. 1757 } 1758 } 1759 } 1760 1761 /** 1762 * Reset the parameters to a null list. 1763 */ 1764 public void clearParameters() 1765 { 1766 1767 synchronized (m_reentryGuard) 1768 { 1769 VariableStack varstack = new VariableStack(); 1770 1771 m_xcontext.setVarStack(varstack); 1772 1773 m_userParams = null; 1774 } 1775 } 1776 1777 1778 /** 1779 * Internal -- push the global variables from the Stylesheet onto 1780 * the context's runtime variable stack. 1781 * <p>If we encounter a variable 1782 * that is already defined in the variable stack, we ignore it. This 1783 * is because the second variable definition will be at a lower import 1784 * precedence. Presumably, global"variables at the same import precedence 1785 * with the same name will have been caught during the recompose process. 1786 * <p>However, if we encounter a parameter that is already defined in the 1787 * variable stack, we need to see if this is a parameter whose value was 1788 * supplied by a setParameter call. If so, we need to "receive" the one 1789 * already in the stack, ignoring this one. If it is just an earlier 1790 * xsl:param or xsl:variable definition, we ignore it using the same 1791 * reasoning as explained above for the variable. 1792 * 1793 * @param contextNode The root of the source tree, can't be null. 1794 * 1795 * @throws TransformerException 1796 */ 1797 protected void pushGlobalVars(int contextNode) throws TransformerException 1798 { 1799 1800 XPathContext xctxt = m_xcontext; 1801 VariableStack vs = xctxt.getVarStack(); 1802 StylesheetRoot sr = getStylesheet(); 1803 Vector vars = sr.getVariablesAndParamsComposed(); 1804 1805 int i = vars.size(); 1806 vs.link(i); 1807 1808 while (--i >= 0) 1809 { 1810 ElemVariable v = (ElemVariable) vars.elementAt(i); 1811 1812 // XObject xobj = v.getValue(this, contextNode); 1813 XObject xobj = new XUnresolvedVariable(v, contextNode, this, 1814 vs.getStackFrame(), 0, true); 1815 1816 if(null == vs.elementAt(i)) 1817 vs.setGlobalVariable(i, xobj); 1818 } 1819 1820 } 1821 1822 /** 1823 * Set an object that will be used to resolve URIs used in 1824 * document(), etc. 1825 * @param resolver An object that implements the URIResolver interface, 1826 * or null. 1827 */ 1828 public void setURIResolver(URIResolver resolver) 1829 { 1830 1831 synchronized (m_reentryGuard) 1832 { 1833 m_xcontext.getSourceTreeManager().setURIResolver(resolver); 1834 } 1835 } 1836 1837 /** 1838 * Get an object that will be used to resolve URIs used in 1839 * document(), etc. 1840 * 1841 * @return An object that implements the URIResolver interface, 1842 * or null. 1843 */ 1844 public URIResolver getURIResolver() 1845 { 1846 return m_xcontext.getSourceTreeManager().getURIResolver(); 1847 } 1848 1849 // ======== End Transformer Implementation ======== 1850 1851 /** 1852 * Set the content event handler. 1853 * 1854 * NEEDSDOC @param handler 1855 * @throws java.lang.NullPointerException If the handler 1856 * is null. 1857 * @see org.xml.sax.XMLReader#setContentHandler 1858 */ 1859 public void setContentHandler(ContentHandler handler) 1860 { 1861 1862 if (handler == null) 1863 { 1864 throw new NullPointerException(XSLMessages.createMessage(XSLTErrorResources.ER_NULL_CONTENT_HANDLER, null)); //"Null content handler"); 1865 } 1866 else 1867 { 1868 m_outputContentHandler = handler; 1869 1870 if (null == m_serializationHandler) 1871 { 1872 ToXMLSAXHandler h = new ToXMLSAXHandler(); 1873 h.setContentHandler(handler); 1874 h.setTransformer(this); 1875 1876 m_serializationHandler = h; 1877 } 1878 else 1879 m_serializationHandler.setContentHandler(handler); 1880 } 1881 } 1882 1883 /** 1884 * Get the content event handler. 1885 * 1886 * @return The current content handler, or null if none was set. 1887 * @see org.xml.sax.XMLReader#getContentHandler 1888 */ 1889 public ContentHandler getContentHandler() 1890 { 1891 return m_outputContentHandler; 1892 } 1893 1894 /** 1895 * Given a stylesheet element, create a result tree fragment from it's 1896 * contents. The fragment will be built within the shared RTF DTM system 1897 * used as a variable stack. 1898 * @param templateParent The template element that holds the fragment. 1899 * @return the NodeHandle for the root node of the resulting RTF. 1900 * 1901 * @throws TransformerException 1902 * @xsl.usage advanced 1903 */ 1904 public int transformToRTF(ElemTemplateElement templateParent) 1905 throws TransformerException 1906 { 1907 // Retrieve a DTM to contain the RTF. At this writing, this may be a 1908 // multi-document DTM (SAX2RTFDTM). 1909 DTM dtmFrag = m_xcontext.getRTFDTM(); 1910 return transformToRTF(templateParent,dtmFrag); 1911 } 1912 1913 /** 1914 * Given a stylesheet element, create a result tree fragment from it's 1915 * contents. The fragment will also use the shared DTM system, but will 1916 * obtain its space from the global variable pool rather than the dynamic 1917 * variable stack. This allows late binding of XUnresolvedVariables without 1918 * the risk that their content will be discarded when the variable stack 1919 * is popped. 1920 * 1921 * @param templateParent The template element that holds the fragment. 1922 * @return the NodeHandle for the root node of the resulting RTF. 1923 * 1924 * @throws TransformerException 1925 * @xsl.usage advanced 1926 */ 1927 public int transformToGlobalRTF(ElemTemplateElement templateParent) 1928 throws TransformerException 1929 { 1930 // Retrieve a DTM to contain the RTF. At this writing, this may be a 1931 // multi-document DTM (SAX2RTFDTM). 1932 DTM dtmFrag = m_xcontext.getGlobalRTFDTM(); 1933 return transformToRTF(templateParent,dtmFrag); 1934 } 1935 1936 /** 1937 * Given a stylesheet element, create a result tree fragment from it's 1938 * contents. 1939 * @param templateParent The template element that holds the fragment. 1940 * @param dtmFrag The DTM to write the RTF into 1941 * @return the NodeHandle for the root node of the resulting RTF. 1942 * 1943 * @throws TransformerException 1944 * @xsl.usage advanced 1945 */ 1946 private int transformToRTF(ElemTemplateElement templateParent,DTM dtmFrag) 1947 throws TransformerException 1948 { 1949 1950 XPathContext xctxt = m_xcontext; 1951 1952 ContentHandler rtfHandler = dtmFrag.getContentHandler(); 1953 1954 // Obtain the ResultTreeFrag's root node. 1955 // NOTE: In SAX2RTFDTM, this value isn't available until after 1956 // the startDocument has been issued, so assignment has been moved 1957 // down a bit in the code. 1958 int resultFragment; // not yet reliably = dtmFrag.getDocument(); 1959 1960 // Save the current result tree handler. 1961 SerializationHandler savedRTreeHandler = this.m_serializationHandler; 1962 1963 1964 // And make a new handler for the RTF. 1965 ToSAXHandler h = new ToXMLSAXHandler(); 1966 h.setContentHandler(rtfHandler); 1967 h.setTransformer(this); 1968 1969 // Replace the old handler (which was already saved) 1970 m_serializationHandler = h; 1971 1972 // use local variable for the current handler 1973 SerializationHandler rth = m_serializationHandler; 1974 1975 try 1976 { 1977 rth.startDocument(); 1978 1979 // startDocument is "bottlenecked" in RTH. We need it acted upon immediately, 1980 // to set the DTM's state as in-progress, so that if the xsl:variable's body causes 1981 // further RTF activity we can keep that from bashing this DTM. 1982 rth.flushPending(); 1983 1984 try 1985 { 1986 1987 // Do the transformation of the child elements. 1988 executeChildTemplates(templateParent, true); 1989 1990 // Make sure everything is flushed! 1991 rth.flushPending(); 1992 1993 // Get the document ID. May not exist until the RTH has not only 1994 // received, but flushed, the startDocument, and may be invalid 1995 // again after the document has been closed (still debating that) 1996 // ... so waiting until just before the end seems simplest/safest. 1997 resultFragment = dtmFrag.getDocument(); 1998 } 1999 finally 2000 { 2001 rth.endDocument(); 2002 } 2003 } 2004 catch (org.xml.sax.SAXException se) 2005 { 2006 throw new TransformerException(se); 2007 } 2008 finally 2009 { 2010 2011 // Restore the previous result tree handler. 2012 this.m_serializationHandler = savedRTreeHandler; 2013 } 2014 2015 return resultFragment; 2016 } 2017 2018 /** 2019 * Get the StringWriter pool, so that StringWriter 2020 * objects may be reused. 2021 * 2022 * @return The string writer pool, not null. 2023 * @xsl.usage internal 2024 */ 2025 public ObjectPool getStringWriterPool() 2026 { 2027 return m_stringWriterObjectPool; 2028 } 2029 2030 /** 2031 * Take the contents of a template element, process it, and 2032 * convert it to a string. 2033 * 2034 * @param elem The parent element whose children will be output 2035 * as a string. 2036 * 2037 * @return The stringized result of executing the elements children. 2038 * 2039 * @throws TransformerException 2040 * @xsl.usage advanced 2041 */ 2042 public String transformToString(ElemTemplateElement elem) 2043 throws TransformerException 2044 { 2045 ElemTemplateElement firstChild = elem.getFirstChildElem(); 2046 if(null == firstChild) 2047 return ""; 2048 if(elem.hasTextLitOnly() && m_optimizer) 2049 { 2050 return ((ElemTextLiteral)firstChild).getNodeValue(); 2051 } 2052 2053 // Save the current result tree handler. 2054 SerializationHandler savedRTreeHandler = this.m_serializationHandler; 2055 2056 // Create a Serializer object that will handle the SAX events 2057 // and build the ResultTreeFrag nodes. 2058 StringWriter sw = (StringWriter) m_stringWriterObjectPool.getInstance(); 2059 2060 m_serializationHandler = 2061 (ToTextStream) m_textResultHandlerObjectPool.getInstance(); 2062 2063 if (null == m_serializationHandler) 2064 { 2065 // if we didn't get one from the pool, go make a new one 2066 2067 2068 Serializer serializer = org.apache.xml.serializer.SerializerFactory.getSerializer( 2069 m_textformat.getProperties()); 2070 m_serializationHandler = (SerializationHandler) serializer; 2071 } 2072 2073 m_serializationHandler.setTransformer(this); 2074 m_serializationHandler.setWriter(sw); 2075 2076 2077 String result; 2078 2079 try 2080 { 2081 /* Don't call startDocument, the SerializationHandler will 2082 * generate its own internal startDocument call anyways 2083 */ 2084 // this.m_serializationHandler.startDocument(); 2085 2086 // Do the transformation of the child elements. 2087 executeChildTemplates(elem, true); 2088 this.m_serializationHandler.endDocument(); 2089 2090 result = sw.toString(); 2091 } 2092 catch (org.xml.sax.SAXException se) 2093 { 2094 throw new TransformerException(se); 2095 } 2096 finally 2097 { 2098 sw.getBuffer().setLength(0); 2099 2100 try 2101 { 2102 sw.close(); 2103 } 2104 catch (Exception ioe){} 2105 2106 m_stringWriterObjectPool.freeInstance(sw); 2107 m_serializationHandler.reset(); 2108 m_textResultHandlerObjectPool.freeInstance(m_serializationHandler); 2109 2110 // Restore the previous result tree handler. 2111 m_serializationHandler = savedRTreeHandler; 2112 } 2113 2114 return result; 2115 } 2116 2117 /** 2118 * Given an element and mode, find the corresponding 2119 * template and process the contents. 2120 * 2121 * @param xslInstruction The calling element. 2122 * @param template The template to use if xsl:for-each, current template for apply-imports, or null. 2123 * @param child The source context node. 2124 * @throws TransformerException 2125 * @return true if applied a template, false if not. 2126 * @xsl.usage advanced 2127 */ 2128 public boolean applyTemplateToNode(ElemTemplateElement xslInstruction, // xsl:apply-templates or xsl:for-each 2129 ElemTemplate template, int child) 2130 throws TransformerException 2131 { 2132 2133 DTM dtm = m_xcontext.getDTM(child); 2134 short nodeType = dtm.getNodeType(child); 2135 boolean isDefaultTextRule = false; 2136 boolean isApplyImports = false; 2137 2138 isApplyImports = ((xslInstruction == null) 2139 ? false 2140 : xslInstruction.getXSLToken() 2141 == Constants.ELEMNAME_APPLY_IMPORTS); 2142 2143 if (null == template || isApplyImports) 2144 { 2145 int maxImportLevel, endImportLevel=0; 2146 2147 if (isApplyImports) 2148 { 2149 maxImportLevel = 2150 template.getStylesheetComposed().getImportCountComposed() - 1; 2151 endImportLevel = 2152 template.getStylesheetComposed().getEndImportCountComposed(); 2153 } 2154 else 2155 { 2156 maxImportLevel = -1; 2157 } 2158 2159 // If we're trying an xsl:apply-imports at the top level (ie there are no 2160 // imported stylesheets), we need to indicate that there is no matching template. 2161 // The above logic will calculate a maxImportLevel of -1 which indicates 2162 // that we should find any template. This is because a value of -1 for 2163 // maxImportLevel has a special meaning. But we don't want that. 2164 // We want to match -no- templates. See bugzilla bug 1170. 2165 if (isApplyImports && (maxImportLevel == -1)) 2166 { 2167 template = null; 2168 } 2169 else 2170 { 2171 2172 // Find the XSL template that is the best match for the 2173 // element. 2174 XPathContext xctxt = m_xcontext; 2175 2176 try 2177 { 2178 xctxt.pushNamespaceContext(xslInstruction); 2179 2180 QName mode = this.getMode(); 2181 2182 if (isApplyImports) 2183 template = m_stylesheetRoot.getTemplateComposed(xctxt, child, mode, 2184 maxImportLevel, endImportLevel, m_quietConflictWarnings, dtm); 2185 else 2186 template = m_stylesheetRoot.getTemplateComposed(xctxt, child, mode, 2187 m_quietConflictWarnings, dtm); 2188 2189 } 2190 finally 2191 { 2192 xctxt.popNamespaceContext(); 2193 } 2194 } 2195 2196 // If that didn't locate a node, fall back to a default template rule. 2197 // See http://www.w3.org/TR/xslt#built-in-rule. 2198 if (null == template) 2199 { 2200 switch (nodeType) 2201 { 2202 case DTM.DOCUMENT_FRAGMENT_NODE : 2203 case DTM.ELEMENT_NODE : 2204 template = m_stylesheetRoot.getDefaultRule(); 2205 break; 2206 case DTM.CDATA_SECTION_NODE : 2207 case DTM.TEXT_NODE : 2208 case DTM.ATTRIBUTE_NODE : 2209 template = m_stylesheetRoot.getDefaultTextRule(); 2210 isDefaultTextRule = true; 2211 break; 2212 case DTM.DOCUMENT_NODE : 2213 template = m_stylesheetRoot.getDefaultRootRule(); 2214 break; 2215 default : 2216 2217 // No default rules for processing instructions and the like. 2218 return false; 2219 } 2220 } 2221 } 2222 2223 // If we are processing the default text rule, then just clone 2224 // the value directly to the result tree. 2225 try 2226 { 2227 pushElemTemplateElement(template); 2228 m_xcontext.pushCurrentNode(child); 2229 pushPairCurrentMatched(template, child); 2230 2231 // Fix copy copy29 test. 2232 if (!isApplyImports) { 2233 DTMIterator cnl = new org.apache.xpath.NodeSetDTM(child, m_xcontext.getDTMManager()); 2234 m_xcontext.pushContextNodeList(cnl); 2235 } 2236 2237 if (isDefaultTextRule) 2238 { 2239 switch (nodeType) 2240 { 2241 case DTM.CDATA_SECTION_NODE : 2242 case DTM.TEXT_NODE : 2243 ClonerToResultTree.cloneToResultTree(child, nodeType, 2244 dtm, getResultTreeHandler(), false); 2245 break; 2246 case DTM.ATTRIBUTE_NODE : 2247 dtm.dispatchCharactersEvents(child, getResultTreeHandler(), false); 2248 break; 2249 } 2250 } 2251 else 2252 { 2253 2254 // Fire a trace event for the template. 2255 2256 if (m_debug) 2257 getTraceManager().fireTraceEvent(template); 2258 // And execute the child templates. 2259 // 9/11/00: If template has been compiled, hand off to it 2260 // since much (most? all?) of the processing has been inlined. 2261 // (It would be nice if there was a single entry point that 2262 // worked for both... but the interpretive system works by 2263 // having the Tranformer execute the children, while the 2264 // compiled obviously has to run its own code. It's 2265 // also unclear that "execute" is really the right name for 2266 // that entry point.) 2267 m_xcontext.setSAXLocator(template); 2268 // m_xcontext.getVarStack().link(); 2269 m_xcontext.getVarStack().link(template.m_frameSize); 2270 executeChildTemplates(template, true); 2271 2272 if (m_debug) 2273 getTraceManager().fireTraceEndEvent(template); 2274 } 2275 } 2276 catch (org.xml.sax.SAXException se) 2277 { 2278 throw new TransformerException(se); 2279 } 2280 finally 2281 { 2282 if (!isDefaultTextRule) 2283 m_xcontext.getVarStack().unlink(); 2284 m_xcontext.popCurrentNode(); 2285 if (!isApplyImports) { 2286 m_xcontext.popContextNodeList(); 2287 } 2288 popCurrentMatched(); 2289 2290 popElemTemplateElement(); 2291 } 2292 2293 return true; 2294 } 2295 2296 2297 /** 2298 * Execute each of the children of a template element. This method 2299 * is only for extension use. 2300 * 2301 * @param elem The ElemTemplateElement that contains the children 2302 * that should execute. 2303 * NEEDSDOC @param context 2304 * @param mode The current mode. 2305 * @param handler The ContentHandler to where the result events 2306 * should be fed. 2307 * 2308 * @throws TransformerException 2309 * @xsl.usage advanced 2310 */ 2311 public void executeChildTemplates( 2312 ElemTemplateElement elem, org.w3c.dom.Node context, QName mode, ContentHandler handler) 2313 throws TransformerException 2314 { 2315 2316 XPathContext xctxt = m_xcontext; 2317 2318 try 2319 { 2320 if(null != mode) 2321 pushMode(mode); 2322 xctxt.pushCurrentNode(xctxt.getDTMHandleFromNode(context)); 2323 executeChildTemplates(elem, handler); 2324 } 2325 finally 2326 { 2327 xctxt.popCurrentNode(); 2328 2329 // I'm not sure where or why this was here. It is clearly in 2330 // error though, without a corresponding pushMode(). 2331 if (null != mode) 2332 popMode(); 2333 } 2334 } 2335 2336 /** 2337 * Execute each of the children of a template element. 2338 * 2339 * @param elem The ElemTemplateElement that contains the children 2340 * that should execute. 2341 * @param shouldAddAttrs true if xsl:attributes should be executed. 2342 * 2343 * @throws TransformerException 2344 * @xsl.usage advanced 2345 */ 2346 public void executeChildTemplates( 2347 ElemTemplateElement elem, boolean shouldAddAttrs) 2348 throws TransformerException 2349 { 2350 2351 // Does this element have any children? 2352 ElemTemplateElement t = elem.getFirstChildElem(); 2353 2354 if (null == t) 2355 return; 2356 2357 if(elem.hasTextLitOnly() && m_optimizer) 2358 { 2359 char[] chars = ((ElemTextLiteral)t).getChars(); 2360 try 2361 { 2362 // Have to push stuff on for tooling... 2363 this.pushElemTemplateElement(t); 2364 m_serializationHandler.characters(chars, 0, chars.length); 2365 } 2366 catch(SAXException se) 2367 { 2368 throw new TransformerException(se); 2369 } 2370 finally 2371 { 2372 this.popElemTemplateElement(); 2373 } 2374 return; 2375 } 2376 2377 // // Check for infinite loops if we have to. 2378 // boolean check = (m_stackGuard.m_recursionLimit > -1); 2379 // 2380 // if (check) 2381 // getStackGuard().push(elem, xctxt.getCurrentNode()); 2382 2383 XPathContext xctxt = m_xcontext; 2384 xctxt.pushSAXLocatorNull(); 2385 int currentTemplateElementsTop = m_currentTemplateElements.size(); 2386 m_currentTemplateElements.push(null); 2387 2388 try 2389 { 2390 // Loop through the children of the template, calling execute on 2391 // each of them. 2392 for (; t != null; t = t.getNextSiblingElem()) 2393 { 2394 if (!shouldAddAttrs 2395 && t.getXSLToken() == Constants.ELEMNAME_ATTRIBUTE) 2396 continue; 2397 2398 xctxt.setSAXLocator(t); 2399 m_currentTemplateElements.setElementAt(t,currentTemplateElementsTop); 2400 t.execute(this); 2401 } 2402 } 2403 catch(RuntimeException re) 2404 { 2405 TransformerException te = new TransformerException(re); 2406 te.setLocator(t); 2407 throw te; 2408 } 2409 finally 2410 { 2411 m_currentTemplateElements.pop(); 2412 xctxt.popSAXLocator(); 2413 } 2414 2415 // Check for infinite loops if we have to 2416 // if (check) 2417 // getStackGuard().pop(); 2418 } 2419 /** 2420 * Execute each of the children of a template element. 2421 * 2422 * @param elem The ElemTemplateElement that contains the children 2423 * that should execute. 2424 * @param handler The ContentHandler to where the result events 2425 * should be fed. 2426 * 2427 * @throws TransformerException 2428 * @xsl.usage advanced 2429 */ 2430 public void executeChildTemplates( 2431 ElemTemplateElement elem, ContentHandler handler) 2432 throws TransformerException 2433 { 2434 2435 SerializationHandler xoh = this.getSerializationHandler(); 2436 2437 // These may well not be the same! In this case when calling 2438 // the Redirect extension, it has already set the ContentHandler 2439 // in the Transformer. 2440 SerializationHandler savedHandler = xoh; 2441 2442 try 2443 { 2444 xoh.flushPending(); 2445 2446 // %REVIEW% Make sure current node is being pushed. 2447 LexicalHandler lex = null; 2448 if (handler instanceof LexicalHandler) { 2449 lex = (LexicalHandler) handler; 2450 } 2451 m_serializationHandler = new ToXMLSAXHandler(handler, lex, savedHandler.getEncoding()); 2452 m_serializationHandler.setTransformer(this); 2453 executeChildTemplates(elem, true); 2454 } 2455 catch (TransformerException e) 2456 { 2457 throw e; 2458 } 2459 catch (SAXException se) { 2460 throw new TransformerException(se); 2461 } 2462 finally 2463 { 2464 m_serializationHandler = savedHandler; 2465 } 2466 } 2467 2468 /** 2469 * Get the keys for the xsl:sort elements. 2470 * Note: Should this go into ElemForEach? 2471 * 2472 * @param foreach Valid ElemForEach element, not null. 2473 * @param sourceNodeContext The current node context in the source tree, 2474 * needed to evaluate the Attribute Value Templates. 2475 * 2476 * @return A Vector of NodeSortKeys, or null. 2477 * 2478 * @throws TransformerException 2479 * @xsl.usage advanced 2480 */ 2481 public Vector processSortKeys(ElemForEach foreach, int sourceNodeContext) 2482 throws TransformerException 2483 { 2484 2485 Vector keys = null; 2486 XPathContext xctxt = m_xcontext; 2487 int nElems = foreach.getSortElemCount(); 2488 2489 if (nElems > 0) 2490 keys = new Vector(); 2491 2492 // March backwards, collecting the sort keys. 2493 for (int i = 0; i < nElems; i++) 2494 { 2495 ElemSort sort = foreach.getSortElem(i); 2496 2497 if (m_debug) 2498 getTraceManager().fireTraceEvent(sort); 2499 2500 String langString = 2501 (null != sort.getLang()) 2502 ? sort.getLang().evaluate(xctxt, sourceNodeContext, foreach) : null; 2503 String dataTypeString = sort.getDataType().evaluate(xctxt, 2504 sourceNodeContext, foreach); 2505 2506 if (dataTypeString.indexOf(":") >= 0) 2507 System.out.println( 2508 "TODO: Need to write the hooks for QNAME sort data type"); 2509 else if (!(dataTypeString.equalsIgnoreCase(Constants.ATTRVAL_DATATYPE_TEXT)) 2510 &&!(dataTypeString.equalsIgnoreCase( 2511 Constants.ATTRVAL_DATATYPE_NUMBER))) 2512 foreach.error(XSLTErrorResources.ER_ILLEGAL_ATTRIBUTE_VALUE, 2513 new Object[]{ Constants.ATTRNAME_DATATYPE, 2514 dataTypeString }); 2515 2516 boolean treatAsNumbers = 2517 ((null != dataTypeString) && dataTypeString.equals( 2518 Constants.ATTRVAL_DATATYPE_NUMBER)) ? true : false; 2519 String orderString = sort.getOrder().evaluate(xctxt, sourceNodeContext, 2520 foreach); 2521 2522 if (!(orderString.equalsIgnoreCase(Constants.ATTRVAL_ORDER_ASCENDING)) 2523 &&!(orderString.equalsIgnoreCase( 2524 Constants.ATTRVAL_ORDER_DESCENDING))) 2525 foreach.error(XSLTErrorResources.ER_ILLEGAL_ATTRIBUTE_VALUE, 2526 new Object[]{ Constants.ATTRNAME_ORDER, 2527 orderString }); 2528 2529 boolean descending = 2530 ((null != orderString) && orderString.equals( 2531 Constants.ATTRVAL_ORDER_DESCENDING)) ? true : false; 2532 AVT caseOrder = sort.getCaseOrder(); 2533 boolean caseOrderUpper; 2534 2535 if (null != caseOrder) 2536 { 2537 String caseOrderString = caseOrder.evaluate(xctxt, sourceNodeContext, 2538 foreach); 2539 2540 if (!(caseOrderString.equalsIgnoreCase(Constants.ATTRVAL_CASEORDER_UPPER)) 2541 &&!(caseOrderString.equalsIgnoreCase( 2542 Constants.ATTRVAL_CASEORDER_LOWER))) 2543 foreach.error(XSLTErrorResources.ER_ILLEGAL_ATTRIBUTE_VALUE, 2544 new Object[]{ Constants.ATTRNAME_CASEORDER, 2545 caseOrderString }); 2546 2547 caseOrderUpper = 2548 ((null != caseOrderString) && caseOrderString.equals( 2549 Constants.ATTRVAL_CASEORDER_UPPER)) ? true : false; 2550 } 2551 else 2552 { 2553 caseOrderUpper = false; 2554 } 2555 2556 keys.addElement(new NodeSortKey(this, sort.getSelect(), treatAsNumbers, 2557 descending, langString, caseOrderUpper, 2558 foreach)); 2559 if (m_debug) 2560 getTraceManager().fireTraceEndEvent(sort); 2561 } 2562 2563 return keys; 2564 } 2565 2566 //========================================================== 2567 // SECTION: TransformState implementation 2568 //========================================================== 2569 2570 /** 2571 * Get the stack of ElemTemplateElements. 2572 * 2573 * @return A copy of stack that contains the xsl element instructions, 2574 * the earliest called in index zero, and the latest called in index size()-1. 2575 */ 2576 public Vector getElementCallstack() 2577 { 2578 Vector elems = new Vector(); 2579 int nStackSize = m_currentTemplateElements.size(); 2580 for(int i = 0; i < nStackSize; i++) 2581 { 2582 ElemTemplateElement elem = (ElemTemplateElement) m_currentTemplateElements.elementAt(i); 2583 if(null != elem) 2584 { 2585 elems.addElement(elem); 2586 } 2587 } 2588 return elems; 2589 } 2590 2591 /** 2592 * Get the count of how many elements are 2593 * active. 2594 * @return The number of active elements on 2595 * the currentTemplateElements stack. 2596 */ 2597 public int getCurrentTemplateElementsCount() 2598 { 2599 return m_currentTemplateElements.size(); 2600 } 2601 2602 2603 /** 2604 * Get the count of how many elements are 2605 * active. 2606 * @return The number of active elements on 2607 * the currentTemplateElements stack. 2608 */ 2609 public ObjectStack getCurrentTemplateElements() 2610 { 2611 return m_currentTemplateElements; 2612 } 2613 2614 /** 2615 * Push the current template element. 2616 * 2617 * @param elem The current ElemTemplateElement (may be null, and then 2618 * set via setCurrentElement). 2619 */ 2620 public void pushElemTemplateElement(ElemTemplateElement elem) 2621 { 2622 m_currentTemplateElements.push(elem); 2623 } 2624 2625 /** 2626 * Pop the current template element. 2627 */ 2628 public void popElemTemplateElement() 2629 { 2630 m_currentTemplateElements.pop(); 2631 } 2632 2633 /** 2634 * Set the top of the current template elements 2635 * stack. 2636 * 2637 * @param e The current ElemTemplateElement about to 2638 * be executed. 2639 */ 2640 public void setCurrentElement(ElemTemplateElement e) 2641 { 2642 m_currentTemplateElements.setTop(e); 2643 } 2644 2645 /** 2646 * Retrieves the current ElemTemplateElement that is 2647 * being executed. 2648 * 2649 * @return The current ElemTemplateElement that is executing, 2650 * should not normally be null. 2651 */ 2652 public ElemTemplateElement getCurrentElement() 2653 { 2654 return (m_currentTemplateElements.size() > 0) ? 2655 (ElemTemplateElement) m_currentTemplateElements.peek() : null; 2656 } 2657 2658 /** 2659 * This method retrieves the current context node 2660 * in the source tree. 2661 * 2662 * @return The current context node (should never be null?). 2663 */ 2664 public int getCurrentNode() 2665 { 2666 return m_xcontext.getCurrentNode(); 2667 } 2668 2669 /** 2670 * Get the call stack of xsl:template elements. 2671 * 2672 * @return A copy of stack that contains the xsl:template 2673 * (ElemTemplate) instructions, the earliest called in index 2674 * zero, and the latest called in index size()-1. 2675 */ 2676 public Vector getTemplateCallstack() 2677 { 2678 Vector elems = new Vector(); 2679 int nStackSize = m_currentTemplateElements.size(); 2680 for(int i = 0; i < nStackSize; i++) 2681 { 2682 ElemTemplateElement elem = (ElemTemplateElement) m_currentTemplateElements.elementAt(i); 2683 if(null != elem && (elem.getXSLToken() != Constants.ELEMNAME_TEMPLATE)) 2684 { 2685 elems.addElement(elem); 2686 } 2687 } 2688 return elems; 2689 } 2690 2691 2692 /** 2693 * This method retrieves the xsl:template 2694 * that is in effect, which may be a matched template 2695 * or a named template. 2696 * 2697 * <p>Please note that the ElemTemplate returned may 2698 * be a default template, and thus may not have a template 2699 * defined in the stylesheet.</p> 2700 * 2701 * @return The current xsl:template, should not be null. 2702 */ 2703 public ElemTemplate getCurrentTemplate() 2704 { 2705 2706 ElemTemplateElement elem = getCurrentElement(); 2707 2708 while ((null != elem) 2709 && (elem.getXSLToken() != Constants.ELEMNAME_TEMPLATE)) 2710 { 2711 elem = elem.getParentElem(); 2712 } 2713 2714 return (ElemTemplate) elem; 2715 } 2716 2717 /** 2718 * Push both the current xsl:template or xsl:for-each onto the 2719 * stack, along with the child node that was matched. 2720 * (Note: should this only be used for xsl:templates?? -sb) 2721 * 2722 * @param template xsl:template or xsl:for-each. 2723 * @param child The child that was matched. 2724 */ 2725 public void pushPairCurrentMatched(ElemTemplateElement template, int child) 2726 { 2727 m_currentMatchTemplates.push(template); 2728 m_currentMatchedNodes.push(child); 2729 } 2730 2731 /** 2732 * Pop the elements that were pushed via pushPairCurrentMatched. 2733 */ 2734 public void popCurrentMatched() 2735 { 2736 m_currentMatchTemplates.pop(); 2737 m_currentMatchedNodes.pop(); 2738 } 2739 2740 /** 2741 * This method retrieves the xsl:template 2742 * that was matched. Note that this may not be 2743 * the same thing as the current template (which 2744 * may be from getCurrentElement()), since a named 2745 * template may be in effect. 2746 * 2747 * @return The pushed template that was pushed via pushPairCurrentMatched. 2748 */ 2749 public ElemTemplate getMatchedTemplate() 2750 { 2751 return (ElemTemplate) m_currentMatchTemplates.peek(); 2752 } 2753 2754 /** 2755 * Retrieves the node in the source tree that matched 2756 * the template obtained via getMatchedTemplate(). 2757 * 2758 * @return The matched node that corresponds to the 2759 * match attribute of the current xsl:template. 2760 */ 2761 public int getMatchedNode() 2762 { 2763 return m_currentMatchedNodes.peepTail(); 2764 } 2765 2766 /** 2767 * Get the current context node list. 2768 * 2769 * @return A reset clone of the context node list. 2770 */ 2771 public DTMIterator getContextNodeList() 2772 { 2773 2774 try 2775 { 2776 DTMIterator cnl = m_xcontext.getContextNodeList(); 2777 2778 return (cnl == null) ? null : (DTMIterator) cnl.cloneWithReset(); 2779 } 2780 catch (CloneNotSupportedException cnse) 2781 { 2782 2783 // should never happen. 2784 return null; 2785 } 2786 } 2787 2788 /** 2789 * Get the TrAX Transformer object in effect. 2790 * 2791 * @return This object. 2792 */ 2793 public Transformer getTransformer() 2794 { 2795 return this; 2796 } 2797 2798 //========================================================== 2799 // SECTION: Accessor Functions 2800 //========================================================== 2801 2802 /** 2803 * Set the stylesheet for this processor. If this is set, then the 2804 * process calls that take only the input .xml will use 2805 * this instead of looking for a stylesheet PI. Also, 2806 * setting the stylesheet is needed if you are going 2807 * to use the processor as a SAX ContentHandler. 2808 * 2809 * @param stylesheetRoot A non-null StylesheetRoot object, 2810 * or null if you wish to clear the stylesheet reference. 2811 */ 2812 public void setStylesheet(StylesheetRoot stylesheetRoot) 2813 { 2814 m_stylesheetRoot = stylesheetRoot; 2815 } 2816 2817 /** 2818 * Get the current stylesheet for this processor. 2819 * 2820 * @return The stylesheet that is associated with this 2821 * transformer. 2822 */ 2823 public final StylesheetRoot getStylesheet() 2824 { 2825 return m_stylesheetRoot; 2826 } 2827 2828 /** 2829 * Get quietConflictWarnings property. If the quietConflictWarnings 2830 * property is set to true, warnings about pattern conflicts won't be 2831 * printed to the diagnostics stream. 2832 * 2833 * @return True if this transformer should not report 2834 * template match conflicts. 2835 */ 2836 public boolean getQuietConflictWarnings() 2837 { 2838 return m_quietConflictWarnings; 2839 } 2840 2841 /** 2842 * If the quietConflictWarnings property is set to 2843 * true, warnings about pattern conflicts won't be 2844 * printed to the diagnostics stream. 2845 * False by default. 2846 * (Currently setting this property will have no effect.) 2847 * 2848 * @param b true if conflict warnings should be suppressed. 2849 */ 2850 public void setQuietConflictWarnings(boolean b) 2851 { 2852 m_quietConflictWarnings = b; 2853 } 2854 2855 /** 2856 * Set the execution context for XPath. 2857 * 2858 * @param xcontext A non-null reference to the XPathContext 2859 * associated with this transformer. 2860 * @xsl.usage internal 2861 */ 2862 public void setXPathContext(XPathContext xcontext) 2863 { 2864 m_xcontext = xcontext; 2865 } 2866 2867 /** 2868 * Get the XPath context associated with this transformer. 2869 * 2870 * @return The XPathContext reference, never null. 2871 */ 2872 public final XPathContext getXPathContext() 2873 { 2874 return m_xcontext; 2875 } 2876 2877 /** 2878 * Get the object used to guard the stack from 2879 * recursion. 2880 * 2881 * @return The StackGuard object, which should never be null. 2882 * @xsl.usage internal 2883 */ 2884 public StackGuard getStackGuard() 2885 { 2886 return m_stackGuard; 2887 } 2888 2889 /** 2890 * Get the recursion limit. 2891 * Used for infinite loop check. If the value is -1, do not 2892 * check for infinite loops. Anyone who wants to enable that 2893 * check should change the value of this variable to be the 2894 * level of recursion that they want to check. Be careful setting 2895 * this variable, if the number is too low, it may report an 2896 * infinite loop situation, when there is none. 2897 * Post version 1.0.0, we'll make this a runtime feature. 2898 * 2899 * @return The limit on recursion, or -1 if no check is to be made. 2900 */ 2901 public int getRecursionLimit() 2902 { 2903 return m_stackGuard.getRecursionLimit(); 2904 } 2905 2906 /** 2907 * Set the recursion limit. 2908 * Used for infinite loop check. If the value is -1, do not 2909 * check for infinite loops. Anyone who wants to enable that 2910 * check should change the value of this variable to be the 2911 * level of recursion that they want to check. Be careful setting 2912 * this variable, if the number is too low, it may report an 2913 * infinite loop situation, when there is none. 2914 * Post version 1.0.0, we'll make this a runtime feature. 2915 * 2916 * @param limit A number that represents the limit of recursion, 2917 * or -1 if no checking is to be done. 2918 */ 2919 public void setRecursionLimit(int limit) 2920 { 2921 m_stackGuard.setRecursionLimit(limit); 2922 } 2923 2924 /** 2925 * Get the SerializationHandler object. 2926 * 2927 * @return The current SerializationHandler, which may not 2928 * be the main result tree manager. 2929 */ 2930 public SerializationHandler getResultTreeHandler() 2931 { 2932 return m_serializationHandler; 2933 } 2934 2935 /** 2936 * Get the SerializationHandler object. 2937 * 2938 * @return The current SerializationHandler, which may not 2939 * be the main result tree manager. 2940 */ 2941 public SerializationHandler getSerializationHandler() 2942 { 2943 return m_serializationHandler; 2944 } 2945 2946 /** 2947 * Get the KeyManager object. 2948 * 2949 * @return A reference to the KeyManager object, which should 2950 * never be null. 2951 */ 2952 public KeyManager getKeyManager() 2953 { 2954 return m_keyManager; 2955 } 2956 2957 /** 2958 * Check to see if this is a recursive attribute definition. 2959 * 2960 * @param attrSet A non-null ElemAttributeSet reference. 2961 * 2962 * @return true if the attribute set is recursive. 2963 */ 2964 public boolean isRecursiveAttrSet(ElemAttributeSet attrSet) 2965 { 2966 2967 if (null == m_attrSetStack) 2968 { 2969 m_attrSetStack = new Stack(); 2970 } 2971 2972 if (!m_attrSetStack.empty()) 2973 { 2974 int loc = m_attrSetStack.search(attrSet); 2975 2976 if (loc > -1) 2977 { 2978 return true; 2979 } 2980 } 2981 2982 return false; 2983 } 2984 2985 /** 2986 * Push an executing attribute set, so we can check for 2987 * recursive attribute definitions. 2988 * 2989 * @param attrSet A non-null ElemAttributeSet reference. 2990 */ 2991 public void pushElemAttributeSet(ElemAttributeSet attrSet) 2992 { 2993 m_attrSetStack.push(attrSet); 2994 } 2995 2996 /** 2997 * Pop the current executing attribute set. 2998 */ 2999 public void popElemAttributeSet() 3000 { 3001 m_attrSetStack.pop(); 3002 } 3003 3004 /** 3005 * Get the table of counters, for optimized xsl:number support. 3006 * 3007 * @return The CountersTable, never null. 3008 */ 3009 public CountersTable getCountersTable() 3010 { 3011 3012 if (null == m_countersTable) 3013 m_countersTable = new CountersTable(); 3014 3015 return m_countersTable; 3016 } 3017 3018 /** 3019 * Tell if the current template rule is null, i.e. if we are 3020 * directly within an apply-templates. Used for xsl:apply-imports. 3021 * 3022 * @return True if the current template rule is null. 3023 */ 3024 public boolean currentTemplateRuleIsNull() 3025 { 3026 return ((!m_currentTemplateRuleIsNull.isEmpty()) 3027 && (m_currentTemplateRuleIsNull.peek() == true)); 3028 } 3029 3030 /** 3031 * Push true if the current template rule is null, false 3032 * otherwise. 3033 * 3034 * @param b True if the we are executing an xsl:for-each 3035 * (or xsl:call-template?). 3036 */ 3037 public void pushCurrentTemplateRuleIsNull(boolean b) 3038 { 3039 m_currentTemplateRuleIsNull.push(b); 3040 } 3041 3042 /** 3043 * Push true if the current template rule is null, false 3044 * otherwise. 3045 */ 3046 public void popCurrentTemplateRuleIsNull() 3047 { 3048 m_currentTemplateRuleIsNull.pop(); 3049 } 3050 3051 /** 3052 * Push a funcion result for the currently active EXSLT 3053 * <code>func:function</code>. 3054 * 3055 * @param val the result of executing an EXSLT 3056 * <code>func:result</code> instruction for the current 3057 * <code>func:function</code>. 3058 */ 3059 public void pushCurrentFuncResult(Object val) { 3060 m_currentFuncResult.push(val); 3061 } 3062 3063 /** 3064 * Pops the result of the currently active EXSLT <code>func:function</code>. 3065 * 3066 * @return the value of the <code>func:function</code> 3067 */ 3068 public Object popCurrentFuncResult() { 3069 return m_currentFuncResult.pop(); 3070 } 3071 3072 /** 3073 * Determines whether an EXSLT <code>func:result</code> instruction has been 3074 * executed for the currently active EXSLT <code>func:function</code>. 3075 * 3076 * @return <code>true</code> if and only if a <code>func:result</code> 3077 * instruction has been executed 3078 */ 3079 public boolean currentFuncResultSeen() { 3080 return !m_currentFuncResult.empty() 3081 && m_currentFuncResult.peek() != null; 3082 } 3083 3084 /** 3085 * Return the message manager. 3086 * 3087 * @return The message manager, never null. 3088 */ 3089 public MsgMgr getMsgMgr() 3090 { 3091 3092 if (null == m_msgMgr) 3093 m_msgMgr = new MsgMgr(this); 3094 3095 return m_msgMgr; 3096 } 3097 3098 /** 3099 * Set the error event listener. 3100 * 3101 * @param listener The new error listener. 3102 * @throws IllegalArgumentException if 3103 */ 3104 public void setErrorListener(ErrorListener listener) 3105 throws IllegalArgumentException 3106 { 3107 3108 synchronized (m_reentryGuard) 3109 { 3110 if (listener == null) 3111 throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_NULL_ERROR_HANDLER, null)); //"Null error handler"); 3112 3113 m_errorHandler = listener; 3114 } 3115 } 3116 3117 /** 3118 * Get the current error event handler. 3119 * 3120 * @return The current error handler, which should never be null. 3121 */ 3122 public ErrorListener getErrorListener() 3123 { 3124 return m_errorHandler; 3125 } 3126 3127 /** 3128 * Get an instance of the trace manager for this transformation. 3129 * This object can be used to set trace listeners on various 3130 * events during the transformation. 3131 * 3132 * @return A reference to the TraceManager, never null. 3133 */ 3134 public TraceManager getTraceManager() 3135 { 3136 return m_traceManager; 3137 } 3138 3139 /** 3140 * Look up the value of a feature. 3141 * 3142 * <p>The feature name is any fully-qualified URI. It is 3143 * possible for an TransformerFactory to recognize a feature name but 3144 * to be unable to return its value; this is especially true 3145 * in the case of an adapter for a SAX1 Parser, which has 3146 * no way of knowing whether the underlying parser is 3147 * validating, for example.</p> 3148 * 3149 * <h3>Open issues:</h3> 3150 * <dl> 3151 * <dt><h4>Should getFeature be changed to hasFeature?</h4></dt> 3152 * <dd>Keith Visco writes: Should getFeature be changed to hasFeature? 3153 * It returns a boolean which indicated whether the "state" 3154 * of feature is "true or false". I assume this means whether 3155 * or not a feature is supported? I know SAX is using "getFeature", 3156 * but to me "hasFeature" is cleaner.</dd> 3157 * </dl> 3158 * 3159 * @param name The feature name, which is a fully-qualified 3160 * URI. 3161 * @return The current state of the feature (true or false). 3162 * @throws org.xml.sax.SAXNotRecognizedException When the 3163 * TransformerFactory does not recognize the feature name. 3164 * @throws org.xml.sax.SAXNotSupportedException When the 3165 * TransformerFactory recognizes the feature name but 3166 * cannot determine its value at this time. 3167 * 3168 * @throws SAXNotRecognizedException 3169 * @throws SAXNotSupportedException 3170 */ 3171 public boolean getFeature(String name) 3172 throws SAXNotRecognizedException, SAXNotSupportedException 3173 { 3174 3175 if ("http://xml.org/trax/features/sax/input".equals(name)) 3176 return true; 3177 else if ("http://xml.org/trax/features/dom/input".equals(name)) 3178 return true; 3179 3180 throw new SAXNotRecognizedException(name); 3181 } 3182 3183 // %TODO% Doc 3184 3185 /** 3186 * NEEDSDOC Method getMode 3187 * 3188 * 3189 * NEEDSDOC (getMode) @return 3190 */ 3191 public QName getMode() 3192 { 3193 return m_modes.isEmpty() ? null : (QName) m_modes.peek(); 3194 } 3195 3196 // %TODO% Doc 3197 3198 /** 3199 * NEEDSDOC Method pushMode 3200 * 3201 * 3202 * NEEDSDOC @param mode 3203 */ 3204 public void pushMode(QName mode) 3205 { 3206 m_modes.push(mode); 3207 } 3208 3209 // %TODO% Doc 3210 3211 /** 3212 * NEEDSDOC Method popMode 3213 * 3214 */ 3215 public void popMode() 3216 { 3217 m_modes.pop(); 3218 } 3219 3220 /** 3221 * Called by SourceTreeHandler to start the transformation 3222 * in a separate thread 3223 * 3224 * NEEDSDOC @param priority 3225 */ 3226 public void runTransformThread(int priority) 3227 { 3228 3229 // used in SourceTreeHandler 3230 Thread t = ThreadControllerWrapper.runThread(this, priority); 3231 this.setTransformThread(t); 3232 } 3233 3234 /** 3235 * Called by this.transform() if isParserEventsOnMain()==false. 3236 * Similar with runTransformThread(), but no priority is set 3237 * and setTransformThread is not set. 3238 */ 3239 public void runTransformThread() 3240 { 3241 ThreadControllerWrapper.runThread(this, -1); 3242 } 3243 3244 /** 3245 * Called by CoRoutineSAXParser. Launches the CoroutineSAXParser 3246 * in a thread, and prepares it to invoke the parser from that thread 3247 * upon request. 3248 * 3249 */ 3250 public static void runTransformThread(Runnable runnable) 3251 { 3252 ThreadControllerWrapper.runThread(runnable, -1); 3253 } 3254 3255 /** 3256 * Used by SourceTreeHandler to wait until the transform 3257 * completes 3258 * 3259 * @throws SAXException 3260 */ 3261 public void waitTransformThread() throws SAXException 3262 { 3263 3264 // This is called to make sure the task is done. 3265 // It is possible that the thread has been reused - 3266 // but for a different transformation. ( what if we 3267 // recycle the transformer ? Not a problem since this is 3268 // still in use. ) 3269 Thread transformThread = this.getTransformThread(); 3270 3271 if (null != transformThread) 3272 { 3273 try 3274 { 3275 ThreadControllerWrapper.waitThread(transformThread, this); 3276 3277 if (!this.hasTransformThreadErrorCatcher()) 3278 { 3279 Exception e = this.getExceptionThrown(); 3280 3281 if (null != e) 3282 { 3283 e.printStackTrace(); 3284 throw new org.xml.sax.SAXException(e); 3285 } 3286 } 3287 3288 this.setTransformThread(null); 3289 } 3290 catch (InterruptedException ie){} 3291 } 3292 } 3293 3294 /** 3295 * Get the exception thrown by the secondary thread (normally 3296 * the transform thread). 3297 * 3298 * @return The thrown exception, or null if no exception was 3299 * thrown. 3300 */ 3301 public Exception getExceptionThrown() 3302 { 3303 return m_exceptionThrown; 3304 } 3305 3306 /** 3307 * Set the exception thrown by the secondary thread (normally 3308 * the transform thread). 3309 * 3310 * @param e The thrown exception, or null if no exception was 3311 * thrown. 3312 */ 3313 public void setExceptionThrown(Exception e) 3314 { 3315 m_exceptionThrown = e; 3316 } 3317 3318 /** 3319 * This is just a way to set the document for run(). 3320 * 3321 * @param doc A non-null reference to the root of the 3322 * tree to be transformed. 3323 */ 3324 public void setSourceTreeDocForThread(int doc) 3325 { 3326 m_doc = doc; 3327 } 3328 3329 /** 3330 * Set the input source for the source tree, which is needed if the 3331 * parse thread is not the main thread, in order for the parse 3332 * thread's run method to get to the input source. 3333 * 3334 * @param source The input source for the source tree. 3335 */ 3336 public void setXMLSource(Source source) 3337 { 3338 m_xmlSource = source; 3339 } 3340 3341 /** 3342 * Tell if the transform method is completed. 3343 * 3344 * @return True if transformNode has completed, or 3345 * an exception was thrown. 3346 */ 3347 public boolean isTransformDone() 3348 { 3349 3350 synchronized (this) 3351 { 3352 return m_isTransformDone; 3353 } 3354 } 3355 3356 /** 3357 * Set if the transform method is completed. 3358 * 3359 * @param done True if transformNode has completed, or 3360 * an exception was thrown. 3361 */ 3362 public void setIsTransformDone(boolean done) 3363 { 3364 3365 synchronized (this) 3366 { 3367 m_isTransformDone = done; 3368 } 3369 } 3370 3371 /** 3372 * From a secondary thread, post the exception, so that 3373 * it can be picked up from the main thread. 3374 * 3375 * @param e The exception that was thrown. 3376 */ 3377 void postExceptionFromThread(Exception e) 3378 { 3379 3380 // Commented out in response to problem reported by Nicola Brown <Nicola.Brown@jacobsrimell.com> 3381 // if(m_reportInPostExceptionFromThread) 3382 // { 3383 // // Consider re-throwing the exception if this flag is set. 3384 // e.printStackTrace(); 3385 // } 3386 // %REVIEW Need DTM equivelent? 3387 // if (m_inputContentHandler instanceof SourceTreeHandler) 3388 // { 3389 // SourceTreeHandler sth = (SourceTreeHandler) m_inputContentHandler; 3390 // 3391 // sth.setExceptionThrown(e); 3392 // } 3393 // ContentHandler ch = getContentHandler(); 3394 3395 // if(ch instanceof SourceTreeHandler) 3396 // { 3397 // SourceTreeHandler sth = (SourceTreeHandler) ch; 3398 // ((TransformerImpl)(sth.getTransformer())).postExceptionFromThread(e); 3399 // } 3400 m_isTransformDone = true; 3401 m_exceptionThrown = e; 3402 ; // should have already been reported via the error handler? 3403 3404 synchronized (this) 3405 { 3406 3407 // See message from me on 3/27/2001 to Patrick Moore. 3408 // String msg = e.getMessage(); 3409 // System.out.println(e.getMessage()); 3410 // Is this really needed? -sb 3411 notifyAll(); 3412 3413 // if (null == msg) 3414 // { 3415 // 3416 // // m_throwNewError = false; 3417 // e.printStackTrace(); 3418 // } 3419 // throw new org.apache.xml.utils.WrappedRuntimeException(e); 3420 } 3421 } 3422 3423 /** 3424 * Run the transform thread. 3425 */ 3426 public void run() 3427 { 3428 3429 m_hasBeenReset = false; 3430 3431 try 3432 { 3433 3434 // int n = ((SourceTreeHandler)getInputContentHandler()).getDTMRoot(); 3435 // transformNode(n); 3436 try 3437 { 3438 m_isTransformDone = false; 3439 3440 // Should no longer be needed... 3441 // if(m_inputContentHandler instanceof TransformerHandlerImpl) 3442 // { 3443 // TransformerHandlerImpl thi = (TransformerHandlerImpl)m_inputContentHandler; 3444 // thi.waitForInitialEvents(); 3445 // } 3446 3447 transformNode(m_doc); 3448 3449 } 3450 catch (Exception e) 3451 { 3452 // e.printStackTrace(); 3453 3454 // Strange that the other catch won't catch this... 3455 if (null != m_transformThread) 3456 postExceptionFromThread(e); // Assume we're on the main thread 3457 else 3458 throw new RuntimeException(e.getMessage()); 3459 } 3460 finally 3461 { 3462 m_isTransformDone = true; 3463 3464 if (m_inputContentHandler instanceof TransformerHandlerImpl) 3465 { 3466 ((TransformerHandlerImpl) m_inputContentHandler).clearCoRoutine(); 3467 } 3468 3469 // synchronized (this) 3470 // { 3471 // notifyAll(); 3472 // } 3473 } 3474 } 3475 catch (Exception e) 3476 { 3477 3478 // e.printStackTrace(); 3479 if (null != m_transformThread) 3480 postExceptionFromThread(e); 3481 else 3482 throw new RuntimeException(e.getMessage()); // Assume we're on the main thread. 3483 } 3484 } 3485 3486 // Fragment re-execution interfaces for a tool. 3487 3488 /** 3489 * This will get a snapshot of the current executing context 3490 * 3491 * 3492 * @return TransformSnapshot object, snapshot of executing context 3493 * @deprecated This is an internal tooling API that nobody seems to be using 3494 */ 3495 public TransformSnapshot getSnapshot() 3496 { 3497 return new TransformSnapshotImpl(this); 3498 } 3499 3500 /** 3501 * This will execute the following XSLT instructions 3502 * from the snapshot point, after the stylesheet execution 3503 * context has been reset from the snapshot point. 3504 * 3505 * @param ts The snapshot of where to start execution 3506 * 3507 * @throws TransformerException 3508 * @deprecated This is an internal tooling API that nobody seems to be using 3509 */ 3510 public void executeFromSnapshot(TransformSnapshot ts) 3511 throws TransformerException 3512 { 3513 3514 ElemTemplateElement template = getMatchedTemplate(); 3515 int child = getMatchedNode(); 3516 3517 pushElemTemplateElement(template); //needed?? 3518 m_xcontext.pushCurrentNode(child); //needed?? 3519 this.executeChildTemplates(template, true); // getResultTreeHandler()); 3520 } 3521 3522 /** 3523 * This will reset the stylesheet execution context 3524 * from the snapshot point. 3525 * 3526 * @param ts The snapshot of where to start execution 3527 * @deprecated This is an internal tooling API that nobody seems to be using 3528 */ 3529 public void resetToStylesheet(TransformSnapshot ts) 3530 { 3531 ((TransformSnapshotImpl) ts).apply(this); 3532 } 3533 3534 /** 3535 * NEEDSDOC Method stopTransformation 3536 * 3537 */ 3538 public void stopTransformation(){} 3539 3540 /** 3541 * Test whether whitespace-only text nodes are visible in the logical 3542 * view of <code>DTM</code>. Normally, this function 3543 * will be called by the implementation of <code>DTM</code>; 3544 * it is not normally called directly from 3545 * user code. 3546 * 3547 * @param elementHandle int Handle of the element. 3548 * @return one of NOTSTRIP, STRIP, or INHERIT. 3549 */ 3550 public short getShouldStripSpace(int elementHandle, DTM dtm) 3551 { 3552 3553 try 3554 { 3555 org.apache.xalan.templates.WhiteSpaceInfo info = 3556 m_stylesheetRoot.getWhiteSpaceInfo(m_xcontext, elementHandle, dtm); 3557 3558 if (null == info) 3559 { 3560 return DTMWSFilter.INHERIT; 3561 } 3562 else 3563 { 3564 3565 // System.out.println("getShouldStripSpace: "+info.getShouldStripSpace()); 3566 return info.getShouldStripSpace() 3567 ? DTMWSFilter.STRIP : DTMWSFilter.NOTSTRIP; 3568 } 3569 } 3570 catch (TransformerException se) 3571 { 3572 return DTMWSFilter.INHERIT; 3573 } 3574 } 3575 /** 3576 * Initializer method. 3577 * 3578 * @param transformer non-null transformer instance 3579 * @param realHandler Content Handler instance 3580 */ 3581 public void init(ToXMLSAXHandler h,Transformer transformer, ContentHandler realHandler) 3582 { 3583 h.setTransformer(transformer); 3584 h.setContentHandler(realHandler); 3585 } 3586 3587 public void setSerializationHandler(SerializationHandler xoh) 3588 { 3589 m_serializationHandler = xoh; 3590 } 3591 3592 3593 3594 /** 3595 * Fire off characters, cdate events. 3596 * @see org.apache.xml.serializer.SerializerTrace#fireGenerateEvent(int, char[], int, int) 3597 */ 3598 public void fireGenerateEvent( 3599 int eventType, 3600 char[] ch, 3601 int start, 3602 int length) { 3603 3604 GenerateEvent ge = new GenerateEvent(this, eventType, ch, start, length); 3605 m_traceManager.fireGenerateEvent(ge); 3606 } 3607 3608 /** 3609 * Fire off startElement, endElement events. 3610 * @see org.apache.xml.serializer.SerializerTrace#fireGenerateEvent(int, String, Attributes) 3611 */ 3612 public void fireGenerateEvent( 3613 int eventType, 3614 String name, 3615 Attributes atts) { 3616 3617 GenerateEvent ge = new GenerateEvent(this, eventType, name, atts); 3618 m_traceManager.fireGenerateEvent(ge); 3619 } 3620 3621 /** 3622 * Fire off processingInstruction events. 3623 * @see org.apache.xml.serializer.SerializerTrace#fireGenerateEvent(int, String, String) 3624 */ 3625 public void fireGenerateEvent(int eventType, String name, String data) { 3626 GenerateEvent ge = new GenerateEvent(this, eventType, name,data); 3627 m_traceManager.fireGenerateEvent(ge); 3628 } 3629 3630 /** 3631 * Fire off comment and entity ref events. 3632 * @see org.apache.xml.serializer.SerializerTrace#fireGenerateEvent(int, String) 3633 */ 3634 public void fireGenerateEvent(int eventType, String data) { 3635 GenerateEvent ge = new GenerateEvent(this, eventType, data); 3636 m_traceManager.fireGenerateEvent(ge); 3637 } 3638 3639 /** 3640 * Fire off startDocument, endDocument events. 3641 * @see org.apache.xml.serializer.SerializerTrace#fireGenerateEvent(int) 3642 */ 3643 public void fireGenerateEvent(int eventType) { 3644 GenerateEvent ge = new GenerateEvent(this, eventType); 3645 m_traceManager.fireGenerateEvent(ge); 3646 } 3647 3648 /** 3649 * @see org.apache.xml.serializer.SerializerTrace#hasTraceListeners() 3650 */ 3651 public boolean hasTraceListeners() { 3652 return m_traceManager.hasTraceListeners(); 3653 } 3654 3655 public boolean getDebug() { 3656 return m_debug; 3657 } 3658 3659 public void setDebug(boolean b) { 3660 m_debug = b; 3661 } 3662 3663 /** 3664 * @return Incremental flag 3665 */ 3666 public boolean getIncremental() { 3667 return m_incremental; 3668 } 3669 3670 /** 3671 * @return Optimization flag 3672 */ 3673 public boolean getOptimize() { 3674 return m_optimizer; 3675 } 3676 3677 /** 3678 * @return Source location flag 3679 */ 3680 public boolean getSource_location() { 3681 return m_source_location; 3682 } 3683 3684 } // end TransformerImpl class 3685