blob: 410412a61e5853cc4fca6d9999a47e38c9f40e78 [file] [log] [blame]
/*******************************************************************************
* Copyright (C) 2008, 2013 Shawn O. Pearce <spearce@spearce.org> and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*******************************************************************************/
package org.eclipse.egit.core;
import java.io.File;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.Collection;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
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.IResourceDeltaVisitor;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Plugin;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.preferences.DefaultScope;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.core.runtime.preferences.InstanceScope;
import org.eclipse.egit.core.internal.CoreText;
import org.eclipse.egit.core.internal.indexdiff.IndexDiffCache;
import org.eclipse.egit.core.internal.job.JobUtil;
import org.eclipse.egit.core.internal.trace.GitTraceLocation;
import org.eclipse.egit.core.op.ConnectProviderOperation;
import org.eclipse.egit.core.op.IgnoreOperation;
import org.eclipse.egit.core.project.GitProjectData;
import org.eclipse.egit.core.project.RepositoryFinder;
import org.eclipse.egit.core.project.RepositoryMapping;
import org.eclipse.egit.core.securestorage.EGitSecureStore;
import org.eclipse.equinox.security.storage.SecurePreferencesFactory;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.util.FS;
import org.eclipse.osgi.service.debug.DebugOptions;
import org.eclipse.osgi.service.debug.DebugOptionsListener;
import org.eclipse.team.core.RepositoryProvider;
import org.osgi.framework.BundleContext;
/**
* The plugin class for the org.eclipse.egit.core plugin. This
* is a singleton class.
*/
public class Activator extends Plugin implements DebugOptionsListener {
private static Activator plugin;
private static String pluginId;
private RepositoryCache repositoryCache;
private IndexDiffCache indexDiffCache;
private RepositoryUtil repositoryUtil;
private EGitSecureStore secureStore;
private AutoShareProjects shareGitProjectsJob;
private IResourceChangeListener preDeleteProjectListener;
private IgnoreDerivedResources ignoreDerivedResourcesListener;
/**
* @return the singleton {@link Activator}
*/
public static Activator getDefault() {
return plugin;
}
/**
* @return the name of this plugin
*/
public static String getPluginId() {
return pluginId;
}
/**
* Utility to create an error status for this plug-in.
*
* @param message User comprehensible message
* @param thr cause
* @return an initialized error status
*/
public static IStatus error(final String message, final Throwable thr) {
return new Status(IStatus.ERROR, getPluginId(), 0, message, thr);
}
/**
* Utility method to log errors in the Egit plugin.
* @param message User comprehensible message
* @param thr The exception through which we noticed the error
*/
public static void logError(final String message, final Throwable thr) {
getDefault().getLog().log(
new Status(IStatus.ERROR, getPluginId(), 0, message, thr));
}
/**
* Construct the {@link Activator} singleton instance
*/
public Activator() {
Activator.setActivator(this);
}
private static void setActivator(Activator a) {
plugin = a;
}
public void start(final BundleContext context) throws Exception {
super.start(context);
pluginId = context.getBundle().getSymbolicName();
// we want to be notified about debug options changes
Dictionary<String, String> props = new Hashtable<String, String>(4);
props.put(DebugOptions.LISTENER_SYMBOLICNAME, pluginId);
context.registerService(DebugOptionsListener.class.getName(), this,
props);
repositoryCache = new RepositoryCache();
indexDiffCache = new IndexDiffCache();
try {
GitProjectData.reconfigureWindowCache();
} catch (RuntimeException e) {
logError(CoreText.Activator_ReconfigureWindowCacheError, e);
}
GitProjectData.attachToWorkspace(true);
IEclipsePreferences node = InstanceScope.INSTANCE.getNode(Activator.getPluginId());
String gitPrefix = node.get(GitCorePreferences.core_gitPrefix, null);
if (gitPrefix != null)
FS.DETECTED.setGitPrefix(new File(gitPrefix));
repositoryUtil = new RepositoryUtil();
secureStore = new EGitSecureStore(SecurePreferencesFactory.getDefault());
registerAutoShareProjects();
registerAutoIgnoreDerivedResources();
registerPreDeleteResourceChangeListener();
}
private void registerPreDeleteResourceChangeListener() {
if (preDeleteProjectListener == null) {
preDeleteProjectListener = new IResourceChangeListener() {
public void resourceChanged(IResourceChangeEvent event) {
IResource resource = event.getResource();
if (resource instanceof IProject) {
IProject project = (IProject) resource;
if (project.isAccessible()) {
if (RepositoryProvider.getProvider(project) instanceof GitProvider) {
IResource dotGit = project
.findMember(Constants.DOT_GIT);
if (dotGit != null
&& dotGit.getType() == IResource.FOLDER)
GitProjectData.reconfigureWindowCache();
}
} else {
// bug 419706: project is closed - use java.io API
IPath locationPath = project.getLocation();
if (locationPath != null) {
File locationDir = locationPath.toFile();
File dotGit = new File(locationDir,
Constants.DOT_GIT);
if (dotGit.exists() && dotGit.isDirectory()) {
GitProjectData.reconfigureWindowCache();
}
}
}
}
}
};
ResourcesPlugin.getWorkspace().addResourceChangeListener(preDeleteProjectListener, IResourceChangeEvent.PRE_DELETE);
}
}
public void optionsChanged(DebugOptions options) {
// initialize the trace stuff
GitTraceLocation.initializeFromOptions(options, isDebugging());
}
/**
* @return cache for Repository objects
*/
public RepositoryCache getRepositoryCache() {
return repositoryCache;
}
/**
* @return cache for index diffs
*/
public IndexDiffCache getIndexDiffCache() {
return indexDiffCache;
}
/**
* @return the {@link RepositoryUtil} instance
*/
public RepositoryUtil getRepositoryUtil() {
return repositoryUtil;
}
/**
* @return the secure store
*/
public EGitSecureStore getSecureStore() {
return secureStore;
}
public void stop(final BundleContext context) throws Exception {
GitProjectData.detachFromWorkspace();
repositoryCache = null;
indexDiffCache.dispose();
indexDiffCache = null;
repositoryUtil.dispose();
repositoryUtil = null;
secureStore = null;
super.stop(context);
plugin = null;
if (preDeleteProjectListener != null) {
ResourcesPlugin.getWorkspace().removeResourceChangeListener(preDeleteProjectListener);
preDeleteProjectListener = null;
}
if (ignoreDerivedResourcesListener != null) {
ResourcesPlugin.getWorkspace().removeResourceChangeListener(
ignoreDerivedResourcesListener);
ignoreDerivedResourcesListener = null;
}
if (shareGitProjectsJob != null) {
ResourcesPlugin.getWorkspace().removeResourceChangeListener(
shareGitProjectsJob);
shareGitProjectsJob = null;
}
}
private void registerAutoShareProjects() {
shareGitProjectsJob = new AutoShareProjects();
ResourcesPlugin.getWorkspace().addResourceChangeListener(
shareGitProjectsJob, IResourceChangeEvent.POST_CHANGE);
}
private static class AutoShareProjects implements
IResourceChangeListener {
private static int INTERESTING_CHANGES = IResourceDelta.ADDED
| IResourceDelta.OPEN;
public AutoShareProjects() {
// empty
}
private boolean doAutoShare() {
IEclipsePreferences d = DefaultScope.INSTANCE.getNode(Activator
.getPluginId());
IEclipsePreferences p = InstanceScope.INSTANCE.getNode(Activator
.getPluginId());
return p.getBoolean(GitCorePreferences.core_autoShareProjects, d
.getBoolean(GitCorePreferences.core_autoShareProjects,
true));
}
public void resourceChanged(IResourceChangeEvent event) {
try {
final Map<IProject, File> projects = new HashMap<IProject, File>();
event.getDelta().accept(new IResourceDeltaVisitor() {
public boolean visit(IResourceDelta delta)
throws CoreException {
return visitConnect(delta, projects);
}
});
if (projects.size() > 0) {
ConnectProviderOperation op = new ConnectProviderOperation(
projects);
JobUtil.scheduleUserJob(op,
CoreText.Activator_AutoShareJobName,
JobFamilies.AUTO_SHARE);
}
} catch (CoreException e) {
Activator.logError(e.getMessage(), e);
return;
}
}
private boolean visitConnect(IResourceDelta delta,
final Map<IProject, File> projects) throws CoreException {
if (!doAutoShare())
return false;
if (delta.getKind() == IResourceDelta.CHANGED
&& (delta.getFlags() & INTERESTING_CHANGES) == 0)
return true;
final IResource resource = delta.getResource();
if (!resource.exists() || !resource.isAccessible() ||
resource.isLinked(IResource.CHECK_ANCESTORS))
return false;
if (resource.getType() != IResource.PROJECT)
return true;
if (RepositoryMapping.getMapping(resource) != null)
return false;
final IProject project = (IProject) resource;
RepositoryProvider provider = RepositoryProvider
.getProvider(project);
// respect if project is already shared with another
// team provider
if (provider != null)
return false;
RepositoryFinder f = new RepositoryFinder(project);
Collection<RepositoryMapping> mappings = f.find(new NullProgressMonitor());
if (mappings.size() != 1)
return false;
RepositoryMapping m = mappings.iterator().next();
IPath gitDirPath = m.getGitDirAbsolutePath();
if (gitDirPath.segmentCount() == 0)
return false;
IPath workingDir = gitDirPath.removeLastSegments(1);
// Don't connect "/" or "C:\"
if (workingDir.isRoot())
return false;
File userHome = FS.DETECTED.userHome();
if (userHome != null) {
Path userHomePath = new Path(userHome.getAbsolutePath());
// Don't connect "/home" or "/home/username"
if (workingDir.isPrefixOf(userHomePath))
return false;
}
// connect
final File repositoryDir = gitDirPath.toFile();
projects.put(project, repositoryDir);
try {
Activator.getDefault().getRepositoryUtil()
.addConfiguredRepository(repositoryDir);
} catch (IllegalArgumentException e) {
logError(CoreText.Activator_AutoSharingFailed, e);
}
return false;
}
}
private void registerAutoIgnoreDerivedResources() {
ignoreDerivedResourcesListener = new IgnoreDerivedResources();
ResourcesPlugin.getWorkspace().addResourceChangeListener(
ignoreDerivedResourcesListener,
IResourceChangeEvent.POST_CHANGE);
}
private static class IgnoreDerivedResources implements
IResourceChangeListener {
protected boolean autoIgnoreDerived() {
IEclipsePreferences d = DefaultScope.INSTANCE.getNode(Activator
.getPluginId());
IEclipsePreferences p = InstanceScope.INSTANCE.getNode(Activator
.getPluginId());
return p.getBoolean(
GitCorePreferences.core_autoIgnoreDerivedResources,
d.getBoolean(
GitCorePreferences.core_autoIgnoreDerivedResources,
true));
}
public void resourceChanged(IResourceChangeEvent event) {
try {
IResourceDelta d = event.getDelta();
if (d == null || !autoIgnoreDerived())
return;
final Set<IPath> toBeIgnored = new LinkedHashSet<IPath>();
d.accept(new IResourceDeltaVisitor() {
public boolean visit(IResourceDelta delta)
throws CoreException {
if ((delta.getKind() & (IResourceDelta.ADDED | IResourceDelta.CHANGED)) == 0)
return false;
int flags = delta.getFlags();
if ((flags != 0)
&& ((flags & IResourceDelta.DERIVED_CHANGED) == 0))
return false;
final IResource r = delta.getResource();
// don't consider resources contained in a project not
// shared with Git team provider
if ((r.getProject() != null)
&& (RepositoryMapping.getMapping(r) == null))
return false;
if (r.isTeamPrivateMember())
return false;
if (r.isDerived()) {
try {
if (!RepositoryUtil.isIgnored(r.getLocation()))
toBeIgnored.add(r.getLocation());
} catch (IOException e) {
logError(
MessageFormat.format(
CoreText.Activator_ignoreResourceFailed,
r.getFullPath()), e);
}
return false;
}
return true;
}
});
if (toBeIgnored.size() > 0)
JobUtil.scheduleUserJob(new IgnoreOperation(toBeIgnored),
CoreText.Activator_autoIgnoreDerivedResources,
JobFamilies.AUTO_IGNORE);
} catch (CoreException e) {
Activator.logError(e.getMessage(), e);
return;
}
}
}
}