Package org.eclipse.egit.ui.internal.branch

Source Code of org.eclipse.egit.ui.internal.branch.BranchOperationUI

/*******************************************************************************
* Copyright (c) 2010, 2013 SAP AG and others.
* 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.branch;

import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;

import org.eclipse.core.resources.IProject;
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.Job;
import org.eclipse.core.runtime.jobs.JobChangeAdapter;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchManager;
import org.eclipse.debug.core.model.ISourceLocator;
import org.eclipse.debug.core.sourcelookup.ISourceContainer;
import org.eclipse.debug.core.sourcelookup.ISourceLookupDirector;
import org.eclipse.debug.core.sourcelookup.containers.ProjectSourceContainer;
import org.eclipse.egit.core.RepositoryUtil;
import org.eclipse.egit.core.internal.util.ProjectUtil;
import org.eclipse.egit.core.op.BranchOperation;
import org.eclipse.egit.core.op.IEGitOperation.PostExecuteTask;
import org.eclipse.egit.core.op.IEGitOperation.PreExecuteTask;
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.decorators.GitLightweightDecorator;
import org.eclipse.egit.ui.internal.dialogs.AbstractBranchSelectionDialog;
import org.eclipse.egit.ui.internal.dialogs.CheckoutDialog;
import org.eclipse.egit.ui.internal.dialogs.DeleteBranchDialog;
import org.eclipse.egit.ui.internal.dialogs.NonDeletedFilesDialog;
import org.eclipse.egit.ui.internal.dialogs.RenameBranchDialog;
import org.eclipse.egit.ui.internal.repository.CreateBranchWizard;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.dialogs.MessageDialogWithToggle;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.window.Window;
import org.eclipse.jface.wizard.WizardDialog;
import org.eclipse.jgit.api.CheckoutResult;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.PlatformUI;

/**
* The UI wrapper for {@link BranchOperation}
*/
public class BranchOperationUI {
  // create
  private final static int MODE_CREATE = 1;

  private final static int MODE_CHECKOUT = 2;

  private final static int MODE_DELETE = 3;

  private final static int MODE_RENAME = 4;

  private final Repository repository;

  private String target;

  private String base;

  /**
   * In the case of checkout conflicts, a dialog is shown to let the user
   * stash, reset or commit. After that, checkout is tried again. The second
   * time we do checkout, we don't want to ask any questions we already asked
   * the first time, so this will be false then.
   */
  private final boolean showQuestionAboutTarget;

  private final int mode;

  /**
   * Create an operation for manipulating branches
   *
   * @param repository
   * @return the {@link BranchOperationUI}
   */
  public static BranchOperationUI rename(Repository repository) {
    return new BranchOperationUI(repository, MODE_RENAME);
  }

  /**
   * Create an operation for manipulating branches
   *
   * @param repository
   * @return the {@link BranchOperationUI}
   */
  public static BranchOperationUI delete(Repository repository) {
    return new BranchOperationUI(repository, MODE_DELETE);
  }

  /**
   * Create an operation for creating a local branch
   *
   * @param repository
   * @return the {@link BranchOperationUI}
   */
  public static BranchOperationUI create(Repository repository) {
    BranchOperationUI op = new BranchOperationUI(repository, MODE_CREATE);
    return op;
  }

  /**
   * Create an operation for creating a local branch with a given base reference
   *
   * @param repository
   * @param baseRef
   * @return the {@link BranchOperationUI}
   */
  public static BranchOperationUI createWithRef(Repository repository, String baseRef) {
    BranchOperationUI op = new BranchOperationUI(repository, MODE_CREATE);
    op.base = baseRef;
    return op;
  }

  /**
   * Create an operation for checking out a local branch
   *
   * @param repository
   * @return the {@link BranchOperationUI}
   */
  public static BranchOperationUI checkout(Repository repository) {
    return new BranchOperationUI(repository, MODE_CHECKOUT);
  }

  /**
   * Create an operation for checking out a branch
   *
   * @param repository
   * @param target
   *            a valid {@link Ref} name or commit id
   * @return the {@link BranchOperationUI}
   */
  public static BranchOperationUI checkout(Repository repository,
      String target) {
    return new BranchOperationUI(repository, target, true);
  }

  /**
   * Create an operation for checking out a branch without showing a question
   * dialog about the target.
   *
   * @param repository
   * @param target
   *            a valid {@link Ref} name or commit id
   * @return the {@link BranchOperationUI}
   */
  public static BranchOperationUI checkoutWithoutQuestion(
      Repository repository, String target) {
    return new BranchOperationUI(repository, target, false);
  }

  /**
   * @param refName
   *            the full ref name which will be checked out
   * @return true if checkout will need additional input from the user before
   *         continuing
   */
  public static boolean checkoutWillShowQuestionDialog(String refName) {
    return shouldShowCheckoutRemoteTrackingDialog(refName);
  }

  /**
   * @param repository
   * @param target
   * @param showQuestionAboutTarget
   */
  private BranchOperationUI(Repository repository, String target,
      boolean showQuestionAboutTarget) {
    this.repository = repository;
    this.target = target;
    this.showQuestionAboutTarget = showQuestionAboutTarget;
    this.mode = 0;
  }

  /**
   * Select and checkout a branch
   *
   * @param repository
   * @param mode
   */
  private BranchOperationUI(Repository repository, int mode) {
    this.repository = repository;
    this.mode = mode;
    this.showQuestionAboutTarget = true;
  }

  /**
   * Starts the operation asynchronously
   */
  public void start() {
    if (!repository.getRepositoryState().canCheckout()) {
      MessageDialog.openError(getShell(),
          UIText.BranchAction_cannotCheckout, NLS.bind(
              UIText.BranchAction_repositoryState, repository
                  .getRepositoryState().getDescription()));
      return;
    }

    if (shouldCancelBecauseOfRunningLaunches())
      return;

    askForTargetIfNecessary();
    if (target == null)
      return;

    String repoName = Activator.getDefault().getRepositoryUtil()
        .getRepositoryName(repository);
    String jobname = NLS.bind(UIText.BranchAction_checkingOut, repoName,
        target);

    final boolean restore = Activator.getDefault().getPreferenceStore()
        .getBoolean(UIPreferences.CHECKOUT_PROJECT_RESTORE);
    final BranchOperation bop = new BranchOperation(repository, target,
        !restore);

    Job job = new WorkspaceJob(jobname) {

      @Override
      public IStatus runInWorkspace(IProgressMonitor monitor) {
        try {
          if (restore) {
            final BranchProjectTracker tracker = new BranchProjectTracker(
                repository);
            final AtomicReference<IMemento> memento = new AtomicReference<IMemento>();
            bop.addPreExecuteTask(new PreExecuteTask() {

              public void preExecute(Repository pRepo,
                  IProgressMonitor pMonitor)
                  throws CoreException {
                // Snapshot current projects before checkout
                // begins
                memento.set(tracker.snapshot());
              }
            });
            bop.addPostExecuteTask(new PostExecuteTask() {

              public void postExecute(Repository pRepo,
                  IProgressMonitor pMonitor)
                  throws CoreException {
                IMemento snapshot = memento.get();
                if (snapshot == null)
                  return;
                // Save previous branch's projects and restore
                // current branch's projects
                tracker.save(snapshot).restore(pMonitor);
              }
            });
          }

          bop.execute(monitor);
        } catch (CoreException e) {
          switch (bop.getResult().getStatus()) {
          case CONFLICTS:
          case NONDELETED:
            break;
          default:
            return Activator.createErrorStatus(
                UIText.BranchAction_branchFailed, e);
          }
        } finally {
          GitLightweightDecorator.refresh();
        }
        return Status.OK_STATUS;
      }

      @Override
      public boolean belongsTo(Object family) {
        if (JobFamilies.CHECKOUT.equals(family))
          return true;
        return super.belongsTo(family);
      }
    };
    job.setUser(true);
    // Set scheduling rule to workspace because we may have to re-create
    // projects using BranchProjectTracker.
    if (restore)
      job.setRule(ResourcesPlugin.getWorkspace().getRoot());
    job.addJobChangeListener(new JobChangeAdapter() {
      @Override
      public void done(IJobChangeEvent cevent) {
        show(bop.getResult());
      }
    });
    job.schedule();
  }

  /**
   * Runs the operation synchronously
   *
   * @param monitor
   * @throws CoreException
   *
   */
  public void run(IProgressMonitor monitor) throws CoreException {
    if (!repository.getRepositoryState().canCheckout()) {
      PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
        public void run() {
          MessageDialog.openError(getShell(),
              UIText.BranchAction_cannotCheckout, NLS.bind(
                  UIText.BranchAction_repositoryState,
                  repository.getRepositoryState()
                      .getDescription()));
        }
      });
      return;
    }

    if (shouldCancelBecauseOfRunningLaunches())
      return;

    askForTargetIfNecessary();
    if (target == null)
      return;

    BranchOperation bop = new BranchOperation(repository, target);
    bop.execute(monitor);

    show(bop.getResult());
  }

  private void askForTargetIfNecessary() {
    if (target == null)
      target = getTargetWithDialog();
    if (target != null && showQuestionAboutTarget) {
      if (shouldShowCheckoutRemoteTrackingDialog(target))
        target = getTargetWithCheckoutRemoteTrackingDialog();
    }
  }

  private static boolean shouldShowCheckoutRemoteTrackingDialog(String refName) {
    boolean isRemoteTrackingBranch = refName != null
        && refName.startsWith(Constants.R_REMOTES);
    if (isRemoteTrackingBranch) {
      boolean showDetachedHeadWarning = Activator.getDefault()
          .getPreferenceStore()
          .getBoolean(UIPreferences.SHOW_DETACHED_HEAD_WARNING);
      // If the user has not yet chosen to ignore the warning about
      // getting into a "detached HEAD" state, then we show them a dialog
      // whether a remote-tracking branch should be checked out with a
      // detached HEAD or checking it out as a new local branch.
      return showDetachedHeadWarning;
    } else {
      return false;
    }
  }

  private String getTargetWithDialog() {
    final String[] dialogResult = new String[1];
    PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable() {
      public void run() {
        dialogResult[0] = getTargetWithDialogInUI();
      }
    });
    return dialogResult[0];
  }

  private String getTargetWithDialogInUI() {
    AbstractBranchSelectionDialog dialog;
    switch (mode) {
    case MODE_CHECKOUT:
      dialog = new CheckoutDialog(getShell(), repository);
      break;
    case MODE_CREATE:
      CreateBranchWizard wiz;
      try {
        if (base == null)
          base = repository.getFullBranch();
        wiz = new CreateBranchWizard(repository, base);
      } catch (IOException e) {
        wiz = new CreateBranchWizard(repository);
      }
      new WizardDialog(getShell(), wiz).open();
      return null;
    case MODE_DELETE:
      new DeleteBranchDialog(getShell(), repository).open();
      return null;
    case MODE_RENAME:
      new RenameBranchDialog(getShell(), repository).open();
      return null;
    default:
      return null;
    }

    if (dialog.open() != Window.OK) {
      return null;
    }
    return dialog.getRefName();
  }

  private String getTargetWithCheckoutRemoteTrackingDialog() {
    final String[] dialogResult = new String[1];
    PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable() {
      public void run() {
        dialogResult[0] = getTargetWithCheckoutRemoteTrackingDialogInUI();
      }
    });
    return dialogResult[0];
  }

  private String getTargetWithCheckoutRemoteTrackingDialogInUI() {
    String[] buttons = new String[] {
        UIText.BranchOperationUI_CheckoutRemoteTrackingAsLocal,
        UIText.BranchOperationUI_CheckoutRemoteTrackingCommit,
        IDialogConstants.CANCEL_LABEL };
    MessageDialog questionDialog = new MessageDialog(
        getShell(),
        UIText.BranchOperationUI_CheckoutRemoteTrackingTitle,
        null,
        UIText.BranchOperationUI_CheckoutRemoteTrackingQuestion,
        MessageDialog.QUESTION, buttons, 0);
    int result = questionDialog.open();
    if (result == 0) {
      // Check out as new local branch
      CreateBranchWizard wizard = new CreateBranchWizard(repository,
          target);
      WizardDialog createBranchDialog = new WizardDialog(getShell(),
          wizard);
      createBranchDialog.open();
      return null;
    } else if (result == 1) {
      // Check out commit
      return target;
    } else {
      // Cancel
      return null;
    }
  }

  private Shell getShell() {
    return PlatformUI.getWorkbench().getDisplay().getActiveShell();
  }

  /**
   * @param result
   *            the result to show
   */
  public void show(final CheckoutResult result) {
    if (result.getStatus() == CheckoutResult.Status.CONFLICTS) {
      PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
        public void run() {
          Shell shell = PlatformUI.getWorkbench()
              .getActiveWorkbenchWindow().getShell();
          CleanupUncomittedChangesDialog cleanupUncomittedChangesDialog = new CleanupUncomittedChangesDialog(
              shell,
              UIText.BranchResultDialog_CheckoutConflictsTitle,
              NLS.bind(
                  UIText.BranchResultDialog_CheckoutConflictsMessage,
                  Repository.shortenRefName(target)),
              repository, result.getConflictList());
          cleanupUncomittedChangesDialog.open();
          if (cleanupUncomittedChangesDialog.shouldContinue()) {
            BranchOperationUI op = BranchOperationUI
                .checkoutWithoutQuestion(repository, target);
            op.start();
          }
        }
      });
    } else if (result.getStatus() == CheckoutResult.Status.NONDELETED) {
      // double-check if the files are still there
      boolean show = false;
      List<String> pathList = result.getUndeletedList();
      for (String path : pathList)
        if (new File(repository.getWorkTree(), path).exists()) {
          show = true;
          break;
        }
      if (!show)
        return;
      PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
        public void run() {
          Shell shell = PlatformUI.getWorkbench()
              .getActiveWorkbenchWindow().getShell();
          new NonDeletedFilesDialog(shell, repository, result
              .getUndeletedList()).open();
        }
      });
    } else if (result.getStatus() == CheckoutResult.Status.OK) {
      if (RepositoryUtil.isDetachedHead(repository))
        showDetachedHeadWarning();
    }
  }

  private void showDetachedHeadWarning() {
    PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
      public void run() {
        IPreferenceStore store = Activator.getDefault()
            .getPreferenceStore();

        if (store.getBoolean(UIPreferences.SHOW_DETACHED_HEAD_WARNING)) {
          String toggleMessage = UIText.BranchResultDialog_DetachedHeadWarningDontShowAgain;
          MessageDialogWithToggle.openInformation(PlatformUI
              .getWorkbench().getActiveWorkbenchWindow()
              .getShell(),
              UIText.BranchOperationUI_DetachedHeadTitle,
              UIText.BranchOperationUI_DetachedHeadMessage,
              toggleMessage, false, store,
              UIPreferences.SHOW_DETACHED_HEAD_WARNING);
        }
      }
    });
  }

  private boolean shouldCancelBecauseOfRunningLaunches() {
    if (mode == MODE_CHECKOUT)
      return false;
    final IPreferenceStore store = Activator.getDefault().getPreferenceStore();
    if (!store
        .getBoolean(UIPreferences.SHOW_RUNNING_LAUNCH_ON_CHECKOUT_WARNING))
      return false;

    final ILaunchConfiguration launchConfiguration = getRunningLaunchConfiguration();
    if (launchConfiguration != null) {
      final boolean[] dialogResult = new boolean[1];
      PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable() {
        public void run() {
          dialogResult[0] = showContinueDialogInUI(store,
              launchConfiguration);
        }
      });
      return dialogResult[0];
    }
    return false;
  }

  private boolean showContinueDialogInUI(final IPreferenceStore store,
      final ILaunchConfiguration launchConfiguration) {
    String[] buttons = new String[] { UIText.BranchOperationUI_Continue,
        IDialogConstants.CANCEL_LABEL };
    String message = NLS.bind(
        UIText.BranchOperationUI_RunningLaunchMessage,
        launchConfiguration.getName());
    MessageDialogWithToggle continueDialog = new MessageDialogWithToggle(
        getShell(), UIText.BranchOperationUI_RunningLaunchTitle, null,
        message, MessageDialog.NONE, buttons, 0,
        UIText.BranchOperationUI_RunningLaunchDontShowAgain, false);
    int result = continueDialog.open();
    // cancel
    if (result == IDialogConstants.CANCEL_ID || result == SWT.DEFAULT)
      return true;
    boolean dontWarnAgain = continueDialog.getToggleState();
    if (dontWarnAgain)
      store.setValue(
          UIPreferences.SHOW_RUNNING_LAUNCH_ON_CHECKOUT_WARNING,
          false);
    return false;
  }

  private ILaunchConfiguration getRunningLaunchConfiguration() {
    Set<IProject> projects = new HashSet<IProject>(
        Arrays.asList(ProjectUtil.getProjects(repository)));

    ILaunchManager launchManager = DebugPlugin.getDefault()
        .getLaunchManager();
    ILaunch[] launches = launchManager.getLaunches();
    for (ILaunch launch : launches) {
      if (launch.isTerminated())
        continue;
      ISourceLocator locator = launch.getSourceLocator();
      if (locator instanceof ISourceLookupDirector) {
        ISourceLookupDirector director = (ISourceLookupDirector) locator;
        ISourceContainer[] containers = director.getSourceContainers();
        if (isAnyProjectInSourceContainers(containers, projects))
          return launch.getLaunchConfiguration();
      }
    }
    return null;
  }

  private boolean isAnyProjectInSourceContainers(
      ISourceContainer[] containers, Set<IProject> projects) {
    for (ISourceContainer container : containers) {
      if (container instanceof ProjectSourceContainer) {
        ProjectSourceContainer projectContainer = (ProjectSourceContainer) container;
        if (projects.contains(projectContainer.getProject()))
          return true;
      }
      try {
        boolean found = isAnyProjectInSourceContainers(
            container.getSourceContainers(), projects);
        if (found)
          return true;
      } catch (CoreException e) {
        // Ignore the child source containers, continue search
      }
    }
    return false;
  }

}
TOP

Related Classes of org.eclipse.egit.ui.internal.branch.BranchOperationUI

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.