/*
* Copyright 2000-2004 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.jetspeed.util;
//java stuff
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.io.Reader;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Iterator;
import java.util.Map;
//Trax support
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.Templates;
//import javax.xml.transform.stream.StreamSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.sax.SAXTransformerFactory;
import javax.xml.transform.sax.TemplatesHandler;
import javax.xml.transform.sax.TransformerHandler;
//xpath objects
import org.apache.xpath.objects.XString;
//SAX Suport
import org.xml.sax.XMLReader;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.XMLReaderFactory;
import org.xml.sax.InputSource;
//DOM Support
import org.w3c.dom.Document;
//Jetspeed stuff
import org.apache.jetspeed.cache.disk.JetspeedDiskCache;
import org.apache.jetspeed.services.logging.JetspeedLogFactoryService;
import org.apache.jetspeed.services.logging.JetspeedLogger;
import org.apache.jetspeed.xml.JetspeedXMLEntityResolver;
/**
* Provides a very simple mechanism to transform a document using XSLT using
* one XML document and another XSL document. This implementation uses the TRAX API.
* It can be used with any TRAX transformer. This can be used for very
* simple XML -> XSL processing to reduce the complexity and possibility of a
* runtime failure.
*
* @author <a href="mailto:burton@apache.org">Kevin A. Burton</a>
* @author <a href="mailto:sgala@apache.org">Santiago Gala</a>
* @version $Id: SimpleTransform.java,v 1.23 2004/02/23 03:23:42 jford Exp $
*/
public class SimpleTransform
{
/**
* Static initialization of the logger for this class
*/
private static final JetspeedLogger logger = JetspeedLogFactoryService.getLogger(SimpleTransform.class.getName());
//FIXME: This code should go into the Turbine XSLTservice.
//Also, it is a temporary hack, as it should be configurable,
// and done later.
static
{
try
{
if( System.getProperty( "org.xml.sax.driver" ) == null )
{
System.setProperty( "org.xml.sax.driver",
"org.apache.xerces.parsers.SAXParser" );
}
}
catch (Throwable t)
{
//be very cautious here. We are in class initialization.
t.printStackTrace();
}
}
/**
* Given a a DOM and a URL to a stylesheet,
* transform the original document.
*/
public static String transform( Document doc,
String stylesheet_url)
throws SAXException
{
return transform( doc, stylesheet_url, null );
}
/**
* Given a a DOM and a URL to a stylesheet,
* transform the original document,
* passing parameters to the stylesheet
*/
public static String transform( Document doc,
String stylesheet_url,
Map params)
throws SAXException
{
// Instantiate a TransformerFactory.
TransformerFactory tFactory = TransformerFactory.newInstance();
// Determine whether the TransformerFactory supports the use of SAXSource
// and SAXResult
if (!tFactory.getFeature(SAXTransformerFactory.FEATURE) )
{
logger.error( "SimpleTransform: nobody told you that we need a SAX Transformer?" );
throw new SAXException( "Invalid SAX Tranformer" );
}
try
{
// Cast the TransformerFactory.
SAXTransformerFactory saxTFactory = ((SAXTransformerFactory) tFactory);
// Create a ContentHandler to handle parsing of the stylesheet.
TemplatesHandler templatesHandler = saxTFactory.newTemplatesHandler();
// Create an XMLReader and set its ContentHandler.
XMLReader reader = XMLReaderFactory.createXMLReader();
reader.setContentHandler(templatesHandler);
// Set it to solve Entities through Jetspeed URL Manager
reader.setEntityResolver( new JetspeedXMLEntityResolver() );
// Parse the stylesheet.
final InputSource xstyle = new InputSource( JetspeedDiskCache.getInstance()
.getEntry( stylesheet_url ).getReader() );
xstyle.setSystemId( stylesheet_url );
reader.parse( xstyle );
//Get the Templates object from the ContentHandler.
Templates templates = templatesHandler.getTemplates();
// Create a ContentHandler to handle parsing of the XML source.
TransformerHandler handler
= saxTFactory.newTransformerHandler(templates);
// Reset the XMLReader's ContentHandler.
reader.setContentHandler(handler);
// Set the ContentHandler to also function as a LexicalHandler, which
// includes "lexical" events (e.g., comments and CDATA).
try
{
reader.setProperty("http://xml.org/sax/properties/lexical-handler", handler);
}
catch( org.xml.sax.SAXNotRecognizedException e ) {}
final Transformer processor = handler.getTransformer();
if( params != null ) {
Iterator keys = params.keySet().iterator();
while( keys.hasNext() )
{
String name = (String) keys.next();
String value = (String) params.get(name);
processor.setParameter(name,
value ); //FIXME: maybe we need to quote again...
// was processor.createXString( value)
}
}
StringWriter pw = new StringWriter();
// Have the XSLTProcessor processor object transform "foo.xml" to
// System.out, using the XSLT instructions found in "foo.xsl".
processor.transform( new DOMSource( doc ),
new StreamResult( pw ) );
try
{
pw.flush();
pw.close();
}
catch (IOException e)
{
//should never really happen
logger.error("Exception", e);
}
return pw.toString();
}
catch (Exception e)
{
logger.error( "Invalid SAX Transformer: " , e );
throw new SAXException( "problem in SAX transform: " + e.toString() );
}
}
/**
* Given a URL to an XML file and a URL to a stylesheet, transform the
* original document.
*/
public static String transform( String url,
String stylesheet_url )
throws SAXException
{
return transform( url, stylesheet_url, null );
}
/**
* Given a URL to an XML file and a URL to a stylesheet, transform the
* original document.
*/
public static String transform( String url,
String stylesheet_url,
Map params )
throws SAXException
{
//bring these URLs local if they happen to be remote
InputSource in;
InputSource style;
try
{
in = new InputSource( JetspeedDiskCache.getInstance().getEntry( url ).getReader() );
style = new InputSource( JetspeedDiskCache.getInstance().getEntry( stylesheet_url ).getReader() );
}
catch (IOException e)
{
logger.error("Exception", e);
//at this point we can just use the original url and stylesheet_url so this shouldn't be a problem
in = new InputSource( url );
style = new InputSource( stylesheet_url );
}
if ( logger.isInfoEnabled() )
{
logger.info( "SimpleTransform: transforming url: " +
url +
" with stylesheet: " +
stylesheet_url );
}
in.setSystemId( url );
style.setSystemId( stylesheet_url );
return transform( in,
style,
params );
}
/**
* Used internally to handle doing XSLT transformations directly.
*/
public static String transform( InputSource content,
InputSource stylesheet,
Map params)
throws SAXException
{
// Instantiate a TransformerFactory.
TransformerFactory tFactory = TransformerFactory.newInstance();
// Determine whether the TransformerFactory supports the use of SAXSource
// and SAXResult
if (!tFactory.getFeature(SAXTransformerFactory.FEATURE) )
{
logger.error( "SimpleTransform: nobody told you that we need a SAX Transformer?" );
throw new SAXException( "Invalid SAX Tranformer" );
}
try
{
// Cast the TransformerFactory.
SAXTransformerFactory saxTFactory = ((SAXTransformerFactory) tFactory);
// Create a ContentHandler to handle parsing of the stylesheet.
TemplatesHandler templatesHandler = saxTFactory.newTemplatesHandler();
// Create an XMLReader and set its ContentHandler.
XMLReader reader = XMLReaderFactory.createXMLReader();
reader.setContentHandler(templatesHandler);
// Parse the stylesheet.
reader.parse( stylesheet );
//Get the Templates object from the ContentHandler.
Templates templates = templatesHandler.getTemplates();
// Create a ContentHandler to handle parsing of the XML source.
TransformerHandler handler
= saxTFactory.newTransformerHandler(templates);
// Reset the XMLReader's ContentHandler.
reader.setContentHandler(handler);
// Set the ContentHandler to also function as a LexicalHandler, which
// includes "lexical" events (e.g., comments and CDATA).
try
{
reader.setProperty("http://xml.org/sax/properties/lexical-handler", handler);
}
catch( org.xml.sax.SAXNotRecognizedException e ) {}
final Transformer processor = handler.getTransformer();
if( params != null )
{
Iterator keys = params.keySet().iterator();
while( keys.hasNext() )
{
String name = (String) keys.next();
String value = (String) params.get(name);
processor.setParameter(name,
new XString( value )
/*FIXME: was processor.createXString( value) */ );
}
}
StringWriter pw = new StringWriter();
// Have the XSLTProcessor processor object transform "foo.xml" to
// System.out, using the XSLT instructions found in "foo.xsl".
processor.transform( new SAXSource( content ),
new StreamResult( pw ) );
try
{
pw.flush();
pw.close();
}
catch (IOException e)
{
//should never really happen
logger.error("Exception", e);
}
return pw.toString();
}
catch (Exception e)
{
logger.error( "Invalid SAX Transformer: " , e);
throw new SAXException( "problem in SAX transform: " + e.toString() );
}
}
/**
* Perform a event based parsing of the given content_url,
* process it with the XSLT stylesheet stylesheet_url, using the params
* parameters, and return a Reader that will do the transformation dynamically.
*
* @param content_url The url of the xml document
* @param stylesheet_url The url of the stylesheet
* @param params A Map containing stylesheet parameters
* @return a Reader on the transformed document
*
*/
public static Reader SAXTransform( String content_url,
String stylesheet_url,
Map params) throws IOException
{
// Instantiate a TransformerFactory.
TransformerFactory tFactory = TransformerFactory.newInstance();
// Determine whether the TransformerFactory supports the use of SAXSource
// and SAXResult
if (!tFactory.getFeature(SAXTransformerFactory.FEATURE) )
{
logger.error( "SimpleTransform: nobody told you that we need a SAX Transformer?" );
throw new IOException( "Invalid SAX Tranformer" );
}
try
{
// Cast the TransformerFactory.
SAXTransformerFactory saxTFactory = ((SAXTransformerFactory) tFactory);
// Create a ContentHandler to handle parsing of the stylesheet.
TemplatesHandler templatesHandler = saxTFactory.newTemplatesHandler();
// Create an XMLReader and set its ContentHandler.
XMLReader reader = XMLReaderFactory.createXMLReader();
reader.setContentHandler(templatesHandler);
// Set it to solve Entities through Jetspeed URL Manager
reader.setEntityResolver( new JetspeedXMLEntityResolver() );
// Parse the stylesheet.
InputSource style = new InputSource( JetspeedDiskCache.getInstance()
.getEntry( stylesheet_url ).getReader() );
style.setSystemId( stylesheet_url );
final InputSource xstyle = style;
reader.parse( xstyle );
//Get the Templates object from the ContentHandler.
Templates templates = templatesHandler.getTemplates();
// Create a ContentHandler to handle parsing of the XML source.
TransformerHandler handler
= saxTFactory.newTransformerHandler(templates);
// Reset the XMLReader's ContentHandler.
reader.setContentHandler(handler);
// Set the ContentHandler to also function as a LexicalHandler, which
// includes "lexical" events (e.g., comments and CDATA).
try
{
reader.setProperty("http://xml.org/sax/properties/lexical-handler", handler);
}
catch( org.xml.sax.SAXNotRecognizedException e ) {}
final Transformer processor = handler.getTransformer();
//Set the parameters (if any)
if( params != null )
{
Iterator keys = params.keySet().iterator();
while( keys.hasNext() )
{
String name = (String) keys.next();
String value = (String) params.get(name);
//FIXME: maybe we need to quote again...
// was processor.createXString( value)
processor.setParameter(name,
new XString( value ) );
}
}
PipedInputStream pis = new PipedInputStream();
PipedOutputStream pos = new PipedOutputStream( pis );
try
{
final Writer pw = new OutputStreamWriter( pos, "utf-8" );
InputSource is = new InputSource( JetspeedDiskCache.getInstance()
.getEntry( content_url ).getReader() );
is.setSystemId( content_url );
final SAXSource xinput = new SAXSource( is );
//Perform the transformation on a new thread, using
// PipedStreams
Thread t = new Thread( new Runnable()
{
public void run()
{
// Have the processor object transform
// "foo.xml" to
// System.out, using the XSLT instructions
//found in "foo.xsl".
logger.debug("Starting SAX thread...");
try
{
processor.transform( xinput,
new StreamResult( pw ) );
pw.close();
logger.debug("...ending SAX thread.");
}
catch( Exception se)
{
logger.debug("Error in SAXTransform" + se.toString(), se );
}
}
} );
t.start();
}
catch (java.io.UnsupportedEncodingException uee)
{
logger.error("Need utf-8 encoding to SAXTransform", uee);
}
return new InputStreamReader ( pis, "utf-8" );
}
catch (Exception e)
{
logger.error( "Invalid SAX Transformer:" , e);
throw new IOException( "problem in SAX transform: " + e.toString() );
}
}
}