com.microsoft.tfs.client.common.ui.dialogs.vc.ConflictDialog.java Source code

Java tutorial

Introduction

Here is the source code for com.microsoft.tfs.client.common.ui.dialogs.vc.ConflictDialog.java

Source

// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See License.txt in the repository root.

package com.microsoft.tfs.client.common.ui.dialogs.vc;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;

import com.microsoft.tfs.client.common.commands.ResolveConflictsCommand;
import com.microsoft.tfs.client.common.commands.TFSCommand;
import com.microsoft.tfs.client.common.commands.vc.QueryConflictsCommand;
import com.microsoft.tfs.client.common.commands.vc.RefreshPendingChangesCommand;
import com.microsoft.tfs.client.common.framework.command.ICommandExecutor;
import com.microsoft.tfs.client.common.framework.resources.command.ResourceChangingCommand;
import com.microsoft.tfs.client.common.repository.TFSRepository;
import com.microsoft.tfs.client.common.ui.Messages;
import com.microsoft.tfs.client.common.ui.conflicts.resolutions.EclipseMergeConflictResolution;
import com.microsoft.tfs.client.common.ui.controls.vc.ConflictTable;
import com.microsoft.tfs.client.common.ui.framework.command.UICommandExecutorFactory;
import com.microsoft.tfs.client.common.ui.framework.dialog.BaseDialog;
import com.microsoft.tfs.client.common.ui.framework.helper.ShellUtils;
import com.microsoft.tfs.client.common.ui.framework.helper.UIHelpers;
import com.microsoft.tfs.client.common.ui.framework.sizing.ControlSize;
import com.microsoft.tfs.client.common.ui.helpers.AutomationIDHelper;
import com.microsoft.tfs.client.common.ui.helpers.ConflictHelpers;
import com.microsoft.tfs.core.clients.versioncontrol.conflicts.ConflictDescription;
import com.microsoft.tfs.core.clients.versioncontrol.conflicts.resolutions.ConflictResolution;
import com.microsoft.tfs.core.clients.versioncontrol.conflicts.resolutions.ConflictResolutionOptions;
import com.microsoft.tfs.core.clients.versioncontrol.conflicts.resolutions.ConflictResolutionStatus;
import com.microsoft.tfs.core.clients.versioncontrol.conflicts.resolutions.ConflictResolutionStatusListener;
import com.microsoft.tfs.core.clients.versioncontrol.conflicts.resolutions.CoreConflictResolution;
import com.microsoft.tfs.core.clients.versioncontrol.conflicts.resolutions.ExternalConflictResolution;
import com.microsoft.tfs.core.clients.versioncontrol.conflicts.resolutions.ExternalConflictResolution.ExternalConflictResolver;
import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.Conflict;
import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.Resolution;
import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.Workspace;
import com.microsoft.tfs.core.clients.versioncontrol.specs.ItemSpec;
import com.microsoft.tfs.util.Check;
import com.microsoft.tfs.util.LocaleUtil;

/**
 * ConflictDialog holds a list of conflicts and allows them to be resolved
 * singularly or en masse.
 *
 * TODO: this needs to be hooked up to the plugin's conflictManager. Will
 * explorer get a conflict manager? oy.
 */
public class ConflictDialog extends BaseDialog implements ConflictResolutionStatusListener {
    public static final String CONFLICTS_TABLE_ID = "ConflictDialog.ConflictTable"; //$NON-NLS-1$

    private final Shell parentShell;
    private final TFSRepository repository;

    private ConflictTable conflictTable;
    private Label countLabel;

    private int resolveCount = 0;

    private final List<ConflictDescription> descriptionList = new ArrayList<ConflictDescription>();
    private final List<ConflictResolution> runningResolutionList = new ArrayList<ConflictResolution>();

    private static final int AUTOMERGE = IDialogConstants.CLIENT_ID;

    public ConflictDialog(final Shell parentShell, final TFSRepository repository,
            final ConflictDescription[] descriptions) {
        super(parentShell);

        this.parentShell = ShellUtils.getBestParent(parentShell);

        Check.notNull(repository, "repository"); //$NON-NLS-1$
        this.repository = repository;

        for (int i = 0; i < descriptions.length; i++) {
            descriptionList.add(descriptions[i]);
        }
    }

    /*
     * (non-Javadoc)
     *
     * @seecom.microsoft.tfs.client.common.ui.shared.dialog.BaseDialog#
     * hookAddToDialogArea(org.eclipse. swt.widgets.Composite)
     */
    @Override
    protected void hookAddToDialogArea(final Composite dialogArea) {
        final GridLayout dialogLayout = new GridLayout(1, false);

        dialogLayout.marginWidth = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN);
        dialogLayout.marginHeight = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_MARGIN);

        dialogArea.setLayout(dialogLayout);

        final Label descriptionLabel = new Label(dialogArea, SWT.NONE);
        descriptionLabel.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, true, false));
        descriptionLabel.setText(Messages.getString("ConflictDialog.DescriptionLabelText")); //$NON-NLS-1$

        /* Make the label larger so that there's spacing beneath it. */
        ControlSize.setCharHeightHint(descriptionLabel, 2);

        final Label conflictLabel = new Label(dialogArea, SWT.NONE);
        AutomationIDHelper.setWidgetID(conflictLabel, CONFLICTS_TABLE_ID);
        conflictLabel.setLayoutData(new GridData(SWT.LEFT, SWT.BOTTOM, true, false));
        conflictLabel.setText(Messages.getString("ConflictDialog.ConflictLabelText")); //$NON-NLS-1$

        // set up the conflict table
        conflictTable = new ConflictTable(dialogArea, SWT.MULTI | SWT.FULL_SELECTION | SWT.BORDER);

        final GridData conflictTableData = new GridData(SWT.FILL, SWT.FILL, true, true);
        conflictTableData.grabExcessHorizontalSpace = true;
        conflictTableData.grabExcessVerticalSpace = true;
        conflictTable.setLayoutData(conflictTableData);

        conflictTable.addSelectionChangedListener(new ISelectionChangedListener() {
            @Override
            public void selectionChanged(final SelectionChangedEvent event) {
                if (conflictTable.getSelectedElements().length == 0) {
                    getButton(IDialogConstants.OK_ID).setEnabled(false);
                } else {
                    getButton(IDialogConstants.OK_ID).setEnabled(true);
                }
            }
        });
        conflictTable.addDoubleClickListener(new IDoubleClickListener() {
            @Override
            public void doubleClick(final DoubleClickEvent event) {
                resolveSelection();
            }
        });
        conflictTable.setFocus();

        ControlSize.setCharHeightHint(conflictTable, 12);

        // add an auto merge all button
        addButtonDescription(AUTOMERGE, Messages.getString("ConflictDialog.AutoMergeAllButtonText"), false); //$NON-NLS-1$
    }

    /*
     * override Dialog.createButtonBar so that we can add a label to the left of
     * the buttons which shows resolution progress, a la MSFT
     *
     * (non-Javadoc)
     *
     * @see
     * org.eclipse.jface.dialogs.Dialog#createButtonBar(org.eclipse.swt.widgets
     * .Composite)
     */
    @Override
    protected Control createButtonBar(final Composite parent) {
        final Composite buttonBar = new Composite(parent, SWT.NONE);

        final GridLayout layout = new GridLayout();
        layout.numColumns = 2;
        layout.makeColumnsEqualWidth = false;
        layout.horizontalSpacing = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_SPACING);
        buttonBar.setLayout(layout);

        final GridData data = new GridData(SWT.FILL, SWT.BOTTOM, true, false);
        data.grabExcessHorizontalSpace = true;
        data.grabExcessVerticalSpace = false;
        buttonBar.setLayoutData(data);

        buttonBar.setFont(parent.getFont());

        // this is our label (eg "2 remaining, 0 resolved")
        countLabel = new Label(buttonBar, SWT.NONE);
        final GridData countLabelData = new GridData(SWT.LEFT, SWT.CENTER, true, true);
        countLabelData.grabExcessHorizontalSpace = true;
        countLabelData.horizontalIndent = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN);
        countLabel.setLayoutData(countLabelData);

        ControlSize.setCharWidthHint(countLabel, 36);

        // add the dialog's button bar to the right
        final Control buttonControl = super.createButtonBar(buttonBar);
        buttonControl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, true, false));

        return buttonBar;
    }

    /*
     * (non-Javadoc)
     *
     * @seecom.microsoft.tfs.client.common.ui.shared.dialog.BaseDialog#
     * hookAfterButtonsCreated()
     */
    @Override
    protected void hookAfterButtonsCreated() {
        getButton(IDialogConstants.CANCEL_ID).setText(Messages.getString("ConflictDialog.CloseButtonTExt")); //$NON-NLS-1$
        getButton(IDialogConstants.OK_ID).setText(Messages.getString("ConflictDialog.ResolveButtonText")); //$NON-NLS-1$

        refreshLabels();
    }

    /**
     * Refresh the conflict list and the conflict table, performed after a
     * conflict has been resolved (successfully or no). This will refresh the
     * list of conflicts, as this may have changed during conflict resolution.
     */
    private void refresh() {
        final Set<ItemSpec> itemSpecSet = new HashSet<ItemSpec>();

        for (final Iterator i = descriptionList.iterator(); i.hasNext();) {
            final ConflictDescription description = (ConflictDescription) i.next();

            final ItemSpec[] descriptionSpecs = description.getConflictItemSpecs();

            if (descriptionSpecs != null) {
                for (int j = 0; j < descriptionSpecs.length; j++) {
                    itemSpecSet.add(descriptionSpecs[j]);
                }
            }
        }

        final ItemSpec[] requerySpecs = itemSpecSet.toArray(new ItemSpec[itemSpecSet.size()]);

        final QueryConflictsCommand requeryCommand = new QueryConflictsCommand(repository, requerySpecs);
        final IStatus requeryStatus = UICommandExecutorFactory.newUICommandExecutor(getShell())
                .execute(requeryCommand);

        if (requeryStatus.isOK()) {
            descriptionList.clear();
            descriptionList.addAll(Arrays.asList(requeryCommand.getConflictDescriptions()));
        }

        refreshLabels();
    }

    /**
     * Refresh the conflict table with the internal list of conflict
     * descriptions, and update any count or status labels.
     */
    private void refreshLabels() {
        conflictTable
                .setConflictDescriptions(descriptionList.toArray(new ConflictDescription[descriptionList.size()]));
        conflictTable.refresh();

        final int conflictCount = descriptionList.size();
        if (conflictCount > 0) {
            conflictTable.setSelection(0);
            getButton(IDialogConstants.OK_ID).setEnabled(true);
            getButton(AUTOMERGE).setEnabled(true);
        } else {
            getButton(IDialogConstants.OK_ID).setEnabled(false);
            getButton(AUTOMERGE).setEnabled(false);
        }

        if (conflictCount == 0) {
            countLabel.setText(Messages.getString("ConflictDialog.NoConflictsRemaining")); //$NON-NLS-1$
        } else if (resolveCount == 0) {
            if (conflictCount == 1) {
                countLabel.setText(Messages.getString("ConflictDialog.OneConflict")); //$NON-NLS-1$
            } else {
                final String messageFormat = Messages.getString("ConflictDialog.MultiConflictsFormat"); //$NON-NLS-1$
                countLabel.setText(MessageFormat.format(messageFormat, conflictCount));
            }
        } else {
            if (conflictCount == 1) {
                final String messageFormat = Messages.getString("ConflictDialog.OneRemainingMultiResolvedFormat"); //$NON-NLS-1$
                countLabel.setText(MessageFormat.format(messageFormat, resolveCount));
            } else {
                final String messageFormat = Messages.getString("ConflictDialog.MultiRemainingMultiResolvedFormat"); //$NON-NLS-1$
                countLabel.setText(MessageFormat.format(messageFormat, conflictCount, resolveCount));

            }
        }
    }

    /*
     * (non-Javadoc)
     *
     * @seecom.microsoft.tfs.client.common.ui.shared.dialog.BaseDialog#
     * provideDialogTitle()
     */
    @Override
    protected String provideDialogTitle() {
        return Messages.getString("ConflictDialog.DialogTitle"); //$NON-NLS-1$
    }

    /*
     * (non-Javadoc)
     *
     * @see
     * com.microsoft.tfs.client.common.ui.shared.dialog.BaseDialog#buttonPressed
     * (int)
     */
    @Override
    protected void buttonPressed(final int buttonId) {
        // TODO; should this really be *auto merge all*? this seems non-
        // intuitive with the selection? maybe it should be "auto merge
        // selected" unless there's none selected, in which case it should
        // be "auto merge all"?
        if (buttonId == AUTOMERGE) {
            automergeAll();
        } else if (buttonId == IDialogConstants.OK_ID) {
            resolveSelection();
        } else if (buttonId == IDialogConstants.CANCEL_ID && cancelResolution()) {
            super.cancelPressed();

            /* Refresh pending changes */
            final RefreshPendingChangesCommand pendingChangesCommand = new RefreshPendingChangesCommand(repository);
            UICommandExecutorFactory.newUICommandExecutor(parentShell).execute(pendingChangesCommand);
        }
    }

    /**
     * Prompt the user to cancel running resolutions. (If there are any.)
     *
     * @return true to exit, false otherwise
     */
    private boolean cancelResolution() {
        synchronized (runningResolutionList) {
            if (runningResolutionList.size() == 0) {
                return true;
            }

            String message;
            if (runningResolutionList.size() == 1) {
                message = Messages.getString("ConflictDialog.ConfirmSingleConflictDialogText"); //$NON-NLS-1$
            } else {
                message = Messages.getString("ConflictDialog.ConfirmMultiConflictsDialogText"); //$NON-NLS-1$
            }

            final boolean cancelling = MessageDialog.openQuestion(getShell(),
                    Messages.getString("ConflictDialog.ConfirmCancelDialogTitle"), //$NON-NLS-1$
                    message);

            if (!cancelling) {
                return false;
            }

            for (final Iterator i = runningResolutionList.iterator(); i.hasNext();) {
                final ConflictResolution resolution = (ConflictResolution) i.next();

                resolution.removeStatusListener(this);
                resolution.cancel();
            }
        }

        return true;
    }

    /**
     * Auto merge all conflicts. Attempt to create a new CoreConflict with
     * Resolution.AcceptMerge.
     */
    private void automergeAll() {
        final List<ConflictResolution> resolutionList = new ArrayList<ConflictResolution>();

        // Remove any running resolutions
        for (final Iterator<ConflictDescription> i = descriptionList.iterator(); i.hasNext();) {
            final ConflictDescription description = i.next();

            if (!isResolving(description)) {
                resolutionList.add(new CoreConflictResolution(description,
                        Messages.getString("ConflictDialog.ResolutionDescription"), //$NON-NLS-1$
                        Messages.getString("ConflictDialog.ResolutionHelpText"), //$NON-NLS-1$
                        ConflictResolutionOptions.NONE, Resolution.ACCEPT_MERGE));
            }
        }

        final ConflictResolution[] resolutions = resolutionList
                .toArray(new ConflictResolution[resolutionList.size()]);

        final ResolveConflictsCommand resolver = new ResolveConflictsCommand(repository, resolutions);
        UICommandExecutorFactory.newUICommandExecutor(getShell()).execute(new ResourceChangingCommand(resolver));

        resolutionFinished(resolutions, resolver.getStatuses());
    }

    /**
     * Queries if an asynchronous conflict resolution is running for the given
     * ConflictDescription.
     *
     * @param description
     *        ConflictDescription to query resolution state for
     * @return true if there's an asynchronous resolution for the given
     *         ConflictDescription
     */
    public boolean isResolving(final ConflictDescription description) {
        synchronized (runningResolutionList) {
            for (final Iterator<ConflictResolution> i = runningResolutionList.iterator(); i.hasNext();) {
                final ConflictResolution resolution = i.next();

                if (resolution.getConflictDescription().equals(description)) {
                    return true;
                }
            }
        }

        return false;
    }

    /**
     * Resolves the selected conflict(s).
     */
    private void resolveSelection() {
        final ConflictDescription[] descriptions = conflictTable.getSelectedElements();

        if (descriptions.length == 1) {
            resolveConflict(descriptions[0]);
        } else if (descriptions.length > 1) {
            resolveConflicts(descriptions);
        }
    }

    /**
     * Raise the dialog prompting the user for conflict resolution, and then
     * attempt to resolve the conflict with that given resolution.
     *
     * @param description
     *        ConflictDescription to resolve
     */
    private void resolveConflict(final ConflictDescription description) {
        final ConflictResolutionDialog resolveDialog = new ConflictResolutionDialog(getShell(), description);

        /*
         * TODO: don't clear this analysis here, at least for the plugin. This
         * will only get stale in the ConflictView, so let it hook up a listener
         * to the filemodificationvalidator, and let it dump conflict analyses
         * when they become stale. (but do leave this here for explorer.)
         */

        /*
         * clear any analysis that's associated with this description, it may be
         * stale
         */
        description.clearAnalysis();

        if (resolveDialog.open() != IDialogConstants.OK_ID) {
            return;
        }

        final ConflictResolution resolution = resolveDialog.getResolution();

        // add this as a status listener so that we can handle async resolutions
        // (eg, ExternalConflictResolution)
        resolution.addStatusListener(this);

        // add this to the running resolution list *now*, before resolution
        // begins, so that we don't deadlock the ui thread with other callbacks
        synchronized (runningResolutionList) {
            runningResolutionList.add(resolution);
        }

        /* Run us in a workspace command. */
        if (resolution instanceof ExternalConflictResolution) {
            ((ExternalConflictResolution) resolution)
                    .setConflictResolver(new ResourceChangingConflictResolver(getShell()));
        }

        // resolve the conflict
        final ResolveConflictsCommand resolver = new ResolveConflictsCommand(repository, resolution);

        final ICommandExecutor commandExecutor;

        /*
         * TODO: this is quick and dirty for SP1, replace with a more elegant
         * solution.
         *
         * When running a command executor with a delay, the internal merge tool
         * will typically pop up parented on the conflict resolution dialog,
         * then the command executor dialog can pop up the "Resolving..."
         * progress dialog parented on the same shell after the delay. This can
         * lead to UI shell parenting deadlocks. Thus, using a command executor
         * with no delay will guarantee that the merge dialog parents itself off
         * the best parent (the progress dialog.)
         */
        if (resolution instanceof EclipseMergeConflictResolution) {
            commandExecutor = UICommandExecutorFactory.newUICommandExecutor(getShell(), 0);
        } else {
            commandExecutor = UICommandExecutorFactory.newUICommandExecutor(getShell());
        }

        commandExecutor.execute(new ResourceChangingCommand(resolver));

        // the status listener will handle notification, etc
    }

    /**
     * Raise the dialog prompting the user for conflict resolutions, and then
     * attempt to resolve the conflicts with that given resolution(s).
     *
     * @param descriptions
     *        ConflictDescriptions to resolve
     */
    private void resolveConflicts(final ConflictDescription[] descriptions) {
        final List<ConflictResolution> resolutionList = new ArrayList<ConflictResolution>();

        final MultipleConflictResolutionDialog resolveDialog = new MultipleConflictResolutionDialog(getShell(),
                descriptions);

        if (resolveDialog.open() != IDialogConstants.OK_ID) {
            return;
        }

        for (int i = 0; i < descriptions.length; i++) {
            final ConflictResolution resolution = resolveDialog.getResolution(descriptions[i]);

            if (resolution != null) {
                resolutionList.add(resolution);
            }
        }

        final ConflictResolution[] resolutions = resolutionList
                .toArray(new ConflictResolution[resolutionList.size()]);

        final ResolveConflictsCommand resolver = new ResolveConflictsCommand(repository, resolutions);
        UICommandExecutorFactory.newUICommandExecutor(getShell()).execute(new ResourceChangingCommand(resolver));

        resolutionFinished(resolutions, resolver.getStatuses());
    }

    /*
     * We'll get a ConflictResolutionStatus on resolution status changes. This
     * will allow us to do any completion or notification on conflicts which
     * were being resolved asynchronously.
     *
     * (non-Javadoc)
     *
     * @seecom.microsoft.tfs.core.vc.conflicts.resolutions.
     * ConflictResolutionStatusListener #
     * statusChanged(com.microsoft.tfs.core.vc
     * .conflicts.resolutions.ConflictResolution ,
     * com.microsoft.tfs.core.vc.conflicts.resolutions.ConflictResolutionStatus)
     */
    @Override
    public void statusChanged(final ConflictResolution resolution, final ConflictResolutionStatus newStatus) {
        // we only care about finished statuses for resolutions that were
        // running asynchronously
        synchronized (runningResolutionList) {
            if (!runningResolutionList.contains(resolution) || newStatus == ConflictResolutionStatus.NOT_STARTED
                    || newStatus == ConflictResolutionStatus.RUNNING) {
                return;
            }

            runningResolutionList.remove(resolution);
        }

        UIHelpers.asyncExec(new Runnable() {
            @Override
            public void run() {
                resolutionFinished(resolution, newStatus);
                refreshLabels();
            }
        });
    }

    /**
     * Complete any resolution for a single conflict. (Raise errors, remove from
     * the list(s), etc.)
     *
     * TODO: this needs to fire to the conflict manager for the plugin.
     *
     * @param resolution
     *        Conflict Resolution that finished
     * @param status
     *        The ConflictResolutionStatus that resolution completed with
     */
    private void resolutionFinished(final ConflictResolution resolution, final ConflictResolutionStatus status) {
        if (status == ConflictResolutionStatus.SUCCESS
                || status == ConflictResolutionStatus.SUCCEEDED_WITH_CONFLICTS) {
            resolveCount++;
        }

        ConflictHelpers.showConflictError(getShell(), resolution, status);

        refresh();
    }

    /**
     * Complete any resolution for multiple conflicts. (Raise errors, remove
     * from the list(s), etc.)
     *
     * TODO: this needs to fire to the conflict manager for the plugin.
     *
     * @param resolutions
     *        Conflict Resolutions that finished
     * @param statuses
     *        The ConflictResolutionStatus for each resolution
     */
    private void resolutionFinished(final ConflictResolution[] resolutions,
            final ConflictResolutionStatus[] statuses) {

        for (int i = 0; i < statuses.length; i++) {
            if (statuses[i] == ConflictResolutionStatus.SUCCESS
                    || statuses[i] == ConflictResolutionStatus.SUCCEEDED_WITH_CONFLICTS) {
                resolveCount++;
            }
        }

        ConflictHelpers.showConflictErrors(getShell(), resolutions, statuses);

        refresh();
    }

    public int getUnresolvedCount() {
        return descriptionList.size();
    }

    public int getResolvedCount() {
        return resolveCount;
    }

    private static class ResourceChangingConflictResolver extends ExternalConflictResolver {
        private final Shell parentShell;

        public ResourceChangingConflictResolver(final Shell parentShell) {
            Check.notNull(parentShell, "parentShell"); //$NON-NLS-1$

            this.parentShell = parentShell;
        }

        @Override
        public boolean resolveConflict(final Workspace workspace, final Conflict conflict) {
            final ConflictResolutionCommand resolveCommand = new ConflictResolutionCommand(workspace, conflict);
            final IStatus resolveStatus = UICommandExecutorFactory.newUICommandExecutor(parentShell)
                    .execute(new ResourceChangingCommand(resolveCommand));

            return (resolveStatus.isOK());
        }
    }

    private static class ConflictResolutionCommand extends TFSCommand {
        private final Workspace workspace;
        private final Conflict conflict;

        public ConflictResolutionCommand(final Workspace workspace, final Conflict conflict) {
            this.workspace = workspace;
            this.conflict = conflict;
        }

        @Override
        public String getName() {
            return Messages.getString("ConflictDialog.ResolveCommandText"); //$NON-NLS-1$
        }

        @Override
        public String getErrorDescription() {
            return Messages.getString("ConflictDialog.ResolveCommandErrorText"); //$NON-NLS-1$
        }

        @Override
        public String getLoggingDescription() {
            return Messages.getString("ConflictDialog.ResolveCommandText", LocaleUtil.ROOT); //$NON-NLS-1$
        }

        @Override
        protected IStatus doRun(final IProgressMonitor progressMonitor) throws Exception {
            workspace.resolveConflict(conflict);

            return Status.OK_STATUS;
        }
    }
}