1 /*******************************************************************************
2 * Copyright (C) 2007, IBM Corporation and others
3 * Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>
4 * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
5 * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
6 * Copyright (C) 2008, Google Inc.
7 * Copyright (C) 2008, Tor Arne Vestbø <torarnv@gmail.com>
9 * All rights reserved. This program and the accompanying materials
10 * are made available under the terms of the Eclipse Public License v1.0
11 * See LICENSE for the full license text, also available.
12 *******************************************************************************/
14 package org
.spearce
.egit
.ui
.internal
.decorators
;
16 import java
.io
.IOException
;
17 import java
.util
.ArrayList
;
18 import java
.util
.Collections
;
19 import java
.util
.HashMap
;
20 import java
.util
.HashSet
;
21 import java
.util
.List
;
25 import org
.eclipse
.core
.resources
.IProject
;
26 import org
.eclipse
.core
.resources
.IResource
;
27 import org
.eclipse
.core
.resources
.IResourceChangeEvent
;
28 import org
.eclipse
.core
.resources
.IResourceChangeListener
;
29 import org
.eclipse
.core
.resources
.IResourceDelta
;
30 import org
.eclipse
.core
.resources
.IResourceDeltaVisitor
;
31 import org
.eclipse
.core
.resources
.IResourceVisitor
;
32 import org
.eclipse
.core
.resources
.ResourcesPlugin
;
33 import org
.eclipse
.core
.resources
.mapping
.ResourceMapping
;
34 import org
.eclipse
.core
.runtime
.CoreException
;
35 import org
.eclipse
.core
.runtime
.IAdaptable
;
36 import org
.eclipse
.core
.runtime
.IStatus
;
37 import org
.eclipse
.jface
.preference
.IPreferenceStore
;
38 import org
.eclipse
.jface
.resource
.ImageDescriptor
;
39 import org
.eclipse
.jface
.util
.IPropertyChangeListener
;
40 import org
.eclipse
.jface
.util
.PropertyChangeEvent
;
41 import org
.eclipse
.jface
.viewers
.IDecoration
;
42 import org
.eclipse
.jface
.viewers
.ILightweightLabelDecorator
;
43 import org
.eclipse
.jface
.viewers
.LabelProvider
;
44 import org
.eclipse
.jface
.viewers
.LabelProviderChangedEvent
;
45 import org
.eclipse
.osgi
.util
.TextProcessor
;
46 import org
.eclipse
.swt
.graphics
.ImageData
;
47 import org
.eclipse
.swt
.widgets
.Display
;
48 import org
.eclipse
.team
.core
.Team
;
49 import org
.eclipse
.team
.ui
.ISharedImages
;
50 import org
.eclipse
.team
.ui
.TeamImages
;
51 import org
.eclipse
.team
.ui
.TeamUI
;
52 import org
.eclipse
.ui
.IContributorResourceAdapter
;
53 import org
.eclipse
.ui
.PlatformUI
;
54 import org
.spearce
.egit
.core
.GitException
;
55 import org
.spearce
.egit
.core
.internal
.util
.ExceptionCollector
;
56 import org
.spearce
.egit
.core
.project
.GitProjectData
;
57 import org
.spearce
.egit
.core
.project
.RepositoryChangeListener
;
58 import org
.spearce
.egit
.core
.project
.RepositoryMapping
;
59 import org
.spearce
.egit
.ui
.Activator
;
60 import org
.spearce
.egit
.ui
.UIIcons
;
61 import org
.spearce
.egit
.ui
.UIPreferences
;
62 import org
.spearce
.egit
.ui
.UIText
;
63 import org
.spearce
.egit
.ui
.internal
.decorators
.IDecoratableResource
.Staged
;
64 import org
.spearce
.jgit
.dircache
.DirCache
;
65 import org
.spearce
.jgit
.dircache
.DirCacheEntry
;
66 import org
.spearce
.jgit
.dircache
.DirCacheIterator
;
67 import org
.spearce
.jgit
.lib
.Constants
;
68 import org
.spearce
.jgit
.lib
.FileMode
;
69 import org
.spearce
.jgit
.lib
.IndexChangedEvent
;
70 import org
.spearce
.jgit
.lib
.ObjectId
;
71 import org
.spearce
.jgit
.lib
.RefsChangedEvent
;
72 import org
.spearce
.jgit
.lib
.Repository
;
73 import org
.spearce
.jgit
.lib
.RepositoryChangedEvent
;
74 import org
.spearce
.jgit
.lib
.RepositoryListener
;
75 import org
.spearce
.jgit
.revwalk
.RevWalk
;
76 import org
.spearce
.jgit
.treewalk
.EmptyTreeIterator
;
77 import org
.spearce
.jgit
.treewalk
.TreeWalk
;
78 import org
.spearce
.jgit
.treewalk
.filter
.PathFilterGroup
;
81 * Supplies annotations for displayed resources
83 * This decorator provides annotations to indicate the status of each resource
84 * when compared to <code>HEAD</code>, as well as the index in the relevant
87 * TODO: Add support for colors and font decoration
89 public class GitLightweightDecorator
extends LabelProvider
implements
90 ILightweightLabelDecorator
, IPropertyChangeListener
,
91 IResourceChangeListener
, RepositoryChangeListener
, RepositoryListener
{
94 * Property constant pointing back to the extension point id of the
97 public static final String DECORATOR_ID
= "org.spearce.egit.ui.internal.decorators.GitLightweightDecorator"; //$NON-NLS-1$
100 * Bit-mask describing interesting changes for IResourceChangeListener
103 private static int INTERESTING_CHANGES
= IResourceDelta
.CONTENT
104 | IResourceDelta
.MOVED_FROM
| IResourceDelta
.MOVED_TO
105 | IResourceDelta
.OPEN
| IResourceDelta
.REPLACED
106 | IResourceDelta
.TYPE
;
109 * Collector for keeping the error view from filling up with exceptions
111 private static ExceptionCollector exceptions
= new ExceptionCollector(
112 UIText
.Decorator_exceptionMessage
, Activator
.getPluginId(),
113 IStatus
.ERROR
, Activator
.getDefault().getLog());
116 * Constructs a new Git resource decorator
118 public GitLightweightDecorator() {
119 TeamUI
.addPropertyChangeListener(this);
120 Activator
.addPropertyChangeListener(this);
121 PlatformUI
.getWorkbench().getThemeManager().getCurrentTheme()
122 .addPropertyChangeListener(this);
123 Repository
.addAnyRepositoryChangedListener(this);
124 GitProjectData
.addRepositoryChangeListener(this);
125 ResourcesPlugin
.getWorkspace().addResourceChangeListener(this,
126 IResourceChangeEvent
.POST_CHANGE
);
132 * @see org.eclipse.jface.viewers.IBaseLabelProvider#dispose()
135 public void dispose() {
137 PlatformUI
.getWorkbench().getThemeManager().getCurrentTheme()
138 .removePropertyChangeListener(this);
139 TeamUI
.removePropertyChangeListener(this);
140 Activator
.removePropertyChangeListener(this);
141 Repository
.removeAnyRepositoryChangedListener(this);
142 GitProjectData
.removeRepositoryChangeListener(this);
143 ResourcesPlugin
.getWorkspace().removeResourceChangeListener(this);
147 * This method should only be called by the decorator thread.
149 * @see org.eclipse.jface.viewers.ILightweightLabelDecorator#decorate(java.lang.Object,
150 * org.eclipse.jface.viewers.IDecoration)
152 public void decorate(Object element
, IDecoration decoration
) {
153 final IResource resource
= getResource(element
);
154 if (resource
== null)
157 // Don't decorate the workspace root
158 if (resource
.getType() == IResource
.ROOT
)
161 // Don't decorate non-existing resources
162 if (!resource
.exists() && !resource
.isPhantom())
165 // Make sure we're dealing with a project under Git revision control
166 final RepositoryMapping mapping
= RepositoryMapping
167 .getMapping(resource
);
171 // Cannot decorate linked resources
172 if (mapping
.getRepoRelativePath(resource
) == null)
175 // Don't decorate if UI plugin is not running
176 Activator activator
= Activator
.getDefault();
177 if (activator
== null)
181 DecorationHelper helper
= new DecorationHelper(activator
182 .getPreferenceStore());
183 helper
.decorate(decoration
,
184 new DecoratableResourceAdapter(resource
));
185 } catch (IOException e
) {
186 handleException(resource
, GitException
.wrapException(e
));
190 private class DecoratableResourceAdapter
implements IDecoratableResource
{
192 private final IResource resource
;
194 private final RepositoryMapping mapping
;
196 private final Repository repository
;
198 private final ObjectId headId
;
200 private String branch
= "";
202 private boolean tracked
= false;
204 private boolean ignored
= false;
206 private boolean dirty
= false;
208 private boolean conflicts
= false;
210 private boolean assumeValid
= false;
212 private Staged staged
= Staged
.NOT_STAGED
;
214 static final int T_HEAD
= 0;
216 static final int T_INDEX
= 1;
218 static final int T_WORKSPACE
= 2;
220 public DecoratableResourceAdapter(IResource resourceToWrap
)
222 resource
= resourceToWrap
;
223 mapping
= RepositoryMapping
.getMapping(resource
);
224 repository
= mapping
.getRepository();
225 headId
= repository
.resolve(Constants
.HEAD
);
227 switch (resource
.getType()) {
229 extractFileProperties();
231 case IResource
.FOLDER
:
232 extractContainerProperties();
234 case IResource
.PROJECT
:
235 extractProjectProperties();
240 private void extractFileProperties() throws IOException
{
241 TreeWalk treeWalk
= createHeadVsIndexTreeWalk();
242 if (treeWalk
== null)
250 // TODO: Also read ignores from .git/info/excludes et al.
251 if (Team
.isIgnoredHint(resource
)) {
256 final DirCacheIterator indexIterator
= treeWalk
.getTree(T_INDEX
,
257 DirCacheIterator
.class);
258 final DirCacheEntry indexEntry
= indexIterator
!= null ? indexIterator
262 if (indexEntry
== null) {
263 staged
= Staged
.REMOVED
;
265 if (indexEntry
.isAssumeValid()) {
268 } else if (indexEntry
.getStage() > 0) {
270 } else if (treeWalk
.getRawMode(T_HEAD
) == FileMode
.MISSING
272 staged
= Staged
.ADDED
;
274 long indexEntryLastModified
= indexEntry
.getLastModified();
275 long resourceLastModified
= resource
.getLocalTimeStamp();
277 // C-Git under Windows stores timestamps with 1-seconds
278 // resolution, so we need to check to see if this is the
279 // case here, and possibly fix the timestamp of the resource
280 // to match the resolution of the index.
281 if (indexEntryLastModified
% 1000 == 0) {
282 resourceLastModified
-= resourceLastModified
% 1000;
285 if (resourceLastModified
!= indexEntryLastModified
) {
286 // TODO: Consider doing a content check here, to rule
287 // out false positives, as we might get mismatch between
288 // timestamps, even if the content is the same
292 if (treeWalk
.getRawMode(T_HEAD
) != treeWalk
294 || !treeWalk
.idEqual(T_HEAD
, T_INDEX
)) {
295 staged
= Staged
.MODIFIED
;
302 private void extractContainerProperties() throws IOException
{
303 TreeWalk treeWalk
= createHeadVsIndexTreeWalk();
304 if (treeWalk
== null)
312 // TODO: Also read ignores from .git/info/excludes et al.
313 if (Team
.isIgnoredHint(resource
)) {
318 // TODO: Compute dirty state for folder, using ContainerTreeIterator
319 // and ContainerDiffFilter
323 private void extractProjectProperties() throws IOException
{
324 branch
= repository
.getBranch();
327 // TODO: Compute dirty state for folder, using ContainerTreeIterator
328 // and ContainerDiffFilter
333 * Adds a filter to the specified tree walk limiting the results to only
334 * those matching the resource specified by
335 * <code>resourceToFilterBy</code>
337 * If the resource does not exists in the current repository, or it has
338 * an empty path (it is the project itself), the filter is not added,
339 * and the method returns <code>null</code>.
342 * the tree walk to add the filter to
343 * @param resourceToFilterBy
344 * the resource to filter by
346 * @return <code>true</code> if the filter could be added,
347 * <code>false</code> otherwise
349 private boolean addResourceFilter(final TreeWalk treeWalk
,
350 final IResource resourceToFilterBy
) {
351 Set
<String
> repositoryPaths
= Collections
.singleton(mapping
352 .getRepoRelativePath(resourceToFilterBy
));
353 if (repositoryPaths
.isEmpty() || repositoryPaths
.contains(""))
356 treeWalk
.setFilter(PathFilterGroup
357 .createFromStrings(repositoryPaths
));
362 * Helper method to create a new tree walk between HEAD and the index.
364 * @return the created tree walk, or null if it could not be created
365 * @throws IOException
366 * if there were errors when creating the tree walk
368 private TreeWalk
createHeadVsIndexTreeWalk() throws IOException
{
369 final TreeWalk treeWalk
= new TreeWalk(repository
);
370 if (!addResourceFilter(treeWalk
, resource
))
373 treeWalk
.setRecursive(treeWalk
.getFilter().shouldBeRecursive());
377 treeWalk
.addTree(new RevWalk(repository
).parseTree(headId
));
379 treeWalk
.addTree(new EmptyTreeIterator());
381 treeWalk
.addTree(new DirCacheIterator(DirCache
.read(repository
)));
385 public String
getName() {
386 return resource
.getName();
389 public int getType() {
390 return resource
.getType();
393 public String
getBranch() {
397 public boolean isTracked() {
401 public boolean isIgnored() {
405 public boolean isDirty() {
409 public Staged
staged() {
413 public boolean hasConflicts() {
417 public boolean isAssumeValid() {
423 * Helper class for doing resource decoration, based on the given
426 * Used for real-time decoration, as well as in the decorator preview
429 public static class DecorationHelper
{
432 public static final String BINDING_RESOURCE_NAME
= "name"; //$NON-NLS-1$
435 public static final String BINDING_BRANCH_NAME
= "branch"; //$NON-NLS-1$
438 public static final String BINDING_DIRTY_FLAG
= "dirty"; //$NON-NLS-1$
441 public static final String BINDING_STAGED_FLAG
= "staged"; //$NON-NLS-1$
443 private IPreferenceStore store
;
446 * Define a cached image descriptor which only creates the image data
449 private static class CachedImageDescriptor
extends ImageDescriptor
{
450 ImageDescriptor descriptor
;
454 public CachedImageDescriptor(ImageDescriptor descriptor
) {
455 this.descriptor
= descriptor
;
458 public ImageData
getImageData() {
460 data
= descriptor
.getImageData();
466 private static ImageDescriptor trackedImage
;
468 private static ImageDescriptor untrackedImage
;
470 private static ImageDescriptor stagedImage
;
472 private static ImageDescriptor stagedAddedImage
;
474 private static ImageDescriptor stagedRemovedImage
;
476 private static ImageDescriptor conflictImage
;
478 private static ImageDescriptor assumeValidImage
;
481 trackedImage
= new CachedImageDescriptor(TeamImages
482 .getImageDescriptor(ISharedImages
.IMG_CHECKEDIN_OVR
));
483 untrackedImage
= new CachedImageDescriptor(UIIcons
.OVR_UNTRACKED
);
484 stagedImage
= new CachedImageDescriptor(UIIcons
.OVR_STAGED
);
485 stagedAddedImage
= new CachedImageDescriptor(UIIcons
.OVR_STAGED_ADD
);
486 stagedRemovedImage
= new CachedImageDescriptor(
487 UIIcons
.OVR_STAGED_REMOVE
);
488 conflictImage
= new CachedImageDescriptor(UIIcons
.OVR_CONFLICT
);
489 assumeValidImage
= new CachedImageDescriptor(UIIcons
.OVR_ASSUMEVALID
);
493 * Constructs a decorator using the rules from the given
494 * <code>preferencesStore</code>
496 * @param preferencesStore
497 * the preferences store with the preferred decorator rules
499 public DecorationHelper(IPreferenceStore preferencesStore
) {
500 store
= preferencesStore
;
504 * Decorates the given <code>decoration</code> based on the state of the
505 * given <code>resource</code>, using the preferences passed when
506 * constructing this decoration helper.
509 * the decoration to decorate
511 * the resource to retrieve state from
513 public void decorate(IDecoration decoration
,
514 IDecoratableResource resource
) {
515 if (resource
.isIgnored())
518 decorateText(decoration
, resource
);
519 decorateIcons(decoration
, resource
);
522 private void decorateText(IDecoration decoration
,
523 IDecoratableResource resource
) {
525 switch (resource
.getType()) {
528 .getString(UIPreferences
.DECORATOR_FILETEXT_DECORATION
);
530 case IResource
.FOLDER
:
532 .getString(UIPreferences
.DECORATOR_FOLDERTEXT_DECORATION
);
534 case IResource
.PROJECT
:
536 .getString(UIPreferences
.DECORATOR_PROJECTTEXT_DECORATION
);
540 Map
<String
, String
> bindings
= new HashMap
<String
, String
>();
541 bindings
.put(BINDING_RESOURCE_NAME
, resource
.getName());
542 bindings
.put(BINDING_BRANCH_NAME
, resource
.getBranch());
543 bindings
.put(BINDING_DIRTY_FLAG
, resource
.isDirty() ?
">" : null);
544 bindings
.put(BINDING_STAGED_FLAG
,
545 resource
.staged() != Staged
.NOT_STAGED ?
"*" : null);
547 decorate(decoration
, format
, bindings
);
550 private void decorateIcons(IDecoration decoration
,
551 IDecoratableResource resource
) {
552 ImageDescriptor overlay
= null;
554 if (resource
.isTracked()) {
555 if (store
.getBoolean(UIPreferences
.DECORATOR_SHOW_TRACKED_ICON
))
556 overlay
= trackedImage
;
559 .getBoolean(UIPreferences
.DECORATOR_SHOW_ASSUME_VALID_ICON
)
560 && resource
.isAssumeValid())
561 overlay
= assumeValidImage
;
563 // Staged overrides tracked
564 Staged staged
= resource
.staged();
565 if (store
.getBoolean(UIPreferences
.DECORATOR_SHOW_STAGED_ICON
)
566 && staged
!= Staged
.NOT_STAGED
) {
567 if (staged
== Staged
.ADDED
)
568 overlay
= stagedAddedImage
;
569 else if (staged
== Staged
.REMOVED
)
570 overlay
= stagedRemovedImage
;
572 overlay
= stagedImage
;
575 // Conflicts override everything
576 if (store
.getBoolean(UIPreferences
.DECORATOR_SHOW_CONFLICTS_ICON
)
577 && resource
.hasConflicts())
578 overlay
= conflictImage
;
580 } else if (store
.getBoolean(UIPreferences
.DECORATOR_SHOW_UNTRACKED_ICON
)) {
581 overlay
= untrackedImage
;
584 // Overlays can only be added once, so do it at the end
585 decoration
.addOverlay(overlay
);
589 * Decorates the given <code>decoration</code>, using the specified text
590 * <code>format</code>, and mapped using the variable bindings from
591 * <code>bindings</code>
594 * the decoration to decorate
596 * the format to base the decoration on
598 * the bindings between variables in the format and actual
601 public static void decorate(IDecoration decoration
, String format
,
602 Map
<String
, String
> bindings
) {
603 StringBuffer prefix
= new StringBuffer();
604 StringBuffer suffix
= new StringBuffer();
605 StringBuffer output
= prefix
;
607 int length
= format
.length();
611 if ((end
= format
.indexOf('{', start
)) > -1) {
612 output
.append(format
.substring(start
+ 1, end
));
613 if ((start
= format
.indexOf('}', end
)) > -1) {
614 String key
= format
.substring(end
+ 1, start
);
617 // Allow users to override the binding
618 if (key
.indexOf(':') > -1) {
619 String
[] keyAndBinding
= key
.split(":", 2);
620 key
= keyAndBinding
[0];
621 if (keyAndBinding
.length
> 1
622 && bindings
.get(key
) != null)
623 bindings
.put(key
, keyAndBinding
[1]);
626 // We use the BINDING_RESOURCE_NAME key to determine if
627 // we are doing the prefix or suffix. The name isn't
628 // actually part of either.
629 if (key
.equals(BINDING_RESOURCE_NAME
)) {
633 s
= bindings
.get(key
);
639 // Support removing prefix character if binding is
641 int curLength
= output
.length();
643 char c
= output
.charAt(curLength
- 1);
644 if (c
== ':' || c
== '@') {
645 output
.deleteCharAt(curLength
- 1);
650 output
.append(format
.substring(end
, length
));
654 output
.append(format
.substring(start
+ 1, length
));
659 String prefixString
= prefix
.toString().replaceAll("^\\s+", "");
660 if (prefixString
!= null) {
661 decoration
.addPrefix(TextProcessor
.process(prefixString
,
662 "()[].")); //$NON-NLS-1$
664 String suffixString
= suffix
.toString().replaceAll("\\s+$", "");
665 if (suffixString
!= null) {
666 decoration
.addSuffix(TextProcessor
.process(suffixString
,
667 "()[].")); //$NON-NLS-1$
672 // -------- Refresh handling --------
675 * Perform a blanket refresh of all decorations
677 public static void refresh() {
678 Display
.getDefault().asyncExec(new Runnable() {
680 Activator
.getDefault().getWorkbench().getDecoratorManager()
681 .update(DECORATOR_ID
);
687 * Callback for IPropertyChangeListener events
689 * If any of the relevant preferences has been changed we refresh all
690 * decorations (all projects and their resources).
692 * @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent)
694 public void propertyChange(PropertyChangeEvent event
) {
695 final String prop
= event
.getProperty();
696 // If the property is of any interest to us
697 if (prop
.equals(TeamUI
.GLOBAL_IGNORES_CHANGED
)
698 || prop
.equals(TeamUI
.GLOBAL_FILE_TYPES_CHANGED
)
699 || prop
.equals(Activator
.DECORATORS_CHANGED
)) {
700 postLabelEvent(new LabelProviderChangedEvent(this, null /* all */));
705 * Callback for IResourceChangeListener events
707 * Schedules a refresh of the changed resource
709 * If the preference for computing deep dirty states has been set we walk
710 * the ancestor tree of the changed resource and update all parents as well.
712 * @see org.eclipse.core.resources.IResourceChangeListener#resourceChanged(org.eclipse.core.resources.IResourceChangeEvent)
714 public void resourceChanged(IResourceChangeEvent event
) {
715 final Set
<IResource
> resourcesToUpdate
= new HashSet
<IResource
>();
717 try { // Compute the changed resources by looking at the delta
718 event
.getDelta().accept(new IResourceDeltaVisitor() {
719 public boolean visit(IResourceDelta delta
) throws CoreException
{
720 final IResource resource
= delta
.getResource();
722 // If the resource is not part of a project under Git
724 final RepositoryMapping mapping
= RepositoryMapping
725 .getMapping(resource
);
726 if (mapping
== null) {
730 if (resource
.getType() == IResource
.ROOT
) {
731 // Continue with the delta
735 if (resource
.getType() == IResource
.PROJECT
) {
736 // If the project is not accessible, don't process it
737 if (!resource
.isAccessible())
741 // If the file has changed but not in a way that we care
743 // (e.g. marker changes to files) then ignore the change
744 if (delta
.getKind() == IResourceDelta
.CHANGED
745 && (delta
.getFlags() & INTERESTING_CHANGES
) == 0) {
749 // All seems good, schedule the resource for update
750 resourcesToUpdate
.add(resource
);
753 }, true /* includePhantoms */);
754 } catch (final CoreException e
) {
755 handleException(null, e
);
758 // If deep decorator calculation is enabled in the preferences we
759 // walk the ancestor tree of each of the changed resources and add
760 // their parents to the update set
761 final IPreferenceStore store
= Activator
.getDefault()
762 .getPreferenceStore();
763 if (store
.getBoolean(UIPreferences
.DECORATOR_CALCULATE_DIRTY
)) {
764 final IResource
[] changedResources
= resourcesToUpdate
765 .toArray(new IResource
[resourcesToUpdate
.size()]);
766 for (int i
= 0; i
< changedResources
.length
; i
++) {
767 IResource current
= changedResources
[i
];
768 while (current
.getType() != IResource
.ROOT
) {
769 current
= current
.getParent();
770 resourcesToUpdate
.add(current
);
775 postLabelEvent(new LabelProviderChangedEvent(this, resourcesToUpdate
780 * Callback for RepositoryListener events
782 * We resolve the repository mapping for the changed repository and forward
783 * that to repositoryChanged(RepositoryMapping).
786 * The original change event
788 private void repositoryChanged(RepositoryChangedEvent e
) {
789 final Set
<RepositoryMapping
> ms
= new HashSet
<RepositoryMapping
>();
790 for (final IProject p
: ResourcesPlugin
.getWorkspace().getRoot()
792 final RepositoryMapping mapping
= RepositoryMapping
.getMapping(p
);
793 if (mapping
!= null && mapping
.getRepository() == e
.getRepository())
796 for (final RepositoryMapping m
: ms
) {
797 repositoryChanged(m
);
805 * org.spearce.jgit.lib.RepositoryListener#indexChanged(org.spearce.jgit
806 * .lib.IndexChangedEvent)
808 public void indexChanged(IndexChangedEvent e
) {
809 repositoryChanged(e
);
816 * org.spearce.jgit.lib.RepositoryListener#refsChanged(org.spearce.jgit.
817 * lib.RefsChangedEvent)
819 public void refsChanged(RefsChangedEvent e
) {
820 repositoryChanged(e
);
824 * Callback for RepositoryChangeListener events, as well as
825 * RepositoryListener events via repositoryChanged()
827 * We resolve the project and schedule a refresh of each resource in the
830 * @see org.spearce.egit.core.project.RepositoryChangeListener#repositoryChanged(org.spearce.egit.core.project.RepositoryMapping)
832 public void repositoryChanged(RepositoryMapping mapping
) {
833 final IProject project
= mapping
.getContainer().getProject();
837 final List
<IResource
> resources
= new ArrayList
<IResource
>();
839 project
.accept(new IResourceVisitor() {
840 public boolean visit(IResource resource
) {
841 resources
.add(resource
);
845 postLabelEvent(new LabelProviderChangedEvent(this, resources
847 } catch (final CoreException e
) {
848 handleException(project
, e
);
852 // -------- Helper methods --------
854 private static IResource
getResource(Object element
) {
855 if (element
instanceof ResourceMapping
) {
856 element
= ((ResourceMapping
) element
).getModelObject();
859 IResource resource
= null;
860 if (element
instanceof IResource
) {
861 resource
= (IResource
) element
;
862 } else if (element
instanceof IAdaptable
) {
863 final IAdaptable adaptable
= (IAdaptable
) element
;
864 resource
= (IResource
) adaptable
.getAdapter(IResource
.class);
865 if (resource
== null) {
866 final IContributorResourceAdapter adapter
= (IContributorResourceAdapter
) adaptable
867 .getAdapter(IContributorResourceAdapter
.class);
869 resource
= adapter
.getAdaptedResource(adaptable
);
877 * Post the label event to the UI thread
882 private void postLabelEvent(final LabelProviderChangedEvent event
) {
883 Display
.getDefault().asyncExec(new Runnable() {
885 fireLabelProviderChanged(event
);
891 * Handle exceptions that occur in the decorator. Exceptions are only logged
892 * for resources that are accessible (i.e. exist in an open project).
895 * The resource that triggered the exception
897 * The exception that occurred
899 private static void handleException(IResource resource
, CoreException e
) {
900 if (resource
== null || resource
.isAccessible())
901 exceptions
.handleException(e
);