@@ -54,11 +54,9 @@
package org.apache.tools.ant.taskdefs;
import java.lang.reflect.Method;
import java.io.File;
import java.util.Enumeration;
import java.util.Vector;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
@@ -67,7 +65,8 @@ import org.apache.tools.ant.AntClassLoader;
import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.types.Reference;
import org.apache.tools.ant.util.FileUtils;
import org.apache.tools.ant.types.XCatalog;
import org.xml.sax.EntityResolver;
/**
* A Task to process via XSLT a set of XML documents. This is
@@ -98,31 +97,65 @@ import org.apache.tools.ant.util.FileUtils;
*/
public class XSLTProcess extends MatchingTask implements XSLTLogger {
/** destination directory */
private File destDir = null;
/** where to find the source XML file, default is the project's basedir */
private File baseDir = null;
/** XSL stylesheet */
private String xslFile = null;
/** extension of the files produced by XSL processing */
private String targetExtension = ".html";
private Vector params = new Vector();
/** additional parameters to be passed to the stylesheets */
private Vector params = new Vector();
/** Input XML document to be used */
private File inFile = null;
/** Output file */
private File outFile = null;
/** The name of the XSL processor to use */
private String processor;
/** Classpath to use when trying to load the XSL processor */
private Path classpath = null;
/** The Liason implementation to use to communicate with the XSL
* processor */
private XSLTLiaison liaison;
/** Flag which indicates if the stylesheet has been loaded into
* the processor */
private boolean stylesheetLoaded = false;
/** force output of target files even if they already exist */
private boolean force = false;
/** Utilities used for file operations */
private FileUtils fileUtils;
/** XSL output method to be used */
private String outputtype = null;
/** for resolving entities such as dtds */
private XCatalog xcatalog;
/** Name of the TRAX Liason class */
private static final String TRAX_LIAISON_CLASS =
"org.apache.tools.ant.taskdefs.optional.TraXLiaison";
/** Name of the now-deprecated XSLP Liason class */
private static final String XSLP_LIASON_CLASS =
"org.apache.tools.ant.taskdefs.optional.XslpLiaison";
/** Name of the Xalan liason class */
private static final String XALAN_LIASON_CLASS =
"org.apache.tools.ant.taskdefs.optional.XalanLiaison";
/**
* Whether to style all files in the included directories as well.
*
@@ -136,10 +169,11 @@ public class XSLTProcess extends MatchingTask implements XSLTLogger {
public XSLTProcess() {
fileUtils = FileUtils.newFileUtils();
} //-- XSLTProcess
/**
* Whether to style all files in the included directories as well.
*
* @param b true if files in included directories are processed.
* @since 1.35, Ant 1.5
*/
public void setScanIncludedDirectories(boolean b) {
@@ -148,29 +182,31 @@ public class XSLTProcess extends MatchingTask implements XSLTLogger {
/**
* Executes the task.
*
* @exception BuildException if there is an execution problem.
*/
public void execute() throws BuildException {
DirectoryScanner scanner;
String[] list;
String[] dirs;
if (xslFile == null) {
throw new BuildException("no stylesheet specified", location);
}
if (baseDir == null) {
baseDir = project.resolveFile(".");
}
liaison = getLiaison();
// check if liaison wants to log errors using us as logger
if(liaison instanceof XSLTLoggerAware) {
if (liaison instanceof XSLTLoggerAware) {
((XSLTLoggerAware)liaison).setLogger(this);
}
log("Using "+liaison.getClass().toString(), Project.MSG_VERBOSE);
log("Using " + liaison.getClass().toString(), Project.MSG_VERBOSE);
File stylesheet = project.resolveFile(xslFile);
if (!stylesheet.exists()) {
stylesheet = fileUtils.resolveFile(baseDir, xslFile);
@@ -179,62 +215,66 @@ public class XSLTProcess extends MatchingTask implements XSLTLogger {
* the wrong version has been used.
*/
if (stylesheet.exists()) {
log("DEPRECATED - the style attribute should be relative to the project\'s");
log("DEPRECATED - the style attribute should be relative "
+ "to the project\'s");
log(" basedir, not the tasks\'s basedir.");
}
}
// if we have an in file and out then process them
if (inFile != null && outFile != null) {
process(inFile, outFile, stylesheet);
return;
}
/*
* if we get here, in and out have not been specified, we are
* in batch processing mode.
*/
//-- make sure Source directory exists...
if (destDir == null ) {
String msg = "destdir attributes must be set!";
throw new BuildException(msg);
}
scanner = getDirectoryScanner(baseDir);
log("Transforming into "+destDir, Project.MSG_INFO);
log("Transforming into " + destDir, Project.MSG_INFO);
// Process all the files marked for styling
list = scanner.getIncludedFiles();
for (int i = 0;i < list.length; ++i) {
for (int i = 0; i < list.length; ++i) {
process( baseDir, list[i], destDir, stylesheet );
}
if (performDirectoryScan) {
// Process all the directories marked for styling
dirs = scanner.getIncludedDirectories();
for (int j = 0;j < dirs.length;++j){
list=new File(baseDir,dirs[j]).list();
for (int i = 0;i < list.length;++i) {
for (int j = 0; j < dirs.length; ++j){
list = new File(baseDir, dirs[j]).list();
for (int i = 0; i < list.length; ++i) {
process( baseDir, list[i], destDir, stylesheet );
}
}
}
} //-- execute
/**
* Set whether to check dependencies, or always generate.
*
* @param force true if always generate.
**/
public void setForce(boolean force) {
this.force = force;
} //-- setForce
/**
* Set the base directory.
*
* @param dir the base directory
**/
public void setBasedir(File dir) {
baseDir = dir;
} //-- setSourceDir
/**
* Set the destination directory into which the XSL result
* files should be copied to
@@ -243,7 +283,7 @@ public class XSLTProcess extends MatchingTask implements XSLTLogger {
public void setDestdir(File dir) {
destDir = dir;
} //-- setDestDir
/**
* Set the desired file extension to be used for the target
* @param name the extension to use
@@ -251,24 +291,30 @@ public class XSLTProcess extends MatchingTask implements XSLTLogger {
public void setExtension(String name) {
targetExtension = name;
} //-- setDestDir
/**
* Sets the file to use for styling relative to the base directory
* of this task.
*
* @param xslFile the stylesheet to use
*/
public void setStyle(String xslFile) {
this.xslFile = xslFile;
}
/**
* Set the classpath to load the Processor through (attribute).
*
* @param classpath the classpath to use when loading the XSL processor
*/
public void setClasspath(Path classpath) {
createClasspath().append(classpath);
}
/**
* Set the classpath to load the Processor through (nested element).
*
* @return a path instance to be configured by the Ant core.
*/
public Path createClasspath() {
if (classpath == null) {
@@ -276,47 +322,71 @@ public class XSLTProcess extends MatchingTask implements XSLTLogger {
}
return classpath.createPath();
}
/**
* Set the classpath to load the Processor through via reference
* (attribute).
*
* @param r the id of the Ant path instance to act as the classpath
* for loading the XSL processor
*/
public void setClasspathRef(Reference r) {
createClasspath().setRefid(r);
}
/**
* Set the name of the XSL processor to use
*
* @param processor the name of the XSL processor
*/
public void setProcessor(String processor) {
this.processor = processor;
}
/**
* store the xcatalog for resolving entities
*
* @param xcatalog the xcatalog instance to use to look up DTDs
*/
public void addXcatalog(XCatalog xcatalog) {
this.xcatalog = xcatalog;
}
/**
* Load processor here instead of in setProcessor - this will be
* called from within execute, so we have access to the latest
* classpath.
*
* @param proc the name of the processor to load.
* @exception Exception if the processor cannot be loaded.
*/
private void resolveProcessor(String proc) throws Exception {
if (proc.equals("trax")) {
final Class clazz =
loadClass("org.apache.tools.ant.taskdefs.optional.TraXLiaison");
loadClass(TRAX_LIAISON_CLASS );
liaison = (XSLTLiaison)clazz.newInstance();
} else if (proc.equals("xslp")) {
log("DEPRECATED - xslp processor is deprecated. Use trax or xalan instead.");
log("DEPRECATED - xslp processor is deprecated. Use trax or "
+ "xalan instead.");
final Class clazz =
loadClass("org.apache.tools.ant.taskdefs.optional.XslpLiaison" );
loadClass(XSLP_LIASON_CLASS );
liaison = (XSLTLiaison) clazz.newInstance();
} else if (proc.equals("xalan")) {
final Class clazz =
loadClass("org.apache.tools.ant.taskdefs.optional.XalanLiaison" );
loadClass(XALAN_LIASON_CLASS );
liaison = (XSLTLiaison)clazz.newInstance();
} else {
liaison = (XSLTLiaison) loadClass(proc).newInstance();
}
}
/**
* Load named class either via the system classloader or a given
* custom classloader.
*
* @param classname the name of the class to load.
* @return the requested class.
* @exception Exception if the class could not be loaded.
*/
private Class loadClass(String classname) throws Exception {
if (classpath == null) {
@@ -328,36 +398,46 @@ public class XSLTProcess extends MatchingTask implements XSLTLogger {
return c;
}
}
/**
* Sets an out file
*
* @param outFile the output File instance.
*/
public void setOut(File outFile){
this.outFile = outFile;
}
/**
* Sets an input xml file to be styled
*
* @param inFile the input file
*/
public void setIn(File inFile){
this.inFile = inFile;
}
/**
* Processes the given input XML file and stores the result
* in the given resultFile.
*
* @param baseDir the base directory for resolving files.
* @param xmlFile the input file
* @param destDir the destination directory
* @param stylesheet the stylesheet to use.
* @exception BuildException if the processing fails.
*/
private void process(File baseDir, String xmlFile, File destDir,
File stylesheet)
throws BuildException {
String fileExt=targetExtension;
File outFile=null;
File inFile=null;
String fileExt = targetExtension;
File outFile = null;
File inFile = null;
try {
long styleSheetLastModified = stylesheet.lastModified();
inFile = new File(baseDir,xmlFile);
inFile = new File(baseDir, xmlFile);
if (inFile.isDirectory()) {
log("Skipping " + inFile + " it is a directory.",
@@ -366,17 +446,18 @@ public class XSLTProcess extends MatchingTask implements XSLTLogger {
}
int dotPos = xmlFile.lastIndexOf('.');
if(dotPos>0){
outFile = new File(destDir,xmlFile.substring(0,xmlFile.lastIndexOf('.'))+fileExt);
}else{
outFile = new File(destDir,xmlFile+fileExt);
if (dotPos > 0) {
outFile = new File(destDir,
xmlFile.substring(0, xmlFile.lastIndexOf('.')) + fileExt);
} else {
outFile = new File(destDir, xmlFile + fileExt);
}
if (force ||
inFile.lastModified() > outFile.lastModified() ||
styleSheetLastModified > outFile.lastModified()) {
ensureDirectoryFor( outFile );
log("Processing "+inFile+" to "+outFile);
log("Processing " + inFile + " to " + outFile);
configureLiaison(stylesheet);
liaison.transform(inFile, outFile);
}
@@ -388,45 +469,70 @@ public class XSLTProcess extends MatchingTask implements XSLTLogger {
if (outFile != null) {
outFile.delete();
}
throw new BuildException(ex);
}
} //-- processXML
private void process(File inFile, File outFile, File stylesheet) throws BuildException {
try{
/**
* Process the input file to the output file with the given stylesheet.
*
* @param inFile the input file to process.
* @param outFile the detination file.
* @param stylesheet the stylesheet to use.
* @exception BuildException if the processing fails.
*/
private void process(File inFile, File outFile, File stylesheet)
throws BuildException {
try {
long styleSheetLastModified = stylesheet.lastModified();
log("In file "+inFile+" time: " + inFile.lastModified() , Project.MSG_DEBUG);
log("Out file "+outFile+" time: " + outFile.lastModified() , Project.MSG_DEBUG);
log("Style file "+xslFile+" time: " + styleSheetLastModified , Project.MSG_DEBUG);
log("In file " + inFile + " time: " + inFile.lastModified(),
Project.MSG_DEBUG);
log("Out file " + outFile + " time: " + outFile.lastModified(),
Project.MSG_DEBUG);
log("Style file " + xslFile + " time: " + styleSheetLastModified,
Project.MSG_DEBUG);
if (force ||
inFile.lastModified() > outFile.lastModified() ||
styleSheetLastModified > outFile.lastModified()) {
ensureDirectoryFor( outFile );
log("Processing " + inFile + " to " + outFile, Project.MSG_INFO);
log("Processing " + inFile + " to " + outFile,
Project.MSG_INFO);
configureLiaison(stylesheet);
liaison.transform(inFile, outFile);
}
}catch (Exception ex) {
} catch (Exception ex) {
log("Failed to process " + inFile, Project.MSG_INFO);
if(outFile!=null) {
outFile.delete();
if (outFile != null) {
outFile.delete();
}
throw new BuildException(ex);
}
}
private void ensureDirectoryFor( File targetFile ) throws BuildException {
/**
* Ensure the directory exists for a given file
*
* @param targetFile the file for which the directories are required.
* @exception BuildException if the directories cannot be created.
*/
private void ensureDirectoryFor(File targetFile)
throws BuildException {
File directory = new File( targetFile.getParent() );
if (!directory.exists()) {
if (!directory.mkdirs()) {
throw new BuildException("Unable to create directory: "
+ directory.getAbsolutePath() );
+ directory.getAbsolutePath() );
}
}
}
/**
* Get the Liason implementation to use in processing.
*
* @return an instance of the XSLTLiason interface.
*/
protected XSLTLiaison getLiaison() {
// if processor wasn't specified, see if TraX is available. If not,
// default it to xslp or xalan, depending on which is in the classpath
@@ -457,40 +563,74 @@ public class XSLTProcess extends MatchingTask implements XSLTLogger {
}
return liaison;
}
/**
* Create an instance of an XSL parameter for configuration by Ant.
*
* @return an instance of the Param class to be configured.
*/
public Param createParam() {
Param p = new Param();
params.addElement(p);
return p;
}
/**
* The Param inner class used to store XSL parameters
*/
public class Param {
private String name=null;
private String expression=null;
/** The parameter name */
private String name = null;
/** The parameter's XSL expression */
private String expression = null;
/**
* Set the parameter name.
*
* @param name the name of the parameter.
*/
public void setName(String name){
this.name = name;
}
/**
* The XSL expression for the parameter value
*
* @param expression the XSL expression representing the
* parameter's value.
*/
public void setExpression(String expression){
this.expression = expression;
}
/**
* Get the parameter name
*
* @return the parameter name
* @exception BuildException if the name is not set.
*/
public String getName() throws BuildException{
if(name==null) {
throw new BuildException("Name attribute is missing.");
if (name == null) {
throw new BuildException("Name attribute is missing.");
}
return name;
}
/**
* Get the parameter expression
*
* @return the parameter expression
* @exception BuildException if the expression is not set.
*/
public String getExpression() throws BuildException{
if(expression==null) {
throw new BuildException("Expression attribute is missing.");
if (expression == null) {
throw new BuildException("Expression attribute is missing.");
}
return expression;
}
}
/**
* Set the output type to use for the transformation. Only "xml" (the
* default) is guaranteed to work for all parsers. Xalan2 also
@@ -500,27 +640,41 @@ public class XSLTProcess extends MatchingTask implements XSLTLogger {
public void setOutputtype(String type) {
this.outputtype = type;
}
/**
* Loads the stylesheet and set xsl:param parameters.
*
* @param stylesheet the file form which to load the stylesheet.
* @exception BuildException if the stylesheet cannot be loaded.
*/
protected void configureLiaison(File stylesheet) throws BuildException {
if (stylesheetLoaded) {
return;
}
stylesheetLoaded = true;
try {
log( "Loading stylesheet " + stylesheet, Project.MSG_INFO);
liaison.setStylesheet( stylesheet );
for(Enumeration e = params.elements();e.hasMoreElements();) {
for (Enumeration e = params.elements(); e.hasMoreElements(); ) {
Param p = (Param)e.nextElement();
liaison.addParam( p.getName(), p.getExpression() );
}
// if liaison is a TraxLiason, use XCatalog as the entity
// resolver
if (liaison.getClass().getName().equals(TRAX_LIAISON_CLASS) &&
xcatalog != null) {
log("Configuring TraxLiaison and calling entity resolver",
Project.MSG_DEBUG);
Method resolver = liaison.getClass()
.getDeclaredMethod("setEntityResolver",
new Class[] {EntityResolver.class});
resolver.invoke(liaison, new Object[] {xcatalog});
}
} catch (Exception ex) {
log("Failed to read stylesheet " + stylesheet, Project.MSG_INFO);
throw new BuildException(ex);
}
}
} //-- XSLTProcess