diff --git a/changelog/src/main/org/apache/maven/vsslib/VssChangeLogFactory.java b/changelog/src/main/org/apache/maven/vsslib/VssChangeLogFactory.java new file mode 100644 index 00000000..3fbec6b5 --- /dev/null +++ b/changelog/src/main/org/apache/maven/vsslib/VssChangeLogFactory.java @@ -0,0 +1,56 @@ +package org.apache.maven.vsslib; + +/* + * ==================================================================== + * Copyright 2001-2004 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. + * =================================================================== + */ + +import org.apache.maven.changelog.ChangeLogGenerator; +import org.apache.maven.changelog.ChangeLogParser; + +/** + * Provides VSS specific instances of the ChangeLogGenerator and ChangeLogParser + * interfaces. + * + * @author Freddy Mallet + */ +public class VssChangeLogFactory implements + org.apache.maven.changelog.ChangeLogFactory { + + /** + * Default no-arg constructor. + */ + public VssChangeLogFactory() { + } + + /** + * Create a VSS specific ChangeLogGenerator. + * + * @return a VSS specific ChangeLogGenerator. + */ + public ChangeLogGenerator createGenerator() { + return new VssChangeLogGenerator(); + } + + /** + * Create a VSS specific ChangeLogParser. + * + * @return a VSS specific ChangeLogParser. + */ + public ChangeLogParser createParser() { + return new VssChangeLogParser(); + } +} \ No newline at end of file diff --git a/changelog/src/main/org/apache/maven/vsslib/VssChangeLogGenerator.java b/changelog/src/main/org/apache/maven/vsslib/VssChangeLogGenerator.java new file mode 100644 index 00000000..aa54c1d9 --- /dev/null +++ b/changelog/src/main/org/apache/maven/vsslib/VssChangeLogGenerator.java @@ -0,0 +1,192 @@ +package org.apache.maven.vsslib; + +/* + * ==================================================================== + * Copyright 2001-2004 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. + * ==================================================================== + */ + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.Collection; +import java.util.Date; +import java.util.Locale; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.maven.changelog.AbstractChangeLogGenerator; +import org.apache.maven.changelog.ChangeLogEntry; +import org.apache.maven.changelog.ChangeLogGenerator; +import org.apache.maven.changelog.ChangeLogParser; +import org.apache.tools.ant.taskdefs.Execute; +import org.apache.tools.ant.types.Commandline; + +/******************************************************************************* + * A Visual Source Safe implementation of the + * {@link org.apache.maven.changelog.ChangeLogGenerator}interface realized + * extending the {@link org.apache.maven.changelog.AbstractChangeLogGenerator}. + * + * The command line build by this class uses the ss History VSS + * command and formats the output in a way the VssChangeLogParser can + * understand. Due to this fact this implementations works only if used within a + * vss view. + * + * The command looks like this: + *

+ * set ssdir=vssRepositoryPath ss History vssProject -YvssUserId,vssUserPassword + * -R -Vd01/12/04~23/11/04 -I-Y + * + * @author Freddy Mallet + */ +public class VssChangeLogGenerator extends AbstractChangeLogGenerator { + + /** + * Log + */ + private static final Log LOG = LogFactory + .getLog(VssChangeLogGenerator.class); + + /*************************************************************************** + * Constructs the appropriate command line to execute the scm's log command. + * For Clearcase it's lshistory. + * + * @see org.apache.maven.changelog.AbstractChangeLogGenerator#getScmLogCommand() + * @return The command line to be executed. + */ + protected Commandline getScmLogCommand() { + VssConnection vssConnection = new VssConnection(getConnection()); + Commandline command = new Commandline(); + command.setExecutable("ss"); + command.createArgument().setValue("History"); + command.createArgument().setValue("$" + vssConnection.getVssProject()); + //User identification to get access to vss repository + if(vssConnection.getVssUserInf() != null){ + command.createArgument().setValue("-Y" + vssConnection.getVssUserInf()); + } + //Display the history of an entire project list + command.createArgument().setValue("-R"); + //Ignore: Do not ask for input under any circumstances. + command.createArgument().setValue("-I-"); + //Display only versions that fall within specified data range. + if (dateRange != null) { + command.createArgument().setValue("-Vd" + dateRange); + } + return command; + } + + /*************************************************************************** + * Construct the command-line argument that is passed to the scm client to + * specify the appropriate date range. + * + * @param before + * The starting point. + * @param to + * The ending point. + * @return A string that can be used to specify a date to a scm system. + * + * @see org.apache.maven.changelog.AbstractChangeLogGenerator#getScmDateArgument(java.util.Date, + * java.util.Date) + */ + protected String getScmDateArgument(Date before, Date to) { + SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy", + Locale.ENGLISH); + String result = sdf.format(to) + "~" + sdf.format(before); + return result; + } + + protected String getScmTagArgument( String tagStart, String tagEnd ) + { + throw new UnsupportedOperationException( "Cannot presently use tags for SCM operations on VSS" ); + } + + /** + * Convert date range from 'number of days' format to 'date to date' format + * + * @param numDaysString + * String + */ + public void setDateRange(String numDaysString) { + int days = Integer.parseInt(numDaysString); + + Date before = new Date(System.currentTimeMillis() - (long) days * 24 + * 60 * 60 * 1000); + Date to = new Date(System.currentTimeMillis() + (long) 1 * 24 * 60 * 60 + * 1000); + } + + /*************************************************************************** + * Execute vss client driving the given parser. This method has been + * overwritten to be able to set 'SSDIR' environment variable. This variable + * set the vss repository + * + * @param parser + * A {@link ChangeLogParser parser}to process the vss output. + * @return A collection of {@link ChangeLogEntry entries}parsed from the + * vss output. + * @throws IOException + * When there are issues executing vss. + * @see ChangeLogGenerator#getEntries(ChangeLogParser) + */ + public Collection getEntries(ChangeLogParser parser) throws IOException { + if (parser == null) { + throw new NullPointerException("parser cannot be null"); + } + + if (base == null) { + throw new NullPointerException("basedir must be set"); + } + + if (!base.exists()) { + throw new FileNotFoundException("Cannot find base dir " + + base.getAbsolutePath()); + } + + clParser = parser; + try { + VssConnection vssConnection = new VssConnection(getConnection()); + + Execute exe = new Execute(this); + + String[] env = exe.getEnvironment(); + if (env == null) { + env = new String[0]; + } + String[] newEnv = new String[env.length + 1]; + for (int i = 0; i < env.length; i++) { + newEnv[i] = env[i]; + } + newEnv[env.length] = "SSDIR=" + vssConnection.getVssDir(); + + exe.setEnvironment(newEnv); + exe.setCommandline(getScmLogCommand().getCommandline()); + //exe.setVMLauncher(false); + exe.setWorkingDirectory(base); + logExecute(exe, base); + + exe.execute(); + + // log messages from stderr + String errors = errorReader.toString().trim(); + if (errors.length() > 0) { + LOG.error(errors); + } + } catch (IOException ioe) { + handleParserException(ioe); + } + + return entries; + } +} diff --git a/changelog/src/main/org/apache/maven/vsslib/VssChangeLogParser.java b/changelog/src/main/org/apache/maven/vsslib/VssChangeLogParser.java new file mode 100644 index 00000000..8c6b93f1 --- /dev/null +++ b/changelog/src/main/org/apache/maven/vsslib/VssChangeLogParser.java @@ -0,0 +1,398 @@ +package org.apache.maven.vsslib; + +/* + * ==================================================================== + * Copyright 2001-2004 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. + * =================================================================== + */ + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.Map; +import java.util.TreeMap; +import java.util.Vector; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.maven.changelog.ChangeLog; +import org.apache.maven.changelog.ChangeLogEntry; +import org.apache.maven.changelog.ChangeLogFile; +import org.apache.maven.changelog.ChangeLogParser; + +/** + * Visual Source Safe specific implementation of ChangeLogParser interface. + * + * @author Freddy Mallet + */ +public class VssChangeLogParser implements + org.apache.maven.changelog.ChangeLogParser { + + /** * Log */ + private static final Log LOG = LogFactory.getLog(VssChangeLogParser.class); + + /** + * Custom date/time formatter. Rounds ChangeLogEntry times to the nearest + * minute. + */ + private static final SimpleDateFormat ENTRY_KEY_TIMESTAMP_FORMAT = new SimpleDateFormat( + "yyyyMMddHHmm"); + + /** + * rcs entries, in reverse (date, time, author, comment) order + */ + private Map entries = new TreeMap(Collections.reverseOrder()); + + // state machine constants for reading Starteam output + /** * expecting file information */ + private static final int GET_FILE = 1; + + /** * expecting file path information */ + private static final int GET_FILE_PATH = 2; + + /** * expecting date */ + private static final int GET_AUTHOR = 3; + + /** * expecting comments */ + private static final int GET_COMMENT = 4; + + /** * expecting revision */ + private static final int GET_REVISION = 5; + + /** * unknown vss history line status */ + private static final int GET_UNKNOWN = 6; + + /** * Marks start of file data */ + private static final String START_FILE = "****"; + + /** * Marks start of file data */ + private static String START_FILE_PATH = "$/"; + + /** * Marks start of revision */ + private static final String START_REVISION = "Version"; + + /** * Marks author data */ + private static final String START_AUTHOR = "User: "; + + /** * Marks comment data */ + private static final String START_COMMENT = "Comment: "; + + /** * last status of the parser */ + private int lastStatus = GET_FILE; + + /** * the current log entry being processed by the parser */ + private ChangeLogEntry currentLogEntry = null; + + /** * the current file being processed by the parser */ + private ChangeLogFile currentFile = null; + + /** * the bean representation of the vss connection string */ + private VssConnection vssConnection; + + /** + * Create a new ChangeLogParser. + */ + public VssChangeLogParser() { + } + + /** + * initialize the parser from the change log + * + * @param changeLog + * the controlling task + * @see ChangeLogParser#init(ChangeLog) + */ + public void init(ChangeLog changeLog) { + String connection = changeLog.getRepositoryConnection(); + try { + vssConnection = new VssConnection(connection); + } catch (Exception e) { + String message = "Unable to parse vss connection string : " + + connection; + throw new IllegalArgumentException(message); + } + } + + /** + * Parse the input stream into a collection. + * + * @param anInputStream + * an input stream containing clearcase log output + * @return a collection of ChangeLogEntry's + * @throws IOException + * when there are errors reading the provided stream + */ + public Collection parse(InputStream inputStream) throws IOException { + BufferedReader stream = new BufferedReader(new InputStreamReader( + inputStream)); + + String line; + + while ((line = stream.readLine()) != null) { + switch (getLineStatus(line)) { + case GET_FILE: + processGetFile(line); + break; + case GET_REVISION: + processGetRevision(line); + break; + case GET_AUTHOR: + processGetAuthor(line); + break; + case GET_FILE_PATH: + processGetFilePath(line); + break; + case GET_COMMENT: + processGetComment(line); + break; + default: + break; + } + } + return entries.values(); + } + + /** + * Process the current input line in the Get Comment state. + * + * @param line + * a line of text from the VSS log output + */ + private void processGetComment(String line) { + String[] commentLine = line.split(":"); + if (commentLine.length == 2) { + getCurrentLogEntry().setComment(commentLine[1]); + } + //Comment suite on a new line + else { + String comment = getCurrentLogEntry().getComment(); + comment = comment + " " + line; + getCurrentLogEntry().setComment(comment); + } + } + + /** + * Process the current input line in the Get Author state. + * + * @param line + * a line of text from the VSS log output + */ + private void processGetAuthor(String line) { + String[] result = line.split("\\s"); + Vector vector = new Vector(); + for (int i = 0; i < result.length; i++) { + if (!result[i].equals("")) { + vector.add(result[i]); + } + } + ChangeLogEntry entry = getCurrentLogEntry(); + entry.setAuthor((String) vector.get(1)); + entry.setDate(parseDate((String) vector.get(3) + " " + + (String) vector.get(5))); + } + + /** + * Process the current input line in the Get File state. + * + * @param line + * a line of text from the VSS log output + */ + private void processGetFile(String line) { + setCurrentLogEntry(new ChangeLogEntry()); + String[] fileLine = line.split(" "); + setCurrentFile(new ChangeLogFile(fileLine[2])); + } + + /** + * Process the current input line in the Get File Path state. + * + * @param line + * a line of text from the VSS log output + */ + private void processGetFilePath(String line) { + if (getCurrentFile() != null) { + String fileName = getCurrentFile().getName(); + + String path = line.substring(line.indexOf("$"), line.length()); + String longPath = path.substring(vssConnection.getVssProject() + .length() + 1, path.length()) + + "/" + fileName; + getCurrentFile().setName(longPath); + addEntry(getCurrentLogEntry(), getCurrentFile()); + } + } + + /** + * Process the current input line in the Get Revision state. + * + * @param line + * a line of text from the VSS log output + */ + private void processGetRevision(String line) { + String[] revisionLine = line.split(" "); + getCurrentFile().setRevision(revisionLine[1]); + } + + /** + * Identify the status of a vss history line + * + * @param the + * line to process + * @return status + */ + private int getLineStatus(String line) { + int argument = GET_UNKNOWN; + if (line.startsWith(START_FILE)) { + argument = GET_FILE; + } else if (line.startsWith(START_REVISION)) { + argument = GET_REVISION; + } else if (line.startsWith(START_AUTHOR)) { + argument = GET_AUTHOR; + } else if (line.indexOf(START_FILE_PATH) != -1) { + argument = GET_FILE_PATH; + } else if (line.startsWith(START_COMMENT)) { + argument = GET_COMMENT; + } else if (getLastStatus() == GET_COMMENT) { + //Comment suite on a new line + argument = getLastStatus(); + } + setLastStatus(argument); + return argument; + } + + /** + * Add a change log entry to the list (if it's not already there) with the + * given file. + * + * @param entry + * a {@link ChangeLogEntry}to be added to the list if another + * with the same key doesn't exist already. If the entry's author + * is null, the entry wont be added + * @param file + * a {@link ChangeLogFile}to be added to the entry + */ + private void addEntry(ChangeLogEntry entry, ChangeLogFile file) { + // do not add if entry is not populated + if (entry.getAuthor() == null) { + return; + } + + String key = ENTRY_KEY_TIMESTAMP_FORMAT.format(entry.getDate()) + + entry.getAuthor() + entry.getComment(); + + if (!entries.containsKey(key)) { + entry.addFile(file); + entries.put(key, entry); + } else { + ChangeLogEntry existingEntry = (ChangeLogEntry) entries.get(key); + existingEntry.addFile(file); + } + } + + /** + * Converts the date timestamp from the svn output into a date object. + * + * @return A date representing the timestamp of the log entry. + */ + private Date parseDate(String dateString) { + try { + SimpleDateFormat format = new SimpleDateFormat("dd.MM.yy HH:mm"); + Date date = format.parse(dateString); + return date; + } catch (ParseException e) { + LOG.error("ParseException Caught", e); + return null; + } + } + + /** + * Getter for property currentFile. + * + * @return Value of property currentFile. + */ + private ChangeLogFile getCurrentFile() { + return currentFile; + } + + /** + * Setter for property currentFile. + * + * @param currentFile + * New value of property currentFile. + */ + private void setCurrentFile(ChangeLogFile currentFile) { + this.currentFile = currentFile; + } + + /** + * Getter for property currentLogEntry. + * + * @return Value of property currentLogEntry. + */ + private ChangeLogEntry getCurrentLogEntry() { + return currentLogEntry; + } + + /** + * Setter for property currentLogEntry. + * + * @param currentLogEntry + * New value of property currentLogEntry. + */ + private void setCurrentLogEntry(ChangeLogEntry currentLogEntry) { + this.currentLogEntry = currentLogEntry; + } + + /** + * Getter for property status. + * + * @return Value of property status. + */ + private int getLastStatus() { + return lastStatus; + } + + /** + * Setter for property status. + * + * @param status + * New value of property status. + */ + private void setLastStatus(int status) { + this.lastStatus = status; + } + + /** + * Clean up any parser resources + * + * @see ChangeLogParser#cleanup() + */ + public void cleanup() { + } + + /** + * Defined in ChangeLogParser interface + * + * @see ChangeLogParser + */ + public void setDateFormatInFile(String s) { + } +} \ No newline at end of file diff --git a/changelog/src/main/org/apache/maven/vsslib/VssConnection.java b/changelog/src/main/org/apache/maven/vsslib/VssConnection.java new file mode 100644 index 00000000..6f857f06 --- /dev/null +++ b/changelog/src/main/org/apache/maven/vsslib/VssConnection.java @@ -0,0 +1,105 @@ +package org.apache.maven.vsslib; + +/* + * ==================================================================== + * Copyright 2001-2004 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. + * ==================================================================== + */ + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Bean representation of the vss connection string + * + * @author Freddy Mallet + */ +public class VssConnection { + + /** Log */ + private static final Log LOG = LogFactory + .getLog(VssChangeLogGenerator.class); + + /** + * VSS repository path + */ + private String vssDir; + + /** + * VSS user information + */ + private String vssUserInf; + + /** + * VSS project + */ + private String vssProject; + + /** + * Create a new VssConnection objet according to the provided vss connection + * string. Here is an example: + * scm:vss:\\lancelot\Vss_EApplications\:guest,password:/Security/LOSCProduction + * the password is not madatory + * + * @param connection + * the vss connection string + */ + public VssConnection(String connection) { + if (!connection.startsWith("scm:vss")) { + throw new IllegalArgumentException( + "repositoy connection string does not specify 'vss' as the scm"); + } + try { + String[] splitedConnection = connection.split(":"); + vssDir = splitedConnection[2]; + if(!splitedConnection[3].equals("")){ + vssUserInf = splitedConnection[3]; + } + vssProject = splitedConnection[4]; + } catch (Exception e) { + String message = "Unable to parse VSS connection string :" + + connection; + LOG.error(message, e); + throw new IllegalArgumentException(message); + } + } + + /** + * Get the vss directory + * + * @return vss directory + */ + public String getVssDir() { + return vssDir; + } + + /** + * Get the vss project + * + * @return vss project + */ + public String getVssProject() { + return vssProject; + } + + /** + * Get the vss user information + * + * @return vss user information + */ + public String getVssUserInf() { + return vssUserInf; + } +} \ No newline at end of file