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