vmassol     01/04/28 05:04:27

  Added:       cactus/src/ant/org/apache/commons/cactus/ant
                        ChangeLogNewsTask.java
  Log:
  new Ant task to extract cvs log information and generate an XML file with the result
  
  Revision  Changes    Path
  1.1                  
jakarta-commons/cactus/src/ant/org/apache/commons/cactus/ant/ChangeLogNewsTask.java
  
  Index: ChangeLogNewsTask.java
  ===================================================================
  /*
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 1999 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 acknowlegement:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Ant", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written
   *    permission, please contact [EMAIL PROTECTED]
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * 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/>.
   */
  package org.apache.commons.cactus.ant;
  
  import java.io.*;
  import java.util.*;
  import java.text.*;
  
  import org.apache.tools.ant.*;
  import org.apache.tools.ant.taskdefs.*;
  import org.apache.tools.ant.types.*;
  
  /**
   * A CVS log task that extract information from the execution of the
   * '<code>cvs log</code>' command and put them in a generated output XML file.
   *
   * Note: I have rewritten this task based on the ChangeLog cvslog task from the
   *       Jakarta Alexandria project (initially written by Jeff Martin)
   *
   * @version @version@
   * @author Jeff Martin <a href="mailto:[EMAIL PROTECTED]";>Jeff Martin</a>
   * @author Vincent Massol <a 
href="mailto:[EMAIL PROTECTED]";>[EMAIL PROTECTED]</a> 
   */
  public class ChangeLogNewsTask extends Task implements ExecuteStreamHandler
  {
      /**
       * Name of properties file containing the user list. This list is used to
       * match a user id retrieved from the '<code>cvs log</code>' command with a
       * display name. The format of the file is : 
       * '<code>[user id] = [display name]</code>'.
       */
      private File m_UserConfigFile;
  
      /**
       * In memory data structure to store the user list of matching pairs of
       * (user id, user display name).
       */
      private Properties m_UserList = new Properties();
  
      /**
       * CVS working directory where the '<code>cvs log</code>' operation is
       * performed.
       */
      private File m_CVSWorkingDirectory;
  
      /**
       * XML Output file containing the results.
       */
      private File m_OutputFile;
  
      /**
       * Date before which the cvs logs are ignored
       */
      private Date m_ThresholdDate;
  
      /**
       * Input stream read in from CVS log command
       */
      private BufferedReader m_Input;
  
      /**
       * Output file stream where results will be written to
       */
      private PrintWriter m_Output;
      
      /**
       * Filesets containting list of files against which the cvs log will be
       * performed. If empty then all files will in the working directory will
       * be checked.
       */
      private Vector m_Filesets = new Vector();
  
      // state machine states
      private final static int GET_ENTRY = 0;
      private final static int GET_FILE = 1;
      private final static int GET_DATE = 2;
      private final static int GET_COMMENT = 3;
      private final static int GET_REVISION = 4;
      private final static int GET_PREVIOUS_REV = 5;
  
      /**
       * Input format for dates read in from cvs log
       */
      private static final SimpleDateFormat INPUT_DATE = 
          new SimpleDateFormat("yyyy/MM/dd");
  
      /**
       * Output format for dates written to the XML file
       */
      private static final SimpleDateFormat OUTPUT_DATE =
          new SimpleDateFormat("yyyy-MM-dd");
  
      /**
       * Output format for times written to the XML file
       */
      private static final SimpleDateFormat OUTPUT_TIME =
          new SimpleDateFormat("hh:mm");
  
      /**
       * Set the properties file name containing the matching list of (user id,
       * user display name). This method is automatically called by the Ant
       * runtime engine when the <code>users</code> attribute is encountered
       * in the Ant XML build file. This attribute is optional.
       *
       * @param theUserConfigFileName the properties file name relative to the
       *        Ant project base directory (basedir attribute in build file).
       */
      public void setUsers(File theUserConfigFileName)
      {
          m_UserConfigFile = theUserConfigFileName;
      }
  
      /**
       * Set the CVS working directory where the cvs log operation will be
       * performed. This method is automatically called by the Ant
       * runtime engine when the <code>work</code> attribute is encountered
       * in the Ant XML build file. This attribute is mandatory.
       *
       * @param theWorkDir the CVS working directory relative to the Ant
       *        project base directory (basedir attribute in build file).
       */
      public void setWork(File theWorkDir)
      {
          m_CVSWorkingDirectory = theWorkDir;
      }
  
      /**
       * Set the output file for the log. This method is automatically called by 
       * the Ant runtime engine when the <code>output</code> attribute is
       * encountered in the Ant XML build file. This attribute is mandatory.
       *
       * @param theOutputFile the XML output file relative to the Ant project
       *                      base directory (i.e. basedir attrifbute in build
       *                      file).
       */
      public void setOutput(File theOutputFile)
      {
          m_OutputFile = theOutputFile;
      }
  
      /**
       * Set the threshold cvs log date. This method is automatically called by 
       * the Ant runtime engine when the <code>date</code> attribute is
       * encountered in the Ant XML build file. This attribute is optional. The
       * format is "yyyy/MM/dd hh:mm".
       *
       * @param theThresholdDate the threshold date before which cvs log are
       *                         ignored.
       */
      public void setDate(String theThresholdDate)
      {
          try {
              m_ThresholdDate = INPUT_DATE.parse(theThresholdDate);
          } catch(ParseException e) {
              throw new BuildException("Bad date format [" + 
                  theThresholdDate + "].");
          }
      }
  
      /**
       * Set the threshold cvs log date by calculating it : "today - elapsed".
       * This method is automatically called by 
       * the Ant runtime engine when the <code>elapsed</code> attribute is
       * encountered in the Ant XML build file. This attribute is optional. The
       * elasped time must be expressed in days.
       *
       * @param theElapsedDays the elapsed time from now in days. All cvs logs
       *                       that are this old will be shown.
       */
      public void setElapsed(Long theElapsedDays)
      {
          long now = System.currentTimeMillis();
          m_ThresholdDate = new Date(
              now - theElapsedDays.longValue() * 24 * 60 * 60 * 1000);
      }
  
      /**
       * Adds a set of files (nested fileset attribute).
       * This method is automatically called by 
       * the Ant runtime engine when the <code>fileset</code> nested tag is
       * encountered in the Ant XML build file. This attribute is optional.
       *
       * @param theSet the fileset that contains the list of files for which
       *               cvs logs will be checked.
       */
      public void addFileset(FileSet theSet)
      {
          m_Filesets.addElement(theSet);
      }
  
      /**
       * Read the user list from the properties file and store the matches in
       * memory. If no properties file has been set, do not do anything.
       */
      private void readUserList()
      {
          if (m_UserConfigFile != null) {
              if (!m_UserConfigFile.exists()) {
                  throw new BuildException("User list configuration file [" + 
                      m_UserConfigFile.getAbsolutePath() + 
                      "] was not found. Please check location.");
              }
              try {
                  m_UserList.load(new FileInputStream(m_UserConfigFile));
              } catch(IOException e) {
                  throw new BuildException(e);
              }
          }
      }
  
      /**
       * Execute task
       */
      public void execute() throws BuildException
      {
          if (m_CVSWorkingDirectory == null) {
              throw new BuildException("The [workDir] attribute must be set");
          }
  
          if (!m_CVSWorkingDirectory.exists()) {
              throw new BuildException("Cannot find CVS working directory [" +
                  m_CVSWorkingDirectory.getAbsolutePath() + "]");
          }
  
          if (m_OutputFile == null) {
              throw new BuildException("The [output] attribute must be set");
          }
  
          readUserList();
  
          Commandline toExecute = new Commandline();
  
          toExecute.setExecutable("cvs");
          toExecute.createArgument().setValue("log");
  
          // Check if a threshold date has been specified
          if (m_ThresholdDate != null) {
              toExecute.createArgument().setValue("-d\">=" + 
                  OUTPUT_DATE.format(m_ThresholdDate) + "\"");
          }
  
          // Check if list of files to check has been specified
          if (!m_Filesets.isEmpty()) {
  
              Enumeration e = m_Filesets.elements();
              while(e.hasMoreElements()) {
                  FileSet fs = (FileSet)e.nextElement();
                  DirectoryScanner ds = fs.getDirectoryScanner(project);
                  String[] srcFiles = ds.getIncludedFiles();
                  for (int i = 0; i < srcFiles.length; i++) {
                      toExecute.createArgument().setValue(srcFiles[i]);
                  }                
              }
          }
  
          Execute exe = new Execute(this);
          exe.setCommandline(toExecute.getCommandline());
          exe.setAntRun(project);
          exe.setWorkingDirectory(m_CVSWorkingDirectory);
          try {
              exe.execute();
          } catch(IOException e) {
              throw new BuildException(e);
          }
      }
  
      /**
       * Set the input stream for the CVS process. As CVS requires no input, this
       * is not used.
       *
       * @param theOs the output stream to write to the standard input stream of
       *              the subprocess (i.e. the CVS process)
       */
      public void setProcessInputStream(OutputStream theOs) throws IOException
      {
      }
  
      /**
       * Set the error stream for reading from CVS log. Not used in the current
       * version (should be handled in future versions).
       *
       * @param theIs the input stream to read from the error stream from the
       *              subprocess (i.e. the CVS process)
       */
      public void setProcessErrorStream(InputStream theIs) throws IOException
      {
      }
  
      /**
       * Set the input stream used to read from CVS log
       *
       * @param theIs the input stream to read from the output stream of the
       *              subprocess (i.e. the CVS process)
       */
      public void setProcessOutputStream(InputStream theIs) throws IOException
      {
          m_Input = new BufferedReader(new InputStreamReader(theIs));
      }
  
      /**
       * Stop handling of the streams (i.e. the cvs process).
       */
      public void stop()
      {
      }
  
      /**
       * Start reading from the cvs log stream.
       */
      public void start() throws IOException
      {
          m_Output = new PrintWriter(new OutputStreamWriter(
              new FileOutputStream(m_OutputFile),"UTF-8"));
  
          String file = null;
          String line = null;
          String date = null;
          String author = null;
          String comment = null;
          String revision = null;
          String previousRev = null;
  
          // Current state in the state machine used to parse the CVS log stream
          int status = GET_FILE;
  
          // RCS entries
          Hashtable entries = new Hashtable();
  
          while ((line = m_Input.readLine()) != null) {
  
              switch(status){
  
                  case GET_FILE:
                      if (line.startsWith("Working file:")) {
                          file = line.substring(14, line.length());
                          status = GET_REVISION;
                      }
                      break;
  
                  case GET_REVISION:
                      if (line.startsWith("revision")) {
                          revision = line.substring(9);
                          status = GET_DATE;
                      }
  
                      // If we encounter a "=====" line, it means there was no
                      // description and thus the entry must be forgotten
                      else if (line.startsWith("======")) {
                          status = GET_FILE;
                      }
                      break;
  
                  case GET_DATE:
                      if (line.startsWith("date:")) {
                          date = line.substring(6, 16);
                          line = line.substring(line.indexOf(";") + 1);
                          author = line.substring(10, line.indexOf(";"));
  
                          if ((m_UserList != null) && m_UserList.containsKey(author)) {
                              author = "<![CDATA[" + m_UserList.getProperty(author) + 
"]]>";
                          }
  
                          status = GET_COMMENT;
                      }
                      break;
  
                  case GET_COMMENT:
                      comment = "";
                      while (line != null && !line.startsWith("======") && 
                          !line.startsWith("------")) {
  
                          comment += line + "\n";
                          line = m_Input.readLine();
                      }
                      comment = "<![CDATA[" + 
                          comment.substring(0,comment.length() - 1) + "]]>";
  
                      status = GET_PREVIOUS_REV;
                      break;
  
                  case GET_PREVIOUS_REV:
                      if (line.startsWith("revision")) {
                          previousRev = line.substring(9);
                          status = GET_FILE;
  
                          Entry entry;
                          if (!entries.containsKey(date + author + comment)) {
                              entry = new Entry(date, author, comment);
                              entries.put(date + author + comment, entry);
                          } else {
                              entry = (Entry)entries.get(date + author + comment);
                          }
                          entry.addFile(file, revision, previousRev);
                      }
                      if (line.startsWith("======")) {
                          status = GET_FILE;
                          Entry entry;
                          if (!entries.containsKey(date + author + comment)) {
                              entry = new Entry(date, author, comment);
                              entries.put(date + author + comment, entry);
                          }else {
                              entry = (Entry)entries.get(date + author + comment);
                          }
                          entry.addFile(file, revision);
                      }
  
              }
              
          }
          m_Output.println("<changelog>");
          Enumeration en = entries.elements();
          while (en.hasMoreElements()) {
              ((Entry)en.nextElement()).print();
          }
          m_Output.println("</changelog>");
          m_Output.flush();
          m_Output.close();
      }
  
      /**
       * CVS entry class
       */
      private class Entry
      {
          /**
           * The entry date
           */
          private Date m_Date;
  
          /**
           * The entry author id
           */
          private final String m_Author;
  
          /**
           * The comment entry
           */
          private final String m_Comment;
  
          /**
           * The list of files that were CVS committed at the same time
           */
          private final Vector m_Files = new Vector();
  
          /**
           * Create an entry.
           *
           * @param theDate the entry's date
           * @param theAuthor the entry's author
           * @param theComment the entry's comment
           */
          public Entry(String theDate, String theAuthor, String theComment)
          {
              try {
                  m_Date = INPUT_DATE.parse(theDate);
              } catch(ParseException e) {
                  log("Bad date format [" + theDate + "].");
              }
              m_Author = theAuthor;
              m_Comment = theComment;
          }
  
          public void addFile(String theFile, String theRevision)
          {
              m_Files.addElement(new RCSFile(theFile, theRevision));
          }
  
          public void addFile(String theFile, String theRevision, String 
thePreviousRev)
          {
              m_Files.addElement(new RCSFile(theFile, theRevision, thePreviousRev));
          }
  
          public String toString()
          {
              return m_Author + "\n" + m_Date + "\n" + m_Files + "\n" + m_Comment;
          }
  
          public void print()
          {
              m_Output.println("\t<entry>");
              m_Output.println("\t\t<date>" + OUTPUT_DATE.format(m_Date) + "</date>");
              m_Output.println("\t\t<time>" +OUTPUT_TIME.format(m_Date) + "</time>");
              m_Output.println("\t\t<author>" + m_Author + "</author>");
  
              Enumeration e = m_Files.elements();
              while (e.hasMoreElements()) {
                  RCSFile file = (RCSFile)e.nextElement();
                  m_Output.println("\t\t<file>");
                  m_Output.println("\t\t\t<name>" + file.getName() + "</name>");
                  m_Output.println("\t\t\t<revision>" + file.getRevision() + 
"</revision>");
                  if (file.getPreviousRev() != null) {
                      m_Output.println("\t\t\t<prevrevision>" + file.getPreviousRev()
                          + "</prevrevision>");
                  }
                  m_Output.println("\t\t</file>");
              }
              m_Output.println("\t\t<msg>" + m_Comment + "</msg>");
              m_Output.println("\t</entry>");
          }
  
          private class RCSFile
          {
              private String m_Name;
              private String m_Rev;
              private String m_PreviousRev;
  
              private RCSFile(String theName, String theRev)
              {
                  this(theName, theRev, null);
              }
  
              private RCSFile(String theName, String theRev, String thePreviousRev)
              {
                  m_Name = theName;
                  m_Rev = theRev;
                  if (!m_Rev.equals(m_PreviousRev)) {
                      m_PreviousRev = thePreviousRev;
                  }
              }
  
              public String getName()
              {
                  return m_Name;
              }
  
              public String getRevision()
              {
                  return m_Rev;
              }
              public String getPreviousRev()
              {
                  return m_PreviousRev;
              }
          }
      }
  
  }
  
  
  

Reply via email to