

import java.io.File;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.taskdefs.ExecuteJava;
import org.apache.tools.ant.taskdefs.MatchingTask;
import org.apache.tools.ant.types.Commandline;
import org.apache.tools.ant.types.Path;

/**
 * Ant task to derive a set of target files from a given set of source
 * files.  The derivation is done by invoking {@link Main#main(String[])}.
 *
 * @layer<antDerive>
 */
    
public class DeriveAntTask extends MatchingTask {

    /**
     * Adds a given argument to the argument list.
     *
     * @layer<antDerive>
     */
    public Commandline.Argument createArg() {
        return command.createArgument() ;
    }

    /**
     * Adds a path for source directory search.
     *
     * @layer<antDerive>
     */
    public Path createSrc() {

        if ( sourcePath == null )
            sourcePath = new Path( getProject() ) ;

        return sourcePath.createPath() ;
    }

    /**
     * Invokes {@link Main#main(String[])} for each source file.
     *
     * @layer<antDerive>
     */
    public void execute() throws BuildException {

        if ( sourcePath == null || sourcePath.size() < 1 )
            throw new BuildException( "srcdir attribute must be set",
                            getLocation() ) ;

        createArg().setValue( listFiles ? "-verbose" : "-quiet" ) ;
        String[] extraArgs = command.getArguments() ;

        int errors = 0 ;

        String[] sourceDirs = sourcePath.list() ;
        for ( int dirIndex = 0; dirIndex < sourceDirs.length; ++dirIndex ) {

            File directory = getProject().resolveFile( sourceDirs [dirIndex] ) ;
            if ( listFiles )
                log( "directory \"" + directory + '"', Project.MSG_INFO ) ;

            if ( ! directory.exists() )
                throw new BuildException( "source directory \""
                                                + directory.getPath()
                                                + "\" does not exist",
                                                getLocation() ) ;

            DirectoryScanner scanner = getDirectoryScanner( directory ) ;
            String[] files = scanner.getIncludedFiles() ;

            if ( files.length > 0 ) {
                for ( int n = 0 ; n < files.length ; ++n )
                    files[n] = new File( directory,files[n] ).toString() ;
                processFiles( files, extraArgs ) ;

            }
            else
                if ( errorIfNoFiles ) {
                    throw new BuildException( "no input files given",getLocation() ) ;

                }
        }

        if ( errors > 0 ) {
            String errorMessage =
                            "derivation failed in "
                            + errors
                            + " source "
                            + ( errors == 1 ? "directory" : "directories" ) ;
            if ( failOnError )
                throw new BuildException( errorMessage, getLocation() ) ;
            else
                log( errorMessage, Project.MSG_ERR ) ;
        }
    }

    /**
     * A <code>main</code> method causes a wrapper class to be generated
     * at the top level of the enclosing package.  As a result, this task
     * will be available as a top-level (<em>not</em> inner) class within
     * the enclosing package.
     *
     * @layer<antDerive>
     */
    public static void main( String[] args ) {
        throw new UnsupportedOperationException( "invoking \"DeriveAntTask.main\" is not supported" ) ;
    }

    /**
     * Processes a given array of source file names with a specified
     * array of command-line options.
     *
     * @layer<antDerive>
     */
    public boolean processFiles( String[] sources, String[] options )
    throws BuildException {

        // Form total argument list by concatenating sources with options:
        //
        String[] args = new String [sources.length + options.length] ;
        System.arraycopy( sources,0, args,0, sources.length ) ;
        System.arraycopy( options,0, args,sources.length, options.length ) ;

        try {
            Main.main( args ) ;
            return true ;

        }
        catch ( BuildException exception ) {
            throw exception ;

        }
        catch ( Exception exception ) {
            log( exception.toString(), Project.MSG_ERR ) ;
            return false ;
        }
    }

    /**
     * If true and if errors occur during derivation, aborts the enclosing
     * build at the end of the task.
     *
     * @layer<antDerive>
     */
    public void setFailonerror( boolean fail ) {
        failOnError = fail ;
    }

    /**
     * If true, list the source files to be handed to the tool.
     *
     * @layer<antDerive>
     */
    public void setListfiles( boolean list ) {
        listFiles = list ;
    }

    /**
     * If true, throws a build error if no source files are found for the
     * tool.  If false and there are no source files, this task just exits
     * without invoking the tool.
     *
     * @layer<antDerive>
     */
    public void setErrorifnofiles( boolean errorIfNoFiles ) {
        this.errorIfNoFiles = errorIfNoFiles ;
    }

    /**
     * An alternative way to specify a source search path.
     * @see #createSrc()
     *
     * @layer<antDerive>
     */
    public void setSrcdir( Path srcDir ) {

        if ( sourcePath == null )
            sourcePath = srcDir ;
        else
            sourcePath.append( srcDir ) ;
    }

    /**
     * Specifies a timeout on the time to derive from a given file.
     *
     * @layer<antDerive>
     */
    public void setTimeout( Long interval ) {
        timeOut = interval ;
    }

    protected Commandline command = new Commandline() ;
    private boolean errorIfNoFiles = true ;
    private boolean failOnError = false ;
    private boolean listFiles = false ;
    private Path sourcePath = null ;
    private Long timeOut = null ;

}
