| /* |
| * The Apache Software License, Version 1.1 |
| * |
| * Copyright (c) 2002-2003 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 "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 apache@apache.org. |
| * |
| * 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.tools.ant.taskdefs.cvslib; |
| |
| import java.io.BufferedReader; |
| import java.io.File; |
| import java.io.FileOutputStream; |
| import java.io.FileReader; |
| import java.io.IOException; |
| import java.io.OutputStreamWriter; |
| import java.io.PrintWriter; |
| import java.io.UnsupportedEncodingException; |
| import java.util.Vector; |
| import java.util.StringTokenizer; |
| |
| import org.apache.tools.ant.BuildException; |
| import org.apache.tools.ant.Project; |
| import org.apache.tools.ant.taskdefs.AbstractCvsTask; |
| import org.apache.tools.ant.util.FileUtils; |
| |
| /** |
| * Examines the output of cvs rdiff between two tags. |
| * |
| * It produces an XML output representing the list of changes. |
| * <PRE> |
| * <!-- Root element --> |
| * <!ELEMENT tagdiff ( entry+ ) > |
| * <!-- Start tag of the report --> |
| * <!ATTLIST tagdiff startTag NMTOKEN #IMPLIED > |
| * <!-- End tag of the report --> |
| * <!ATTLIST tagdiff endTag NMTOKEN #IMPLIED > |
| * <!-- Start date of the report --> |
| * <!ATTLIST tagdiff startDate NMTOKEN #IMPLIED > |
| * <!-- End date of the report --> |
| * <!ATTLIST tagdiff endDate NMTOKEN #IMPLIED > |
| * |
| * <!-- CVS tag entry --> |
| * <!ELEMENT entry ( file ) > |
| * <!-- File added, changed or removed --> |
| * <!ELEMENT file ( name, revision?, prevrevision? ) > |
| * <!-- Name of the file --> |
| * <!ELEMENT name ( #PCDATA ) > |
| * <!-- Revision number --> |
| * <!ELEMENT revision ( #PCDATA ) > |
| * <!-- Previous revision number --> |
| * <!ELEMENT prevrevision ( #PCDATA ) > |
| * </PRE> |
| * |
| * @author <a href="mailto:fred@castify.net">Frederic Lavigne</a> |
| * @author <a href="mailto:rob@springwellfarms.ca">Rob van Oostrum</a> |
| * @version $Revision$ $Date$ |
| * @since Ant 1.5 |
| * @ant.task name="cvstagdiff" |
| */ |
| public class CvsTagDiff extends AbstractCvsTask { |
| /** |
| * Token to identify the word file in the rdiff log |
| */ |
| static final String FILE_STRING = "File "; |
| /** |
| * Token to identify the word file in the rdiff log |
| */ |
| static final String TO_STRING = " to "; |
| /** |
| * Token to identify a new file in the rdiff log |
| */ |
| static final String FILE_IS_NEW = " is new;"; |
| /** |
| * Token to identify the revision |
| */ |
| static final String REVISION = "revision "; |
| |
| /** |
| * Token to identify a modified file in the rdiff log |
| */ |
| static final String FILE_HAS_CHANGED = " changed from revision "; |
| |
| /** |
| * Token to identify a removed file in the rdiff log |
| */ |
| static final String FILE_WAS_REMOVED = " is removed"; |
| |
| /** |
| * The cvs package/module to analyse |
| */ |
| private String mypackage; |
| |
| /** |
| * The earliest tag from which diffs are to be included in the report. |
| */ |
| private String mystartTag; |
| |
| /** |
| * The latest tag from which diffs are to be included in the report. |
| */ |
| private String myendTag; |
| |
| /** |
| * The earliest date from which diffs are to be included in the report. |
| */ |
| private String mystartDate; |
| |
| /** |
| * The latest date from which diffs are to be included in the report. |
| */ |
| private String myendDate; |
| |
| /** |
| * The file in which to write the diff report. |
| */ |
| private File mydestfile; |
| |
| /** |
| * Used to create the temp file for cvs log |
| */ |
| private FileUtils myfileUtils = FileUtils.newFileUtils(); |
| |
| /** |
| * The package/module to analyze. |
| * @param p the name of the package to analyse |
| */ |
| public void setPackage(String p) { |
| mypackage = p; |
| } |
| |
| /** |
| * Set the start tag. |
| * |
| * @param s the start tag. |
| */ |
| public void setStartTag(String s) { |
| mystartTag = s; |
| } |
| |
| /** |
| * Set the start date. |
| * |
| * @param s the start date. |
| */ |
| public void setStartDate(String s) { |
| mystartDate = s; |
| } |
| |
| /** |
| * Set the end tag. |
| * |
| * @param s the end tag. |
| */ |
| public void setEndTag(String s) { |
| myendTag = s; |
| } |
| |
| /** |
| * Set the end date. |
| * |
| * @param s the end date. |
| */ |
| public void setEndDate(String s) { |
| myendDate = s; |
| } |
| |
| /** |
| * Set the output file for the diff. |
| * |
| * @param f the output file for the diff. |
| */ |
| public void setDestFile(File f) { |
| mydestfile = f; |
| } |
| |
| /** |
| * Execute task. |
| * |
| * @exception BuildException if an error occurs |
| */ |
| public void execute() throws BuildException { |
| // validate the input parameters |
| validate(); |
| |
| // build the rdiff command |
| addCommandArgument("rdiff"); |
| addCommandArgument("-s"); |
| if (mystartTag != null) { |
| addCommandArgument("-r"); |
| addCommandArgument(mystartTag); |
| } else { |
| addCommandArgument("-D"); |
| addCommandArgument(mystartDate); |
| } |
| if (myendTag != null) { |
| addCommandArgument("-r"); |
| addCommandArgument(myendTag); |
| } else { |
| addCommandArgument("-D"); |
| addCommandArgument(myendDate); |
| } |
| // support multiple packages |
| StringTokenizer myTokenizer = new StringTokenizer(mypackage); |
| while (myTokenizer.hasMoreTokens()) { |
| addCommandArgument(myTokenizer.nextToken()); |
| } |
| // force command not to be null |
| setCommand(""); |
| File tmpFile = null; |
| try { |
| tmpFile = myfileUtils.createTempFile("cvstagdiff", ".log", null); |
| tmpFile.deleteOnExit(); |
| setOutput(tmpFile); |
| |
| // run the cvs command |
| super.execute(); |
| |
| // parse the rdiff |
| CvsTagEntry[] entries = parseRDiff(tmpFile); |
| |
| // write the tag diff |
| writeTagDiff(entries); |
| |
| } finally { |
| if (tmpFile != null) { |
| tmpFile.delete(); |
| } |
| } |
| } |
| |
| /** |
| * Parse the tmpFile and return and array of CvsTagEntry to be |
| * written in the output. |
| * |
| * @param tmpFile the File containing the output of the cvs rdiff command |
| * @return the entries in the output |
| * @exception BuildException if an error occurs |
| */ |
| private CvsTagEntry[] parseRDiff(File tmpFile) throws BuildException { |
| // parse the output of the command |
| BufferedReader reader = null; |
| |
| try { |
| reader = new BufferedReader(new FileReader(tmpFile)); |
| |
| // entries are of the form: |
| //CVS 1.11 |
| // File module/filename is new; current revision 1.1 |
| //CVS 1.11.9 |
| // File module/filename is new; cvstag_2003_11_03_2 revision 1.1 |
| // or |
| // File module/filename changed from revision 1.4 to 1.6 |
| // or |
| // File module/filename is removed; not included in |
| // release tag SKINLF_12 |
| //CVS 1.11.9 |
| // File testantoine/antoine.bat is removed; TESTANTOINE_1 revision 1.1.1.1 |
| // |
| // get rid of 'File module/" |
| String toBeRemoved = FILE_STRING + mypackage + "/"; |
| int headerLength = toBeRemoved.length(); |
| Vector entries = new Vector(); |
| |
| String line = reader.readLine(); |
| int index; |
| CvsTagEntry entry = null; |
| |
| while (null != line) { |
| if (line.length() > headerLength) { |
| if (line.startsWith(toBeRemoved)) { |
| line = line.substring(headerLength); |
| } else { |
| line = line.substring(FILE_STRING.length()); |
| } |
| |
| if ((index = line.indexOf(FILE_IS_NEW)) != -1) { |
| //CVS 1.11 |
| //File apps/websphere/lib/something.jar is new; current revision 1.2 |
| //CVS 1.11.9 |
| //File apps/websphere/lib/something.jar is new; cvstag_2003_11_03_2 revision 1.2 |
| // it is a new file |
| // set the revision but not the prevrevision |
| String filename = line.substring(0, index); |
| String rev = null; |
| int indexrev = -1; |
| if ((indexrev = line.indexOf(REVISION, index)) != -1) { |
| rev = line.substring(indexrev + REVISION.length()); |
| } |
| entry = new CvsTagEntry(filename, rev); |
| entries.addElement(entry); |
| log(entry.toString(), Project.MSG_VERBOSE); |
| } else if ((index = line.indexOf(FILE_HAS_CHANGED)) != -1) { |
| // it is a modified file |
| // set the revision and the prevrevision |
| String filename = line.substring(0, index); |
| int revSeparator = line.indexOf(" to ", index); |
| String prevRevision = |
| line.substring(index + FILE_HAS_CHANGED.length(), |
| revSeparator); |
| String revision = line.substring(revSeparator + TO_STRING.length()); |
| entry = new CvsTagEntry(filename, |
| revision, |
| prevRevision); |
| entries.addElement(entry); |
| log(entry.toString(), Project.MSG_VERBOSE); |
| } else if ((index = line.indexOf(FILE_WAS_REMOVED)) != -1) { |
| // it is a removed file |
| String filename = line.substring(0, index); |
| String rev = null; |
| int indexrev = -1; |
| if ((indexrev = line.indexOf(REVISION, index)) != -1) { |
| rev = line.substring(indexrev + REVISION.length()); |
| } |
| entry = new CvsTagEntry(filename, null, rev); |
| entries.addElement(entry); |
| log(entry.toString(), Project.MSG_VERBOSE); |
| } |
| } |
| line = reader.readLine(); |
| } |
| |
| CvsTagEntry[] array = new CvsTagEntry[entries.size()]; |
| entries.copyInto(array); |
| |
| return array; |
| } catch (IOException e) { |
| throw new BuildException("Error in parsing", e); |
| } finally { |
| if (reader != null) { |
| try { |
| reader.close(); |
| } catch (IOException e) { |
| log(e.toString(), Project.MSG_ERR); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Write the rdiff log. |
| * |
| * @param entries a <code>CvsTagEntry[]</code> value |
| * @exception BuildException if an error occurs |
| */ |
| private void writeTagDiff(CvsTagEntry[] entries) throws BuildException { |
| FileOutputStream output = null; |
| try { |
| output = new FileOutputStream(mydestfile); |
| PrintWriter writer = new PrintWriter( |
| new OutputStreamWriter(output, "UTF-8")); |
| writer.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); |
| writer.print("<tagdiff "); |
| if (mystartTag != null) { |
| writer.print("startTag=\"" + mystartTag + "\" "); |
| } else { |
| writer.print("startDate=\"" + mystartDate + "\" "); |
| } |
| if (myendTag != null) { |
| writer.print("endTag=\"" + myendTag + "\" "); |
| } else { |
| writer.print("endDate=\"" + myendDate + "\" "); |
| } |
| |
| writer.print("cvsroot=\"" + getCvsRoot() + "\" "); |
| writer.print("package=\"" + mypackage + "\" "); |
| |
| writer.println(">"); |
| for (int i = 0, c = entries.length; i < c; i++) { |
| writeTagEntry(writer, entries[i]); |
| } |
| writer.println("</tagdiff>"); |
| writer.flush(); |
| writer.close(); |
| } catch (UnsupportedEncodingException uee) { |
| log(uee.toString(), Project.MSG_ERR); |
| } catch (IOException ioe) { |
| throw new BuildException(ioe.toString(), ioe); |
| } finally { |
| if (null != output) { |
| try { |
| output.close(); |
| } catch (IOException ioe) { |
| log(ioe.toString(), Project.MSG_ERR); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Write a single entry to the given writer. |
| * |
| * @param writer a <code>PrintWriter</code> value |
| * @param entry a <code>CvsTagEntry</code> value |
| */ |
| private void writeTagEntry(PrintWriter writer, CvsTagEntry entry) { |
| writer.println("\t<entry>"); |
| writer.println("\t\t<file>"); |
| writer.println("\t\t\t<name>" + entry.getFile() + "</name>"); |
| if (entry.getRevision() != null) { |
| writer.println("\t\t\t<revision>" + entry.getRevision() |
| + "</revision>"); |
| } |
| if (entry.getPreviousRevision() != null) { |
| writer.println("\t\t\t<prevrevision>" |
| + entry.getPreviousRevision() + "</prevrevision>"); |
| } |
| writer.println("\t\t</file>"); |
| writer.println("\t</entry>"); |
| } |
| |
| /** |
| * Validate the parameters specified for task. |
| * |
| * @exception BuildException if a parameter is not correctly set |
| */ |
| private void validate() throws BuildException { |
| if (null == mypackage) { |
| throw new BuildException("Package/module must be set."); |
| } |
| |
| if (null == mydestfile) { |
| throw new BuildException("Destfile must be set."); |
| } |
| |
| if (null == mystartTag && null == mystartDate) { |
| throw new BuildException("Start tag or start date must be set."); |
| } |
| |
| if (null != mystartTag && null != mystartDate) { |
| throw new BuildException("Only one of start tag and start date " |
| + "must be set."); |
| } |
| |
| if (null == myendTag && null == myendDate) { |
| throw new BuildException("End tag or end date must be set."); |
| } |
| |
| if (null != myendTag && null != myendDate) { |
| throw new BuildException("Only one of end tag and end date must " |
| + "be set."); |
| } |
| } |
| } |