package org.apache.maven.changelog;

/* ====================================================================
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2002 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/>.
 *
 * ====================================================================
 */

// java imports
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
// maven imports
import org.apache.maven.executor.ProjectExecutor;
import org.apache.maven.project.Developer;

/**
 * Change log task. It uses a ChangeLogGenerator and ChangeLogParser to create
 * a Collection of ChangeLogEntry objects, which are used to produce an XML
 * output that represents the list of changes.
 *
 *
 * @author <a href="mailto:glenn@somanetworks.com">Glenn McAllister</a>
 * @author <a href="mailto:jeff.martin@synamic.co.uk">Jeff Martin</a>
 * @author <a href="mailto:jason@zenplex.com">Jason van Zyl</a>
 * @author <a href="mailto:dion@multitask.com.au">dIon Gillard</a>
 * @author <a href="mailto:bodewig@apache.org">Stefan Bodewig</a>
 * @author <a href="mailto:peter@apache.org">Peter Donald</a>
 * @version $Id: ChangeLog.java,v 1.6 2002/06/04 15:26:24 dion Exp $
 */
public class ChangeLog extends ProjectExecutor
{
    /**
     * Used to specify the range of log entries to retrieve.
     */
    private String range;

    /**
     * Input dir. Working directory for running CVS executable
     */
    private File base;

    /**
     * Output file for xml document
     */
    private File output;

    /** change log entries parsed */
    private Collection entries;

    /**
     * The classname of our ChangeLogFactory, defaulting to Maven's built in
     * CVS factory.
     */
    private String clFactoryClass = 
        "org.apache.maven.cvslib.CvsChangeLogFactory";

    /**
     * Set the ChangeLogFactory class name.  If this isn't set, the factory
     * defaults to Maven's build in CVS factory.
     *
     * @param factoryClassName the fully qualified factory class name
     */
    public void setFactory(String factoryClassName)
    {
        clFactoryClass = factoryClassName;
    }
   
    /**
     * Set the range of log entries to process; the interpretation of this
     * parameter depends on the generator.
     *
     * @param range the range of log entries.
     */
    public void setRange(String range)
    {
        this.range = range;
    }

    /**
     * Get the range of log entries to process; the interpretation of the range
     * depends on the generator implementation.
     *
     * @return the range of log entries.
     */
    public String getRange()
    {
        return range;
    }

    /**
     * Set the base directory for the change log generator.
     * @param base the base directory
     */
    public void setBasedir(File base)
    {
        this.base = base;
    }

    /**
     * Get the base directory for the change log generator.
     *
     * @return the base directory
     */
    public File getBasedir()
    {
        return base;
    }

    /**
     * Set the output file for the log.
     * @param output the output file
     */
    public void setOutput(File output)
    {
        this.output = output;
    }

    /**
     * Execute task.
     * @throws FileNotFoundException if {@link ChangeLog#base} doesn't exist
     * @throws IOException if there are problems running CVS
     * @throws UnsupportedEncodingException if the underlying platform doesn't
     *      support ISO-8859-1 encoding
     */
    public void doExecute() throws FileNotFoundException, IOException, 
        UnsupportedEncodingException
    {
        if (output == null)
        {
            throw new NullPointerException("output must be set");
        }

        generateEntries();
        replaceAuthorIdWithName();
        createDocument();
    }
   
    /**
     * Create the change log entries.
     * @throws IOException if there is a problem creating the change log
     * entries.
     */
    private void generateEntries() throws IOException
    {
        ChangeLogFactory factory = createFactory();
        ChangeLogGenerator generator = factory.createGenerator();
        ChangeLogParser parser = factory.createParser();

        generator.init(this);
        parser.init(this);

        try
        {
            setEntries(generator.getEntries(parser));
            log("ChangeLog found: " + getEntries().size() + " entries");
        }
        finally
        {
            parser.cleanup();
            generator.cleanup();
        }
    }

    /**
     * Create a new instance of the ChangeLogFactory specified by the
     * <code>clFactory</code> member.
     *
     * @return the new ChangeLogFactory instance
     * @throws IOException if there is a problem creating the instance.
     */
    private ChangeLogFactory createFactory() throws IOException
    {
        try
        {
            Class clazz = Class.forName(clFactoryClass);
            return (ChangeLogFactory) clazz.newInstance();
        }
        catch (ClassNotFoundException cnfe)
        {
            throw new IOException("Cannot find class " + clFactoryClass +
                    " " + cnfe.toString());
        }
        catch (IllegalAccessException iae)
        {
            throw new IOException("Cannot access class " + clFactoryClass +
                    " " + iae.toString());
        }
        catch (InstantiationException ie)
        {
            throw new IOException("Cannot instantiate class " + clFactoryClass +
                    " " + ie.toString());
        }
    }
    
    /**
     * Set up list of developers mapping id to name.
     * @task This should be a facility on the maven project itself
     * @return a list of developers ids and names
     */
    private Properties getUserList()
    {
        Properties userList = new Properties();
        
        List developers = getMavenProject().getDevelopers();

        Developer developer = null;
        for (Iterator i = developers.iterator(); i.hasNext();)
        {
            developer = (Developer) i.next();
            userList.put(developer.getId(), developer.getName());
        }
        
        return userList;
    }

    /**
     * replace all known author's id's with their maven specified names
     */
    private void replaceAuthorIdWithName()
    {
        Properties userList = getUserList();
        ChangeLogEntry entry = null;
        for (Iterator i = getEntries().iterator(); i.hasNext();)
        {
            entry = (ChangeLogEntry) i.next();
            if (userList.containsKey(entry.getAuthor()))
            {
                entry.setAuthor(userList.getProperty(entry.getAuthor()));
            }
        }
    }
    
    /**
     * Create the XML document from the currently available details
     * @throws FileNotFoundException when the output file previously provided
     *      does not exist
     * @throws UnsupportedEncodingException when the platform doesn't support
     *      ISO-8859-1 encoding
     */
    private void createDocument() throws FileNotFoundException,
        UnsupportedEncodingException
    {
        
        PrintWriter out = new PrintWriter(new OutputStreamWriter(
            new FileOutputStream(output), getOutputEncoding()));
        out.println(toXML());
        out.flush();
        out.close();
    }
    
    /**
     * @return an XML document representing this change log and it's entries 
     */
    private String toXML()
    {
        StringBuffer buffer = new StringBuffer();
        buffer.append("<?xml version=\"1.0\" encoding=\"")
            .append(getOutputEncoding())
            .append("\" ?>\n")
            .append("<document>\n")
            .append("<properties><title>Change Log</title></properties>\n")
            .append("<body>\n")
            .append("<section name=\"Change Log\"/>\n")
            .append("<changelog>\n");
        
        for (Iterator i = getEntries().iterator(); i.hasNext();)
        {
            buffer.append(((ChangeLogEntry) i.next()).toXML());
        }

        buffer.append("</changelog>\n")
            .append("</body>\n")
            .append("</document>\n");
        
        return buffer.toString();
    }
    
    /**
     * Getter for property entries.
     * @return Value of property entries.
     */
    public Collection getEntries()
    {
        if (entries == null) 
        {
            entries = Arrays.asList(new Object[0]);
        }
        return entries;
    }
    
    /** 
     * Setter for property entries.
     * @param entries New value of property entries.
     */
    public void setEntries(Collection entries)
    {
        this.entries = entries;
    }
    
} // end of ChangeLog
