1 /*******************************************************************************
2 * Copyright (c) 2011, 2017 Chris Aniszczyk <caniszczyk@gmail.com> and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License 2.0
5 * which accompanies this distribution, and is available at
6 * https://www.eclipse.org/legal/epl-2.0/
8 * SPDX-License-Identifier: EPL-2.0
11 * Chris Aniszczyk <caniszczyk@gmail.com> - initial implementation
12 * EclipseSource - Filtered Viewer
13 * Thomas Wolf <thomas.wolf@paranor.ch> - deferred loading
14 *******************************************************************************/
15 package org
.eclipse
.egit
.ui
.internal
.reflog
;
18 import java
.io
.IOException
;
19 import java
.util
.Objects
;
21 import org
.eclipse
.core
.runtime
.Assert
;
22 import org
.eclipse
.core
.runtime
.IProgressMonitor
;
23 import org
.eclipse
.core
.runtime
.jobs
.IJobChangeEvent
;
24 import org
.eclipse
.core
.runtime
.jobs
.ISchedulingRule
;
25 import org
.eclipse
.core
.runtime
.jobs
.JobChangeAdapter
;
26 import org
.eclipse
.egit
.ui
.Activator
;
27 import org
.eclipse
.egit
.ui
.internal
.UIText
;
28 import org
.eclipse
.jface
.viewers
.AbstractTreeViewer
;
29 import org
.eclipse
.jface
.viewers
.ITreeContentProvider
;
30 import org
.eclipse
.jface
.viewers
.Viewer
;
31 import org
.eclipse
.jgit
.api
.Git
;
32 import org
.eclipse
.jgit
.lib
.Repository
;
33 import org
.eclipse
.jgit
.revwalk
.RevWalk
;
34 import org
.eclipse
.swt
.widgets
.Control
;
35 import org
.eclipse
.ui
.model
.WorkbenchAdapter
;
36 import org
.eclipse
.ui
.progress
.DeferredTreeContentManager
;
37 import org
.eclipse
.ui
.progress
.IDeferredWorkbenchAdapter
;
38 import org
.eclipse
.ui
.progress
.IElementCollector
;
41 * A content provider for reflog entries given a repository and a ref.
43 public class ReflogViewContentProvider
implements ITreeContentProvider
{
45 private DeferredTreeContentManager loader
;
47 private Object currentInput
;
50 * Serializes concurrent attempts to load the reflog.
52 private static class ReflogSchedulingRule
implements ISchedulingRule
{
54 private final File gitDir
;
56 public ReflogSchedulingRule(File gitDir
) {
61 public boolean contains(ISchedulingRule rule
) {
62 if (rule
instanceof ReflogSchedulingRule
) {
63 return Objects
.equals(gitDir
,
64 ((ReflogSchedulingRule
) rule
).gitDir
);
70 public boolean isConflicting(ISchedulingRule rule
) {
71 return rule
instanceof ReflogSchedulingRule
;
76 private static final WorkbenchAdapter ERROR_ELEMENT
= new WorkbenchAdapter() {
79 public String
getLabel(Object object
) {
80 return UIText
.ReflogView_ErrorOnLoad
;
86 * Input class for this content provider.
88 public static class ReflogInput
extends WorkbenchAdapter
89 implements IDeferredWorkbenchAdapter
{
91 private final Repository repository
;
93 private final String ref
;
95 private final ISchedulingRule rule
;
97 private ReflogItem
[] refLog
;
100 * Create input with non-null repository and non-null ref
105 public ReflogInput(Repository repository
, String ref
) {
106 Assert
.isNotNull(repository
, "Repository cannot be null"); //$NON-NLS-1$
107 Assert
.isNotNull(ref
, "Ref cannot be null"); //$NON-NLS-1$
108 this.repository
= repository
;
110 this.rule
= new ReflogSchedulingRule(repository
.getDirectory());
118 public Repository
getRepository() {
127 public String
getRef() {
132 public Object
[] getChildren(Object o
) {
133 if (refLog
!= null) {
140 public void fetchDeferredChildren(Object object
,
141 IElementCollector collector
, IProgressMonitor monitor
) {
142 if (refLog
!= null) {
143 return; // Already loaded.
145 try (Git git
= new Git(repository
);
146 RevWalk walk
= new RevWalk(repository
)) {
147 refLog
= git
.reflog().setRef(ref
).call().stream()
149 String commitMessage
= null;
152 .parseCommit(entry
.getNewId())
154 } catch (IOException e
) {
157 return new ReflogItem(ReflogInput
.this, entry
,
160 .toArray(ReflogItem
[]::new);
161 collector
.add(refLog
, monitor
);
162 } catch (Exception e
) {
163 Activator
.logError("Error running reflog command", e
); //$NON-NLS-1$
164 collector
.add(ERROR_ELEMENT
, monitor
);
169 public boolean isContainer() {
174 public ISchedulingRule
getRule(Object object
) {
180 public Object
[] getElements(Object inputElement
) {
181 return getChildren(inputElement
);
185 public void inputChanged(Viewer viewer
, Object oldInput
, Object newInput
) {
186 if (oldInput
!= null && loader
!= null) {
187 loader
.cancel(oldInput
);
189 currentInput
= newInput
;
190 if (viewer
instanceof AbstractTreeViewer
&& newInput
!= null) {
191 loader
= new DeferredBatchLoader((AbstractTreeViewer
) viewer
);
192 loader
.addUpdateCompleteListener(new JobChangeAdapter() {
195 public void done(IJobChangeEvent event
) {
196 if (event
.getResult().isOK()) {
197 // Force a selection event
198 viewer
.setSelection(viewer
.getSelection());
206 public void dispose() {
207 if (currentInput
!= null && loader
!= null) {
208 loader
.cancel(currentInput
);
215 public Object
[] getChildren(Object parentElement
) {
216 if (parentElement
instanceof ReflogInput
&& loader
!= null) {
217 Object
[] knownChildren
= ((ReflogInput
) parentElement
)
218 .getChildren(parentElement
);
219 if (knownChildren
!= null) {
220 return knownChildren
;
222 return loader
.getChildren(parentElement
);
224 return new Object
[0];
228 public Object
getParent(Object element
) {
233 public boolean hasChildren(Object element
) {
238 * A variant of {@link DeferredTreeContentManager} that doesn't use a
239 * separate UI job to fill in the tree. With UI jobs, it's simply impossible
240 * to know what has already been added when there are several loading jobs.
241 * For our use case (load the whole reflog, then add it to the tree) a
242 * {@link org.eclipse.swt.widgets.Display#syncExec(Runnable) syncExec()} is
245 private static class DeferredBatchLoader
246 extends DeferredTreeContentManager
{
248 private AbstractTreeViewer viewer
;
250 public DeferredBatchLoader(AbstractTreeViewer viewer
) {
252 this.viewer
= viewer
;
256 * Add child nodes, removing the error element if appropriate. Contrary
257 * to the super implementation, this does <em>not</em> use a UI job but
258 * a simple {@link org.eclipse.swt.widgets.Display#syncExec(Runnable)
262 * to add the {@code children} to
264 * to add to the {@code parent}
269 protected void addChildren(Object parent
, Object
[] children
,
270 IProgressMonitor monitor
) {
271 Control control
= viewer
.getControl();
272 if (control
== null || control
.isDisposed()) {
275 control
.getDisplay().syncExec(() -> {
276 if (!control
.isDisposed()) {
278 control
.setRedraw(false);
279 if (children
.length
!= 1
280 || children
[0] != ERROR_ELEMENT
) {
281 viewer
.remove(ERROR_ELEMENT
);
283 viewer
.add(parent
, children
);
285 control
.setRedraw(true);