blob: 3f85ecade6da98941104c478478d189e562b8fb3 [file] [log] [blame]
/*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 1999-2003 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Xalan" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation and was
* originally based on software copyright (c) 1999, Lotus
* Development Corporation., http://www.lotus.com. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
package org.apache.xalan.client;
import java.applet.Applet;
import java.awt.Graphics;
import java.net.URL;
import java.net.MalformedURLException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.StringReader;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
// Needed Xalan classes
import org.apache.xalan.res.XSLMessages;
import org.apache.xalan.res.XSLTErrorResources;
// Needed TRaX classes
import javax.xml.transform.Result;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.Templates;
import javax.xml.transform.Source;
import javax.xml.transform.Result;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
/**
* <meta name="usage" content="general"/>
* Provides applet host for the XSLT processor. To perform transformations on an HTML client:
* <ol>
* <li>Use an &lt;applet&gt; tag to embed this applet in the HTML client.</li>
* <li>Use the DocumentURL and StyleURL PARAM tags or the {@link #setDocumentURL} and
* {@link #setStyleURL} methods to specify the XML source document and XSL stylesheet.</li>
* <li>Call the {@link #getHtmlText} method (or one of the transformToHtml() methods)
* to perform the transformation and return the result as a String.</li>
* </ol>
*/
public class XSLTProcessorApplet extends Applet
{
/**
* The stylesheet processor.
* @serial
*/
TransformerFactory m_tfactory = null;
/**
* @serial
*/
private String m_styleURL;
/**
* @serial
*/
private String m_documentURL;
// Parameter names. To change a name of a parameter, you need only make
// a single change. Simply modify the value of the parameter string below.
//--------------------------------------------------------------------------
/**
* @serial
*/
private final String PARAM_styleURL = "styleURL";
/**
* @serial
*/
private final String PARAM_documentURL = "documentURL";
// We'll keep the DOM trees around, so tell which trees
// are cached.
/**
* @serial
*/
private String m_styleURLOfCached = null;
/**
* @serial
*/
private String m_documentURLOfCached = null;
/**
* Save this for use on the worker thread; may not be necessary.
* @serial
*/
private URL m_codeBase = null;
/**
* @serial
*/
private String m_treeURL = null;
/**
* DocumentBase URL
* @serial
*/
private URL m_documentBase = null;
/**
* Thread stuff for the trusted worker thread.
*/
transient private Thread m_callThread = null;
/**
*/
transient private TrustedAgent m_trustedAgent = null;
/**
* Thread for running TrustedAgent.
*/
transient private Thread m_trustedWorker = null;
/**
* Where the worker thread puts the HTML text.
*/
transient private String m_htmlText = null;
/**
* Where the worker thread puts the document/stylesheet text.
*/
transient private String m_sourceText = null;
/**
* Stylesheet attribute name and value that the caller can set.
*/
transient private String m_nameOfIDAttrOfElemToModify = null;
/**
*/
transient private String m_elemIdToModify = null;
/**
*/
transient private String m_attrNameToSet = null;
/**
*/
transient private String m_attrValueToSet = null;
/**
* The XSLTProcessorApplet constructor takes no arguments.
*/
public XSLTProcessorApplet(){}
/**
* Get basic information about the applet
* @return A String with the applet name and author.
*/
public String getAppletInfo()
{
return "Name: XSLTProcessorApplet\r\n" + "Author: Scott Boag";
}
/**
* Get descriptions of the applet parameters.
* @return A two-dimensional array of Strings with Name, Type, and Description
* for each parameter.
*/
public String[][] getParameterInfo()
{
String[][] info =
{
{ PARAM_styleURL, "String", "URL to an XSL stylesheet" },
{ PARAM_documentURL, "String", "URL to an XML document" },
};
return info;
}
/**
* Standard applet initialization.
*/
public void init()
{
// PARAMETER SUPPORT
// The following code retrieves the value of each parameter
// specified with the <PARAM> tag and stores it in a member
// variable.
//----------------------------------------------------------------------
String param;
// styleURL: Parameter description
//----------------------------------------------------------------------
param = getParameter(PARAM_styleURL);
if (param != null)
setStyleURL(param);
// documentURL: Parameter description
//----------------------------------------------------------------------
param = getParameter(PARAM_documentURL);
if (param != null)
setDocumentURL(param);
m_codeBase = this.getCodeBase();
m_documentBase = this.getDocumentBase();
// If you use a ResourceWizard-generated "control creator" class to
// arrange controls in your applet, you may want to call its
// CreateControls() method from within this method. Remove the following
// call to resize() before adding the call to CreateControls();
// CreateControls() does its own resizing.
//----------------------------------------------------------------------
resize(320, 240);
}
/**
* Automatically called when the HTML client containing the applet loads.
* This method starts execution of the applet thread.
*/
public void start()
{
m_trustedAgent = new TrustedAgent();
Thread currentThread = Thread.currentThread();
m_trustedWorker = new Thread(currentThread.getThreadGroup(),
m_trustedAgent);
m_trustedWorker.start();
try
{
m_tfactory = TransformerFactory.newInstance();
this.showStatus("Causing Transformer and Parser to Load and JIT...");
// Prime the pump so that subsequent transforms are faster.
StringReader xmlbuf = new StringReader("<?xml version='1.0'?><foo/>");
StringReader xslbuf = new StringReader(
"<?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>");
PrintWriter pw = new PrintWriter(new StringWriter());
synchronized (m_tfactory)
{
Templates templates = m_tfactory.newTemplates(new StreamSource(xslbuf));
Transformer transformer = templates.newTransformer();
transformer.transform(new StreamSource(xmlbuf), new StreamResult(pw));
}
System.out.println("Primed the pump!");
this.showStatus("Ready to go!");
}
catch (Exception e)
{
this.showStatus("Could not prime the pump!");
System.out.println("Could not prime the pump!");
e.printStackTrace();
}
}
/**
* Do not call; this applet contains no UI or visual components.
*
*/
public void paint(Graphics g){}
/**
* Automatically called when the HTML page containing the applet is no longer
* on the screen. Stops execution of the applet thread.
*/
public void stop()
{
if (null != m_trustedWorker)
{
m_trustedWorker.stop();
// m_trustedWorker.destroy();
m_trustedWorker = null;
}
m_styleURLOfCached = null;
m_documentURLOfCached = null;
}
/**
* Cleanup; called when applet is terminated and unloaded.
*/
public void destroy()
{
if (null != m_trustedWorker)
{
m_trustedWorker.stop();
// m_trustedWorker.destroy();
m_trustedWorker = null;
}
m_styleURLOfCached = null;
m_documentURLOfCached = null;
}
/**
* Set the URL to the XSL stylesheet that will be used
* to transform the input XML. No processing is done yet.
* @param urlString valid URL string for XSL stylesheet.
*/
public void setStyleURL(String urlString)
{
m_styleURL = urlString;
}
/**
* Set the URL to the XML document that will be transformed
* with the XSL stylesheet. No processing is done yet.
* @param urlString valid URL string for XML document.
*/
public void setDocumentURL(String urlString)
{
m_documentURL = urlString;
}
/**
* The processor keeps a cache of the source and
* style trees, so call this method if they have changed
* or you want to do garbage collection.
*/
public void freeCache()
{
m_styleURLOfCached = null;
m_documentURLOfCached = null;
}
/**
* Set an attribute in the stylesheet, which gives the ability
* to have some dynamic selection control.
* @param nameOfIDAttrOfElemToModify The name of an attribute to search for a unique id.
* @param elemId The unique ID to look for.
* @param attrName Once the element is found, the name of the attribute to set.
* @param value The value to set the attribute to.
*/
public void setStyleSheetAttribute(String nameOfIDAttrOfElemToModify,
String elemId, String attrName,
String value)
{
m_nameOfIDAttrOfElemToModify = nameOfIDAttrOfElemToModify;
m_elemIdToModify = elemId;
m_attrNameToSet = attrName;
m_attrValueToSet = value;
}
/**
* Stylesheet parameter key
*/
transient String m_key;
/**
* Stylesheet parameter value
*/
transient String m_expression;
/**
* Submit a stylesheet parameter.
*
* @param key stylesheet parameter key
* @param expr the parameter expression to be submitted.
* @see javax.xml.transform.Transformer#setParameter(String,Object)
*/
public void setStylesheetParam(String key, String expr)
{
m_key = key;
m_expression = expr;
}
/**
* Given a String containing markup, escape the markup so it
* can be displayed in the browser.
*
* @param s String to escape
*
* The escaped string.
*/
public String escapeString(String s)
{
StringBuffer sb = new StringBuffer();
int length = s.length();
for (int i = 0; i < length; i++)
{
char ch = s.charAt(i);
if ('<' == ch)
{
sb.append("&lt;");
}
else if ('>' == ch)
{
sb.append("&gt;");
}
else if ('&' == ch)
{
sb.append("&amp;");
}
else if (0xd800 <= ch && ch < 0xdc00)
{
// UTF-16 surrogate
int next;
if (i + 1 >= length)
{
throw new RuntimeException(
XSLMessages.createMessage(
XSLTErrorResources.ER_INVALID_UTF16_SURROGATE,
new Object[]{ Integer.toHexString(ch) })); //"Invalid UTF-16 surrogate detected: "
//+Integer.toHexString(ch)+ " ?");
}
else
{
next = s.charAt(++i);
if (!(0xdc00 <= next && next < 0xe000))
throw new RuntimeException(
XSLMessages.createMessage(
XSLTErrorResources.ER_INVALID_UTF16_SURROGATE,
new Object[]{
Integer.toHexString(ch) + " "
+ Integer.toHexString(next) })); //"Invalid UTF-16 surrogate detected: "
//+Integer.toHexString(ch)+" "+Integer.toHexString(next));
next = ((ch - 0xd800) << 10) + next - 0xdc00 + 0x00010000;
}
sb.append("&#x");
sb.append(Integer.toHexString(next));
sb.append(";");
}
else
{
sb.append(ch);
}
}
return sb.toString();
}
/**
* Assuming the stylesheet URL and the input XML URL have been set,
* perform the transformation and return the result as a String.
*
* @return A string that contains the contents pointed to by the URL.
*
*/
public String getHtmlText()
{
m_trustedAgent.m_getData = true;
m_callThread = Thread.currentThread();
try
{
synchronized (m_callThread)
{
m_callThread.wait();
}
}
catch (InterruptedException ie)
{
System.out.println(ie.getMessage());
}
return m_htmlText;
}
/**
* Get an XML document (or stylesheet)
*
* @param treeURL valid URL string for the document.
*
* @return document
*
* @throws IOException
*/
public String getTreeAsText(String treeURL) throws IOException
{
m_treeURL = treeURL;
m_trustedAgent.m_getData = true;
m_trustedAgent.m_getSource = true;
m_callThread = Thread.currentThread();
try
{
synchronized (m_callThread)
{
m_callThread.wait();
}
}
catch (InterruptedException ie)
{
System.out.println(ie.getMessage());
}
return m_sourceText;
}
/**
* Use a Transformer to copy the source document
* to a StreamResult.
*
* @return the document as a string
*/
private String getSource() throws TransformerException
{
StringWriter osw = new StringWriter();
PrintWriter pw = new PrintWriter(osw, false);
String text = "";
try
{
URL docURL = new URL(m_documentBase, m_treeURL);
synchronized (m_tfactory)
{
Transformer transformer = m_tfactory.newTransformer();
StreamSource source = new StreamSource(docURL.toString());
StreamResult result = new StreamResult(pw);
transformer.transform(source, result);
text = osw.toString();
}
}
catch (MalformedURLException e)
{
e.printStackTrace();
System.exit(-1);
}
catch (Exception any_error)
{
any_error.printStackTrace();
}
return text;
}
/**
* Get the XML source Tree as a text string suitable
* for display in a browser. Note that this is for display of the
* XML itself, not for rendering of HTML by the browser.
*
* @return XML source document as a string.
* @throws Exception thrown if tree can not be converted.
*/
public String getSourceTreeAsText() throws Exception
{
return getTreeAsText(m_documentURL);
}
/**
* Get the XSL style Tree as a text string suitable
* for display in a browser. Note that this is for display of the
* XML itself, not for rendering of HTML by the browser.
*
* @return The XSL stylesheet as a string.
* @throws Exception thrown if tree can not be converted.
*/
public String getStyleTreeAsText() throws Exception
{
return getTreeAsText(m_styleURL);
}
/**
* Get the HTML result Tree as a text string suitable
* for display in a browser. Note that this is for display of the
* XML itself, not for rendering of HTML by the browser.
*
* @return Transformation result as unmarked text.
* @throws Exception thrown if tree can not be converted.
*/
public String getResultTreeAsText() throws Exception
{
return escapeString(getHtmlText());
}
/**
* Process a document and a stylesheet and return
* the transformation result. If one of these is null, the
* existing value (of a previous transformation) is not affected.
*
* @param doc URL string to XML document
* @param style URL string to XSL stylesheet
*
* @return HTML transformation result
*/
public String transformToHtml(String doc, String style)
{
if (null != doc)
{
m_documentURL = doc;
}
if (null != style)
{
m_styleURL = style;
}
return getHtmlText();
}
/**
* Process a document and a stylesheet and return
* the transformation result. Use the xsl:stylesheet PI to find the
* document, if one exists.
*
* @param doc URL string to XML document containing an xsl:stylesheet PI.
*
* @return HTML transformation result
*/
public String transformToHtml(String doc)
{
if (null != doc)
{
m_documentURL = doc;
}
m_styleURL = null;
return getHtmlText();
}
/**
* Process the transformation.
*
* @return The transformation result as a string.
*
* @throws TransformerException
*/
private String processTransformation() throws TransformerException
{
String htmlData = null;
this.showStatus("Waiting for Transformer and Parser to finish loading and JITing...");
synchronized (m_tfactory)
{
URL documentURL = null;
URL styleURL = null;
StringWriter osw = new StringWriter();
PrintWriter pw = new PrintWriter(osw, false);
StreamResult result = new StreamResult(pw);
this.showStatus("Begin Transformation...");
try
{
documentURL = new URL(m_codeBase, m_documentURL);
StreamSource xmlSource = new StreamSource(documentURL.toString());
styleURL = new URL(m_codeBase, m_styleURL);
StreamSource xslSource = new StreamSource(styleURL.toString());
Transformer transformer = m_tfactory.newTransformer(xslSource);
if (null != m_key)
transformer.setParameter(m_key, m_expression);
transformer.transform(xmlSource, result);
}
catch (TransformerConfigurationException tfe)
{
tfe.printStackTrace();
System.exit(-1);
}
catch (MalformedURLException e)
{
e.printStackTrace();
System.exit(-1);
}
this.showStatus("Transformation Done!");
htmlData = osw.toString();
}
return htmlData;
}
/**
* This class maintains a worker thread that that is
* trusted and can do things like access data. You need
* this because the thread that is called by the browser
* is not trusted and can't access data from the URLs.
*/
class TrustedAgent implements Runnable
{
/**
* Specifies whether the worker thread should perform a transformation.
*/
public boolean m_getData = false;
/**
* Specifies whether the worker thread should get an XML document or XSL stylesheet.
*/
public boolean m_getSource = false;
/**
* The real work is done from here.
*
*/
public void run()
{
while (true)
{
m_trustedWorker.yield();
if (m_getData) // Perform a transformation or get a document.
{
try
{
m_getData = false;
m_htmlText = null;
m_sourceText = null;
if (m_getSource) // Get a document.
{
m_getSource = false;
m_sourceText = getSource();
}
else // Perform a transformation.
m_htmlText = processTransformation();
}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
synchronized (m_callThread)
{
m_callThread.notify();
}
}
}
else
{
try
{
m_trustedWorker.sleep(50);
}
catch (InterruptedException ie)
{
ie.printStackTrace();
}
}
}
}
}
}