blob: c5789642c43e4286b90401fde950389520553cf3 [file] [log] [blame]
/**********************************************************************
Copyright (c) 2004 Dan Rubel 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:
Dan Rubel - initial API and implementation
**********************************************************************/
package org.eclipse.team.internal.ccvs.core;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.core.runtime.jobs.MultiRule;
import org.eclipse.team.core.ProjectSetCapability;
import org.eclipse.team.core.ProjectSetSerializationContext;
import org.eclipse.team.core.RepositoryProvider;
import org.eclipse.team.core.TeamException;
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.connection.CVSRepositoryLocation;
import org.eclipse.team.internal.ccvs.core.connection.CVSServerException;
import org.eclipse.team.internal.ccvs.core.resources.CVSWorkspaceRoot;
import org.eclipse.team.internal.ccvs.core.resources.RemoteFolder;
import org.eclipse.team.internal.ccvs.core.resources.RemoteModule;
import org.eclipse.team.internal.ccvs.core.syncinfo.FolderSyncInfo;
/**
* An object for serializing and deserializing
* of references to CVS based projects. Given a project, it can produce a
* UTF-8 encoded String which can be stored in a file.
* Given this String, it can load a project into the workspace.
*
* @since 3.0
*/
public class CVSProjectSetCapability extends ProjectSetCapability {
/**
* Override superclass implementation to return an array of project references.
*
* @see ProjectSetSerializer#asReference(IProject[], ProjectSetSerializationContext, IProgressMonitor)
*/
public String[] asReference(
IProject[] projects,
ProjectSetSerializationContext context,
IProgressMonitor monitor)
throws TeamException {
String[] result = new String[projects.length];
for (int i = 0; i < projects.length; i++)
result[i] = asReference(projects[i]);
return result;
}
/**
* Answer a string representing the specified project
*
* @param project the project (not <code>null</code>)
* @return the project reference (not <code>null</code>)
* @throws CVSException
*/
private String asReference(IProject project) throws TeamException {
StringBuffer buffer = new StringBuffer();
buffer.append("1.0,"); //$NON-NLS-1$
CVSTeamProvider provider = (CVSTeamProvider)RepositoryProvider.getProvider(project);
CVSWorkspaceRoot root = provider.getCVSWorkspaceRoot();
CVSRepositoryLocation location = CVSRepositoryLocation.fromString(root.getRemoteLocation().getLocation());
location.setUserMuteable(true);
String repoLocation = location.getLocation();
buffer.append(repoLocation);
buffer.append(","); //$NON-NLS-1$
ICVSFolder folder = root.getLocalRoot();
FolderSyncInfo syncInfo = folder.getFolderSyncInfo();
String module = syncInfo.getRepository();
buffer.append(module);
buffer.append(","); //$NON-NLS-1$
String projectName = folder.getName();
buffer.append(projectName);
CVSTag tag = syncInfo.getTag();
if (tag != null) {
if (tag.getType() != CVSTag.DATE) {
buffer.append(","); //$NON-NLS-1$
String tagName = tag.getName();
buffer.append(tagName);
}
}
return buffer.toString();
}
/**
* Override superclass implementation to load the referenced projects into the workspace.
*
* @see org.eclipse.team.core.ProjectSetSerializer#addToWorkspace(java.lang.String[], org.eclipse.team.core.ProjectSetSerializationContext, org.eclipse.core.runtime.IProgressMonitor)
*/
public IProject[] addToWorkspace(
String[] referenceStrings,
ProjectSetSerializationContext context,
IProgressMonitor monitor)
throws TeamException {
monitor = Policy.monitorFor(monitor);
Policy.checkCanceled(monitor);
// Confirm the projects to be loaded
Map infoMap = new HashMap(referenceStrings.length);
IProject[] projects = asProjects(referenceStrings, infoMap);
projects = confirmOverwrite(context, projects);
if (projects == null)
return new IProject[0];
// Load the projects
return checkout(projects, infoMap, monitor);
}
/**
* Translate the reference strings into projects to be loaded
* and build a mapping of project to project load information.
*
* @param referenceStrings project references
* @param infoMap a mapping of project to project load information
* @return the projects to be loaded
*/
private IProject[] asProjects(String[] referenceStrings, Map infoMap) throws CVSException {
Collection result = new ArrayList();
for (int i = 0; i < referenceStrings.length; i++) {
StringTokenizer tokenizer = new StringTokenizer(referenceStrings[i], ","); //$NON-NLS-1$
String version = tokenizer.nextToken();
// If this is a newer version, then ignore it
if (!version.equals("1.0")) //$NON-NLS-1$
continue;
LoadInfo info = new LoadInfo(tokenizer);
IProject proj = info.getProject();
result.add(proj);
infoMap.put(proj, info);
}
return (IProject[]) result.toArray(new IProject[result.size()]);
}
/**
* Checkout projects from the CVS repository
*
* @param projects the projects to be loaded from the repository
* @param infoMap a mapping of project to project load information
* @param monitor the progress monitor (not <code>null</code>)
*/
private IProject[] checkout(
IProject[] projects,
Map infoMap,
IProgressMonitor monitor)
throws TeamException {
monitor.beginTask("", 1000 * projects.length); //$NON-NLS-1$
List result = new ArrayList();
try {
for (int i = 0; i < projects.length; i++) {
if (monitor.isCanceled())
break;
IProject project = projects[i];
LoadInfo info = (LoadInfo) infoMap.get(project);
if (info != null && info.checkout(new SubProgressMonitor(monitor, 1000)))
result.add(project);
}
}
finally {
monitor.done();
}
return (IProject[])result.toArray(new IProject[result.size()]);
}
/**
* Internal class for adding projects to the workspace
*/
class LoadInfo {
private final ICVSRepositoryLocation repositoryLocation;
private final String module;
private final IProject project;
private final CVSTag tag;
/**
* Construct a new instance wrappering the specified project reference
*
* @param projRef the project reference
*/
LoadInfo(StringTokenizer tokenizer) throws CVSException {
String repo = tokenizer.nextToken();
repositoryLocation = getRepositoryLocationFromString(repo);
module = tokenizer.nextToken();
String projectName = tokenizer.nextToken();
project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName);
if (tokenizer.hasMoreTokens()) {
String tagName = tokenizer.nextToken();
tag = new CVSTag(tagName, CVSTag.BRANCH);
}
else {
tag = null;
}
}
/**
* Answer the project referenced by this object.
* The project may or may not already exist.
*
* @return the project (not <code>null</code>)
*/
private IProject getProject() {
return project;
}
/**
* Checkout the project specified by this reference.
*
* @param monitor project monitor
* @return true if loaded, else false
* @throws TeamException
*/
boolean checkout(IProgressMonitor monitor) throws TeamException {
if (repositoryLocation == null)
return false;
CVSProjectSetCapability.checkout(
repositoryLocation,
project,
module,
tag,
monitor);
return true;
}
}
/**
* Extract the CVS repository location information from the specified string
*
* @param repo the repository location as a string
* @return the CVS repository information
* @throws CVSException
*/
private static ICVSRepositoryLocation getRepositoryLocationFromString(String repo) throws CVSException {
// create the new location
ICVSRepositoryLocation newLocation = CVSRepositoryLocation.fromString(repo);
if (newLocation.getUsername() == null || newLocation.getUsername().length() == 0) {
// look for an existing location that matched
ICVSRepositoryLocation[] locations = CVSProviderPlugin.getPlugin().getKnownRepositories();
for (int i = 0; i < locations.length; i++) {
ICVSRepositoryLocation location = locations[i];
if (location.getMethod() == newLocation.getMethod()
&& location.getHost().equals(newLocation.getHost())
&& location.getPort() == newLocation.getPort()
&& location.getRootDirectory().equals(newLocation.getRootDirectory()))
return location;
}
}
return newLocation;
}
/**
* Checkout a CVS module.
*
* The provided project represents the target project. Any existing contents
* may or may not get overwritten. If project is <code>null</code> then a project
* will be created based on the provided sourceModule. If soureModule is null,
* then the project name will be used as the module to
* check out. If both are absent, an exception is thrown.
*
* Resources existing in the local file system at the target project location but now
* known to the workbench will be overwritten.
*
* After the successful completion of this method, the project will exist
* and be open.
*/
public static void checkout(
ICVSRepositoryLocation repository,
IProject project,
String sourceModule,
CVSTag tag,
IProgressMonitor monitor)
throws TeamException {
if (sourceModule == null)
sourceModule = project.getName();
checkout(new ICVSRemoteFolder[] { new RemoteFolder(null, repository, sourceModule, tag)},
new IProject[] { project }, monitor);
}
/**
* Checkout the remote resources into the local workspace. Each resource will
* be checked out into the corresponding project. If the corresponding project is
* null or if projects is null, the name of the remote resource is used as the name of the project.
*
* Resources existing in the local file system at the target project location but now
* known to the workbench will be overwritten.
*/
public static void checkout(final ICVSRemoteFolder[] resources, final IProject[] projects, final IProgressMonitor monitor) throws TeamException {
final TeamException[] eHolder = new TeamException[1];
try {
IWorkspaceRunnable workspaceRunnable = new IWorkspaceRunnable() {
public void run(IProgressMonitor pm) throws CoreException {
try {
pm.beginTask(null, 1000 * resources.length);
// Get the location of the workspace root
ICVSFolder root = CVSWorkspaceRoot.getCVSFolderFor(ResourcesPlugin.getWorkspace().getRoot());
for (int i=0;i<resources.length;i++) {
IProject project = null;
RemoteFolder resource = (RemoteFolder)resources[i];
// Determine the provided target project if there is one
if (projects != null)
project = projects[i];
// Determine the remote module to be checked out
String moduleName;
if (resource instanceof RemoteModule) {
moduleName = ((RemoteModule)resource).getName();
} else {
moduleName = resource.getRepositoryRelativePath();
}
// Open a connection session to the repository
ICVSRepositoryLocation repository = resource.getRepository();
Session session = new Session(repository, root);
try {
session.open(Policy.subMonitorFor(pm, 50), false /* read-only */);
// Determine the local target projects (either the project provider or the module expansions)
final Set targetProjects = new HashSet();
if (project == null) {
// Fetch the module expansions
IStatus status = Request.EXPAND_MODULES.execute(session, new String[] {moduleName}, Policy.subMonitorFor(pm, 50));
if (status.getCode() == CVSStatus.SERVER_ERROR) {
throw new CVSServerException(status);
}
// Convert the module expansions to local projects
String[] expansions = session.getModuleExpansions();
for (int j = 0; j < expansions.length; j++) {
targetProjects.add(ResourcesPlugin.getWorkspace().getRoot().getProject(new Path(expansions[j]).segment(0)));
}
} else {
targetProjects.add(project);
}
// Prepare the target projects to receive resources
root.run(new ICVSRunnable() {
public void run(IProgressMonitor monitor) throws CVSException {
scrubProjects((IProject[]) targetProjects.toArray(new IProject[targetProjects.size()]), monitor);
}
}, Policy.subMonitorFor(pm, 100));
// 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[]{moduleName},
null,
Policy.subMonitorFor(pm, 800));
if (status.getCode() == CVSStatus.SERVER_ERROR) {
// XXX Should we cleanup any partially checked out projects?
throw new CVSServerException(status);
}
// Bring the project into the workspace
refreshProjects((IProject[])targetProjects.toArray(new IProject[targetProjects.size()]), Policy.subMonitorFor(pm, 100));
} finally {
session.close();
}
}
}
catch (TeamException e) {
// Pass it outside the workspace runnable
eHolder[0] = e;
} finally {
pm.done();
}
// CoreException and OperationCanceledException are propagated
}
};
ResourcesPlugin.getWorkspace().run(workspaceRunnable, new MultiRule(projects), 0, monitor);
} catch (CoreException e) {
throw CVSException.wrapException(e);
} finally {
monitor.done();
}
// Re-throw the TeamException, if one occurred
if (eHolder[0] != null) {
throw eHolder[0];
}
}
/*
* Bring the provied projects into the workspace
*/
/* internal use only */ static void refreshProjects(IProject[] projects, IProgressMonitor monitor) throws CoreException, TeamException {
monitor.beginTask(Policy.bind("CVSProvider.Creating_projects_2"), projects.length * 100); //$NON-NLS-1$
try {
for (int i = 0; i < projects.length; i++) {
IProject project = projects[i];
// Register the project with Team
RepositoryProvider.map(project, CVSProviderPlugin.getTypeId());
CVSTeamProvider provider = (CVSTeamProvider)RepositoryProvider.getProvider(project, CVSProviderPlugin.getTypeId());
provider.setWatchEditEnabled(CVSProviderPlugin.getPlugin().isWatchEditEnabled());
}
} finally {
monitor.done();
}
}
/*
* Delete the target projects before checking out
*/
/* internal use only */ static void scrubProjects(IProject[] projects, IProgressMonitor monitor) throws CVSException {
if (projects == null) {
monitor.done();
return;
}
monitor.beginTask(Policy.bind("CVSProvider.Scrubbing_projects_1"), projects.length * 100); //$NON-NLS-1$
try {
for (int i=0;i<projects.length;i++) {
IProject project = projects[i];
if (project != null && project.exists()) {
if(!project.isOpen()) {
project.open(Policy.subMonitorFor(monitor, 10));
}
// 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
monitor.subTask(Policy.bind("CVSProvider.Scrubbing_local_project_1")); //$NON-NLS-1$
// unmap the project from any previous repository provider
if (RepositoryProvider.getProvider(project) != null)
RepositoryProvider.unmap(project);
IResource[] children = project.members(IContainer.INCLUDE_TEAM_PRIVATE_MEMBERS);
IProgressMonitor subMonitor = Policy.subMonitorFor(monitor, 80);
subMonitor.beginTask(null, children.length * 100);
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(subMonitor, 100));
}
}
} finally {
subMonitor.done();
}
} else if (project != null) {
// Make sure there is no directory in the local file system.
File location = new File(project.getParent().getLocation().toFile(), project.getName());
if (location.exists()) {
deepDelete(location);
}
}
}
} catch (CoreException e) {
throw CVSException.wrapException(e);
} finally {
monitor.done();
}
}
private static void deepDelete(File resource) {
if (resource.isDirectory()) {
File[] fileList = resource.listFiles();
for (int i = 0; i < fileList.length; i++) {
deepDelete(fileList[i]);
}
}
resource.delete();
}
}