blob: 9506b19fc2a928a7108c94c87f80e2ad505939c0 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011 SAP AG.
* 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
*
* Contributors:
* Mathias Kinzler (SAP AG) - initial implementation
*******************************************************************************/
package org.eclipse.egit.ui.internal.pull;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.resources.WorkspaceJob;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.IJobChangeEvent;
import org.eclipse.core.runtime.jobs.IJobChangeListener;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.runtime.jobs.JobChangeAdapter;
import org.eclipse.egit.core.op.PullOperation;
import org.eclipse.egit.ui.Activator;
import org.eclipse.egit.ui.JobFamilies;
import org.eclipse.egit.ui.UIPreferences;
import org.eclipse.egit.ui.internal.UIText;
import org.eclipse.egit.ui.internal.branch.CleanupUncomittedChangesDialog;
import org.eclipse.egit.ui.internal.credentials.EGitCredentialsProvider;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jgit.api.PullResult;
import org.eclipse.jgit.api.RebaseResult;
import org.eclipse.jgit.errors.TransportException;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.PlatformUI;
/**
* UI wrapper for {@link PullOperation}
*/
public class PullOperationUI extends JobChangeAdapter {
private static final IStatus NOT_TRIED_STATUS = new Status(IStatus.ERROR,
Activator.getPluginId(), UIText.PullOperationUI_NotTriedMessage);
private final Repository[] repositories;
/**
* Holds the number of PullOperationUIs that need to complete before the
* pull results can be shown. The number includes this instance of
* PullOperationUI and all subtasks spawned by it.
*/
private final AtomicInteger tasksToWaitFor = new AtomicInteger(1);
/** pull results per repository */
protected final Map<Repository, Object> results = Collections
.synchronizedMap(new LinkedHashMap<Repository, Object>());
private final PullOperation pullOperation;
/**
* @param repositories
*/
public PullOperationUI(Set<Repository> repositories) {
this.repositories = repositories.toArray(new Repository[repositories
.size()]);
int timeout = Activator.getDefault().getPreferenceStore().getInt(
UIPreferences.REMOTE_CONNECTION_TIMEOUT);
pullOperation = new PullOperation(repositories, timeout);
pullOperation.setCredentialsProvider(new EGitCredentialsProvider());
for (Repository repository : repositories)
results.put(repository, NOT_TRIED_STATUS);
}
/**
* Starts this operation asynchronously
*/
public void start() {
start(this);
}
private void start(IJobChangeListener jobChangeListener) {
// figure out a job name
String jobName;
if (this.repositories.length == 1) {
String repoName = Activator.getDefault().getRepositoryUtil()
.getRepositoryName(repositories[0]);
String shortBranchName;
try {
shortBranchName = repositories[0].getBranch();
} catch (IOException e) {
// ignore here
shortBranchName = ""; //$NON-NLS-1$
}
jobName = NLS.bind(UIText.PullOperationUI_PullingTaskName,
shortBranchName, repoName);
} else
jobName = UIText.PullOperationUI_PullingMultipleTaskName;
Job job = new WorkspaceJob(jobName) {
@Override
public IStatus runInWorkspace(IProgressMonitor monitor) {
execute(monitor);
// we always return OK and handle display of errors on our own
return Status.OK_STATUS;
}
@Override
public boolean belongsTo(Object family) {
if (JobFamilies.PULL.equals(family))
return true;
return super.belongsTo(family);
}
};
job.setRule(ResourcesPlugin.getWorkspace().getRoot());
job.setUser(true);
job.addJobChangeListener(jobChangeListener);
job.schedule();
}
/**
* Starts this operation synchronously.
*
* @param monitor
*/
public void execute(IProgressMonitor monitor) {
try {
pullOperation.execute(monitor);
results.putAll(pullOperation.getResults());
} catch (CoreException e) {
if (e.getStatus().getSeverity() == IStatus.CANCEL)
results.putAll(pullOperation.getResults());
else
Activator.handleError(e.getMessage(), e, true);
}
}
public void done(IJobChangeEvent event) {
PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
public void run() {
Map<Repository, Object> res = new LinkedHashMap<Repository, Object>(
PullOperationUI.this.results);
Shell shell = PlatformUI.getWorkbench()
.getActiveWorkbenchWindow().getShell();
handlePullResults(res, shell);
}
});
}
/**
* Post-process the pull results, allowing the user to deal with uncommitted
* changes and re-pull if the initial pull failed because of these changes
*
* @param resultsMap
* a copy of the initial pull results
* @param shell
*/
private void handlePullResults(final Map<Repository, Object> resultsMap,
Shell shell) {
for (Entry<Repository, Object> entry : resultsMap.entrySet()) {
Object result = entry.getValue();
if (result instanceof PullResult) {
PullResult pullResult = (PullResult) result;
if (pullResult.getRebaseResult() != null
&& RebaseResult.Status.UNCOMMITTED_CHANGES == pullResult
.getRebaseResult().getStatus()) {
handleUncommittedChanges(entry.getKey(), pullResult
.getRebaseResult().getUncommittedChanges(), shell);
}
}
}
if (tasksToWaitFor.decrementAndGet() == 0 && !results.isEmpty())
showResults(shell);
}
/**
* @param repository
* @param files
* uncommitted files that were in the way for the rebase
* @param shell
*/
private void handleUncommittedChanges(final Repository repository,
final List<String> files, Shell shell) {
String repoName = Activator.getDefault().getRepositoryUtil()
.getRepositoryName(repository);
CleanupUncomittedChangesDialog cleanupUncomittedChangesDialog = new CleanupUncomittedChangesDialog(
shell,
MessageFormat
.format(UIText.AbstractRebaseCommandHandler_cleanupDialog_title,
repoName),
UIText.AbstractRebaseCommandHandler_cleanupDialog_text,
repository, files);
cleanupUncomittedChangesDialog.open();
if (cleanupUncomittedChangesDialog.shouldContinue()) {
final PullOperationUI parentOperation = this;
final PullOperationUI pullOperationUI = new PullOperationUI(
Collections.singleton(repository));
tasksToWaitFor.incrementAndGet();
pullOperationUI.start(new JobChangeAdapter() {
@Override
public void done(IJobChangeEvent event) {
// put the result from this subtask into the result map of
// the outer PullOperationUI and display the results if all
// subtasks have reported back
parentOperation.results.putAll(pullOperationUI.results);
int missing = parentOperation.tasksToWaitFor.decrementAndGet();
if (missing == 0)
parentOperation.showResults();
}
});
}
}
private void showResults() {
PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
public void run() {
Shell shell = PlatformUI.getWorkbench()
.getActiveWorkbenchWindow().getShell();
showResults(shell);
}
});
}
private void showResults(final Shell shell) {
if (this.results.isEmpty())
// shouldn't really happen, but just in case...
return;
else if (this.results.size() == 1) {
Entry<Repository, Object> entry = this.results.entrySet()
.iterator().next();
if (entry.getValue() instanceof PullResult)
new PullResultDialog(shell, entry.getKey(), (PullResult) entry
.getValue()).open();
else {
IStatus status = (IStatus) entry.getValue();
if (status == NOT_TRIED_STATUS) {
MessageDialog
.openInformation(
shell,
UIText.PullOperationUI_PullCanceledWindowTitle,
UIText.PullOperationUI_PullOperationCanceledMessage);
} else if (status.getException() instanceof TransportException) {
ErrorDialog.openError(shell,
UIText.PullOperationUI_PullFailed,
UIText.PullOperationUI_ConnectionProblem,
status);
} else
Activator.handleError(status.getMessage(), status
.getException(), true);
}
} else
new MultiPullResultDialog(shell, results).open();
}
}