/*******************************************************************************
* Copyright (C) 2008, 2015 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.net.Authenticator;
import java.net.ProxySelector;
import java.text.MessageFormat;
import java.util.Collection;
import java.util.Collections;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.net.proxy.IProxyService;
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.IConfigurationElement;
import org.eclipse.core.runtime.IExtension;
import org.eclipse.core.runtime.IExtensionPoint;
import org.eclipse.core.runtime.IExtensionRegistry;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IRegistryEventListener;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Plugin;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
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.internal.util.ResourceUtil;
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.merge.MergeStrategy;
import org.eclipse.jgit.transport.SshSessionFactory;
import org.eclipse.jgit.util.FS;
import org.eclipse.jsch.core.IJSchService;
import org.eclipse.osgi.service.debug.DebugOptions;
import org.eclipse.osgi.service.debug.DebugOptionsListener;
import org.eclipse.osgi.util.NLS;
import org.eclipse.team.core.RepositoryProvider;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
/**
* 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;
private MergeStrategyRegistryListener mergeStrategyRegistryListener;
/**
* @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(error(message, thr));
}
/**
* Log an info message for this plug-in
*
* @param message
*/
public static void logInfo(final String message) {
getDefault().getLog().log(
new Status(IStatus.INFO, getPluginId(), 0, message, null));
}
/**
* Utility to create a warning status for this plug-in.
*
* @param message
* User comprehensible message
* @param thr
* cause
* @return an initialized warning status
*/
public static IStatus warning(final String message, final Throwable thr) {
return new Status(IStatus.WARNING, getPluginId(), 0, message, thr);
}
/**
* Utility method to log warnings for this plug-in.
*
* @param message
* User comprehensible message
* @param thr
* The exception through which we noticed the warning
*/
public static void logWarning(final String message, final Throwable thr) {
getDefault().getLog().log(warning(message, thr));
}
/**
* Construct the {@link Activator} singleton instance
*/
public Activator() {
Activator.setActivator(this);
}
private static void setActivator(Activator a) {
plugin = a;
}
@Override
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);
setupSSH(context);
setupProxy(context);
repositoryCache = new RepositoryCache();
indexDiffCache = new IndexDiffCache();
try {
GitProjectData.reconfigureWindowCache();
} catch (RuntimeException e) {
logError(CoreText.Activator_ReconfigureWindowCacheError, e);
}
GitProjectData.attachToWorkspace();
repositoryUtil = new RepositoryUtil();
secureStore = new EGitSecureStore(SecurePreferencesFactory.getDefault());
registerAutoShareProjects();
registerAutoIgnoreDerivedResources();
registerPreDeleteResourceChangeListener();
registerMergeStrategyRegistryListener();
}
@SuppressWarnings("unchecked")
private void setupSSH(final BundleContext context) {
final ServiceReference ssh;
ssh = context.getServiceReference(IJSchService.class.getName());
if (ssh != null) {
SshSessionFactory.setInstance(new EclipseSshSessionFactory(
(IJSchService) context.getService(ssh)));
}
}
@SuppressWarnings("unchecked")
private void setupProxy(final BundleContext context) {
final ServiceReference proxy;
proxy = context.getServiceReference(IProxyService.class.getName());
if (proxy != null) {
ProxySelector.setDefault(new EclipseProxySelector(
(IProxyService) context.getService(proxy)));
Authenticator.setDefault(new EclipseAuthenticator(
(IProxyService) context.getService(proxy)));
}
}
private void registerPreDeleteResourceChangeListener() {
if (preDeleteProjectListener == null) {
preDeleteProjectListener = new IResourceChangeListener() {
@Override
public void resourceChanged(IResourceChangeEvent event) {
IResource resource = event.getResource();
if (resource instanceof IProject) {
IProject project = (IProject) resource;
if (project.isAccessible()) {
if (ResourceUtil.isSharedWithGit(project)) {
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);
}
}
@Override
public void optionsChanged(DebugOptions options) {
// initialize the trace stuff
GitTraceLocation.initializeFromOptions(options, isDebugging());
}
/**
* Provides the 3-way merge strategy to use according to the user's
* preferences. The preferred merge strategy is JGit's default merge
* strategy unless the user has explicitly chosen a different strategy among
* the registered strategies.
*
* @return The MergeStrategy to use, can be {@code null}, in which case the
* default merge strategy should be used as defined by JGit.
* @since 4.1
*/
public MergeStrategy getPreferredMergeStrategy() {
// Get preferences set by user in the UI
final IEclipsePreferences prefs = InstanceScope.INSTANCE
.getNode(Activator.getPluginId());
String preferredMergeStrategyKey = prefs.get(
GitCorePreferences.core_preferredMergeStrategy, null);
// Get default preferences, wherever they are defined
if (preferredMergeStrategyKey == null
|| preferredMergeStrategyKey.isEmpty()) {
final IEclipsePreferences defaultPrefs = DefaultScope.INSTANCE
.getNode(Activator.getPluginId());
preferredMergeStrategyKey = defaultPrefs.get(
GitCorePreferences.core_preferredMergeStrategy, null);
}
if (preferredMergeStrategyKey != null
&& !preferredMergeStrategyKey.isEmpty()
&& !GitCorePreferences.core_preferredMergeStrategy_Default
.equals(preferredMergeStrategyKey)) {
MergeStrategy result = MergeStrategy.get(preferredMergeStrategyKey);
if (result != null) {
return result;
}
logError(NLS.bind(CoreText.Activator_invalidPreferredMergeStrategy,
preferredMergeStrategyKey), null);
}
return null;
}
/**
* @return Provides a read-only view of the registered MergeStrategies
* available.
* @since 4.1
*/
public Collection<MergeStrategyDescriptor> getRegisteredMergeStrategies() {
if (mergeStrategyRegistryListener == null) {
return Collections.emptyList();
}
return mergeStrategyRegistryListener.getStrategies();
}
private void registerMergeStrategyRegistryListener() {
mergeStrategyRegistryListener = new MergeStrategyRegistryListener(
Platform.getExtensionRegistry());
Platform.getExtensionRegistry().addListener(
mergeStrategyRegistryListener,
"org.eclipse.egit.core.mergeStrategy"); //$NON-NLS-1$
}
/**
* @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;
}
@Override
public void stop(final BundleContext context) throws Exception {
if (mergeStrategyRegistryListener != null) {
Platform.getExtensionRegistry()
.removeListener(mergeStrategyRegistryListener);
mergeStrategyRegistryListener = null;
}
if (preDeleteProjectListener != null) {
ResourcesPlugin.getWorkspace().removeResourceChangeListener(preDeleteProjectListener);
preDeleteProjectListener = null;
}
if (ignoreDerivedResourcesListener != null) {
ResourcesPlugin.getWorkspace().removeResourceChangeListener(
ignoreDerivedResourcesListener);
ignoreDerivedResourcesListener.stop();
ignoreDerivedResourcesListener = null;
}
if (shareGitProjectsJob != null) {
ResourcesPlugin.getWorkspace().removeResourceChangeListener(
shareGitProjectsJob);
shareGitProjectsJob.stop();
shareGitProjectsJob = null;
}
GitProjectData.detachFromWorkspace();
indexDiffCache.dispose();
indexDiffCache = null;
repositoryCache.clear();
repositoryCache = null;
repositoryUtil.dispose();
repositoryUtil = null;
secureStore = null;
super.stop(context);
plugin = 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;
private final CheckProjectsToShare checkProjectsJob;
public AutoShareProjects() {
checkProjectsJob = new CheckProjectsToShare();
}
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 stop() {
boolean isRunning = !checkProjectsJob.cancel();
Job.getJobManager().cancel(JobFamilies.AUTO_SHARE);
try {
if (isRunning) {
checkProjectsJob.join();
}
Job.getJobManager().join(JobFamilies.AUTO_SHARE,
new NullProgressMonitor());
} catch (OperationCanceledException e) {
// Ignore
} catch (InterruptedException e) {
logError(e.getLocalizedMessage(), e);
}
}
@Override
public void resourceChanged(IResourceChangeEvent event) {
if (!doAutoShare()) {
return;
}
try {
final Set<IProject> projectCandidates = new LinkedHashSet<>();
event.getDelta().accept(new IResourceDeltaVisitor() {
@Override
public boolean visit(IResourceDelta delta)
throws CoreException {
return collectOpenedProjects(delta,
projectCandidates);
}
});
if(!projectCandidates.isEmpty()){
checkProjectsJob.addProjectsToCheck(projectCandidates);
}
} catch (CoreException e) {
Activator.logError(e.getMessage(), e);
return;
}
}
/*
* This method should not use RepositoryMapping.getMapping(project) or
* RepositoryProvider.getProvider(project) which can trigger
* RepositoryProvider.map(project) and deadlock current thread. See
* https://bugs.eclipse.org/bugs/show_bug.cgi?id=468270
*/
private boolean collectOpenedProjects(IResourceDelta delta,
Set<IProject> projects) {
if (delta.getKind() == IResourceDelta.CHANGED
&& (delta.getFlags() & INTERESTING_CHANGES) == 0) {
return true;
}
final IResource resource = delta.getResource();
if (resource.getType() == IResource.ROOT) {
return true;
}
if (resource.getType() != IResource.PROJECT) {
return false;
}
if (!resource.isAccessible() || resource.getLocation() == null) {
return false;
}
projects.add((IProject) resource);
return false;
}
}
private static class CheckProjectsToShare extends Job {
private Object lock = new Object();
private Set<IProject> projectCandidates;
public CheckProjectsToShare() {
super(CoreText.Activator_AutoShareJobName);
this.projectCandidates = new LinkedHashSet<IProject>();
setUser(false);
setSystem(true);
}
public void addProjectsToCheck(Set<IProject> projects) {
synchronized (lock) {
this.projectCandidates.addAll(projects);
if (!projectCandidates.isEmpty()) {
schedule(100);
}
}
}
@Override
protected IStatus run(IProgressMonitor monitor) {
Set<IProject> projectsToCheck;
synchronized (lock) {
projectsToCheck = projectCandidates;
projectCandidates = new LinkedHashSet<>();
}
if (projectsToCheck.isEmpty()) {
return Status.OK_STATUS;
}
final Map<IProject, File> projects = new HashMap<IProject, File>();
for (IProject project : projectsToCheck) {
if (monitor.isCanceled()) {
return Status.CANCEL_STATUS;
}
if (project.isAccessible()) {
try {
visitConnect(project, projects);
} catch (CoreException e) {
logError(e.getMessage(), e);
}
}
}
if (monitor.isCanceled()) {
return Status.CANCEL_STATUS;
}
if (projects.size() > 0) {
ConnectProviderOperation op = new ConnectProviderOperation(
projects);
op.setRefreshResources(false);
JobUtil.scheduleUserJob(op,
CoreText.Activator_AutoShareJobName,
JobFamilies.AUTO_SHARE);
}
return Status.OK_STATUS;
}
private void visitConnect(IProject project,
final Map<IProject, File> projects) throws CoreException {
if (RepositoryMapping.getMapping(project) != null) {
return;
}
RepositoryProvider provider = RepositoryProvider
.getProvider(project);
// respect if project is already shared with another
// team provider
if (provider != null) {
return;
}
RepositoryFinder f = new RepositoryFinder(project);
f.setFindInChildren(false);
List<RepositoryMapping> mappings = f
.find(new NullProgressMonitor());
if (mappings.isEmpty()) {
return;
}
RepositoryMapping m = mappings.get(0);
IPath gitDirPath = m.getGitDirAbsolutePath();
if (gitDirPath == null || gitDirPath.segmentCount() == 0) {
return;
}
IPath workingDir = gitDirPath.removeLastSegments(1);
// Don't connect "/" or "C:\"
if (workingDir.isRoot()) {
return;
}
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;
}
}
// connect
File repositoryDir = gitDirPath.toFile();
projects.put(project, repositoryDir);
// If we had more than one mapping: add the last one as
// 'configured' repository. We don't want to add submodules,
// that would only lead to problems when a configured repository
// is deleted.
int nofMappings = mappings.size();
if (nofMappings > 1) {
IPath lastPath = mappings.get(nofMappings - 1)
.getGitDirAbsolutePath();
if (lastPath != null) {
repositoryDir = lastPath.toFile();
}
}
try {
Activator.getDefault().getRepositoryUtil()
.addConfiguredRepository(repositoryDir);
} catch (IllegalArgumentException e) {
logError(CoreText.Activator_AutoSharingFailed, e);
}
}
}
private void registerAutoIgnoreDerivedResources() {
ignoreDerivedResourcesListener = new IgnoreDerivedResources();
ResourcesPlugin.getWorkspace().addResourceChangeListener(
ignoreDerivedResourcesListener,
IResourceChangeEvent.POST_CHANGE);
}
/**
* @return true if the derived resources should be automatically added to
* the .gitignore files
*/
public static 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));
}
/**
* @return {@code true} if files that get deleted should be automatically
* staged
* @since 4.6
*/
public static boolean autoStageDeletion() {
IEclipsePreferences d = DefaultScope.INSTANCE
.getNode(Activator.getPluginId());
IEclipsePreferences p = InstanceScope.INSTANCE
.getNode(Activator.getPluginId());
boolean autoStageDeletion = p.getBoolean(
GitCorePreferences.core_autoStageDeletion,
d.getBoolean(GitCorePreferences.core_autoStageDeletion, false));
return autoStageDeletion;
}
/**
* @return {@code true} if files that are moved should be automatically
* staged
* @since 4.6
*/
public static boolean autoStageMoves() {
IEclipsePreferences d = DefaultScope.INSTANCE
.getNode(Activator.getPluginId());
IEclipsePreferences p = InstanceScope.INSTANCE
.getNode(Activator.getPluginId());
boolean autoStageMoves = p.getBoolean(
GitCorePreferences.core_autoStageMoves,
d.getBoolean(GitCorePreferences.core_autoStageMoves, false));
return autoStageMoves;
}
private static class IgnoreDerivedResources implements
IResourceChangeListener {
public void stop() {
Job.getJobManager().cancel(JobFamilies.AUTO_IGNORE);
try {
Job.getJobManager().join(JobFamilies.AUTO_IGNORE,
new NullProgressMonitor());
} catch (OperationCanceledException e) {
// Ignore
} catch (InterruptedException e) {
logError(e.getLocalizedMessage(), e);
}
}
@Override
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() {
@Override
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 {
IPath location = r.getLocation();
if (RepositoryUtil.canBeAutoIgnored(location)) {
toBeIgnored.add(location);
}
} 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;
}
}
}
/**
* Describes a MergeStrategy which can be registered with the mergeStrategy
* extension point.
*
* @since 4.1
*/
public static class MergeStrategyDescriptor {
private final String name;
private final String label;
private final Class<?> implementedBy;
/**
* @param name
* The referred strategy's name, to use for retrieving the
* strategy from MergeRegistry via
* {@link MergeStrategy#get(String)}
* @param label
* The label to display to users so they can select the
* strategy they need
* @param implementedBy
* The class of the MergeStrategy registered through the
* mergeStrategy extension point
*/
public MergeStrategyDescriptor(String name, String label,
Class<?> implementedBy) {
this.name = name;
this.label = label;
this.implementedBy = implementedBy;
}
/**
* @return The actual strategy's name, which can be used to retrieve
* that actual strategy via {@link MergeStrategy#get(String)}.
*/
public String getName() {
return name;
}
/**
* @return The strategy label, for display purposes.
*/
public String getLabel() {
return label;
}
/**
* @return The class of the MergeStrategy registered through the
* mergeStrategy extension point.
*/
public Class<?> getImplementedBy() {
return implementedBy;
}
}
private static class MergeStrategyRegistryListener implements
IRegistryEventListener {
private Map<String, MergeStrategyDescriptor> strategies;
private MergeStrategyRegistryListener(IExtensionRegistry registry) {
strategies = new LinkedHashMap<>();
IConfigurationElement[] elements = registry
.getConfigurationElementsFor("org.eclipse.egit.core.mergeStrategy"); //$NON-NLS-1$
loadMergeStrategies(elements);
}
private Collection<MergeStrategyDescriptor> getStrategies() {
return Collections.unmodifiableCollection(strategies.values());
}
@Override
public void added(IExtension[] extensions) {
for (IExtension extension : extensions) {
loadMergeStrategies(extension.getConfigurationElements());
}
}
@Override
public void added(IExtensionPoint[] extensionPoints) {
// Nothing to do here
}
@Override
public void removed(IExtension[] extensions) {
for (IExtension extension : extensions) {
for (IConfigurationElement element : extension
.getConfigurationElements()) {
try {
Object ext = element.createExecutableExtension("class"); //$NON-NLS-1$
if (ext instanceof MergeStrategy) {
MergeStrategy strategy = (MergeStrategy) ext;
strategies.remove(strategy.getName());
}
} catch (CoreException e) {
Activator.logError(CoreText.MergeStrategy_UnloadError,
e);
}
}
}
}
@Override
public void removed(IExtensionPoint[] extensionPoints) {
// Nothing to do here
}
private void loadMergeStrategies(IConfigurationElement[] elements) {
for (IConfigurationElement element : elements) {
try {
Object ext = element.createExecutableExtension("class"); //$NON-NLS-1$
if (ext instanceof MergeStrategy) {
MergeStrategy strategy = (MergeStrategy) ext;
String name = element.getAttribute("name"); //$NON-NLS-1$
if (name == null || name.isEmpty()) {
name = strategy.getName();
}
if (canRegister(name, strategy)) {
if (MergeStrategy.get(name) == null) {
MergeStrategy.register(name, strategy);
}
strategies
.put(name,
new MergeStrategyDescriptor(
name,
element.getAttribute("label"), //$NON-NLS-1$
strategy.getClass()));
}
}
} catch (CoreException e) {
Activator.logError(CoreText.MergeStrategy_LoadError, e);
}
}
}
/**
* Checks whether it's possible to register the provided strategy with
* the given name
*
* @param name
* Name to use to register the strategy
* @param strategy
* Strategy to register
* @return <code>true</code> if the name is neither null nor empty, no
* other strategy is already register for the same name, and the
* name is not one of the core JGit strategies. If the given
* name is that of a core JGit strategy, the method will return
* <code>true</code> only if the strategy is the matching JGit
* strategy for that name.
*/
private boolean canRegister(String name, MergeStrategy strategy) {
boolean result = true;
if (name == null || name.isEmpty()) {
// name is mandatory
Activator.logError(
NLS.bind(CoreText.MergeStrategy_MissingName,
strategy.getClass()), null);
result = false;
} else if (strategies.containsKey(name)) {
// Other strategy already registered for this name
Activator.logError(NLS.bind(
CoreText.MergeStrategy_DuplicateName, new Object[] {
name, strategies.get(name).getImplementedBy(),
strategy.getClass() }), null);
result = false;
} else if (MergeStrategy.get(name) != null
&& MergeStrategy.get(name) != strategy) {
// The name is reserved by a core JGit strategy, and the
// provided instance is not that of JGit
Activator.logError(NLS.bind(
CoreText.MergeStrategy_ReservedName, new Object[] {
name, MergeStrategy.get(name).getClass(),
strategy.getClass() }), null);
result = false;
}
return result;
}
}
}