Java tutorial
/******************************************************************************* * Copyright (c) 2008 MercurialEclipse project 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: * Bastian Doetsch - implementation * Andrei Loskutov - bug fixes * Amenel Voglozin - User information features: repo logical names in dialog msgs. ******************************************************************************/ package com.vectrace.MercurialEclipse.synchronize.actions; import java.lang.reflect.InvocationTargetException; import java.util.Collections; import java.util.HashMap; import java.util.Set; import java.util.TreeSet; import org.eclipse.compare.structuremergeviewer.IDiffElement; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.team.ui.synchronize.ISynchronizePageConfiguration; import org.eclipse.team.ui.synchronize.SynchronizeModelOperation; import org.eclipse.ui.statushandlers.StatusManager; import com.vectrace.MercurialEclipse.MercurialEclipsePlugin; import com.vectrace.MercurialEclipse.commands.HgPushPullClient; import com.vectrace.MercurialEclipse.exception.HgException; import com.vectrace.MercurialEclipse.menu.PushHandler; import com.vectrace.MercurialEclipse.model.ChangeSet; import com.vectrace.MercurialEclipse.model.HgRoot; import com.vectrace.MercurialEclipse.model.IHgRepositoryLocation; import com.vectrace.MercurialEclipse.preferences.MercurialPreferenceConstants; import com.vectrace.MercurialEclipse.synchronize.MercurialSynchronizeParticipant; import com.vectrace.MercurialEclipse.synchronize.MercurialSynchronizeSubscriber; import com.vectrace.MercurialEclipse.synchronize.Messages; import com.vectrace.MercurialEclipse.synchronize.cs.ChangesetGroup; import com.vectrace.MercurialEclipse.synchronize.cs.RepositoryChangesetGroup; import com.vectrace.MercurialEclipse.team.MercurialTeamProvider; import com.vectrace.MercurialEclipse.utils.ResourceUtils; import com.vectrace.MercurialEclipse.utils.StringUtils; public class PushPullSynchronizeOperation extends SynchronizeModelOperation { private final MercurialSynchronizeParticipant participant; private final boolean update; private final boolean isPull; private Set<? extends Object> targets; public PushPullSynchronizeOperation(ISynchronizePageConfiguration configuration, IDiffElement[] elements, Set<? extends Object> target, boolean isPull, boolean update) { super(configuration, elements); this.targets = target; this.participant = (MercurialSynchronizeParticipant) configuration.getParticipant(); this.isPull = isPull; this.update = update; } /** * Collect roots for the given object * @param monitor The progress monitor * @param hgRoots (Output) Collected roots are added to this * @param changeSet Current changeset. * @param target The object to get roots from * @return The new value for the selected changeset */ private void getRoots(final IProgressMonitor monitor, final Set<HgRoot> hgRoots, final Object target) { HgRoot hgRoot = null; if (target instanceof IProject) { hgRoot = MercurialTeamProvider.getHgRoot((IProject) target); } else if (target instanceof ChangeSet) { hgRoot = ((ChangeSet) target).getHgRoot(); } if (target instanceof ChangesetGroup) { ChangesetGroup group = (ChangesetGroup) target; checkChangesets(monitor, group); if (monitor.isCanceled()) { return; } // Alternative: Find all the heads and push/pull them individually (without doing // workspace refreshes in between) hgRoot = group.getChangesets().iterator().next().getHgRoot(); } if (target instanceof RepositoryChangesetGroup) { RepositoryChangesetGroup group = (RepositoryChangesetGroup) target; checkChangesets(monitor, group); if (monitor.isCanceled()) { return; } hgRoot = group.getRoot(); } if (hgRoot == null) { String message = "No hg root found for: " + target + ". Operation cancelled."; Status status = new Status(IStatus.WARNING, MercurialEclipsePlugin.ID, message); StatusManager.getManager().handle(status, StatusManager.SHOW); monitor.setCanceled(true); return; } hgRoots.add(hgRoot); } public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { Set<HgRoot> hgRoots = new TreeSet<HgRoot>(); ChangeSet changeSet = null; // TODO Make a changeRequest class of a pair with hgroot and changeset(s) for (Object target : targets) { if (target instanceof ChangeSet) { targets = Collections.singleton(target); changeSet = (ChangeSet) target; } } for (Object target : targets) { getRoots(monitor, hgRoots, target); if (monitor.isCanceled()) { return; } } //Inform the user on how many projects is about to update for (HgRoot root : hgRoots) { checkProjects(monitor, root); if (monitor.isCanceled()) { return; } } monitor.beginTask(getTaskName(hgRoots), 1); String jobName = isPull ? Messages.getString("PushPullSynchronizeOperation.PullJob") : Messages.getString("PushPullSynchronizeOperation.PushJob"); PushPullJob job = new PushPullJob(jobName, hgRoots, changeSet, monitor); if (changeSet == null) { for (HgRoot hgRoot : hgRoots) { job.setBranch(hgRoot, MercurialSynchronizeSubscriber.getSyncBranch(hgRoot)); } } job.schedule(); } private String getTaskName(Set<HgRoot> hgRoot) { String taskName; // TODO: use repo location map information better if (isPull) { taskName = Messages.getString("PushPullSynchronizeOperation.PullTask") + " "; for (IHgRepositoryLocation loc : participant.getRepositoryLocations().getLocations()) { taskName += loc.getLocation() + " "; } } else { taskName = Messages.getString("PushPullSynchronizeOperation.PushTask") + " "; for (HgRoot root : hgRoot) { taskName += root.getName() + " "; } } return taskName; } private void checkChangesets(final IProgressMonitor monitor, ChangesetGroup group) { checkChangesets(monitor, group.getChangesets().size(), group.getRepositoryChangesetGroup()); } private void checkChangesets(final IProgressMonitor monitor, RepositoryChangesetGroup rcGroup) { int csCount; if (isPull) { csCount = rcGroup.getIncoming().getChangesets().size(); } else { csCount = rcGroup.getOutgoing().getChangesets().size(); } checkChangesets(monitor, csCount, rcGroup); } private void checkChangesets(final IProgressMonitor monitor, int csCount, RepositoryChangesetGroup rcGroup) { if (csCount < 1) { // paranoia... monitor.setCanceled(true); return; } final String title; final String message; // // Get the repo logical name in order to embed it in the message shown to the user. // IPreferenceStore store = MercurialEclipsePlugin.getDefault().getPreferenceStore(); boolean showRepoLogicalName = store .getBoolean(MercurialPreferenceConstants.PREF_SHOW_LOGICAL_NAME_OF_REPOSITORIES); String repoName = ""; if (rcGroup.getRoot() != null && showRepoLogicalName) { IHgRepositoryLocation repoLocation = participant.getRepositoryLocation(rcGroup.getRoot()); if (!StringUtils.isEmpty(repoLocation.getLogicalName())) { repoName = " ([" + repoLocation.getLogicalName() + "])"; } } // if (isPull) { title = "Hg Pull"; message = "Pulling " + csCount + " changesets (or more) from the remote repository" + repoName + ".\n" + "The pull will fetch the *latest* version available remotely.\n" + "Continue?"; } else { if (csCount == 1) { return; } title = "Hg Push"; message = "Pushing " + csCount + " changesets to the remote repository" + repoName + ". Continue?"; } getShell().getDisplay().syncExec(new Runnable() { public void run() { if (!MercurialEclipsePlugin.showDontShowAgainConfirmDialog(title, message, MessageDialog.CONFIRM, MercurialPreferenceConstants.PREF_SHOW_PULL_WARNING_DIALOG, getShell())) { monitor.setCanceled(true); } } }); } /** * Checks whether all conditions (related to projects) necessary for a normal operation are * fulfilled. The monitor is canceled when the operation should not continue. * <p> * Conditions: * <ul> * <li>There must be at least one project. * <li>All projects impacted must be open. * </ul> */ private void checkProjects(final IProgressMonitor monitor, HgRoot hgRoot) { Set<IProject> projects = ResourceUtils.getProjects(hgRoot); if (!isPull || projects.size() <= 1) { if (projects.size() == 0) { // paranoia monitor.setCanceled(true); } return; } final String title = "Hg Pull"; final String message = "Pull will affect " + projects.size() + " projects in workspace. Continue?"; getShell().getDisplay().syncExec(new Runnable() { public void run() { if (!MercurialEclipsePlugin.showDontShowAgainConfirmDialog(title, message, MessageDialog.CONFIRM, MercurialPreferenceConstants.PREF_SHOW_MULTIPLE_PROJECTS_DIALOG, getShell())) { monitor.setCanceled(true); } } }); } private final class PushPullJob extends /*NON UI!*/Job { private final IProgressMonitor opMonitor; private final Set<HgRoot> hgRoots; private final ChangeSet changeSet; private final HashMap<HgRoot, String> branches = new HashMap<HgRoot, String>(); /** * @param name Human readable name * @param hgRoot The hg root * @param changeSet The changeset, may be null to push/pull everything * @param opMonitor The progress monitor */ private PushPullJob(String name, Set<HgRoot> hgRoot, ChangeSet changeSet, IProgressMonitor opMonitor) { super(name); this.hgRoots = hgRoot; this.changeSet = changeSet; this.opMonitor = opMonitor; } /** * @param branch The branch name, or null for all/any */ public void setBranch(HgRoot root, String branch) { this.branches.put(root, branch); } @Override protected IStatus run(IProgressMonitor moni) { try { for (HgRoot hgRoot : hgRoots) { IStatus stat = run(moni, hgRoot); if (stat != null) { return stat; } } } finally { opMonitor.done(); } return Status.OK_STATUS; } protected IStatus run(IProgressMonitor moni, final HgRoot hgRoot) { String branch = branches.get(hgRoot); IHgRepositoryLocation location = participant.getRepositoryLocation(hgRoot); if (location == null) { return Status.OK_STATUS; } // re-validate the location as it might have changed credentials... try { location = MercurialEclipsePlugin.getRepoManager().getRepoLocation(location.getLocation()); } catch (HgException e1) { MercurialEclipsePlugin.logError(e1); return Status.OK_STATUS; } if (opMonitor.isCanceled() || moni.isCanceled()) { return Status.CANCEL_STATUS; } try { if (isPull) { boolean rebase = false; boolean force = false; boolean timeout = true; HgPushPullClient.pull(hgRoot, changeSet, location, update, rebase, force, timeout, false, branch, moni); // pull client does the refresh automatically, no extra job required here } else { HgPushPullClient.push(hgRoot, location, false, changeSet, Integer.MAX_VALUE, branch, moni); // push client does the refresh automatically, no extra job required here } return null; } catch (final HgException ex) { MercurialEclipsePlugin.logError(ex); if (!isPull) { // try to recover: open the default dialog, where user can change some // settings like password/force flag etc (issue #10720) MercurialEclipsePlugin.getStandardDisplay().asyncExec(new Runnable() { public void run() { try { PushHandler handler = new PushHandler(); handler.setInitialMessage( Messages.getString("PushPullSynchronizeOperation.PushFailed") + " " + ex.getConciseMessage()); handler.run(hgRoot); } catch (Exception e) { MercurialEclipsePlugin.logError(e); } } }); } return Status.CANCEL_STATUS; } } } }