1. /*
  2. * The Apache Software License, Version 1.1
  3. *
  4. *
  5. * Copyright (c) 1999 The Apache Software Foundation. All rights
  6. * reserved.
  7. *
  8. * Redistribution and use in source and binary forms, with or without
  9. * modification, are permitted provided that the following conditions
  10. * are met:
  11. *
  12. * 1. Redistributions of source code must retain the above copyright
  13. * notice, this list of conditions and the following disclaimer.
  14. *
  15. * 2. Redistributions in binary form must reproduce the above copyright
  16. * notice, this list of conditions and the following disclaimer in
  17. * the documentation and/or other materials provided with the
  18. * distribution.
  19. *
  20. * 3. The end-user documentation included with the redistribution,
  21. * if any, must include the following acknowledgment:
  22. * "This product includes software developed by the
  23. * Apache Software Foundation (http://www.apache.org/)."
  24. * Alternately, this acknowledgment may appear in the software itself,
  25. * if and wherever such third-party acknowledgments normally appear.
  26. *
  27. * 4. The names "Xalan" and "Apache Software Foundation" must
  28. * not be used to endorse or promote products derived from this
  29. * software without prior written permission. For written
  30. * permission, please contact apache@apache.org.
  31. *
  32. * 5. Products derived from this software may not be called "Apache",
  33. * nor may "Apache" appear in their name, without prior written
  34. * permission of the Apache Software Foundation.
  35. *
  36. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  37. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  38. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  39. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  40. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  41. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  42. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  43. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  44. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  45. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  46. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  47. * SUCH DAMAGE.
  48. * ====================================================================
  49. *
  50. * This software consists of voluntary contributions made by many
  51. * individuals on behalf of the Apache Software Foundation and was
  52. * originally based on software copyright (c) 1999, Lotus
  53. * Development Corporation., http://www.lotus.com. For more
  54. * information on the Apache Software Foundation, please see
  55. * <http://www.apache.org/>.
  56. */
  57. package org.apache.xalan.transformer;
  58. // Java imports
  59. import java.io.File;
  60. import java.io.FileOutputStream;
  61. import java.io.IOException;
  62. import java.io.StringWriter;
  63. import java.io.UnsupportedEncodingException;
  64. import java.util.Enumeration;
  65. import java.util.NoSuchElementException;
  66. import java.util.Properties;
  67. import java.util.Stack;
  68. import java.util.StringTokenizer;
  69. import java.util.Vector;
  70. import javax.xml.parsers.DocumentBuilder;
  71. import javax.xml.transform.ErrorListener;
  72. import javax.xml.transform.OutputKeys;
  73. import javax.xml.transform.Result;
  74. import javax.xml.transform.Source;
  75. import javax.xml.transform.Transformer;
  76. import javax.xml.transform.TransformerException;
  77. import javax.xml.transform.URIResolver;
  78. import javax.xml.transform.dom.DOMResult;
  79. import javax.xml.transform.sax.SAXResult;
  80. import javax.xml.transform.stream.StreamResult;
  81. import org.apache.xalan.processor.TransformerFactoryImpl;
  82. import org.apache.xalan.res.XSLMessages;
  83. import org.apache.xalan.res.XSLTErrorResources;
  84. import org.apache.xalan.serialize.Method;
  85. import org.apache.xalan.serialize.Serializer;
  86. import org.apache.xalan.serialize.SerializerFactory;
  87. import org.apache.xalan.templates.AVT;
  88. import org.apache.xalan.templates.Constants;
  89. import org.apache.xalan.templates.ElemAttributeSet;
  90. import org.apache.xalan.templates.ElemForEach;
  91. import org.apache.xalan.templates.ElemSort;
  92. import org.apache.xalan.templates.ElemTemplate;
  93. import org.apache.xalan.templates.ElemTemplateElement;
  94. import org.apache.xalan.templates.ElemTextLiteral;
  95. import org.apache.xalan.templates.ElemVariable;
  96. import org.apache.xalan.templates.OutputProperties;
  97. import org.apache.xalan.templates.Stylesheet;
  98. import org.apache.xalan.templates.StylesheetComposed;
  99. import org.apache.xalan.templates.StylesheetRoot;
  100. import org.apache.xalan.templates.WhiteSpaceInfo;
  101. import org.apache.xalan.templates.XUnresolvedVariable;
  102. import org.apache.xalan.trace.TraceManager;
  103. import org.apache.xml.dtm.DTM;
  104. import org.apache.xml.dtm.DTMIterator;
  105. import org.apache.xml.dtm.DTMManager;
  106. import org.apache.xml.dtm.DTMWSFilter;
  107. import org.apache.xml.utils.BoolStack;
  108. import org.apache.xml.utils.DOMBuilder;
  109. import org.apache.xml.utils.NodeVector;
  110. import org.apache.xml.utils.ObjectPool;
  111. import org.apache.xml.utils.ObjectStack;
  112. import org.apache.xml.utils.QName;
  113. import org.apache.xml.utils.SAXSourceLocator;
  114. import org.apache.xml.utils.WrappedRuntimeException;
  115. import org.apache.xpath.Arg;
  116. import org.apache.xpath.DOMHelper;
  117. import org.apache.xpath.VariableStack;
  118. import org.apache.xpath.XPathContext;
  119. import org.apache.xpath.objects.XObject;
  120. import org.w3c.dom.Document;
  121. import org.w3c.dom.DocumentFragment;
  122. import org.w3c.dom.Node;
  123. import org.w3c.dom.Text;
  124. import org.xml.sax.ContentHandler;
  125. import org.xml.sax.SAXException;
  126. import org.xml.sax.SAXNotRecognizedException;
  127. import org.xml.sax.SAXNotSupportedException;
  128. import org.xml.sax.SAXParseException;
  129. import org.xml.sax.ext.DeclHandler;
  130. import org.xml.sax.ext.LexicalHandler;
  131. //dml
  132. import org.apache.xpath.ExtensionsProvider;
  133. import org.apache.xalan.extensions.ExtensionsTable;
  134. /**
  135. * <meta name="usage" content="advanced"/>
  136. * This class implements the
  137. * {@link javax.xml.transform.Transformer} interface, and is the core
  138. * representation of the transformation execution.</p>
  139. */
  140. public class TransformerImpl extends Transformer
  141. implements Runnable, DTMWSFilter, ExtensionsProvider
  142. {
  143. // Synch object to gaurd against setting values from the TrAX interface
  144. // or reentry while the transform is going on.
  145. /** NEEDSDOC Field m_reentryGuard */
  146. private Boolean m_reentryGuard = new Boolean(true);
  147. /**
  148. * This is null unless we own the stream.
  149. */
  150. private java.io.FileOutputStream m_outputStream = null;
  151. /**
  152. * True if the parser events should be on the main thread,
  153. * false if not. Experemental. Can not be set right now.
  154. */
  155. private boolean m_parserEventsOnMain = true;
  156. /** The thread that the transformer is running on. */
  157. private Thread m_transformThread;
  158. /** The base URL of the source tree. */
  159. private String m_urlOfSource = null;
  160. /** The Result object at the start of the transform, if any. */
  161. private Result m_outputTarget = null;
  162. /**
  163. * The output format object set by the user. May be null.
  164. */
  165. private OutputProperties m_outputFormat;
  166. /** The output serializer */
  167. private Serializer m_serializer;
  168. /**
  169. * The content handler for the source input tree.
  170. */
  171. ContentHandler m_inputContentHandler;
  172. /**
  173. * The content handler for the result tree.
  174. */
  175. private ContentHandler m_outputContentHandler = null;
  176. // /*
  177. // * Use member variable to store param variables as they're
  178. // * being created, use member variable so we don't
  179. // * have to create a new vector every time.
  180. // */
  181. // private Vector m_newVars = new Vector();
  182. /** The JAXP Document Builder, mainly to create Result Tree Fragments. */
  183. DocumentBuilder m_docBuilder = null;
  184. /**
  185. * A pool of ResultTreeHandlers, for serialization of a subtree to text.
  186. * Please note that each of these also holds onto a Text Serializer.
  187. */
  188. private ObjectPool m_textResultHandlerObjectPool =
  189. new ObjectPool("org.apache.xalan.transformer.ResultTreeHandler");
  190. /**
  191. * Related to m_textResultHandlerObjectPool, this is a pool of
  192. * StringWriters, which are passed to the Text Serializers.
  193. * (I'm not sure if this is really needed any more. -sb)
  194. */
  195. private ObjectPool m_stringWriterObjectPool =
  196. new ObjectPool("java.io.StringWriter");
  197. /**
  198. * A static text format object, which can be used over and
  199. * over to create the text serializers.
  200. */
  201. private OutputProperties m_textformat = new OutputProperties(Method.Text);
  202. // Commenteded out in response to problem reported by
  203. // Nicola Brown <Nicola.Brown@jacobsrimell.com>
  204. // /**
  205. // * Flag to let us know if an exception should be reported inside the
  206. // * postExceptionFromThread method. This is needed if the transform is
  207. // * being generated from SAX events, and thus there is no central place
  208. // * to report the exception from. (An exception is usually picked up in
  209. // * the main thread from the transform thread in {@link #transform(Source source)}
  210. // * from {@link #getExceptionThrown()}. )
  211. // */
  212. // private boolean m_reportInPostExceptionFromThread = false;
  213. /**
  214. * A node vector used as a stack to track the current
  215. * ElemTemplateElement. Needed for the
  216. * org.apache.xalan.transformer.TransformState interface,
  217. * so a tool can discover the calling template. Note the use of an array
  218. * for this limits the recursion depth to 4K.
  219. */
  220. ObjectStack m_currentTemplateElements
  221. = new ObjectStack(XPathContext.RECURSIONLIMIT);
  222. /** The top of the currentTemplateElements stack. */
  223. //int m_currentTemplateElementsTop = 0;
  224. /**
  225. * A node vector used as a stack to track the current
  226. * ElemTemplate that was matched.
  227. * Needed for the
  228. * org.apache.xalan.transformer.TransformState interface,
  229. * so a tool can discover the matched template
  230. */
  231. Stack m_currentMatchTemplates = new Stack();
  232. /**
  233. * A node vector used as a stack to track the current
  234. * node that was matched.
  235. * Needed for the
  236. * org.apache.xalan.transformer.TransformState interface,
  237. * so a tool can discover the matched
  238. * node.
  239. */
  240. NodeVector m_currentMatchedNodes = new NodeVector();
  241. /**
  242. * The root of a linked set of stylesheets.
  243. */
  244. private StylesheetRoot m_stylesheetRoot = null;
  245. /**
  246. * If this is set to true, do not warn about pattern
  247. * match conflicts.
  248. */
  249. private boolean m_quietConflictWarnings = true;
  250. /**
  251. * The liason to the XML parser, so the XSL processor
  252. * can handle included files, and the like, and do the
  253. * initial parse of the XSL document.
  254. */
  255. private XPathContext m_xcontext;
  256. /**
  257. * Object to guard agains infinite recursion when
  258. * doing queries.
  259. */
  260. private StackGuard m_stackGuard;
  261. /**
  262. * Output handler to bottleneck SAX events.
  263. */
  264. private ResultTreeHandler m_resultTreeHandler;
  265. /** The key manager, which manages xsl:keys. */
  266. private KeyManager m_keyManager = new KeyManager();
  267. /**
  268. * Stack for the purposes of flagging infinite recursion with
  269. * attribute sets.
  270. */
  271. Stack m_attrSetStack = null;
  272. /**
  273. * The table of counters for xsl:number support.
  274. * @see ElemNumber
  275. */
  276. CountersTable m_countersTable = null;
  277. /**
  278. * Is > 0 when we're processing a for-each.
  279. */
  280. BoolStack m_currentTemplateRuleIsNull = new BoolStack();
  281. /**
  282. * The message manager, which manages error messages, warning
  283. * messages, and other types of message events.
  284. */
  285. private MsgMgr m_msgMgr;
  286. /**
  287. * This is a compile-time flag to turn off calling
  288. * of trace listeners. Set this to false for optimization purposes.
  289. */
  290. public static boolean S_DEBUG = false;
  291. /**
  292. * The SAX error handler, where errors and warnings are sent.
  293. */
  294. private ErrorListener m_errorHandler =
  295. new org.apache.xml.utils.DefaultErrorHandler();
  296. /**
  297. * The trace manager.
  298. */
  299. private TraceManager m_traceManager = new TraceManager(this);
  300. /**
  301. * If the transform thread throws an exception, the exception needs to
  302. * be stashed away so that the main thread can pass it on to the
  303. * client.
  304. */
  305. private Exception m_exceptionThrown = null;
  306. /**
  307. * The InputSource for the source tree, which is needed if the
  308. * parse thread is not the main thread, in order for the parse
  309. * thread's run method to get to the input source.
  310. * (Delete this if reversing threads is outlawed. -sb)
  311. */
  312. private Source m_xmlSource;
  313. /**
  314. * This is needed for support of setSourceTreeDocForThread(Node doc),
  315. * which must be called in order for the transform thread's run
  316. * method to obtain the root of the source tree to be transformed.
  317. */
  318. private int m_doc;
  319. /**
  320. * If the the transform is on the secondary thread, we
  321. * need to know when it is done, so we can return.
  322. */
  323. private boolean m_isTransformDone = false;
  324. /** Flag to to tell if the tranformer needs to be reset. */
  325. private boolean m_hasBeenReset = false;
  326. /** NEEDSDOC Field m_shouldReset */
  327. private boolean m_shouldReset = true;
  328. /**
  329. * NEEDSDOC Method setShouldReset
  330. *
  331. *
  332. * NEEDSDOC @param shouldReset
  333. */
  334. public void setShouldReset(boolean shouldReset)
  335. {
  336. m_shouldReset = shouldReset;
  337. }
  338. /**
  339. * A stack of current template modes.
  340. */
  341. private Stack m_modes = new Stack();
  342. //==========================================================
  343. // SECTION: Constructor
  344. //==========================================================
  345. /**
  346. * Construct a TransformerImpl.
  347. *
  348. * @param stylesheet The root of the stylesheet tree.
  349. */
  350. public TransformerImpl(StylesheetRoot stylesheet)
  351. // throws javax.xml.transform.TransformerException
  352. {
  353. setStylesheet(stylesheet);
  354. setXPathContext(new XPathContext(this));
  355. getXPathContext().setNamespaceContext(stylesheet);
  356. m_stackGuard = new StackGuard(this);
  357. }
  358. // ================ ExtensionsTable ===================
  359. /**
  360. * The table of ExtensionHandlers.
  361. */
  362. private ExtensionsTable m_extensionsTable = null;
  363. /**
  364. * Get the extensions table object.
  365. *
  366. * @return The extensions table.
  367. */
  368. public ExtensionsTable getExtensionsTable()
  369. {
  370. return m_extensionsTable;
  371. }
  372. /**
  373. * If the stylesheet contains extensions, set the extensions table object.
  374. *
  375. *
  376. * @param sroot The stylesheet.
  377. * @throws javax.xml.transform.TransformerException
  378. */
  379. void setExtensionsTable(StylesheetRoot sroot)
  380. throws javax.xml.transform.TransformerException
  381. {
  382. try
  383. {
  384. if (sroot.getExtensions() != null)
  385. m_extensionsTable = new ExtensionsTable(sroot);
  386. }
  387. catch (javax.xml.transform.TransformerException te)
  388. {te.printStackTrace();}
  389. }
  390. //== Implementation of the XPath ExtensionsProvider interface.
  391. public boolean functionAvailable(String ns, String funcName)
  392. throws javax.xml.transform.TransformerException
  393. {
  394. return getExtensionsTable().functionAvailable(ns, funcName);
  395. }
  396. public boolean elementAvailable(String ns, String elemName)
  397. throws javax.xml.transform.TransformerException
  398. {
  399. return getExtensionsTable().elementAvailable(ns, elemName);
  400. }
  401. public Object extFunction(String ns, String funcName,
  402. Vector argVec, Object methodKey)
  403. throws javax.xml.transform.TransformerException
  404. {//System.out.println("TransImpl.extFunction() " + ns + " " + funcName +" " + getExtensionsTable());
  405. return getExtensionsTable().extFunction(ns, funcName,
  406. argVec, methodKey,
  407. getXPathContext().getExpressionContext());
  408. }
  409. //=========================
  410. /**
  411. * Reset the state. This needs to be called after a process() call
  412. * is invoked, if the processor is to be used again.
  413. */
  414. public void reset()
  415. {
  416. if (!m_hasBeenReset && m_shouldReset)
  417. {
  418. m_hasBeenReset = true;
  419. if (this.m_outputStream != null)
  420. {
  421. try
  422. {
  423. m_outputStream.close();
  424. }
  425. catch (java.io.IOException ioe){}
  426. }
  427. m_outputStream = null;
  428. // I need to look more carefully at which of these really
  429. // needs to be reset.
  430. m_countersTable = null;
  431. m_xcontext.reset();
  432. m_xcontext.getVarStack().reset();
  433. resetUserParameters();
  434. m_currentTemplateElements.removeAllElements();
  435. m_currentMatchTemplates.removeAllElements();
  436. m_currentMatchedNodes.removeAllElements();
  437. m_resultTreeHandler = null;
  438. m_outputTarget = null;
  439. m_keyManager = new KeyManager();
  440. m_attrSetStack = null;
  441. m_countersTable = null;
  442. m_currentTemplateRuleIsNull = new BoolStack();
  443. m_xmlSource = null;
  444. m_doc = DTM.NULL;
  445. m_isTransformDone = false;
  446. m_transformThread = null;
  447. // m_inputContentHandler = null;
  448. // For now, reset the document cache each time.
  449. m_xcontext.getSourceTreeManager().reset();
  450. }
  451. // m_reportInPostExceptionFromThread = false;
  452. }
  453. /**
  454. * <code>getProperty</code> returns the current setting of the
  455. * property described by the <code>property</code> argument.
  456. *
  457. * %REVIEW% Obsolete now that source_location is handled in the TransformerFactory?
  458. *
  459. * @param property a <code>String</code> value
  460. * @return a <code>boolean</code> value
  461. */
  462. public boolean getProperty(String property)
  463. {
  464. return false;
  465. }
  466. /**
  467. * Set a runtime property for this <code>TransformerImpl</code>.
  468. *
  469. * %REVIEW% Obsolete now that source_location is handled in the TransformerFactory?
  470. *
  471. * @param property a <code>String</code> value
  472. * @param value an <code>Object</code> value
  473. */
  474. public void setProperty(String property, Object value)
  475. {
  476. }
  477. // ========= Transformer Interface Implementation ==========
  478. /**
  479. * <meta name="usage" content="experimental"/>
  480. * Get true if the parser events should be on the main thread,
  481. * false if not. Experimental. Can not be set right now.
  482. *
  483. * @return true if the parser events should be on the main thread,
  484. * false if not.
  485. */
  486. public boolean isParserEventsOnMain()
  487. {
  488. return m_parserEventsOnMain;
  489. }
  490. /**
  491. * <meta name="usage" content="internal"/>
  492. * Get the thread that the transform process is on.
  493. *
  494. * @return The thread that the transform process is on, or null.
  495. */
  496. public Thread getTransformThread()
  497. {
  498. return m_transformThread;
  499. }
  500. /**
  501. * <meta name="usage" content="internal"/>
  502. * Get the thread that the transform process is on.
  503. *
  504. * @param t The transform thread, may be null.
  505. */
  506. public void setTransformThread(Thread t)
  507. {
  508. m_transformThread = t;
  509. }
  510. /** NEEDSDOC Field m_hasTransformThreadErrorCatcher */
  511. private boolean m_hasTransformThreadErrorCatcher = false;
  512. /**
  513. * Return true if the transform was initiated from the transform method,
  514. * otherwise it was probably done from a pure parse events.
  515. *
  516. * NEEDSDOC ($objectName$) @return
  517. */
  518. public boolean hasTransformThreadErrorCatcher()
  519. {
  520. return m_hasTransformThreadErrorCatcher;
  521. }
  522. /**
  523. * Process the source tree to SAX parse events.
  524. * @param source The input for the source tree.
  525. *
  526. * @throws TransformerException
  527. */
  528. public void transform(Source source) throws TransformerException
  529. {
  530. transform(source, true);
  531. }
  532. /**
  533. * Process the source tree to SAX parse events.
  534. * @param source The input for the source tree.
  535. * @param shouldRelease Flag indicating whether to release DTMManager.
  536. *
  537. * @throws TransformerException
  538. */
  539. public void transform(Source source, boolean shouldRelease) throws TransformerException
  540. {
  541. try
  542. {
  543. // Patch for bugzilla #13863. If we don't reset the namespaceContext
  544. // then we will get a NullPointerException if transformer is reused
  545. // (for stylesheets that use xsl:key). Not sure if this should go
  546. // here or in reset(). -is
  547. if(getXPathContext().getNamespaceContext() == null){
  548. getXPathContext().setNamespaceContext(getStylesheet());
  549. }
  550. String base = source.getSystemId();
  551. // If no systemID of the source, use the base of the stylesheet.
  552. if(null == base)
  553. {
  554. base = m_stylesheetRoot.getBaseIdentifier();
  555. }
  556. // As a last resort, use the current user dir.
  557. if(null == base)
  558. {
  559. String currentDir = "";
  560. try {
  561. currentDir = System.getProperty("user.dir");
  562. }
  563. catch (SecurityException se) {}// user.dir not accessible from applet
  564. if (currentDir.startsWith(java.io.File.separator))
  565. base = "file://" + currentDir;
  566. else
  567. base = "file:///" + currentDir;
  568. base = base + java.io.File.separatorChar
  569. + source.getClass().getName();
  570. }
  571. setBaseURLOfSource(base);
  572. DTMManager mgr = m_xcontext.getDTMManager();
  573. DTM dtm = mgr.getDTM(source, false, this, true, true);
  574. dtm.setDocumentBaseURI(base);
  575. boolean hardDelete = true; // %REVIEW% I have to think about this. -sb
  576. try
  577. {
  578. // NOTE: This will work because this is _NOT_ a shared DTM, and thus has
  579. // only a single Document node. If it could ever be an RTF or other
  580. // shared DTM, look at dtm.getDocumentRoot(nodeHandle).
  581. this.transformNode(dtm.getDocument());
  582. }
  583. finally
  584. {
  585. if (shouldRelease)
  586. mgr.release(dtm, hardDelete);
  587. }
  588. // Kick off the parse. When the ContentHandler gets
  589. // the startDocument event, it will call transformNode( node ).
  590. // reader.parse( xmlSource );
  591. // This has to be done to catch exceptions thrown from
  592. // the transform thread spawned by the STree handler.
  593. Exception e = getExceptionThrown();
  594. if (null != e)
  595. {
  596. if (e instanceof javax.xml.transform.TransformerException)
  597. {
  598. throw (javax.xml.transform.TransformerException) e;
  599. }
  600. else if (e instanceof org.apache.xml.utils.WrappedRuntimeException)
  601. {
  602. fatalError(
  603. ((org.apache.xml.utils.WrappedRuntimeException) e).getException());
  604. }
  605. else
  606. {
  607. throw new javax.xml.transform.TransformerException(e);
  608. }
  609. }
  610. else if (null != m_resultTreeHandler)
  611. {
  612. m_resultTreeHandler.endDocument();
  613. }
  614. }
  615. catch (org.apache.xml.utils.WrappedRuntimeException wre)
  616. {
  617. Throwable throwable = wre.getException();
  618. while (throwable
  619. instanceof org.apache.xml.utils.WrappedRuntimeException)
  620. {
  621. throwable =
  622. ((org.apache.xml.utils.WrappedRuntimeException) throwable).getException();
  623. }
  624. fatalError(throwable);
  625. }
  626. // Patch attributed to David Eisenberg <david@catcode.com>
  627. catch (org.xml.sax.SAXParseException spe)
  628. {
  629. fatalError(spe);
  630. }
  631. catch (org.xml.sax.SAXException se)
  632. {
  633. m_errorHandler.fatalError(new TransformerException(se));
  634. }
  635. finally
  636. {
  637. m_hasTransformThreadErrorCatcher = false;
  638. // This looks to be redundent to the one done in TransformNode.
  639. reset();
  640. }
  641. }
  642. private void fatalError(Throwable throwable) throws TransformerException
  643. {
  644. if (throwable instanceof org.xml.sax.SAXParseException)
  645. m_errorHandler.fatalError(new TransformerException(throwable.getMessage(),new SAXSourceLocator((org.xml.sax.SAXParseException)throwable)));
  646. else
  647. m_errorHandler.fatalError(new TransformerException(throwable));
  648. }
  649. /**
  650. * Get the base URL of the source.
  651. *
  652. * @return The base URL of the source tree, or null.
  653. */
  654. public String getBaseURLOfSource()
  655. {
  656. return m_urlOfSource;
  657. }
  658. /**
  659. * Get the base URL of the source.
  660. *
  661. *
  662. * NEEDSDOC @param base
  663. * @return The base URL of the source tree, or null.
  664. */
  665. public void setBaseURLOfSource(String base)
  666. {
  667. m_urlOfSource = base;
  668. }
  669. /**
  670. * Get the original output target.
  671. *
  672. * @return The Result object used to kick of the transform or null.
  673. */
  674. public Result getOutputTarget()
  675. {
  676. return m_outputTarget;
  677. }
  678. /**
  679. * Set the original output target. This is useful when using a SAX transform and
  680. * supplying a ContentHandler or when the URI of the output target should
  681. * not be the same as the systemID of the original output target.
  682. *
  683. *
  684. * NEEDSDOC @param outputTarget
  685. */
  686. public void setOutputTarget(Result outputTarget)
  687. {
  688. m_outputTarget = outputTarget;
  689. }
  690. /**
  691. * Get an output property that is in effect for the
  692. * transformation. The property specified may be a property
  693. * that was set with setOutputProperty, or it may be a
  694. * property specified in the stylesheet.
  695. *
  696. * @param name A non-null String that specifies an output
  697. * property name, which may be namespace qualified.
  698. *
  699. * NEEDSDOC @param qnameString
  700. *
  701. * @return The string value of the output property, or null
  702. * if no property was found.
  703. *
  704. * @throws IllegalArgumentException If the property is not supported.
  705. *
  706. * @see javax.xml.transform.OutputKeys
  707. */
  708. public String getOutputProperty(String qnameString)
  709. throws IllegalArgumentException
  710. {
  711. String value = null;
  712. OutputProperties props = getOutputFormat();
  713. value = props.getProperty(qnameString);
  714. if (null == value)
  715. {
  716. if (!props.isLegalPropertyKey(qnameString))
  717. throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_OUTPUT_PROPERTY_NOT_RECOGNIZED, new Object[]{qnameString})); //"output property not recognized: "
  718. //+ qnameString);
  719. }
  720. return value;
  721. }
  722. /**
  723. * Get the value of a property, without using the default properties. This
  724. * can be used to test if a property has been explicitly set by the stylesheet
  725. * or user.
  726. *
  727. * @param name The property name, which is a fully-qualified URI.
  728. *
  729. * NEEDSDOC @param qnameString
  730. *
  731. * @return The value of the property, or null if not found.
  732. *
  733. * @throws IllegalArgumentException If the property is not supported,
  734. * and is not namespaced.
  735. */
  736. public String getOutputPropertyNoDefault(String qnameString)
  737. throws IllegalArgumentException
  738. {
  739. String value = null;
  740. OutputProperties props = getOutputFormat();
  741. value = (String) props.getProperties().get(qnameString);
  742. if (null == value)
  743. {
  744. if (!props.isLegalPropertyKey(qnameString))
  745. throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_OUTPUT_PROPERTY_NOT_RECOGNIZED, new Object[]{qnameString})); //"output property not recognized: "
  746. // + qnameString);
  747. }
  748. return value;
  749. }
  750. /**
  751. * Set the value of a property. Recognized properties are:
  752. *
  753. * <p>"http://xml.apache.org/xslt/sourcebase" - the base URL for the
  754. * source, which is needed when pure SAX ContentHandler transformation
  755. * is to be done.</p>
  756. *
  757. * @param name The property name, which is a fully-qualified URI.
  758. * @param value The requested value for the property.
  759. * @throws IllegalArgumentException if the property name is not legal.
  760. */
  761. public void setOutputProperty(String name, String value)
  762. throws IllegalArgumentException
  763. {
  764. synchronized (m_reentryGuard)
  765. {
  766. // Get the output format that was set by the user, otherwise get the
  767. // output format from the stylesheet.
  768. if (null == m_outputFormat)
  769. {
  770. m_outputFormat =
  771. (OutputProperties) getStylesheet().getOutputComposed().clone();
  772. }
  773. if (!m_outputFormat.isLegalPropertyKey(name))
  774. throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_OUTPUT_PROPERTY_NOT_RECOGNIZED, new Object[]{name})); //"output property not recognized: "
  775. //+ name);
  776. m_outputFormat.setProperty(name, value);
  777. }
  778. }
  779. /**
  780. * Set the output properties for the transformation. These
  781. * properties will override properties set in the templates
  782. * with xsl:output.
  783. *
  784. * <p>If argument to this function is null, any properties
  785. * previously set will be removed.</p>
  786. *
  787. * @param oformat A set of output properties that will be
  788. * used to override any of the same properties in effect
  789. * for the transformation.
  790. *
  791. * @see javax.xml.transform.OutputKeys
  792. * @see java.util.Properties
  793. *
  794. * @throws IllegalArgumentException if any of the argument keys are not
  795. * recognized and are not namespace qualified.
  796. */
  797. public void setOutputProperties(Properties oformat)
  798. throws IllegalArgumentException
  799. {
  800. synchronized (m_reentryGuard)
  801. {
  802. if (null != oformat)
  803. {
  804. // See if an *explicit* method was set.
  805. String method = (String) oformat.get(OutputKeys.METHOD);
  806. if (null != method)
  807. m_outputFormat = new OutputProperties(method);
  808. else if(m_outputFormat==null)
  809. m_outputFormat = new OutputProperties();
  810. }
  811. if (null != oformat)
  812. {
  813. m_outputFormat.copyFrom(oformat);
  814. }
  815. // copyFrom does not set properties that have been already set, so
  816. // this must be called after, which is a bit in the reverse from
  817. // what one might think.
  818. m_outputFormat.copyFrom(m_stylesheetRoot.getOutputProperties());
  819. }
  820. }
  821. /**
  822. * Get a copy of the output properties for the transformation. These
  823. * properties will override properties set in the templates
  824. * with xsl:output.
  825. *
  826. * <p>Note that mutation of the Properties object returned will not
  827. * effect the properties that the transformation contains.</p>
  828. *
  829. * @returns A copy of the set of output properties in effect
  830. * for the next transformation.
  831. *
  832. * NEEDSDOC ($objectName$) @return
  833. */
  834. public Properties getOutputProperties()
  835. {
  836. return (Properties) getOutputFormat().getProperties().clone();
  837. }
  838. /**
  839. * Create a result ContentHandler from a Result object, based
  840. * on the current OutputProperties.
  841. *
  842. * @param outputTarget Where the transform result should go,
  843. * should not be null.
  844. *
  845. * @return A valid ContentHandler that will create the
  846. * result tree when it is fed SAX events.
  847. *
  848. * @throws TransformerException
  849. */
  850. public ContentHandler createResultContentHandler(Result outputTarget)
  851. throws TransformerException
  852. {
  853. return createResultContentHandler(outputTarget, getOutputFormat());
  854. }
  855. /**
  856. * Create a ContentHandler from a Result object and an OutputProperties.
  857. *
  858. * @param outputTarget Where the transform result should go,
  859. * should not be null.
  860. * @param format The OutputProperties object that will contain
  861. * instructions on how to serialize the output.
  862. *
  863. * @return A valid ContentHandler that will create the
  864. * result tree when it is fed SAX events.
  865. *
  866. * @throws TransformerException
  867. */
  868. public ContentHandler createResultContentHandler(
  869. Result outputTarget, OutputProperties format)
  870. throws TransformerException
  871. {
  872. ContentHandler handler = null;
  873. // If the Result object contains a Node, then create
  874. // a ContentHandler that will add nodes to the input node.
  875. org.w3c.dom.Node outputNode = null;
  876. if (outputTarget instanceof DOMResult)
  877. {
  878. outputNode = ((DOMResult) outputTarget).getNode();
  879. org.w3c.dom.Document doc;
  880. short type;
  881. if (null != outputNode)
  882. {
  883. type = outputNode.getNodeType();
  884. doc = (org.w3c.dom.Node.DOCUMENT_NODE == type)
  885. ? (org.w3c.dom.Document) outputNode
  886. : outputNode.getOwnerDocument();
  887. }
  888. else
  889. {
  890. doc = org.apache.xpath.DOMHelper.createDocument();
  891. outputNode = doc;
  892. type = outputNode.getNodeType();
  893. ((DOMResult) outputTarget).setNode(outputNode);
  894. }
  895. handler =
  896. (org.w3c.dom.Node.DOCUMENT_FRAGMENT_NODE == type)
  897. ? new DOMBuilder(doc, (org.w3c.dom.DocumentFragment) outputNode)
  898. : new DOMBuilder(doc, outputNode);
  899. }
  900. else if (outputTarget instanceof SAXResult)
  901. {
  902. handler = ((SAXResult) outputTarget).getHandler();
  903. if (null == handler)
  904. throw new IllegalArgumentException(
  905. "handler can not be null for a SAXResult");
  906. }
  907. // Otherwise, create a ContentHandler that will serialize the
  908. // result tree to either a stream or a writer.
  909. else if (outputTarget instanceof StreamResult)
  910. {
  911. StreamResult sresult = (StreamResult) outputTarget;
  912. String method = format.getProperty(OutputKeys.METHOD);
  913. try
  914. {
  915. Serializer serializer =
  916. SerializerFactory.getSerializer(format.getProperties());
  917. if (null != sresult.getWriter())
  918. serializer.setWriter(sresult.getWriter());
  919. else if (null != sresult.getOutputStream())
  920. serializer.setOutputStream(sresult.getOutputStream());
  921. else if (null != sresult.getSystemId())
  922. {
  923. String fileURL = sresult.getSystemId();
  924. if (fileURL.startsWith("file:///"))
  925. {
  926. if (fileURL.substring(8).indexOf(":") >0)
  927. fileURL = fileURL.substring(8);
  928. else
  929. fileURL = fileURL.substring(7);
  930. }
  931. m_outputStream = new java.io.FileOutputStream(fileURL);
  932. serializer.setOutputStream(m_outputStream);
  933. }
  934. else
  935. throw new TransformerException(XSLMessages.createMessage(XSLTErrorResources.ER_NO_OUTPUT_SPECIFIED, null)); //"No output specified!");
  936. handler = serializer.asContentHandler();
  937. this.setSerializer(serializer);
  938. }
  939. catch (UnsupportedEncodingException uee)
  940. {
  941. throw new TransformerException(uee);
  942. }
  943. catch (IOException ioe)
  944. {
  945. throw new TransformerException(ioe);
  946. }
  947. }
  948. else
  949. {
  950. 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 "
  951. //+ outputTarget.getClass().getName()
  952. //+ "!");
  953. }
  954. return handler;
  955. }
  956. /**
  957. * Process the source tree to the output result.
  958. * @param xmlSource The input for the source tree.
  959. * @param outputTarget The output source target.
  960. *
  961. * @throws TransformerException
  962. */
  963. public void transform(Source xmlSource, Result outputTarget)
  964. throws TransformerException
  965. {
  966. transform(xmlSource, outputTarget, true);
  967. }
  968. /**
  969. * Process the source tree to the output result.
  970. * @param xmlSource The input for the source tree.
  971. * @param outputTarget The output source target.
  972. * @param shouldRelease Flag indicating whether to release DTMManager.
  973. *
  974. * @throws TransformerException
  975. */
  976. public void transform(Source xmlSource, Result outputTarget, boolean shouldRelease)
  977. throws TransformerException
  978. {
  979. synchronized (m_reentryGuard)
  980. {
  981. ContentHandler handler = createResultContentHandler(outputTarget);
  982. m_outputTarget = outputTarget;
  983. this.setContentHandler(handler);
  984. transform(xmlSource, shouldRelease);
  985. }
  986. }
  987. /**
  988. * Process the source node to the output result, if the
  989. * processor supports the "http://xml.org/trax/features/dom/input"
  990. * feature.
  991. * %REVIEW% Do we need a Node version of this?
  992. * @param node The input source node, which can be any valid DTM node.
  993. * @param outputTarget The output source target.
  994. *
  995. * @throws TransformerException
  996. */
  997. public void transformNode(int node, Result outputTarget)
  998. throws TransformerException
  999. {
  1000. ContentHandler handler = createResultContentHandler(outputTarget);
  1001. m_outputTarget = outputTarget;
  1002. this.setContentHandler(handler);
  1003. transformNode(node);
  1004. }
  1005. /**
  1006. * Process the source node to the output result, if the
  1007. * processor supports the "http://xml.org/trax/features/dom/input"
  1008. * feature.
  1009. * %REVIEW% Do we need a Node version of this?
  1010. * @param node The input source node, which can be any valid DTM node.
  1011. * @param outputTarget The output source target.
  1012. *
  1013. * @throws TransformerException
  1014. */
  1015. public void transformNode(int node) throws TransformerException
  1016. {
  1017. //dml
  1018. setExtensionsTable(getStylesheet());
  1019. // Make sure we're not writing to the same output content handler.
  1020. synchronized (m_outputContentHandler)
  1021. {
  1022. m_hasBeenReset = false;
  1023. XPathContext xctxt = getXPathContext();
  1024. DTM dtm = xctxt.getDTM(node);
  1025. try
  1026. {
  1027. pushGlobalVars(node);
  1028. // ==========
  1029. // Give the top-level templates a chance to pass information into
  1030. // the context (this is mainly for setting up tables for extensions).
  1031. StylesheetRoot stylesheet = this.getStylesheet();
  1032. int n = stylesheet.getGlobalImportCount();
  1033. for (int i = 0; i < n; i++)
  1034. {
  1035. StylesheetComposed imported = stylesheet.getGlobalImport(i);
  1036. int includedCount = imported.getIncludeCountComposed();
  1037. for (int j = -1; j < includedCount; j++)
  1038. {
  1039. Stylesheet included = imported.getIncludeComposed(j);
  1040. included.runtimeInit(this);
  1041. for (ElemTemplateElement child = included.getFirstChildElem();
  1042. child != null; child = child.getNextSiblingElem())
  1043. {
  1044. child.runtimeInit(this);
  1045. }
  1046. }
  1047. }
  1048. // ===========
  1049. // System.out.println("Calling applyTemplateToNode - "+Thread.currentThread().getName());
  1050. DTMIterator dtmIter = new org.apache.xpath.axes.SelfIteratorNoPredicate();
  1051. dtmIter.setRoot(node, xctxt);
  1052. xctxt.pushContextNodeList(dtmIter);
  1053. try
  1054. {
  1055. this.applyTemplateToNode(null, null, node);
  1056. }
  1057. finally
  1058. {
  1059. xctxt.popContextNodeList();
  1060. }
  1061. // m_stylesheetRoot.getStartRule().execute(this);
  1062. // System.out.println("Done with applyTemplateToNode - "+Thread.currentThread().getName());
  1063. if (null != m_resultTreeHandler)
  1064. {
  1065. m_resultTreeHandler.endDocument();
  1066. }
  1067. }
  1068. catch (Exception se)
  1069. {
  1070. // System.out.println(Thread.currentThread().getName()+" threw an exception! "
  1071. // +se.getMessage());
  1072. // If an exception was thrown, we need to make sure that any waiting
  1073. // handlers can terminate, which I guess is best done by sending
  1074. // an endDocument.
  1075. // SAXSourceLocator
  1076. while(se instanceof org.apache.xml.utils.WrappedRuntimeException)
  1077. {
  1078. Exception e = ((org.apache.xml.utils.WrappedRuntimeException)se).getException();
  1079. if(null != e)
  1080. se = e;
  1081. }
  1082. if (null != m_resultTreeHandler)
  1083. {
  1084. try
  1085. {
  1086. if(se instanceof org.xml.sax.SAXParseException)
  1087. m_resultTreeHandler.fatalError((org.xml.sax.SAXParseException)se);
  1088. else if(se instanceof TransformerException)
  1089. {
  1090. TransformerException te = ((TransformerException)se);
  1091. SAXSourceLocator sl = new SAXSourceLocator( te.getLocator() );
  1092. m_resultTreeHandler.fatalError(new org.xml.sax.SAXParseException(te.getMessage(), sl, te));
  1093. }
  1094. else
  1095. {
  1096. m_resultTreeHandler.fatalError(new org.xml.sax.SAXParseException(se.getMessage(), new SAXSourceLocator(), se));
  1097. }
  1098. }
  1099. catch (Exception e){}
  1100. }
  1101. if(se instanceof TransformerException)
  1102. {
  1103. m_errorHandler.fatalError((TransformerException)se);
  1104. }
  1105. else if(se instanceof org.xml.sax.SAXParseException)
  1106. {
  1107. m_errorHandler.fatalError(new TransformerException(se.getMessage(),
  1108. new SAXSourceLocator((org.xml.sax.SAXParseException)se),
  1109. se));
  1110. }
  1111. else
  1112. {
  1113. m_errorHandler.fatalError(new TransformerException(se));
  1114. }
  1115. }
  1116. finally
  1117. {
  1118. this.reset();
  1119. }
  1120. }
  1121. }
  1122. /**
  1123. * Get a SAX2 ContentHandler for the input.
  1124. *
  1125. * @return A valid ContentHandler, which should never be null, as
  1126. * long as getFeature("http://xml.org/trax/features/sax/input")
  1127. * returns true.
  1128. */
  1129. public ContentHandler getInputContentHandler()
  1130. {
  1131. return getInputContentHandler(false);
  1132. }
  1133. /**
  1134. * Get a SAX2 ContentHandler for the input.
  1135. *
  1136. * @param doDocFrag true if a DocumentFragment should be created as
  1137. * the root, rather than a Document.
  1138. *
  1139. * @return A valid ContentHandler, which should never be null, as
  1140. * long as getFeature("http://xml.org/trax/features/sax/input")
  1141. * returns true.
  1142. */
  1143. public ContentHandler getInputContentHandler(boolean doDocFrag)
  1144. {
  1145. if (null == m_inputContentHandler)
  1146. {
  1147. // if(null == m_urlOfSource && null != m_stylesheetRoot)
  1148. // m_urlOfSource = m_stylesheetRoot.getBaseIdentifier();
  1149. m_inputContentHandler = new TransformerHandlerImpl(this, doDocFrag,
  1150. m_urlOfSource);
  1151. }
  1152. return m_inputContentHandler;
  1153. }
  1154. /**
  1155. * Get a SAX2 DeclHandler for the input.
  1156. * @return A valid DeclHandler, which should never be null, as
  1157. * long as getFeature("http://xml.org/trax/features/sax/input")
  1158. * returns true.
  1159. */
  1160. public DeclHandler getInputDeclHandler()
  1161. {
  1162. if (m_inputContentHandler instanceof DeclHandler)
  1163. return (DeclHandler) m_inputContentHandler;
  1164. else
  1165. return null;
  1166. }
  1167. /**
  1168. * Get a SAX2 LexicalHandler for the input.
  1169. * @return A valid LexicalHandler, which should never be null, as
  1170. * long as getFeature("http://xml.org/trax/features/sax/input")
  1171. * returns true.
  1172. */
  1173. public LexicalHandler getInputLexicalHandler()
  1174. {
  1175. if (m_inputContentHandler instanceof LexicalHandler)
  1176. return (LexicalHandler) m_inputContentHandler;
  1177. else
  1178. return null;
  1179. }
  1180. /**
  1181. * Set the output properties for the transformation. These
  1182. * properties will override properties set in the templates
  1183. * with xsl:output.
  1184. *
  1185. * @param oformat A valid OutputProperties object (which will
  1186. * not be mutated), or null.
  1187. */
  1188. public void setOutputFormat(OutputProperties oformat)
  1189. {
  1190. m_outputFormat = oformat;
  1191. }
  1192. /**
  1193. * Get the output properties used for the transformation.
  1194. *
  1195. * @return the output format that was set by the user,
  1196. * otherwise the output format from the stylesheet.
  1197. */
  1198. public OutputProperties getOutputFormat()
  1199. {
  1200. // Get the output format that was set by the user, otherwise get the
  1201. // output format from the stylesheet.
  1202. OutputProperties format = (null == m_outputFormat)
  1203. ? getStylesheet().getOutputComposed()
  1204. : m_outputFormat;
  1205. return format;
  1206. }
  1207. /**
  1208. * <meta name="usage" content="internal"/>
  1209. * Get the current serializer in use, which may well not
  1210. * be the main serializer (for instance, this may well be
  1211. * a text serializer for string creation from templates).
  1212. *
  1213. * @return The current serializer, or null if there is none.
  1214. */
  1215. public Serializer getSerializer()
  1216. {
  1217. return m_serializer;
  1218. }
  1219. /**
  1220. * <meta name="usage" content="internal"/>
  1221. * Set the current serializer.
  1222. *
  1223. * @param s The current serializer, or null.
  1224. */
  1225. public void setSerializer(Serializer s)
  1226. {
  1227. m_serializer = s;
  1228. }
  1229. /**
  1230. * Set a parameter for the templates.
  1231. *
  1232. * @param name The name of the parameter.
  1233. * @param namespace The namespace of the parameter.
  1234. * @param value The value object. This can be any valid Java object
  1235. * -- it's up to the processor to provide the proper
  1236. * coersion to the object, or simply pass it on for use
  1237. * in extensions.
  1238. */
  1239. public void setParameter(String name, String namespace, Object value)
  1240. {
  1241. VariableStack varstack = getXPathContext().getVarStack();
  1242. QName qname = new QName(namespace, name);
  1243. XObject xobject = XObject.create(value, getXPathContext());
  1244. StylesheetRoot sroot = m_stylesheetRoot;
  1245. Vector vars = sroot.getVariablesAndParamsComposed();
  1246. int i = vars.size();
  1247. while (--i >= 0)
  1248. {
  1249. ElemVariable variable = (ElemVariable)vars.elementAt(i);
  1250. if(variable.getXSLToken() == Constants.ELEMNAME_PARAMVARIABLE &&
  1251. variable.getName().equals(qname))
  1252. {
  1253. varstack.setGlobalVariable(i, xobject);
  1254. }
  1255. }
  1256. }
  1257. /** NEEDSDOC Field m_userParams */
  1258. Vector m_userParams;
  1259. /**
  1260. * Set a parameter for the transformation.
  1261. *
  1262. * @param name The name of the parameter,
  1263. * which may have a namespace URI.
  1264. * @param value The value object. This can be any valid Java object
  1265. * -- it's up to the processor to provide the proper
  1266. * coersion to the object, or simply pass it on for use
  1267. * in extensions.
  1268. */
  1269. public void setParameter(String name, Object value)
  1270. {
  1271. StringTokenizer tokenizer = new StringTokenizer(name, "{}", false);
  1272. try
  1273. {
  1274. // The first string might be the namespace, or it might be
  1275. // the local name, if the namespace is null.
  1276. String s1 = tokenizer.nextToken();
  1277. String s2 = tokenizer.hasMoreTokens() ? tokenizer.nextToken() : null;
  1278. if (null == m_userParams)
  1279. m_userParams = new Vector();
  1280. if (null == s2)
  1281. {
  1282. replaceOrPushUserParam(new QName(s1), XObject.create(value, getXPathContext()));
  1283. setParameter(s1, null, value);
  1284. }
  1285. else
  1286. {
  1287. replaceOrPushUserParam(new QName(s1, s2), XObject.create(value, getXPathContext()));
  1288. setParameter(s2, s1, value);
  1289. }
  1290. }
  1291. catch (java.util.NoSuchElementException nsee)
  1292. {
  1293. // Should throw some sort of an error.
  1294. }
  1295. }
  1296. /**
  1297. * NEEDSDOC Method replaceOrPushUserParam
  1298. *
  1299. *
  1300. * NEEDSDOC @param qname
  1301. * NEEDSDOC @param xval
  1302. */
  1303. private void replaceOrPushUserParam(QName qname, XObject xval)
  1304. {
  1305. int n = m_userParams.size();
  1306. for (int i = n - 1; i >= 0; i--)
  1307. {
  1308. Arg arg = (Arg) m_userParams.elementAt(i);
  1309. if (arg.getQName().equals(qname))
  1310. {
  1311. m_userParams.setElementAt(new Arg(qname, xval, true), i);
  1312. return;
  1313. }
  1314. }
  1315. m_userParams.addElement(new Arg(qname, xval, true));
  1316. }
  1317. /**
  1318. * Get a parameter that was explicitly set with setParameter
  1319. * or setParameters.
  1320. *
  1321. *
  1322. * NEEDSDOC @param name
  1323. * @return A parameter that has been set with setParameter
  1324. * or setParameters,
  1325. * *not* all the xsl:params on the stylesheet (which require
  1326. * a transformation Source to be evaluated).
  1327. */
  1328. public Object getParameter(String name)
  1329. {
  1330. try
  1331. {
  1332. // VariableStack varstack = getXPathContext().getVarStack();
  1333. // The first string might be the namespace, or it might be
  1334. // the local name, if the namespace is null.
  1335. QName qname = QName.getQNameFromString(name);
  1336. if (null == m_userParams)
  1337. return null;
  1338. int n = m_userParams.size();
  1339. for (int i = n - 1; i >= 0; i--)
  1340. {
  1341. Arg arg = (Arg) m_userParams.elementAt(i);
  1342. if (arg.getQName().equals(qname))
  1343. {
  1344. return arg.getVal().object();
  1345. }
  1346. }
  1347. return null;
  1348. }
  1349. catch (java.util.NoSuchElementException nsee)
  1350. {
  1351. // Should throw some sort of an error.
  1352. return null;
  1353. }
  1354. }
  1355. /**
  1356. * Reset parameters that the user specified for the transformation.
  1357. * Called during transformer.reset() after we have cleared the
  1358. * variable stack. We need to make sure that user params are
  1359. * reset so that the transformer object can be reused.
  1360. */
  1361. private void resetUserParameters()
  1362. {
  1363. try
  1364. {
  1365. if (null == m_userParams)
  1366. return;
  1367. int n = m_userParams.size();
  1368. for (int i = n - 1; i >= 0; i--)
  1369. {
  1370. Arg arg = (Arg) m_userParams.elementAt(i);
  1371. QName name = arg.getQName();
  1372. // The first string might be the namespace, or it might be
  1373. // the local name, if the namespace is null.
  1374. String s1 = name.getNamespace();
  1375. String s2 = name.getLocalPart();
  1376. setParameter(s2, s1, arg.getVal().object());
  1377. }
  1378. }
  1379. catch (java.util.NoSuchElementException nsee)
  1380. {
  1381. // Should throw some sort of an error.
  1382. }
  1383. }
  1384. /**
  1385. * Set a bag of parameters for the transformation. Note that
  1386. * these will not be additive, they will replace the existing
  1387. * set of parameters.
  1388. *
  1389. * @param name The name of the parameter,
  1390. * which may have a namespace URI.
  1391. * @param value The value object. This can be any valid Java object
  1392. * -- it's up to the processor to provide the proper
  1393. * coersion to the object, or simply pass it on for use
  1394. * in extensions.
  1395. *
  1396. * NEEDSDOC @param params
  1397. */
  1398. public void setParameters(Properties params)
  1399. {
  1400. clearParameters();
  1401. Enumeration names = params.propertyNames();
  1402. while (names.hasMoreElements())
  1403. {
  1404. String name = params.getProperty((String) names.nextElement());
  1405. StringTokenizer tokenizer = new StringTokenizer(name, "{}", false);
  1406. try
  1407. {
  1408. // The first string might be the namespace, or it might be
  1409. // the local name, if the namespace is null.
  1410. String s1 = tokenizer.nextToken();
  1411. String s2 = tokenizer.hasMoreTokens() ? tokenizer.nextToken() : null;
  1412. if (null == s2)
  1413. setParameter(s1, null, params.getProperty(name));
  1414. else
  1415. setParameter(s2, s1, params.getProperty(name));
  1416. }
  1417. catch (java.util.NoSuchElementException nsee)
  1418. {
  1419. // Should throw some sort of an error.
  1420. }
  1421. }
  1422. }
  1423. /**
  1424. * Reset the parameters to a null list.
  1425. */
  1426. public void clearParameters()
  1427. {
  1428. synchronized (m_reentryGuard)
  1429. {
  1430. VariableStack varstack = new VariableStack();
  1431. m_xcontext.setVarStack(varstack);
  1432. m_userParams = null;
  1433. }
  1434. }
  1435. /**
  1436. * Internal -- push the global variables from the Stylesheet onto
  1437. * the context's runtime variable stack.
  1438. * <p>If we encounter a variable
  1439. * that is already defined in the variable stack, we ignore it. This
  1440. * is because the second variable definition will be at a lower import
  1441. * precedence. Presumably, global"variables at the same import precedence
  1442. * with the same name will have been caught during the recompose process.
  1443. * <p>However, if we encounter a parameter that is already defined in the
  1444. * variable stack, we need to see if this is a parameter whose value was
  1445. * supplied by a setParameter call. If so, we need to "receive" the one
  1446. * already in the stack, ignoring this one. If it is just an earlier
  1447. * xsl:param or xsl:variable definition, we ignore it using the same
  1448. * reasoning as explained above for the variable.
  1449. *
  1450. * @param contextNode The root of the source tree, can't be null.
  1451. *
  1452. * @throws TransformerException
  1453. */
  1454. protected void pushGlobalVars(int contextNode) throws TransformerException
  1455. {
  1456. XPathContext xctxt = m_xcontext;
  1457. VariableStack vs = xctxt.getVarStack();
  1458. StylesheetRoot sr = getStylesheet();
  1459. Vector vars = sr.getVariablesAndParamsComposed();
  1460. int i = vars.size();
  1461. vs.link(i);
  1462. while (--i >= 0)
  1463. {
  1464. ElemVariable v = (ElemVariable) vars.elementAt(i);
  1465. // XObject xobj = v.getValue(this, contextNode);
  1466. XObject xobj = new XUnresolvedVariable(v, contextNode, this,
  1467. vs.getStackFrame(), 0, true);
  1468. if(null == vs.elementAt(i))
  1469. vs.setGlobalVariable(i, xobj);
  1470. }
  1471. }
  1472. /**
  1473. * Set an object that will be used to resolve URIs used in
  1474. * document(), etc.
  1475. * @param resolver An object that implements the URIResolver interface,
  1476. * or null.
  1477. */
  1478. public void setURIResolver(URIResolver resolver)
  1479. {
  1480. synchronized (m_reentryGuard)
  1481. {
  1482. m_xcontext.getSourceTreeManager().setURIResolver(resolver);
  1483. }
  1484. }
  1485. /**
  1486. * Get an object that will be used to resolve URIs used in
  1487. * document(), etc.
  1488. *
  1489. * @return An object that implements the URIResolver interface,
  1490. * or null.
  1491. */
  1492. public URIResolver getURIResolver()
  1493. {
  1494. return m_xcontext.getSourceTreeManager().getURIResolver();
  1495. }
  1496. // ======== End Transformer Implementation ========
  1497. /**
  1498. * Set the content event handler.
  1499. *
  1500. * @param resolver The new content handler.
  1501. *
  1502. * NEEDSDOC @param handler
  1503. * @throws java.lang.NullPointerException If the handler
  1504. * is null.
  1505. * @see org.xml.sax.XMLReader#setContentHandler
  1506. */
  1507. public void setContentHandler(ContentHandler handler)
  1508. {
  1509. if (handler == null)
  1510. {
  1511. throw new NullPointerException(XSLMessages.createMessage(XSLTErrorResources.ER_NULL_CONTENT_HANDLER, null)); //"Null content handler");
  1512. }
  1513. else
  1514. {
  1515. m_outputContentHandler = handler;
  1516. if (null == m_resultTreeHandler)
  1517. m_resultTreeHandler = new ResultTreeHandler(this, handler);
  1518. else
  1519. m_resultTreeHandler.setContentHandler(handler);
  1520. }
  1521. }
  1522. /**
  1523. * Get the content event handler.
  1524. *
  1525. * @return The current content handler, or null if none was set.
  1526. * @see org.xml.sax.XMLReader#getContentHandler
  1527. */
  1528. public ContentHandler getContentHandler()
  1529. {
  1530. return m_outputContentHandler;
  1531. }
  1532. /**
  1533. * <meta name="usage" content="advanced"/>
  1534. * Given a stylesheet element, create a result tree fragment from it's
  1535. * contents. The fragment will be built within the shared RTF DTM system
  1536. * used as a variable stack.
  1537. * @param templateParent The template element that holds the fragment.
  1538. * @return the NodeHandle for the root node of the resulting RTF.
  1539. *
  1540. * @throws TransformerException
  1541. */
  1542. public int transformToRTF(ElemTemplateElement templateParent)
  1543. throws TransformerException
  1544. {
  1545. // Retrieve a DTM to contain the RTF. At this writing, this may be a
  1546. // multi-document DTM (SAX2RTFDTM).
  1547. DTM dtmFrag = m_xcontext.getRTFDTM();
  1548. return transformToRTF(templateParent,dtmFrag);
  1549. }
  1550. /**
  1551. * <meta name="usage" content="advanced"/>
  1552. * Given a stylesheet element, create a result tree fragment from it's
  1553. * contents. The fragment will also use the shared DTM system, but will
  1554. * obtain its space from the global variable pool rather than the dynamic
  1555. * variable stack. This allows late binding of XUnresolvedVariables without
  1556. * the risk that their content will be discarded when the variable stack
  1557. * is popped.
  1558. *
  1559. * @param templateParent The template element that holds the fragment.
  1560. * @return the NodeHandle for the root node of the resulting RTF.
  1561. *
  1562. * @throws TransformerException
  1563. */
  1564. public int transformToGlobalRTF(ElemTemplateElement templateParent)
  1565. throws TransformerException
  1566. {
  1567. // Retrieve a DTM to contain the RTF. At this writing, this may be a
  1568. // multi-document DTM (SAX2RTFDTM).
  1569. DTM dtmFrag = m_xcontext.getGlobalRTFDTM();
  1570. return transformToRTF(templateParent,dtmFrag);
  1571. }
  1572. /**
  1573. * <meta name="usage" content="advanced"/>
  1574. * Given a stylesheet element, create a result tree fragment from it's
  1575. * contents.
  1576. * @param templateParent The template element that holds the fragment.
  1577. * @param dtmFrag The DTM to write the RTF into
  1578. * @return the NodeHandle for the root node of the resulting RTF.
  1579. *
  1580. * @throws TransformerException
  1581. */
  1582. private int transformToRTF(ElemTemplateElement templateParent,DTM dtmFrag)
  1583. throws TransformerException
  1584. {
  1585. XPathContext xctxt = m_xcontext;
  1586. ContentHandler rtfHandler = dtmFrag.getContentHandler();
  1587. // Obtain the ResultTreeFrag's root node.
  1588. // NOTE: In SAX2RTFDTM, this value isn't available until after
  1589. // the startDocument has been issued, so assignment has been moved
  1590. // down a bit in the code.
  1591. int resultFragment; // not yet reliably = dtmFrag.getDocument();
  1592. // Save the current result tree handler.
  1593. ResultTreeHandler savedRTreeHandler = this.m_resultTreeHandler;
  1594. // And make a new handler for the RTF.
  1595. m_resultTreeHandler = new ResultTreeHandler(this, rtfHandler);
  1596. ResultTreeHandler rth = m_resultTreeHandler;
  1597. try
  1598. {
  1599. rth.startDocument();
  1600. // startDocument is "bottlenecked" in RTH. We need it acted upon immediately,
  1601. // to set the DTM's state as in-progress, so that if the xsl:variable's body causes
  1602. // further RTF activity we can keep that from bashing this DTM.
  1603. rth.flushPending();
  1604. try
  1605. {
  1606. // Do the transformation of the child elements.
  1607. executeChildTemplates(templateParent, true);
  1608. // Make sure everything is flushed!
  1609. rth.flushPending();
  1610. // Get the document ID. May not exist until the RTH has not only
  1611. // received, but flushed, the startDocument, and may be invalid
  1612. // again after the document has been closed (still debating that)
  1613. // ... so waiting until just before the end seems simplest/safest.
  1614. resultFragment = dtmFrag.getDocument();
  1615. }
  1616. finally
  1617. {
  1618. rth.endDocument();
  1619. }
  1620. }
  1621. catch (org.xml.sax.SAXException se)
  1622. {
  1623. throw new TransformerException(se);
  1624. }
  1625. finally
  1626. {
  1627. // Restore the previous result tree handler.
  1628. this.m_resultTreeHandler = savedRTreeHandler;
  1629. }
  1630. return resultFragment;
  1631. }
  1632. /**
  1633. * <meta name="usage" content="internal"/>
  1634. * Get the StringWriter pool, so that StringWriter
  1635. * objects may be reused.
  1636. *
  1637. * @return The string writer pool, not null.
  1638. */
  1639. public ObjectPool getStringWriterPool()
  1640. {
  1641. return m_stringWriterObjectPool;
  1642. }
  1643. /**
  1644. * <meta name="usage" content="advanced"/>
  1645. * Take the contents of a template element, process it, and
  1646. * convert it to a string.
  1647. *
  1648. * @param elem The parent element whose children will be output
  1649. * as a string.
  1650. * @param transformer The XSLT transformer instance.
  1651. * @param sourceNode The current source node context.
  1652. * @param mode The current xslt mode.
  1653. *
  1654. * @return The stringized result of executing the elements children.
  1655. *
  1656. * @throws TransformerException
  1657. */
  1658. public String transformToString(ElemTemplateElement elem)
  1659. throws TransformerException
  1660. {
  1661. ElemTemplateElement firstChild = elem.getFirstChildElem();
  1662. if(null == firstChild)
  1663. return "";
  1664. if(elem.hasTextLitOnly() && org.apache.xalan.processor.TransformerFactoryImpl.m_optimize)
  1665. {
  1666. return ((ElemTextLiteral)firstChild).getNodeValue();
  1667. }
  1668. // Save the current result tree handler.
  1669. ResultTreeHandler savedRTreeHandler = this.m_resultTreeHandler;
  1670. // Create a Serializer object that will handle the SAX events
  1671. // and build the ResultTreeFrag nodes.
  1672. StringWriter sw = (StringWriter) m_stringWriterObjectPool.getInstance();
  1673. m_resultTreeHandler =
  1674. (ResultTreeHandler) m_textResultHandlerObjectPool.getInstance();
  1675. Serializer serializer = m_resultTreeHandler.getSerializer();
  1676. try
  1677. {
  1678. if (null == serializer)
  1679. {
  1680. serializer =
  1681. SerializerFactory.getSerializer(m_textformat.getProperties());
  1682. m_resultTreeHandler.setSerializer(serializer);
  1683. serializer.setWriter(sw);
  1684. ContentHandler shandler = serializer.asContentHandler();
  1685. m_resultTreeHandler.init(this, shandler);
  1686. }
  1687. else
  1688. {
  1689. // Leave Commented. -sb
  1690. // serializer.setWriter(sw);
  1691. // serializer.setOutputFormat(m_textformat);
  1692. // ContentHandler shandler = serializer.asContentHandler();
  1693. // m_resultTreeHandler.setContentHandler(shandler);
  1694. }
  1695. }
  1696. catch (IOException ioe)
  1697. {
  1698. throw new TransformerException(ioe);
  1699. }
  1700. String result;
  1701. try
  1702. {
  1703. this.m_resultTreeHandler.startDocument();
  1704. // Do the transformation of the child elements.
  1705. executeChildTemplates(elem, true);
  1706. this.m_resultTreeHandler.endDocument();
  1707. result = sw.toString();
  1708. }
  1709. catch (org.xml.sax.SAXException se)
  1710. {
  1711. throw new TransformerException(se);
  1712. }
  1713. finally
  1714. {
  1715. sw.getBuffer().setLength(0);
  1716. try
  1717. {
  1718. sw.close();
  1719. }
  1720. catch (Exception ioe){}
  1721. m_stringWriterObjectPool.freeInstance(sw);
  1722. m_textResultHandlerObjectPool.freeInstance(m_resultTreeHandler);
  1723. m_resultTreeHandler.reset();
  1724. // Restore the previous result tree handler.
  1725. m_resultTreeHandler = savedRTreeHandler;
  1726. }
  1727. return result;
  1728. }
  1729. /**
  1730. * <meta name="usage" content="advanced"/>
  1731. * Given an element and mode, find the corresponding
  1732. * template and process the contents.
  1733. *
  1734. * @param xslInstruction The calling element.
  1735. * @param template The template to use if xsl:for-each, or null.
  1736. * @param child The source context node.
  1737. * @param mode The current mode, may be null.
  1738. * @throws TransformerException
  1739. * @return true if applied a template, false if not.
  1740. */
  1741. public boolean applyTemplateToNode(ElemTemplateElement xslInstruction, // xsl:apply-templates or xsl:for-each
  1742. ElemTemplate template, int child)
  1743. throws TransformerException
  1744. {
  1745. DTM dtm = m_xcontext.getDTM(child);
  1746. short nodeType = dtm.getNodeType(child);
  1747. boolean isDefaultTextRule = false;
  1748. boolean isApplyImports = false;
  1749. if (null == template)
  1750. {
  1751. int maxImportLevel, endImportLevel=0;
  1752. isApplyImports = ((xslInstruction == null)
  1753. ? false
  1754. : xslInstruction.getXSLToken()
  1755. == Constants.ELEMNAME_APPLY_IMPORTS);
  1756. if (isApplyImports)
  1757. {
  1758. maxImportLevel =
  1759. xslInstruction.getStylesheetComposed().getImportCountComposed() - 1;
  1760. endImportLevel =
  1761. xslInstruction.getStylesheetComposed().getEndImportCountComposed();
  1762. }
  1763. else
  1764. {
  1765. maxImportLevel = -1;
  1766. }
  1767. // If we're trying an xsl:apply-imports at the top level (ie there are no
  1768. // imported stylesheets), we need to indicate that there is no matching template.
  1769. // The above logic will calculate a maxImportLevel of -1 which indicates
  1770. // that we should find any template. This is because a value of -1 for
  1771. // maxImportLevel has a special meaning. But we don't want that.
  1772. // We want to match -no- templates. See bugzilla bug 1170.
  1773. if (isApplyImports && (maxImportLevel == -1))
  1774. {
  1775. template = null;
  1776. }
  1777. else
  1778. {
  1779. // Find the XSL template that is the best match for the
  1780. // element.
  1781. XPathContext xctxt = m_xcontext;
  1782. try
  1783. {
  1784. xctxt.pushNamespaceContext(xslInstruction);
  1785. QName mode = this.getMode();
  1786. if (isApplyImports)
  1787. template = m_stylesheetRoot.getTemplateComposed(xctxt, child, mode,
  1788. maxImportLevel, endImportLevel, m_quietConflictWarnings, dtm);
  1789. else
  1790. template = m_stylesheetRoot.getTemplateComposed(xctxt, child, mode,
  1791. m_quietConflictWarnings, dtm);
  1792. }
  1793. finally
  1794. {
  1795. xctxt.popNamespaceContext();
  1796. }
  1797. }
  1798. // If that didn't locate a node, fall back to a default template rule.
  1799. // See http://www.w3.org/TR/xslt#built-in-rule.
  1800. if (null == template)
  1801. {
  1802. switch (nodeType)
  1803. {
  1804. case DTM.DOCUMENT_FRAGMENT_NODE :
  1805. case DTM.ELEMENT_NODE :
  1806. template = m_stylesheetRoot.getDefaultRule();
  1807. break;
  1808. case DTM.CDATA_SECTION_NODE :
  1809. case DTM.TEXT_NODE :
  1810. case DTM.ATTRIBUTE_NODE :
  1811. template = m_stylesheetRoot.getDefaultTextRule();
  1812. isDefaultTextRule = true;
  1813. break;
  1814. case DTM.DOCUMENT_NODE :
  1815. template = m_stylesheetRoot.getDefaultRootRule();
  1816. break;
  1817. default :
  1818. // No default rules for processing instructions and the like.
  1819. return false;
  1820. }
  1821. }
  1822. }
  1823. // If we are processing the default text rule, then just clone
  1824. // the value directly to the result tree.
  1825. try
  1826. {
  1827. pushElemTemplateElement(template);
  1828. m_xcontext.pushCurrentNode(child);
  1829. pushPairCurrentMatched(template, child);
  1830. // Fix copy copy29 test.
  1831. if (!isApplyImports) {
  1832. DTMIterator cnl = new org.apache.xpath.NodeSetDTM(child, m_xcontext.getDTMManager());
  1833. m_xcontext.pushContextNodeList(cnl);
  1834. }
  1835. if (isDefaultTextRule)
  1836. {
  1837. switch (nodeType)
  1838. {
  1839. case DTM.CDATA_SECTION_NODE :
  1840. case DTM.TEXT_NODE :
  1841. ClonerToResultTree.cloneToResultTree(child, nodeType,
  1842. dtm, getResultTreeHandler(), false);
  1843. break;
  1844. case DTM.ATTRIBUTE_NODE :
  1845. dtm.dispatchCharactersEvents(child, getResultTreeHandler(), false);
  1846. break;
  1847. }
  1848. }
  1849. else
  1850. {
  1851. // Fire a trace event for the template.
  1852. if (TransformerImpl.S_DEBUG)
  1853. getTraceManager().fireTraceEvent(template);
  1854. // And execute the child templates.
  1855. // 9/11/00: If template has been compiled, hand off to it
  1856. // since much (most? all?) of the processing has been inlined.
  1857. // (It would be nice if there was a single entry point that
  1858. // worked for both... but the interpretive system works by
  1859. // having the Tranformer execute the children, while the
  1860. // compiled obviously has to run its own code. It's
  1861. // also unclear that "execute" is really the right name for
  1862. // that entry point.)
  1863. m_xcontext.setSAXLocator(template);
  1864. // m_xcontext.getVarStack().link();
  1865. m_xcontext.getVarStack().link(template.m_frameSize);
  1866. executeChildTemplates(template, true);
  1867. if (TransformerImpl.S_DEBUG)
  1868. getTraceManager().fireTraceEndEvent(template);
  1869. }
  1870. }
  1871. catch (org.xml.sax.SAXException se)
  1872. {
  1873. throw new TransformerException(se);
  1874. }
  1875. finally
  1876. {
  1877. m_xcontext.getVarStack().unlink();
  1878. m_xcontext.popCurrentNode();
  1879. if (!isApplyImports) {
  1880. m_xcontext.popContextNodeList();
  1881. popCurrentMatched();
  1882. }
  1883. popElemTemplateElement();
  1884. }
  1885. return true;
  1886. }
  1887. /**
  1888. * <meta name="usage" content="advanced"/>
  1889. * Execute each of the children of a template element. This method
  1890. * is only for extension use.
  1891. *
  1892. * @param elem The ElemTemplateElement that contains the children
  1893. * that should execute.
  1894. * @param sourceNode The current context node.
  1895. * NEEDSDOC @param context
  1896. * @param mode The current mode.
  1897. * @param handler The ContentHandler to where the result events
  1898. * should be fed.
  1899. *
  1900. * @throws TransformerException
  1901. */
  1902. public void executeChildTemplates(
  1903. ElemTemplateElement elem, org.w3c.dom.Node context, QName mode, ContentHandler handler)
  1904. throws TransformerException
  1905. {
  1906. XPathContext xctxt = m_xcontext;
  1907. try
  1908. {
  1909. if(null != mode)
  1910. pushMode(mode);
  1911. xctxt.pushCurrentNode(xctxt.getDTMHandleFromNode(context));
  1912. executeChildTemplates(elem, handler);
  1913. }
  1914. finally
  1915. {
  1916. xctxt.popCurrentNode();
  1917. // I'm not sure where or why this was here. It is clearly in
  1918. // error though, without a corresponding pushMode().
  1919. if (null != mode)
  1920. popMode();
  1921. }
  1922. }
  1923. /**
  1924. * <meta name="usage" content="advanced"/>
  1925. * Execute each of the children of a template element.
  1926. *
  1927. * @param elem The ElemTemplateElement that contains the children
  1928. * that should execute.
  1929. * @param handler The ContentHandler to where the result events
  1930. * should be fed.
  1931. *
  1932. * @throws TransformerException
  1933. */
  1934. public void executeChildTemplates(
  1935. ElemTemplateElement elem, ContentHandler handler)
  1936. throws TransformerException
  1937. {
  1938. ResultTreeHandler rth = this.getResultTreeHandler();
  1939. // These may well not be the same! In this case when calling
  1940. // the Redirect extension, it has already set the ContentHandler
  1941. // in the Transformer.
  1942. ContentHandler savedRTHHandler = rth.getContentHandler();
  1943. ContentHandler savedHandler = this.getContentHandler();
  1944. try
  1945. {
  1946. getResultTreeHandler().flushPending();
  1947. this.setContentHandler(handler);
  1948. // %REVIEW% Make sure current node is being pushed.
  1949. executeChildTemplates(elem, true);
  1950. }
  1951. catch (org.xml.sax.SAXException se)
  1952. {
  1953. throw new TransformerException(se);
  1954. }
  1955. finally
  1956. {
  1957. this.setContentHandler(savedHandler);
  1958. // This fixes a bug where the ResultTreeHandler's ContentHandler
  1959. // was being reset to the wrong ContentHandler.
  1960. rth.setContentHandler(savedRTHHandler);
  1961. }
  1962. }
  1963. /**
  1964. * <meta name="usage" content="advanced"/>
  1965. * Execute each of the children of a template element.
  1966. *
  1967. * @param transformer The XSLT transformer instance.
  1968. *
  1969. * @param elem The ElemTemplateElement that contains the children
  1970. * that should execute.
  1971. * @param sourceNode The current context node.
  1972. * @param mode The current mode.
  1973. * @param shouldAddAttrs true if xsl:attributes should be executed.
  1974. *
  1975. * @throws TransformerException
  1976. */
  1977. public void executeChildTemplates(
  1978. ElemTemplateElement elem, boolean shouldAddAttrs)
  1979. throws TransformerException
  1980. {
  1981. // Does this element have any children?
  1982. ElemTemplateElement t = elem.getFirstChildElem();
  1983. if (null == t)
  1984. return;
  1985. if(elem.hasTextLitOnly() && org.apache.xalan.processor.TransformerFactoryImpl.m_optimize)
  1986. {
  1987. char[] chars = ((ElemTextLiteral)t).getChars();
  1988. try
  1989. {
  1990. // Have to push stuff on for tooling...
  1991. this.pushElemTemplateElement(t);
  1992. m_resultTreeHandler.characters(chars, 0, chars.length);
  1993. }
  1994. catch(SAXException se)
  1995. {
  1996. throw new TransformerException(se);
  1997. }
  1998. finally
  1999. {
  2000. this.popElemTemplateElement();
  2001. }
  2002. return;
  2003. }
  2004. // // Check for infinite loops if we have to.
  2005. // boolean check = (m_stackGuard.m_recursionLimit > -1);
  2006. //
  2007. // if (check)
  2008. // getStackGuard().push(elem, xctxt.getCurrentNode());
  2009. XPathContext xctxt = m_xcontext;
  2010. xctxt.pushSAXLocatorNull();
  2011. int currentTemplateElementsTop = m_currentTemplateElements.size();
  2012. m_currentTemplateElements.push(null);
  2013. try
  2014. {
  2015. // Loop through the children of the template, calling execute on
  2016. // each of them.
  2017. for (; t != null; t = t.getNextSiblingElem())
  2018. {
  2019. if (!shouldAddAttrs
  2020. && t.getXSLToken() == Constants.ELEMNAME_ATTRIBUTE)
  2021. continue;
  2022. xctxt.setSAXLocator(t);
  2023. m_currentTemplateElements.setElementAt(t,currentTemplateElementsTop);
  2024. t.execute(this);
  2025. }
  2026. }
  2027. catch(RuntimeException re)
  2028. {
  2029. TransformerException te = new TransformerException(re);
  2030. te.setLocator(t);
  2031. throw te;
  2032. }
  2033. finally
  2034. {
  2035. m_currentTemplateElements.pop();
  2036. xctxt.popSAXLocator();
  2037. }
  2038. // Check for infinite loops if we have to
  2039. // if (check)
  2040. // getStackGuard().pop();
  2041. }
  2042. /**
  2043. * <meta name="usage" content="advanced"/>
  2044. * Get the keys for the xsl:sort elements.
  2045. * Note: Should this go into ElemForEach?
  2046. *
  2047. * @param foreach Valid ElemForEach element, not null.
  2048. * @param sourceNodeContext The current node context in the source tree,
  2049. * needed to evaluate the Attribute Value Templates.
  2050. *
  2051. * @return A Vector of NodeSortKeys, or null.
  2052. *
  2053. * @throws TransformerException
  2054. */
  2055. public Vector processSortKeys(ElemForEach foreach, int sourceNodeContext)
  2056. throws TransformerException
  2057. {
  2058. Vector keys = null;
  2059. XPathContext xctxt = m_xcontext;
  2060. int nElems = foreach.getSortElemCount();
  2061. if (nElems > 0)
  2062. keys = new Vector();
  2063. // March backwards, collecting the sort keys.
  2064. for (int i = 0; i < nElems; i++)
  2065. {
  2066. ElemSort sort = foreach.getSortElem(i);
  2067. if (TransformerImpl.S_DEBUG)
  2068. getTraceManager().fireTraceEvent(sort);
  2069. String langString =
  2070. (null != sort.getLang())
  2071. ? sort.getLang().evaluate(xctxt, sourceNodeContext, foreach) : null;
  2072. String dataTypeString = sort.getDataType().evaluate(xctxt,
  2073. sourceNodeContext, foreach);
  2074. if (dataTypeString.indexOf(":") >= 0)
  2075. System.out.println(
  2076. "TODO: Need to write the hooks for QNAME sort data type");
  2077. else if (!(dataTypeString.equalsIgnoreCase(Constants.ATTRVAL_DATATYPE_TEXT))
  2078. &&!(dataTypeString.equalsIgnoreCase(
  2079. Constants.ATTRVAL_DATATYPE_NUMBER)))
  2080. foreach.error(XSLTErrorResources.ER_ILLEGAL_ATTRIBUTE_VALUE,
  2081. new Object[]{ Constants.ATTRNAME_DATATYPE,
  2082. dataTypeString });
  2083. boolean treatAsNumbers =
  2084. ((null != dataTypeString) && dataTypeString.equals(
  2085. Constants.ATTRVAL_DATATYPE_NUMBER)) ? true : false;
  2086. String orderString = sort.getOrder().evaluate(xctxt, sourceNodeContext,
  2087. foreach);
  2088. if (!(orderString.equalsIgnoreCase(Constants.ATTRVAL_ORDER_ASCENDING))
  2089. &&!(orderString.equalsIgnoreCase(
  2090. Constants.ATTRVAL_ORDER_DESCENDING)))
  2091. foreach.error(XSLTErrorResources.ER_ILLEGAL_ATTRIBUTE_VALUE,
  2092. new Object[]{ Constants.ATTRNAME_ORDER,
  2093. orderString });
  2094. boolean descending =
  2095. ((null != orderString) && orderString.equals(
  2096. Constants.ATTRVAL_ORDER_DESCENDING)) ? true : false;
  2097. AVT caseOrder = sort.getCaseOrder();
  2098. boolean caseOrderUpper;
  2099. if (null != caseOrder)
  2100. {
  2101. String caseOrderString = caseOrder.evaluate(xctxt, sourceNodeContext,
  2102. foreach);
  2103. if (!(caseOrderString.equalsIgnoreCase(Constants.ATTRVAL_CASEORDER_UPPER))
  2104. &&!(caseOrderString.equalsIgnoreCase(
  2105. Constants.ATTRVAL_CASEORDER_LOWER)))
  2106. foreach.error(XSLTErrorResources.ER_ILLEGAL_ATTRIBUTE_VALUE,
  2107. new Object[]{ Constants.ATTRNAME_CASEORDER,
  2108. caseOrderString });
  2109. caseOrderUpper =
  2110. ((null != caseOrderString) && caseOrderString.equals(
  2111. Constants.ATTRVAL_CASEORDER_UPPER)) ? true : false;
  2112. }
  2113. else
  2114. {
  2115. caseOrderUpper = false;
  2116. }
  2117. keys.addElement(new NodeSortKey(this, sort.getSelect(), treatAsNumbers,
  2118. descending, langString, caseOrderUpper,
  2119. foreach));
  2120. if (TransformerImpl.S_DEBUG)
  2121. getTraceManager().fireTraceEndEvent(sort);
  2122. }
  2123. return keys;
  2124. }
  2125. //==========================================================
  2126. // SECTION: TransformState implementation
  2127. //==========================================================
  2128. /**
  2129. * Get the stack of ElemTemplateElements.
  2130. *
  2131. * @return A copy of stack that contains the xsl element instructions,
  2132. * the earliest called in index zero, and the latest called in index size()-1.
  2133. */
  2134. public Vector getElementCallstack()
  2135. {
  2136. Vector elems = new Vector();
  2137. int nStackSize = m_currentTemplateElements.size();
  2138. for(int i = 0; i < nStackSize; i++)
  2139. {
  2140. ElemTemplateElement elem = (ElemTemplateElement) m_currentTemplateElements.elementAt(i);
  2141. if(null != elem)
  2142. {
  2143. elems.addElement(elem);
  2144. }
  2145. }
  2146. return elems;
  2147. }
  2148. /**
  2149. * Get the count of how many elements are
  2150. * active.
  2151. * @return The number of active elements on
  2152. * the currentTemplateElements stack.
  2153. */
  2154. public int getCurrentTemplateElementsCount()
  2155. {
  2156. return m_currentTemplateElements.size();
  2157. }
  2158. /**
  2159. * Get the count of how many elements are
  2160. * active.
  2161. * @return The number of active elements on
  2162. * the currentTemplateElements stack.
  2163. */
  2164. public ObjectStack getCurrentTemplateElements()
  2165. {
  2166. return m_currentTemplateElements;
  2167. }
  2168. /**
  2169. * Push the current template element.
  2170. *
  2171. * @param elem The current ElemTemplateElement (may be null, and then
  2172. * set via setCurrentElement).
  2173. */
  2174. public void pushElemTemplateElement(ElemTemplateElement elem)
  2175. {
  2176. m_currentTemplateElements.push(elem);
  2177. }
  2178. /**
  2179. * Pop the current template element.
  2180. */
  2181. public void popElemTemplateElement()
  2182. {
  2183. m_currentTemplateElements.pop();
  2184. }
  2185. /**
  2186. * Set the top of the current template elements
  2187. * stack.
  2188. *
  2189. * @param e The current ElemTemplateElement about to
  2190. * be executed.
  2191. */
  2192. public void setCurrentElement(ElemTemplateElement e)
  2193. {
  2194. m_currentTemplateElements.setTop(e);
  2195. }
  2196. /**
  2197. * Retrieves the current ElemTemplateElement that is
  2198. * being executed.
  2199. *
  2200. * @return The current ElemTemplateElement that is executing,
  2201. * should not normally be null.
  2202. */
  2203. public ElemTemplateElement getCurrentElement()
  2204. {
  2205. return (m_currentTemplateElements.size() > 0) ?
  2206. (ElemTemplateElement) m_currentTemplateElements.peek() : null;
  2207. }
  2208. /**
  2209. * This method retrieves the current context node
  2210. * in the source tree.
  2211. *
  2212. * @return The current context node (should never be null?).
  2213. */
  2214. public int getCurrentNode()
  2215. {
  2216. return m_xcontext.getCurrentNode();
  2217. }
  2218. /**
  2219. * Get the call stack of xsl:template elements.
  2220. *
  2221. * @return A copy of stack that contains the xsl:template
  2222. * (ElemTemplate) instructions, the earliest called in index
  2223. * zero, and the latest called in index size()-1.
  2224. */
  2225. public Vector getTemplateCallstack()
  2226. {
  2227. Vector elems = new Vector();
  2228. int nStackSize = m_currentTemplateElements.size();
  2229. for(int i = 0; i < nStackSize; i++)
  2230. {
  2231. ElemTemplateElement elem = (ElemTemplateElement) m_currentTemplateElements.elementAt(i);
  2232. if(null != elem && (elem.getXSLToken() != Constants.ELEMNAME_TEMPLATE))
  2233. {
  2234. elems.addElement(elem);
  2235. }
  2236. }
  2237. return elems;
  2238. }
  2239. /**
  2240. * This method retrieves the xsl:template
  2241. * that is in effect, which may be a matched template
  2242. * or a named template.
  2243. *
  2244. * <p>Please note that the ElemTemplate returned may
  2245. * be a default template, and thus may not have a template
  2246. * defined in the stylesheet.</p>
  2247. *
  2248. * @return The current xsl:template, should not be null.
  2249. */
  2250. public ElemTemplate getCurrentTemplate()
  2251. {
  2252. ElemTemplateElement elem = getCurrentElement();
  2253. while ((null != elem)
  2254. && (elem.getXSLToken() != Constants.ELEMNAME_TEMPLATE))
  2255. {
  2256. elem = elem.getParentElem();
  2257. }
  2258. return (ElemTemplate) elem;
  2259. }
  2260. /**
  2261. * Push both the current xsl:template or xsl:for-each onto the
  2262. * stack, along with the child node that was matched.
  2263. * (Note: should this only be used for xsl:templates?? -sb)
  2264. *
  2265. * @param template xsl:template or xsl:for-each.
  2266. * @param child The child that was matched.
  2267. */
  2268. public void pushPairCurrentMatched(ElemTemplateElement template, int child)
  2269. {
  2270. m_currentMatchTemplates.push(template);
  2271. m_currentMatchedNodes.push(child);
  2272. }
  2273. /**
  2274. * Pop the elements that were pushed via pushPairCurrentMatched.
  2275. */
  2276. public void popCurrentMatched()
  2277. {
  2278. m_currentMatchTemplates.pop();
  2279. m_currentMatchedNodes.pop();
  2280. }
  2281. /**
  2282. * This method retrieves the xsl:template
  2283. * that was matched. Note that this may not be
  2284. * the same thing as the current template (which
  2285. * may be from getCurrentElement()), since a named
  2286. * template may be in effect.
  2287. *
  2288. * @return The pushed template that was pushed via pushPairCurrentMatched.
  2289. */
  2290. public ElemTemplate getMatchedTemplate()
  2291. {
  2292. return (ElemTemplate) m_currentMatchTemplates.peek();
  2293. }
  2294. /**
  2295. * Retrieves the node in the source tree that matched
  2296. * the template obtained via getMatchedTemplate().
  2297. *
  2298. * @return The matched node that corresponds to the
  2299. * match attribute of the current xsl:template.
  2300. */
  2301. public int getMatchedNode()
  2302. {
  2303. return m_currentMatchedNodes.peepTail();
  2304. }
  2305. /**
  2306. * Get the current context node list.
  2307. *
  2308. * @return A reset clone of the context node list.
  2309. */
  2310. public DTMIterator getContextNodeList()
  2311. {
  2312. try
  2313. {
  2314. DTMIterator cnl = m_xcontext.getContextNodeList();
  2315. return (cnl == null) ? null : (DTMIterator) cnl.cloneWithReset();
  2316. }
  2317. catch (CloneNotSupportedException cnse)
  2318. {
  2319. // should never happen.
  2320. return null;
  2321. }
  2322. }
  2323. /**
  2324. * Get the TrAX Transformer object in effect.
  2325. *
  2326. * @return This object.
  2327. */
  2328. public Transformer getTransformer()
  2329. {
  2330. return this;
  2331. }
  2332. //==========================================================
  2333. // SECTION: Accessor Functions
  2334. //==========================================================
  2335. /**
  2336. * Set the stylesheet for this processor. If this is set, then the
  2337. * process calls that take only the input .xml will use
  2338. * this instead of looking for a stylesheet PI. Also,
  2339. * setting the stylesheet is needed if you are going
  2340. * to use the processor as a SAX ContentHandler.
  2341. *
  2342. * @param stylesheetRoot A non-null StylesheetRoot object,
  2343. * or null if you wish to clear the stylesheet reference.
  2344. */
  2345. public void setStylesheet(StylesheetRoot stylesheetRoot)
  2346. {
  2347. m_stylesheetRoot = stylesheetRoot;
  2348. }
  2349. /**
  2350. * Get the current stylesheet for this processor.
  2351. *
  2352. * @return The stylesheet that is associated with this
  2353. * transformer.
  2354. */
  2355. public final StylesheetRoot getStylesheet()
  2356. {
  2357. return m_stylesheetRoot;
  2358. }
  2359. /**
  2360. * Get quietConflictWarnings property. If the quietConflictWarnings
  2361. * property is set to true, warnings about pattern conflicts won't be
  2362. * printed to the diagnostics stream.
  2363. *
  2364. * @return True if this transformer should not report
  2365. * template match conflicts.
  2366. */
  2367. public boolean getQuietConflictWarnings()
  2368. {
  2369. return m_quietConflictWarnings;
  2370. }
  2371. /**
  2372. * If the quietConflictWarnings property is set to
  2373. * true, warnings about pattern conflicts won't be
  2374. * printed to the diagnostics stream.
  2375. * False by default.
  2376. * (Currently setting this property will have no effect.)
  2377. *
  2378. * @param b true if conflict warnings should be suppressed.
  2379. */
  2380. public void setQuietConflictWarnings(boolean b)
  2381. {
  2382. m_quietConflictWarnings = b;
  2383. }
  2384. /**
  2385. * <meta name="usage" content="internal"/>
  2386. * Set the execution context for XPath.
  2387. *
  2388. * @param xcontext A non-null reference to the XPathContext
  2389. * associated with this transformer.
  2390. */
  2391. public void setXPathContext(XPathContext xcontext)
  2392. {
  2393. m_xcontext = xcontext;
  2394. }
  2395. /**
  2396. * Get the XPath context associated with this transformer.
  2397. *
  2398. * @return The XPathContext reference, never null.
  2399. */
  2400. public final XPathContext getXPathContext()
  2401. {
  2402. return m_xcontext;
  2403. }
  2404. /**
  2405. * <meta name="usage" content="internal"/>
  2406. * Get the object used to guard the stack from
  2407. * recursion.
  2408. *
  2409. * @return The StackGuard object, which should never be null.
  2410. */
  2411. public StackGuard getStackGuard()
  2412. {
  2413. return m_stackGuard;
  2414. }
  2415. /**
  2416. * Get the recursion limit.
  2417. * Used for infinite loop check. If the value is -1, do not
  2418. * check for infinite loops. Anyone who wants to enable that
  2419. * check should change the value of this variable to be the
  2420. * level of recursion that they want to check. Be careful setting
  2421. * this variable, if the number is too low, it may report an
  2422. * infinite loop situation, when there is none.
  2423. * Post version 1.0.0, we'll make this a runtime feature.
  2424. *
  2425. * @return The limit on recursion, or -1 if no check is to be made.
  2426. */
  2427. public int getRecursionLimit()
  2428. {
  2429. return m_stackGuard.getRecursionLimit();
  2430. }
  2431. /**
  2432. * Set the recursion limit.
  2433. * Used for infinite loop check. If the value is -1, do not
  2434. * check for infinite loops. Anyone who wants to enable that
  2435. * check should change the value of this variable to be the
  2436. * level of recursion that they want to check. Be careful setting
  2437. * this variable, if the number is too low, it may report an
  2438. * infinite loop situation, when there is none.
  2439. * Post version 1.0.0, we'll make this a runtime feature.
  2440. *
  2441. * @param limit A number that represents the limit of recursion,
  2442. * or -1 if no checking is to be done.
  2443. */
  2444. public void setRecursionLimit(int limit)
  2445. {
  2446. m_stackGuard.setRecursionLimit(limit);
  2447. }
  2448. /**
  2449. * Get the ResultTreeHandler object.
  2450. *
  2451. * @return The current ResultTreeHandler, which may not
  2452. * be the main result tree manager.
  2453. */
  2454. public ResultTreeHandler getResultTreeHandler()
  2455. {
  2456. return m_resultTreeHandler;
  2457. }
  2458. /**
  2459. * Get the KeyManager object.
  2460. *
  2461. * @return A reference to the KeyManager object, which should
  2462. * never be null.
  2463. */
  2464. public KeyManager getKeyManager()
  2465. {
  2466. return m_keyManager;
  2467. }
  2468. /**
  2469. * Check to see if this is a recursive attribute definition.
  2470. *
  2471. * @param attrSet A non-null ElemAttributeSet reference.
  2472. *
  2473. * @return true if the attribute set is recursive.
  2474. */
  2475. public boolean isRecursiveAttrSet(ElemAttributeSet attrSet)
  2476. {
  2477. if (null == m_attrSetStack)
  2478. {
  2479. m_attrSetStack = new Stack();
  2480. }
  2481. if (!m_attrSetStack.empty())
  2482. {
  2483. int loc = m_attrSetStack.search(attrSet);
  2484. if (loc > -1)
  2485. {
  2486. return true;
  2487. }
  2488. }
  2489. return false;
  2490. }
  2491. /**
  2492. * Push an executing attribute set, so we can check for
  2493. * recursive attribute definitions.
  2494. *
  2495. * @param attrSet A non-null ElemAttributeSet reference.
  2496. */
  2497. public void pushElemAttributeSet(ElemAttributeSet attrSet)
  2498. {
  2499. m_attrSetStack.push(attrSet);
  2500. }
  2501. /**
  2502. * Pop the current executing attribute set.
  2503. */
  2504. public void popElemAttributeSet()
  2505. {
  2506. m_attrSetStack.pop();
  2507. }
  2508. /**
  2509. * Get the table of counters, for optimized xsl:number support.
  2510. *
  2511. * @return The CountersTable, never null.
  2512. */
  2513. public CountersTable getCountersTable()
  2514. {
  2515. if (null == m_countersTable)
  2516. m_countersTable = new CountersTable();
  2517. return m_countersTable;
  2518. }
  2519. /**
  2520. * Tell if the current template rule is null, i.e. if we are
  2521. * directly within an apply-templates. Used for xsl:apply-imports.
  2522. *
  2523. * @return True if the current template rule is null.
  2524. */
  2525. public boolean currentTemplateRuleIsNull()
  2526. {
  2527. return ((!m_currentTemplateRuleIsNull.isEmpty())
  2528. && (m_currentTemplateRuleIsNull.peek() == true));
  2529. }
  2530. /**
  2531. * Push true if the current template rule is null, false
  2532. * otherwise.
  2533. *
  2534. * @param b True if the we are executing an xsl:for-each
  2535. * (or xsl:call-template?).
  2536. */
  2537. public void pushCurrentTemplateRuleIsNull(boolean b)
  2538. {
  2539. m_currentTemplateRuleIsNull.push(b);
  2540. }
  2541. /**
  2542. * Push true if the current template rule is null, false
  2543. * otherwise.
  2544. */
  2545. public void popCurrentTemplateRuleIsNull()
  2546. {
  2547. m_currentTemplateRuleIsNull.pop();
  2548. }
  2549. /**
  2550. * Return the message manager.
  2551. *
  2552. * @return The message manager, never null.
  2553. */
  2554. public MsgMgr getMsgMgr()
  2555. {
  2556. if (null == m_msgMgr)
  2557. m_msgMgr = new MsgMgr(this);
  2558. return m_msgMgr;
  2559. }
  2560. /**
  2561. * Set the error event listener.
  2562. *
  2563. * @param listener The new error listener.
  2564. * @throws IllegalArgumentException if
  2565. */
  2566. public void setErrorListener(ErrorListener listener)
  2567. throws IllegalArgumentException
  2568. {
  2569. synchronized (m_reentryGuard)
  2570. {
  2571. if (listener == null)
  2572. throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_NULL_ERROR_HANDLER, null)); //"Null error handler");
  2573. m_errorHandler = listener;
  2574. }
  2575. }
  2576. /**
  2577. * Get the current error event handler.
  2578. *
  2579. * @return The current error handler, which should never be null.
  2580. */
  2581. public ErrorListener getErrorListener()
  2582. {
  2583. return m_errorHandler;
  2584. }
  2585. /**
  2586. * Get an instance of the trace manager for this transformation.
  2587. * This object can be used to set trace listeners on various
  2588. * events during the transformation.
  2589. *
  2590. * @return A reference to the TraceManager, never null.
  2591. */
  2592. public TraceManager getTraceManager()
  2593. {
  2594. return m_traceManager;
  2595. }
  2596. /**
  2597. * Look up the value of a feature.
  2598. *
  2599. * <p>The feature name is any fully-qualified URI. It is
  2600. * possible for an TransformerFactory to recognize a feature name but
  2601. * to be unable to return its value; this is especially true
  2602. * in the case of an adapter for a SAX1 Parser, which has
  2603. * no way of knowing whether the underlying parser is
  2604. * validating, for example.</p>
  2605. *
  2606. * <h3>Open issues:</h3>
  2607. * <dl>
  2608. * <dt><h4>Should getFeature be changed to hasFeature?</h4></dt>
  2609. * <dd>Keith Visco writes: Should getFeature be changed to hasFeature?
  2610. * It returns a boolean which indicated whether the "state"
  2611. * of feature is "true or false". I assume this means whether
  2612. * or not a feature is supported? I know SAX is using "getFeature",
  2613. * but to me "hasFeature" is cleaner.</dd>
  2614. * </dl>
  2615. *
  2616. * @param name The feature name, which is a fully-qualified
  2617. * URI.
  2618. * @return The current state of the feature (true or false).
  2619. * @throws org.xml.sax.SAXNotRecognizedException When the
  2620. * TransformerFactory does not recognize the feature name.
  2621. * @throws org.xml.sax.SAXNotSupportedException When the
  2622. * TransformerFactory recognizes the feature name but
  2623. * cannot determine its value at this time.
  2624. *
  2625. * @throws SAXNotRecognizedException
  2626. * @throws SAXNotSupportedException
  2627. */
  2628. public boolean getFeature(String name)
  2629. throws SAXNotRecognizedException, SAXNotSupportedException
  2630. {
  2631. if ("http://xml.org/trax/features/sax/input".equals(name))
  2632. return true;
  2633. else if ("http://xml.org/trax/features/dom/input".equals(name))
  2634. return true;
  2635. throw new SAXNotRecognizedException(name);
  2636. }
  2637. // %TODO% Doc
  2638. /**
  2639. * NEEDSDOC Method getMode
  2640. *
  2641. *
  2642. * NEEDSDOC (getMode) @return
  2643. */
  2644. public QName getMode()
  2645. {
  2646. return m_modes.isEmpty() ? null : (QName) m_modes.peek();
  2647. }
  2648. // %TODO% Doc
  2649. /**
  2650. * NEEDSDOC Method pushMode
  2651. *
  2652. *
  2653. * NEEDSDOC @param mode
  2654. */
  2655. public void pushMode(QName mode)
  2656. {
  2657. m_modes.push(mode);
  2658. }
  2659. // %TODO% Doc
  2660. /**
  2661. * NEEDSDOC Method popMode
  2662. *
  2663. */
  2664. public void popMode()
  2665. {
  2666. m_modes.pop();
  2667. }
  2668. ////////////////////////
  2669. // Implement Runnable //
  2670. ////////////////////////
  2671. /**
  2672. * Base thread controler for xalan. Must be overriden with
  2673. * a derived class to support thread pooling.
  2674. *
  2675. * All thread-related stuff is in this class.
  2676. *
  2677. * <p><em>WARNING!</em> This class will probably move since the DTM
  2678. * CoroutineSAXParser depends on it. This class should move
  2679. * to the CoroutineSAXParser. You can use it, but be aware
  2680. * that your code will have to change when the move occurs.</p>
  2681. */
  2682. public static class ThreadControler
  2683. {
  2684. /**
  2685. * Will get a thread from the pool, execute the task
  2686. * and return the thread to the pool.
  2687. *
  2688. * The return value is used only to wait for completion
  2689. *
  2690. *
  2691. * NEEDSDOC @param task
  2692. * @param priority if >0 the task will run with the given priority
  2693. * ( doesn't seem to be used in xalan, since it's allways the default )
  2694. * @returns The thread that is running the task, can be used
  2695. * to wait for completion
  2696. *
  2697. * NEEDSDOC ($objectName$) @return
  2698. */
  2699. public Thread run(Runnable task, int priority)
  2700. {
  2701. Thread t = new Thread(task);
  2702. t.start();
  2703. // if( priority > 0 )
  2704. // t.setPriority( priority );
  2705. return t;
  2706. }
  2707. /**
  2708. * Wait until the task is completed on the worker
  2709. * thread.
  2710. *
  2711. * NEEDSDOC @param worker
  2712. * NEEDSDOC @param task
  2713. *
  2714. * @throws InterruptedException
  2715. */
  2716. public void waitThread(Thread worker, Runnable task)
  2717. throws InterruptedException
  2718. {
  2719. // This should wait until the transformThread is considered not alive.
  2720. worker.join();
  2721. }
  2722. }
  2723. /** NEEDSDOC Field tpool */
  2724. static ThreadControler tpool = new ThreadControler();
  2725. /**
  2726. * Change the ThreadControler that will be used to
  2727. * manage the transform threads.
  2728. *
  2729. * NEEDSDOC @param tp
  2730. */
  2731. public static void setThreadControler(ThreadControler tp)
  2732. {
  2733. tpool = tp;
  2734. }
  2735. /**
  2736. * Called by SourceTreeHandler to start the transformation
  2737. * in a separate thread
  2738. *
  2739. * NEEDSDOC @param priority
  2740. */
  2741. public void runTransformThread(int priority)
  2742. {
  2743. // used in SourceTreeHandler
  2744. Thread t = tpool.run(this, priority);
  2745. this.setTransformThread(t);
  2746. }
  2747. /**
  2748. * Called by this.transform() if isParserEventsOnMain()==false.
  2749. * Similar with runTransformThread(), but no priority is set
  2750. * and setTransformThread is not set.
  2751. */
  2752. public void runTransformThread()
  2753. {
  2754. tpool.run(this, -1);
  2755. }
  2756. /**
  2757. * Called by CoRoutineSAXParser. Launches the CoroutineSAXParser
  2758. * in a thread, and prepares it to invoke the parser from that thread
  2759. * upon request.
  2760. *
  2761. */
  2762. public static void runTransformThread(Runnable runnable)
  2763. {
  2764. tpool.run(runnable, -1);
  2765. }
  2766. /**
  2767. * Used by SourceTreeHandler to wait until the transform
  2768. * completes
  2769. *
  2770. * @throws SAXException
  2771. */
  2772. public void waitTransformThread() throws SAXException
  2773. {
  2774. // This is called to make sure the task is done.
  2775. // It is possible that the thread has been reused -
  2776. // but for a different transformation. ( what if we
  2777. // recycle the transformer ? Not a problem since this is
  2778. // still in use. )
  2779. Thread transformThread = this.getTransformThread();
  2780. if (null != transformThread)
  2781. {
  2782. try
  2783. {
  2784. tpool.waitThread(transformThread, this);
  2785. if (!this.hasTransformThreadErrorCatcher())
  2786. {
  2787. Exception e = this.getExceptionThrown();
  2788. if (null != e)
  2789. {
  2790. e.printStackTrace();
  2791. throw new org.xml.sax.SAXException(e);
  2792. }
  2793. }
  2794. this.setTransformThread(null);
  2795. }
  2796. catch (InterruptedException ie){}
  2797. }
  2798. }
  2799. /**
  2800. * Get the exception thrown by the secondary thread (normally
  2801. * the transform thread).
  2802. *
  2803. * @return The thrown exception, or null if no exception was
  2804. * thrown.
  2805. */
  2806. public Exception getExceptionThrown()
  2807. {
  2808. return m_exceptionThrown;
  2809. }
  2810. /**
  2811. * Set the exception thrown by the secondary thread (normally
  2812. * the transform thread).
  2813. *
  2814. * @param e The thrown exception, or null if no exception was
  2815. * thrown.
  2816. */
  2817. public void setExceptionThrown(Exception e)
  2818. {
  2819. m_exceptionThrown = e;
  2820. }
  2821. /**
  2822. * This is just a way to set the document for run().
  2823. *
  2824. * @param doc A non-null reference to the root of the
  2825. * tree to be transformed.
  2826. */
  2827. public void setSourceTreeDocForThread(int doc)
  2828. {
  2829. m_doc = doc;
  2830. }
  2831. /**
  2832. * Set the input source for the source tree, which is needed if the
  2833. * parse thread is not the main thread, in order for the parse
  2834. * thread's run method to get to the input source.
  2835. *
  2836. * @param source The input source for the source tree.
  2837. */
  2838. public void setXMLSource(Source source)
  2839. {
  2840. m_xmlSource = source;
  2841. }
  2842. /**
  2843. * Tell if the transform method is completed.
  2844. *
  2845. * @return True if transformNode has completed, or
  2846. * an exception was thrown.
  2847. */
  2848. public boolean isTransformDone()
  2849. {
  2850. synchronized (this)
  2851. {
  2852. return m_isTransformDone;
  2853. }
  2854. }
  2855. /**
  2856. * Set if the transform method is completed.
  2857. *
  2858. * @param done True if transformNode has completed, or
  2859. * an exception was thrown.
  2860. */
  2861. public void setIsTransformDone(boolean done)
  2862. {
  2863. synchronized (this)
  2864. {
  2865. m_isTransformDone = done;
  2866. }
  2867. }
  2868. /**
  2869. * From a secondary thread, post the exception, so that
  2870. * it can be picked up from the main thread.
  2871. *
  2872. * @param e The exception that was thrown.
  2873. */
  2874. void postExceptionFromThread(Exception e)
  2875. {
  2876. // Commented out in response to problem reported by Nicola Brown <Nicola.Brown@jacobsrimell.com>
  2877. // if(m_reportInPostExceptionFromThread)
  2878. // {
  2879. // // Consider re-throwing the exception if this flag is set.
  2880. // e.printStackTrace();
  2881. // }
  2882. // %REVIEW Need DTM equivelent?
  2883. // if (m_inputContentHandler instanceof SourceTreeHandler)
  2884. // {
  2885. // SourceTreeHandler sth = (SourceTreeHandler) m_inputContentHandler;
  2886. //
  2887. // sth.setExceptionThrown(e);
  2888. // }
  2889. ContentHandler ch = getContentHandler();
  2890. // if(ch instanceof SourceTreeHandler)
  2891. // {
  2892. // SourceTreeHandler sth = (SourceTreeHandler) ch;
  2893. // ((TransformerImpl)(sth.getTransformer())).postExceptionFromThread(e);
  2894. // }
  2895. m_isTransformDone = true;
  2896. m_exceptionThrown = e;
  2897. ; // should have already been reported via the error handler?
  2898. synchronized (this)
  2899. {
  2900. // See message from me on 3/27/2001 to Patrick Moore.
  2901. // String msg = e.getMessage();
  2902. // System.out.println(e.getMessage());
  2903. // Is this really needed? -sb
  2904. notifyAll();
  2905. // if (null == msg)
  2906. // {
  2907. //
  2908. // // m_throwNewError = false;
  2909. // e.printStackTrace();
  2910. // }
  2911. // throw new org.apache.xml.utils.WrappedRuntimeException(e);
  2912. }
  2913. }
  2914. /**
  2915. * Run the transform thread.
  2916. */
  2917. public void run()
  2918. {
  2919. m_hasBeenReset = false;
  2920. try
  2921. {
  2922. // int n = ((SourceTreeHandler)getInputContentHandler()).getDTMRoot();
  2923. // transformNode(n);
  2924. try
  2925. {
  2926. m_isTransformDone = false;
  2927. // Should no longer be needed...
  2928. // if(m_inputContentHandler instanceof TransformerHandlerImpl)
  2929. // {
  2930. // TransformerHandlerImpl thi = (TransformerHandlerImpl)m_inputContentHandler;
  2931. // thi.waitForInitialEvents();
  2932. // }
  2933. transformNode(m_doc);
  2934. }
  2935. catch (Exception e)
  2936. {
  2937. // e.printStackTrace();
  2938. // Strange that the other catch won't catch this...
  2939. if (null != m_transformThread)
  2940. postExceptionFromThread(e); // Assume we're on the main thread
  2941. else
  2942. throw new RuntimeException(e.getMessage());
  2943. }
  2944. finally
  2945. {
  2946. m_isTransformDone = true;
  2947. if (m_inputContentHandler instanceof TransformerHandlerImpl)
  2948. {
  2949. ((TransformerHandlerImpl) m_inputContentHandler).clearCoRoutine();
  2950. }
  2951. // synchronized (this)
  2952. // {
  2953. // notifyAll();
  2954. // }
  2955. }
  2956. }
  2957. catch (Exception e)
  2958. {
  2959. // e.printStackTrace();
  2960. if (null != m_transformThread)
  2961. postExceptionFromThread(e);
  2962. else
  2963. throw new RuntimeException(e.getMessage()); // Assume we're on the main thread.
  2964. }
  2965. }
  2966. // Fragment re-execution interfaces for a tool.
  2967. /**
  2968. * This will get a snapshot of the current executing context
  2969. *
  2970. *
  2971. * @return TransformerSnapshot object, snapshot of executing context
  2972. */
  2973. public TransformSnapshot getSnapshot()
  2974. {
  2975. return new TransformSnapshotImpl(this);
  2976. }
  2977. /**
  2978. * This will execute the following XSLT instructions
  2979. * from the snapshot point, after the stylesheet execution
  2980. * context has been reset from the snapshot point.
  2981. *
  2982. * @param ts The snapshot of where to start execution
  2983. *
  2984. * @throws TransformerException
  2985. */
  2986. public void executeFromSnapshot(TransformSnapshot ts)
  2987. throws TransformerException
  2988. {
  2989. ElemTemplateElement template = getMatchedTemplate();
  2990. int child = getMatchedNode();
  2991. pushElemTemplateElement(template); //needed??
  2992. m_xcontext.pushCurrentNode(child); //needed??
  2993. this.executeChildTemplates(template, true); // getResultTreeHandler());
  2994. }
  2995. /**
  2996. * This will reset the stylesheet execution context
  2997. * from the snapshot point.
  2998. *
  2999. * @param ts The snapshot of where to start execution
  3000. */
  3001. public void resetToStylesheet(TransformSnapshot ts)
  3002. {
  3003. ((TransformSnapshotImpl) ts).apply(this);
  3004. }
  3005. /**
  3006. * NEEDSDOC Method stopTransformation
  3007. *
  3008. */
  3009. public void stopTransformation(){}
  3010. /**
  3011. * Test whether whitespace-only text nodes are visible in the logical
  3012. * view of <code>DTM</code>. Normally, this function
  3013. * will be called by the implementation of <code>DTM</code>
  3014. * it is not normally called directly from
  3015. * user code.
  3016. *
  3017. * @param elementHandle int Handle of the element.
  3018. * @return one of NOTSTRIP, STRIP, or INHERIT.
  3019. */
  3020. public short getShouldStripSpace(int elementHandle, DTM dtm)
  3021. {
  3022. try
  3023. {
  3024. org.apache.xalan.templates.WhiteSpaceInfo info =
  3025. m_stylesheetRoot.getWhiteSpaceInfo(m_xcontext, elementHandle, dtm);
  3026. if (null == info)
  3027. {
  3028. return DTMWSFilter.INHERIT;
  3029. }
  3030. else
  3031. {
  3032. // System.out.println("getShouldStripSpace: "+info.getShouldStripSpace());
  3033. return info.getShouldStripSpace()
  3034. ? DTMWSFilter.STRIP : DTMWSFilter.NOTSTRIP;
  3035. }
  3036. }
  3037. catch (TransformerException se)
  3038. {
  3039. return DTMWSFilter.INHERIT;
  3040. }
  3041. }
  3042. } // end TransformerImpl class