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
;
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
;
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
;
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
;
93 * @return the singleton {@link Activator}
95 public static Activator
getDefault() {
100 * @return the name of this plugin
102 public static String
getPluginId() {
107 * Utility to create an error status for this plug-in.
109 * @param message User comprehensible message
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
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.
140 * User comprehensible message
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.
153 * User comprehensible message
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
165 Activator
.setActivator(this);
168 private static void setActivator(Activator a
) {
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,
188 repositoryCache
= new RepositoryCache();
189 indexDiffCache
= new IndexDiffCache();
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());
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());
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() {
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();
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
,
256 if (dotGit
.exists() && dotGit
.isDirectory()) {
257 GitProjectData
.reconfigureWindowCache();
264 ResourcesPlugin
.getWorkspace().addResourceChangeListener(preDeleteProjectListener
, IResourceChangeEvent
.PRE_DELETE
);
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.
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) {
307 logError(NLS
.bind(CoreText
.Activator_invalidPreferredMergeStrategy
,
308 preferredMergeStrategyKey
), null);
314 * @return Provides a read-only view of the registered MergeStrategies
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() {
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;
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
406 IEclipsePreferences p
= InstanceScope
.INSTANCE
.getNode(Activator
408 return p
.getBoolean(GitCorePreferences
.core_autoShareProjects
, d
409 .getBoolean(GitCorePreferences
.core_autoShareProjects
,
414 public void resourceChanged(IResourceChangeEvent event
) {
415 if (!doAutoShare()) {
419 final Set
<IProject
> projectCandidates
= new LinkedHashSet
<>();
420 event
.getDelta().accept(new IResourceDeltaVisitor() {
422 public boolean visit(IResourceDelta delta
)
423 throws CoreException
{
424 return collectOpenedProjects(delta
,
428 if(!projectCandidates
.isEmpty()){
429 checkProjectsJob
.addProjectsToCheck(projectCandidates
);
431 } catch (CoreException e
) {
432 Activator
.logError(e
.getMessage(), e
);
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) {
449 final IResource resource
= delta
.getResource();
450 if (resource
.getType() == IResource
.ROOT
) {
453 if (resource
.getType() != IResource
.PROJECT
) {
456 if (!resource
.isAccessible() || resource
.getLocation() == null) {
459 projects
.add((IProject
) resource
);
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
>();
477 public void addProjectsToCheck(Set
<IProject
> projects
) {
478 synchronized (lock
) {
479 this.projectCandidates
.addAll(projects
);
480 if (!projectCandidates
.isEmpty()) {
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()) {
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(
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) {
527 RepositoryProvider provider
= RepositoryProvider
528 .getProvider(project
);
529 // respect if project is already shared with another
531 if (provider
!= null) {
534 RepositoryFinder f
= new RepositoryFinder(project
);
535 f
.setFindInChildren(false);
536 List
<RepositoryMapping
> mappings
= f
537 .find(new NullProgressMonitor());
538 if (mappings
.isEmpty()) {
541 RepositoryMapping m
= mappings
.get(0);
542 IPath gitDirPath
= m
.getGitDirAbsolutePath();
543 if (gitDirPath
== null || gitDirPath
.segmentCount() == 0) {
547 IPath workingDir
= gitDirPath
.removeLastSegments(1);
548 // Don't connect "/" or "C:\"
549 if (workingDir
.isRoot()) {
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
)) {
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
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();
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
,
608 private static class IgnoreDerivedResources
implements
609 IResourceChangeListener
{
613 public void resourceChanged(IResourceChangeEvent event
) {
615 IResourceDelta d
= event
.getDelta();
616 if (d
== null || !autoIgnoreDerived()) {
620 final Set
<IPath
> toBeIgnored
= new LinkedHashSet
<IPath
>();
622 d
.accept(new IResourceDeltaVisitor() {
625 public boolean visit(IResourceDelta delta
)
626 throws CoreException
{
627 if ((delta
.getKind() & (IResourceDelta
.ADDED
| IResourceDelta
.CHANGED
)) == 0)
629 int flags
= delta
.getFlags();
631 && ((flags
& IResourceDelta
.DERIVED_CHANGED
) == 0))
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))
640 if (r
.isTeamPrivateMember())
645 IPath location
= r
.getLocation();
646 if (RepositoryUtil
.canBeAutoIgnored(location
)) {
647 toBeIgnored
.add(location
);
649 } catch (IOException e
) {
651 MessageFormat
.format(
652 CoreText
.Activator_ignoreResourceFailed
,
653 r
.getFullPath()), e
);
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
);
672 * Describes a MergeStrategy which can be registered with the mergeStrategy
677 public static class MergeStrategyDescriptor
{
678 private final String name
;
680 private final String label
;
682 private final Class
<?
> implementedBy
;
686 * The referred strategy's name, to use for retrieving the
687 * strategy from MergeRegistry via
688 * {@link MergeStrategy#get(String)}
690 * The label to display to users so they can select the
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
) {
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() {
712 * @return The strategy label, for display purposes.
714 public String
getLabel() {
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());
744 public void added(IExtension
[] extensions
) {
745 for (IExtension extension
: extensions
) {
746 loadMergeStrategies(extension
.getConfigurationElements());
751 public void added(IExtensionPoint
[] extensionPoints
) {
752 // Nothing to do here
756 public void removed(IExtension
[] extensions
) {
757 for (IExtension extension
: extensions
) {
758 for (IConfigurationElement element
: extension
759 .getConfigurationElements()) {
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
,
775 public void removed(IExtensionPoint
[] extensionPoints
) {
776 // Nothing to do here
779 private void loadMergeStrategies(IConfigurationElement
[] elements
) {
780 for (IConfigurationElement element
: elements
) {
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
);
795 new MergeStrategyDescriptor(
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
812 * Name to use to register the 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()) {
827 NLS
.bind(CoreText
.MergeStrategy_MissingName
,
828 strategy
.getClass()), null);
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);
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);