From d422d75ee407a7e2476c0e3358766d367e5d6878 Mon Sep 17 00:00:00 2001 From: Philipp Thun Date: Thu, 3 Feb 2011 09:39:38 +0100 Subject: Queue decoration requests In order to avoid the creation of an individual TreeWalk for each resource, decoration requests are queued and then processed by a separate job using a single TreeWalk with n path filters (n = number of files). Project nodes and folders are processed as before. Bug: 325393 Change-Id: I0d63413c644b4dcfe9aaa6052e397673feaae6fb Signed-off-by: Philipp Thun Signed-off-by: Chris Aniszczyk --- .../src/org/eclipse/egit/ui/Activator.java | 11 ++ .../src/org/eclipse/egit/ui/UIText.java | 6 + .../internal/decorators/DecoratableResource.java | 118 ++++++++++++ .../decorators/DecoratableResourceAdapter.java | 144 ++------------- .../decorators/DecoratableResourceHelper.java | 202 +++++++++++++++++++++ .../ui/internal/decorators/GitDecoratorJob.java | 89 +++++++++ .../decorators/GitLightweightDecorator.java | 132 ++++++++++---- .../src/org/eclipse/egit/ui/uitext.properties | 4 + 8 files changed, 543 insertions(+), 163 deletions(-) create mode 100644 org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/decorators/DecoratableResource.java create mode 100644 org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/decorators/DecoratableResourceHelper.java create mode 100644 org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/decorators/GitDecoratorJob.java diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/Activator.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/Activator.java index 0f8b7eb276..edaa760bad 100644 --- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/Activator.java +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/Activator.java @@ -551,6 +551,17 @@ public class Activator extends AbstractUIPlugin implements DebugOptionsListener return new Status(IStatus.ERROR, getPluginId(), message, throwable); } + /** + * Creates an error status + * + * @param message + * a localized message + * @return a new Status object + */ + public static IStatus createErrorStatus(String message) { + return new Status(IStatus.ERROR, getPluginId(), message); + } + /** * @return the {@link RepositoryUtil} instance */ diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/UIText.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/UIText.java index c34ea9f1d3..23226acc34 100644 --- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/UIText.java +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/UIText.java @@ -2713,6 +2713,12 @@ public class UIText extends NLS { /** */ public static String GitBranchSynchronizeWizardPage_deselectAll; + /** */ + public static String GitLightweightDecorator_AsynchronousDecorationError; + + /** */ + public static String GitLightweightDecorator_ResourceError; + /** */ public static String GitTraceConfigurationDialog_ApplyButton; diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/decorators/DecoratableResource.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/decorators/DecoratableResource.java new file mode 100644 index 0000000000..176964d986 --- /dev/null +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/decorators/DecoratableResource.java @@ -0,0 +1,118 @@ +/******************************************************************************* + * Copyright (C) 2011, Philipp Thun + * + * 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.ui.internal.decorators; + +import org.eclipse.core.resources.IResource; + +/** + * Basic implementation of IDecoratableResource + * + * @see IDecoratableResource + */ +class DecoratableResource implements IDecoratableResource { + + /** + * Resource to be decorated + */ + IResource resource = null; + + /** + * Name of the repository of the resource + */ + String repositoryName = null; + + /** + * Current branch of the resource + */ + String branch = null; + + /** + * Flag indicating whether or not the resource is tracked + */ + boolean tracked = false; + + /** + * Flag indicating whether or not the resource is ignored + */ + boolean ignored = false; + + /** + * Flag indicating whether or not the resource has changes that are not + * staged + */ + boolean dirty = false; + + /** + * Staged state of the resource + */ + Staged staged = Staged.NOT_STAGED; + + /** + * Flag indicating whether or not the resource has merge conflicts + */ + boolean conflicts = false; + + /** + * Flag indicating whether or not the resource is assumed valid + */ + boolean assumeValid = false; + + /** + * Constructs a new decoratable resource + * + * This object represents the state of a resource used as a basis for + * decoration. + * + * @param resource + * resource to be decorated + */ + DecoratableResource(IResource resource) { + this.resource = resource; + } + + public int getType() { + return resource != null ? resource.getType() : 0; + } + + public String getName() { + return resource != null ? resource.getName() : null; + } + + public String getRepositoryName() { + return repositoryName; + } + + public String getBranch() { + return branch; + } + + public boolean isTracked() { + return tracked; + } + + public boolean isIgnored() { + return ignored; + } + + public boolean isDirty() { + return dirty; + } + + public Staged staged() { + return staged; + } + + public boolean hasConflicts() { + return conflicts; + } + + public boolean isAssumeValid() { + return assumeValid; + } +} diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/decorators/DecoratableResourceAdapter.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/decorators/DecoratableResourceAdapter.java index 8d8e01ddfa..3424c23348 100644 --- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/decorators/DecoratableResourceAdapter.java +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/decorators/DecoratableResourceAdapter.java @@ -30,7 +30,6 @@ import org.eclipse.egit.ui.Activator; import org.eclipse.egit.ui.UIPreferences; import org.eclipse.egit.ui.internal.trace.GitTraceLocation; import org.eclipse.jface.preference.IPreferenceStore; -import org.eclipse.jgit.dircache.DirCacheEntry; import org.eclipse.jgit.dircache.DirCacheIterator; import org.eclipse.jgit.errors.IncorrectObjectTypeException; import org.eclipse.jgit.errors.MissingObjectException; @@ -48,9 +47,7 @@ import org.eclipse.jgit.treewalk.filter.AndTreeFilter; import org.eclipse.jgit.treewalk.filter.PathFilterGroup; import org.eclipse.jgit.treewalk.filter.TreeFilter; -class DecoratableResourceAdapter implements IDecoratableResource { - - private final IResource resource; +class DecoratableResourceAdapter extends DecoratableResource { private final RepositoryMapping mapping; @@ -60,35 +57,13 @@ class DecoratableResourceAdapter implements IDecoratableResource { private final IPreferenceStore store; - private final String branch; - - private final String repositoryName; - - private boolean tracked = false; - - private boolean ignored = false; - - private boolean dirty = false; - - private boolean conflicts = false; - - private boolean assumeValid = false; - - private Staged staged = Staged.NOT_STAGED; - private final boolean trace; - static final int T_HEAD = 0; - - static final int T_INDEX = 1; - - static final int T_WORKSPACE = 2; - @SuppressWarnings("fallthrough") public DecoratableResourceAdapter(IResource resourceToWrap) throws IOException { + super(resourceToWrap); trace = GitTraceLocation.DECORATION.isActive(); - resource = resourceToWrap; long start = 0; if (trace) { GitTraceLocation.getTrace().trace( @@ -154,59 +129,7 @@ class DecoratableResourceAdapter implements IDecoratableResource { } private void extractResourceProperties(TreeWalk treeWalk) throws IOException { - final ContainerTreeIterator workspaceIterator = treeWalk.getTree( - T_WORKSPACE, ContainerTreeIterator.class); - final ResourceEntry resourceEntry = workspaceIterator != null ? workspaceIterator - .getResourceEntry() : null; - - if (resourceEntry == null) - return; - - if (workspaceIterator != null && workspaceIterator.isEntryIgnored()) { - ignored = true; - return; - } - - final int mHead = treeWalk.getRawMode(T_HEAD); - final int mIndex = treeWalk.getRawMode(T_INDEX); - - if (mHead == FileMode.MISSING.getBits() - && mIndex == FileMode.MISSING.getBits()) - return; - - tracked = true; - - if (mHead == FileMode.MISSING.getBits()) { - staged = Staged.ADDED; - } else if (mIndex == FileMode.MISSING.getBits()) { - staged = Staged.REMOVED; - } else if (mHead != mIndex - || (mIndex != FileMode.TREE.getBits() && !treeWalk.idEqual( - T_HEAD, T_INDEX))) { - staged = Staged.MODIFIED; - } else { - staged = Staged.NOT_STAGED; - } - - final DirCacheIterator indexIterator = treeWalk.getTree(T_INDEX, - DirCacheIterator.class); - final DirCacheEntry indexEntry = indexIterator != null ? indexIterator - .getDirCacheEntry() : null; - - if (indexEntry == null) - return; - - if (indexEntry.getStage() > 0) - conflicts = true; - - if (indexEntry.isAssumeValid()) { - dirty = false; - assumeValid = true; - } else { - if (workspaceIterator != null - && workspaceIterator.isModified(indexEntry, true)) - dirty = true; - } + DecoratableResourceHelper.decorateResource(this, treeWalk); } private class RecursiveStateFilter extends TreeFilter { @@ -231,7 +154,8 @@ class DecoratableResourceAdapter implements IDecoratableResource { GitTraceLocation.DECORATION.getLocation(), treeWalk.getPathString()); final WorkingTreeIterator workingTreeIterator = treeWalk.getTree( - T_WORKSPACE, WorkingTreeIterator.class); + DecoratableResourceHelper.T_WORKSPACE, + WorkingTreeIterator.class); if (workingTreeIterator != null) { if (workingTreeIterator instanceof ContainerTreeIterator) { final ContainerTreeIterator workspaceIterator = @@ -245,8 +169,10 @@ class DecoratableResourceAdapter implements IDecoratableResource { } if (resource.getFullPath().isPrefixOf( resourceEntry.getResource().getFullPath()) - && treeWalk.getFileMode(T_HEAD) == FileMode.MISSING - && treeWalk.getFileMode(T_INDEX) == FileMode.MISSING) { + && treeWalk + .getFileMode(DecoratableResourceHelper.T_HEAD) == FileMode.MISSING + && treeWalk + .getFileMode(DecoratableResourceHelper.T_INDEX) == FileMode.MISSING) { // we reached the folder to decorate (or are beyond) // we can cut if the current entry does not // exist in head and index @@ -272,8 +198,10 @@ class DecoratableResourceAdapter implements IDecoratableResource { } if (resPath.isPrefixOf(wdPath) - && treeWalk.getFileMode(T_HEAD) == FileMode.MISSING - && treeWalk.getFileMode(T_INDEX) == FileMode.MISSING) { + && treeWalk + .getFileMode(DecoratableResourceHelper.T_HEAD) == FileMode.MISSING + && treeWalk + .getFileMode(DecoratableResourceHelper.T_INDEX) == FileMode.MISSING) { // we reached the folder to decorate (or are beyond) // we can cut if the current entry does not // exist in head and index @@ -286,7 +214,8 @@ class DecoratableResourceAdapter implements IDecoratableResource { } } - if (FileMode.TREE.equals(treeWalk.getRawMode(T_WORKSPACE))) + if (FileMode.TREE.equals(treeWalk + .getRawMode(DecoratableResourceHelper.T_WORKSPACE))) return shouldRecurse(treeWalk); // Backup current state so far @@ -310,7 +239,8 @@ class DecoratableResourceAdapter implements IDecoratableResource { private boolean shouldRecurse(TreeWalk treeWalk) throws IOException { final WorkingTreeIterator workspaceIterator = treeWalk.getTree( - T_WORKSPACE, WorkingTreeIterator.class); + DecoratableResourceHelper.T_WORKSPACE, + WorkingTreeIterator.class); if (workspaceIterator instanceof AdaptableFileTreeIterator) return true; @@ -425,44 +355,4 @@ class DecoratableResourceAdapter implements IDecoratableResource { treeWalk.addTree(IteratorService.createInitialIterator(repository)); return treeWalk; } - - public String getName() { - return resource.getName(); - } - - public int getType() { - return resource.getType(); - } - - public String getRepositoryName() { - return repositoryName; - } - - public String getBranch() { - return branch; - } - - public boolean isTracked() { - return tracked; - } - - public boolean isIgnored() { - return ignored; - } - - public boolean isDirty() { - return dirty; - } - - public Staged staged() { - return staged; - } - - public boolean hasConflicts() { - return conflicts; - } - - public boolean isAssumeValid() { - return assumeValid; - } } diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/decorators/DecoratableResourceHelper.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/decorators/DecoratableResourceHelper.java new file mode 100644 index 0000000000..bfe3bdbc97 --- /dev/null +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/decorators/DecoratableResourceHelper.java @@ -0,0 +1,202 @@ +/******************************************************************************* + * Copyright (C) 2011, Philipp Thun + * + * 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.ui.internal.decorators; + +import java.io.IOException; +import java.util.ArrayList; + +import org.eclipse.core.resources.IResource; +import org.eclipse.egit.core.ContainerTreeIterator; +import org.eclipse.egit.core.ContainerTreeIterator.ResourceEntry; +import org.eclipse.egit.core.IteratorService; +import org.eclipse.egit.core.project.RepositoryMapping; +import org.eclipse.egit.ui.internal.decorators.IDecoratableResource.Staged; +import org.eclipse.jgit.dircache.DirCacheEntry; +import org.eclipse.jgit.dircache.DirCacheIterator; +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.FileMode; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.revwalk.RevWalk; +import org.eclipse.jgit.treewalk.EmptyTreeIterator; +import org.eclipse.jgit.treewalk.TreeWalk; +import org.eclipse.jgit.treewalk.filter.PathFilterGroup; + +/** + * Helper class to create decoratable resources + * + * @see IDecoratableResource + */ +class DecoratableResourceHelper { + + static final int T_HEAD = 0; + + static final int T_INDEX = 1; + + static final int T_WORKSPACE = 2; + + static IDecoratableResource[] createDecoratableResources( + final IResource[] resources) throws IOException { + // Use first (available) resource to get repository mapping + int i = 0; + while (resources[i] == null) { + i++; + if (i >= resources.length) + // Array only contains nulls + return null; + } + final RepositoryMapping mapping = RepositoryMapping + .getMapping(resources[i]); + + final IDecoratableResource[] decoratableResources = new IDecoratableResource[resources.length]; + + ArrayList resourcePaths = new ArrayList(); + for (i = 0; i < resources.length; i++) { + final IResource resource = resources[i]; + if (resource != null && resource.getProject().isOpen()) { + switch (resource.getType()) { + case IResource.FILE: + // Add file path to list used for bulk decoration + resourcePaths.add(mapping.getRepoRelativePath(resource)); + break; + case IResource.FOLDER: + case IResource.PROJECT: + // Decorate folder and project node separately + try { + decoratableResources[i] = new DecoratableResourceAdapter( + resource); + } catch (IOException e) { + // Ignore - decoratableResources[i] is null + } + resourcePaths.add(null); + break; + } + } else { + resourcePaths.add(null); + } + } + + // Check resource paths before proceeding with bulk decoration + boolean containsAtLeastOnePath = false; + for (final String p : resourcePaths) { + if (p != null) { + containsAtLeastOnePath = true; + break; + } + } + if (!containsAtLeastOnePath) + return decoratableResources; + + final TreeWalk treeWalk = createThreeWayTreeWalk(mapping, resourcePaths); + if (treeWalk != null) + while (treeWalk.next()) { + i = resourcePaths.indexOf(treeWalk.getPathString()); + if (i != -1) { + try { + decoratableResources[i] = decorateResource( + new DecoratableResource(resources[i]), treeWalk); + } catch (IOException e) { + // Ignore - decoratableResources[i] is null + } + } + } + return decoratableResources; + } + + private static TreeWalk createThreeWayTreeWalk( + final RepositoryMapping mapping, + final ArrayList resourcePaths) throws IOException { + final Repository repository = mapping.getRepository(); + final TreeWalk treeWalk = new TreeWalk(repository); + + // Copy path list... + final ArrayList paths = new ArrayList(resourcePaths); + while (paths.remove(null)) { + // ... and remove nulls + } + + treeWalk.setFilter(PathFilterGroup.createFromStrings(paths)); + treeWalk.setRecursive(true); + treeWalk.reset(); + + // Repository + final ObjectId headId = repository.resolve(Constants.HEAD); + if (headId != null) + treeWalk.addTree(new RevWalk(repository).parseTree(headId)); + else + treeWalk.addTree(new EmptyTreeIterator()); + + // Index + treeWalk.addTree(new DirCacheIterator(repository.readDirCache())); + + // Working directory + treeWalk.addTree(IteratorService.createInitialIterator(repository)); + + return treeWalk; + } + + static DecoratableResource decorateResource( + final DecoratableResource decoratableResource, + final TreeWalk treeWalk) throws IOException { + final ContainerTreeIterator workspaceIterator = treeWalk.getTree( + T_WORKSPACE, ContainerTreeIterator.class); + final ResourceEntry resourceEntry = workspaceIterator != null ? workspaceIterator + .getResourceEntry() : null; + + if (resourceEntry == null) + return null; + + if (workspaceIterator != null && workspaceIterator.isEntryIgnored()) { + decoratableResource.ignored = true; + return decoratableResource; + } + + final int mHead = treeWalk.getRawMode(T_HEAD); + final int mIndex = treeWalk.getRawMode(T_INDEX); + + if (mHead == FileMode.MISSING.getBits() + && mIndex == FileMode.MISSING.getBits()) + return decoratableResource; + + decoratableResource.tracked = true; + + if (mHead == FileMode.MISSING.getBits()) { + decoratableResource.staged = Staged.ADDED; + } else if (mIndex == FileMode.MISSING.getBits()) { + decoratableResource.staged = Staged.REMOVED; + } else if (mHead != mIndex + || (mIndex != FileMode.TREE.getBits() && !treeWalk.idEqual( + T_HEAD, T_INDEX))) { + decoratableResource.staged = Staged.MODIFIED; + } else { + decoratableResource.staged = Staged.NOT_STAGED; + } + + final DirCacheIterator indexIterator = treeWalk.getTree(T_INDEX, + DirCacheIterator.class); + final DirCacheEntry indexEntry = indexIterator != null ? indexIterator + .getDirCacheEntry() : null; + + if (indexEntry == null) + return decoratableResource; + + if (indexEntry.getStage() > 0) + decoratableResource.conflicts = true; + + if (indexEntry.isAssumeValid()) { + decoratableResource.dirty = false; + decoratableResource.assumeValid = true; + } else { + if (workspaceIterator != null + && workspaceIterator.isModified(indexEntry, true)) + decoratableResource.dirty = true; + } + return decoratableResource; + } +} diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/decorators/GitDecoratorJob.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/decorators/GitDecoratorJob.java new file mode 100644 index 0000000000..a14884297b --- /dev/null +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/decorators/GitDecoratorJob.java @@ -0,0 +1,89 @@ +/******************************************************************************* + * Copyright (C) 2011, Philipp Thun + * + * 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.ui.internal.decorators; + +import java.util.HashMap; +import java.util.HashSet; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.Job; + +/** + * Job decorating Git resources asynchronously + */ +public class GitDecoratorJob extends Job { + + /** + * Constant defining the delay between two runs of the GitDecoratorJob in + * milliseconds + */ + private static final long DELAY = 10L; + + /** + * There is one dedicated job for each repository + */ + private static HashMap jobs = new HashMap(); + + /** + * Get the job dedicated for a given repository + * + * @param gitDir + * the .git directory's full path used as unique identifier of a + * repository + * @return GitDecoratorJob the job dedicated for the given repository + */ + public static synchronized GitDecoratorJob getJobForRepository( + final String gitDir) { + GitDecoratorJob job = jobs.get(gitDir); + if (job == null) { + job = new GitDecoratorJob("GitDecoratorJob[" + gitDir + "]"); //$NON-NLS-1$ //$NON-NLS-2$ + job.setSystem(true); + job.setPriority(DECORATE); + jobs.put(gitDir, job); + } + return job; + } + + private GitDecoratorJob(final String name) { + super(name); + } + + private HashSet elementList = new HashSet(); + + /** + * Add an element to the queue of decoration requests + * + * @param element + * the element to be decorated + */ + public synchronized void addDecorationRequest(Object element) { + if (elementList.add(element)) { + // Schedule job + if (getState() == NONE) + schedule(DELAY); + } + } + + @Override + public IStatus run(IProgressMonitor monitor) { + while (!elementList.isEmpty()) { + final Object[] elements; + synchronized (this) { + // Get decoration requests as array and clear the queue + elements = elementList.toArray(new Object[elementList.size()]); + elementList.clear(); + } + // Call GitLightweightDecorator to process the decoration requests + GitLightweightDecorator.processDecoration(elements); + } + return Status.OK_STATUS; + } +} diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/decorators/GitLightweightDecorator.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/decorators/GitLightweightDecorator.java index 3ae2e35b92..eef36783fa 100644 --- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/decorators/GitLightweightDecorator.java +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/decorators/GitLightweightDecorator.java @@ -34,7 +34,6 @@ import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.QualifiedName; -import org.eclipse.core.runtime.Status; import org.eclipse.egit.core.internal.util.ExceptionCollector; import org.eclipse.egit.core.project.GitProjectData; import org.eclipse.egit.core.project.RepositoryChangeListener; @@ -216,61 +215,122 @@ public class GitLightweightDecorator extends LabelProvider implements if (!resource.exists() && !resource.isPhantom()) return; - // Don't decorate ignored resources (e.g. bin folder content) - if (Team.isIgnoredHint(resource)) - return; - - // Make sure we're dealing with a project under Git revision control - final RepositoryMapping mapping = RepositoryMapping - .getMapping(resource); - if (mapping == null) - return; - - // Cannot decorate linked resources - if (mapping.getRepoRelativePath(resource) == null) - return; - try { - IDecoratableResource decoratableResource = null; - final DecorationHelper helper = new DecorationHelper( - activator.getPreferenceStore()); - final Long refreshed = (Long) resource .getSessionProperty(REFRESHED_KEY); if (refreshed != null) { // Stored decoratable resource is available - final IWorkspaceRoot root = resource.getWorkspace().getRoot(); - final Long refresh = (Long) root + final Long refresh = (Long) resource.getWorkspace().getRoot() .getSessionProperty(REFRESH_KEY); if (refresh == null - || refresh.longValue() < refreshed.longValue()) { + || refresh.longValue() <= refreshed.longValue()) { // Stored decoratable resource is up-to-date - decoratableResource = (IDecoratableResource) resource + final IDecoratableResource decoratableResource = (IDecoratableResource) resource .getSessionProperty(DECORATABLE_RESOURCE_KEY); if (decoratableResource != null) { // Use stored decoratable resource + final DecorationHelper helper = new DecorationHelper( + activator.getPreferenceStore()); helper.decorate(decoration, decoratableResource); return; } } } + } catch (CoreException e) { + handleException(resource, e); + return; + } + + // Don't decorate ignored resources (e.g. bin folder content) + if (Team.isIgnoredHint(resource)) + return; + + // Make sure we're dealing with a project under Git revision control + final RepositoryMapping mapping = RepositoryMapping + .getMapping(resource); + if (mapping == null) + return; + + // Cannot decorate linked resources + if (mapping.getRepoRelativePath(resource) == null) + return; + + // No (up-to-date) stored decoratable resource is available, thus + // decoration request is added to the queue + GitDecoratorJob.getJobForRepository( + mapping.getGitDirAbsolutePath().toString()) + .addDecorationRequest(element); + } + + /** + * Process decoration requests for the given list of elements + * + * @param elements + * the list of elements to be decorated + */ + static void processDecoration(final Object[] elements) { + final GitLightweightDecorator decorator = (GitLightweightDecorator) Activator + .getDefault().getWorkbench().getDecoratorManager() + .getBaseLabelProvider(DECORATOR_ID); + if (decorator != null) + decorator.prepareDecoration(elements); + else + exceptions + .handleException(new CoreException( + Activator + .createErrorStatus(UIText.GitLightweightDecorator_AsynchronousDecorationError))); + } + + private void prepareDecoration(final Object[] elements) { + if (elements == null) + return; - // No (up-to-date) stored decoratable resource is available - decoratableResource = new DecoratableResourceAdapter(resource); - helper.decorate(decoration, decoratableResource); + final IResource[] resources = new IResource[elements.length]; + for (int i = 0; i < elements.length; i++) { + if (elements[i] != null) + resources[i] = getResource(elements[i]); + } - // Store decoratable resource in session - resource.setSessionProperty(DECORATABLE_RESOURCE_KEY, - decoratableResource); - // Set (new) 'refreshed' timestamp - resource.setSessionProperty(REFRESHED_KEY, - Long.valueOf(System.currentTimeMillis())); + try { + // Calculate resource decorations + IDecoratableResource[] decoratableResources = DecoratableResourceHelper + .createDecoratableResources(resources); + + // Store decoration result in session property for each resource + for (int i = 0; i < decoratableResources.length; i++) { + try { + if (decoratableResources[i] != null) { + // Store decoratable resource in session + resources[i].setSessionProperty( + DECORATABLE_RESOURCE_KEY, + decoratableResources[i]); + // Set (new) 'refreshed' timestamp + resources[i].setSessionProperty(REFRESHED_KEY, + Long.valueOf(System.currentTimeMillis())); + } else { + if (resources[i] != null) + handleException( + resources[i], + new CoreException( + Activator + .createErrorStatus(UIText.GitLightweightDecorator_ResourceError))); + } + } catch (CoreException e) { + handleException(resources[i], e); + } + } } catch (IOException e) { - handleException(resource, new CoreException(new Status( - IStatus.ERROR, Activator.getPluginId(), e.getMessage(), e))); - } catch (CoreException e) { - handleException(resource, e); + exceptions + .handleException(new CoreException( + Activator + .createErrorStatus( + UIText.GitLightweightDecorator_AsynchronousDecorationError, + e))); + return; } + + // Re-trigger decoration process (in UI thread) + fireLabelEvent(new LabelProviderChangedEvent(this)); } /** diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/uitext.properties b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/uitext.properties index c383648162..5f9323dd27 100644 --- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/uitext.properties +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/uitext.properties @@ -958,6 +958,10 @@ GitBranchSynchronizeWizardPage_repositories=Repositories GitBranchSynchronizeWizardPage_branches=Branches GitBranchSynchronizeWizardPage_selectAll=Select All GitBranchSynchronizeWizardPage_deselectAll=Deselect All + +GitLightweightDecorator_AsynchronousDecorationError=Asynchronous decoration requests for Git resources could not be processed +GitLightweightDecorator_ResourceError=Resource could not be decorated + GitTraceConfigurationDialog_ApplyButton=&Apply GitTraceConfigurationDialog_DefaultButton=&Default GitTraceConfigurationDialog_DialogTitle=Maintain the Git Trace Configuration -- cgit v1.2.3