git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@277653 13f79535-47bb-0310-9956-ffa450edef68master
@@ -18,9 +18,3 @@ SVN, matching what the traditional Ant task(s) vor CVS could do. | |||||
If you need more than what this libary provides, we encourage you to | If you need more than what this libary provides, we encourage you to | ||||
check out the existing alternatives. | check out the existing alternatives. | ||||
=============== | |||||
The first cut will mimic the implementation of the <cvs> task, it will | |||||
even be split into an abstract task and a very thin real task - it may | |||||
be possible to base tasks similar to the optional CVS tasks on the | |||||
abstract task as well. |
@@ -0,0 +1,61 @@ | |||||
<?xml version="1.0"?> | |||||
<!-- | |||||
Copyright 2005 The Apache Software Foundation | |||||
Licensed under the Apache License, Version 2.0 (the "License"); | |||||
you may not use this file except in compliance with the License. | |||||
You may obtain a copy of the License at | |||||
http://www.apache.org/licenses/LICENSE-2.0 | |||||
Unless required by applicable law or agreed to in writing, software | |||||
distributed under the License is distributed on an "AS IS" BASIS, | |||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
See the License for the specific language governing permissions and | |||||
limitations under the License. | |||||
--> | |||||
<project name="changelog-test" basedir="../../../" | |||||
default="log" xmlns:svn="antlib:org.apache.tools.ant.taskdefs.svn"> | |||||
<property name="tmpdir" value="tmpdir"/> | |||||
<property name="tpfdir" value="${tmpdir}/tpf"/> | |||||
<property name="file" value="ebcdic.h"/> | |||||
<target name="setup"> | |||||
<mkdir dir="${tmpdir}"/> | |||||
<svn:svn | |||||
svnURL="http://svn.apache.org/repos/asf/httpd/httpd/trunk/os/tpf/" | |||||
dest="${tmpdir}"/> | |||||
</target> | |||||
<target name="log" depends="setup"> | |||||
<svn:changelog failonerror="true" dest="${tpfdir}" | |||||
destfile="${tmpdir}/log.xml"/> | |||||
</target> | |||||
<target name="start" depends="setup"> | |||||
<svn:changelog failonerror="true" dest="${tpfdir}" | |||||
destfile="${tmpdir}/log.xml" start="151000"/> | |||||
</target> | |||||
<target name="startDate" depends="setup"> | |||||
<svn:changelog failonerror="true" dest="${tpfdir}" | |||||
destfile="${tmpdir}/log.xml" start="{2004-12-24}"/> | |||||
</target> | |||||
<target name="end" depends="setup"> | |||||
<svn:changelog failonerror="true" dest="${tpfdir}" | |||||
destfile="${tmpdir}/log.xml" end="151000"/> | |||||
</target> | |||||
<target name="endDate" depends="setup"> | |||||
<svn:changelog failonerror="true" dest="${tpfdir}" | |||||
destfile="${tmpdir}/log.xml" end="{2004-12-24}"/> | |||||
</target> | |||||
<target name="cleanup"> | |||||
<delete dir="${tmpdir}" /> | |||||
</target> | |||||
</project> |
@@ -69,6 +69,11 @@ public abstract class AbstractSvnTask extends Task { | |||||
*/ | */ | ||||
private boolean quiet = false; | private boolean quiet = false; | ||||
/** | |||||
* be verbose | |||||
*/ | |||||
private boolean verbose = false; | |||||
/** | /** | ||||
* report only, don't change any files. | * report only, don't change any files. | ||||
*/ | */ | ||||
@@ -466,6 +471,14 @@ public abstract class AbstractSvnTask extends Task { | |||||
quiet = q; | quiet = q; | ||||
} | } | ||||
/** | |||||
* If true, be verbose. | |||||
* @param q if true, be verbose. | |||||
*/ | |||||
public void setVerbose(boolean v) { | |||||
verbose = v; | |||||
} | |||||
/** | /** | ||||
* If true, report only and don't change any files. | * If true, report only and don't change any files. | ||||
* | * | ||||
@@ -523,7 +536,7 @@ public abstract class AbstractSvnTask extends Task { | |||||
* <li> | * <li> | ||||
* quiet | * quiet | ||||
* </li> | * </li> | ||||
* <li>svnroot</li> | |||||
* <li>verbose</li> | |||||
* <li>dryrun</li> | * <li>dryrun</li> | ||||
* </ul> | * </ul> | ||||
*/ | */ | ||||
@@ -535,6 +548,9 @@ public abstract class AbstractSvnTask extends Task { | |||||
if (quiet) { | if (quiet) { | ||||
c.createArgument(true).setValue("--quiet"); | c.createArgument(true).setValue("--quiet"); | ||||
} | } | ||||
if (verbose) { | |||||
c.createArgument(true).setValue("--verbose"); | |||||
} | |||||
if (dryrun) { | if (dryrun) { | ||||
c.createArgument(true).setValue("--dry-run"); | c.createArgument(true).setValue("--dry-run"); | ||||
} | } | ||||
@@ -0,0 +1,134 @@ | |||||
/* | |||||
* Copyright 2005 The Apache Software Foundation | |||||
* | |||||
* Licensed under the Apache License, Version 2.0 (the "License"); | |||||
* you may not use this file except in compliance with the License. | |||||
* You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, software | |||||
* distributed under the License is distributed on an "AS IS" BASIS, | |||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
* See the License for the specific language governing permissions and | |||||
* limitations under the License. | |||||
* | |||||
*/ | |||||
package org.apache.tools.ant.taskdefs.svn; | |||||
import java.io.ByteArrayOutputStream; | |||||
import java.io.IOException; | |||||
import java.io.OutputStream; | |||||
/** | |||||
* Invokes {@link #processLine processLine} whenever a full line has | |||||
* been written to this stream. | |||||
* | |||||
* <p>Tries to be smart about line separators.</p> | |||||
*/ | |||||
public abstract class LineOrientedOutputStream extends OutputStream { | |||||
/** Initial buffer size. */ | |||||
private static final int INTIAL_SIZE = 132; | |||||
/** Carriage return */ | |||||
private static final int CR = 0x0d; | |||||
/** Linefeed */ | |||||
private static final int LF = 0x0a; | |||||
private ByteArrayOutputStream buffer | |||||
= new ByteArrayOutputStream(INTIAL_SIZE); | |||||
private boolean skip = false; | |||||
/** | |||||
* Write the data to the buffer and flush the buffer, if a line | |||||
* separator is detected. | |||||
* | |||||
* @param cc data to log (byte). | |||||
*/ | |||||
public final void write(int cc) throws IOException { | |||||
final byte c = (byte) cc; | |||||
if ((c == '\n') || (c == '\r')) { | |||||
if (!skip) { | |||||
processBuffer(); | |||||
} | |||||
} else { | |||||
buffer.write(cc); | |||||
} | |||||
skip = (c == '\r'); | |||||
} | |||||
/** | |||||
* Flush this log stream | |||||
*/ | |||||
public final void flush() throws IOException { | |||||
if (buffer.size() > 0) { | |||||
processBuffer(); | |||||
} | |||||
} | |||||
/** | |||||
* Converts the buffer to a string and sends it to | |||||
* <code>processLine</code> | |||||
*/ | |||||
private void processBuffer() throws IOException { | |||||
try { | |||||
processLine(buffer.toString()); | |||||
} finally { | |||||
buffer.reset(); | |||||
} | |||||
} | |||||
/** | |||||
* Processes a line. | |||||
* | |||||
* @param line the line to log. | |||||
*/ | |||||
protected abstract void processLine(String line) throws IOException; | |||||
/** | |||||
* Writes all remaining | |||||
*/ | |||||
public final void close() throws IOException { | |||||
if (buffer.size() > 0) { | |||||
processBuffer(); | |||||
} | |||||
super.close(); | |||||
} | |||||
/** | |||||
* Write a block of characters to the output stream | |||||
* | |||||
* @param b the array containing the data | |||||
* @param off the offset into the array where data starts | |||||
* @param len the length of block | |||||
* | |||||
* @throws IOException if the data cannot be written into the stream. | |||||
*/ | |||||
public final void write(byte[] b, int off, int len) throws IOException { | |||||
// find the line breaks and pass other chars through in blocks | |||||
int offset = off; | |||||
int blockStartOffset = offset; | |||||
int remaining = len; | |||||
while (remaining > 0) { | |||||
while (remaining > 0 && b[offset] != LF && b[offset] != CR) { | |||||
offset++; | |||||
remaining--; | |||||
} | |||||
// either end of buffer or a line separator char | |||||
int blockLength = offset - blockStartOffset; | |||||
if (blockLength > 0) { | |||||
buffer.write(b, blockStartOffset, blockLength); | |||||
} | |||||
while (remaining > 0 && (b[offset] == LF || b[offset] == CR)) { | |||||
write(b[offset]); | |||||
offset++; | |||||
remaining--; | |||||
} | |||||
blockStartOffset = offset; | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,188 @@ | |||||
/* | |||||
* Copyright 2005 The Apache Software Foundation | |||||
* | |||||
* Licensed under the Apache License, Version 2.0 (the "License"); | |||||
* you may not use this file except in compliance with the License. | |||||
* You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, software | |||||
* distributed under the License is distributed on an "AS IS" BASIS, | |||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
* See the License for the specific language governing permissions and | |||||
* limitations under the License. | |||||
* | |||||
*/ | |||||
package org.apache.tools.ant.taskdefs.svn; | |||||
import java.text.ParseException; | |||||
import java.text.SimpleDateFormat; | |||||
import java.util.Date; | |||||
import java.util.ArrayList; | |||||
/** | |||||
* A class used to parse the output of the svn log command. | |||||
* | |||||
* @version $Revision$ $Date$ | |||||
*/ | |||||
class SvnChangeLogParser extends LineOrientedOutputStream { | |||||
private final static int GET_ENTRY_LINE = 0; | |||||
private final static int GET_REVISION_LINE = 1; | |||||
private final static int GET_PATHS = 2; | |||||
private final static int GET_MESSAGE = 3; | |||||
private String message = ""; | |||||
private Date date = null; | |||||
private String author = null; | |||||
private String revision = null; | |||||
private ArrayList paths = new ArrayList(); | |||||
/** input format for dates read in from cvs log */ | |||||
private static final String PATTERN = "yyyy-MM-dd HH:mm:ss"; | |||||
private static final SimpleDateFormat INPUT_DATE | |||||
= new SimpleDateFormat(PATTERN); | |||||
private final ArrayList entries = new ArrayList(); | |||||
private int status = GET_ENTRY_LINE; | |||||
/** | |||||
* Get a list of rcs entries as an array. | |||||
* | |||||
* @return a list of rcs entries as an array | |||||
*/ | |||||
public SvnEntry[] getEntrySetAsArray() { | |||||
return (SvnEntry[]) entries.toArray(new SvnEntry[entries.size()]); | |||||
} | |||||
/** | |||||
* Receive notification about the process writing | |||||
* to standard output. | |||||
* @param line the line to process | |||||
*/ | |||||
public void processLine(final String line) { | |||||
switch(status) { | |||||
case GET_ENTRY_LINE: | |||||
// make sure attributes are reset when | |||||
// working on a 'new' file. | |||||
reset(); | |||||
processEntryStart(line); | |||||
break; | |||||
case GET_REVISION_LINE: | |||||
processRevision(line); | |||||
break; | |||||
case GET_MESSAGE: | |||||
processMessage(line); | |||||
break; | |||||
case GET_PATHS: | |||||
processPath(line); | |||||
break; | |||||
default: | |||||
// Do nothing | |||||
break; | |||||
} | |||||
} | |||||
/** | |||||
* Process a line while in "GET_MESSAGE" state. | |||||
* | |||||
* @param line the line | |||||
*/ | |||||
private void processMessage(final String line) { | |||||
final String lineSeparator = System.getProperty("line.separator"); | |||||
if (line.equals("------------------------------------------------------------------------")) { | |||||
//We have ended changelog for that particular revision | |||||
//so we can save it | |||||
final int end | |||||
= message.length() - lineSeparator.length(); //was -1 | |||||
message = message.substring(0, end); | |||||
saveEntry(); | |||||
status = GET_REVISION_LINE; | |||||
} else { | |||||
message += line + lineSeparator; | |||||
} | |||||
} | |||||
/** | |||||
* Process a line while in "GET_ENTRY_LINE" state. | |||||
* | |||||
* @param line the line to process | |||||
*/ | |||||
private void processEntryStart(final String line) { | |||||
if (line.equals("------------------------------------------------------------------------")) { | |||||
status = GET_REVISION_LINE; | |||||
} | |||||
} | |||||
/** | |||||
* Process a line while in "REVISION" state. | |||||
* | |||||
* @param line the line to process | |||||
*/ | |||||
private void processRevision(final String line) { | |||||
int index = line.indexOf(" |"); | |||||
if (line.startsWith("r") | |||||
&& (line.endsWith("lines") || line.endsWith("line")) | |||||
&& index > -1) { | |||||
revision = line.substring(1, index); | |||||
int end = line.indexOf(" |", index + 1); | |||||
author = line.substring(index + 3, end); | |||||
String d = line.substring(end + 3, end + 3 + PATTERN.length()); | |||||
date = parseDate(d); | |||||
status = GET_PATHS; | |||||
} | |||||
} | |||||
/** | |||||
* Process a line while in "GET_PATHS" state. | |||||
* | |||||
* @param line the line to process | |||||
*/ | |||||
private void processPath(final String line) { | |||||
if (line.startsWith("Changed paths:")) { | |||||
// ignore | |||||
} else if (line.equals("")) { | |||||
status = GET_MESSAGE; | |||||
} else { | |||||
paths.add(line.substring(5)); | |||||
} | |||||
} | |||||
/** | |||||
* Utility method that saves the current entry. | |||||
*/ | |||||
private void saveEntry() { | |||||
SvnEntry entry = new SvnEntry(date, revision, author, message, | |||||
paths); | |||||
entries.add(entry); | |||||
} | |||||
/** | |||||
* Parse date out from expected format. | |||||
* | |||||
* @param date the string holding date | |||||
* @return the date object or null if unknown date format | |||||
*/ | |||||
private Date parseDate(final String date) { | |||||
try { | |||||
return INPUT_DATE.parse(date); | |||||
} catch (ParseException e) { | |||||
return null; | |||||
} | |||||
} | |||||
/** | |||||
* Reset all internal attributes except status. | |||||
*/ | |||||
public void reset() { | |||||
this.date = null; | |||||
this.author = null; | |||||
this.message = ""; | |||||
this.revision = null; | |||||
this.paths.clear(); | |||||
} | |||||
} |
@@ -0,0 +1,398 @@ | |||||
/* | |||||
* Copyright 2005 The Apache Software Foundation | |||||
* | |||||
* Licensed under the Apache License, Version 2.0 (the "License"); | |||||
* you may not use this file except in compliance with the License. | |||||
* You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, software | |||||
* distributed under the License is distributed on an "AS IS" BASIS, | |||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
* See the License for the specific language governing permissions and | |||||
* limitations under the License. | |||||
* | |||||
*/ | |||||
package org.apache.tools.ant.taskdefs.svn; | |||||
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.io.UnsupportedEncodingException; | |||||
import java.text.ParseException; | |||||
import java.text.SimpleDateFormat; | |||||
import java.util.Date; | |||||
import java.util.Enumeration; | |||||
import java.util.Properties; | |||||
import java.util.Vector; | |||||
import org.apache.tools.ant.BuildException; | |||||
import org.apache.tools.ant.DirectoryScanner; | |||||
import org.apache.tools.ant.Project; | |||||
import org.apache.tools.ant.taskdefs.LogOutputStream; | |||||
import org.apache.tools.ant.taskdefs.PumpStreamHandler; | |||||
import org.apache.tools.ant.taskdefs.cvslib.CvsUser; | |||||
import org.apache.tools.ant.types.FileSet; | |||||
import org.apache.tools.ant.util.FileUtils; | |||||
/** | |||||
* Examines the output of svn log and group related changes together. | |||||
* | |||||
* It produces an XML output representing the list of changes. | |||||
* <pre> | |||||
* <font color=#0000ff><!-- Root element --></font> | |||||
* <font color=#6a5acd><!ELEMENT</font> changelog <font color=#ff00ff>(entry</font><font color=#ff00ff>+</font><font color=#ff00ff>)</font><font color=#6a5acd>></font> | |||||
* <font color=#0000ff><!-- SVN Entry --></font> | |||||
* <font color=#6a5acd><!ELEMENT</font> entry <font color=#ff00ff>(date,time,revision,author,file</font><font color=#ff00ff>+,msg</font><font color=#ff00ff>,msg)</font><font color=#6a5acd>></font> | |||||
* <font color=#0000ff><!-- Date of svn entry --></font> | |||||
* <font color=#6a5acd><!ELEMENT</font> date <font color=#ff00ff>(#PCDATA)</font><font color=#6a5acd>></font> | |||||
* <font color=#0000ff><!-- Time of svn entry --></font> | |||||
* <font color=#6a5acd><!ELEMENT</font> time <font color=#ff00ff>(#PCDATA)</font><font color=#6a5acd>></font> | |||||
* <font color=#0000ff><!-- Author of change --></font> | |||||
* <font color=#6a5acd><!ELEMENT</font> author <font color=#ff00ff>(#PCDATA)</font><font color=#6a5acd>></font> | |||||
* <font color=#0000ff><!-- commit message --></font> | |||||
* <font color=#6a5acd><!ELEMENT</font> msg <font color=#ff00ff>(#PCDATA)</font><font color=#6a5acd>></font> | |||||
* <font color=#0000ff><!-- List of files affected --></font> | |||||
* <font color=#6a5acd><!ELEMENT</font> file <font color=#ff00ff>(name</font><font color=#ff00ff>?</font><font color=#ff00ff>)</font><font color=#6a5acd>></font> | |||||
* <font color=#0000ff><!-- Name of the file --></font> | |||||
* <font color=#6a5acd><!ELEMENT</font> name <font color=#ff00ff>(#PCDATA)</font><font color=#6a5acd>></font> | |||||
* <font color=#0000ff><!-- Revision number --></font> | |||||
* <font color=#6a5acd><!ELEMENT</font> revision <font color=#ff00ff>(#PCDATA)</font><font color=#6a5acd>></font> | |||||
* </pre> | |||||
* | |||||
* @ant.task name="svnchangelog" category="scm" | |||||
*/ | |||||
public class SvnChangeLogTask extends AbstractSvnTask { | |||||
/** User list */ | |||||
private File usersFile; | |||||
/** User list */ | |||||
private Vector svnUsers = new Vector(); | |||||
/** Input dir */ | |||||
private File inputDir; | |||||
/** Output file */ | |||||
private File destFile; | |||||
/** The earliest revision at which to start processing entries. */ | |||||
private String startRevision; | |||||
/** The latest revision at which to stop processing entries. */ | |||||
private String endRevision; | |||||
/** | |||||
* Filesets containing list of files against which the svn log will be | |||||
* performed. If empty then all files in the working directory will | |||||
* be checked. | |||||
*/ | |||||
private final Vector filesets = new Vector(); | |||||
/** | |||||
* Set the base dir for svn. | |||||
* | |||||
* @param inputDir The new dir value | |||||
*/ | |||||
public void setDir(final File inputDir) { | |||||
this.inputDir = inputDir; | |||||
} | |||||
/** | |||||
* Set the output file for the log. | |||||
* | |||||
* @param destFile The new destfile value | |||||
*/ | |||||
public void setDestfile(final File destFile) { | |||||
this.destFile = destFile; | |||||
} | |||||
/** | |||||
* Set a lookup list of user names & addresses | |||||
* | |||||
* @param usersFile The file containing the users info. | |||||
*/ | |||||
public void setUsersfile(final File usersFile) { | |||||
this.usersFile = usersFile; | |||||
} | |||||
/** | |||||
* Add a user to list changelog knows about. | |||||
* | |||||
* @param user the user | |||||
*/ | |||||
public void addUser(final CvsUser user) { | |||||
svnUsers.addElement(user); | |||||
} | |||||
/** | |||||
* Set the revision at which the changelog should start. | |||||
* | |||||
* @param start The revision at which the changelog should start. | |||||
*/ | |||||
public void setStart(final String start) { | |||||
this.startRevision = start; | |||||
} | |||||
/** | |||||
* Set the revision at which the changelog should stop. | |||||
* | |||||
* @param endRevision The revision at which the changelog should stop. | |||||
*/ | |||||
public void setEnd(final String endRevision) { | |||||
this.endRevision = endRevision; | |||||
} | |||||
/** | |||||
* Set the number of days worth of log entries to process. | |||||
* | |||||
* @param days the number of days of log to process. | |||||
*/ | |||||
public void setDaysinpast(final int days) { | |||||
final long time = System.currentTimeMillis() | |||||
- (long) days * 24 * 60 * 60 * 1000; | |||||
final SimpleDateFormat outputDate = | |||||
new SimpleDateFormat("{yyyy-MM-dd}"); | |||||
setStart(outputDate.format(new Date(time))); | |||||
} | |||||
/** | |||||
* Adds a set of files about which svn logs will be generated. | |||||
* | |||||
* @param fileSet a set of files about which svn logs will be generated. | |||||
*/ | |||||
public void addFileset(final FileSet fileSet) { | |||||
filesets.addElement(fileSet); | |||||
} | |||||
/** | |||||
* Execute task | |||||
* | |||||
* @exception BuildException if something goes wrong executing the | |||||
* svn command | |||||
*/ | |||||
public void execute() throws BuildException { | |||||
File savedDir = inputDir; // may be altered in validate | |||||
try { | |||||
validate(); | |||||
final Properties userList = new Properties(); | |||||
loadUserlist(userList); | |||||
for (int i = 0, size = svnUsers.size(); i < size; i++) { | |||||
final CvsUser user = (CvsUser) svnUsers.get(i); | |||||
user.validate(); | |||||
userList.put(user.getUserID(), user.getDisplayname()); | |||||
} | |||||
setSubCommand("log"); | |||||
setVerbose(true); | |||||
if (null != startRevision) { | |||||
if (null != endRevision) { | |||||
setRevision(startRevision + ":" + endRevision); | |||||
} else { | |||||
setRevision(startRevision + ":HEAD"); | |||||
} | |||||
} | |||||
// Check if list of files to check has been specified | |||||
if (!filesets.isEmpty()) { | |||||
final Enumeration e = filesets.elements(); | |||||
while (e.hasMoreElements()) { | |||||
final FileSet fileSet = (FileSet) e.nextElement(); | |||||
final DirectoryScanner scanner = | |||||
fileSet.getDirectoryScanner(getProject()); | |||||
final String[] files = scanner.getIncludedFiles(); | |||||
for (int i = 0; i < files.length; i++) { | |||||
addSubCommandArgument(files[i]); | |||||
} | |||||
} | |||||
} | |||||
final SvnChangeLogParser parser = new SvnChangeLogParser(); | |||||
final PumpStreamHandler handler = | |||||
new PumpStreamHandler(parser, | |||||
new LogOutputStream(this, | |||||
Project.MSG_ERR)); | |||||
log(getSubCommand(), Project.MSG_VERBOSE); | |||||
setDest(inputDir); | |||||
setExecuteStreamHandler(handler); | |||||
super.execute(); | |||||
final SvnEntry[] entrySet = parser.getEntrySetAsArray(); | |||||
final SvnEntry[] filteredEntrySet = filterEntrySet(entrySet); | |||||
replaceAuthorIdWithName(userList, filteredEntrySet); | |||||
writeChangeLog(filteredEntrySet); | |||||
} finally { | |||||
inputDir = savedDir; | |||||
} | |||||
} | |||||
/** | |||||
* Validate the parameters specified for task. | |||||
* | |||||
* @throws BuildException if fails validation checks | |||||
*/ | |||||
private void validate() | |||||
throws BuildException { | |||||
if (null == inputDir) { | |||||
inputDir = getDest(); | |||||
} | |||||
if (null == destFile) { | |||||
final String message = "Destfile must be set."; | |||||
throw new BuildException(message); | |||||
} | |||||
if (!inputDir.exists()) { | |||||
final String message = "Cannot find base dir " | |||||
+ inputDir.getAbsolutePath(); | |||||
throw new BuildException(message); | |||||
} | |||||
if (null != usersFile && !usersFile.exists()) { | |||||
final String message = "Cannot find user lookup list " | |||||
+ usersFile.getAbsolutePath(); | |||||
throw new BuildException(message); | |||||
} | |||||
} | |||||
/** | |||||
* Load the userlist from the userList file (if specified) and add to | |||||
* list of users. | |||||
* | |||||
* @param userList the file of users | |||||
* @throws BuildException if file can not be loaded for some reason | |||||
*/ | |||||
private void loadUserlist(final Properties userList) | |||||
throws BuildException { | |||||
if (null != usersFile) { | |||||
try { | |||||
userList.load(new FileInputStream(usersFile)); | |||||
} catch (final IOException ioe) { | |||||
throw new BuildException(ioe.toString(), ioe); | |||||
} | |||||
} | |||||
} | |||||
/** | |||||
* Filter the specified entries according to an appropriate rule. | |||||
* | |||||
* @param entrySet the entry set to filter | |||||
* @return the filtered entry set | |||||
*/ | |||||
private SvnEntry[] filterEntrySet(final SvnEntry[] entrySet) { | |||||
final Vector results = new Vector(); | |||||
for (int i = 0; i < entrySet.length; i++) { | |||||
final SvnEntry svnEntry = entrySet[i]; | |||||
if (null != endRevision && !isBeforeEndRevision(svnEntry)) { | |||||
//Skip revisions that are too late | |||||
continue; | |||||
} | |||||
results.addElement(svnEntry); | |||||
} | |||||
final SvnEntry[] resultArray = new SvnEntry[results.size()]; | |||||
results.copyInto(resultArray); | |||||
return resultArray; | |||||
} | |||||
/** | |||||
* replace all known author's id's with their maven specified names | |||||
*/ | |||||
private void replaceAuthorIdWithName(final Properties userList, | |||||
final SvnEntry[] entrySet) { | |||||
for (int i = 0; i < entrySet.length; i++) { | |||||
final SvnEntry entry = entrySet[ i ]; | |||||
if (userList.containsKey(entry.getAuthor())) { | |||||
entry.setAuthor(userList.getProperty(entry.getAuthor())); | |||||
} | |||||
} | |||||
} | |||||
/** | |||||
* Print changelog to file specified in task. | |||||
* | |||||
* @param entrySet the entry set to write. | |||||
* @throws BuildException if there is an error writing changelog. | |||||
*/ | |||||
private void writeChangeLog(final SvnEntry[] entrySet) | |||||
throws BuildException { | |||||
FileOutputStream output = null; | |||||
try { | |||||
output = new FileOutputStream(destFile); | |||||
final PrintWriter writer = | |||||
new PrintWriter(new OutputStreamWriter(output, "UTF-8")); | |||||
final SvnChangeLogWriter serializer = new SvnChangeLogWriter(); | |||||
serializer.printChangeLog(writer, entrySet); | |||||
} catch (final UnsupportedEncodingException uee) { | |||||
getProject().log(uee.toString(), Project.MSG_ERR); | |||||
} catch (final IOException ioe) { | |||||
throw new BuildException(ioe.toString(), ioe); | |||||
} finally { | |||||
FileUtils.close(output); | |||||
} | |||||
} | |||||
private static final String PATTERN = "yyyy-MM-dd"; | |||||
private static final SimpleDateFormat INPUT_DATE | |||||
= new SimpleDateFormat(PATTERN); | |||||
/** | |||||
* Checks whether a given entry is before the given end revision, | |||||
* using revision numbers or date information as appropriate. | |||||
*/ | |||||
private boolean isBeforeEndRevision(SvnEntry entry) { | |||||
if (endRevision.startsWith("{") | |||||
&& endRevision.length() >= 2 + PATTERN.length() ) { | |||||
try { | |||||
Date endDate = | |||||
INPUT_DATE.parse(endRevision.substring(1, | |||||
PATTERN.length() | |||||
+ 1)); | |||||
return entry.getDate().before(endDate); | |||||
} catch (ParseException e) { | |||||
} | |||||
} else { | |||||
try { | |||||
int endRev = Integer.parseInt(endRevision); | |||||
int entryRev = Integer.parseInt(entry.getRevision()); | |||||
return endRev >= entryRev; | |||||
} catch (NumberFormatException e) { | |||||
} // end of try-catch | |||||
} | |||||
// failed to parse revision, use a save fallback | |||||
return true; | |||||
} | |||||
} | |||||
@@ -0,0 +1,81 @@ | |||||
/* | |||||
* Copyright 2005 The Apache Software Foundation | |||||
* | |||||
* Licensed under the Apache License, Version 2.0 (the "License"); | |||||
* you may not use this file except in compliance with the License. | |||||
* You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, software | |||||
* distributed under the License is distributed on an "AS IS" BASIS, | |||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
* See the License for the specific language governing permissions and | |||||
* limitations under the License. | |||||
* | |||||
*/ | |||||
package org.apache.tools.ant.taskdefs.svn; | |||||
import java.io.PrintWriter; | |||||
import java.text.SimpleDateFormat; | |||||
/** | |||||
* Class used to generate an XML changelog. | |||||
*/ | |||||
public class SvnChangeLogWriter { | |||||
/** output format for dates written to xml file */ | |||||
private static final SimpleDateFormat OUTPUT_DATE | |||||
= new SimpleDateFormat("yyyy-MM-dd"); | |||||
/** output format for times written to xml file */ | |||||
private static final SimpleDateFormat OUTPUT_TIME | |||||
= new SimpleDateFormat("HH:mm"); | |||||
/** | |||||
* Print out the specified entries. | |||||
* | |||||
* @param output writer to which to send output. | |||||
* @param entries the entries to be written. | |||||
*/ | |||||
public void printChangeLog(final PrintWriter output, | |||||
final SvnEntry[] entries) { | |||||
output.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); | |||||
output.println("<changelog>"); | |||||
for (int i = 0; i < entries.length; i++) { | |||||
final SvnEntry entry = entries[i]; | |||||
printEntry(output, entry); | |||||
} | |||||
output.println("</changelog>"); | |||||
output.flush(); | |||||
output.close(); | |||||
} | |||||
/** | |||||
* Print out an individual entry in changelog. | |||||
* | |||||
* @param entry the entry to print | |||||
* @param output writer to which to send output. | |||||
*/ | |||||
private void printEntry(final PrintWriter output, final SvnEntry entry) { | |||||
output.println("\t<entry>"); | |||||
output.println("\t\t<date>" + OUTPUT_DATE.format(entry.getDate()) | |||||
+ "</date>"); | |||||
output.println("\t\t<time>" + OUTPUT_TIME.format(entry.getDate()) | |||||
+ "</time>"); | |||||
output.println("\t\t<author><![CDATA[" + entry.getAuthor() | |||||
+ "]]></author>"); | |||||
output.println("\t\t<revision><![CDATA[" + entry.getRevision() | |||||
+ "]]></revision>"); | |||||
String[] paths = entry.getPaths(); | |||||
for (int i = 0; i < paths.length; i++) { | |||||
output.println("\t\t<file>"); | |||||
output.println("\t\t\t<name><![CDATA[" + paths[i] + "]]></name>"); | |||||
output.println("\t\t</file>"); | |||||
} | |||||
output.println("\t\t<msg><![CDATA[" + entry.getMessage() + "]]></msg>"); | |||||
output.println("\t</entry>"); | |||||
} | |||||
} | |||||
@@ -0,0 +1,115 @@ | |||||
/* | |||||
* Copyright 2005 The Apache Software Foundation | |||||
* | |||||
* Licensed under the Apache License, Version 2.0 (the "License"); | |||||
* you may not use this file except in compliance with the License. | |||||
* You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, software | |||||
* distributed under the License is distributed on an "AS IS" BASIS, | |||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
* See the License for the specific language governing permissions and | |||||
* limitations under the License. | |||||
* | |||||
*/ | |||||
package org.apache.tools.ant.taskdefs.svn; | |||||
import java.util.ArrayList; | |||||
import java.util.Collection; | |||||
import java.util.Collections; | |||||
import java.util.Date; | |||||
public class SvnEntry { | |||||
private final Date date; | |||||
private final String revision; | |||||
private String author; | |||||
private final String message; | |||||
private final ArrayList paths = new ArrayList(); | |||||
/** | |||||
* Creates a new instance of a SvnEntry | |||||
* @param date the date | |||||
* @param author the author | |||||
* @param message a message to be added to the revision | |||||
*/ | |||||
public SvnEntry(final Date date, final String revision, | |||||
final String author, final String message) { | |||||
this(date, revision, author, message, Collections.EMPTY_LIST); | |||||
} | |||||
/** | |||||
* Creates a new instance of a SvnEntry | |||||
* @param date the date | |||||
* @param author the author | |||||
* @param message a message to be added to the revision | |||||
*/ | |||||
public SvnEntry(final Date date, final String revision, | |||||
final String author, final String message, | |||||
final Collection paths) { | |||||
this.date = date; | |||||
this.revision = revision; | |||||
this.author = author; | |||||
this.message = message; | |||||
this.paths.addAll(paths); | |||||
} | |||||
/** | |||||
* Adds a path to the SvnEntry | |||||
* @param path the path to add | |||||
* @param revision the revision | |||||
*/ | |||||
public void addPath(final String name) { | |||||
paths.add(name); | |||||
} | |||||
/** | |||||
* Gets the date of the SvnEntry | |||||
* @return the date | |||||
*/ | |||||
public Date getDate() { | |||||
return date; | |||||
} | |||||
/** | |||||
* Gets the revision of the SvnEntry | |||||
* @return the date | |||||
*/ | |||||
public String getRevision() { | |||||
return revision; | |||||
} | |||||
/** | |||||
* Sets the author of the SvnEntry | |||||
* @param author the author | |||||
*/ | |||||
public void setAuthor(final String author) { | |||||
this.author = author; | |||||
} | |||||
/** | |||||
* Gets the author of the SvnEntry | |||||
* @return the author | |||||
*/ | |||||
public String getAuthor() { | |||||
return author; | |||||
} | |||||
/** | |||||
* Gets the message for the SvnEntry | |||||
* @return the message | |||||
*/ | |||||
public String getMessage() { | |||||
return message; | |||||
} | |||||
/** | |||||
* Gets the paths in this SvnEntry | |||||
* @return the files | |||||
*/ | |||||
public String[] getPaths() { | |||||
return (String[]) paths.toArray(new String[paths.size()]); | |||||
} | |||||
} |
@@ -19,4 +19,8 @@ | |||||
name="svn" | name="svn" | ||||
classname="org.apache.tools.ant.taskdefs.svn.Svn" | classname="org.apache.tools.ant.taskdefs.svn.Svn" | ||||
/> | /> | ||||
<taskdef | |||||
name="changelog" | |||||
classname="org.apache.tools.ant.taskdefs.svn.SvnChangeLogTask" | |||||
/> | |||||
</antlib> | </antlib> |