blob: d4c30af3cae9991008f97c009de987687d8a2eb2 [file] [log] [blame]
package org.eclipse.team.ccvs.core;
/*
* (c) Copyright IBM Corp. 2000, 2002.
* All Rights Reserved.
*/
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceVisitor;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.team.core.IFileTypeRegistry;
import org.eclipse.team.core.ITeamNature;
import org.eclipse.team.core.ITeamProvider;
import org.eclipse.team.core.TeamException;
import org.eclipse.team.core.TeamPlugin;
import org.eclipse.team.core.sync.IRemoteSyncElement;
import org.eclipse.team.internal.ccvs.core.CVSException;
import org.eclipse.team.internal.ccvs.core.CVSProvider;
import org.eclipse.team.internal.ccvs.core.Policy;
import org.eclipse.team.internal.ccvs.core.client.Command;
import org.eclipse.team.internal.ccvs.core.client.Commit;
import org.eclipse.team.internal.ccvs.core.client.ResponseHandler;
import org.eclipse.team.internal.ccvs.core.client.Session;
import org.eclipse.team.internal.ccvs.core.client.Tag;
import org.eclipse.team.internal.ccvs.core.client.Update;
import org.eclipse.team.internal.ccvs.core.client.Command.LocalOption;
import org.eclipse.team.internal.ccvs.core.client.listeners.DiffListener;
import org.eclipse.team.internal.ccvs.core.connection.CVSRepositoryLocation;
import org.eclipse.team.internal.ccvs.core.connection.CVSServerException;
import org.eclipse.team.internal.ccvs.core.resources.CVSRemoteSyncElement;
import org.eclipse.team.internal.ccvs.core.resources.CVSWorkspaceRoot;
import org.eclipse.team.internal.ccvs.core.resources.RemoteFile;
import org.eclipse.team.internal.ccvs.core.resources.RemoteFolder;
import org.eclipse.team.internal.ccvs.core.resources.RemoteFolderTreeBuilder;
import org.eclipse.team.internal.ccvs.core.syncinfo.FolderSyncInfo;
import org.eclipse.team.internal.ccvs.core.syncinfo.ResourceSyncInfo;
import org.eclipse.team.internal.ccvs.core.util.Assert;
/**
* This class acts as both the ITeamNature and the ITeamProvider instances
* required by the Team core.
*
* The current stat of this class and it's plugin is EXPERIMENTAL.
* As such, it is subject to change except in it's conformance to the
* TEAM API which it implements.
*
* Questions:
*
* How should a project/reource rename/move effect the provider?
*
* Currently we always update with -P. Is this OK?
* - A way to allow customizable options would be nice
*
* Is the -l option valid for commit and does it work properly for update and commit?
*
* Do we need an IUserInteractionProvider in the CVS core
* - prompt for user info (caching could be separate)
* - get release comments
* - prompt for overwrite of unmanaged files
*
* Need a mechanism for communicating meta-information (provided by Team?)
*
* Should pass null when there are no options for a cvs command
*
* We currently write the files to disk and do a refreshLocal to
* have them appear in Eclipse. This may be changed in the future.
*/
public class CVSTeamProvider implements ITeamNature, ITeamProvider {
private CVSWorkspaceRoot workspaceRoot;
private IProject project;
private String comment = ""; //$NON-NLS-1$
/**
* No-arg Constructor for IProjectNature conformance
*/
public CVSTeamProvider() {
}
/**
* @see IProjectNature#configure()
*/
public void configure() throws CoreException {
}
/**
* @see IProjectNature#deconfigure()
*/
public void deconfigure() throws CoreException {
}
/**
* @see IProjectNature#getProject()
*/
public IProject getProject() {
return project;
}
/**
* @see ITeamNature#configureProvider(Properties)
*/
public void configureProvider(Properties configuration) throws TeamException {
}
/**
* @see IProjectNature#setProject(IProject)
*/
public void setProject(IProject project) {
this.project = project;
try {
this.workspaceRoot = new CVSWorkspaceRoot(project);
// Ensure that the project has CVS info
if (workspaceRoot.getLocalRoot().getFolderSyncInfo() == null) {
throw new CVSException(new CVSStatus(CVSStatus.ERROR, Policy.bind("CVSTeamProvider.noFolderInfo", project.getName()))); //$NON-NLS-1$
}
} catch (CVSException e) {
// Log any problems creating the CVS managed resource
CVSProviderPlugin.log(e);
}
}
/**
* @see ITeamNature#getProvider()
*/
public ITeamProvider getProvider() throws TeamException {
if (workspaceRoot == null) {
throw new TeamException(new Status(IStatus.ERROR, CVSProviderPlugin.ID, TeamException.UNABLE, Policy.bind("CVSTeamProvider.initializationFailed", new Object[]{project.getName()}), null)); //$NON-NLS-1$
}
return this;
}
/**
* Add the given resources to the project.
* <p>
* The sematics follow that of CVS in the sense that any folders
* being added are created remotely as a result of this operation
* while files are created remotely on the next commit.
* </p>
* <p>
* This method uses the team file type registry to determine the type
* of added files. If the extension of the file is not in the registry,
* the file is assumed to be binary.
* </p>
* <p>
* NOTE: for now we do three operations: one each for folders, text files and binary files.
* We should optimize this when time permits to either use one operations or defer server
* contact until the next commit.
* </p>
*
* <p>
* There are special semantics for adding the project itself to the repo. In this case, the project
* must be included in the resources array.
* </p>
*/
public void add(IResource[] resources, int depth, IProgressMonitor progress) throws TeamException {
// Visit the children of the resources using the depth in order to
// determine which folders, text files and binary files need to be added
// A TreeSet is needed for the folders so they are in the right order (i.e. parents created before children)
final SortedSet folders = new TreeSet();
// Sets are required for the files to ensure that files will not appear twice if there parent was added as well
// and the depth isn't zero
final Set textfiles = new HashSet(resources.length);
final Set binaryfiles = new HashSet(resources.length);
final IFileTypeRegistry registry = TeamPlugin.getFileTypeRegistry();
final TeamException[] eHolder = new TeamException[1];
for (int i=0; i<resources.length; i++) {
final IResource currentResource = resources[i];
// Throw an exception if the resource is not a child of the receiver
checkIsChild(currentResource);
try {
// Auto-add parents if they are not already managed
IContainer parent = currentResource.getParent();
ICVSResource cvsResource = CVSWorkspaceRoot.getCVSResourceFor(currentResource);
while (parent.getType() != IResource.ROOT && parent.getType() != IResource.PROJECT && ! cvsResource.isManaged()) {
folders.add(parent.getProjectRelativePath().toString());
parent = parent.getParent();
}
// Auto-add children
currentResource.accept(new IResourceVisitor() {
public boolean visit(IResource resource) {
ICVSResource mResource = CVSWorkspaceRoot.getCVSResourceFor(resource);
// Add the resource is its not already managed and it was either
// added explicitly (is equal currentResource) or is not ignored
if (! mResource.isManaged() && (currentResource.equals(resource) || ! mResource.isIgnored())) {
String name = resource.getProjectRelativePath().toString();
if (resource.getType() == IResource.FILE) {
String extension = resource.getFileExtension();
if ((extension != null) && ("true".equals(registry.getValue(extension, "isText")))) //$NON-NLS-1$ //$NON-NLS-2$
textfiles.add(name);
else
binaryfiles.add(name);
} else
folders.add(name);
}
// Always return true and let the depth determine if children are visited
return true;
}
}, depth, false);
} catch (CoreException e) {
throw new CVSException(new Status(IStatus.ERROR, CVSProviderPlugin.ID, TeamException.UNABLE, Policy.bind("CVSTeamProvider.visitError", new Object[] {resources[i].getFullPath()}), e)); //$NON-NLS-1$
}
}
// If an exception occured during the visit, throw it here
if (eHolder[0] != null)
throw eHolder[0];
// XXX Do we need to add the project
// Add the folders, followed by files!
IStatus status;
Session s = new Session(workspaceRoot.getRemoteLocation(), workspaceRoot.getLocalRoot());
progress.beginTask(null, 100);
try {
// Opening the session takes 10% of the time
s.open(Policy.subMonitorFor(progress, 10));
if (!folders.isEmpty()) {
status = Command.ADD.execute(s,
Command.NO_GLOBAL_OPTIONS,
Command.NO_LOCAL_OPTIONS,
(String[])folders.toArray(new String[folders.size()]),
null,
Policy.subMonitorFor(progress, 30));
if (status.getCode() == CVSStatus.SERVER_ERROR) {
throw new CVSServerException(status);
}
}
if (!textfiles.isEmpty()) {
status = Command.ADD.execute(s,
Command.NO_GLOBAL_OPTIONS,
Command.NO_LOCAL_OPTIONS,
(String[])textfiles.toArray(new String[textfiles.size()]),
null,
Policy.subMonitorFor(progress, 30));
if (status.getCode() == CVSStatus.SERVER_ERROR) {
throw new CVSServerException(status);
}
}
if (!binaryfiles.isEmpty()) {
status = Command.ADD.execute(s,
Command.NO_GLOBAL_OPTIONS,
new LocalOption[] { Command.KSUBST_BINARY },
(String[])binaryfiles.toArray(new String[binaryfiles.size()]),
null,
Policy.subMonitorFor(progress, 30));
if (status.getCode() == CVSStatus.SERVER_ERROR) {
throw new CVSServerException(status);
}
}
} finally {
s.close();
progress.done();
}
}
/**
* Checkin any local changes using "cvs commit ...".
*
* @see ITeamProvider#checkin(IResource[], int, IProgressMonitor)
*/
public void checkin(IResource[] resources, int depth, IProgressMonitor progress) throws TeamException {
// Build the local options
List localOptions = new ArrayList();
localOptions.add(Commit.makeArgumentOption(Command.MESSAGE_OPTION, comment));
// If the depth is not infinite, we want the -l option
if (depth != IResource.DEPTH_INFINITE) {
localOptions.add(Commit.DO_NOT_RECURSE);
}
LocalOption[] commandOptions = (LocalOption[])localOptions.toArray(new LocalOption[localOptions.size()]);
// Build the arguments list
String[] arguments = getValidArguments(resources, commandOptions);
// Commit the resources
IStatus status;
Session s = new Session(workspaceRoot.getRemoteLocation(), workspaceRoot.getLocalRoot());
progress.beginTask(null, 100);
try {
// Opening the session takes 20% of the time
s.open(Policy.subMonitorFor(progress, 20));
status = Command.COMMIT.execute(s,
Command.NO_GLOBAL_OPTIONS,
commandOptions,
arguments, null,
Policy.subMonitorFor(progress, 80));
} finally {
s.close();
progress.done();
}
if (status.getCode() == CVSStatus.SERVER_ERROR) {
throw new CVSServerException(status);
}
}
/**
* Checkout the provided resources so they can be modified locally and committed.
*
* Currently, we support only the optimistic model so checkout does nothing.
*
* @see ITeamProvider#checkout(IResource[], int, IProgressMonitor)
*/
public void checkout(IResource[] resources, int depth, IProgressMonitor progress) throws TeamException {
}
/**
* @see ITeamProvider#delete(IResource[], int, IProgressMonitor)
*/
public void delete(IResource[] resources, final IProgressMonitor progress) throws TeamException {
try {
progress.beginTask(null, 100);
// Delete any files locally and record the names.
// Use a resource visitor to ensure the proper depth is obtained
final IProgressMonitor subProgress = Policy.infiniteSubMonitorFor(progress, 30);
subProgress.beginTask(null, 256);
final List files = new ArrayList(resources.length);
final TeamException[] eHolder = new TeamException[1];
for (int i=0;i<resources.length;i++) {
IResource resource = resources[i];
checkIsChild(resource);
try {
if (resource.exists()) {
resource.accept(new IResourceVisitor() {
public boolean visit(IResource resource) {
try {
ICVSResource cvsResource = workspaceRoot.getCVSResourceFor(resource);
if (cvsResource.isManaged()) {
String name = resource.getProjectRelativePath().toString();
if (resource.getType() == IResource.FILE) {
files.add(name);
((IFile)resource).delete(false, true, subProgress);
}
}
} catch (CoreException e) {
eHolder[0] = wrapException(e);
// If there was a problem, don't visit the children
return false;
}
// Always return true and let the depth determine if children are visited
return true;
}
}, IResource.DEPTH_INFINITE, false);
} else if (resource.getType() == IResource.FILE) {
// If the resource doesn't exist but is a file, queue it for removal
files.add(resource.getProjectRelativePath().toString());
}
} catch (CoreException e) {
throw wrapException(e);
}
}
subProgress.done();
// If an exception occured during the visit, throw it here
if (eHolder[0] != null)
throw eHolder[0];
// Remove the files remotely
IStatus status;
Session s = new Session(workspaceRoot.getRemoteLocation(), workspaceRoot.getLocalRoot());
s.open(progress);
try {
status = Command.REMOVE.execute(s,
Command.NO_GLOBAL_OPTIONS,
Command.NO_LOCAL_OPTIONS,
(String[])files.toArray(new String[files.size()]),
null,
Policy.subMonitorFor(progress, 70));
} finally {
s.close();
}
if (status.getCode() == CVSStatus.SERVER_ERROR) {
throw new CVSServerException(status);
}
} finally {
progress.done();
}
}
/**
* Diff the resources against the repository and write the
* output to the provided PrintStream in a form that is usable
* as a patch
*/
public void diff(IResource[] resources, LocalOption[] options, PrintStream stream,
IProgressMonitor progress) throws TeamException {
// Build the arguments list
String[] arguments = getValidArguments(resources, options);
IStatus status;
Session s = new Session(workspaceRoot.getRemoteLocation(), workspaceRoot.getLocalRoot());
progress.beginTask(null, 100);
try {
s.open(Policy.subMonitorFor(progress, 20));
status = Command.DIFF.execute(s,
Command.NO_GLOBAL_OPTIONS,
options,
arguments,
new DiffListener(stream),
Policy.subMonitorFor(progress, 80));
} finally {
s.close();
progress.done();
}
}
/**
* Replace the local version of the provided resources with the remote using "cvs update -C ..."
*
* @see ITeamProvider#get(IResource[], int, IProgressMonitor)
*/
public void get(IResource[] resources, final int depth, IProgressMonitor progress) throws TeamException {
get(resources, depth, null, progress);
}
public void get(IResource[] resources, final int depth, CVSTag tag, IProgressMonitor progress) throws TeamException {
try {
progress.beginTask(null, 100);
final IProgressMonitor subProgress = Policy.infiniteSubMonitorFor(progress, 30);
// Need to correct any outgoing additions and deletions so the remote contents will be retrieved properly
ICVSResourceVisitor visitor = new ICVSResourceVisitor() {
public void visitFile(ICVSFile file) throws CVSException {
ResourceSyncInfo info = file.getSyncInfo();
if (info == null || info.isAdded()) {
// Delete the file if it's unmanaged or doesn't exist remotely
file.delete();
file.unmanage();
} else if (info.isDeleted()) {
// If deleted, null the sync info so the file will be refetched
file.unmanage();
}
subProgress.worked(1);
}
public void visitFolder(ICVSFolder folder) throws CVSException {
// Visit the children of the folder as appropriate
if (depth == IResource.DEPTH_INFINITE)
folder.acceptChildren(this);
else if (depth == IResource.DEPTH_ONE) {
ICVSFile[] files = folder.getFiles();
for (int i = 0; i < files.length; i++) {
files[i].accept(this);
}
}
subProgress.worked(1);
}
};
subProgress.beginTask(null, 512);
for (int i = 0; i < resources.length; i++) {
subProgress.subTask(Policy.bind("CVSTeamProvider.scrubbingResource", resources[i].getFullPath().toString()));
IResource resource = resources[i];
workspaceRoot.getLocalRoot().getChild(resource.getProjectRelativePath().toString()).accept(visitor);
}
subProgress.done();
// Perform an update, ignoring any local file modifications
List options = new ArrayList();
options.add(Update.IGNORE_LOCAL_CHANGES);
if(depth != IResource.DEPTH_INFINITE) {
options.add(Command.DO_NOT_RECURSE);
}
LocalOption[] commandOptions = (LocalOption[]) options.toArray(new LocalOption[options.size()]);
update(resources, commandOptions, tag, null, Policy.subMonitorFor(progress, 70));
} finally {
progress.done();
}
}
/**
* @see ITeamProvider#hasRemote(IResource)
* XXX to be removed when sync methods are removed from ITeamProvider
*/
public boolean hasRemote(IResource resource) {
try {
ICVSResource cvsResource = workspaceRoot.getCVSResourceFor(resource);
int type = resource.getType();
if(type!=IResource.FILE) {
if(type==IResource.PROJECT) {
return ((ICVSFolder)cvsResource).isCVSFolder();
} else {
return cvsResource.isManaged();
}
} else {
ResourceSyncInfo info = cvsResource.getSyncInfo();
if(info!=null) {
return !info.isAdded();
} else {
return false;
}
}
} catch(CVSException e) {
return false;
}
}
/**
* @see ITeamProvider#isLocallyCheckedOut(IResource)
* XXX to be removed when sync methods are removed from ITeamProvider
*/
public boolean isCheckedOut(IResource resource) {
ICVSResource cvsResource = CVSWorkspaceRoot.getCVSResourceFor(resource);
return cvsResource.isManaged();
}
/*
* Use specialiazed tagging to move all local changes (including additions and
* deletions) to the specified branch.
*/
public void makeBranch(IResource[] resources, CVSTag versionTag, CVSTag branchTag, boolean moveToBranch, IProgressMonitor monitor) throws TeamException {
// Determine the total amount of work
int totalWork = 10 + (versionTag!= null ? 60 : 40) + (moveToBranch ? 20 : 0);
monitor.beginTask(Policy.bind("CVSTeamProvider.makeBranch"), totalWork); //$NON-NLS-1$
try {
// Build the arguments list
String[] arguments = getValidArguments(resources, Command.NO_LOCAL_OPTIONS);
// Tag the remote resources
Session s = new Session(workspaceRoot.getRemoteLocation(), workspaceRoot.getLocalRoot());
try {
s.open(Policy.subMonitorFor(monitor, 10));
IStatus status;
if (versionTag != null) {
// Version using tag and braqnch using rtag
status = Command.CUSTOM_TAG.execute(s,
Command.NO_GLOBAL_OPTIONS,
Command.NO_LOCAL_OPTIONS,
versionTag,
arguments,
null,
Policy.subMonitorFor(monitor, 40));
if (status.getCode() != CVSStatus.SERVER_ERROR) {
status = Command.RTAG.execute(s,
Command.NO_GLOBAL_OPTIONS,
Command.NO_LOCAL_OPTIONS,
versionTag,
branchTag,
arguments,
Policy.subMonitorFor(monitor, 20));
}
} else {
// Just branch using tag
status = Command.CUSTOM_TAG.execute(s,
Command.NO_GLOBAL_OPTIONS,
Command.NO_LOCAL_OPTIONS,
branchTag,
arguments,
null,
Policy.subMonitorFor(monitor, 40));
}
if (status.getCode() == CVSStatus.SERVER_ERROR) {
throw new CVSServerException(status);
}
} finally {
s.close();
}
// Set the tag of the local resources to the branch tag (The uodate command will not
// properly update "cvs added" and "cvs removed" resources so a custom visitor is used
if (moveToBranch) {
setTag(resources, branchTag, Policy.infiniteSubMonitorFor(monitor, 20));
}
} finally {
monitor.done();
}
}
/**
* Update the sync info of the local resource associated with the sync element such that
* the revision of the local resource matches that of the remote resource.
* This will allow commits on the local resource to succeed.
*
* Only file resources can be merged.
*/
public void merged(IRemoteSyncElement[] elements) throws TeamException {
for (int i=0;i<elements.length;i++) {
((CVSRemoteSyncElement)elements[i]).makeOutgoing(null);
}
}
/**
* @see ITeamProvider#move(IResource, IPath, IProgressMonitor)
*/
public void moved(IPath source, IResource resource, IProgressMonitor progress) throws TeamException {
}
/**
* Set the comment to be used on the next checkin
*/
public void setComment(String comment) {
this.comment = comment;
}
/**
* Set the connection method for the given resource's
* project. If the conection method name is invalid (i.e.
* no corresponding registered connection method), false is returned.
*/
public boolean setConnectionInfo(IResource resource, String methodName, IUserInfo userInfo, IProgressMonitor monitor) throws TeamException {
checkIsChild(resource);
try {
monitor.beginTask(Policy.bind("CVSTeamProvider.connectionInfo", project.getName()), 100); //$NON-NLS-1$
if (!CVSRepositoryLocation.validateConnectionMethod(methodName))
return false;
// Get the original location
ICVSRepositoryLocation location = workspaceRoot.getRemoteLocation();
// Make a copy to work on
CVSRepositoryLocation newLocation = CVSRepositoryLocation.fromString(location.getLocation());
newLocation.setMethod(methodName);
newLocation.setUserInfo(userInfo);
// Validate that a connection can be made with the new location
try {
newLocation.validateConnection(Policy.subMonitorFor(monitor, 20));
} catch (CVSException e) {
// XXX We should really only do this if it didn't exist previously
CVSProviderPlugin.getProvider().disposeRepository(newLocation);
throw e;
}
// Add the location to the provider
CVSProvider.getInstance().addRepository(newLocation);
// Set the project to use the new Locations
setRemoteRoot(newLocation, Policy.infiniteSubMonitorFor(monitor, 80));
return true;
} finally {
monitor.done();
}
}
/*
* This method sets the tag for a project.
* It expects to be passed an InfiniteSubProgressMonitor
*/
private void setTag(IResource[] resources, final CVSTag tag, final IProgressMonitor monitor) throws TeamException {
try {
// 512 ticks gives us a maximum of 2048 which seems reasonable for folders and files in a project
monitor.beginTask(Policy.bind("CVSTeamProvider.folderInfo", project.getName()), 512); //$NON-NLS-1$
// Visit all the children folders in order to set the root in the folder sync info
for (int i = 0; i < resources.length; i++) {
CVSWorkspaceRoot.getCVSResourceFor(resources[i]).accept(new ICVSResourceVisitor() {
public void visitFile(ICVSFile file) throws CVSException {
monitor.worked(1);
ResourceSyncInfo info = file.getSyncInfo();
if (info != null) {
monitor.subTask(Policy.bind("CVSTeamProvider.updatingFile", info.getName())); //$NON-NLS-1$
file.setSyncInfo(new ResourceSyncInfo(info.getName(),
(info.isDeleted() ? info.DELETED_PREFIX : "") + info.getRevision(), //$NON-NLS-1$
info.getTimeStamp(), info.getKeywordMode(), tag, info.getPermissions()));
}
};
public void visitFolder(ICVSFolder folder) throws CVSException {
monitor.worked(1);
FolderSyncInfo info = folder.getFolderSyncInfo();
if (info != null) {
monitor.subTask(Policy.bind("CVSTeamProvider.updatingFolder", info.getRepository())); //$NON-NLS-1$
folder.setFolderSyncInfo(new FolderSyncInfo(info.getRepository(), info.getRoot(), tag, info.getIsStatic()));
folder.acceptChildren(this);
}
};
});
}
} finally {
monitor.done();
}
}
/**
* Tag the resources in the CVS repository with the given tag.
*
* The returned IStatus will be a status containing any errors or warnings.
* If the returned IStatus is a multi-status, the code indicates the severity.
* Possible codes are:
* CVSStatus.OK - Nothing to report
* CVSStatus.SERVER_ERROR - The server reported an error
* any other code - warning messages received from the server
*/
public IStatus tag(IResource[] resources, int depth, CVSTag tag, IProgressMonitor progress) throws TeamException {
// Build the local options
List localOptions = new ArrayList();
// If the depth is not infinite, we want the -l option
if (depth != IResource.DEPTH_INFINITE)
localOptions.add(Tag.DO_NOT_RECURSE);
LocalOption[] commandOptions = (LocalOption[])localOptions.toArray(new LocalOption[localOptions.size()]);
// Build the arguments list
String[] arguments = getValidArguments(resources, commandOptions);
IStatus status;
Session s = new Session(workspaceRoot.getRemoteLocation(), workspaceRoot.getLocalRoot());
progress.beginTask(null, 100);
try {
// Opening the session takes 20% of the time
s.open(Policy.subMonitorFor(progress, 20));
status = Command.TAG.execute(s,
Command.NO_GLOBAL_OPTIONS,
commandOptions,
tag,
arguments,
null,
Policy.subMonitorFor(progress, 80));
} finally {
s.close();
progress.done();
}
if (status.getCode() == CVSStatus.SERVER_ERROR) {
throw new CVSServerException(status);
}
return status;
}
/**
* Currently, we support only the optimistic model so uncheckout dores nothing.
*
* @see ITeamProvider#uncheckout(IResource[], int, IProgressMonitor)
*/
public void uncheckout(IResource[] resources, int depth, IProgressMonitor progress) throws TeamException {
}
/**
* Generally useful update.
*
* The tag parameter determines any stickyness after the update is run. If tag is null, any tagging on the
* resources being updated remain the same. If the tag is a branch, version or date tag, then the resources
* will be appropriatly tagged. If the tag is HEAD, then there will be no tag on the resources (same as -A
* clear sticky option).
*
*/
public void update(IResource[] resources, LocalOption[] options, CVSTag tag, ResponseHandler handler, IProgressMonitor progress) throws TeamException {
// Build the local options
List localOptions = new ArrayList();
// Use the appropriate tag options
if (tag != null) {
localOptions.add(Update.makeTagOption(tag));
}
// save old handler, to be reset after command is run
ResponseHandler oldHandler = null;
if(handler!=null) {
oldHandler = Command.getResponseHandler(handler.getResponseID());
Command.registerResponseHandler(handler);
}
// Build the arguments list
localOptions.addAll(Arrays.asList(options));
LocalOption[] commandOptions = (LocalOption[])localOptions.toArray(new LocalOption[localOptions.size()]);
String[] arguments = getValidArguments(resources, commandOptions);
IStatus status;
Session s = new Session(workspaceRoot.getRemoteLocation(), workspaceRoot.getLocalRoot());
progress.beginTask(null, 100);
try {
// Opening the session takes 20% of the time
s.open(Policy.subMonitorFor(progress, 20));
status = Command.UPDATE.execute(s,
Command.NO_GLOBAL_OPTIONS,
commandOptions,
arguments,
null,
Policy.subMonitorFor(progress, 80));
} finally {
progress.done();
s.close();
if(oldHandler!=null) {
Command.registerResponseHandler(oldHandler);
}
}
if (status.getCode() == CVSStatus.SERVER_ERROR) {
// XXX diff errors??
throw new CVSServerException(status);
}
}
public static String getMessageFor(Exception e) {
String message = Policy.bind(e.getClass().getName(), new Object[] {e.getMessage()});
if (message.equals(e.getClass().getName()))
message = Policy.bind("CVSTeamProvider.exception", new Object[] {e.toString()}); //$NON-NLS-1$
return message;
}
/*
* @see ITeamProvider#validateEdit(IFile[], Object)
*/
public IStatus validateEdit(IFile[] files, Object context) {
//todo: we can assume that there is only one file due to the way FileModificationValidator is implemented
return
(files[0].isReadOnly())
? new CVSStatus(CVSStatus.ERROR, Policy.bind("FileModificationValidator.isReadOnly")) //$NON-NLS-1$
: new CVSStatus(CVSStatus.OK, Policy.bind("ok")); //$NON-NLS-1$
}
/*
* @see ITeamProvider#validateSave(IFile)
*/
public IStatus validateSave(IFile file) {
return new CVSStatus(CVSStatus.OK, Policy.bind("ok")); //$NON-NLS-1$
}
/*
* @see ITeamProvider#refreshState(IResource[], int, IProgressMonitor)
*/
public void refreshState(IResource[] resources, int depth, IProgressMonitor progress) throws TeamException {
Assert.isTrue(false);
}
/*
* @see ITeamProvider#isOutOfDate(IResource)
* XXX to be removed when sync methods are removed from ITeamProvider
*/
public boolean isOutOfDate(IResource resource) {
Assert.isTrue(false);
return false;
}
/*
* @see ITeamProvider#isDirty(IResource)
*/
public boolean isDirty(IResource resource) {
Assert.isTrue(false);
return false;
}
public CVSWorkspaceRoot getCVSWorkspaceRoot() {
return workspaceRoot;
}
/*
* Generate an exception if the resource is not a child of the project
*/
private void checkIsChild(IResource resource) throws CVSException {
if (!isChildResource(resource))
throw new CVSException(new Status(IStatus.ERROR, CVSProviderPlugin.ID, TeamException.UNABLE,
Policy.bind("CVSTeamProvider.invalidResource", //$NON-NLS-1$
new Object[] {resource.getFullPath().toString(), project.getName()}),
null));
}
/*
* Get the arguments to be passed to a commit or update
*/
private String[] getValidArguments(IResource[] resources, LocalOption[] options) throws CVSException {
int depth = Command.DO_NOT_RECURSE.isElementOf(options) ? IResource.DEPTH_ZERO : IResource.DEPTH_INFINITE;
List arguments = new ArrayList(resources.length);
for (int i=0;i<resources.length;i++) {
checkIsChild(resources[i]);
// A depth of zero is only valid for files
if ((depth != IResource.DEPTH_ZERO) || (resources[i].getType() == IResource.FILE)) {
IPath cvsPath = resources[i].getFullPath().removeFirstSegments(1);
if (cvsPath.segmentCount() == 0) {
arguments.add(Session.CURRENT_LOCAL_FOLDER);
}
else
arguments.add(cvsPath.toString());
}
}
return (String[])arguments.toArray(new String[arguments.size()]);
}
/*
* This method expects to be passed an InfiniteSubProgressMonitor
*/
private void setRemoteRoot(ICVSRepositoryLocation location, final IProgressMonitor monitor) throws TeamException {
// Check if there is a differnece between the new and old roots
final String root = location.getLocation();
if (root.equals(workspaceRoot.getRemoteLocation()))
return;
try {
// 256 ticks gives us a maximum of 1024 which seems reasonable for folders is a project
monitor.beginTask(Policy.bind("CVSTeamProvider.folderInfo", project.getName()), 256); //$NON-NLS-1$
// Visit all the children folders in order to set the root in the folder sync info
workspaceRoot.getLocalRoot().accept(new ICVSResourceVisitor() {
public void visitFile(ICVSFile file) throws CVSException {};
public void visitFolder(ICVSFolder folder) throws CVSException {
monitor.worked(1);
FolderSyncInfo info = folder.getFolderSyncInfo();
if (info != null) {
monitor.subTask(Policy.bind("CVSTeamProvider.updatingFolder", info.getRepository())); //$NON-NLS-1$
folder.setFolderSyncInfo(new FolderSyncInfo(info.getRepository(), root, info.getTag(), info.getIsStatic()));
folder.acceptChildren(this);
}
};
});
} finally {
monitor.done();
}
}
/*
* Helper to indicate if the resource is a child of the receiver's project
*/
private boolean isChildResource(IResource resource) {
return resource.getProject().getName().equals(project.getName());
}
private static TeamException wrapException(CoreException e) {
return new TeamException(statusFor(e));
}
private static IStatus statusFor(CoreException e) {
// We should be taking out any status from the CVSException
// and creating an array of IStatus!
return new Status(IStatus.ERROR, CVSProviderPlugin.ID, TeamException.UNABLE, getMessageFor(e), e);
}
}