Move EclipseAuthenticator and EclipseProxySelector to egit.core
[egit/eclipse.git] / org.eclipse.egit.core / src / org / eclipse / egit / core / Activator.java
blob8bee0b46e1c8639114dce300cefd79d35494a58f
1 /*******************************************************************************
2 * Copyright (C) 2008, 2015 Shawn O. Pearce <spearce@spearce.org> and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
7 *******************************************************************************/
8 package org.eclipse.egit.core;
10 import java.io.File;
11 import java.io.IOException;
12 import java.net.Authenticator;
13 import java.net.ProxySelector;
14 import java.text.MessageFormat;
15 import java.util.Collection;
16 import java.util.Collections;
17 import java.util.Dictionary;
18 import java.util.HashMap;
19 import java.util.Hashtable;
20 import java.util.LinkedHashMap;
21 import java.util.LinkedHashSet;
22 import java.util.List;
23 import java.util.Map;
24 import java.util.Set;
26 import org.eclipse.core.net.proxy.IProxyService;
27 import org.eclipse.core.resources.IProject;
28 import org.eclipse.core.resources.IResource;
29 import org.eclipse.core.resources.IResourceChangeEvent;
30 import org.eclipse.core.resources.IResourceChangeListener;
31 import org.eclipse.core.resources.IResourceDelta;
32 import org.eclipse.core.resources.IResourceDeltaVisitor;
33 import org.eclipse.core.resources.ResourcesPlugin;
34 import org.eclipse.core.runtime.CoreException;
35 import org.eclipse.core.runtime.IConfigurationElement;
36 import org.eclipse.core.runtime.IExtension;
37 import org.eclipse.core.runtime.IExtensionPoint;
38 import org.eclipse.core.runtime.IExtensionRegistry;
39 import org.eclipse.core.runtime.IPath;
40 import org.eclipse.core.runtime.IProgressMonitor;
41 import org.eclipse.core.runtime.IRegistryEventListener;
42 import org.eclipse.core.runtime.IStatus;
43 import org.eclipse.core.runtime.NullProgressMonitor;
44 import org.eclipse.core.runtime.Path;
45 import org.eclipse.core.runtime.Platform;
46 import org.eclipse.core.runtime.Plugin;
47 import org.eclipse.core.runtime.Status;
48 import org.eclipse.core.runtime.jobs.Job;
49 import org.eclipse.core.runtime.preferences.DefaultScope;
50 import org.eclipse.core.runtime.preferences.IEclipsePreferences;
51 import org.eclipse.core.runtime.preferences.InstanceScope;
52 import org.eclipse.egit.core.internal.CoreText;
53 import org.eclipse.egit.core.internal.indexdiff.IndexDiffCache;
54 import org.eclipse.egit.core.internal.job.JobUtil;
55 import org.eclipse.egit.core.internal.trace.GitTraceLocation;
56 import org.eclipse.egit.core.internal.util.ResourceUtil;
57 import org.eclipse.egit.core.op.ConnectProviderOperation;
58 import org.eclipse.egit.core.op.IgnoreOperation;
59 import org.eclipse.egit.core.project.GitProjectData;
60 import org.eclipse.egit.core.project.RepositoryFinder;
61 import org.eclipse.egit.core.project.RepositoryMapping;
62 import org.eclipse.egit.core.securestorage.EGitSecureStore;
63 import org.eclipse.equinox.security.storage.SecurePreferencesFactory;
64 import org.eclipse.jgit.lib.Constants;
65 import org.eclipse.jgit.merge.MergeStrategy;
66 import org.eclipse.jgit.transport.SshSessionFactory;
67 import org.eclipse.jgit.util.FS;
68 import org.eclipse.jsch.core.IJSchService;
69 import org.eclipse.osgi.service.debug.DebugOptions;
70 import org.eclipse.osgi.service.debug.DebugOptionsListener;
71 import org.eclipse.osgi.util.NLS;
72 import org.eclipse.team.core.RepositoryProvider;
73 import org.osgi.framework.BundleContext;
74 import org.osgi.framework.ServiceReference;
76 /**
77 * The plugin class for the org.eclipse.egit.core plugin. This
78 * is a singleton class.
80 public class Activator extends Plugin implements DebugOptionsListener {
81 private static Activator plugin;
82 private static String pluginId;
83 private RepositoryCache repositoryCache;
84 private IndexDiffCache indexDiffCache;
85 private RepositoryUtil repositoryUtil;
86 private EGitSecureStore secureStore;
87 private AutoShareProjects shareGitProjectsJob;
88 private IResourceChangeListener preDeleteProjectListener;
89 private IgnoreDerivedResources ignoreDerivedResourcesListener;
90 private MergeStrategyRegistryListener mergeStrategyRegistryListener;
92 /**
93 * @return the singleton {@link Activator}
95 public static Activator getDefault() {
96 return plugin;
99 /**
100 * @return the name of this plugin
102 public static String getPluginId() {
103 return pluginId;
107 * Utility to create an error status for this plug-in.
109 * @param message User comprehensible message
110 * @param thr cause
111 * @return an initialized error status
113 public static IStatus error(final String message, final Throwable thr) {
114 return new Status(IStatus.ERROR, getPluginId(), 0, message, thr);
118 * Utility method to log errors in the Egit plugin.
119 * @param message User comprehensible message
120 * @param thr The exception through which we noticed the error
122 public static void logError(final String message, final Throwable thr) {
123 getDefault().getLog().log(error(message, thr));
127 * Log an info message for this plug-in
129 * @param message
131 public static void logInfo(final String message) {
132 getDefault().getLog().log(
133 new Status(IStatus.INFO, getPluginId(), 0, message, null));
137 * Utility to create a warning status for this plug-in.
139 * @param message
140 * User comprehensible message
141 * @param thr
142 * cause
143 * @return an initialized warning status
145 public static IStatus warning(final String message, final Throwable thr) {
146 return new Status(IStatus.WARNING, getPluginId(), 0, message, thr);
150 * Utility method to log warnings for this plug-in.
152 * @param message
153 * User comprehensible message
154 * @param thr
155 * The exception through which we noticed the warning
157 public static void logWarning(final String message, final Throwable thr) {
158 getDefault().getLog().log(warning(message, thr));
162 * Construct the {@link Activator} singleton instance
164 public Activator() {
165 Activator.setActivator(this);
168 private static void setActivator(Activator a) {
169 plugin = a;
172 @Override
173 public void start(final BundleContext context) throws Exception {
175 super.start(context);
177 pluginId = context.getBundle().getSymbolicName();
179 // we want to be notified about debug options changes
180 Dictionary<String, String> props = new Hashtable<String, String>(4);
181 props.put(DebugOptions.LISTENER_SYMBOLICNAME, pluginId);
182 context.registerService(DebugOptionsListener.class.getName(), this,
183 props);
185 setupSSH(context);
186 setupProxy(context);
188 repositoryCache = new RepositoryCache();
189 indexDiffCache = new IndexDiffCache();
190 try {
191 GitProjectData.reconfigureWindowCache();
192 } catch (RuntimeException e) {
193 logError(CoreText.Activator_ReconfigureWindowCacheError, e);
195 GitProjectData.attachToWorkspace();
197 repositoryUtil = new RepositoryUtil();
199 secureStore = new EGitSecureStore(SecurePreferencesFactory.getDefault());
201 registerAutoShareProjects();
202 registerAutoIgnoreDerivedResources();
203 registerPreDeleteResourceChangeListener();
204 registerMergeStrategyRegistryListener();
207 @SuppressWarnings("unchecked")
208 private void setupSSH(final BundleContext context) {
209 final ServiceReference ssh;
211 ssh = context.getServiceReference(IJSchService.class.getName());
212 if (ssh != null) {
213 SshSessionFactory.setInstance(new EclipseSshSessionFactory(
214 (IJSchService) context.getService(ssh)));
218 @SuppressWarnings("unchecked")
219 private void setupProxy(final BundleContext context) {
220 final ServiceReference proxy;
222 proxy = context.getServiceReference(IProxyService.class.getName());
223 if (proxy != null) {
224 ProxySelector.setDefault(new EclipseProxySelector(
225 (IProxyService) context.getService(proxy)));
226 Authenticator.setDefault(new EclipseAuthenticator(
227 (IProxyService) context.getService(proxy)));
231 private void registerPreDeleteResourceChangeListener() {
232 if (preDeleteProjectListener == null) {
233 preDeleteProjectListener = new IResourceChangeListener() {
235 @Override
236 public void resourceChanged(IResourceChangeEvent event) {
237 IResource resource = event.getResource();
238 if (resource instanceof IProject) {
239 IProject project = (IProject) resource;
240 if (project.isAccessible()) {
241 if (ResourceUtil.isSharedWithGit(project)) {
242 IResource dotGit = project
243 .findMember(Constants.DOT_GIT);
244 if (dotGit != null && dotGit
245 .getType() == IResource.FOLDER) {
246 GitProjectData.reconfigureWindowCache();
249 } else {
250 // bug 419706: project is closed - use java.io API
251 IPath locationPath = project.getLocation();
252 if (locationPath != null) {
253 File locationDir = locationPath.toFile();
254 File dotGit = new File(locationDir,
255 Constants.DOT_GIT);
256 if (dotGit.exists() && dotGit.isDirectory()) {
257 GitProjectData.reconfigureWindowCache();
264 ResourcesPlugin.getWorkspace().addResourceChangeListener(preDeleteProjectListener, IResourceChangeEvent.PRE_DELETE);
268 @Override
269 public void optionsChanged(DebugOptions options) {
270 // initialize the trace stuff
271 GitTraceLocation.initializeFromOptions(options, isDebugging());
275 * Provides the 3-way merge strategy to use according to the user's
276 * preferences. The preferred merge strategy is JGit's default merge
277 * strategy unless the user has explicitly chosen a different strategy among
278 * the registered strategies.
280 * @return The MergeStrategy to use, can be {@code null}, in which case the
281 * default merge strategy should be used as defined by JGit.
282 * @since 4.1
284 public MergeStrategy getPreferredMergeStrategy() {
285 // Get preferences set by user in the UI
286 final IEclipsePreferences prefs = InstanceScope.INSTANCE
287 .getNode(Activator.getPluginId());
288 String preferredMergeStrategyKey = prefs.get(
289 GitCorePreferences.core_preferredMergeStrategy, null);
291 // Get default preferences, wherever they are defined
292 if (preferredMergeStrategyKey == null
293 || preferredMergeStrategyKey.isEmpty()) {
294 final IEclipsePreferences defaultPrefs = DefaultScope.INSTANCE
295 .getNode(Activator.getPluginId());
296 preferredMergeStrategyKey = defaultPrefs.get(
297 GitCorePreferences.core_preferredMergeStrategy, null);
299 if (preferredMergeStrategyKey != null
300 && !preferredMergeStrategyKey.isEmpty()
301 && !GitCorePreferences.core_preferredMergeStrategy_Default
302 .equals(preferredMergeStrategyKey)) {
303 MergeStrategy result = MergeStrategy.get(preferredMergeStrategyKey);
304 if (result != null) {
305 return result;
307 logError(NLS.bind(CoreText.Activator_invalidPreferredMergeStrategy,
308 preferredMergeStrategyKey), null);
310 return null;
314 * @return Provides a read-only view of the registered MergeStrategies
315 * available.
316 * @since 4.1
318 public Collection<MergeStrategyDescriptor> getRegisteredMergeStrategies() {
319 return mergeStrategyRegistryListener.getStrategies();
322 private void registerMergeStrategyRegistryListener() {
323 mergeStrategyRegistryListener = new MergeStrategyRegistryListener(
324 Platform.getExtensionRegistry());
325 Platform.getExtensionRegistry().addListener(
326 mergeStrategyRegistryListener,
327 "org.eclipse.egit.core.mergeStrategy"); //$NON-NLS-1$
331 * @return cache for Repository objects
333 public RepositoryCache getRepositoryCache() {
334 return repositoryCache;
338 * @return cache for index diffs
340 public IndexDiffCache getIndexDiffCache() {
341 return indexDiffCache;
345 * @return the {@link RepositoryUtil} instance
347 public RepositoryUtil getRepositoryUtil() {
348 return repositoryUtil;
352 * @return the secure store
354 public EGitSecureStore getSecureStore() {
355 return secureStore;
358 @Override
359 public void stop(final BundleContext context) throws Exception {
360 GitProjectData.detachFromWorkspace();
361 repositoryCache.clear();
362 repositoryCache = null;
363 indexDiffCache.dispose();
364 indexDiffCache = null;
365 repositoryUtil.dispose();
366 repositoryUtil = null;
367 secureStore = null;
368 super.stop(context);
369 plugin = null;
370 if (preDeleteProjectListener != null) {
371 ResourcesPlugin.getWorkspace().removeResourceChangeListener(preDeleteProjectListener);
372 preDeleteProjectListener = null;
374 if (ignoreDerivedResourcesListener != null) {
375 ResourcesPlugin.getWorkspace().removeResourceChangeListener(
376 ignoreDerivedResourcesListener);
377 ignoreDerivedResourcesListener = null;
379 if (shareGitProjectsJob != null) {
380 ResourcesPlugin.getWorkspace().removeResourceChangeListener(
381 shareGitProjectsJob);
382 shareGitProjectsJob = null;
386 private void registerAutoShareProjects() {
387 shareGitProjectsJob = new AutoShareProjects();
388 ResourcesPlugin.getWorkspace().addResourceChangeListener(
389 shareGitProjectsJob, IResourceChangeEvent.POST_CHANGE);
392 private static class AutoShareProjects implements IResourceChangeListener {
394 private static int INTERESTING_CHANGES = IResourceDelta.ADDED
395 | IResourceDelta.OPEN;
397 private final CheckProjectsToShare checkProjectsJob;
399 public AutoShareProjects() {
400 checkProjectsJob = new CheckProjectsToShare();
403 private boolean doAutoShare() {
404 IEclipsePreferences d = DefaultScope.INSTANCE.getNode(Activator
405 .getPluginId());
406 IEclipsePreferences p = InstanceScope.INSTANCE.getNode(Activator
407 .getPluginId());
408 return p.getBoolean(GitCorePreferences.core_autoShareProjects, d
409 .getBoolean(GitCorePreferences.core_autoShareProjects,
410 true));
413 @Override
414 public void resourceChanged(IResourceChangeEvent event) {
415 if (!doAutoShare()) {
416 return;
418 try {
419 final Set<IProject> projectCandidates = new LinkedHashSet<>();
420 event.getDelta().accept(new IResourceDeltaVisitor() {
421 @Override
422 public boolean visit(IResourceDelta delta)
423 throws CoreException {
424 return collectOpenedProjects(delta,
425 projectCandidates);
428 if(!projectCandidates.isEmpty()){
429 checkProjectsJob.addProjectsToCheck(projectCandidates);
431 } catch (CoreException e) {
432 Activator.logError(e.getMessage(), e);
433 return;
438 * This method should not use RepositoryMapping.getMapping(project) or
439 * RepositoryProvider.getProvider(project) which can trigger
440 * RepositoryProvider.map(project) and deadlock current thread. See
441 * https://bugs.eclipse.org/bugs/show_bug.cgi?id=468270
443 private boolean collectOpenedProjects(IResourceDelta delta,
444 Set<IProject> projects) {
445 if (delta.getKind() == IResourceDelta.CHANGED
446 && (delta.getFlags() & INTERESTING_CHANGES) == 0) {
447 return true;
449 final IResource resource = delta.getResource();
450 if (resource.getType() == IResource.ROOT) {
451 return true;
453 if (resource.getType() != IResource.PROJECT) {
454 return false;
456 if (!resource.isAccessible() || resource.getLocation() == null) {
457 return false;
459 projects.add((IProject) resource);
460 return false;
465 private static class CheckProjectsToShare extends Job {
466 private Object lock = new Object();
468 private Set<IProject> projectCandidates;
470 public CheckProjectsToShare() {
471 super(CoreText.Activator_AutoShareJobName);
472 this.projectCandidates = new LinkedHashSet<IProject>();
473 setUser(false);
474 setSystem(true);
477 public void addProjectsToCheck(Set<IProject> projects) {
478 synchronized (lock) {
479 this.projectCandidates.addAll(projects);
480 if (!projectCandidates.isEmpty()) {
481 schedule(100);
486 @Override
487 protected IStatus run(IProgressMonitor monitor) {
488 Set<IProject> projectsToCheck;
489 synchronized (lock) {
490 projectsToCheck = projectCandidates;
491 projectCandidates = new LinkedHashSet<>();
493 if (projectsToCheck.isEmpty()) {
494 return Status.OK_STATUS;
497 final Map<IProject, File> projects = new HashMap<IProject, File>();
498 for (IProject project : projectsToCheck) {
499 if (project.isAccessible()) {
500 try {
501 visitConnect(project, projects);
502 } catch (CoreException e) {
503 logError(e.getMessage(), e);
507 if (monitor.isCanceled()) {
508 return Status.CANCEL_STATUS;
510 if (projects.size() > 0) {
511 ConnectProviderOperation op = new ConnectProviderOperation(
512 projects);
513 op.setRefreshResources(false);
514 JobUtil.scheduleUserJob(op,
515 CoreText.Activator_AutoShareJobName,
516 JobFamilies.AUTO_SHARE);
518 return Status.OK_STATUS;
521 private void visitConnect(IProject project,
522 final Map<IProject, File> projects) throws CoreException {
524 if (RepositoryMapping.getMapping(project) != null) {
525 return;
527 RepositoryProvider provider = RepositoryProvider
528 .getProvider(project);
529 // respect if project is already shared with another
530 // team provider
531 if (provider != null) {
532 return;
534 RepositoryFinder f = new RepositoryFinder(project);
535 f.setFindInChildren(false);
536 List<RepositoryMapping> mappings = f
537 .find(new NullProgressMonitor());
538 if (mappings.isEmpty()) {
539 return;
541 RepositoryMapping m = mappings.get(0);
542 IPath gitDirPath = m.getGitDirAbsolutePath();
543 if (gitDirPath == null || gitDirPath.segmentCount() == 0) {
544 return;
547 IPath workingDir = gitDirPath.removeLastSegments(1);
548 // Don't connect "/" or "C:\"
549 if (workingDir.isRoot()) {
550 return;
553 File userHome = FS.DETECTED.userHome();
554 if (userHome != null) {
555 Path userHomePath = new Path(userHome.getAbsolutePath());
556 // Don't connect "/home" or "/home/username"
557 if (workingDir.isPrefixOf(userHomePath)) {
558 return;
562 // connect
563 File repositoryDir = gitDirPath.toFile();
564 projects.put(project, repositoryDir);
566 // If we had more than one mapping: add the last one as
567 // 'configured' repository. We don't want to add submodules,
568 // that would only lead to problems when a configured repository
569 // is deleted.
570 int nofMappings = mappings.size();
571 if (nofMappings > 1) {
572 IPath lastPath = mappings.get(nofMappings - 1)
573 .getGitDirAbsolutePath();
574 if (lastPath != null) {
575 repositoryDir = lastPath.toFile();
578 try {
579 Activator.getDefault().getRepositoryUtil()
580 .addConfiguredRepository(repositoryDir);
581 } catch (IllegalArgumentException e) {
582 logError(CoreText.Activator_AutoSharingFailed, e);
587 private void registerAutoIgnoreDerivedResources() {
588 ignoreDerivedResourcesListener = new IgnoreDerivedResources();
589 ResourcesPlugin.getWorkspace().addResourceChangeListener(
590 ignoreDerivedResourcesListener,
591 IResourceChangeEvent.POST_CHANGE);
595 * @return true if the derived resources should be automatically added to
596 * the .gitignore files
598 public static boolean autoIgnoreDerived() {
599 IEclipsePreferences d = DefaultScope.INSTANCE
600 .getNode(Activator.getPluginId());
601 IEclipsePreferences p = InstanceScope.INSTANCE
602 .getNode(Activator.getPluginId());
603 return p.getBoolean(GitCorePreferences.core_autoIgnoreDerivedResources,
604 d.getBoolean(GitCorePreferences.core_autoIgnoreDerivedResources,
605 true));
608 private static class IgnoreDerivedResources implements
609 IResourceChangeListener {
612 @Override
613 public void resourceChanged(IResourceChangeEvent event) {
614 try {
615 IResourceDelta d = event.getDelta();
616 if (d == null || !autoIgnoreDerived()) {
617 return;
620 final Set<IPath> toBeIgnored = new LinkedHashSet<IPath>();
622 d.accept(new IResourceDeltaVisitor() {
624 @Override
625 public boolean visit(IResourceDelta delta)
626 throws CoreException {
627 if ((delta.getKind() & (IResourceDelta.ADDED | IResourceDelta.CHANGED)) == 0)
628 return false;
629 int flags = delta.getFlags();
630 if ((flags != 0)
631 && ((flags & IResourceDelta.DERIVED_CHANGED) == 0))
632 return false;
634 final IResource r = delta.getResource();
635 // don't consider resources contained in a project not
636 // shared with Git team provider
637 if ((r.getProject() != null)
638 && (RepositoryMapping.getMapping(r) == null))
639 return false;
640 if (r.isTeamPrivateMember())
641 return false;
643 if (r.isDerived()) {
644 try {
645 IPath location = r.getLocation();
646 if (RepositoryUtil.canBeAutoIgnored(location)) {
647 toBeIgnored.add(location);
649 } catch (IOException e) {
650 logError(
651 MessageFormat.format(
652 CoreText.Activator_ignoreResourceFailed,
653 r.getFullPath()), e);
655 return false;
657 return true;
660 if (toBeIgnored.size() > 0)
661 JobUtil.scheduleUserJob(new IgnoreOperation(toBeIgnored),
662 CoreText.Activator_autoIgnoreDerivedResources,
663 JobFamilies.AUTO_IGNORE);
664 } catch (CoreException e) {
665 Activator.logError(e.getMessage(), e);
666 return;
672 * Describes a MergeStrategy which can be registered with the mergeStrategy
673 * extension point.
675 * @since 4.1
677 public static class MergeStrategyDescriptor {
678 private final String name;
680 private final String label;
682 private final Class<?> implementedBy;
685 * @param name
686 * The referred strategy's name, to use for retrieving the
687 * strategy from MergeRegistry via
688 * {@link MergeStrategy#get(String)}
689 * @param label
690 * The label to display to users so they can select the
691 * strategy they need
692 * @param implementedBy
693 * The class of the MergeStrategy registered through the
694 * mergeStrategy extension point
696 public MergeStrategyDescriptor(String name, String label,
697 Class<?> implementedBy) {
698 this.name = name;
699 this.label = label;
700 this.implementedBy = implementedBy;
704 * @return The actual strategy's name, which can be used to retrieve
705 * that actual strategy via {@link MergeStrategy#get(String)}.
707 public String getName() {
708 return name;
712 * @return The strategy label, for display purposes.
714 public String getLabel() {
715 return label;
719 * @return The class of the MergeStrategy registered through the
720 * mergeStrategy extension point.
722 public Class<?> getImplementedBy() {
723 return implementedBy;
727 private static class MergeStrategyRegistryListener implements
728 IRegistryEventListener {
730 private Map<String, MergeStrategyDescriptor> strategies;
732 private MergeStrategyRegistryListener(IExtensionRegistry registry) {
733 strategies = new LinkedHashMap<>();
734 IConfigurationElement[] elements = registry
735 .getConfigurationElementsFor("org.eclipse.egit.core.mergeStrategy"); //$NON-NLS-1$
736 loadMergeStrategies(elements);
739 private Collection<MergeStrategyDescriptor> getStrategies() {
740 return Collections.unmodifiableCollection(strategies.values());
743 @Override
744 public void added(IExtension[] extensions) {
745 for (IExtension extension : extensions) {
746 loadMergeStrategies(extension.getConfigurationElements());
750 @Override
751 public void added(IExtensionPoint[] extensionPoints) {
752 // Nothing to do here
755 @Override
756 public void removed(IExtension[] extensions) {
757 for (IExtension extension : extensions) {
758 for (IConfigurationElement element : extension
759 .getConfigurationElements()) {
760 try {
761 Object ext = element.createExecutableExtension("class"); //$NON-NLS-1$
762 if (ext instanceof MergeStrategy) {
763 MergeStrategy strategy = (MergeStrategy) ext;
764 strategies.remove(strategy.getName());
766 } catch (CoreException e) {
767 Activator.logError(CoreText.MergeStrategy_UnloadError,
774 @Override
775 public void removed(IExtensionPoint[] extensionPoints) {
776 // Nothing to do here
779 private void loadMergeStrategies(IConfigurationElement[] elements) {
780 for (IConfigurationElement element : elements) {
781 try {
782 Object ext = element.createExecutableExtension("class"); //$NON-NLS-1$
783 if (ext instanceof MergeStrategy) {
784 MergeStrategy strategy = (MergeStrategy) ext;
785 String name = element.getAttribute("name"); //$NON-NLS-1$
786 if (name == null || name.isEmpty()) {
787 name = strategy.getName();
789 if (canRegister(name, strategy)) {
790 if (MergeStrategy.get(name) == null) {
791 MergeStrategy.register(name, strategy);
793 strategies
794 .put(name,
795 new MergeStrategyDescriptor(
796 name,
797 element.getAttribute("label"), //$NON-NLS-1$
798 strategy.getClass()));
801 } catch (CoreException e) {
802 Activator.logError(CoreText.MergeStrategy_LoadError, e);
808 * Checks whether it's possible to register the provided strategy with
809 * the given name
811 * @param name
812 * Name to use to register the strategy
813 * @param strategy
814 * Strategy to register
815 * @return <code>true</code> if the name is neither null nor empty, no
816 * other strategy is already register for the same name, and the
817 * name is not one of the core JGit strategies. If the given
818 * name is that of a core JGit strategy, the method will return
819 * <code>true</code> only if the strategy is the matching JGit
820 * strategy for that name.
822 private boolean canRegister(String name, MergeStrategy strategy) {
823 boolean result = true;
824 if (name == null || name.isEmpty()) {
825 // name is mandatory
826 Activator.logError(
827 NLS.bind(CoreText.MergeStrategy_MissingName,
828 strategy.getClass()), null);
829 result = false;
830 } else if (strategies.containsKey(name)) {
831 // Other strategy already registered for this name
832 Activator.logError(NLS.bind(
833 CoreText.MergeStrategy_DuplicateName, new Object[] {
834 name, strategies.get(name).getImplementedBy(),
835 strategy.getClass() }), null);
836 result = false;
837 } else if (MergeStrategy.get(name) != null
838 && MergeStrategy.get(name) != strategy) {
839 // The name is reserved by a core JGit strategy, and the
840 // provided instance is not that of JGit
841 Activator.logError(NLS.bind(
842 CoreText.MergeStrategy_ReservedName, new Object[] {
843 name, MergeStrategy.get(name).getClass(),
844 strategy.getClass() }), null);
845 result = false;
847 return result;