@@ -58,6 +58,11 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.IOException;
import java.io.BufferedOutputStream;
import java.io.BufferedInputStream;
import java.io.OutputStream;
import java.io.InputStream;
import java.util.Vector;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.taskdefs.XSLTLiaison;
import org.apache.tools.ant.taskdefs.XSLTLiaison;
@@ -80,6 +85,7 @@ import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import javax.xml.transform.stream.StreamSource;
import javax.xml.transform.Source;
import javax.xml.transform.Source;
import javax.xml.transform.URIResolver;
import javax.xml.transform.URIResolver;
import javax.xml.transform.SourceLocator;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.sax.SAXSource;
@@ -93,118 +99,154 @@ import javax.xml.transform.sax.SAXSource;
*/
*/
public class TraXLiaison implements XSLTLiaison, ErrorListener, XSLTLoggerAware {
public class TraXLiaison implements XSLTLiaison, ErrorListener, XSLTLoggerAware {
/**
* the name of the factory implementation class to use
* or null for default JAXP lookup.
*/
private String factoryName = null;
/** The trax TransformerFactory */
/** The trax TransformerFactory */
private TransformerFactory tfactory = null;
private TransformerFactory tfactory = null;
/** stylesheet stream, close it asap */
private FileInputStream xslStream = null;
/** Stylesheet template */
private Templates templates = null;
/** transformer */
private Transformer transformer = null;
/** stylesheet to use for transformation */
private File stylesheet;
private XSLTLogger logger;
private XSLTLogger logger;
/** possible resolver for publicIds */
/** possible resolver for publicIds */
private EntityResolver entityResolver;
private EntityResolver entityResolver;
/** transformer to use for processing files */
private Transformer transformer;
/** possible resolver for URIs */
/** possible resolver for URIs */
private URIResolver uriResolver;
private URIResolver uriResolver;
public TraXLiaison() throws Exception {
tfactory = TransformerFactory.newInstance();
tfactory.setErrorListener(this);
}
/**
* Set the output property for the current transformer.
* Note that the stylesheet must be set prior to calling
* this method.
* @param name the output property name.
* @param value the output property value.
*/
public void setOutputProperty(String name, String value){
if (transformer == null){
throw new IllegalStateException("stylesheet must be set prior to setting the output properties");
}
transformer.setOutputProperty(name, value);
}
/** transformer output properties */
private Vector outputProperties = new Vector();
//------------------- IMPORTANT
// 1) Don't use the StreamSource(File) ctor. It won't work with
// xalan prior to 2.2 because of systemid bugs.
/** stylesheet parameters */
private Vector params = new Vector();
// 2) Use a stream so that you can close it yourself quickly
// and avoid keeping the handle until the object is garbaged.
// (always keep control), otherwise you won't be able to delete
// the file quickly on windows.
/** factory attributes */
private Vector attributes = new Vector();
// 3) Always set the systemid to the source for imports, includes...
// in xsl and xml...
public TraXLiaison() throws Exception {
}
public void setStylesheet(File stylesheet) throws Exception {
public void setStylesheet(File stylesheet) throws Exception {
xslStream = new FileInputStream(stylesheet);
StreamSource src = new StreamSource(xslStream);
src.setSystemId(getSystemId(stylesheet));
templates = tfactory.newTemplates(src);
transformer = templates.newTransformer();
transformer.setErrorListener(this);
this.stylesheet = stylesheet;
}
}
public void transform(File infile, File outfile) throws Exception {
public void transform(File infile, File outfile) throws Exception {
FileInputStream fis = null;
FileOutputStream fos = null;
if (transformer == null) {
transformer = newTransformer();
}
InputStream fis = null;
OutputStream fos = null;
try {
try {
fis = new FileInputStream(infile);
fos = new FileOutputStream(outfile);
// FIXME: need to use a SAXSource as the source for the transform
// so we can plug in our own entity resolver
Source src = null;
if (entityResolver != null) {
if (tfactory.getFeature(SAXSource.FEATURE)) {
SAXParserFactory spFactory = SAXParserFactory.newInstance();
spFactory.setNamespaceAware(true);
XMLReader reader = spFactory.newSAXParser().getXMLReader();
reader.setEntityResolver(entityResolver);
src = new SAXSource(reader, new InputSource(fis));
} else {
throw new IllegalStateException("xcatalog specified, but " +
"parser doesn't support SAX");
}
} else {
src = new StreamSource(fis);
}
src.setSystemId(getSystemId(infile));
fis = new BufferedInputStream(new FileInputStream(infile));
fos = new BufferedOutputStream(new FileOutputStream(outfile));
StreamResult res = new StreamResult(fos);
StreamResult res = new StreamResult(fos);
// not sure what could be the need of this...
// not sure what could be the need of this...
res.setSystemId(getSystemId(outfile));
res.setSystemId(getSystemId(outfile));
if (uriResolver != null)
transformer.setURIResolver(uriResolver);
Source src = getSource(fis, infile);
transformer.transform(src, res);
transformer.transform(src, res);
} finally {
} finally {
// make sure to close all handles, otherwise the garbage
// make sure to close all handles, otherwise the garbage
// collector will close them...whenever possible and
// collector will close them...whenever possible and
// Windows may complain about not being able to delete files.
// Windows may complain about not being able to delete files.
try {
try {
if (xslStream != null){
xslStream.close();
}
} catch (IOException ignored){}
try {
if (fis != null){
if (fis != null) {
fis.close();
fis.close();
}
}
} catch (IOException ignored){}
} catch (IOException ignored) {
}
try {
try {
if (fos != null){
if (fos != null) {
fos.close();
fos.close();
}
}
} catch (IOException ignored){}
} catch (IOException ignored) {
}
}
}
/**
* Get the source instance from the stream and id of the file.
* @param is the stream containing the stylesheet data.
* @param infile the file that will be used for the systemid.
* @return the configured source instance matching the stylesheet.
* @throws Exception if there is a problem creating the source.
*/
private Source getSource(InputStream is, File infile) throws Exception {
// todo: is this comment still relevant ??
// FIXME: need to use a SAXSource as the source for the transform
// so we can plug in our own entity resolver
Source src = null;
if (entityResolver != null) {
if (getFactory().getFeature(SAXSource.FEATURE)) {
SAXParserFactory spFactory = SAXParserFactory.newInstance();
spFactory.setNamespaceAware(true);
XMLReader reader = spFactory.newSAXParser().getXMLReader();
reader.setEntityResolver(entityResolver);
src = new SAXSource(reader, new InputSource(is));
} else {
throw new IllegalStateException("xcatalog specified, but " +
"parser doesn't support SAX");
}
} else {
src = new StreamSource(is);
}
src.setSystemId(getSystemId(infile));
return src;
}
/**
* Create a new transformer based on the liaison settings
* @return the newly created and configured transformer.
* @throws Exception thrown if there is an error during creation.
* @see #setStylesheet(java.io.File)
* @see #addParam(java.lang.String, java.lang.String)
* @see #setOutputProperty(java.lang.String, java.lang.String)
*/
private Transformer newTransformer() throws Exception {
// WARN: Don't use the StreamSource(File) ctor. It won't work with
// xalan prior to 2.2 because of systemid bugs.
// Use a stream so that you can close it yourself quickly
// and avoid keeping the handle until the object is garbaged.
// (always keep control), otherwise you won't be able to delete
// the file quickly on windows.
InputStream xslStream = new BufferedInputStream(
new FileInputStream(stylesheet));
try {
StreamSource src = new StreamSource(xslStream);
// Always set the systemid to the source for imports, includes...
// in xsl and xml...
src.setSystemId(getSystemId(stylesheet));
Templates templates = getFactory().newTemplates(src);
Transformer transformer = templates.newTransformer();
// configure the transformer...
transformer.setErrorListener(this);
if (uriResolver != null) {
transformer.setURIResolver(uriResolver);
}
for (int i = 0; i < params.size(); i++) {
final String[] pair = (String[]) params.elementAt(i);
transformer.setParameter(pair[0], pair[1]);
}
for (int i = 0; i < outputProperties.size(); i++) {
final String[] pair = (String[]) outputProperties.elementAt(i);
transformer.setOutputProperty(pair[0], pair[1]);
}
return transformer;
} finally {
try {
xslStream.close();
} catch (IOException ignored) {
}
}
}
}
}
@@ -212,7 +254,7 @@ public class TraXLiaison implements XSLTLiaison, ErrorListener, XSLTLoggerAware
// crimson will complain that it cannot resolve relative entities
// crimson will complain that it cannot resolve relative entities
// because it grabs the base uri via lastIndexOf('/') without
// because it grabs the base uri via lastIndexOf('/') without
// making sure it is really a /'ed path
// making sure it is really a /'ed path
protected String getSystemId(File file){
protected String getSystemId(File file) {
String path = file.getAbsolutePath();
String path = file.getAbsolutePath();
path = path.replace('\\', '/');
path = path.replace('\\', '/');
@@ -224,24 +266,107 @@ public class TraXLiaison implements XSLTLiaison, ErrorListener, XSLTLoggerAware
return FILE_PROTOCOL_PREFIX + path;
return FILE_PROTOCOL_PREFIX + path;
}
}
public void addParam(String name, String value){
transformer.setParameter(name, value);
/**
* return the Transformer factory associated to this liaison.
* @return the Transformer factory associated to this liaison.
* @throws BuildException thrown if there is a problem creating
* the factory.
* @see #setFactory(String)
* @since Ant 1.6
*/
private TransformerFactory getFactory() throws BuildException {
if (tfactory != null) {
return tfactory;
}
// not initialized yet, so create the factory
if (factoryName == null) {
tfactory = TransformerFactory.newInstance();
} else {
try {
Class clazz = Class.forName(factoryName);
tfactory = (TransformerFactory) clazz.newInstance();
} catch (Exception e) {
throw new BuildException(e);
}
}
tfactory.setErrorListener(this);
// specific attributes for the transformer
for (int i = 0; i < attributes.size(); i++) {
final Object[] pair = (Object[])attributes.elementAt(i);
tfactory.setAttribute((String)pair[0], pair[1]);
}
return tfactory;
}
/**
* Set the factory name to use instead of JAXP default lookup.
* @param name the fully qualified class name of the factory to use
* or null for the default JAXP look up mechanism.
* @since Ant 1.6
*/
public void setFactory(String name) {
factoryName = name;
}
/**
* Set a custom attribute for the JAXP factory implementation.
* @param name the attribute name.
* @param value the value of the attribute, usually a boolean
* string or object.
* @since Ant 1.6
*/
public void setAttribute(String name, Object value){
final Object[] pair = new Object[]{name, value};
attributes.addElement(pair);
}
/**
* Set the output property for the current transformer.
* Note that the stylesheet must be set prior to calling
* this method.
* @param name the output property name.
* @param value the output property value.
* @since Ant 1.5
*/
public void setOutputProperty(String name, String value) {
final String[] pair = new String[]{name, value};
outputProperties.addElement(pair);
}
/** Set the class to resolve entities during the transformation
*/
public void setEntityResolver(EntityResolver aResolver) {
entityResolver = aResolver;
}
/** Set the class to resolve URIs during the transformation
*/
public void setURIResolver(URIResolver aResolver) {
uriResolver = aResolver;
}
public void addParam(String name, String value) {
final String[] pair = new String[]{name, value};
params.addElement(pair);
}
}
public void setLogger(XSLTLogger l) {
public void setLogger(XSLTLogger l) {
logger = l;
logger = l;
}
}
public void error(TransformerException e) {
public void error(TransformerException e) {
logError(e, "Error");
logError(e, "Error");
}
}
public void fatalError(TransformerException e) {
public void fatalError(TransformerException e) {
logError(e, "Fatal Error");
logError(e, "Fatal Error");
throw new BuildException("Fatal error during transformation", e);
throw new BuildException("Fatal error during transformation", e);
}
}
public void warning(TransformerException e) {
public void warning(TransformerException e) {
logError(e, "Warning");
logError(e, "Warning");
}
}
@@ -251,20 +376,24 @@ public class TraXLiaison implements XSLTLiaison, ErrorListener, XSLTLoggerAware
}
}
StringBuffer msg = new StringBuffer();
StringBuffer msg = new StringBuffer();
if (e.getLocator() != null) {
if (e.getLocator().getSystemId() != null) {
String url = e.getLocator().getSystemId();
SourceLocator locator = e.getLocator();
if (locator != null) {
String systemid = locator.getSystemId();
if (systemid != null) {
String url = systemid;
if (url.startsWith("file:///")) {
if (url.startsWith("file:///")) {
url = url.substring(8);
url = url.substring(8);
}
}
msg.append(url);
msg.append(url);
} else {
} else {
msg.append("Unknown file");
msg.append("Unknown file");
}
}
if (e.getLocator().getLineNumber() != -1) {
msg.append(":" + e.getLocator().getLineNumber());
if (e.getLocator().getColumnNumber() != -1) {
msg.append(":" + e.getLocator().getColumnNumber());
int line = locator.getLineNumber();
if (line != -1) {
msg.append(":" + line);
int column = locator.getColumnNumber();
if (column != -1) {
msg.append(":" + column);
}
}
}
}
}
}
@@ -277,16 +406,4 @@ public class TraXLiaison implements XSLTLiaison, ErrorListener, XSLTLoggerAware
logger.log(msg.toString());
logger.log(msg.toString());
}
}
/** Set the class to resolve entities during the transformation
*/
public void setEntityResolver(EntityResolver aResolver) {
entityResolver = aResolver;
}
/** Set the class to resolve URIs during the transformation
*/
public void setURIResolver(URIResolver aResolver) {
uriResolver = aResolver;
}
} //-- TraXLiaison
} //-- TraXLiaison