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.client;
  58. import java.applet.Applet;
  59. import java.awt.Graphics;
  60. import java.net.URL;
  61. import java.net.MalformedURLException;
  62. import java.io.PrintWriter;
  63. import java.io.StringWriter;
  64. import java.io.StringReader;
  65. import java.io.IOException;
  66. import java.io.InputStream;
  67. import java.util.Properties;
  68. // Needed Xalan classes
  69. import org.apache.xalan.res.XSLMessages;
  70. import org.apache.xalan.res.XSLTErrorResources;
  71. // Needed TRaX classes
  72. import javax.xml.transform.Result;
  73. import javax.xml.transform.TransformerFactory;
  74. import javax.xml.transform.TransformerConfigurationException;
  75. import javax.xml.transform.Transformer;
  76. import javax.xml.transform.TransformerException;
  77. import javax.xml.transform.Templates;
  78. import javax.xml.transform.Source;
  79. import javax.xml.transform.Result;
  80. import javax.xml.transform.stream.StreamResult;
  81. import javax.xml.transform.stream.StreamSource;
  82. /**
  83. * <meta name="usage" content="general"/>
  84. * Provides applet host for the XSLT processor. To perform transformations on an HTML client:
  85. * <ol>
  86. * <li>Use an <applet> tag to embed this applet in the HTML client.</li>
  87. * <li>Use the DocumentURL and StyleURL PARAM tags or the {@link #setDocumentURL} and
  88. * {@link #setStyleURL} methods to specify the XML source document and XSL stylesheet.</li>
  89. * <li>Call the {@link #getHtmlText} method (or one of the transformToHtml() methods)
  90. * to perform the transformation and return the result as a String.</li>
  91. * </ol>
  92. */
  93. public class XSLTProcessorApplet extends Applet
  94. {
  95. /**
  96. * The stylesheet processor.
  97. * @serial
  98. */
  99. TransformerFactory m_tfactory = null;
  100. /**
  101. * @serial
  102. */
  103. private String m_styleURL;
  104. /**
  105. * @serial
  106. */
  107. private String m_documentURL;
  108. // Parameter names. To change a name of a parameter, you need only make
  109. // a single change. Simply modify the value of the parameter string below.
  110. //--------------------------------------------------------------------------
  111. /**
  112. * @serial
  113. */
  114. private final String PARAM_styleURL = "styleURL";
  115. /**
  116. * @serial
  117. */
  118. private final String PARAM_documentURL = "documentURL";
  119. // We'll keep the DOM trees around, so tell which trees
  120. // are cached.
  121. /**
  122. * @serial
  123. */
  124. private String m_styleURLOfCached = null;
  125. /**
  126. * @serial
  127. */
  128. private String m_documentURLOfCached = null;
  129. /**
  130. * Save this for use on the worker thread; may not be necessary.
  131. * @serial
  132. */
  133. private URL m_codeBase = null;
  134. /**
  135. * @serial
  136. */
  137. private String m_treeURL = null;
  138. /**
  139. * DocumentBase URL
  140. * @serial
  141. */
  142. private URL m_documentBase = null;
  143. /**
  144. * Thread stuff for the trusted worker thread.
  145. */
  146. transient private Thread m_callThread = null;
  147. /**
  148. */
  149. transient private TrustedAgent m_trustedAgent = null;
  150. /**
  151. * Thread for running TrustedAgent.
  152. */
  153. transient private Thread m_trustedWorker = null;
  154. /**
  155. * Where the worker thread puts the HTML text.
  156. */
  157. transient private String m_htmlText = null;
  158. /**
  159. * Where the worker thread puts the document/stylesheet text.
  160. */
  161. transient private String m_sourceText = null;
  162. /**
  163. * Stylesheet attribute name and value that the caller can set.
  164. */
  165. transient private String m_nameOfIDAttrOfElemToModify = null;
  166. /**
  167. */
  168. transient private String m_elemIdToModify = null;
  169. /**
  170. */
  171. transient private String m_attrNameToSet = null;
  172. /**
  173. */
  174. transient private String m_attrValueToSet = null;
  175. /**
  176. * The XSLTProcessorApplet constructor takes no arguments.
  177. */
  178. public XSLTProcessorApplet(){}
  179. /**
  180. * Get basic information about the applet
  181. * @return A String with the applet name and author.
  182. */
  183. public String getAppletInfo()
  184. {
  185. return "Name: XSLTProcessorApplet\r\n" + "Author: Scott Boag";
  186. }
  187. /**
  188. * Get descriptions of the applet parameters.
  189. * @return A two-dimensional array of Strings with Name, Type, and Description
  190. * for each parameter.
  191. */
  192. public String[][] getParameterInfo()
  193. {
  194. String[][] info =
  195. {
  196. { PARAM_styleURL, "String", "URL to an XSL stylesheet" },
  197. { PARAM_documentURL, "String", "URL to an XML document" },
  198. };
  199. return info;
  200. }
  201. /**
  202. * Standard applet initialization.
  203. */
  204. public void init()
  205. {
  206. // PARAMETER SUPPORT
  207. // The following code retrieves the value of each parameter
  208. // specified with the <PARAM> tag and stores it in a member
  209. // variable.
  210. //----------------------------------------------------------------------
  211. String param;
  212. // styleURL: Parameter description
  213. //----------------------------------------------------------------------
  214. param = getParameter(PARAM_styleURL);
  215. if (param != null)
  216. setStyleURL(param);
  217. // documentURL: Parameter description
  218. //----------------------------------------------------------------------
  219. param = getParameter(PARAM_documentURL);
  220. if (param != null)
  221. setDocumentURL(param);
  222. m_codeBase = this.getCodeBase();
  223. m_documentBase = this.getDocumentBase();
  224. // If you use a ResourceWizard-generated "control creator" class to
  225. // arrange controls in your applet, you may want to call its
  226. // CreateControls() method from within this method. Remove the following
  227. // call to resize() before adding the call to CreateControls();
  228. // CreateControls() does its own resizing.
  229. //----------------------------------------------------------------------
  230. resize(320, 240);
  231. }
  232. /**
  233. * Automatically called when the HTML client containing the applet loads.
  234. * This method starts execution of the applet thread.
  235. */
  236. public void start()
  237. {
  238. m_trustedAgent = new TrustedAgent();
  239. Thread currentThread = Thread.currentThread();
  240. m_trustedWorker = new Thread(currentThread.getThreadGroup(),
  241. m_trustedAgent);
  242. m_trustedWorker.start();
  243. try
  244. {
  245. m_tfactory = TransformerFactory.newInstance();
  246. this.showStatus("Causing Transformer and Parser to Load and JIT...");
  247. // Prime the pump so that subsequent transforms are faster.
  248. StringReader xmlbuf = new StringReader("<?xml version='1.0'?><foo/>");
  249. StringReader xslbuf = new StringReader(
  250. "<?xml version='1.0'?><xsl:stylesheet xmlns:xsl='http://www.w3.org/1999/XSL/Transform' version='1.0'><xsl:template match='foo'><out/></xsl:template></xsl:stylesheet>");
  251. PrintWriter pw = new PrintWriter(new StringWriter());
  252. synchronized (m_tfactory)
  253. {
  254. Templates templates = m_tfactory.newTemplates(new StreamSource(xslbuf));
  255. Transformer transformer = templates.newTransformer();
  256. transformer.transform(new StreamSource(xmlbuf), new StreamResult(pw));
  257. }
  258. System.out.println("Primed the pump!");
  259. this.showStatus("Ready to go!");
  260. }
  261. catch (Exception e)
  262. {
  263. this.showStatus("Could not prime the pump!");
  264. System.out.println("Could not prime the pump!");
  265. e.printStackTrace();
  266. }
  267. }
  268. /**
  269. * Do not call; this applet contains no UI or visual components.
  270. *
  271. */
  272. public void paint(Graphics g){}
  273. /**
  274. * Automatically called when the HTML page containing the applet is no longer
  275. * on the screen. Stops execution of the applet thread.
  276. */
  277. public void stop()
  278. {
  279. if (null != m_trustedWorker)
  280. {
  281. m_trustedWorker.stop();
  282. // m_trustedWorker.destroy();
  283. m_trustedWorker = null;
  284. }
  285. m_styleURLOfCached = null;
  286. m_documentURLOfCached = null;
  287. }
  288. /**
  289. * Cleanup; called when applet is terminated and unloaded.
  290. */
  291. public void destroy()
  292. {
  293. if (null != m_trustedWorker)
  294. {
  295. m_trustedWorker.stop();
  296. // m_trustedWorker.destroy();
  297. m_trustedWorker = null;
  298. }
  299. m_styleURLOfCached = null;
  300. m_documentURLOfCached = null;
  301. }
  302. /**
  303. * Set the URL to the XSL stylesheet that will be used
  304. * to transform the input XML. No processing is done yet.
  305. * @param urlString valid URL string for XSL stylesheet.
  306. */
  307. public void setStyleURL(String urlString)
  308. {
  309. m_styleURL = urlString;
  310. }
  311. /**
  312. * Set the URL to the XML document that will be transformed
  313. * with the XSL stylesheet. No processing is done yet.
  314. * @param urlString valid URL string for XML document.
  315. */
  316. public void setDocumentURL(String urlString)
  317. {
  318. m_documentURL = urlString;
  319. }
  320. /**
  321. * The processor keeps a cache of the source and
  322. * style trees, so call this method if they have changed
  323. * or you want to do garbage collection.
  324. */
  325. public void freeCache()
  326. {
  327. m_styleURLOfCached = null;
  328. m_documentURLOfCached = null;
  329. }
  330. /**
  331. * Set an attribute in the stylesheet, which gives the ability
  332. * to have some dynamic selection control.
  333. * @param nameOfIDAttrOfElemToModify The name of an attribute to search for a unique id.
  334. * @param elemId The unique ID to look for.
  335. * @param attrName Once the element is found, the name of the attribute to set.
  336. * @param value The value to set the attribute to.
  337. */
  338. public void setStyleSheetAttribute(String nameOfIDAttrOfElemToModify,
  339. String elemId, String attrName,
  340. String value)
  341. {
  342. m_nameOfIDAttrOfElemToModify = nameOfIDAttrOfElemToModify;
  343. m_elemIdToModify = elemId;
  344. m_attrNameToSet = attrName;
  345. m_attrValueToSet = value;
  346. }
  347. /**
  348. * Stylesheet parameter key
  349. */
  350. transient String m_key;
  351. /**
  352. * Stylesheet parameter value
  353. */
  354. transient String m_expression;
  355. /**
  356. * Submit a stylesheet parameter.
  357. *
  358. * @param key stylesheet parameter key
  359. * @param expr the parameter expression to be submitted.
  360. * @see javax.xml.transform.Transformer#setParameter(String,Object)
  361. */
  362. public void setStylesheetParam(String key, String expr)
  363. {
  364. m_key = key;
  365. m_expression = expr;
  366. }
  367. /**
  368. * Given a String containing markup, escape the markup so it
  369. * can be displayed in the browser.
  370. *
  371. * @param s String to escape
  372. *
  373. * The escaped string.
  374. */
  375. public String escapeString(String s)
  376. {
  377. StringBuffer sb = new StringBuffer();
  378. int length = s.length();
  379. for (int i = 0; i < length; i++)
  380. {
  381. char ch = s.charAt(i);
  382. if ('<' == ch)
  383. {
  384. sb.append("<");
  385. }
  386. else if ('>' == ch)
  387. {
  388. sb.append(">");
  389. }
  390. else if ('&' == ch)
  391. {
  392. sb.append("&");
  393. }
  394. else if (0xd800 <= ch && ch < 0xdc00)
  395. {
  396. // UTF-16 surrogate
  397. int next;
  398. if (i + 1 >= length)
  399. {
  400. throw new RuntimeException(
  401. XSLMessages.createMessage(
  402. XSLTErrorResources.ER_INVALID_UTF16_SURROGATE,
  403. new Object[]{ Integer.toHexString(ch) })); //"Invalid UTF-16 surrogate detected: "
  404. //+Integer.toHexString(ch)+ " ?");
  405. }
  406. else
  407. {
  408. next = s.charAt(++i);
  409. if (!(0xdc00 <= next && next < 0xe000))
  410. throw new RuntimeException(
  411. XSLMessages.createMessage(
  412. XSLTErrorResources.ER_INVALID_UTF16_SURROGATE,
  413. new Object[]{
  414. Integer.toHexString(ch) + " "
  415. + Integer.toHexString(next) })); //"Invalid UTF-16 surrogate detected: "
  416. //+Integer.toHexString(ch)+" "+Integer.toHexString(next));
  417. next = ((ch - 0xd800) << 10) + next - 0xdc00 + 0x00010000;
  418. }
  419. sb.append("&#x");
  420. sb.append(Integer.toHexString(next));
  421. sb.append(";");
  422. }
  423. else
  424. {
  425. sb.append(ch);
  426. }
  427. }
  428. return sb.toString();
  429. }
  430. /**
  431. * Assuming the stylesheet URL and the input XML URL have been set,
  432. * perform the transformation and return the result as a String.
  433. *
  434. * @return A string that contains the contents pointed to by the URL.
  435. *
  436. */
  437. public String getHtmlText()
  438. {
  439. m_trustedAgent.m_getData = true;
  440. m_callThread = Thread.currentThread();
  441. try
  442. {
  443. synchronized (m_callThread)
  444. {
  445. m_callThread.wait();
  446. }
  447. }
  448. catch (InterruptedException ie)
  449. {
  450. System.out.println(ie.getMessage());
  451. }
  452. return m_htmlText;
  453. }
  454. /**
  455. * Get an XML document (or stylesheet)
  456. *
  457. * @param treeURL valid URL string for the document.
  458. *
  459. * @return document
  460. *
  461. * @throws IOException
  462. */
  463. public String getTreeAsText(String treeURL) throws IOException
  464. {
  465. m_treeURL = treeURL;
  466. m_trustedAgent.m_getData = true;
  467. m_trustedAgent.m_getSource = true;
  468. m_callThread = Thread.currentThread();
  469. try
  470. {
  471. synchronized (m_callThread)
  472. {
  473. m_callThread.wait();
  474. }
  475. }
  476. catch (InterruptedException ie)
  477. {
  478. System.out.println(ie.getMessage());
  479. }
  480. return m_sourceText;
  481. }
  482. /**
  483. * Use a Transformer to copy the source document
  484. * to a StreamResult.
  485. *
  486. * @return the document as a string
  487. */
  488. private String getSource() throws TransformerException
  489. {
  490. StringWriter osw = new StringWriter();
  491. PrintWriter pw = new PrintWriter(osw, false);
  492. String text = "";
  493. try
  494. {
  495. URL docURL = new URL(m_documentBase, m_treeURL);
  496. synchronized (m_tfactory)
  497. {
  498. Transformer transformer = m_tfactory.newTransformer();
  499. StreamSource source = new StreamSource(docURL.toString());
  500. StreamResult result = new StreamResult(pw);
  501. transformer.transform(source, result);
  502. text = osw.toString();
  503. }
  504. }
  505. catch (MalformedURLException e)
  506. {
  507. e.printStackTrace();
  508. System.exit(-1);
  509. }
  510. catch (Exception any_error)
  511. {
  512. any_error.printStackTrace();
  513. }
  514. return text;
  515. }
  516. /**
  517. * Get the XML source Tree as a text string suitable
  518. * for display in a browser. Note that this is for display of the
  519. * XML itself, not for rendering of HTML by the browser.
  520. *
  521. * @return XML source document as a string.
  522. * @throws Exception thrown if tree can not be converted.
  523. */
  524. public String getSourceTreeAsText() throws Exception
  525. {
  526. return getTreeAsText(m_documentURL);
  527. }
  528. /**
  529. * Get the XSL style Tree as a text string suitable
  530. * for display in a browser. Note that this is for display of the
  531. * XML itself, not for rendering of HTML by the browser.
  532. *
  533. * @return The XSL stylesheet as a string.
  534. * @throws Exception thrown if tree can not be converted.
  535. */
  536. public String getStyleTreeAsText() throws Exception
  537. {
  538. return getTreeAsText(m_styleURL);
  539. }
  540. /**
  541. * Get the HTML result Tree as a text string suitable
  542. * for display in a browser. Note that this is for display of the
  543. * XML itself, not for rendering of HTML by the browser.
  544. *
  545. * @return Transformation result as unmarked text.
  546. * @throws Exception thrown if tree can not be converted.
  547. */
  548. public String getResultTreeAsText() throws Exception
  549. {
  550. return escapeString(getHtmlText());
  551. }
  552. /**
  553. * Process a document and a stylesheet and return
  554. * the transformation result. If one of these is null, the
  555. * existing value (of a previous transformation) is not affected.
  556. *
  557. * @param doc URL string to XML document
  558. * @param style URL string to XSL stylesheet
  559. *
  560. * @return HTML transformation result
  561. */
  562. public String transformToHtml(String doc, String style)
  563. {
  564. if (null != doc)
  565. {
  566. m_documentURL = doc;
  567. }
  568. if (null != style)
  569. {
  570. m_styleURL = style;
  571. }
  572. return getHtmlText();
  573. }
  574. /**
  575. * Process a document and a stylesheet and return
  576. * the transformation result. Use the xsl:stylesheet PI to find the
  577. * document, if one exists.
  578. *
  579. * @param doc URL string to XML document containing an xsl:stylesheet PI.
  580. *
  581. * @return HTML transformation result
  582. */
  583. public String transformToHtml(String doc)
  584. {
  585. if (null != doc)
  586. {
  587. m_documentURL = doc;
  588. }
  589. m_styleURL = null;
  590. return getHtmlText();
  591. }
  592. /**
  593. * Process the transformation.
  594. *
  595. * @return The transformation result as a string.
  596. *
  597. * @throws TransformerException
  598. */
  599. private String processTransformation() throws TransformerException
  600. {
  601. String htmlData = null;
  602. this.showStatus("Waiting for Transformer and Parser to finish loading and JITing...");
  603. synchronized (m_tfactory)
  604. {
  605. URL documentURL = null;
  606. URL styleURL = null;
  607. StringWriter osw = new StringWriter();
  608. PrintWriter pw = new PrintWriter(osw, false);
  609. StreamResult result = new StreamResult(pw);
  610. this.showStatus("Begin Transformation...");
  611. try
  612. {
  613. documentURL = new URL(m_codeBase, m_documentURL);
  614. StreamSource xmlSource = new StreamSource(documentURL.toString());
  615. styleURL = new URL(m_codeBase, m_styleURL);
  616. StreamSource xslSource = new StreamSource(styleURL.toString());
  617. Transformer transformer = m_tfactory.newTransformer(xslSource);
  618. if (null != m_key)
  619. transformer.setParameter(m_key, m_expression);
  620. transformer.transform(xmlSource, result);
  621. }
  622. catch (TransformerConfigurationException tfe)
  623. {
  624. tfe.printStackTrace();
  625. System.exit(-1);
  626. }
  627. catch (MalformedURLException e)
  628. {
  629. e.printStackTrace();
  630. System.exit(-1);
  631. }
  632. this.showStatus("Transformation Done!");
  633. htmlData = osw.toString();
  634. }
  635. return htmlData;
  636. }
  637. /**
  638. * This class maintains a worker thread that that is
  639. * trusted and can do things like access data. You need
  640. * this because the thread that is called by the browser
  641. * is not trusted and can't access data from the URLs.
  642. */
  643. class TrustedAgent implements Runnable
  644. {
  645. /**
  646. * Specifies whether the worker thread should perform a transformation.
  647. */
  648. public boolean m_getData = false;
  649. /**
  650. * Specifies whether the worker thread should get an XML document or XSL stylesheet.
  651. */
  652. public boolean m_getSource = false;
  653. /**
  654. * The real work is done from here.
  655. *
  656. */
  657. public void run()
  658. {
  659. while (true)
  660. {
  661. m_trustedWorker.yield();
  662. if (m_getData) // Perform a transformation or get a document.
  663. {
  664. try
  665. {
  666. m_getData = false;
  667. m_htmlText = null;
  668. m_sourceText = null;
  669. if (m_getSource) // Get a document.
  670. {
  671. m_getSource = false;
  672. m_sourceText = getSource();
  673. }
  674. else // Perform a transformation.
  675. m_htmlText = processTransformation();
  676. }
  677. catch (Exception e)
  678. {
  679. e.printStackTrace();
  680. }
  681. finally
  682. {
  683. synchronized (m_callThread)
  684. {
  685. m_callThread.notify();
  686. }
  687. }
  688. }
  689. else
  690. {
  691. try
  692. {
  693. m_trustedWorker.sleep(50);
  694. }
  695. catch (InterruptedException ie)
  696. {
  697. ie.printStackTrace();
  698. }
  699. }
  700. }
  701. }
  702. }
  703. }