/* ===============================================================================
*
* Part of the InfoGlue Content Management Platform (www.infoglue.org)
*
* ===============================================================================
*
* Copyright (C)
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License version 2, as published by the
* Free Software Foundation. See the file LICENSE.html for more information.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY, including the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc. / 59 Temple
* Place, Suite 330 / Boston, MA 02111-1307 / USA.
*
* ===============================================================================
*/
package org.infoglue.deliver.taglib.common;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.Reader;
import java.io.StringReader;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.servlet.jsp.JspException;
import javax.xml.transform.Source;
import javax.xml.transform.Templates;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import net.sf.saxon.om.NodeInfo;
import net.sf.saxon.tinytree.TinyBuilder;
import net.sf.saxon.tinytree.TinyDocumentImpl;
import org.apache.log4j.Logger;
import org.infoglue.deliver.taglib.TemplateControllerTag;
import org.infoglue.deliver.util.CacheController;
import org.infoglue.deliver.util.Timer;
import org.w3c.dom.Document;
/**
* This tag help modifying texts in different ways. An example is to html-encode strings or replace all
* whitespaces with or replacing linebreaks with <br/>
*/
public class XSLTransformTag extends TemplateControllerTag
{
private final static Logger logger = Logger.getLogger(XSLTransformTag.class.getName());
/**
* The universal version identifier.
*/
private static final long serialVersionUID = 8603406098980150888L;
private Object xml;
private String xmlFile;
private String xmlString;
private Object source;
private boolean cacheStyle = true;
private String styleFile;
private String styleString;
private String outputFormat = "string";
private Map parameters = new HashMap();
private Map outputParameters = new HashMap();
/**
* Default constructor.
*/
public XSLTransformTag()
{
super();
}
/**
* Initializes the parameters to make it accessible for the children tags (if any).
*
* @return indication of whether to evaluate the body or not.
* @throws JspException if an error occurred while processing this tag.
*/
public int doStartTag() throws JspException
{
return EVAL_BODY_INCLUDE;
}
/**
* Process the end tag. Modifies the string according to settings made.
*
* @return indication of whether to continue evaluating the JSP page.
* @throws JspException if an error occurred while processing this tag.
*/
public int doEndTag() throws JspException
{
Timer timer = new Timer();
java.lang.System.setProperty("javax.xml.transform.TransformerFactory", "net.sf.saxon.TransformerFactoryImpl");
Source xmlSource = null;
if(xml != null)
{
if(xml instanceof String)
xmlSource = new StreamSource(new StringReader(this.xmlString));
else if(xml instanceof Reader)
xmlSource = new StreamSource((Reader)xml);
}
else if(this.source != null)
{
if(logger.isDebugEnabled())
logger.info("Input:" + this.source.getClass().getName());
if(this.source instanceof DOMResult)
xmlSource = new DOMSource(((DOMResult)this.source).getNode());
else if(this.source instanceof DOMSource)
xmlSource = (DOMSource)this.source;
else if(this.source instanceof SAXSource)
xmlSource = (SAXSource)this.source;
else if(this.source instanceof StreamSource)
xmlSource = (StreamSource)this.source;
else if(this.source instanceof Document)
xmlSource = new DOMSource((Document)this.source);
else if(this.source instanceof NodeInfo)
xmlSource = (Source)this.source;
else if(this.source instanceof TinyDocumentImpl)
xmlSource = (TinyDocumentImpl)this.source;
else
throw new JspException("Bad source - must be either org.w3c.Document or javax.xml.transform.Source");
}
else if(this.xmlFile != null)
{
xmlSource = new StreamSource(new File(this.xmlFile));
}
else if(this.xmlString != null)
{
xmlSource = new StreamSource(new StringReader(this.xmlString));
}
Templates pss = null;
Transformer transformer = null;
try
{
pss = tryCache(this.styleFile, this.styleString, cacheStyle);
transformer = pss.newTransformer();
if(logger.isDebugEnabled())
logger.info("outputFormat:" + this.outputFormat);
if(this.outputFormat.equalsIgnoreCase("string"))
{
java.io.ByteArrayOutputStream outputXmlResult = new java.io.ByteArrayOutputStream();
BufferedOutputStream bos = new BufferedOutputStream(outputXmlResult);
Iterator parametersIterator = parameters.keySet().iterator();
while(parametersIterator.hasNext())
{
String name = (String)parametersIterator.next();
Object value = parameters.get(name);
transformer.setParameter(name, value);
}
Iterator outputParametersIterator = outputParameters.keySet().iterator();
while(parametersIterator.hasNext())
{
String name = (String)parametersIterator.next();
String value = (String)parameters.get(name);
transformer.setOutputProperty(name, value);
}
transformer.transform(xmlSource, new StreamResult(bos));
bos.close();
String result = outputXmlResult.toString();
setResultAttribute(result);
}
else if(this.outputFormat.equalsIgnoreCase("document"))
{
DOMResult domResult = new DOMResult();
Iterator parametersIterator = parameters.keySet().iterator();
while(parametersIterator.hasNext())
{
String name = (String)parametersIterator.next();
Object value = parameters.get(name);
transformer.setParameter(name, value);
}
Iterator outputParametersIterator = outputParameters.keySet().iterator();
while(parametersIterator.hasNext())
{
String name = (String)parametersIterator.next();
String value = (String)parameters.get(name);
transformer.setOutputProperty(name, value);
}
transformer.transform(xmlSource, domResult);
setResultAttribute(domResult.getNode());
}
else if(this.outputFormat.equalsIgnoreCase("tinyDocument"))
{
Iterator parametersIterator = parameters.keySet().iterator();
while(parametersIterator.hasNext())
{
String name = (String)parametersIterator.next();
Object value = parameters.get(name);
transformer.setParameter(name, value);
}
Iterator outputParametersIterator = outputParameters.keySet().iterator();
while(parametersIterator.hasNext())
{
String name = (String)parametersIterator.next();
String value = (String)parameters.get(name);
transformer.setOutputProperty(name, value);
}
TinyBuilder builder = new TinyBuilder();
transformer.transform(xmlSource, builder);
setResultAttribute(builder.getCurrentRoot());
}
if(logger.isInfoEnabled())
timer.printElapsedTime("Saxon Transform to dom document took");
}
catch (Exception e)
{
logger.error("Error transforming with SAXON:" + e.getMessage(), e);
}
finally
{
try
{
transformer.clearParameters();
transformer.reset();
parameters.clear();
outputParameters.clear();
}
catch (NoSuchMethodError e)
{
logger.warn("Problem resetting transformer -wrong java version:" + e.getMessage());
}
catch (Exception e)
{
logger.warn("Problem resetting transformer:" + e.getMessage(), e);
}
transformer = null;
pss = null;
xmlSource = null;
this.xml = null;
this.xmlFile = null;
this.source = null;
this.xmlString = null;
this.styleFile = null;
this.styleString = null;
this.outputFormat = "string";
this.cacheStyle = true;
java.lang.System.clearProperty("javax.xml.transform.TransformerFactory");
}
return EVAL_PAGE;
}
/**
* Maintain prepared stylesheets in memory for reuse
*/
private synchronized Templates tryCache(String url, String xslString, boolean cacheTemplate) throws Exception
{
Templates x = null;
if(url != null)
{
String path = this.getController().getHttpServletRequest().getRealPath(url);
if (path==null)
{
throw new Exception("Stylesheet " + url + " not found");
}
x = (Templates)CacheController.getCachedObject("XSLTemplatesCache", path);
if (x==null)
{
TransformerFactory factory = TransformerFactory.newInstance();
x = factory.newTemplates(new StreamSource(new File(path)));
if(cacheTemplate)
CacheController.cacheObject("XSLTemplatesCache", path, x);
}
}
else if(xslString != null)
{
x = (Templates)CacheController.getCachedObject("XSLTemplatesCache", xslString.hashCode());
if (x==null)
{
TransformerFactory factory = TransformerFactory.newInstance();
x = factory.newTemplates(new StreamSource(new StringReader(xslString)));
if(cacheTemplate)
CacheController.cacheObject("XSLTemplatesCache", xslString.hashCode(), x);
}
}
return x;
}
public void setXmlFile(String xmlFile) throws JspException
{
this.xmlFile = evaluateString("XSLTransform", "xmlFile", xmlFile);
}
public void setXmlString(String xmlString) throws JspException
{
this.xmlString = evaluateString("XSLTransform", "xmlString", xmlString);
}
public void setSource(String source) throws JspException
{
this.source = evaluate("XSLTransform", "source", source, Object.class);
}
public void setSourceObject(Object source) throws JspException
{
this.source = source;
}
public void setStyleFile(String styleFile) throws JspException
{
this.styleFile = evaluateString("XSLTransform", "styleFile", styleFile);
}
public void setStyleString(String styleString) throws JspException
{
this.styleString = evaluateString("XSLTransform", "styleString", styleString);
}
public void setCacheStyle(boolean cacheStyle) throws JspException
{
this.cacheStyle = cacheStyle;
}
public void setOutputFormat(String outputFormat) throws JspException
{
this.outputFormat = evaluateString("XSLTransform", "outputFormat", outputFormat);
}
protected final void addParameter(final String name, final Object value, final String scope)
{
if(scope != null && scope.equalsIgnoreCase("outputProperty"))
outputParameters.put(name, value);
else
parameters.put(name, value);
}
}