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

Java tutorial

Introduction

Here is the source code for com.microsoft.tfs.client.common.ui.dialogs.vc.EditLabelDialog.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.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;

import com.microsoft.tfs.client.common.commands.vc.QueryItemsCommand;
import com.microsoft.tfs.client.common.framework.command.CommandExecutor;
import com.microsoft.tfs.client.common.repository.TFSRepository;
import com.microsoft.tfs.client.common.ui.Messages;
import com.microsoft.tfs.client.common.ui.TFSCommonUIClientPlugin;
import com.microsoft.tfs.client.common.ui.controls.generic.ProgressMonitorControl;
import com.microsoft.tfs.client.common.ui.controls.vc.LabelItemTable;
import com.microsoft.tfs.client.common.ui.controls.vc.LabelItemTable.LabelItem;
import com.microsoft.tfs.client.common.ui.controls.vc.LabelItemTable.LabelItemStatus;
import com.microsoft.tfs.client.common.ui.controls.vc.VersionPickerControl.VersionDescription;
import com.microsoft.tfs.client.common.ui.framework.dialog.ExtendedButtonDialog;
import com.microsoft.tfs.client.common.ui.framework.layout.GridDataBuilder;
import com.microsoft.tfs.client.common.ui.framework.sizing.ControlSize;
import com.microsoft.tfs.client.common.ui.vc.serveritem.ServerItemType;
import com.microsoft.tfs.client.common.ui.vc.serveritem.VersionedItemSource;
import com.microsoft.tfs.client.common.util.DateHelper;
import com.microsoft.tfs.client.common.vc.VersionSpecHelper;
import com.microsoft.tfs.core.clients.versioncontrol.GetItemsOptions;
import com.microsoft.tfs.core.clients.versioncontrol.VersionControlConstants;
import com.microsoft.tfs.core.clients.versioncontrol.path.ServerPath;
import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.DeletedState;
import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.Item;
import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.ItemSet;
import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.ItemType;
import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.RecursionType;
import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.VersionControlLabel;
import com.microsoft.tfs.core.clients.versioncontrol.specs.ItemSpec;
import com.microsoft.tfs.core.clients.versioncontrol.specs.LabelItemSpec;
import com.microsoft.tfs.core.clients.versioncontrol.specs.version.ChangesetVersionSpec;
import com.microsoft.tfs.core.clients.versioncontrol.specs.version.DateVersionSpec;
import com.microsoft.tfs.core.clients.versioncontrol.specs.version.LatestVersionSpec;
import com.microsoft.tfs.core.clients.versioncontrol.specs.version.VersionSpec;
import com.microsoft.tfs.core.exceptions.TECoreException;
import com.microsoft.tfs.util.Check;
import com.microsoft.tfs.util.CollatorFactory;

public class EditLabelDialog extends ExtendedButtonDialog {
    private static final Log log = LogFactory.getLog(EditLabelDialog.class);

    private final TFSRepository repository;
    private final VersionControlLabel initialLabel;
    private String initialName;
    private String initialComment;
    private final String initialServerPath;
    private final RecursionType initialRecursionType;
    private final VersionSpec initialVersionSpec;

    private final LabelContentsTree labelContentsTree = new LabelContentsTree();

    private Text nameText;
    private Text commentText;
    private LabelItemTable labelItemsTable;
    private ProgressMonitorControl progressMonitorControl;

    private boolean ignoreCommentModifyEvents = false;

    private boolean labelModified = false;
    private EditLabelResults editLabelResults;

    private final static int BUTTON_ADD_ID = IDialogConstants.CLIENT_ID + 1;
    private final static int BUTTON_REMOVE_ID = IDialogConstants.CLIENT_ID + 2;

    /**
     * Constructs an edit label dialog given an existing
     * {@link VersionControlLabel}.
     *
     * @param parentShell
     *        the shell to parent from (not null)
     * @param repository
     *        the repository to edit the label in (not null)
     * @param initialLabel
     *        the version control label to edit (not null)
     */
    public EditLabelDialog(final Shell parentShell, final TFSRepository repository,
            final VersionControlLabel initialLabel) {
        this(parentShell, repository, initialLabel, null, null, null);

        Check.notNull(repository, "repository"); //$NON-NLS-1$
        Check.notNull(initialLabel, "initialLabel"); //$NON-NLS-1$
    }

    /**
     * Constructs an edit label dialog with no existing label entries, suitable
     * for creating a new label. The given initialServerPath will be used as the
     * default server path for browsing new label items.
     *
     * @param parentShell
     *        the shell to parent from (not null)
     * @param repository
     *        the repository to edit the label in (not null)
     * @param initialServerPath
     *        the initial server path to use when browsing new label items (not
     *        null)
     */
    public EditLabelDialog(final Shell parentShell, final TFSRepository repository,
            final String initialServerPath) {
        this(parentShell, repository, null, initialServerPath, null, null);

        Check.notNull(repository, "repository"); //$NON-NLS-1$
        Check.notNull(initialServerPath, "initialServerPath"); //$NON-NLS-1$
    }

    /**
     * Constructs an edit label dialog and populates label items from a query on
     * the given server path at the given version spec. Suitable for creating
     * new labels after prompting for a first label entry. The query will occur
     * when the dialog is opened.
     *
     * @param parentShell
     *        the shell to parent from (not null)
     * @param repository
     *        the repository to edit the label in (not null)
     * @param initialServerPath
     *        the initial server path used to populate the label (not null)
     * @param recursionType
     *        the recursion type to query the given server path on (not null)
     * @param versionSpec
     *        the version spec to add the given server item with (not null)
     */
    public EditLabelDialog(final Shell parentShell, final TFSRepository repository, final String initialServerPath,
            final RecursionType recursionType, final VersionSpec versionSpec) {
        this(parentShell, repository, null, initialServerPath, recursionType, versionSpec);

        Check.notNull(repository, "repository"); //$NON-NLS-1$
        Check.notNull(initialServerPath, "initialServerPath"); //$NON-NLS-1$
        Check.notNull(recursionType, "recursionType"); //$NON-NLS-1$
        Check.notNull(versionSpec, "versionSpec"); //$NON-NLS-1$
    }

    private EditLabelDialog(final Shell parentShell, final TFSRepository repository,
            final VersionControlLabel initialLabel, final String initialServerPath,
            final RecursionType recursionType, final VersionSpec versionSpec) {
        super(parentShell);

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

        this.repository = repository;
        this.initialLabel = initialLabel;
        this.initialServerPath = initialServerPath;
        initialRecursionType = recursionType;
        initialVersionSpec = versionSpec;

        addExtendedButtonDescription(BUTTON_ADD_ID, Messages.getString("EditLabelDialog.AddButtonText"), false); //$NON-NLS-1$
        addExtendedButtonDescription(BUTTON_REMOVE_ID, Messages.getString("EditLabelDialog.RemoveButtonText"), //$NON-NLS-1$
                false);

        setOptionPersistGeometry(false);
    }

    @Override
    protected String provideDialogTitle() {
        if (initialLabel == null) {
            return Messages.getString("EditLabelDialog.NewLabelDialogTitle"); //$NON-NLS-1$
        } else {
            String scope = ServerPath.getTeamProjectName(initialLabel.getScope());

            if (scope == null) {
                scope = initialLabel.getScope();
            }

            final String messageFormat = Messages.getString("EditLabelDialog.DialogTitleFormat"); //$NON-NLS-1$
            return MessageFormat.format(messageFormat, scope);
        }
    }

    public void setName(final String initialName) {
        this.initialName = initialName;
    }

    public void setComment(final String initialComment) {
        this.initialComment = initialComment;
    }

    @Override
    protected void hookAddToDialogArea(final Composite dialogArea) {
        super.hookAddToDialogArea(dialogArea);

        final GridLayout layout = new GridLayout(2, false);
        layout.marginWidth = getHorizontalMargin();
        layout.marginHeight = getVerticalMargin();
        layout.horizontalSpacing = getHorizontalSpacing();
        layout.verticalSpacing = getVerticalSpacing();
        dialogArea.setLayout(layout);

        final Label nameLabel = new Label(dialogArea, SWT.NONE);
        nameLabel.setText(Messages.getString("EditLabelDialog.NameLabelText")); //$NON-NLS-1$

        nameText = new Text(dialogArea, SWT.BORDER);
        GridDataBuilder.newInstance().hGrab().hFill().applyTo(nameText);
        nameText.setTextLimit(ApplyLabelDialog.NAME_MAX_LENGTH);

        final Label commentLabel = new Label(dialogArea, SWT.NONE);
        commentLabel.setText(Messages.getString("EditLabelDialog.CommentLabelText")); //$NON-NLS-1$
        GridDataBuilder.newInstance().hSpan(2).vIndent(getVerticalSpacing()).applyTo(commentLabel);

        commentText = new Text(dialogArea, SWT.MULTI | SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL);
        GridDataBuilder.newInstance().hSpan(2).hGrab().hFill().applyTo(commentText);
        ControlSize.setCharHeightHint(commentText, 5);
        ControlSize.setCharWidthHint(commentText, 60);
        commentText.addModifyListener(new ModifyListener() {
            @Override
            public void modifyText(final ModifyEvent e) {
                if (ignoreCommentModifyEvents == false) {
                    labelModified = true;
                }
            }
        });
        commentText.setTextLimit(ApplyLabelDialog.COMMENT_MAX_LENGTH);

        final Label itemsLabel = new Label(dialogArea, SWT.NONE);
        itemsLabel.setText(Messages.getString("EditLabelDialog.ItemsLabelText")); //$NON-NLS-1$
        GridDataBuilder.newInstance().hSpan(2).applyTo(itemsLabel);

        labelItemsTable = new LabelItemTable(dialogArea, SWT.MULTI | SWT.FULL_SELECTION);
        GridDataBuilder.newInstance().hSpan(2).hGrab().hFill().vGrab().vFill().hCHint(labelItemsTable, 10)
                .wHint(getMinimumMessageAreaWidth()).applyTo(labelItemsTable);

        progressMonitorControl = new ProgressMonitorControl(dialogArea, SWT.NONE);
        GridDataBuilder.newInstance().hSpan(2).hGrab().hFill().vIndent(getVerticalSpacing())
                .applyTo(progressMonitorControl);

        if (initialLabel != null) {
            nameText.setText(initialLabel.getName());
            nameText.setEditable(false);
            nameText.setEnabled(false);

            ignoreCommentModifyEvents = true;
            commentText.setText(initialLabel.getComment());
            ignoreCommentModifyEvents = false;
        }

        /*
         * When we come through the ApplyLabelDialog, it checks to ensure that
         * the label does not already exist. Thus the name is not editable.
         */
        if (initialName != null) {
            nameText.setText(initialName);
            nameText.setEditable(false);
        }

        if (initialComment != null) {
            commentText.setText(initialComment);
        }

        nameText.addModifyListener(new ModifyListener() {
            @Override
            public void modifyText(final ModifyEvent e) {
                getButton(IDialogConstants.OK_ID).setEnabled(nameText.getText().length() > 0);
            }
        });

        labelItemsTable.addSelectionChangedListener(new ISelectionChangedListener() {
            @Override
            public void selectionChanged(final SelectionChangedEvent event) {
                getButton(BUTTON_REMOVE_ID).setEnabled(!event.getSelection().isEmpty());
            }
        });
    }

    @Override
    protected void hookCustomButtonPressed(final int buttonId) {
        if (buttonId == BUTTON_ADD_ID) {
            addPressed();
        } else if (buttonId == BUTTON_REMOVE_ID) {
            removePressed();
        }
    }

    @Override
    protected void hookDialogIsOpen() {
        if (!(initialLabel != null
                || (initialServerPath != null && initialRecursionType != null && initialVersionSpec != null))) {
            getButton(IDialogConstants.OK_ID).setEnabled(nameText.getText().length() > 0);
            return;
        }

        getButton(BUTTON_ADD_ID).setEnabled(false);
        getButton(BUTTON_REMOVE_ID).setEnabled(false);
        nameText.setEnabled(false);
        commentText.setEnabled(false);
        labelItemsTable.setEnabled(false);
        getButton(IDialogConstants.OK_ID).setEnabled(false);

        Runnable runnable;

        if (initialLabel != null) {
            runnable = new Runnable() {
                @Override
                public void run() {
                    addFromLabel(initialLabel);
                }
            };
        } else if (initialServerPath != null && initialRecursionType != null && initialVersionSpec != null) {
            labelModified = true;

            runnable = new Runnable() {
                @Override
                public void run() {
                    addFromQuery(initialServerPath, initialRecursionType, initialVersionSpec, new Runnable() {
                        @Override
                        public void run() {
                            if (initialLabel == null && nameText.getText().length() == 0) {
                                nameText.setFocus();
                            }
                        }
                    });
                }
            };
        } else {
            return;
        }

        new Thread(runnable).start();
    }

    private void addPressed() {
        final ServerItemVersionDialog chooseVersionDialog = new ServerItemVersionDialog(getShell(), repository,
                Messages.getString("EditLabelDialog.SelectItemToLabel"), //$NON-NLS-1$
                initialServerPath, new VersionedItemSource(repository, LatestVersionSpec.INSTANCE),
                ServerItemType.ALL, LatestVersionSpec.INSTANCE);

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

        labelModified = true;

        final ItemSpec addedItemSpec = chooseVersionDialog.getItemSpec();
        final VersionSpec addedVersionSpec = chooseVersionDialog.getVersionSpec();

        if (addedVersionSpec == null || (addedVersionSpec instanceof ChangesetVersionSpec
                && ((ChangesetVersionSpec) addedVersionSpec).getChangeset() == 0)) {
            String errorMessage = Messages.getString("EditLabelDialog.InvalidVersionDialogText"); //$NON-NLS-1$

            if (chooseVersionDialog.getVersionType() == VersionDescription.DATE) {
                errorMessage = Messages.getString("EditLabelDialog.InvalidDateDialogText"); //$NON-NLS-1$
            } else if (chooseVersionDialog.getVersionType() == VersionDescription.CHANGESET) {
                errorMessage = Messages.getString("EditLabelDialog.InvalidChangesetDialogText"); //$NON-NLS-1$
            }

            MessageDialog.openError(getShell(), Messages.getString("EditLabelDialog.InvalidVersionDialogTitle"), //$NON-NLS-1$
                    errorMessage);
            return;
        }

        getButton(BUTTON_ADD_ID).setEnabled(false);
        getButton(BUTTON_REMOVE_ID).setEnabled(false);
        nameText.setEnabled(false);
        commentText.setEnabled(false);
        labelItemsTable.setEnabled(false);
        getButton(IDialogConstants.OK_ID).setEnabled(false);

        new Thread() {
            @Override
            public void run() {
                addFromQuery(addedItemSpec.getItem(), addedItemSpec.getRecursionType(), addedVersionSpec, null);
            }
        }.start();
    }

    private void addFromLabel(final VersionControlLabel label) {
        final List<LabelItem> labelItemList = new ArrayList<LabelItem>();

        final IProgressMonitor progressMonitor = progressMonitorControl.getProgressMonitor();

        final Shell shell = getShell();

        if (shell == null) {
            return;
        }

        final Display display = shell.getDisplay();

        display.syncExec(new Runnable() {
            @Override
            public void run() {
                progressMonitor.beginTask(Messages.getString("EditLabelDialog.ProgressBuildLabeling"), //$NON-NLS-1$
                        IProgressMonitor.UNKNOWN);
            }
        });

        try {
            synchronized (labelContentsTree) {
                final Item[] items = label.getItems();

                for (int i = 0; i < items.length; i++) {
                    final LabelItem labelItem = new LabelItem(items[i].getServerItem(), RecursionType.NONE,
                            new ChangesetVersionSpec(items[i].getChangeSetID()), items[i].getDeletionID(),
                            LabelItemStatus.EXISTS);

                    labelContentsTree.put(labelItem);
                    labelItemList.add(labelItem);
                }
            }

            final LabelItem[] items = labelItemList.toArray(new LabelItem[labelItemList.size()]);

            display.syncExec(new Runnable() {
                @Override
                public void run() {
                    if (labelItemsTable != null && !labelItemsTable.isDisposed()) {
                        labelItemsTable.setItems(items);
                    }
                }
            });

        } finally {
            display.syncExec(new Runnable() {
                @Override
                public void run() {
                    progressMonitor.done();
                }
            });
        }

        addFinished(Status.OK_STATUS, null);
    }

    private void addFromQuery(final String serverPath, final RecursionType recursionType,
            final VersionSpec versionSpec, final Runnable finishedHandler) {
        final ItemSpec[] queryItemSpecs = new ItemSpec[] { new ItemSpec(serverPath, recursionType) };

        final QueryItemsCommand queryCommand = new QueryItemsCommand(repository, queryItemSpecs, versionSpec,
                DeletedState.ANY, ItemType.ANY, GetItemsOptions.UNSORTED);

        final IProgressMonitor progressMonitor = progressMonitorControl.getProgressMonitor();

        final Shell shell = getShell();

        if (shell == null) {
            return;
        }

        final Display display = shell.getDisplay();

        display.syncExec(new Runnable() {
            @Override
            public void run() {
                final String messageFormat = Messages.getString("EditLabelDialog.AddingLabelProgressFormat"); //$NON-NLS-1$
                final String message = MessageFormat.format(messageFormat, serverPath);

                progressMonitor.beginTask(message, IProgressMonitor.UNKNOWN);
                progressMonitor.subTask(Messages.getString("EditLabelDialog.ProgressSubtask")); //$NON-NLS-1$
            }
        });

        try {
            IStatus queryStatus = new CommandExecutor().execute(queryCommand);

            if (!queryStatus.isOK()) {
                if (queryStatus.getException() != null && queryStatus.getException() instanceof TECoreException
                        && queryStatus.getException().getMessage().startsWith("TF14021: ")) //$NON-NLS-1$
                {
                    final Date date = ((DateVersionSpec) versionSpec).getDate().getTime();
                    final String messageFormat = Messages.getString("EditLabelDialog.InvalidDateFormat"); //$NON-NLS-1$
                    final String message = MessageFormat.format(messageFormat,
                            DateHelper.getDefaultDateTimeFormat().format(date));

                    queryStatus = new Status(IStatus.ERROR, TFSCommonUIClientPlugin.PLUGIN_ID, 0, message, null);
                }

                addFinished(queryStatus, finishedHandler);
                return;
            }

            final ItemSet[] itemSet = queryCommand.getItemSets();

            if (itemSet == null || itemSet.length != 1 || itemSet[0].getItems().length < 1) {
                final String messageFormat = Messages.getString("EditLabelDialog.ServerDoesNotContainItemFormat"); //$NON-NLS-1$
                final String message = MessageFormat.format(messageFormat, serverPath,
                        VersionSpecHelper.getVersionSpecDescription(versionSpec));

                addFinished(new Status(IStatus.ERROR, TFSCommonUIClientPlugin.PLUGIN_ID, 0, message, null),
                        finishedHandler);

                return;
            }

            display.syncExec(new Runnable() {
                @Override
                public void run() {
                    progressMonitor.subTask(Messages.getString("EditLabelDialog.ProgressSubtaskLabing")); //$NON-NLS-1$
                }
            });

            final LabelItem[] labelItems;

            synchronized (labelContentsTree) {
                final Item[] items = itemSet[0].getItems();

                for (int i = 0; i < items.length; i++) {
                    final String itemServerItem = items[i].getServerItem();

                    /*
                     * The first element return from query items is the item we
                     * queried for -- which we are actually adding to the label.
                     * Any additional items are children, and are "implicit"
                     * adds, merely drawn in the table control but not sent to
                     * the server.
                     */
                    final LabelItemStatus itemStatus = (i == 0) ? LabelItemStatus.ADD
                            : LabelItemStatus.ADD_IMPLICIT;
                    final RecursionType itemRecursionType = (items[i].getItemType() == ItemType.FOLDER)
                            ? RecursionType.FULL
                            : RecursionType.NONE;
                    final LabelItem labelItem = new LabelItem(itemServerItem, itemRecursionType, versionSpec,
                            items[i].getDeletionID(), itemStatus);

                    labelContentsTree.put(labelItem);
                }

                labelItems = labelContentsTree.getLabelItems();
            }

            display.syncExec(new Runnable() {
                @Override
                public void run() {
                    if (labelItemsTable != null && !labelItemsTable.isDisposed()) {
                        labelItemsTable.setItems(labelItems);
                    }
                }
            });
        } finally {
            display.syncExec(new Runnable() {
                @Override
                public void run() {
                    progressMonitor.done();
                }
            });
        }

        addFinished(Status.OK_STATUS, finishedHandler);

        return;
    }

    private void addFinished(final IStatus status, final Runnable finishedHandler) {
        final Shell shell = getShell();

        if (shell == null) {
            return;
        }

        shell.getDisplay().syncExec(new Runnable() {
            @Override
            public void run() {
                if (!status.isOK()) {
                    ErrorDialog.openError(shell, Messages.getString("EditLabelDialog.CantQueryDialogTitle"), //$NON-NLS-1$
                            null, status);
                }

                if (nameText.isDisposed()) {
                    return;
                }

                nameText.setEnabled(initialLabel == null);
                commentText.setEnabled(true);
                labelItemsTable.setEnabled(true);
                getButton(BUTTON_ADD_ID).setEnabled(true);
                getButton(BUTTON_REMOVE_ID).setEnabled(!labelItemsTable.getSelection().isEmpty());
                getButton(IDialogConstants.OK_ID).setEnabled(nameText.getText().length() > 0);

                if (finishedHandler != null) {
                    finishedHandler.run();
                }
            }
        });
    }

    private void removePressed() {
        final LabelItem[] selectedItems = labelItemsTable.getSelectedItems();

        if (selectedItems.length == 0) {
            return;
        }

        labelModified = true;

        final LabelContentsTreeTransaction removeTransaction = new LabelContentsTreeTransaction();

        synchronized (labelContentsTree) {
            for (int i = 0; i < selectedItems.length; i++) {
                labelContentsTree.remove(removeTransaction, selectedItems[i].getServerItem());
            }
        }

        final IStatus removeStatus = removeTransaction.getStatus();

        if (!removeStatus.isOK()) {
            ErrorDialog.openError(getShell(), Messages.getString("EditLabelDialog.CantRemoveDialogTitle"), //$NON-NLS-1$
                    null, removeStatus);
        }

        final Shell shell = getShell();

        BusyIndicator.showWhile(shell.getDisplay(), new Runnable() {
            @Override
            public void run() {
                shell.getDisplay().syncExec(new Runnable() {
                    @Override
                    public void run() {
                        labelItemsTable.removeItems(removeTransaction.getAffectedLabelItems());
                    }
                });
            }
        });
    }

    @Override
    protected void okPressed() {
        if (!labelModified) {
            editLabelResults = new EditLabelResults(null, null, null);
            setReturnCode(IDialogConstants.CANCEL_ID);
            close();
            return;
        }

        if (initialLabel == null) {
            final String labelName = nameText.getText().trim();

            if (labelName.length() == 0) {
                final String title = Messages.getString("EditLabelDialog.InvalidNameDialogTitle"); //$NON-NLS-1$
                final String message = Messages.getString("EditLabelDialog.InvalidNameDialogText"); //$NON-NLS-1$

                MessageDialog.openError(getShell(), title, message);
                return;
            }

            if (labelName.length() >= ApplyLabelDialog.NAME_MAX_LENGTH) {
                final String title = Messages.getString("EditLabelDialog.NametooLongDialogTitle"); //$NON-NLS-1$
                final String messageFormat = Messages.getString("EditLabelDialog.NameTooLongFormat"); //$NON-NLS-1$
                final String message = MessageFormat.format(messageFormat, (ApplyLabelDialog.NAME_MAX_LENGTH + 1));

                MessageDialog.openError(getShell(), title, message);
                return;
            }

            for (int i = 0; i < ApplyLabelDialog.INVALID_NAME_CHARS.length; i++) {
                if (labelName.indexOf(ApplyLabelDialog.INVALID_NAME_CHARS[i]) >= 0) {
                    final String title = Messages.getString("EditLabelDialog.InvalidNameDialogTitle"); //$NON-NLS-1$
                    final String messageFormat = Messages.getString("EditLabelDialog.InvalidCharInLabelFormat"); //$NON-NLS-1$
                    final String message = MessageFormat.format(messageFormat,
                            ApplyLabelDialog.INVALID_NAME_CHARS[i]);

                    MessageDialog.openError(getShell(), title, message);
                    return;
                }
            }
        }

        if (commentText.getText().trim().length() > ApplyLabelDialog.COMMENT_MAX_LENGTH) {
            final String title = Messages.getString("EditLabelDialog.CommentTooLongDialogTitle"); //$NON-NLS-1$
            final String messageFormat = Messages.getString("EditLabelDialog.CommentTooLongFormat"); //$NON-NLS-1$
            final String message = MessageFormat.format(messageFormat, ApplyLabelDialog.COMMENT_MAX_LENGTH);

            MessageDialog.openError(getShell(), title, message);
            return;
        }

        getButton(BUTTON_ADD_ID).setEnabled(false);
        getButton(BUTTON_REMOVE_ID).setEnabled(false);
        nameText.setEnabled(false);
        commentText.setEnabled(false);
        labelItemsTable.setEnabled(false);
        getButton(IDialogConstants.OK_ID).setEnabled(false);

        final IProgressMonitor progressMonitor = progressMonitorControl.getProgressMonitor();
        progressMonitor.beginTask(Messages.getString("EditLabelDialog.ProgressAnalyzingLabel"), //$NON-NLS-1$
                IProgressMonitor.UNKNOWN);

        new Thread(new Runnable() {
            @Override
            public void run() {
                final boolean isDelete;
                final ItemSpec[] deletes;
                final LabelItemSpec[] adds;

                synchronized (labelContentsTree) {
                    final LabelContentsTreeDelta delta = labelContentsTree.getDelta();

                    isDelete = (initialLabel != null && delta.getExistsCount() == 0 && delta.getAdds().length == 0);
                    deletes = delta.getDeletes();
                    adds = delta.getAdds();
                }

                final Shell shell = getShell();

                if (shell == null) {
                    return;
                }

                shell.getDisplay().syncExec(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            if (isDelete && !MessageDialog.openQuestion(getShell(),
                                    Messages.getString("EditLabelDialog.RemoveLabelDialogTitle"), //$NON-NLS-1$
                                    Messages.getString("EditLabelDialog.RemoveLabelDialogText"))) //$NON-NLS-1$
                            {
                                progressMonitor.done();

                                nameText.setEnabled(initialLabel == null);
                                commentText.setEnabled(true);
                                labelItemsTable.setEnabled(true);
                                getButton(BUTTON_ADD_ID).setEnabled(true);
                                getButton(BUTTON_REMOVE_ID).setEnabled(!labelItemsTable.getSelection().isEmpty());
                                getButton(IDialogConstants.OK_ID).setEnabled(nameText.getText().length() > 0);

                                return;
                            }

                            VersionControlLabel newLabel;

                            if (isDelete) {
                                newLabel = null;
                            } else {
                                final String name = initialLabel != null ? initialLabel.getName()
                                        : nameText.getText().trim();
                                final String owner = initialLabel != null ? initialLabel.getOwner()
                                        : VersionControlConstants.AUTHENTICATED_USER;
                                final String ownerDisplayName = initialLabel != null
                                        ? initialLabel.getOwnerDisplayName()
                                        : null;
                                final String scope = initialLabel != null ? initialLabel.getScope() : null;
                                final String comment = commentText.getText().trim();

                                newLabel = new VersionControlLabel(name, owner, ownerDisplayName, scope, comment);
                            }

                            editLabelResults = new EditLabelResults(newLabel, deletes, adds);

                            EditLabelDialog.super.okPressed();
                        } catch (final Throwable e) {
                            progressMonitor.done();

                            MessageDialog.openError(getShell(),
                                    Messages.getString("EditLabelDialog.LabelErrorDialogTitle"), //$NON-NLS-1$
                                    e.getMessage());

                            nameText.setEnabled(initialLabel == null);
                            commentText.setEnabled(true);
                            labelItemsTable.setEnabled(true);
                            getButton(BUTTON_ADD_ID).setEnabled(true);
                            getButton(BUTTON_REMOVE_ID).setEnabled(!labelItemsTable.getSelection().isEmpty());
                            getButton(IDialogConstants.OK_ID).setEnabled(nameText.getText().length() > 0);

                            return;
                        }
                    }
                });
            }
        }).start();
    }

    public EditLabelResults getEditLabelResults() {
        return editLabelResults;
    }

    public static class EditLabelResults {
        private final VersionControlLabel label;
        private final ItemSpec[] deletes;
        private final LabelItemSpec[] adds;

        public EditLabelResults(final VersionControlLabel label, final ItemSpec[] deletes,
                final LabelItemSpec[] adds) {
            this.label = label;
            this.deletes = deletes;
            this.adds = adds;
        }

        public VersionControlLabel getLabel() {
            return label;
        }

        public ItemSpec[] getDeletes() {
            return deletes;
        }

        public LabelItemSpec[] getAdds() {
            return adds;
        }
    }

    private static class LabelContentsTree {
        /**
         * Case-insensitive server item string maps to label contents tree node.
         */
        private final Map<String, LabelContentsTreeNode> labelItemMap = new TreeMap<String, LabelContentsTreeNode>(
                CollatorFactory.getCaseInsensitiveCollator());

        public LabelContentsTree() {
        }

        public void put(final LabelItem labelItem) {
            final LabelContentsTreeNode node = getOrCreateNode(labelItem.getServerItem());
            node.setLabelItem(labelItem);
        }

        private LabelContentsTreeNode getOrCreateNode(final String serverItem) {
            /* See if there's an existing entry in the tree */
            LabelContentsTreeNode node = labelItemMap.get(serverItem);

            if (node == null) {
                /* No existing node, create a placeholder. */
                node = new LabelContentsTreeNode(new LabelItem(serverItem, RecursionType.NONE,
                        new ChangesetVersionSpec(0), 0, LabelItemStatus.NONE));

                labelItemMap.put(serverItem, node);

                /* If we're not the root, find our parent */
                if (!ServerPath.equals(ServerPath.ROOT, serverItem)) {
                    String parentPath = ServerPath.getParent(serverItem);

                    if (parentPath.equals("$")) //$NON-NLS-1$
                    {
                        parentPath = ServerPath.ROOT;
                    }

                    final LabelContentsTreeNode parentNode = getOrCreateNode(parentPath);

                    parentNode.addChild(node);
                }
            }

            return node;
        }

        public LabelItem[] getLabelItems() {
            final List<LabelItem> labelItemList = new ArrayList<LabelItem>();

            for (final Iterator<LabelContentsTreeNode> i = labelItemMap.values().iterator(); i.hasNext();) {
                final LabelItem labelItem = i.next().getLabelItem();
                final LabelItemStatus status = labelItem.getItemStatus();

                if (status == LabelItemStatus.ADD || status == LabelItemStatus.ADD_IMPLICIT
                        || status == LabelItemStatus.EXISTS) {
                    labelItemList.add(labelItem);
                }
            }

            return labelItemList.toArray(new LabelItem[labelItemList.size()]);
        }

        public void remove(final LabelContentsTreeTransaction transaction, final String serverItem) {
            final LabelContentsTreeNode removeNode = labelItemMap.get(serverItem);

            if (removeNode == null) {
                String messageFormat = "The item {0} does not exist in the label tree."; //$NON-NLS-1$
                String message = MessageFormat.format(messageFormat, serverItem);
                log.error(message);

                messageFormat = Messages.getString("EditLabelDialog.ItemNotInLabelFormat"); //$NON-NLS-1$
                message = MessageFormat.format(messageFormat, serverItem);
                transaction.addError(message);
                return;
            }

            /*
             * This was previously removed as part of a recursive folder delete.
             * Ignore.
             */
            if (transaction.isAffectedNode(removeNode)) {
                return;
            }

            LabelItemStatus newStatus;

            /*
             * This file or folder was explicitly added to this label by the
             * user. We should simply not add this.
             */
            if (removeNode.getLabelItem().getItemStatus() == LabelItemStatus.ADD) {
                newStatus = LabelItemStatus.NONE;
            }
            /*
             * This file or folder was implicitly added due to recursion. We
             * should now exclude this.
             */
            else if (removeNode.getLabelItem().getItemStatus() == LabelItemStatus.ADD_IMPLICIT) {
                newStatus = LabelItemStatus.EXCLUDE;
            }
            /*
             * This file or folder exists in the existing label. We need to
             * remove this.
             */
            else if (removeNode.getLabelItem().getItemStatus() == LabelItemStatus.EXISTS) {
                newStatus = LabelItemStatus.REMOVE;
            } else {
                final String status = removeNode.getLabelItem().getItemStatus().toString();
                String messageFormat = "Could not remove item {0} from the label, it is in state {1}"; //$NON-NLS-1$
                String message = MessageFormat.format(messageFormat, serverItem, status);
                log.error(message);

                messageFormat = Messages.getString("EditLabelDialog.ItemInconsistentStateFormat"); //$NON-NLS-1$
                message = MessageFormat.format(messageFormat, serverItem);
                transaction.addError(message);
                return;
            }

            setStatusRecursive(transaction, removeNode, newStatus);
        }

        private void setStatusRecursive(final LabelContentsTreeTransaction transaction,
                final LabelContentsTreeNode node, final LabelItemStatus newStatus) {
            Check.notNull(node, "node"); //$NON-NLS-1$

            node.getLabelItem().setItemStatus(newStatus);
            transaction.addAffectedNode(node);

            /* Apply to children */
            for (final Iterator<LabelContentsTreeNode> i = node.getChildren().iterator(); i.hasNext();) {
                final LabelContentsTreeNode child = i.next();

                setStatusRecursive(transaction, child, newStatus);
            }
        }

        public LabelContentsTreeDelta getDelta() {
            final List<ItemSpec> removeList = new ArrayList<ItemSpec>();
            final List<LabelItemSpec> addList = new ArrayList<LabelItemSpec>();
            int existsCount = 0;

            for (final Iterator<LabelContentsTreeNode> i = labelItemMap.values().iterator(); i.hasNext();) {
                final LabelContentsTreeNode treeNode = i.next();
                final LabelItem labelItem = treeNode.getLabelItem();
                final LabelItemStatus labelItemStatus = labelItem.getItemStatus();

                final ItemSpec itemSpec = new ItemSpec(labelItem.getServerItem(), labelItem.getRecursionType());

                if (labelItemStatus == LabelItemStatus.ADD) {
                    addList.add(new LabelItemSpec(itemSpec, labelItem.getVersionSpec(), false));
                } else if (labelItemStatus == LabelItemStatus.EXCLUDE) {
                    addList.add(new LabelItemSpec(itemSpec, labelItem.getVersionSpec(), true));
                } else if (labelItemStatus == LabelItemStatus.REMOVE) {
                    removeList.add(itemSpec);
                } else if (labelItemStatus == LabelItemStatus.EXISTS) {
                    existsCount++;
                }

                /*
                 * Ignore placeholders, existing files in the label or files
                 * that are implicitly added.
                 */
            }

            final ItemSpec[] removes = removeList.toArray(new ItemSpec[removeList.size()]);
            final LabelItemSpec[] adds = addList.toArray(new LabelItemSpec[addList.size()]);

            return new LabelContentsTreeDelta(existsCount, removes, adds);
        }
    }

    private static class LabelContentsTreeTransaction {
        private final List<IStatus> errorList = new ArrayList<IStatus>();
        private final Map<LabelContentsTreeNode, LabelItem> affectedNodes = new HashMap<LabelContentsTreeNode, LabelItem>();

        public void addError(final String errorMessage) {
            addError(new Status(IStatus.ERROR, TFSCommonUIClientPlugin.PLUGIN_ID, 0, errorMessage, null));
        }

        public void addError(final IStatus status) {
            errorList.add(status);
        }

        public IStatus getStatus() {
            if (errorList.size() == 0) {
                return Status.OK_STATUS;
            } else if (errorList.size() == 1) {
                return errorList.get(0);
            } else {
                final IStatus[] errors = errorList.toArray(new IStatus[errorList.size()]);
                return new MultiStatus(TFSCommonUIClientPlugin.PLUGIN_ID, 0, errors,
                        Messages.getString("EditLabelDialog.LabelingErrors"), //$NON-NLS-1$
                        null);
            }
        }

        public void addAffectedNode(final LabelContentsTreeNode node) {
            affectedNodes.put(node, node.getLabelItem());
        }

        public boolean isAffectedNode(final LabelContentsTreeNode node) {
            return affectedNodes.containsKey(node);
        }

        public LabelItem[] getAffectedLabelItems() {
            return affectedNodes.values().toArray(new LabelItem[affectedNodes.values().size()]);
        }
    }

    private static class LabelContentsTreeNode {
        private final List<LabelContentsTreeNode> children = new ArrayList<LabelContentsTreeNode>();

        private LabelItem labelItem;

        public LabelContentsTreeNode(final LabelItem labelItem) {
            this.labelItem = labelItem;
        }

        public void addChild(final LabelContentsTreeNode child) {
            children.add(child);
        }

        public List<LabelContentsTreeNode> getChildren() {
            return children;
        }

        public void setLabelItem(final LabelItem labelItem) {
            this.labelItem = labelItem;
        }

        public LabelItem getLabelItem() {
            return labelItem;
        }
    }

    private static class LabelContentsTreeDelta {
        private final int existsCount;
        private final ItemSpec[] deletes;
        private final LabelItemSpec[] adds;

        public LabelContentsTreeDelta(final int existsCount, final ItemSpec[] deletes, final LabelItemSpec[] adds) {
            this.existsCount = existsCount;
            this.deletes = deletes;
            this.adds = adds;
        }

        public int getExistsCount() {
            return existsCount;
        }

        public ItemSpec[] getDeletes() {
            return deletes;
        }

        public LabelItemSpec[] getAdds() {
            return adds;
        }
    }
}