blob: 27d518c7297691e08cb9ef77556da03f97e908d1 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2003 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.team.internal.ccvs.ui.operations;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.team.core.RepositoryProvider;
import org.eclipse.team.core.TeamException;
import org.eclipse.team.internal.ccvs.core.CVSException;
import org.eclipse.team.internal.ccvs.core.CVSProviderPlugin;
import org.eclipse.team.internal.ccvs.core.CVSStatus;
import org.eclipse.team.internal.ccvs.core.CVSTag;
import org.eclipse.team.internal.ccvs.core.CVSTeamProvider;
import org.eclipse.team.internal.ccvs.core.ICVSFile;
import org.eclipse.team.internal.ccvs.core.ICVSFolder;
import org.eclipse.team.internal.ccvs.core.ICVSRemoteFolder;
import org.eclipse.team.internal.ccvs.core.ICVSRepositoryLocation;
import org.eclipse.team.internal.ccvs.core.ICVSResource;
import org.eclipse.team.internal.ccvs.core.ICVSResourceVisitor;
import org.eclipse.team.internal.ccvs.core.client.Checkout;
import org.eclipse.team.internal.ccvs.core.client.Command;
import org.eclipse.team.internal.ccvs.core.client.Request;
import org.eclipse.team.internal.ccvs.core.client.Session;
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.resources.CVSWorkspaceRoot;
import org.eclipse.team.internal.ccvs.core.syncinfo.FolderSyncInfo;
import org.eclipse.team.internal.ccvs.ui.CVSUIPlugin;
import org.eclipse.team.internal.ccvs.ui.Policy;
/**
* This method checks out one or more remote folders from the same repository
* into an existing project or folder in the workspace. The target project
* must either be shared with the same repository or it must not be shared
* with any repository
*/
public class CheckoutIntoOperation extends CheckoutOperation {
private boolean recursive;
private ICVSFolder localFolder;
private String localFolderName;
/**
* Constructor which takes a set of remote folders and the local folder into which the folders should be
* loaded.
*/
public CheckoutIntoOperation(Shell shell, ICVSRemoteFolder[] remoteFolders, IContainer localFolder, boolean recursive) {
super(shell, remoteFolders);
this.recursive = recursive;
this.localFolder = CVSWorkspaceRoot.getCVSFolderFor(localFolder);
}
/**
* Constructor which takes a single remote folder and the target local folder.
*/
public CheckoutIntoOperation(Shell shell, ICVSRemoteFolder remoteFolder, IContainer localFolder, boolean recursive) {
this(shell, new ICVSRemoteFolder[] { remoteFolder }, localFolder.getParent(), recursive);
this.localFolderName = localFolder.getName();
}
/* (non-Javadoc)
* @see org.eclipse.team.internal.ccvs.ui.operations.CheckoutOperation#getTaskName()
*/
protected String getTaskName() {
ICVSRemoteFolder[] remoteFolders = getRemoteFolders();
String localFolderName = ""; //$NON-NLS-1$
try {
localFolderName = getLocalFolder().getIResource().getFullPath().toString();
} catch (CVSException e) {
CVSUIPlugin.log(e);
}
return Policy.bind("CheckoutIntoOperation.taskname", new Integer(remoteFolders.length).toString(), localFolderName); //$NON-NLS-1$
}
/**
* @return
*/
public ICVSFolder getLocalFolder() {
return localFolder;
}
/**
* @return
*/
public boolean isRecursive() {
return recursive;
}
/* (non-Javadoc)
* @see org.eclipse.team.internal.ccvs.ui.operations.CheckoutOperation#checkout(org.eclipse.team.internal.ccvs.core.ICVSRemoteFolder, org.eclipse.core.runtime.IProgressMonitor)
*/
protected IStatus checkout(ICVSRemoteFolder folder, IProgressMonitor monitor) throws CVSException {
return checkout(folder, getLocalFolder(), isRecursive(), monitor);
}
/* (non-Javadoc)
* @see org.eclipse.team.internal.ccvs.ui.operations.CheckoutOperation#checkout(org.eclipse.team.internal.ccvs.core.ICVSRemoteFolder[], org.eclipse.core.runtime.IProgressMonitor)
*/
protected void checkout(ICVSRemoteFolder[] folders, IProgressMonitor monitor) throws CVSException {
monitor.beginTask(null, 100);
ISchedulingRule rule = getSchedulingRule();
try {
// Obtain a scheduling rule on the projects were about to overwrite
Platform.getJobManager().beginRule(rule);
super.checkout(folders, Policy.subMonitorFor(monitor, 90));
refreshRoot(getLocalRoot(getLocalFolder()), Policy.subMonitorFor(monitor, 10));
} finally {
Platform.getJobManager().endRule(rule);
monitor.done();
}
}
/*
* Prepare the local folders to receive the remote folders. If localFolderName is not null, then
* if will be the only target folder of the checkout. Otherwise, the remote folder
* could expand to multiple local folders witinb the given parent folder.
*/
private ICVSFolder[] prepareLocalFolders(Session session, ICVSRemoteFolder remoteFolder, ICVSFolder parentFolder, String localFolderName, IProgressMonitor monitor) throws CVSException {
Set targetFolderSet = new HashSet();
monitor.beginTask(null, 30);
if (localFolderName == null) {
// Determine which local folders will be afected
IStatus status = Request.EXPAND_MODULES.execute(session, new String[] { remoteFolder.getRepositoryRelativePath()}, Policy.subMonitorFor(monitor, 10));
if (status.getCode() == CVSStatus.SERVER_ERROR) {
addError(status);
return null;
}
// Convert the module expansions to target folders
String[] expansions = session.getModuleExpansions();
for (int j = 0; j < expansions.length; j++) {
String childPath = new Path(expansions[j]).segment(0);
ICVSResource resource = parentFolder.getChild(childPath);
if (resource != null && !resource.isFolder()) {
// The target folder conflicts with an existing file
addError(new CVSStatus(IStatus.ERROR, Policy.bind("CheckoutIntoOperation.targetIsFile", remoteFolder.getName(), resource.getIResource().getFullPath().toString()))); //$NON-NLS-1$
return null;
}
targetFolderSet.add(parentFolder.getFolder(childPath));
}
} else {
targetFolderSet.add(parentFolder.getFolder(localFolderName));
}
final ICVSFolder[] targetFolders = (ICVSFolder[]) targetFolderSet.toArray(new ICVSFolder[targetFolderSet.size()]);
// Ensure that the checkout will not conflict with existing resources
IStatus status = validateTargetFolders(remoteFolder, targetFolders, Policy.subMonitorFor(monitor, 10));
if (!status.isOK()) {
addError(status);
return null;
}
// Prepare the target projects to receive resources
status = scrubFolders(remoteFolder, targetFolders, Policy.subMonitorFor(monitor, 10));
// return the target projects if the scrub succeeded
if (status.isOK()) {
return targetFolders;
} else {
addError(status);
return null;
}
}
/*
* Ensure that the new folders will not conflict with existing folders (even those that are pruned).
*/
private IStatus validateTargetFolders(ICVSRemoteFolder remoteFolder, ICVSFolder[] targetFolders, IProgressMonitor monitor) throws CVSException {
for (int i = 0; i < targetFolders.length; i++) {
ICVSFolder targetFolder = targetFolders[i];
FolderSyncInfo localInfo = targetFolder.getFolderSyncInfo();
FolderSyncInfo remoteInfo = remoteFolder.getFolderSyncInfo();
if (!remoteInfo.isSameMapping(localInfo)) {
if (localInfo != null ) {
if (isRemoteChildOfParent(targetFolder)) {
// if the local folder is child of it's parent remotely (i.e. path of child is parent/child)
// then the remote cannot be loaded.
String message;
if (targetFolder.exists()) {
message = Policy.bind("CheckoutIntoOperation.targetIsFolder", remoteFolder.getName(), targetFolder.getIResource().getFullPath().toString()); //$NON-NLS-1$
} else {
message = Policy.bind("CheckoutIntoOperation.targetIsPrunedFolder", remoteFolder.getName(), targetFolder.getFolderSyncInfo().getRepository()); //$NON-NLS-1$
}
return new CVSStatus(IStatus.ERROR, message);
}
}
// Verify that no other folders in the local workspace are mapped to the remote folder
IStatus status = validateUniqueMapping(remoteFolder, targetFolder, Policy.subMonitorFor(monitor, 10));
if (!status.isOK()) return status;
}
}
return OK;
}
/*
* Return true if the given local folder is a direct descendant of it's local parent in
* the repository as well
*/
private boolean isRemoteChildOfParent(ICVSFolder targetFolder) throws CVSException {
FolderSyncInfo localInfo = targetFolder.getFolderSyncInfo();
if (localInfo == null) return false;
FolderSyncInfo parentInfo = targetFolder.getParent().getFolderSyncInfo();
if (parentInfo == null) return false;
IPath childPath = new Path(localInfo.getRepository());
IPath parentPath = new Path(parentInfo.getRepository());
return parentPath.isPrefixOf(childPath);
}
/**
* @param targetFolder
* @return
*/
private IContainer getLocalRoot(ICVSFolder targetFolder) throws CVSException {
return targetFolder.getIResource().getProject();
}
/*
* Ensure that there is no equivalent mapping alreay in the local workspace
*/
private IStatus validateUniqueMapping(final ICVSRemoteFolder remoteFolder, final ICVSFolder targetFolder, IProgressMonitor iProgressMonitor) throws CVSException {
final IContainer root = getLocalRoot(targetFolder);
final FolderSyncInfo remoteInfo = remoteFolder.getFolderSyncInfo();
if (remoteInfo.equals(FolderSyncInfo.VIRTUAL_DIRECTORY)) {
// We can't really check the mapping ahead of time
// so we'll let the operation continue
return OK;
}
ICVSFolder cvsFolder = CVSWorkspaceRoot.getCVSFolderFor(root);
try {
cvsFolder.accept(new ICVSResourceVisitor() {
public void visitFile(ICVSFile file) throws CVSException {
// do nothing
}
public void visitFolder(ICVSFolder folder) throws CVSException {
if (!folder.isCVSFolder()) return;
IResource resource = folder.getIResource();
if (resource == null) return;
FolderSyncInfo info = folder.getFolderSyncInfo();
if (info.isSameMapping(remoteInfo)) {
throw new CVSException(Policy.bind("CheckoutIntoOperation.mappingAlreadyExists", //$NON-NLS-1$
new Object[] {
remoteFolder.getName(),
targetFolder.getIResource().getFullPath().toString(),
resource.getFullPath().toString()
}));
}
folder.acceptChildren(this);
}
});
} catch (CVSException e) {
return e.getStatus();
}
return OK;
}
/*
* Purge the local contents of the given folders
*/
private IStatus scrubFolders(ICVSRemoteFolder remoteFolder, ICVSFolder[] targetFolders, IProgressMonitor monitor) throws CVSException {
monitor.beginTask(null, 100 * targetFolders.length);
// Prompt first before any work is done
if (targetFolders.length > 1) {
setInvolvesMultipleResources(true);
}
for (int i=0;i<targetFolders.length;i++) {
ICVSFolder targetFolder = targetFolders[i];
if (needsPromptForOverwrite(targetFolder, Policy.subMonitorFor(monitor, 50)) && !promptToOverwrite(targetFolder)) {
return new CVSStatus(IStatus.INFO, Policy.bind("CheckoutIntoOperation.cancelled", remoteFolder.getName())); //$NON-NLS-1$
}
}
for (int i = 0; i < targetFolders.length; i++) {
IStatus status = scrubFolder(targetFolders[i], Policy.subMonitorFor(monitor, 50));
if (!status.isOK()) return status;
}
monitor.done();
return OK;
}
private boolean needsPromptForOverwrite(ICVSFolder targetFolder, IProgressMonitor monitor) throws CVSException {
return targetFolder.isModified(monitor);
}
private boolean promptToOverwrite(ICVSFolder folder) {
return promptToOverwrite(
Policy.bind("CheckoutOperation.confirmOverwrite"), //$NON-NLS-1$
Policy.bind("CheckoutIntoOperation.overwriteMessage", folder.getName())); //$NON-NLS-1$
}
private IStatus scrubFolder(ICVSFolder folder, IProgressMonitor monitor) throws CVSException {
if (folder.exists() || folder.isCVSFolder()) {
// Unmanage first so we don't get outgoing deletions
folder.unmanage(Policy.subMonitorFor(monitor, 50));
if (folder.exists()) folder.delete();
}
return OK;
}
private IStatus checkout(final ICVSRemoteFolder remoteFolder, ICVSFolder parentFolder, boolean recurse, IProgressMonitor monitor) throws CVSException {
// Open a connection session to the repository
monitor.beginTask(null, 100);
ICVSRepositoryLocation repository = remoteFolder.getRepository();
Session session = new Session(repository, parentFolder);
try {
session.open(Policy.subMonitorFor(monitor, 5), false /* read-only */);
// Determine which local folders will be affected
ICVSFolder[] targetFolders = prepareLocalFolders(session, remoteFolder, parentFolder, localFolderName, Policy.subMonitorFor(monitor, 5));
if (targetFolders == null) {
// an error occured and has been added to the operation's error list
return getLastError();
}
// Add recurse option
List localOptions = new ArrayList();
if (!recurse)
localOptions.add(Update.DO_NOT_RECURSE);
if (localFolderName != null) {
localOptions.add(Checkout.makeDirectoryNameOption(localFolderName));
}
// Prune empty directories if pruning enabled
if (CVSProviderPlugin.getPlugin().getPruneEmptyDirectories())
localOptions.add(Checkout.PRUNE_EMPTY_DIRECTORIES);
// Add the options related to the CVSTag
CVSTag tag = remoteFolder.getTag();
if (tag == null) {
// A null tag in a remote resource indicates HEAD
tag = CVSTag.DEFAULT;
}
localOptions.add(Update.makeTagOption(tag));
// Perform the checkout
IStatus status = Command.CHECKOUT.execute(session,
Command.NO_GLOBAL_OPTIONS,
(LocalOption[])localOptions.toArray(new LocalOption[localOptions.size()]),
new String[] { remoteFolder.getRepositoryRelativePath() },
null,
Policy.subMonitorFor(monitor, 80));
if (!status.isOK()) {
return status;
}
manageFolders(targetFolders, repository.getLocation());
return OK;
} finally {
session.close();
}
}
private void manageFolders(ICVSFolder[] targetFolders, String root) throws CVSException {
for (int i = 0; i < targetFolders.length; i++) {
manageFolder(targetFolders[i], root);
}
}
private static void manageFolder(ICVSFolder folder, String root) throws CVSException {
// Ensure that the parent is a CVS folder
ICVSFolder parent = folder.getParent();
if (!parent.isCVSFolder()) {
parent.setFolderSyncInfo(new FolderSyncInfo(FolderSyncInfo.VIRTUAL_DIRECTORY, root, CVSTag.DEFAULT, true));
IResource resource = parent.getIResource();
if (resource.getType() != IResource.PROJECT) {
manageFolder(parent, root);
}
}
// reset the folder sync info so it will be managed by it's parent
folder.setFolderSyncInfo(folder.getFolderSyncInfo());
}
/*
* Bring the provided projects into the workspace
*/
private static void refreshRoot(IContainer root, IProgressMonitor monitor) throws CVSException {
try {
IProject project = root.getProject();
CVSTeamProvider provider = (CVSTeamProvider)RepositoryProvider.getProvider(project, CVSProviderPlugin.getTypeId());
if (provider == null) {
// Register the project with Team
RepositoryProvider.map(project, CVSProviderPlugin.getTypeId());
// TODO: This should be somewhere else
provider = (CVSTeamProvider)RepositoryProvider.getProvider(project, CVSProviderPlugin.getTypeId());
provider.setWatchEditEnabled(CVSProviderPlugin.getPlugin().isWatchEditEnabled());
}
} catch (TeamException e) {
throw CVSException.wrapException(e);
}
}
public String getName() {
return getTaskName();
}
/* (non-Javadoc)
* @see org.eclipse.team.internal.ccvs.ui.operations.CVSOperation#getSchedulingRule()
*/
protected ISchedulingRule getSchedulingRule() {
try {
// Use the project of the target folder as the scheduling rule
return getLocalFolder().getIResource().getProject();
} catch (CVSException e) {
CVSUIPlugin.log(e);
return null;
}
}
}