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> | ||||