You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@ant.apache.org by do...@apache.org on 2001/12/16 13:04:44 UTC
cvs commit: jakarta-ant/proposal/myrmidon/src/java/org/apache/antlib/xml TraXLiaison.java XMLValidateTask.java XSLTLiaison.java XSLTParam.java XSLTProcess.java XalanLiaison.java
donaldp 01/12/16 04:04:44
Added: proposal/myrmidon/src/java/org/apache/antlib/xml
TraXLiaison.java XMLValidateTask.java
XSLTLiaison.java XSLTParam.java XSLTProcess.java
XalanLiaison.java
Log:
Start to move the non-deprecated XML files into new antlib - making sure they are compatible with myrmidon
Revision Changes Path
1.1 jakarta-ant/proposal/myrmidon/src/java/org/apache/antlib/xml/TraXLiaison.java
Index: TraXLiaison.java
===================================================================
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package org.apache.antlib.xml;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import javax.xml.transform.ErrorListener;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Templates;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import org.apache.avalon.framework.logger.AbstractLogEnabled;
import org.apache.avalon.framework.logger.LogEnabled;
/**
* Concrete liaison for XSLT processor implementing TraX. (ie JAXP 1.1)
*
* @author <a href="mailto:rubys@us.ibm.com">Sam Ruby</a>
* @author <a href="mailto:dims@yahoo.com">Davanum Srinivas</a>
* @author <a href="mailto:sbailliez@apache.org">Stephane Bailliez</a>
*/
public class TraXLiaison
extends AbstractLogEnabled
implements XSLTLiaison, ErrorListener
{
/**
* The trax TransformerFactory
*/
private TransformerFactory tfactory;
/**
* stylesheet stream, close it asap
*/
private FileInputStream xslStream;
/**
* Stylesheet template
*/
private Templates templates;
/**
* transformer
*/
private Transformer transformer;
public TraXLiaison()
throws Exception
{
tfactory = TransformerFactory.newInstance();
tfactory.setErrorListener( this );
}
public void setOutputtype( String type )
throws Exception
{
transformer.setOutputProperty( OutputKeys.METHOD, type );
}
//------------------- IMPORTANT
// 1) Don't use the StreamSource(File) ctor. It won't work with
// xalan prior to 2.2 because of systemid bugs.
// 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.
// 3) Always set the systemid to the source for imports, includes...
// in xsl and xml...
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 );
}
public void addParam( String name, String value )
{
transformer.setParameter( name, value );
}
public void error( TransformerException e )
{
logError( e, "Error" );
}
public void fatalError( TransformerException e )
{
logError( e, "Fatal Error" );
}
public void transform( File infile, File outfile )
throws Exception
{
FileInputStream fis = null;
FileOutputStream fos = null;
try
{
fis = new FileInputStream( infile );
fos = new FileOutputStream( outfile );
StreamSource src = new StreamSource( fis );
src.setSystemId( getSystemId( infile ) );
StreamResult res = new StreamResult( fos );
// not sure what could be the need of this...
res.setSystemId( getSystemId( outfile ) );
transformer.transform( src, res );
}
finally
{
// make sure to close all handles, otherwise the garbage
// collector will close them...whenever possible and
// Windows may complain about not being able to delete files.
try
{
if( xslStream != null )
{
xslStream.close();
}
}
catch( IOException ignored )
{
}
try
{
if( fis != null )
{
fis.close();
}
}
catch( IOException ignored )
{
}
try
{
if( fos != null )
{
fos.close();
}
}
catch( IOException ignored )
{
}
}
}
public void warning( TransformerException e )
{
logError( e, "Warning" );
}
// make sure that the systemid is made of '/' and not '\' otherwise
// crimson will complain that it cannot resolve relative entities
// because it grabs the base uri via lastIndexOf('/') without
// making sure it is really a /'ed path
protected String getSystemId( File file )
{
String path = file.getAbsolutePath();
path = path.replace( '\\', '/' );
return FILE_PROTOCOL_PREFIX + path;
}
private void logError( TransformerException e, String type )
{
StringBuffer msg = new StringBuffer();
if( e.getLocator() != null )
{
if( e.getLocator().getSystemId() != null )
{
String url = e.getLocator().getSystemId();
if( url.startsWith( "file:///" ) )
url = url.substring( 8 );
msg.append( url );
}
else
{
msg.append( "Unknown file" );
}
if( e.getLocator().getLineNumber() != -1 )
{
msg.append( ":" + e.getLocator().getLineNumber() );
if( e.getLocator().getColumnNumber() != -1 )
{
msg.append( ":" + e.getLocator().getColumnNumber() );
}
}
}
msg.append( ": " + type + "! " );
msg.append( e.getMessage() );
if( e.getCause() != null )
{
msg.append( " Cause: " + e.getCause() );
}
getLogger().info( msg.toString() );
}
}//-- TraXLiaison
1.1 jakarta-ant/proposal/myrmidon/src/java/org/apache/antlib/xml/XMLValidateTask.java
Index: XMLValidateTask.java
===================================================================
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package org.apache.antlib.xml;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import org.apache.myrmidon.api.TaskException;
import org.apache.tools.ant.AntClassLoader;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.FileSet;
import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.types.Reference;
import org.xml.sax.EntityResolver;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.Parser;
import org.xml.sax.SAXException;
import org.xml.sax.SAXNotRecognizedException;
import org.xml.sax.SAXNotSupportedException;
import org.xml.sax.SAXParseException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.ParserAdapter;
/**
* The <code>XMLValidateTask</code> checks that an XML document is valid, with a
* SAX validating parser.
*
* @author Raphael Pierquin <a href="mailto:raphael.pierquin@agisphere.com">
* raphael.pierquin@agisphere.com</a>
*/
public class XMLValidateTask
extends Task
{
/**
* The default implementation parser classname used by the task to process
* validation.
*/
// The crimson implementation is shipped with ant.
public static String DEFAULT_XML_READER_CLASSNAME = "org.apache.crimson.parser.XMLReaderImpl";
protected static String INIT_FAILED_MSG = "Could not start xml validation: ";
// ant task properties
// defaults
protected boolean failOnError = true;
protected boolean warn = true;
protected boolean lenient = false;
protected String readerClassName = DEFAULT_XML_READER_CLASSNAME;
protected File file = null;// file to be validated
protected Vector filesets = new Vector();
/**
* the parser is viewed as a SAX2 XMLReader. If a SAX1 parser is specified,
* it's wrapped in an adapter that make it behave as a XMLReader. a more
* 'standard' way of doing this would be to use the JAXP1.1 SAXParser
* interface.
*/
protected XMLReader xmlReader = null;// XMLReader used to validation process
protected ValidatorErrorHandler errorHandler
= new ValidatorErrorHandler();// to report sax parsing errors
protected Hashtable features = new Hashtable();
/**
* The list of configured DTD locations
*/
public Vector dtdLocations = new Vector();// sets of file to be validated
protected Path classpath;
/**
* Specify the class name of the SAX parser to be used. (optional)
*
* @param className should be an implementation of SAX2 <code>org.xml.sax.XMLReader</code>
* or SAX2 <code>org.xml.sax.Parser</code>. <p>
*
* if className is an implementation of <code>org.xml.sax.Parser</code>
* , {@link #setLenient(boolean)}, will be ignored. <p>
*
* if not set, the default {@link #DEFAULT_XML_READER_CLASSNAME} will
* be used.
* @see org.xml.sax.XMLReader
* @see org.xml.sax.Parser
*/
public void setClassName( String className )
{
readerClassName = className;
}
/**
* Specify the classpath to be searched to load the parser (optional)
*
* @param classpath The new Classpath value
*/
public void setClasspath( Path classpath )
throws TaskException
{
if( this.classpath == null )
{
this.classpath = classpath;
}
else
{
this.classpath.append( classpath );
}
}
/**
* @param r The new ClasspathRef value
* @see #setClasspath
*/
public void setClasspathRef( Reference r )
throws TaskException
{
createClasspath().setRefid( r );
}
/**
* Specify how parser error are to be handled. <p>
*
* If set to <code>true</code> (default), throw a TaskException if the
* parser yields an error.
*
* @param fail The new FailOnError value
*/
public void setFailOnError( boolean fail )
{
failOnError = fail;
}
/**
* specifify the file to be checked
*
* @param file The new File value
*/
public void setFile( File file )
{
this.file = file;
}
/**
* Specify whether the parser should be validating. Default is <code>true</code>
* . <p>
*
* If set to false, the validation will fail only if the parsed document is
* not well formed XML. <p>
*
* this option is ignored if the specified class with {@link
* #setClassName(String)} is not a SAX2 XMLReader.
*
* @param bool The new Lenient value
*/
public void setLenient( boolean bool )
{
lenient = bool;
}
/**
* Specify how parser error are to be handled. <p>
*
* If set to <code>true
*</true>
*(default), log a warn message for each SAX warn event.
*
* @param bool The new Warn value
*/
public void setWarn( boolean bool )
{
warn = bool;
}
/**
* specifify a set of file to be checked
*
* @param set The feature to be added to the Fileset attribute
*/
public void addFileset( FileSet set )
{
filesets.addElement( set );
}
/**
* @return Description of the Returned Value
* @see #setClasspath
*/
public Path createClasspath()
throws TaskException
{
if( this.classpath == null )
{
this.classpath = new Path( project );
}
return this.classpath.createPath();
}
/**
* Create a DTD location record. This stores the location of a DTD. The DTD
* is identified by its public Id. The location may either be a file
* location or a resource location.
*
* @return Description of the Returned Value
*/
public DTDLocation createDTD()
{
DTDLocation dtdLocation = new DTDLocation();
dtdLocations.addElement( dtdLocation );
return dtdLocation;
}
public void execute()
throws TaskException
{
int fileProcessed = 0;
if( file == null && ( filesets.size() == 0 ) )
{
throw new TaskException( "Specify at least one source - a file or a fileset." );
}
initValidator();
if( file != null )
{
if( file.exists() && file.canRead() && file.isFile() )
{
doValidate( file );
fileProcessed++;
}
else
{
String errorMsg = "File " + file + " cannot be read";
if( failOnError )
throw new TaskException( errorMsg );
else
log( errorMsg, Project.MSG_ERR );
}
}
for( int i = 0; i < filesets.size(); i++ )
{
FileSet fs = (FileSet)filesets.elementAt( i );
DirectoryScanner ds = fs.getDirectoryScanner( project );
String[] files = ds.getIncludedFiles();
for( int j = 0; j < files.length; j++ )
{
File srcFile = new File( fs.getDir( project ), files[ j ] );
doValidate( srcFile );
fileProcessed++;
}
}
getLogger().info( fileProcessed + " file(s) have been successfully validated." );
}
protected EntityResolver getEntityResolver()
{
LocalResolver resolver = new LocalResolver();
for( Enumeration i = dtdLocations.elements(); i.hasMoreElements(); )
{
DTDLocation location = (DTDLocation)i.nextElement();
resolver.registerDTD( location );
}
return resolver;
}
/*
* set a feature on the parser.
* TODO: find a way to set any feature from build.xml
*/
private boolean setFeature( String feature, boolean value, boolean warn )
{
boolean toReturn = false;
try
{
xmlReader.setFeature( feature, value );
toReturn = true;
}
catch( SAXNotRecognizedException e )
{
if( warn )
log( "Could not set feature '"
+ feature
+ "' because the parser doesn't recognize it",
Project.MSG_WARN );
}
catch( SAXNotSupportedException e )
{
if( warn )
log( "Could not set feature '"
+ feature
+ "' because the parser doesn't support it",
Project.MSG_WARN );
}
return toReturn;
}
/*
* parse the file
*/
private void doValidate( File afile )
throws TaskException
{
try
{
log( "Validating " + afile.getName() + "... ", Project.MSG_VERBOSE );
errorHandler.init( afile );
InputSource is = new InputSource( new FileReader( afile ) );
String uri = "file:" + afile.getAbsolutePath().replace( '\\', '/' );
for( int index = uri.indexOf( '#' ); index != -1;
index = uri.indexOf( '#' ) )
{
uri = uri.substring( 0, index ) + "%23" + uri.substring( index + 1 );
}
is.setSystemId( uri );
xmlReader.parse( is );
}
catch( SAXException ex )
{
if( failOnError )
throw new TaskException( "Could not validate document " + afile );
}
catch( IOException ex )
{
throw new TaskException( "Could not validate document " + afile, ex );
}
if( errorHandler.getFailure() )
{
if( failOnError )
throw new TaskException( afile + " is not a valid XML document." );
else
log( afile + " is not a valid XML document", Project.MSG_ERR );
}
}
/**
* init the parser : load the parser class, and set features if necessary
*/
private void initValidator()
throws TaskException
{
try
{
// load the parser class
// with JAXP, we would use a SAXParser factory
Class readerClass = null;
//Class readerImpl = null;
//Class parserImpl = null;
if( classpath != null )
{
AntClassLoader loader = new AntClassLoader( project, classpath );
// loader.addSystemPackageRoot("org.xml"); // needed to avoid conflict
readerClass = loader.loadClass( readerClassName );
AntClassLoader.initializeClass( readerClass );
}
else
readerClass = Class.forName( readerClassName );
// then check it implements XMLReader
if( XMLReader.class.isAssignableFrom( readerClass ) )
{
xmlReader = (XMLReader)readerClass.newInstance();
log( "Using SAX2 reader " + readerClassName, Project.MSG_VERBOSE );
}
else
{
// see if it is a SAX1 Parser
if( Parser.class.isAssignableFrom( readerClass ) )
{
Parser parser = (Parser)readerClass.newInstance();
xmlReader = new ParserAdapter( parser );
log( "Using SAX1 parser " + readerClassName, Project.MSG_VERBOSE );
}
else
{
throw new TaskException( INIT_FAILED_MSG
+ readerClassName
+ " implements nor SAX1 Parser nor SAX2 XMLReader." );
}
}
}
catch( ClassNotFoundException e )
{
throw new TaskException( INIT_FAILED_MSG + readerClassName, e );
}
catch( InstantiationException e )
{
throw new TaskException( INIT_FAILED_MSG + readerClassName, e );
}
catch( IllegalAccessException e )
{
throw new TaskException( INIT_FAILED_MSG + readerClassName, e );
}
xmlReader.setEntityResolver( getEntityResolver() );
xmlReader.setErrorHandler( errorHandler );
if( !( xmlReader instanceof ParserAdapter ) )
{
// turn validation on
if( !lenient )
{
boolean ok = setFeature( "http://xml.org/sax/features/validation", true, true );
if( !ok )
{
throw new TaskException( INIT_FAILED_MSG
+ readerClassName
+ " doesn't provide validation" );
}
}
// set other features
Enumeration enum = features.keys();
while( enum.hasMoreElements() )
{
String featureId = (String)enum.nextElement();
setFeature( featureId, ( (Boolean)features.get( featureId ) ).booleanValue(), true );
}
}
}
public static class DTDLocation
{
private String publicId = null;
private String location = null;
public void setLocation( String location )
{
this.location = location;
}
public void setPublicId( String publicId )
{
this.publicId = publicId;
}
public String getLocation()
{
return location;
}
public String getPublicId()
{
return publicId;
}
}
/*
* ValidatorErrorHandler role :
* <ul>
* <li> log SAX parse exceptions,
* <li> remember if an error occured
* </ul>
*/
protected class ValidatorErrorHandler implements ErrorHandler
{
protected File currentFile = null;
protected String lastErrorMessage = null;
protected boolean failed = false;
// did an error happen during last parsing ?
public boolean getFailure()
{
return failed;
}
public void error( SAXParseException exception )
{
failed = true;
doLog( exception, Project.MSG_ERR );
}
public void fatalError( SAXParseException exception )
{
failed = true;
doLog( exception, Project.MSG_ERR );
}
public void init( File file )
{
currentFile = file;
failed = false;
}
public void warning( SAXParseException exception )
{
// depending on implementation, XMLReader can yield hips of warning,
// only output then if user explicitely asked for it
if( warn )
doLog( exception, Project.MSG_WARN );
}
private String getMessage( SAXParseException e )
{
String sysID = e.getSystemId();
if( sysID != null )
{
try
{
int line = e.getLineNumber();
int col = e.getColumnNumber();
return new URL( sysID ).getFile() +
( line == -1 ? "" : ( ":" + line +
( col == -1 ? "" : ( ":" + col ) ) ) ) +
": " + e.getMessage();
}
catch( MalformedURLException mfue )
{
}
}
return e.getMessage();
}
private void doLog( SAXParseException e, int logLevel )
{
log( getMessage( e ), logLevel );
}
}
private class LocalResolver
implements EntityResolver
{
private Hashtable fileDTDs = new Hashtable();
private Hashtable resourceDTDs = new Hashtable();
private Hashtable urlDTDs = new Hashtable();
public LocalResolver()
{
}
public void registerDTD( String publicId, String location )
{
if( location == null )
{
return;
}
File fileDTD = new File( location );
if( fileDTD.exists() )
{
if( publicId != null )
{
fileDTDs.put( publicId, fileDTD );
log( "Mapped publicId " + publicId + " to file " + fileDTD, Project.MSG_VERBOSE );
}
return;
}
if( LocalResolver.this.getClass().getResource( location ) != null )
{
if( publicId != null )
{
resourceDTDs.put( publicId, location );
log( "Mapped publicId " + publicId + " to resource " + location, Project.MSG_VERBOSE );
}
}
try
{
if( publicId != null )
{
URL urldtd = new URL( location );
urlDTDs.put( publicId, urldtd );
}
}
catch( MalformedURLException e )
{
//ignored
}
}
public void registerDTD( DTDLocation location )
{
registerDTD( location.getPublicId(), location.getLocation() );
}
public InputSource resolveEntity( String publicId, String systemId )
throws SAXException
{
File dtdFile = (File)fileDTDs.get( publicId );
if( dtdFile != null )
{
try
{
log( "Resolved " + publicId + " to local file " + dtdFile, Project.MSG_VERBOSE );
return new InputSource( new FileInputStream( dtdFile ) );
}
catch( FileNotFoundException ex )
{
// ignore
}
}
String dtdResourceName = (String)resourceDTDs.get( publicId );
if( dtdResourceName != null )
{
InputStream is = this.getClass().getResourceAsStream( dtdResourceName );
if( is != null )
{
log( "Resolved " + publicId + " to local resource " + dtdResourceName, Project.MSG_VERBOSE );
return new InputSource( is );
}
}
URL dtdUrl = (URL)urlDTDs.get( publicId );
if( dtdUrl != null )
{
try
{
InputStream is = dtdUrl.openStream();
log( "Resolved " + publicId + " to url " + dtdUrl, Project.MSG_VERBOSE );
return new InputSource( is );
}
catch( IOException ioe )
{
//ignore
}
}
log( "Could not resolve ( publicId: " + publicId + ", systemId: " + systemId + ") to a local entity",
Project.MSG_INFO );
return null;
}
}
}
1.1 jakarta-ant/proposal/myrmidon/src/java/org/apache/antlib/xml/XSLTLiaison.java
Index: XSLTLiaison.java
===================================================================
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package org.apache.antlib.xml;
import java.io.File;
/**
* Proxy interface for XSLT processors.
*
* @author <a href="mailto:rubys@us.ibm.com">Sam Ruby</a>
* @author <a href="mailto:sbailliez@apache.org">Stephane Bailliez</a>
* @see XSLTProcess
*/
public interface XSLTLiaison
{
/**
* the file protocol prefix for systemid. This file protocol must be
* appended to an absolute path. Typically: <tt>FILE_PROTOCOL_PREFIX +
* file.getAbsolutePath()</tt> This is not correct in specification terms
* since an absolute url in Unix is file:// + file.getAbsolutePath() while
* it is file:/// + file.getAbsolutePath() under Windows. Whatever, it
* should not be a problem to put file:/// in every case since most parsers
* for now incorrectly makes no difference between it.. and users also have
* problem with that :)
*/
String FILE_PROTOCOL_PREFIX = "file:///";
/**
* set the stylesheet to use for the transformation.
*
* @param stylesheet the stylesheet to be used for transformation.
* @exception Exception Description of Exception
*/
void setStylesheet( File stylesheet )
throws Exception;
/**
* Add a parameter to be set during the XSL transformation.
*
* @param name the parameter name.
* @param expression the parameter value as an expression string.
* @throws Exception thrown if any problems happens.
*/
void addParam( String name, String expression )
throws Exception;
/**
* set the output type to use for the transformation. Only "xml" (the
* default) is guaranteed to work for all parsers. Xalan2 also supports
* "html" and "text".
*
* @param type the output method to use
* @exception Exception Description of Exception
*/
void setOutputtype( String type )
throws Exception;
/**
* Perform the transformation of a file into another.
*
* @param infile the input file, probably an XML one. :-)
* @param outfile the output file resulting from the transformation
* @see #setStylesheet(File)
* @throws Exception thrown if any problems happens.
*/
void transform( File infile, File outfile )
throws Exception;
}//-- XSLTLiaison
1.1 jakarta-ant/proposal/myrmidon/src/java/org/apache/antlib/xml/XSLTParam.java
Index: XSLTParam.java
===================================================================
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.antlib.xml;
import org.apache.myrmidon.api.TaskException;
public class XSLTParam
{
private String m_name;
private String m_expression;
public void setExpression( String expression )
{
m_expression = expression;
}
public void setName( String name )
{
m_name = name;
}
public String getExpression()
throws TaskException
{
if( m_expression == null )
{
throw new TaskException( "Expression attribute is missing." );
}
return m_expression;
}
public String getName()
throws TaskException
{
if( m_name == null )
{
throw new TaskException( "Name attribute is missing." );
}
return m_name;
}
}
1.1 jakarta-ant/proposal/myrmidon/src/java/org/apache/antlib/xml/XSLTProcess.java
Index: XSLTProcess.java
===================================================================
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package org.apache.antlib.xml;
import java.io.File;
import java.util.Enumeration;
import java.util.Vector;
import java.util.ArrayList;
import java.util.Iterator;
import org.apache.myrmidon.api.TaskException;
import org.apache.tools.ant.AntClassLoader;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.taskdefs.MatchingTask;
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>
*/
public class XSLTProcess
extends MatchingTask
{
private File m_destDir;
private File m_baseDir;
private String m_xslFile;
private String m_targetExtension = ".html";
private ArrayList m_params = new ArrayList();
private File m_inFile;
private File m_outFile;
private Path m_classpath;
private boolean m_stylesheetLoaded;
private boolean m_force;
private String m_outputtype;
private FileUtils m_fileUtils;
private XSLTLiaison m_liaison;
private String m_processor;
/**
* Creates a new XSLTProcess Task.
*/
public XSLTProcess()
{
m_fileUtils = FileUtils.newFileUtils();
}//-- setForce
/**
* Set the base directory.
*
* @param dir The new Basedir value
*/
public void setBasedir( File dir )
{
m_baseDir = dir;
}
/**
* Set the classpath to load the Processor through (attribute).
*
* @param classpath The new Classpath value
*/
public void setClasspath( Path classpath )
throws TaskException
{
createClasspath().append( classpath );
}
/**
* Set the classpath to load the Processor through via reference
* (attribute).
*
* @param r The new ClasspathRef value
*/
public void setClasspathRef( Reference r )
throws TaskException
{
createClasspath().setRefid( r );
}
/**
* Set the destination directory into which the XSL result files should be
* copied to
*
* @param dir The new Destdir value
*/
public void setDestdir( File dir )
{
m_destDir = dir;
}
/**
* Set the desired file extension to be used for the target
*
* @param name the extension to use
*/
public void setExtension( String name )
{
m_targetExtension = name;
}
/**
* Set whether to check dependencies, or always generate.
*
* @param force The new Force value
*/
public void setForce( boolean force )
{
this.m_force = force;
}
/**
* Sets an input xml file to be styled
*
* @param inFile The new In value
*/
public void setIn( File inFile )
{
this.m_inFile = inFile;
}
/**
* Sets an out file
*
* @param outFile The new Out value
*/
public void setOut( File outFile )
{
this.m_outFile = outFile;
}
/**
* Set the output type to use for the transformation. Only "xml" (the
* default) is guaranteed to work for all parsers. Xalan2 also supports
* "html" and "text".
*
* @param type the output method to use
*/
public void setOutputtype( String type )
{
this.m_outputtype = type;
}
public void setProcessor( String processor )
{
this.m_processor = processor;
}//-- setDestDir
/**
* Sets the file to use for styling relative to the base directory of this
* task.
*
* @param xslFile The new Style value
*/
public void setStyle( String xslFile )
{
this.m_xslFile = xslFile;
}
/**
* Set the classpath to load the Processor through (nested element).
*
* @return Description of the Returned Value
*/
public Path createClasspath()
throws TaskException
{
if( m_classpath == null )
{
m_classpath = new Path( project );
}
return m_classpath.createPath();
}
public XSLTParam createParam()
{
XSLTParam p = new XSLTParam();
m_params.add( p );
return p;
}//-- XSLTProcess
/**
* Executes the task.
*
* @exception TaskException Description of Exception
*/
public void execute()
throws TaskException
{
DirectoryScanner scanner;
String[] list;
String[] dirs;
if( m_xslFile == null )
{
throw new TaskException( "no stylesheet specified" );
}
if( m_baseDir == null )
{
m_baseDir = getBaseDirectory();
}
m_liaison = getLiaison();
// check if liaison wants to log errors using us as logger
setupLogger( m_liaison );
log( "Using " + m_liaison.getClass().toString(), Project.MSG_VERBOSE );
File stylesheet = resolveFile( m_xslFile );
// if we have an in file and out then process them
if( m_inFile != null && m_outFile != null )
{
process( m_inFile, m_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( m_destDir == null )
{
String msg = "destdir attributes must be set!";
throw new TaskException( msg );
}
scanner = getDirectoryScanner( m_baseDir );
log( "Transforming into " + m_destDir, Project.MSG_INFO );
// Process all the files marked for styling
list = scanner.getIncludedFiles();
for( int i = 0; i < list.length; ++i )
{
process( m_baseDir, list[ i ], m_destDir, stylesheet );
}
// Process all the directoried marked for styling
dirs = scanner.getIncludedDirectories();
for( int j = 0; j < dirs.length; ++j )
{
list = new File( m_baseDir, dirs[ j ] ).list();
for( int i = 0; i < list.length; ++i )
process( m_baseDir, list[ i ], m_destDir, stylesheet );
}
}
protected XSLTLiaison getLiaison()
throws TaskException
{
// 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( m_liaison == null )
{
if( m_processor != null )
{
try
{
resolveProcessor( m_processor );
}
catch( Exception e )
{
throw new TaskException( "Error", e );
}
}
else
{
try
{
resolveProcessor( "trax" );
}
catch( Throwable e1 )
{
try
{
resolveProcessor( "xalan" );
}
catch( Throwable e2 )
{
try
{
resolveProcessor( "adaptx" );
}
catch( Throwable e3 )
{
try
{
resolveProcessor( "xslp" );
}
catch( Throwable e4 )
{
e4.printStackTrace();
e3.printStackTrace();
e2.printStackTrace();
throw new TaskException( "Error", e1 );
}
}
}
}
}
}
return m_liaison;
}
/**
* Loads the stylesheet and set xsl:param parameters.
*
* @param stylesheet Description of Parameter
* @exception TaskException Description of Exception
*/
protected void configureLiaison( File stylesheet )
throws TaskException
{
if( m_stylesheetLoaded )
{
return;
}
m_stylesheetLoaded = true;
try
{
getLogger().info( "Loading stylesheet " + stylesheet );
m_liaison.setStylesheet( stylesheet );
final Iterator params = m_params.iterator();
while( params.hasNext() )
{
final XSLTParam param = (XSLTParam)params.next();
m_liaison.addParam( param.getName(), param.getExpression() );
}
}
catch( final Exception e )
{
getLogger().info( "Failed to read stylesheet " + stylesheet );
throw new TaskException( "Error", e );
}
}
private void ensureDirectoryFor( File targetFile )
throws TaskException
{
File directory = new File( targetFile.getParent() );
if( !directory.exists() )
{
if( !directory.mkdirs() )
{
throw new TaskException( "Unable to create directory: "
+ directory.getAbsolutePath() );
}
}
}
/**
* Load named class either via the system classloader or a given custom
* classloader.
*
* @param classname Description of Parameter
* @return Description of the Returned Value
* @exception Exception Description of Exception
*/
private Class loadClass( String classname )
throws Exception
{
if( m_classpath == null )
{
return Class.forName( classname );
}
else
{
AntClassLoader al = new AntClassLoader( project, m_classpath );
Class c = al.loadClass( classname );
AntClassLoader.initializeClass( c );
return c;
}
}
/**
* Processes the given input XML file and stores the result in the given
* resultFile.
*
* @param baseDir Description of Parameter
* @param xmlFile Description of Parameter
* @param destDir Description of Parameter
* @param stylesheet Description of Parameter
* @exception TaskException Description of Exception
*/
private void process( File baseDir, String xmlFile, File destDir,
File stylesheet )
throws TaskException
{
String fileExt = m_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( m_force ||
inFile.lastModified() > outFile.lastModified() ||
styleSheetLastModified > outFile.lastModified() )
{
ensureDirectoryFor( outFile );
getLogger().info( "Processing " + inFile + " to " + outFile );
configureLiaison( stylesheet );
m_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
getLogger().info( "Failed to process " + inFile );
if( outFile != null )
{
outFile.delete();
}
throw new TaskException( "Error", ex );
}
}//-- processXML
private void process( File inFile, File outFile, File stylesheet )
throws TaskException
{
try
{
final long styleSheetLastModified = stylesheet.lastModified();
getLogger().debug( "In file " + inFile + " time: " + inFile.lastModified() );
getLogger().debug( "Out file " + outFile + " time: " + outFile.lastModified() );
getLogger().debug( "Style file " + m_xslFile + " time: " + styleSheetLastModified );
if( m_force ||
inFile.lastModified() > outFile.lastModified() ||
styleSheetLastModified > outFile.lastModified() )
{
ensureDirectoryFor( outFile );
getLogger().info( "Processing " + inFile + " to " + outFile );
configureLiaison( stylesheet );
m_liaison.transform( inFile, outFile );
}
}
catch( Exception ex )
{
getLogger().info( "Failed to process " + inFile );
if( outFile != null )
outFile.delete();
throw new TaskException( "Error", ex );
}
}
/**
* Load processor here instead of in setProcessor - this will be called from
* within execute, so we have access to the latest classpath.
*
* @param proc Description of Parameter
* @exception Exception Description of Exception
*/
private void resolveProcessor( String proc )
throws Exception
{
if( proc.equals( "trax" ) )
{
final Class clazz =
loadClass( "org.apache.tools.ant.taskdefs.optional.TraXLiaison" );
m_liaison = (XSLTLiaison)clazz.newInstance();
}
else if( proc.equals( "xalan" ) )
{
final Class clazz =
loadClass( "org.apache.tools.ant.taskdefs.optional.XalanLiaison" );
m_liaison = (XSLTLiaison)clazz.newInstance();
}
else
{
m_liaison = (XSLTLiaison)loadClass( proc ).newInstance();
}
}
}
1.1 jakarta-ant/proposal/myrmidon/src/java/org/apache/antlib/xml/XalanLiaison.java
Index: XalanLiaison.java
===================================================================
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package org.apache.antlib.xml;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import org.apache.myrmidon.api.TaskException;
import org.apache.xalan.xslt.XSLTInputSource;
import org.apache.xalan.xslt.XSLTProcessor;
import org.apache.xalan.xslt.XSLTProcessorFactory;
import org.apache.xalan.xslt.XSLTResultTarget;
/**
* Concrete liaison for Xalan 1.x API.
*
* @author <a href="mailto:rubys@us.ibm.com">Sam Ruby</a>
* @author <a href="mailto:sbailliez@apache.org">Stephane Bailliez</a>
*/
public class XalanLiaison
implements XSLTLiaison
{
private XSLTProcessor processor;
private File stylesheet;
public XalanLiaison()
throws Exception
{
processor = XSLTProcessorFactory.getProcessor();
}
public void setOutputtype( String type )
throws Exception
{
if( !type.equals( "xml" ) )
throw new TaskException( "Unsupported output type: " + type );
}
public void setStylesheet( File stylesheet )
throws Exception
{
this.stylesheet = stylesheet;
}
public void addParam( String name, String value )
{
processor.setStylesheetParam( name, value );
}
public void transform( File infile, File outfile )
throws Exception
{
FileInputStream fis = null;
FileOutputStream fos = null;
FileInputStream xslStream = null;
try
{
xslStream = new FileInputStream( stylesheet );
fis = new FileInputStream( infile );
fos = new FileOutputStream( outfile );
// systemid such as file:/// + getAbsolutePath() are considered
// invalid here...
XSLTInputSource xslSheet = new XSLTInputSource( xslStream );
xslSheet.setSystemId( stylesheet.getAbsolutePath() );
XSLTInputSource src = new XSLTInputSource( fis );
src.setSystemId( infile.getAbsolutePath() );
XSLTResultTarget res = new XSLTResultTarget( fos );
processor.process( src, xslSheet, res );
}
finally
{
// make sure to close all handles, otherwise the garbage
// collector will close them...whenever possible and
// Windows may complain about not being able to delete files.
try
{
if( xslStream != null )
{
xslStream.close();
}
}
catch( IOException ignored )
{
}
try
{
if( fis != null )
{
fis.close();
}
}
catch( IOException ignored )
{
}
try
{
if( fos != null )
{
fos.close();
}
}
catch( IOException ignored )
{
}
}
}
}//-- XalanLiaison
--
To unsubscribe, e-mail: <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>