blob: fbc84ca1a7a2336fc1f6ff9675bd2b88b38bf218 [file] [log] [blame]
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package git4idea.commands;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vfs.VirtualFile;
import git4idea.i18n.GitBundle;
import git4idea.util.GitUIUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.awt.*;
/**
* Handler utilities that allow running handlers with progress indicators
*/
public class GitHandlerUtil {
private GitHandlerUtil() {
}
/**
* Execute simple process synchronously with progress
*
* @param handler a handler
* @param operationTitle an operation title shown in progress dialog
* @param operationName an operation name shown in failure dialog
* @return A stdout content or null if there was error (exit code != 0 or exception during start).
*/
@Nullable
public static String doSynchronously(final GitSimpleHandler handler, final String operationTitle, @NonNls final String operationName) {
handler.addListener(new GitHandlerListenerBase(handler, operationName) {
protected String getErrorText() {
String text = handler.getStderr();
if (text.length() == 0) {
text = handler.getStdout();
}
return text;
}
});
final ProgressManager manager = ProgressManager.getInstance();
manager.runProcessWithProgressSynchronously(new Runnable() {
public void run() {
runInCurrentThread(handler, manager.getProgressIndicator(), true,
operationTitle);
}
}, operationTitle, false, handler.project());
if (!handler.isStarted() || handler.getExitCode() != 0) {
return null;
}
return handler.getStdout();
}
/**
* Execute simple process synchronously with progress
*
* @param handler a handler
* @param operationTitle an operation title shown in progress dialog
* @param operationName an operation name shown in failure dialog
* @return An exit code
*/
public static int doSynchronously(final GitLineHandler handler, final String operationTitle, @NonNls final String operationName) {
final ProgressManager manager = ProgressManager.getInstance();
manager.run(new Task.Modal(handler.project(), operationTitle, false) {
public void run(@NotNull final ProgressIndicator indicator) {
handler.addLineListener(new GitLineHandlerListenerProgress(indicator, handler, operationName, true));
runInCurrentThread(handler, indicator, true, operationTitle);
}
});
if (!handler.isStarted()) {
return -1;
}
return handler.getExitCode();
}
/**
* Run handler in the current thread
*
* @param handler a handler to run
* @param indicator a progress manager
* @param setIndeterminateFlag if true handler is configured as indeterminate
* @param operationName
*/
public static void runInCurrentThread(final GitHandler handler,
final ProgressIndicator indicator,
final boolean setIndeterminateFlag,
@Nullable final String operationName) {
runInCurrentThread(handler, new Runnable() {
public void run() {
if (indicator != null) {
indicator.setText(operationName == null ? GitBundle.message("git.running", handler.printableCommandLine()) : operationName);
indicator.setText2("");
if (setIndeterminateFlag) {
indicator.setIndeterminate(true);
}
}
}
});
}
/**
* Run handler in the current thread
*
* @param handler a handler to run
* @param postStartAction an action that is executed
*/
public static void runInCurrentThread(final GitHandler handler, @Nullable final Runnable postStartAction) {
handler.runInCurrentThread(postStartAction);
}
public static String formatOperationName(String operation, @NotNull VirtualFile root) {
return operation + " '" + root.getName() + "'...";
}
/**
* A base class for handler listener that implements error handling logic
*/
private abstract static class GitHandlerListenerBase implements GitHandlerListener {
/**
* a handler
*/
protected final GitHandler myHandler;
/**
* a operation name for the handler
*/
protected final String myOperationName;
/**
* if true, the errors are shown when process is terminated
*/
protected boolean myShowErrors;
/**
* A constructor
*
* @param handler a handler instance
* @param operationName an operation name
*/
public GitHandlerListenerBase(final GitHandler handler, final String operationName) {
this(handler, operationName, true);
}
/**
* A constructor
*
* @param handler a handler instance
* @param operationName an operation name
* @param showErrors if true, the errors are shown when process is terminated
*/
public GitHandlerListenerBase(final GitHandler handler, final String operationName, boolean showErrors) {
myHandler = handler;
myOperationName = operationName;
myShowErrors = showErrors;
}
/**
* {@inheritDoc}
*/
public void processTerminated(final int exitCode) {
if (exitCode != 0 && !myHandler.isIgnoredErrorCode(exitCode)) {
ensureError(exitCode);
if (myShowErrors) {
EventQueue.invokeLater(new Runnable() {
public void run() {
GitUIUtil.showOperationErrors(myHandler.project(), myHandler.errors(), myOperationName);
}
});
}
}
}
/**
* Ensure that at least one error is available in case if the process exited with non-zero exit code
*
* @param exitCode the exit code of the process
*/
protected void ensureError(final int exitCode) {
if (myHandler.errors().isEmpty()) {
String text = getErrorText();
if ((text == null || text.length() == 0) && myHandler.errors().isEmpty()) {
//noinspection ThrowableInstanceNeverThrown
myHandler.addError(new VcsException(GitBundle.message("git.error.exit", exitCode)));
}
else {
//noinspection ThrowableInstanceNeverThrown
myHandler.addError(new VcsException(text));
}
}
}
/**
* @return error text for the handler, if null or empty string a default message is used.
*/
protected abstract String getErrorText();
/**
* {@inheritDoc}
*/
public void startFailed(final Throwable exception) {
//noinspection ThrowableInstanceNeverThrown
myHandler.addError(new VcsException("Git start failed: " + exception.getMessage(), exception));
if (myShowErrors) {
EventQueue.invokeLater(new Runnable() {
public void run() {
GitUIUtil.showOperationError(myHandler.project(), myOperationName, exception.getMessage());
}
});
}
}
}
/**
* A base class for line handler listeners
*/
private abstract static class GitLineHandlerListenerBase extends GitHandlerListenerBase implements GitLineHandlerListener {
/**
* A constructor
*
* @param handler a handler instance
* @param operationName an operation name
* @param showErrors if true, the errors are shown when process is terminated
*/
public GitLineHandlerListenerBase(GitHandler handler, String operationName, boolean showErrors) {
super(handler, operationName, showErrors);
}
}
/**
* A base class for line handler listeners
*/
public static class GitLineHandlerListenerProgress extends GitLineHandlerListenerBase {
/**
* a progress manager to use
*/
private final ProgressIndicator myProgressIndicator;
/**
* A constructor
*
* @param manager the project manager
* @param handler a handler instance
* @param operationName an operation name
* @param showErrors if true, the errors are shown when process is terminated
*/
public GitLineHandlerListenerProgress(final ProgressIndicator manager, GitHandler handler, String operationName, boolean showErrors) {
super(handler, operationName, showErrors); //To change body of overridden methods use File | Settings | File Templates.
myProgressIndicator = manager;
}
/**
* {@inheritDoc}
*/
protected String getErrorText() {
// all lines are already calculated as errors
return "";
}
/**
* {@inheritDoc}
*/
public void onLineAvailable(final String line, final Key outputType) {
if (isErrorLine(line.trim())) {
//noinspection ThrowableInstanceNeverThrown
myHandler.addError(new VcsException(line));
}
if (myProgressIndicator != null) {
myProgressIndicator.setText2(line);
}
}
}
/**
* Check if the line is an error line
*
* @param text a line to check
* @return true if the error line
*/
protected static boolean isErrorLine(String text) {
for (String prefix : GitImpl.ERROR_INDICATORS) {
if (text.startsWith(prefix)) {
return true;
}
}
return false;
}
}