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 v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
9 * Chris Aniszczyk <caniszczyk@gmail.com> - initial implementation
10 * EclipseSource - Filtered Viewer
11 * Thomas Wolf <thomas.wolf@paranor.ch> - deferred loading
12 *******************************************************************************/
13 package org
.eclipse
.egit
.ui
.internal
.reflog
;
16 import java
.util
.Objects
;
18 import org
.eclipse
.core
.runtime
.Assert
;
19 import org
.eclipse
.core
.runtime
.IProgressMonitor
;
20 import org
.eclipse
.core
.runtime
.jobs
.IJobChangeEvent
;
21 import org
.eclipse
.core
.runtime
.jobs
.ISchedulingRule
;
22 import org
.eclipse
.core
.runtime
.jobs
.JobChangeAdapter
;
23 import org
.eclipse
.egit
.ui
.Activator
;
24 import org
.eclipse
.egit
.ui
.internal
.UIText
;
25 import org
.eclipse
.jface
.viewers
.AbstractTreeViewer
;
26 import org
.eclipse
.jface
.viewers
.ITreeContentProvider
;
27 import org
.eclipse
.jface
.viewers
.Viewer
;
28 import org
.eclipse
.jgit
.api
.Git
;
29 import org
.eclipse
.jgit
.lib
.Repository
;
30 import org
.eclipse
.swt
.widgets
.Control
;
31 import org
.eclipse
.ui
.model
.WorkbenchAdapter
;
32 import org
.eclipse
.ui
.progress
.DeferredTreeContentManager
;
33 import org
.eclipse
.ui
.progress
.IDeferredWorkbenchAdapter
;
34 import org
.eclipse
.ui
.progress
.IElementCollector
;
37 * A content provider for reflog entries given a repository and a ref.
39 public class ReflogViewContentProvider
implements ITreeContentProvider
{
41 private DeferredTreeContentManager loader
;
43 private Object currentInput
;
46 * Serializes concurrent attempts to load the reflog.
48 private static class ReflogSchedulingRule
implements ISchedulingRule
{
50 private final File gitDir
;
52 public ReflogSchedulingRule(File gitDir
) {
57 public boolean contains(ISchedulingRule rule
) {
58 if (rule
instanceof ReflogSchedulingRule
) {
59 return Objects
.equals(gitDir
,
60 ((ReflogSchedulingRule
) rule
).gitDir
);
66 public boolean isConflicting(ISchedulingRule rule
) {
67 return rule
instanceof ReflogSchedulingRule
;
72 private static final WorkbenchAdapter ERROR_ELEMENT
= new WorkbenchAdapter() {
75 public String
getLabel(Object object
) {
76 return UIText
.ReflogView_ErrorOnLoad
;
82 * Input class for this content provider.
84 public static class ReflogInput
extends WorkbenchAdapter
85 implements IDeferredWorkbenchAdapter
{
87 private final Repository repository
;
89 private final String ref
;
91 private final ISchedulingRule rule
;
93 private ReflogItem
[] refLog
;
96 * Create input with non-null repository and non-null ref
101 public ReflogInput(Repository repository
, String ref
) {
102 Assert
.isNotNull(repository
, "Repository cannot be null"); //$NON-NLS-1$
103 Assert
.isNotNull(ref
, "Ref cannot be null"); //$NON-NLS-1$
104 this.repository
= repository
;
106 this.rule
= new ReflogSchedulingRule(repository
.getDirectory());
114 public Repository
getRepository() {
123 public String
getRef() {
128 public Object
[] getChildren(Object o
) {
129 if (refLog
!= null) {
136 public void fetchDeferredChildren(Object object
,
137 IElementCollector collector
, IProgressMonitor monitor
) {
138 if (refLog
!= null) {
139 return; // Already loaded.
141 try (Git git
= new Git(repository
)) {
142 refLog
= git
.reflog().setRef(ref
).call().stream()
143 .map(entry
-> new ReflogItem(ReflogInput
.this, entry
))
144 .toArray(ReflogItem
[]::new);
145 collector
.add(refLog
, monitor
);
146 } catch (Exception e
) {
147 Activator
.logError("Error running reflog command", e
); //$NON-NLS-1$
148 collector
.add(ERROR_ELEMENT
, monitor
);
153 public boolean isContainer() {
158 public ISchedulingRule
getRule(Object object
) {
164 public Object
[] getElements(Object inputElement
) {
165 return getChildren(inputElement
);
169 public void inputChanged(Viewer viewer
, Object oldInput
, Object newInput
) {
170 if (oldInput
!= null && loader
!= null) {
171 loader
.cancel(oldInput
);
173 currentInput
= newInput
;
174 if (viewer
instanceof AbstractTreeViewer
&& newInput
!= null) {
175 loader
= new DeferredBatchLoader((AbstractTreeViewer
) viewer
);
176 loader
.addUpdateCompleteListener(new JobChangeAdapter() {
179 public void done(IJobChangeEvent event
) {
180 if (event
.getResult().isOK()) {
181 // Force a selection event
182 viewer
.setSelection(viewer
.getSelection());
190 public void dispose() {
191 if (currentInput
!= null && loader
!= null) {
192 loader
.cancel(currentInput
);
199 public Object
[] getChildren(Object parentElement
) {
200 if (parentElement
instanceof ReflogInput
&& loader
!= null) {
201 Object
[] knownChildren
= ((ReflogInput
) parentElement
)
202 .getChildren(parentElement
);
203 if (knownChildren
!= null) {
204 return knownChildren
;
206 return loader
.getChildren(parentElement
);
208 return new Object
[0];
212 public Object
getParent(Object element
) {
217 public boolean hasChildren(Object element
) {
222 * A variant of {@link DeferredTreeContentManager} that doesn't use a
223 * separate UI job to fill in the tree. With UI jobs, it's simply impossible
224 * to know what has already been added when there are several loading jobs.
225 * For our use case (load the whole reflog, then add it to the tree) a
226 * {@link org.eclipse.swt.widgets.Display#syncExec(Runnable) syncExec()} is
229 private static class DeferredBatchLoader
230 extends DeferredTreeContentManager
{
232 private AbstractTreeViewer viewer
;
234 public DeferredBatchLoader(AbstractTreeViewer viewer
) {
236 this.viewer
= viewer
;
240 * Add child nodes, removing the error element if appropriate. Contrary
241 * to the super implementation, this does <em>not</em> use a UI job but
242 * a simple {@link org.eclipse.swt.widgets.Display#syncExec(Runnable)
246 * to add the {@code children} to
248 * to add to the {@code parent}
253 protected void addChildren(Object parent
, Object
[] children
,
254 IProgressMonitor monitor
) {
255 Control control
= viewer
.getControl();
256 if (control
== null || control
.isDisposed()) {
259 control
.getDisplay().syncExec(() -> {
260 if (!control
.isDisposed()) {
261 if (children
.length
!= 1 || children
[0] != ERROR_ELEMENT
) {
262 viewer
.remove(ERROR_ELEMENT
);
264 viewer
.add(parent
, children
);