blob: 3d2c3a98672804e8f083ca28dc3feaabb8dab405 [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.io.File;
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.IProjectDescription;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
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.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.core.runtime.jobs.MultiRule;
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.ICVSFolder;
import org.eclipse.team.internal.ccvs.core.ICVSRemoteFolder;
import org.eclipse.team.internal.ccvs.core.ICVSRepositoryLocation;
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.ui.Policy;
/**
* This class acts as an abstract class for checkout operations.
* It provides a few common methods.
*/
public abstract class CheckoutProjectOperation extends CheckoutOperation {
private String targetLocation;
/**
* @param shell
* @param targetLocation
*/
public CheckoutProjectOperation(Shell shell, ICVSRemoteFolder[] remoteFolders, String targetLocation) {
super(shell, remoteFolders);
this.targetLocation = targetLocation;
}
/**
* Create and open the project, using a custom location if there is one.
*
* @param project
* @param monitor
* @throws CVSException
*/
protected void createAndOpenProject(IProject project, IProgressMonitor monitor) throws CVSException {
try {
monitor.beginTask(null, 5);
IProjectDescription desc = getDescriptionFor(project);
if (project.exists()) {
if (desc != null) {
project.move(desc, true, Policy.subMonitorFor(monitor, 3));
}
} else {
if (desc == null) {
// create in default location
project.create(Policy.subMonitorFor(monitor, 3));
} else {
// create in some other location
project.create(desc, Policy.subMonitorFor(monitor, 3));
}
}
if (!project.isOpen()) {
project.open(Policy.subMonitorFor(monitor, 2));
}
} catch (CoreException e) {
throw CVSException.wrapException(e);
} finally {
monitor.done();
}
}
protected IProjectDescription getDescriptionFor(IProject project) {
if (targetLocation == null) return null;
String projectName = project.getName();
IProjectDescription description = ResourcesPlugin.getWorkspace().newProjectDescription(projectName);
description.setLocation(getTargetLocationFor(project));
return description;
}
/**
* Return the target location where the given project should be located or
* null if the default location should be used.
*
* @param project
*/
protected IPath getTargetLocationFor(IProject project) {
if (targetLocation == null) return null;
return new Path(targetLocation);
}
protected String getRemoteModuleName(ICVSRemoteFolder resource) {
String moduleName;
if (resource.isDefinedModule()) {
moduleName = resource.getName();
} else {
moduleName = resource.getRepositoryRelativePath();
}
return moduleName;
}
protected IStatus checkout(ICVSRemoteFolder resource, IProject project, IProgressMonitor pm) throws CVSException {
// Get the location and the workspace root
ICVSFolder root = CVSWorkspaceRoot.getCVSFolderFor(ResourcesPlugin.getWorkspace().getRoot());
ICVSRepositoryLocation repository = resource.getRepository();
// Open a connection session to the repository
Session session = new Session(repository, root);
pm.beginTask(null, 100);
session.open(Policy.subMonitorFor(pm, 5), false /* read-only */);
try {
// Determine the local target projects (either the project provider or the module expansions)
// Note: Module expansions can be run over the same connection as a checkout
IProject[] targetProjects = determineProjects(session, resource, project, Policy.subMonitorFor(pm, 5));
if (targetProjects == null) {
// An error occurred and was recorded so return it
return getLastError();
} else if (targetProjects.length == 0) {
return OK;
}
ISchedulingRule rule = getSchedulingRule(targetProjects);
try {
// Obtain a scheduling rule on the projects were about to overwrite
Platform.getJobManager().beginRule(rule);
IStatus result = performCheckout(session, resource, targetProjects, project != null, Policy.subMonitorFor(pm, 90));
return result;
} finally {
Platform.getJobManager().endRule(rule);
}
} catch (CVSException e) {
// An exception occurred either during the module-expansion or checkout
// Since we were able to make a connection, return the status so the
// checkout of any other modules can proceed
return e.getStatus();
} finally {
session.close();
pm.done();
}
}
private ISchedulingRule getSchedulingRule(IProject[] projects) {
if (projects.length == 1) {
return projects[0];
} else {
return new MultiRule(projects);
}
}
private IStatus performCheckout(Session session, ICVSRemoteFolder resource, IProject[] targetProjects, boolean sendModuleName, IProgressMonitor pm) throws CVSException {
// Set the task name of the progress monitor to let the user know
// which project we're on. Don't use subTask since that will be
// changed when the checkout command is run.
String taskName;
if (targetProjects.length == 1) {
taskName = Policy.bind("CheckoutProjectOperation.8", resource.getName(), targetProjects[0].getName()); //$NON-NLS-1$
} else {
taskName = Policy.bind("CheckoutProjectOperation.9", resource.getName(), String.valueOf(targetProjects.length)); //$NON-NLS-1$
}
pm.beginTask(taskName, 100);
pm.setTaskName(taskName);
try {
// Scrub the local contents if requested
if (performScrubProjects()) {
IStatus result = scrubProjects(resource, targetProjects, Policy.subMonitorFor(pm, 9));
if (!result.isOK()) {
return result;
}
}
// Determine if the target project is the same name as the remote folder
// in which case we'll use -d to flatten the directory structure
IProject project;
if (targetProjects.length == 1 && (sendModuleName || targetProjects[0].getName().equals(resource.getName()))) {
project = targetProjects[0];
} else {
project = null;
}
// Build the local options
List localOptions = new ArrayList();
// Add the option to load into the target project if one was supplied
if (project != null) {
localOptions.add(Checkout.makeDirectoryNameOption(project.getName()));
}
// 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 = resource.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[]{getRemoteModuleName(resource)},
null,
Policy.subMonitorFor(pm, 90));
if (status.getCode() == CVSStatus.SERVER_ERROR) {
// Any created projects will exist but will not be mapped to CVS
return status;
}
// Bring the project into the workspace
refreshProjects(targetProjects, Policy.subMonitorFor(pm, 1));
return OK;
} finally {
pm.done();
}
}
/*
* Determine the workspace project(s) that will be affected by the checkout.
* If project is not null, then it will be the only target project of the checkout.
* Otherwise, the remote folder could expand to multiple projects.
*
* If the remote resource is a folder which is not a root folder (i.e. a/b/c),
* then the target project will be the last segment (i.e. c).
*/
private IProject[] determineProjects(Session session, final ICVSRemoteFolder remoteFolder, IProject project, IProgressMonitor pm) throws CVSException {
Set targetProjectSet = new HashSet();
String moduleName = getRemoteModuleName(remoteFolder);
if (project == null) {
// Fetch the module expansions
IStatus status = Request.EXPAND_MODULES.execute(session, new String[] {moduleName}, pm);
if (status.getCode() == CVSStatus.SERVER_ERROR) {
collectStatus(status);
return null;
}
// Convert the module expansions to local projects
String[] expansions = session.getModuleExpansions();
if (expansions.length == 1 && expansions[0].equals(moduleName)) {
// For a remote folder, use the last segment as the project to be created
String lastSegment = new Path(expansions[0]).lastSegment();
targetProjectSet.add(ResourcesPlugin.getWorkspace().getRoot().getProject(lastSegment));
} else {
for (int j = 0; j < expansions.length; j++) {
targetProjectSet.add(ResourcesPlugin.getWorkspace().getRoot().getProject(new Path(expansions[j]).segment(0)));
}
}
} else {
targetProjectSet.add(project);
}
// Return the local projects affected by the checkout
IProject[] targetProjects = (IProject[]) targetProjectSet.toArray(new IProject[targetProjectSet.size()]);
return targetProjects;
}
/**
* Return true if the target projects should be scrubbed before the checkout occurs.
* Default is to scrub the projects. Can be overridden by subclasses.
*/
protected boolean performScrubProjects() {
return true;
}
/*
* This method is invoked to scrub the local projects that are the check out target of
* a single remote module.
*/
private IStatus scrubProjects(ICVSRemoteFolder remoteFolder, IProject[] projects, IProgressMonitor monitor) throws CVSException {
if (projects == null) {
monitor.done();
return OK;
}
// Prompt first before any work is done
if (projects.length > 1) {
setInvolvesMultipleResources(true);
}
for (int i=0;i<projects.length;i++) {
IProject project = projects[i];
if (needsPromptForOverwrite(project) && !promptToOverwrite(remoteFolder, project)) {
Policy.cancelOperation();
}
}
// Create the projects and remove any previous content
monitor.beginTask(null, projects.length * 100); //$NON-NLS-1$
for (int i=0;i<projects.length;i++) {
IProject project = projects[i];
createAndOpenProject(project, Policy.subMonitorFor(monitor, 10));
scrubProject(project, Policy.subMonitorFor(monitor, 90));
}
monitor.done();
return OK;
}
private void scrubProject(IProject project, IProgressMonitor monitor) throws CVSException {
try {
// unmap the project from any previous repository provider
if (RepositoryProvider.getProvider(project) != null)
RepositoryProvider.unmap(project);
// We do not want to delete the project to avoid a project deletion delta
// We do not want to delete the .project to avoid core exceptions
IResource[] children = project.members(IContainer.INCLUDE_TEAM_PRIVATE_MEMBERS);
monitor.beginTask(null, children.length * 100);
monitor.subTask(Policy.bind("CheckoutOperation.scrubbingProject", project.getName())); //$NON-NLS-1$
try {
for (int j = 0; j < children.length; j++) {
if ( ! children[j].getName().equals(".project")) {//$NON-NLS-1$
children[j].delete(true /*force*/, Policy.subMonitorFor(monitor, 100));
}
}
} finally {
monitor.done();
}
} catch (TeamException e) {
throw CVSException.wrapException(e);
} catch (CoreException e) {
throw CVSException.wrapException(e);
}
}
protected boolean needsPromptForOverwrite(IProject project) {
// First, check the description location
IProjectDescription desc = getDescriptionFor(project);
if (desc != null) {
File localLocation = desc.getLocation().toFile();
return localLocation.exists();
}
// Next, check if the resource itself exists
if (project.exists()) return true;
// Finally, check if the location in the workspace exists;
File localLocation = getFileLocation(project);
if (localLocation.exists()) return true;
// The target doesn't exist
return false;
}
protected File getFileLocation(IProject project) {
return new File(project.getParent().getLocation().toFile(), project.getName());
}
/**
* @param project
* @return
*/
private boolean promptToOverwrite(ICVSRemoteFolder remoteFolder, IProject project) {
return promptToOverwrite(Policy.bind("CheckoutOperation.confirmOverwrite"), getOverwritePromptMessage(remoteFolder, project)); //$NON-NLS-1$
}
protected String getOverwritePromptMessage(ICVSRemoteFolder remoteFolder, IProject project) {
File localLocation = getFileLocation(project);
if(project.exists()) {
return Policy.bind("CheckoutOperation.thisResourceExists", project.getName(), getRemoteModuleName(remoteFolder));//$NON-NLS-1$
} else {
return Policy.bind("CheckoutOperation.thisExternalFileExists", project.getName(), getRemoteModuleName(remoteFolder));//$NON-NLS-1$
}
}
/*
* Bring the provied projects into the workspace
*/
private void refreshProjects(IProject[] projects, IProgressMonitor monitor) throws CVSException {
monitor.beginTask(null, projects.length * 100);
try {
for (int i = 0; i < projects.length; i++) {
IProject project = projects[i];
// Register the project with Team
try {
monitor.subTask(Policy.bind("CheckoutOperation.refreshingProject", project.getName())); //$NON-NLS-1$
RepositoryProvider.map(project, CVSProviderPlugin.getTypeId());
} catch (TeamException e) {
throw CVSException.wrapException(e);
}
CVSTeamProvider provider = (CVSTeamProvider)RepositoryProvider.getProvider(project, CVSProviderPlugin.getTypeId());
provider.setWatchEditEnabled(CVSProviderPlugin.getPlugin().isWatchEditEnabled());
}
} finally {
monitor.done();
}
}
protected String getTaskName() {
ICVSRemoteFolder[] remoteFolders = getRemoteFolders();
if (remoteFolders.length == 1) {
return Policy.bind("CheckoutSingleProjectOperation.taskname", remoteFolders[0].getName()); //$NON-NLS-1$
} else {
return Policy.bind("CheckoutMultipleProjectsOperation.taskName", new Integer(remoteFolders.length).toString()); //$NON-NLS-1$
}
}
}