blob: 2d29412e771af41de80c4967c3a250f97b2d6733 [file] [log] [blame]
/*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999 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 acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Ant", 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 names without prior written
* permission of the Apache Group.
*
* 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. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
package org.apache.tools.ant.taskdefs;
import java.io.*;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.StringTokenizer;
import java.util.Vector;
import org.apache.tools.ant.*;
import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.types.Reference;
import org.apache.tools.ant.util.FileUtils;
/**
* A Task to process via XSLT a set of XML documents. This is
* useful for building views of XML based documentation.
* arguments:
* <ul>
* <li>basedir
* <li>destdir
* <li>style
* <li>includes
* <li>excludes
* </ul>
* Of these arguments, the <b>sourcedir</b> and <b>destdir</b> are required.
* <p>
* This task will recursively scan the sourcedir and destdir
* looking for XML documents to process via XSLT. Any other files,
* such as images, or html files in the source directory will be
* copied into the destination directory.
*
* @author <a href="mailto:kvisco@exoffice.com">Keith Visco</a>
* @author <a href="mailto:rubys@us.ibm.com">Sam Ruby</a>
* @author <a href="mailto:russgold@acm.org">Russell Gold</a>
* @author <a href="stefan.bodewig@epost.de">Stefan Bodewig</a>
* @version $Revision$ $Date$
*/
public class XSLTProcess extends MatchingTask {
private File destDir = null;
private File baseDir = null;
private String xslFile = null;
private String targetExtension = ".html";
private Vector params = new Vector();
private File inFile = null;
private File outFile = null;
private String processor;
private Path classpath = null;
private XSLTLiaison liaison;
private boolean stylesheetLoaded = false;
private boolean force = false;
private FileUtils fileUtils;
/**
* Creates a new XSLTProcess Task.
**/
public XSLTProcess() {
fileUtils = FileUtils.newFileUtils();
} //-- XSLTProcess
/**
* Executes the task.
*/
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();
log("Using "+liaison.getClass().toString(), Project.MSG_VERBOSE);
File stylesheet = project.resolveFile(xslFile);
if (!stylesheet.exists()) {
stylesheet = fileUtils.resolveFile(baseDir, xslFile);
/*
* shouldn't throw out deprecation warnings before we know,
* the wrong version has been used.
*/
if (stylesheet.exists()) {
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);
// Process all the files marked for styling
list = scanner.getIncludedFiles();
for (int i = 0;i < list.length; ++i) {
process( baseDir, list[i], destDir, stylesheet );
}
// Process all the directoried 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)
process( baseDir, list[i], destDir, stylesheet );
}
} //-- execute
/**
* Set whether to check dependencies, or always generate.
**/
public void setForce(boolean force) {
this.force = force;
} //-- setForce
/**
* Set 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
* @param dirName the name of the destination directory
**/
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
**/
public void setExtension(String name) {
targetExtension = name;
} //-- setDestDir
/**
* Sets the file to use for styling relative to the base directory
* of this task.
*/
public void setStyle(String xslFile) {
this.xslFile = xslFile;
}
/**
* Set the classpath to load the Processor through (attribute).
*/
public void setClasspath(Path classpath) {
createClasspath().append(classpath);
}
/**
* Set the classpath to load the Processor through (nested element).
*/
public Path createClasspath() {
if (classpath == null) {
classpath = new Path(project);
}
return classpath.createPath();
}
/**
* Set the classpath to load the Processor through via reference
* (attribute).
*/
public void setClasspathRef(Reference r) {
createClasspath().setRefid(r);
}
public void setProcessor(String processor) {
this.processor = processor;
}
/**
* Load processor here instead of in setProcessor - this will be
* called from within execute, so we have access to the latest
* classpath.
*/
private void resolveProcessor(String proc) throws Exception {
if (proc.equals("trax")) {
final Class clazz =
loadClass("org.apache.tools.ant.taskdefs.optional.TraXLiaison");
liaison = (XSLTLiaison)clazz.newInstance();
} else if (proc.equals("xslp")) {
log("DEPRECATED - xslp processor is deprecated. Use trax or xalan instead.");
final Class clazz =
loadClass("org.apache.tools.ant.taskdefs.optional.XslpLiaison");
liaison = (XSLTLiaison) clazz.newInstance();
} else if (proc.equals("xalan")) {
final Class clazz =
loadClass("org.apache.tools.ant.taskdefs.optional.XalanLiaison");
liaison = (XSLTLiaison)clazz.newInstance();
} else {
liaison = (XSLTLiaison) loadClass(proc).newInstance();
}
}
/**
* Load named class either via the system classloader or a given
* custom classloader.
*/
private Class loadClass(String classname) throws Exception {
if (classpath == null) {
return Class.forName(classname);
} else {
AntClassLoader al = new AntClassLoader(project, classpath);
Class c = al.loadClass(classname);
AntClassLoader.initializeClass(c);
return c;
}
}
/**
* Sets an out file
*/
public void setOut(File outFile){
this.outFile = outFile;
}
/**
* Sets an input xml file to be styled
*/
public void setIn(File inFile){
this.inFile = inFile;
}
/**
* Processes the given input XML file and stores the result
* in the given resultFile.
**/
private void process(File baseDir, String xmlFile, File destDir,
File stylesheet)
throws BuildException {
String fileExt=targetExtension;
File outFile=null;
File inFile=null;
try {
long styleSheetLastModified = stylesheet.lastModified();
inFile = new File(baseDir,xmlFile);
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 (force ||
inFile.lastModified() > outFile.lastModified() ||
styleSheetLastModified > outFile.lastModified()) {
ensureDirectoryFor( outFile );
log("Transforming into "+destDir);
configureLiaison(stylesheet);
liaison.transform(inFile, outFile);
}
}
catch (Exception ex) {
// If failed to process document, must delete target document,
// or it will not attempt to process it the second time
log("Failed to process " + inFile, Project.MSG_INFO);
if (outFile != null) {
outFile.delete();
}
throw new BuildException(ex);
}
} //-- processXML
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);
if (force ||
inFile.lastModified() > outFile.lastModified() ||
styleSheetLastModified > outFile.lastModified()) {
ensureDirectoryFor( outFile );
log("Processing " + inFile + " to " + outFile, Project.MSG_INFO);
configureLiaison(stylesheet);
liaison.transform(inFile, outFile);
}
}catch (Exception ex) {
log("Failed to process " + inFile, Project.MSG_INFO);
if(outFile!=null)outFile.delete();
throw new BuildException(ex);
}
}
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() );
}
}
}
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
if (liaison == null) {
if (processor != null) {
try {
resolveProcessor(processor);
} catch (Exception e) {
throw new BuildException(e);
}
} else {
try {
resolveProcessor("trax");
} catch (Throwable e1) {
try {
resolveProcessor("xslp");
} catch (Throwable e2) {
try {
resolveProcessor("xalan");
} catch (Throwable e3) {
e3.printStackTrace();
e2.printStackTrace();
throw new BuildException(e1);
}
}
}
}
}
return liaison;
}
public Param createParam() {
Param p = new Param();
params.addElement(p);
return p;
}
public class Param {
private String name=null;
private String expression=null;
public void setName(String name){
this.name = name;
}
public void setExpression(String expression){
this.expression = expression;
}
public String getName() throws BuildException{
if(name==null)throw new BuildException("Name attribute is missing.");
return name;
}
public String getExpression() throws BuildException{
if(expression==null)throw new BuildException("Expression attribute is missing.");
return expression;
}
}
/**
* Loads the stylesheet and set xsl:param parameters.
*/
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();) {
Param p = (Param)e.nextElement();
liaison.addParam( p.getName(), p.getExpression() );
}
} catch (Exception ex) {
log("Failed to read stylesheet " + stylesheet, Project.MSG_INFO);
throw new BuildException(ex);
}
}
} //-- XSLTProcess