package org.apache.maven.tasklist;

/* ====================================================================
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2001 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 acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Apache" and "Apache Software Foundation" and
 *    "Apache Maven" 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",
 *    "Apache Maven", nor may "Apache" appear in their name, without
 *    prior written permission of the Apache Software Foundation.
 *
 * 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/>.
 *
 * ====================================================================
 */

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.Iterator;

import org.apache.maven.executor.FileProcessingExecutor;
import org.apache.maven.java.SourceTool;
import org.apache.tools.ant.Project;

/**
 * An executor that utilizes a visitor to generate a task list from
 * the various task tags in JavaDoc of a project's source tree.  The
 * task list is generated as an XML file.
 *
 * @author <a href="mailto:pete-apache-dev@kazmier.com">Pete Kazmier</a>
 * @version $Id: TaskListExecutor.java,v 1.3 2002/06/07 16:46:21 kaz Exp $
 */
public class TaskListExecutor extends FileProcessingExecutor
{
    /** The output file for the XML TaskList report. */
    private File outputFile;

    /** The JavaDoc tag that represents a task, defaults to "task". */
    private String taskTag = "task";

    /** Source tool reference for parsing files. */
    private SourceTool sourceTool;

    /** Reference to the visitor. */
    private TaskListVisitor visitor;

    /** A list of Tasks accumulated by the TaskListVisitor keyed on filename. */
    private TaskList taskList = new TaskList();

    /**
     * Sets the output file used when generating the task list report.
     *
     * @param outputFile The output file.
     */
    public void setOutputFile(File outputFile)
    {
        this.outputFile = outputFile;
    }

    /**
     * Sets the tag named used to represent a task in JavaDoc.  This
     * defaults to "task"; however, we'll leave this as an undocumented
     * feature in the event someone wants to change (like Vincent).
     *
     * @param taskTag The tag name used to represent a task.
     */
    public void setTaskTag(String taskTag)
    {
        this.taskTag = taskTag;
    }

    /**
     * Set Output encoding.
     * @param outputEncoding the Charset Encoding that the object is executing in
     */
    public void setOutputEncoding(String outputEncoding)
    {
        taskList.setOutputEncoding(outputEncoding);
    }        

    /**
     * Initializes the source tool and the appropriate visitor.  This
     * method is invoked once (not once per file) before any processing
     * begins.  The source tool is used to parse each file of the source
     * tree while passing a TaskListVisitor to accumulate tasks found.
     */
    protected void doPreProcessing()
    {
        sourceTool = new SourceTool();
        visitor = new TaskListVisitor();
        visitor.setTaskTag(taskTag);
        sourceTool.setVisitor(visitor);
    }

    /**
     * Processes a file using the source tool which in turn passes the
     * TaskListVisitor through the AST of each file to accumulate a
     * {@link TaskList} of {@link TaskListEntry} objects.  This TaskList
     * is then processed in doPostProcessing.
     *
     * @param file The file to parse.
     */
    protected void doProcessFile(File file)
    {
        try
        {
            /*
             * Open each file and pass it to the source tool for parsing.
             */
            FileInputStream fis = new FileInputStream(file);
            sourceTool.parse(fis);
            fis.close();

            /*
             * Process any Tasks that were found in the file.  These Tasks
             * are accumulated by the TaskListVisitor and are available via
             * its getTasks() method.  We create a TaskListEntry object
             * which associates a filename with the Tasks found in that file.
             */
            processResults(relativeFilename(file));
        }
        catch (Exception e)
        {
            log("Non-fatal error while parsing file: " + file);
            log(e.toString(), Project.MSG_DEBUG);
        }
    }

    /**
     * Prints the results of the executor to a file as XML if a task
     * list contains entries.  This method is invoked once (not once per
     * file) after processing is completed.
     */
    protected void doPostProcessing()
    {
        /*
         * No need to generate a task list if there aren't any entries
         * in the list!
         */
        if (taskList.getTaskListEntries().size() == 0)
        {
            return;
        }

        try
        {
            PrintWriter out = new PrintWriter(new OutputStreamWriter(
                    new FileOutputStream(outputFile), taskList.getOutputEncoding()));

            /*
             * Let the task list render itself.
             */
            out.println(taskList.toXML());
            out.close();
        }
        catch (IOException e)
        {
            log("Could not write the task list to " + outputFile);
            log(e.toString(), Project.MSG_DEBUG);
        }
    }

    /**
     * Checks to see if any Tasks were collected by the visitor for the
     * specified file and creates a new {@link TaskListEntry} for those
     * Tasks.  The entry is then added to the {@link TaskList}, and the
     * visitor's list of tasks is cleared.
     *
     * @param filename The filename that these Tasks came from.
     */
    private void processResults(String filename)
    {
        /*
         * If the visitor didn't collect any tasks, then there is no
         * further processing that needs to occur.
         */
        if (visitor.getTasks().size() == 0)
        {
            return;
        }

        /*
         * Create a new TaskListEntry for this filename and its Tasks.
         */
        TaskListEntry taskListEntry = new TaskListEntry();
        taskListEntry.setFilename(filename);

        for (Iterator i = visitor.getTasks().iterator(); i.hasNext();)
        {
            Task task = (Task) i.next();
            taskListEntry.addTask(task);
        }

        /*
         * Add the new entry to the TaskList, and then clear the list
         * of tasks present in the Visitor so they are not accounted
         * more than once.  If we didn't care about the filename, we
         * could just let the visitor accumulate all the tasks and forgo
         * any processing until the end in doPostProcessing.
         */
        taskList.addTaskListEntry(taskListEntry);
        visitor.getTasks().clear();
    }

    /**
     * Converts the absolute filename into the relative filename from
     * the base directory specified as part of the Ant task.
     *
     * @param file The file.
     * @return The relative pathname from the base specified by the Ant
     * task.
     */
    private String relativeFilename(File file)
    {
        String filename = file.toString();
        String base = getBase().toString();

        /*
         * The base can be a single file, or a directory.  First, lets
         * make sure that the two aren't equal (i.e. the base was
         * specified as a file), if they are, then just return the
         * filename.  Otherwise, strip the base from the filename.
         */
        if (base.equals(filename))
        {
            return filename;
        }
        else
        {
            return filename.substring(base.length() + 1);
        }
    }
}

