package com.lgc.buildmagic;

import java.io.File;
import java.io.IOException;

import org.apache.tools.ant.Task;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.BuildException;

import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.types.DirSet;
import org.apache.tools.ant.types.FileSet;
import org.apache.tools.ant.types.FileList;
import org.apache.tools.ant.types.Reference;

/**
 * Calls a given target for all defined sub-builds.
 *
 * @author <a href="mailto:ddevienne@lgc.com">Dominique Devienne</a>
 * @version Sep 2002 - Copyright (c) 2002, Landmark Graphics Corp.
 */
public class Which
             extends Task {

    /** The search path used to find a file. */
    private Path searchpath;

    /** The simple name of the file to search. */
    private String basename;

    /**
     * The property name to contain the full
     * pathname of the file searched for.
     */
    private String property;

    /**
     * Runs the various sub-builds.
     */
    public void execute()
                throws BuildException {
        if (property == null) {
            throw new BuildException("property attribute is required", 
                                     getLocation());
        }

        if (searchpath == null) {
            throw new BuildException("No searchpath specified",
                                     getLocation());
        }

        final String[] filenames = searchpath.list();
        final int count = filenames.length;
        if (count < 1) {
            throw new BuildException("Empty searchpath specified",
                                     getLocation());
        }

        for (int i=0; i<count; ++i) {
            File parent = new File(filenames[i]);
            if (!parent.isDirectory()) {
                // Skip non-directories in the search path
                continue;
            }

            log("Searching " + parent, Project.MSG_DEBUG);
            File file = new File(parent, basename);
            if (file.exists() && file.isFile()) {
                String fullpath = null;
                try {
                    fullpath = file.getCanonicalPath();
                } catch (IOException e) {
                    throw new BuildException(e, getLocation());
                }
                getProject().setNewProperty(property, fullpath);
                return;
            }
        }
    }

    /**
     * Sets the default build file name to append to directory
     * names found in the build path.
     *
     * @param  basename the short build file name. Defaults to "build.xml".
     */
    public void setBasename(String basename) {
        this.basename = basename;
    }

    /**
     * Sets the name of the property to contain the full pathname of the 
     * file searched for, if found.
     *
     * @param property the name of the property to set.
     */
    public void setProperty(String property) {
        this.property = property;
    }

    /**
     * Adds a directory set to the implicit build path.
     * <p>
     * <em>Note that the directories will be added to the build path
     * in no particular order, so if order is significant, one should
     * use a file list instead!</em>
     *
     * @param  set the directory set to add.
     */
    public void addDirset(DirSet set) {
        getSearchpath().addDirset(set);
    }

    /**
     * Adds a file set to the implicit build path.
     * <p>
     * <em>Note that the directories will be added to the build path
     * in no particular order, so if order is significant, one should
     * use a file list instead!</em>
     *
     * @param  set the file set to add.
     */
    public void addFileset(FileSet set) {
        getSearchpath().addFileset(set);
    }

    /**
     * Adds an ordered file list to the implicit build path.
     * <p>
     * <em>Note that contrary to file and directory sets, file lists
     * can reference non-existent files or directories!</em>
     *
     * @param  list the file list to add.
     */
    public void addFilelist(FileList list) {
        getSearchpath().addFilelist(list);
    }

    /**
     * Set the searchpath to be used to find sub-projects.
     * 
     * @param  s an Ant Path object containing the searchpath.
     */
    public void setSearchpath(Path s) {
        getSearchpath().append(s);
    }
    
    /**
     * Creates a nested build path, and add it to the implicit build path.
     *
     * @return the newly created nested build path.
     */
    public Path createSearchpath() {
        return getSearchpath().createPath();
    }

    /**
     * Creates a nested <code>&lt;searchpathelement&gt;</code>,
     * and add it to the implicit build path.
     *
     * @return the newly created nested build path element.
     */
    public Path.PathElement createSearchpathElement()
                            throws BuildException {
        return getSearchpath().createPathElement();
    }

    /**
     * Gets the implicit build path, creating it if <code>null</code>.
     *
     * @return the implicit build path.
     */
    private Path getSearchpath() {
        if (searchpath == null) {
            searchpath = new Path(getProject());
        }
        return searchpath;
    }

    /**
     * Searchpath to use, by reference.
     *
     * @param  r a reference to an Ant Path object containing the searchpath.
     */
    public void setSearchpathRef(Reference r) {
        createSearchpath().setRefid(r);
    }

} // END class Which

