blob: 24ad316a2ab878d0c91205a32a212d2f1580b001 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2002 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v0.5
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v05.html
*
* Contributors:
* IBM - Initial API and implementation
******************************************************************************/
package org.eclipse.team.internal.ccvs.core;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IPluginDescriptor;
import org.eclipse.core.runtime.ISafeRunnable;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Plugin;
import org.eclipse.core.runtime.Status;
import org.eclipse.team.core.RepositoryProvider;
import org.eclipse.team.core.TeamException;
import org.eclipse.team.internal.ccvs.core.client.Command;
import org.eclipse.team.internal.ccvs.core.client.Command.KSubstOption;
import org.eclipse.team.internal.ccvs.core.client.Command.QuietOption;
import org.eclipse.team.internal.ccvs.core.client.listeners.IConsoleListener;
import org.eclipse.team.internal.ccvs.core.util.AddDeleteMoveListener;
import org.eclipse.team.internal.ccvs.core.util.MoveDeleteHook;
import org.eclipse.team.internal.ccvs.core.util.ProjectDescriptionManager;
import org.eclipse.team.internal.ccvs.core.util.SyncFileChangeListener;
import org.eclipse.team.internal.ccvs.core.util.Util;
public class CVSProviderPlugin extends Plugin {
// external command to run for ext connection method
public static final String DEFAULT_CVS_RSH = "ssh"; //$NON-NLS-1$
// remote command to run for ext connection method
public static final String DEFAULT_CVS_SERVER = "cvs"; //$NON-NLS-1$
// determines if empty directories received from the server should be pruned.
public static final boolean DEFAULT_PRUNE = true;
// determines if new directories should be discovered during update.
public static final boolean DEFAULT_FETCH = true;
// communication timeout with the server
public static final int DEFAULT_TIMEOUT = 60;
// file transfer compression level (0 - 9)
public static final int DEFAULT_COMPRESSION_LEVEL = 0;
// default text keyword substitution mode
public static final KSubstOption DEFAULT_TEXT_KSUBST_OPTION = Command.KSUBST_TEXT;
// cvs plugin extension points and ids
public static final String ID = "org.eclipse.team.cvs.core"; //$NON-NLS-1$
public static final String PT_AUTHENTICATOR = "authenticator"; //$NON-NLS-1$
public static final String PT_CONNECTIONMETHODS = "connectionmethods"; //$NON-NLS-1$
// Directory to cache file contents
private static final String CACHE_DIRECTORY = ".cache"; //$NON-NLS-1$
private QuietOption quietness;
private int compressionLevel = DEFAULT_COMPRESSION_LEVEL;
private KSubstOption defaultTextKSubstOption = DEFAULT_TEXT_KSUBST_OPTION;
private int communicationsTimeout = DEFAULT_TIMEOUT;
private boolean pruneEmptyDirectories = DEFAULT_PRUNE;
private boolean fetchAbsentDirectories = DEFAULT_FETCH;
private boolean promptOnFileDelete = true;
private boolean promptOnFolderDelete = true;
private boolean showTasksOnAddAndDelete = false;
private boolean replaceUnmanaged = true;
private String cvsRshCommand = DEFAULT_CVS_RSH;
private String cvsServer = DEFAULT_CVS_SERVER;
private IConsoleListener consoleListener;
private static CVSProviderPlugin instance;
// CVS specific resource delta listeners
private IResourceChangeListener projectDescriptionListener;
private IResourceChangeListener metaFileSyncListener;
private AddDeleteMoveListener addDeleteMoveListener;
/**
* The identifier for the CVS nature
* (value <code>"org.eclipse.team.cvs.core.nature"</code>).
* The presence of this nature on a project indicates that it is
* CVS-capable.
*
* @see org.eclipse.core.resources.IProject#hasNature
*/
private static final String NATURE_ID = ID + ".cvsnature"; //$NON-NLS-1$
/**
* Constructor for CVSProviderPlugin.
* @param descriptor
*/
public CVSProviderPlugin(IPluginDescriptor descriptor) {
super(descriptor);
instance = this;
}
/**
* Convenience method for logging CVSExceptiuons to the plugin log
*/
public static void log(TeamException e) {
// For now, we'll log the status. However we should do more
instance.getLog().log(e.getStatus());
}
public static void log(IStatus status) {
// For now, we'll log the status. However we should do more
instance.getLog().log(status);
}
/**
* Returns the singleton plug-in instance.
*
* @return the plugin instance
*/
public static CVSProviderPlugin getPlugin() {
return instance;
}
/**
* Get the ICVSProvider
*/
public static ICVSProvider getProvider() {
return CVSProvider.getInstance();
}
/**
* Answers the repository provider type id for the cvs plugin
*/
public static String getTypeId() {
return NATURE_ID;
}
/**
* Sets the file transfer compression level. (if supported)
* Valid levels are: 0 (disabled), 1 (worst/fastest) - 9 (best/slowest)
*/
public void setCompressionLevel(int level) {
compressionLevel = level;
}
/**
* Gets the file transfer compression level.
*/
public int getCompressionLevel() {
return compressionLevel;
}
/**
* Sets the default keyword substitution mode for text files.
*/
public void setDefaultTextKSubstOption(KSubstOption ksubst) {
defaultTextKSubstOption = ksubst;
}
/**
* Gets the default keyword substitution mode for text files.
*/
public KSubstOption getDefaultTextKSubstOption() {
return defaultTextKSubstOption;
}
/**
* Should the CVS adapter prune empty directories
*/
public boolean getPruneEmptyDirectories() {
return pruneEmptyDirectories;
}
/**
* Set whether the CVS adapter should prune empty directories
*/
public void setPruneEmptyDirectories(boolean prune) {
pruneEmptyDirectories = prune;
}
/**
* Get the communications timeout value in seconds
*/
public int getTimeout() {
return communicationsTimeout;
}
/**
* Set the timeout value for communications to a value in seconds.
* The value must be greater than or equal 0. If is it 0, there is no timeout.
*/
public void setTimeout(int timeout) {
this.communicationsTimeout = Math.max(0, timeout);
}
/**
* Set the quietness option to use with cvs commands.
* Can be "", "-q" or "-Q"
*/
public void setQuietness(QuietOption option) {
this.quietness = option;
}
/**
* Get the quietness option for commands
*/
public QuietOption getQuietness() {
return quietness;
}
/**
* Set the console listener for commands.
* @param consoleListener the listener
*/
public void setConsoleListener(IConsoleListener consoleListener) {
this.consoleListener = consoleListener;
}
/**
* Get the console listener for commands.
* @return the consoleListener, or null
*/
public IConsoleListener getConsoleListener() {
return consoleListener;
}
/**
* @see Plugin#startup()
*/
public void startup() throws CoreException {
super.startup();
Policy.localize("org.eclipse.team.internal.ccvs.core.messages"); //$NON-NLS-1$
// Start the synchronizer first as the startup of CVSProvider may use it.
CVSProvider.startup();
// Initialize CVS change listeners. Note tha the report type is important.
IWorkspace workspace = ResourcesPlugin.getWorkspace();
projectDescriptionListener = new ProjectDescriptionManager();
metaFileSyncListener = new SyncFileChangeListener();
addDeleteMoveListener = new AddDeleteMoveListener();
workspace.addResourceChangeListener(projectDescriptionListener, IResourceChangeEvent.PRE_AUTO_BUILD);
workspace.addResourceChangeListener(metaFileSyncListener, IResourceChangeEvent.PRE_AUTO_BUILD);
workspace.addResourceChangeListener(addDeleteMoveListener, IResourceChangeEvent.POST_AUTO_BUILD);
CVSProviderPlugin.getPlugin().addResourceStateChangeListener(addDeleteMoveListener);
createCacheDirectory();
CVSTeamProvider.setMoveDeleteHook(new MoveDeleteHook());
}
/**
* @see Plugin#shutdown()
*/
public void shutdown() throws CoreException {
super.shutdown();
CVSProvider.shutdown();
// remove listeners
IWorkspace workspace = ResourcesPlugin.getWorkspace();
workspace.removeResourceChangeListener(projectDescriptionListener);
workspace.removeResourceChangeListener(metaFileSyncListener);
workspace.removeResourceChangeListener(addDeleteMoveListener);
deleteCacheDirectory();
}
/*
* Add a resource change listener to the workspace in order to respond to
* resource deletions and moves and to ensure or project desription file is up to date.
*/
private void initializeChangeListener() {
// Build a change listener for changes to thr project meta-information
IResourceChangeListener projectChangeListener = new IResourceChangeListener() {
public void resourceChanged(IResourceChangeEvent event) {
try {
IResourceDelta root = event.getDelta();
IResourceDelta[] projectDeltas = root.getAffectedChildren(IResourceDelta.CHANGED);
for (int i = 0; i < projectDeltas.length; i++) {
IResourceDelta delta = projectDeltas[i];
IResource resource = delta.getResource();
if (resource.getType() == IResource.PROJECT) {
IProject project = (IProject)resource;
// Get the team provider for the project and
RepositoryProvider provider = RepositoryProvider.getProvider(project, getTypeId());
if(provider==null) continue;
/* Check if the project description changed. */
if ((delta.getFlags() & IResourceDelta.DESCRIPTION) != 0) {
/* The project description changed. Write the file. */
ProjectDescriptionManager.writeProjectDescriptionIfNecessary((CVSTeamProvider)provider, project, Policy.monitorFor(null));
}
/* Check if the .vcm_meta file for the project is in the delta. */
IResourceDelta[] children = delta.getAffectedChildren(IResourceDelta.REMOVED);
for (int j = 0; j < children.length; j++) {
IResourceDelta childDelta = children[j];
IResource childResource = childDelta.getResource();
if (ProjectDescriptionManager.isProjectDescription(childResource)) {
ProjectDescriptionManager.writeProjectDescriptionIfNecessary((CVSTeamProvider)provider, project, Policy.monitorFor(null));
}
}
}
}
} catch (CVSException ex) {
Util.logError(Policy.bind("CVSProviderPlugin.cannotUpdateDescription"), ex); //$NON-NLS-1$
}
}
};
ResourcesPlugin.getWorkspace().addResourceChangeListener(projectChangeListener, IResourceChangeEvent.POST_AUTO_BUILD);
}
/**
* Gets the cvsRshCommand.
* @return Returns a String
*/
public String getCvsRshCommand() {
return cvsRshCommand;
}
/**
* Sets the cvsRshCommand.
* @param cvsRshCommand The cvsRshCommand to set
*/
public void setCvsRshCommand(String cvsRshCommand) {
this.cvsRshCommand = cvsRshCommand;
}
/**
* Gets the cvsServer.
* @return Returns a String
*/
public String getCvsServer() {
return cvsServer;
}
/**
* Sets the cvsServer.
* @param cvsServer The cvsServer to set
*/
public void setCvsServer(String cvsServer) {
this.cvsServer = cvsServer;
}
/**
* Gets the etchAbsentDirectories.
* @return Returns a boolean
*/
public boolean getFetchAbsentDirectories() {
return fetchAbsentDirectories;
}
/**
* Sets the fetchAbsentDirectories.
* @param etchAbsentDirectories The etchAbsentDirectories to set
*/
public void setFetchAbsentDirectories(boolean fetchAbsentDirectories) {
this.fetchAbsentDirectories = fetchAbsentDirectories;
}
public boolean getPromptOnFileDelete() {
return promptOnFileDelete;
}
public void setPromptOnFileDelete(boolean prompt) {
promptOnFileDelete = prompt;
}
public boolean getPromptOnFolderDelete() {
return promptOnFolderDelete;
}
public void setPromptOnFolderDelete(boolean prompt) {
promptOnFolderDelete = prompt;
}
private static List listeners = new ArrayList();
/*
* @see ITeamManager#addResourceStateChangeListener(IResourceStateChangeListener)
*/
public static void addResourceStateChangeListener(IResourceStateChangeListener listener) {
listeners.add(listener);
}
/*
* @see ITeamManager#removeResourceStateChangeListener(IResourceStateChangeListener)
*/
public static void removeResourceStateChangeListener(IResourceStateChangeListener listener) {
listeners.remove(listener);
}
/*
* @see ITeamManager#broadcastResourceStateChanges(IResource[])
*/
public static void broadcastResourceStateChanges(final IResource[] resources) {
for(Iterator it=listeners.iterator(); it.hasNext();) {
final IResourceStateChangeListener listener = (IResourceStateChangeListener)it.next();
ISafeRunnable code = new ISafeRunnable() {
public void run() throws Exception {
listener.resourceStateChanged(resources);
}
public void handleException(Throwable e) {
// don't log the exception....it is already being logged in Platform#run
}
};
Platform.run(code);
}
}
protected static void broadcastProjectConfigured(final IProject project) {
for(Iterator it=listeners.iterator(); it.hasNext();) {
final IResourceStateChangeListener listener = (IResourceStateChangeListener)it.next();
ISafeRunnable code = new ISafeRunnable() {
public void run() throws Exception {
listener.projectConfigured(project);
}
public void handleException(Throwable e) {
// don't log the exception....it is already being logged in Platform#run
}
};
Platform.run(code);
}
}
protected static void broadcastProjectDeconfigured(final IProject project) {
for(Iterator it=listeners.iterator(); it.hasNext();) {
final IResourceStateChangeListener listener = (IResourceStateChangeListener)it.next();
ISafeRunnable code = new ISafeRunnable() {
public void run() throws Exception {
listener.projectDeconfigured(project);
}
public void handleException(Throwable e) {
// don't log the exception....it is already being logged in Platform#run
}
};
Platform.run(code);
}
}
/**
* Gets the showTasksOnAddAndDelete.
* @return Returns a boolean
*/
public boolean getShowTasksOnAddAndDelete() {
return showTasksOnAddAndDelete;
}
/**
* Sets the showTasksOnAddAndDelete.
* @param showTasksOnAddAndDelete The showTasksOnAddAndDelete to set
*/
public void setShowTasksOnAddAndDelete(boolean showTasksOnAddAndDelete) {
this.showTasksOnAddAndDelete = showTasksOnAddAndDelete;
}
/**
* Gets the replaceUnmanaged.
* @return Returns a boolean
*/
public boolean isReplaceUnmanaged() {
return replaceUnmanaged;
}
/**
* Sets the replaceUnmanaged.
* @param replaceUnmanaged The replaceUnmanaged to set
*/
public void setReplaceUnmanaged(boolean replaceUnmanaged) {
this.replaceUnmanaged = replaceUnmanaged;
}
private void createCacheDirectory() {
try {
IPath cacheLocation = getStateLocation().append(CACHE_DIRECTORY);
File file = cacheLocation.toFile();
if (file.exists()) {
deleteFile(file);
}
file.mkdir();
} catch (IOException e) {
log(new Status(IStatus.ERROR, ID, 0, Policy.bind("CVSProviderPlugin.errorCreatingCache", e.getMessage()), e)); //$NON-NLS-1$
}
}
private void deleteCacheDirectory() {
try {
IPath cacheLocation = getStateLocation().append(CACHE_DIRECTORY);
File file = cacheLocation.toFile();
if (file.exists()) {
deleteFile(file);
}
} catch (IOException e) {
log(new Status(IStatus.ERROR, ID, 0, Policy.bind("CVSProviderPlugin.errorDeletingCache", e.getMessage()), e)); //$NON-NLS-1$
}
}
private void deleteFile(File file) throws IOException {
if (file.isDirectory()) {
File[] children = file.listFiles();
for (int i = 0; i < children.length; i++) {
deleteFile(children[i]);
}
}
file.delete();
}
public File getCacheFileFor(String path) throws IOException {
return new File(getStateLocation().append(CACHE_DIRECTORY).toFile(), path);
}
}