<commit_before>/*
 * Copyright 2000-2010 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.Disposable;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.VcsException;
import git4idea.GitVcs;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.atomic.AtomicReference;

/**
 * A Task to run the given GitHandler with ability to cancel it.
 * Cancellation is implemented with a {@link java.util.Timer} which checks whether the ProgressIndicator was cancelled and kills
 * the GitHandler in that case.
 *
 * A GitTask may be executed synchronously ({@link #executeModal()} or asynchronously ({@link #executeAsync(GitTask.ResultHandler)}.
 * Result of the execution is encapsulated in {@link GitTaskResult}.
 *
 * @see {@link git4idea.commands.GitHandler#kill()}
 * @author <NAME>
 */
public class GitTask {

  private static final Logger LOG = Logger.getInstance(GitTask.class);

  private final Project myProject;
  private final GitHandler myHandler;
  private final String myTitle;
  private final AtomicReference<GitTaskResult> myResult = new AtomicReference<GitTaskResult>(GitTaskResult.INITIAL);
  private GitProgressAnalyzer myProgressAnalyzer;
  private boolean myExecuteResultInAwt = true;

  public GitTask(Project project, GitHandler handler, String title) {
    myProject = project;
    myHandler = handler;
    myTitle = title;
  }

  /**
   * Executes this task synchronously, with a modal progress dialog.
   * @return Result of the task execution.
   */
  public GitTaskResult executeModal() {
    ModalTask task = new ModalTask(myProject, myHandler, myTitle) {
      public void execute(ProgressIndicator indicator) {
        addListeners(this, indicator);
        GitHandlerUtil.runInCurrentThread(myHandler, indicator, false, myTitle);
      }

      @Override
      public void onSuccess() {
        if (!myHandler.errors().isEmpty()) {
          myResult.set(GitTaskResult.GIT_ERROR);
        } else {
          myResult.set(GitTaskResult.OK);
        }
      }

      @Override
      public void onCancel() {
        myResult.set(GitTaskResult.CANCELLED);
      }
    };

    ProgressManager.getInstance().run(task);
    return myResult.get();
  }

  /**
   * Executes the task synchronously, with a modal progress dialog.
   * @param resultHandler callback which will be called after task execution.
   */
  public void executeModal(GitTaskResultHandler resultHandler) {
    resultHandler.run(executeModal());
  }

  /**
   * Executes this task asynchronously, in backgrond. Calls the resultHandler when finished.
   * @param resultHandler callback called after the task has finished or was cancelled by user or automatically.
   */
  public void executeAsync(final GitTaskResultHandler resultHandler) {
    executeInBackground(false, resultHandler);
  }

  public void executeInBackground(boolean sync, final GitTaskResultHandler resultHandler) {
    final Object LOCK = new Object();
    BackgroundableTask task = new BackgroundableTask(myProject, myHandler, myTitle) {
      public void execute(ProgressIndicator indicator) {
        addListeners(this, indicator);
        GitHandlerUtil.runInCurrentThread(myHandler, indicator, false, myTitle);
      }

      @Override
      public void onSuccess() {
        ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
          @Override public void run() {
            if (!myHandler.errors().isEmpty()) { // TODO: handle errors smarter: an error may be not a complete failure.
              myResult.set(GitTaskResult.GIT_ERROR);
            } else {
              myResult.set(GitTaskResult.OK);
            }
            resultHandler.run(myResult.get());
            synchronized (LOCK) {
              LOCK.notifyAll();
            }
          }
        });
      }

      @Override
      public void onCancel() {
        ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
          @Override public void run() {
            myResult.set(GitTaskResult.CANCELLED);
            resultHandler.run(GitTaskResult.CANCELLED);
            synchronized (LOCK) {
              LOCK.notifyAll();
            }
          }
        });
      }
    };

    GitVcs.runInBackground(task);
    if (sync) {
      try {
        synchronized (LOCK) {
          LOCK.wait();
        }
      } catch (InterruptedException e) {
        LOG.error(e);
      }
    }
  }

  private void addListeners(final TaskExecution task, final ProgressIndicator indicator) {
    if (indicator != null) {
      indicator.setIndeterminate(myProgressAnalyzer == null);
    }
    // When receives an error line, adds a VcsException to the GitHandler.
    final GitLineHandlerListener listener = new GitLineHandlerListener() {<commit_msg>GitTask: execute result on proper thread<commit_after>/*
 * Copyright 2000-2010 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.Disposable;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.util.ui.UIUtil;
import git4idea.GitVcs;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.atomic.AtomicReference;

/**
 * A Task to run the given GitHandler with ability to cancel it.
 * Cancellation is implemented with a {@link java.util.Timer} which checks whether the ProgressIndicator was cancelled and kills
 * the GitHandler in that case.
 *
 * A GitTask may be executed synchronously ({@link #executeModal()} or asynchronously ({@link #executeAsync(GitTask.ResultHandler)}.
 * Result of the execution is encapsulated in {@link GitTaskResult}.
 *
 * @see {@link git4idea.commands.GitHandler#kill()}
 * @author <NAME>
 */
public class GitTask {

  private static final Logger LOG = Logger.getInstance(GitTask.class);

  private final Project myProject;
  private final GitHandler myHandler;
  private final String myTitle;
  private final AtomicReference<GitTaskResult> myResult = new AtomicReference<GitTaskResult>(GitTaskResult.INITIAL);
  private GitProgressAnalyzer myProgressAnalyzer;
  private boolean myExecuteResultInAwt = true;

  public GitTask(Project project, GitHandler handler, String title) {
    myProject = project;
    myHandler = handler;
    myTitle = title;
  }

  /**
   * Executes this task synchronously, with a modal progress dialog.
   * @return Result of the task execution.
   */
  public GitTaskResult executeModal() {
    ModalTask task = new ModalTask(myProject, myHandler, myTitle) {
      public void execute(ProgressIndicator indicator) {
        addListeners(this, indicator);
        GitHandlerUtil.runInCurrentThread(myHandler, indicator, false, myTitle);
      }

      @Override
      public void onSuccess() {
        if (!myHandler.errors().isEmpty()) {
          myResult.set(GitTaskResult.GIT_ERROR);
        } else {
          myResult.set(GitTaskResult.OK);
        }
      }

      @Override
      public void onCancel() {
        myResult.set(GitTaskResult.CANCELLED);
      }
    };

    ProgressManager.getInstance().run(task);
    return myResult.get();
  }

  /**
   * Executes the task synchronously, with a modal progress dialog.
   * @param resultHandler callback which will be called after task execution.
   */
  public void executeModal(GitTaskResultHandler resultHandler) {
    resultHandler.run(executeModal());
  }

  /**
   * Executes this task asynchronously, in backgrond. Calls the resultHandler when finished.
   * @param resultHandler callback called after the task has finished or was cancelled by user or automatically.
   */
  public void executeAsync(final GitTaskResultHandler resultHandler) {
    executeInBackground(false, resultHandler);
  }

  public void executeInBackground(boolean sync, final GitTaskResultHandler resultHandler) {
    final Object LOCK = new Object();
    BackgroundableTask task = new BackgroundableTask(myProject, myHandler, myTitle) {
      public void execute(ProgressIndicator indicator) {
        addListeners(this, indicator);
        GitHandlerUtil.runInCurrentThread(myHandler, indicator, false, myTitle);
      }

      @Override
      public void onSuccess() {
        final Runnable successRunnable = new Runnable() {
          @Override
          public void run() {
            if (!myHandler.errors().isEmpty()) { // TODO: handle errors smarter: an error may be not a complete failure.
              myResult.set(GitTaskResult.GIT_ERROR);
            }
            else {
              myResult.set(GitTaskResult.OK);
            }
            resultHandler.run(myResult.get());
            synchronized (LOCK) {
              LOCK.notifyAll();
            }
          }
        };
        executeInProperThread(successRunnable);
      }

      @Override
      public void onCancel() {
        final Runnable cancelRunnable = new Runnable() {
          @Override
          public void run() {
            myResult.set(GitTaskResult.CANCELLED);
            resultHandler.run(GitTaskResult.CANCELLED);
            synchronized (LOCK) {
              LOCK.notifyAll();
            }
          }
        };
        executeInProperThread(cancelRunnable);
      }
    };

    GitVcs.runInBackground(task);
    if (sync) {
      try {
        synchronized (LOCK) {
          LOCK.wait();
        }
      } catch (InterruptedException e) {
        LOG.error(e);
      }
    }
  }

  // executes on a pooled thread or on AWT thread depending on the setting.
  private void executeInProperThread(Runnable runnable) {
    if (myExecuteResultInAwt) {
      UIUtil.invokeAndWaitIfNeeded(runnable);
    } else {
      ApplicationManager.getApplication().executeOnPooledThread(runnable);
    }
  }

  private void addListeners(final TaskExecution task, final ProgressIndicator indicator) {
    if (indicator != null) {
      indicator.setIndeterminate(myProgressAnalyzer == null);
    }
    // When receives an error line, adds a VcsException to the GitHandler.
    final GitLineHandlerListener listener = new GitLineHandlerListener() {
{'fraction_non_alphanumeric': 0.0890467536758632, 'fraction_numerical': 0.002478110028085247, 'mean_word_length': 3.1979889042995837, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4224331', 'n_tokens_mistral': 3344, 'n_tokens_neox': 3258, 'n_words': 933}