Toolbar button to switch repositories in git views
[egit/eclipse.git] / org.eclipse.egit.ui / src / org / eclipse / egit / ui / internal / reflog / ReflogViewContentProvider.java
blobfa972e92ff176a22e7f0155560d07c7707b601e4
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
8 * Contributors:
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;
15 import java.io.File;
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;
36 /**
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;
45 /**
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) {
53 this.gitDir = gitDir;
56 @Override
57 public boolean contains(ISchedulingRule rule) {
58 if (rule instanceof ReflogSchedulingRule) {
59 return Objects.equals(gitDir,
60 ((ReflogSchedulingRule) rule).gitDir);
62 return false;
65 @Override
66 public boolean isConflicting(ISchedulingRule rule) {
67 return rule instanceof ReflogSchedulingRule;
72 private static final WorkbenchAdapter ERROR_ELEMENT = new WorkbenchAdapter() {
74 @Override
75 public String getLabel(Object object) {
76 return UIText.ReflogView_ErrorOnLoad;
81 /**
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;
95 /**
96 * Create input with non-null repository and non-null ref
98 * @param repository
99 * @param 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;
105 this.ref = ref;
106 this.rule = new ReflogSchedulingRule(repository.getDirectory());
110 * Get repository
112 * @return repository
114 public Repository getRepository() {
115 return repository;
119 * Retrieves the ref.
121 * @return the ref
123 public String getRef() {
124 return ref;
127 @Override
128 public Object[] getChildren(Object o) {
129 if (refLog != null) {
130 return refLog;
132 return null;
135 @Override
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);
152 @Override
153 public boolean isContainer() {
154 return true;
157 @Override
158 public ISchedulingRule getRule(Object object) {
159 return rule;
163 @Override
164 public Object[] getElements(Object inputElement) {
165 return getChildren(inputElement);
168 @Override
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() {
178 @Override
179 public void done(IJobChangeEvent event) {
180 if (event.getResult().isOK()) {
181 // Force a selection event
182 viewer.setSelection(viewer.getSelection());
189 @Override
190 public void dispose() {
191 if (currentInput != null && loader != null) {
192 loader.cancel(currentInput);
194 currentInput = null;
195 loader = null;
198 @Override
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];
211 @Override
212 public Object getParent(Object element) {
213 return null;
216 @Override
217 public boolean hasChildren(Object element) {
218 return false;
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
227 * sufficient.
229 private static class DeferredBatchLoader
230 extends DeferredTreeContentManager {
232 private AbstractTreeViewer viewer;
234 public DeferredBatchLoader(AbstractTreeViewer viewer) {
235 super(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)
243 * syncExec()}.
245 * @param parent
246 * to add the {@code children} to
247 * @param children
248 * to add to the {@code parent}
249 * @param monitor
250 * is ignored
252 @Override
253 protected void addChildren(Object parent, Object[] children,
254 IProgressMonitor monitor) {
255 Control control = viewer.getControl();
256 if (control == null || control.isDisposed()) {
257 return;
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);