blob: 3a3bb9f8885f7595f4e07e0f2949905c98f6614f [file] [log] [blame]
package org.apache.maven.scm.provider.integrity;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 com.mks.api.Command;
import com.mks.api.MultiValue;
import com.mks.api.Option;
import com.mks.api.response.APIException;
import com.mks.api.response.Field;
import com.mks.api.response.Item;
import com.mks.api.response.Response;
import com.mks.api.response.WorkItem;
import com.mks.api.response.WorkItemIterator;
import com.mks.api.si.SIModelTypeName;
import org.apache.maven.scm.ChangeFile;
import org.apache.maven.scm.ChangeSet;
import org.apache.maven.scm.ScmFile;
import org.apache.maven.scm.ScmFileStatus;
import org.apache.maven.scm.command.changelog.ChangeLogSet;
import org.codehaus.plexus.util.StringUtils;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
/**
* This class represents an MKS Integrity Sandbox and provides an encapsulation
* for executing typical Sandbox operations
*
* @author <a href="mailto:cletus@mks.com">Cletus D'Souza</a>
* @since 1.6
*/
public class Sandbox
{
// Our date format
public static final SimpleDateFormat RLOG_DATEFORMAT = new SimpleDateFormat( "MMMMM d, yyyy - h:mm:ss a" );
// File Separator
private String fs = System.getProperty( "file.separator" );
// MKS API Session Object
private APISession api;
// Other sandbox specific class variables
private Project siProject;
private String sandboxDir;
private String cpid;
// Flag to indicate the overall add operation was successful
private boolean addSuccess;
// Flag to indicate the overall check-in operation was successful
private boolean ciSuccess;
/**
* Fixes the default includes/excludes patterns for compatibility with MKS Integrity's 'si viewnonmembers' command
*
* @param pattern String pattern representing the includes/excludes file/directory list
*/
public static String formatFilePatterns( String pattern )
{
StringBuilder sb = new StringBuilder();
if ( null != pattern && pattern.length() > 0 )
{
String[] tokens = StringUtils.split( pattern, "," );
for ( int i = 0; i < tokens.length; i++ )
{
String tkn = tokens[i].trim();
if ( tkn.indexOf( "file:" ) != 0 && tkn.indexOf( "dir:" ) != 0 )
{
sb.append( tkn.indexOf( '.' ) > 0
? StringUtils.replaceOnce( tkn, "**/", "file:" )
: StringUtils.replaceOnce( tkn, "**/", "dir:" ) );
}
else
{
sb.append( tkn );
}
sb.append( i < tokens.length ? "," : "" );
}
}
return sb.toString();
}
/**
* The Sandbox constructor
*
* @param api MKS API Session object
* @param cmProject Project object
* @param dir Absolute path to the location for the Sandbox directory
*/
public Sandbox( APISession api, Project cmProject, String dir )
{
siProject = cmProject;
sandboxDir = dir;
this.api = api;
cpid = System.getProperty( "maven.scm.integrity.cpid" );
cpid = ( ( null == cpid || cpid.length() == 0 ) ? ":none" : cpid );
addSuccess = true;
ciSuccess = true;
}
/**
* Attempts to figure out if the current sandbox already exists and is valid
*
* @param sandbox The client-side fully qualified path to the sandbox pj
* @return true/false depending on whether or not this location has a valid sandbox
* @throws APIException
*/
private boolean isValidSandbox( String sandbox )
throws APIException
{
Command cmd = new Command( Command.SI, "sandboxinfo" );
cmd.addOption( new Option( "sandbox", sandbox ) );
api.getLogger().debug( "Validating existing sandbox: " + sandbox );
Response res = api.runCommand( cmd );
WorkItemIterator wit = res.getWorkItems();
try
{
WorkItem wi = wit.next();
return wi.getField( "fullConfigSyntax" ).getValueAsString().equalsIgnoreCase(
siProject.getConfigurationPath() );
}
catch ( APIException aex )
{
ExceptionHandler eh = new ExceptionHandler( aex );
api.getLogger().error( "MKS API Exception: " + eh.getMessage() );
api.getLogger().debug( eh.getCommand() + " completed with exit code " + eh.getExitCode() );
return false;
}
}
/**
* Inspects the MKS API Response object's Item field to determine whether or nor a working file delta exists
*
* @param wfdelta MKS API Response object's Item representing the Working File Delta
* @return true if the working file is a delta; false otherwise
*/
private boolean isDelta( Item wfdelta )
{
// Return false if there is no working file
return wfdelta.getField( "isDelta" ).getBoolean().booleanValue();
}
/**
* Executes a 'si add' command using the message for the description
*
* @param memberFile Full path to the new member's location
* @param message Description for the new member's archive
* @return MKS API Response object
* @throws APIException
*/
private Response add( File memberFile, String message )
throws APIException
{
// Setup the add command
api.getLogger().info( "Adding member: " + memberFile.getAbsolutePath() );
Command siAdd = new Command( Command.SI, "add" );
siAdd.addOption( new Option( "onExistingArchive", "sharearchive" ) );
siAdd.addOption( new Option( "cpid", cpid ) );
if ( null != message && message.length() > 0 )
{
siAdd.addOption( new Option( "description", message ) );
}
siAdd.addOption( new Option( "cwd", memberFile.getParentFile().getAbsolutePath() ) );
siAdd.addSelection( memberFile.getName() );
return api.runCommand( siAdd );
}
/**
* Executes a 'si ci' command using the relativeName for the member name and message for the description
*
* @param memberFile Full path to the member's current sandbox location
* @param relativeName Relative path from the nearest subproject or project
* @param message Description for checking in the new update
* @return MKS API Response object
* @throws APIException
*/
private Response checkin( File memberFile, String relativeName, String message )
throws APIException
{
// Setup the check-in command
api.getLogger().info( "Checking in member: " + memberFile.getAbsolutePath() );
Command sici = new Command( Command.SI, "ci" );
sici.addOption( new Option( "cpid", cpid ) );
if ( null != message && message.length() > 0 )
{
sici.addOption( new Option( "description", message ) );
}
sici.addOption( new Option( "cwd", memberFile.getParentFile().getAbsolutePath() ) );
sici.addSelection( relativeName );
return api.runCommand( sici );
}
/**
* Executes a 'si drop' command using the relativeName for the member name
*
* @param memberFile Full path to the member's current sandbox location
* @param relativeName Relative path from the nearest subproject or project
* @return MKS API Response object
* @throws APIException
*/
private Response dropMember( File memberFile, String relativeName )
throws APIException
{
// Setup the drop command
api.getLogger().info( "Dropping member " + memberFile.getAbsolutePath() );
Command siDrop = new Command( Command.SI, "drop" );
siDrop.addOption( new Option( "cwd", memberFile.getParentFile().getAbsolutePath() ) );
siDrop.addOption( new Option( "noconfirm" ) );
siDrop.addOption( new Option( "cpid", cpid ) );
siDrop.addOption( new Option( "delete" ) );
siDrop.addSelection( relativeName );
return api.runCommand( siDrop );
}
/**
* Executes a 'si diff' command to see if the working file has actually changed. Even though the
* working file delta might be true, that doesn't always mean the file has actually changed.
*
* @param memberFile Full path to the member's current sandbox location
* @param relativeName Relative path from the nearest subproject or project
* @return MKS API Response object
*/
private boolean hasMemberChanged( File memberFile, String relativeName )
{
// Setup the differences command
Command siDiff = new Command( Command.SI, "diff" );
siDiff.addOption( new Option( "cwd", memberFile.getParentFile().getAbsolutePath() ) );
siDiff.addSelection( relativeName );
try
{
// Run the diff command...
Response res = api.runCommand( siDiff );
try
{
// Return the changed flag...
return res.getWorkItems().next().getResult().getField( "resultant" ).getItem().getField(
"different" ).getBoolean().booleanValue();
}
catch ( NullPointerException npe )
{
api.getLogger().warn( "Couldn't figure out differences for file: " + memberFile.getAbsolutePath() );
api.getLogger().warn(
"Null value found along response object for WorkItem/Result/Field/Item/Field.getBoolean()" );
api.getLogger().warn( "Proceeding with the assumption that the file has changed!" );
}
}
catch ( APIException aex )
{
ExceptionHandler eh = new ExceptionHandler( aex );
api.getLogger().warn( "Couldn't figure out differences for file: " + memberFile.getAbsolutePath() );
api.getLogger().warn( eh.getMessage() );
api.getLogger().warn( "Proceeding with the assumption that the file has changed!" );
api.getLogger().debug( eh.getCommand() + " completed with exit Code " + eh.getExitCode() );
}
return true;
}
/**
* Returns the full path name to the current Sandbox directory
*
* @return
*/
public String getSandboxDir()
{
return sandboxDir;
}
/**
* Executes a 'si lock' command using the relativeName of the file
*
* @param memberFile Full path to the member's current sandbox location
* @param relativeName Relative path from the nearest subproject or project
* @return MKS API Response object
* @throws APIException
*/
public Response lock( File memberFile, String relativeName )
throws APIException
{
// Setup the lock command
api.getLogger().debug( "Locking member: " + memberFile.getAbsolutePath() );
Command siLock = new Command( Command.SI, "lock" );
siLock.addOption( new Option( "revision", ":member" ) );
siLock.addOption( new Option( "cpid", cpid ) );
siLock.addOption( new Option( "cwd", memberFile.getParentFile().getAbsolutePath() ) );
siLock.addSelection( relativeName );
// Execute the lock command
return api.runCommand( siLock );
}
/**
* Executes a 'si unlock' command using the relativeName of the file
*
* @param memberFile Full path to the member's current sandbox location
* @param relativeName Relative path from the nearest subproject or project
* @return MKS API Response object
* @throws APIException
*/
public Response unlock( File memberFile, String relativeName )
throws APIException
{
// Setup the unlock command
api.getLogger().debug( "Unlocking member: " + memberFile.getAbsolutePath() );
Command siUnlock = new Command( Command.SI, "unlock" );
siUnlock.addOption( new Option( "revision", ":member" ) );
siUnlock.addOption( new Option( "action", "remove" ) );
siUnlock.addOption( new Option( "cwd", memberFile.getParentFile().getAbsolutePath() ) );
siUnlock.addSelection( relativeName );
// Execute the unlock command
return api.runCommand( siUnlock );
}
/**
* Removes the registration for the Sandbox in the user's profile
*
* @return The API Response associated with executing this command
* @throws APIException
*/
public Response drop()
throws APIException
{
File project = new File( siProject.getProjectName() );
File sandboxpj = new File( sandboxDir + fs + project.getName() );
// Check to see if the sandbox file already exists and its OK to use
api.getLogger().debug( "Sandbox Project File: " + sandboxpj.getAbsolutePath() );
Command cmd = new Command( Command.SI, "dropsandbox" );
cmd.addOption( new Option( "delete", "members" ) );
cmd.addOption( new Option( "sandbox", sandboxpj.getAbsolutePath() ) );
cmd.addOption( new Option( "cwd", sandboxDir ) );
return api.runCommand( cmd );
}
/**
* Creates a new Sandbox in the sandboxDir specified
*
* @return true if the operation is successful; false otherwise
* @throws APIException
*/
public boolean create()
throws APIException
{
File project = new File( siProject.getProjectName() );
File sandboxpj = new File( sandboxDir + fs + project.getName() );
// Check to see if the sandbox file already exists and its OK to use
api.getLogger().debug( "Sandbox Project File: " + sandboxpj.getAbsolutePath() );
if ( sandboxpj.isFile() )
{
// Validate this sandbox
if ( isValidSandbox( sandboxpj.getAbsolutePath() ) )
{
api.getLogger().debug(
"Reusing existing Sandbox in " + sandboxDir + " for project " + siProject.getConfigurationPath() );
return true;
}
else
{
api.getLogger().error(
"An invalid Sandbox exists in " + sandboxDir + ". Please provide a different location!" );
return false;
}
}
else // Create a new sandbox in the location specified
{
api.getLogger().debug(
"Creating Sandbox in " + sandboxDir + " for project " + siProject.getConfigurationPath() );
try
{
Command cmd = new Command( Command.SI, "createsandbox" );
cmd.addOption( new Option( "recurse" ) );
cmd.addOption( new Option( "nopopulate" ) );
cmd.addOption( new Option( "project", siProject.getConfigurationPath() ) );
cmd.addOption( new Option( "cwd", sandboxDir ) );
api.runCommand( cmd );
}
catch ( APIException aex )
{
// Check to see if this exception is due an existing sandbox registry entry
ExceptionHandler eh = new ExceptionHandler( aex );
if ( eh.getMessage().indexOf( "There is already a registered entry" ) > 0 )
{
// This will re-validate the sandbox, if Maven blew away the old directory
return create();
}
else
{
throw aex;
}
}
return true;
}
}
/**
* Resynchronizes an existing Sandbox
* Assumes that the create() call has already been made to validate this sandbox
*
* @throws APIException
*/
public Response resync()
throws APIException
{
api.getLogger().debug(
"Resynchronizing Sandbox in " + sandboxDir + " for project " + siProject.getConfigurationPath() );
Command cmd = new Command( Command.SI, "resync" );
cmd.addOption( new Option( "recurse" ) );
cmd.addOption( new Option( "populate" ) );
cmd.addOption( new Option( "cwd", sandboxDir ) );
return api.runCommand( cmd );
}
/**
* Executes a 'si makewritable' command to allow edits to all files in the Sandbox directory
*
* @return MKS API Response object
* @throws APIException
*/
public Response makeWriteable()
throws APIException
{
api.getLogger().debug(
"Setting files to writeable in " + sandboxDir + " for project " + siProject.getConfigurationPath() );
Command cmd = new Command( Command.SI, "makewritable" );
cmd.addOption( new Option( "recurse" ) );
cmd.addOption( new Option( "cwd", sandboxDir ) );
return api.runCommand( cmd );
}
/**
* Executes a 'si revert' command to roll back changes to all files in the Sandbox directory
*
* @return MKS API Response object
* @throws APIException
*/
public Response revertMembers()
throws APIException
{
api.getLogger().debug(
"Reverting changes in sandbox " + sandboxDir + " for project " + siProject.getConfigurationPath() );
Command cmd = new Command( Command.SI, "revert" );
cmd.addOption( new Option( "recurse" ) );
cmd.addOption( new Option( "cwd", sandboxDir ) );
return api.runCommand( cmd );
}
/**
* Executes a 'si viewnonmembers' command filtering the results using the exclude and include lists
*
* @param exclude Pattern containing the exclude file list
* @param include Pattern containing the include file list
* @return List of ScmFile objects representing the new files in the Sandbox
* @throws APIException
*/
public List<ScmFile> getNewMembers( String exclude, String include )
throws APIException
{
// Store a list of files that were added to the repository
List<ScmFile> filesAdded = new ArrayList<ScmFile>();
Command siViewNonMem = new Command( Command.SI, "viewnonmembers" );
siViewNonMem.addOption( new Option( "recurse" ) );
if ( null != exclude && exclude.length() > 0 )
{
siViewNonMem.addOption( new Option( "exclude", exclude ) );
}
if ( null != include && include.length() > 0 )
{
siViewNonMem.addOption( new Option( "include", include ) );
}
siViewNonMem.addOption( new Option( "noincludeFormers" ) );
siViewNonMem.addOption( new Option( "cwd", sandboxDir ) );
Response response = api.runCommand( siViewNonMem );
for ( WorkItemIterator wit = response.getWorkItems(); wit.hasNext(); )
{
filesAdded.add(
new ScmFile( wit.next().getField( "absolutepath" ).getValueAsString(), ScmFileStatus.ADDED ) );
}
return filesAdded;
}
/**
* Adds a list of files to the MKS Integrity SCM Project
*
* @param exclude Pattern containing the exclude file list
* @param include Pattern containing the include file list
* @param message Description for the member's archive
* @return
*/
public List<ScmFile> addNonMembers( String exclude, String include, String message )
{
// Re-initialize the overall addSuccess to be true for now
addSuccess = true;
// Store a list of files that were actually added to the repository
List<ScmFile> filesAdded = new ArrayList<ScmFile>();
api.getLogger().debug( "Looking for new members in sandbox dir: " + sandboxDir );
try
{
List<ScmFile> newFileList = getNewMembers( exclude, include );
for ( Iterator<ScmFile> sit = newFileList.iterator(); sit.hasNext(); )
{
try
{
ScmFile localFile = sit.next();
// Attempt to add the file to the Integrity repository
add( new File( localFile.getPath() ), message );
// If it was a success, then add it to the list of files that were actually added
filesAdded.add( localFile );
}
catch ( APIException aex )
{
// Set the addSuccess to false, since we ran into a problem
addSuccess = false;
ExceptionHandler eh = new ExceptionHandler( aex );
api.getLogger().error( "MKS API Exception: " + eh.getMessage() );
api.getLogger().debug( eh.getCommand() + " completed with exit Code " + eh.getExitCode() );
}
}
}
catch ( APIException aex )
{
// Set the addSuccess to false, since we ran into a problem
addSuccess = false;
ExceptionHandler eh = new ExceptionHandler( aex );
api.getLogger().error( "MKS API Exception: " + eh.getMessage() );
api.getLogger().debug( eh.getCommand() + " completed with exit Code " + eh.getExitCode() );
}
return filesAdded;
}
/**
* Returns the overall success of the add operation
*
* @return
*/
public boolean getOverallAddSuccess()
{
return addSuccess;
}
/**
* Inspects the MKS API Response object's Item field to determine whether or nor a working file exists
*
* @param wfdelta MKS API Response object's Item representing the Working File Delta
* @return
*/
public boolean hasWorkingFile( Item wfdelta )
{
// Return false if there is no working file
return !wfdelta.getField( "noWorkingFile" ).getBoolean().booleanValue();
}
/**
* Executes a 'si viewsandbox' and parses the output for changed or dropped working files
*
* @return A list of MKS API Response WorkItem objects representing the changes in the Sandbox
* @throws APIException
*/
public List<WorkItem> getChangeList()
throws APIException
{
// Store a list of files that were changed/removed to the repository
List<WorkItem> changedFiles = new ArrayList<WorkItem>();
// Setup the view sandbox command to figure out what has changed...
Command siViewSandbox = new Command( Command.SI, "viewsandbox" );
// Create the --fields option
MultiValue mv = new MultiValue( "," );
mv.add( "name" );
mv.add( "context" );
mv.add( "wfdelta" );
mv.add( "memberarchive" );
siViewSandbox.addOption( new Option( "fields", mv ) );
siViewSandbox.addOption( new Option( "recurse" ) );
siViewSandbox.addOption( new Option( "noincludeDropped" ) );
siViewSandbox.addOption( new Option( "filterSubs" ) );
siViewSandbox.addOption( new Option( "cwd", sandboxDir ) );
// Run the view sandbox command
Response r = api.runCommand( siViewSandbox );
// Check-in all changed files, drop all members with missing working files
for ( WorkItemIterator wit = r.getWorkItems(); wit.hasNext(); )
{
WorkItem wi = wit.next();
api.getLogger().debug( "Inspecting file: " + wi.getField( "name" ).getValueAsString() );
if ( wi.getModelType().equals( SIModelTypeName.MEMBER ) )
{
Item wfdeltaItem = (Item) wi.getField( "wfdelta" ).getValue();
// Proceed with this entry only if it is an actual working file delta
if ( isDelta( wfdeltaItem ) )
{
File memberFile = new File( wi.getField( "name" ).getValueAsString() );
if ( hasWorkingFile( wfdeltaItem ) )
{
// Only report on files that have actually changed...
if ( hasMemberChanged( memberFile, wi.getId() ) )
{
changedFiles.add( wi );
}
}
else
{
// Also report on dropped files
changedFiles.add( wi );
}
}
}
}
return changedFiles;
}
/**
* Wrapper function to check-in all changes and drop members associated with missing working files
*
* @param message Description for the changes
* @return
*/
public List<ScmFile> checkInUpdates( String message )
{
// Re-initialize the overall ciSuccess to be true for now
ciSuccess = true;
// Store a list of files that were changed/removed to the repository
List<ScmFile> changedFiles = new ArrayList<ScmFile>();
api.getLogger().debug( "Looking for changed and dropped members in sandbox dir: " + sandboxDir );
try
{
// Let the list of changed files
List<WorkItem> changeList = getChangeList();
// Check-in all changed files, drop all members with missing working files
for ( Iterator<WorkItem> wit = changeList.iterator(); wit.hasNext(); )
{
try
{
WorkItem wi = wit.next();
File memberFile = new File( wi.getField( "name" ).getValueAsString() );
// Check-in files that have actually changed...
if ( hasWorkingFile( (Item) wi.getField( "wfdelta" ).getValue() ) )
{
// Lock each member as you go...
lock( memberFile, wi.getId() );
// Commit the changes...
checkin( memberFile, wi.getId(), message );
// Update the changed file list
changedFiles.add( new ScmFile( memberFile.getAbsolutePath(), ScmFileStatus.CHECKED_IN ) );
}
else
{
// Drop the member if there is no working file
dropMember( memberFile, wi.getId() );
// Update the changed file list
changedFiles.add( new ScmFile( memberFile.getAbsolutePath(), ScmFileStatus.DELETED ) );
}
}
catch ( APIException aex )
{
// Set the ciSuccess to false, since we ran into a problem
ciSuccess = false;
ExceptionHandler eh = new ExceptionHandler( aex );
api.getLogger().error( "MKS API Exception: " + eh.getMessage() );
api.getLogger().debug( eh.getCommand() + " completed with exit Code " + eh.getExitCode() );
}
}
}
catch ( APIException aex )
{
// Set the ciSuccess to false, since we ran into a problem
ciSuccess = false;
ExceptionHandler eh = new ExceptionHandler( aex );
api.getLogger().error( "MKS API Exception: " + eh.getMessage() );
api.getLogger().debug( eh.getCommand() + " completed with exit Code " + eh.getExitCode() );
}
return changedFiles;
}
/**
* Returns the overall success of the check-in operation
*
* @return
*/
public boolean getOverallCheckInSuccess()
{
return ciSuccess;
}
/**
* Creates one subproject per directory, as required.
*
* @param dirPath A relative path structure of folders
* @return Response containing the result for the created subproject
* @throws APIException
*/
public Response createSubproject( String dirPath )
throws APIException
{
// Setup the create subproject command
api.getLogger().debug( "Creating subprojects for: " + dirPath + "/project.pj" );
Command siCreateSubproject = new Command( Command.SI, "createsubproject" );
siCreateSubproject.addOption( new Option( "cpid", cpid ) );
siCreateSubproject.addOption( new Option( "createSubprojects" ) );
siCreateSubproject.addOption( new Option( "cwd", sandboxDir ) );
siCreateSubproject.addSelection( dirPath + "/project.pj" );
// Execute the create subproject command
return api.runCommand( siCreateSubproject );
}
/**
* Executes the 'si rlog' command to generate a list of changed revision found between startDate and endDate
*
* @param startDate The date range for the beginning of the operation
* @param endDate The date range for the end of the operation
* @return ChangeLogSet containing a list of changes grouped by Change Pacakge ID
* @throws APIException
*/
public ChangeLogSet getChangeLog( Date startDate, Date endDate )
throws APIException
{
// Initialize our return object
ChangeLogSet changeLog = new ChangeLogSet( startDate, endDate );
// By default we're going to group-by change package
// Non change package changes will be lumped into one big Change Set
Hashtable<String, ChangeSet> changeSetHash = new Hashtable<String, ChangeSet>();
// Lets prepare our si rlog command for execution
Command siRlog = new Command( Command.SI, "rlog" );
siRlog.addOption( new Option( "recurse" ) );
MultiValue rFilter = new MultiValue( ":" );
rFilter.add( "daterange" );
rFilter.add( "'" + RLOG_DATEFORMAT.format( startDate ) + "'-'" + RLOG_DATEFORMAT.format( endDate ) + "'" );
siRlog.addOption( new Option( "rfilter", rFilter ) );
siRlog.addOption( new Option( "cwd", sandboxDir ) );
// Execute the si rlog command
Response response = api.runCommand( siRlog );
for ( WorkItemIterator wit = response.getWorkItems(); wit.hasNext(); )
{
WorkItem wi = wit.next();
String memberName = wi.getContext();
// We're going to have to do a little dance to get the correct server file name
memberName = memberName.substring( 0, memberName.lastIndexOf( '/' ) );
memberName = memberName + '/' + wi.getId();
memberName = memberName.replace( '\\', '/' );
// Now lets get the revisions for this file
Field revisionsFld = wi.getField( "revisions" );
if ( null != revisionsFld && revisionsFld.getDataType().equals( Field.ITEM_LIST_TYPE )
&& null != revisionsFld.getList() )
{
@SuppressWarnings( "unchecked" ) List<Item> revList = revisionsFld.getList();
for ( Iterator<Item> lit = revList.iterator(); lit.hasNext(); )
{
Item revisionItem = lit.next();
String revision = revisionItem.getId();
String author = revisionItem.getField( "author" ).getItem().getId();
// Attempt to get the full name, if available
try
{
author = revisionItem.getField( "author" ).getItem().getField( "fullname" ).getValueAsString();
}
catch ( NullPointerException npe )
{ /* ignore */ }
String cpid = ":none";
// Attempt to get the cpid for this revision
try
{
cpid = revisionItem.getField( "cpid" ).getItem().getId();
}
catch ( NullPointerException npe )
{ /* ignore */ }
// Get the Change Package summary for this revision
String comment = cpid + ": " + revisionItem.getField( "cpsummary" ).getValueAsString();
// Get the date associated with this revision
Date date = revisionItem.getField( "date" ).getDateTime();
// Lets create our ChangeFile based on the information we've gathered so far
ChangeFile changeFile = new ChangeFile( memberName, revision );
// Check to see if we already have a ChangeSet grouping for this revision
ChangeSet changeSet = changeSetHash.get( cpid );
if ( null != changeSet )
{
// Set the date of the ChangeSet to the oldest entry
if ( changeSet.getDate().after( date ) )
{
changeSet.setDate( date );
}
// Add the new ChangeFile
changeSet.addFile( changeFile );
// Update the changeSetHash
changeSetHash.put( cpid, changeSet );
}
else // Create a new ChangeSet grouping and add the ChangeFile
{
List<ChangeFile> changeFileList = new ArrayList<ChangeFile>();
changeFileList.add( changeFile );
changeSet = new ChangeSet( date, comment, author, changeFileList );
// Update the changeSetHash with an initial entry for the cpid
changeSetHash.put( cpid, changeSet );
}
}
}
}
// Update the Change Log with the Change Sets
List<ChangeSet> changeSetList = new ArrayList<ChangeSet>();
changeSetList.addAll( changeSetHash.values() );
changeLog.setChangeSets( changeSetList );
return changeLog;
}
}