com.microsoft.tfs.client.common.ui.wit.dialogs.WorkItemPickerDialog.java Source code

Java tutorial

Introduction

Here is the source code for com.microsoft.tfs.client.common.ui.wit.dialogs.WorkItemPickerDialog.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.wit.dialogs;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;

import com.microsoft.tfs.client.common.server.TFSServer;
import com.microsoft.tfs.client.common.ui.Messages;
import com.microsoft.tfs.client.common.ui.controls.generic.compatibility.table.DoubleClickEvent;
import com.microsoft.tfs.client.common.ui.controls.generic.compatibility.table.DoubleClickListener;
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.MessageBoxHelpers;
import com.microsoft.tfs.client.common.ui.framework.helper.SWTUtil;
import com.microsoft.tfs.client.common.ui.framework.sizing.ControlSize;
import com.microsoft.tfs.client.common.ui.helpers.ComboHelper;
import com.microsoft.tfs.client.common.ui.teamexplorer.helpers.WorkItemHelpers;
import com.microsoft.tfs.client.common.ui.wit.results.QueryResultsControl;
import com.microsoft.tfs.client.common.ui.wit.results.data.QueryResultCommand;
import com.microsoft.tfs.client.common.ui.wit.results.data.QueryResultData;
import com.microsoft.tfs.core.clients.workitem.CoreFieldReferenceNames;
import com.microsoft.tfs.core.clients.workitem.WorkItem;
import com.microsoft.tfs.core.clients.workitem.WorkItemClient;
import com.microsoft.tfs.core.clients.workitem.WorkItemQueryUtils;
import com.microsoft.tfs.core.clients.workitem.form.WIFormLinksControlWITypeFilter;
import com.microsoft.tfs.core.clients.workitem.form.WIFormLinksControlWITypeFilterEnum;
import com.microsoft.tfs.core.clients.workitem.form.WIFormLinksControlWITypeFilters;
import com.microsoft.tfs.core.clients.workitem.internal.wiqlparse.Condition;
import com.microsoft.tfs.core.clients.workitem.internal.wiqlparse.Node;
import com.microsoft.tfs.core.clients.workitem.internal.wiqlparse.NodeAndOperator;
import com.microsoft.tfs.core.clients.workitem.internal.wiqlparse.NodeCondition;
import com.microsoft.tfs.core.clients.workitem.internal.wiqlparse.NodeFieldName;
import com.microsoft.tfs.core.clients.workitem.internal.wiqlparse.NodeOrOperator;
import com.microsoft.tfs.core.clients.workitem.internal.wiqlparse.NodeSelect;
import com.microsoft.tfs.core.clients.workitem.internal.wiqlparse.NodeString;
import com.microsoft.tfs.core.clients.workitem.internal.wiqlparse.Parser;
import com.microsoft.tfs.core.clients.workitem.link.WorkItemLinkUtils;
import com.microsoft.tfs.core.clients.workitem.project.Project;
import com.microsoft.tfs.core.clients.workitem.query.DisplayFieldList;
import com.microsoft.tfs.core.clients.workitem.query.Query;
import com.microsoft.tfs.core.clients.workitem.query.StoredQuery;
import com.microsoft.tfs.core.clients.workitem.queryhierarchy.QueryDefinition;
import com.microsoft.tfs.core.clients.workitem.queryhierarchy.QueryItemType;
import com.microsoft.tfs.core.clients.workitem.wittype.WorkItemType;
import com.microsoft.tfs.core.clients.workitem.wittype.WorkItemTypeCollection;
import com.microsoft.tfs.util.Check;

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

    private final TFSServer server;

    /*
     * the work item client used by this dialog
     */
    private final WorkItemClient workItemClient;

    /*
     * The initial project to select, or null if there is no initial project.
     */
    private final Project initialProject;

    /*
     * The work item type filters defined on a links control (can be null).
     */
    private final WIFormLinksControlWITypeFilters wiFilters;

    /*
     * widgets that need to be accessed after creation
     */
    private Combo projectCombo;
    private Composite savedQueryArea;
    private Text savedQueryText;
    private Button savedQueryButton;
    private Text idsText;
    private Text titleText;
    private Combo workItemTypeCombo;
    private Button findButton;
    private Button savedQueryModeButton;
    private Button idsModeButton;
    private Button titleModeButton;
    private QueryResultsControl resultsControl;
    private Composite resultsComposite;
    private Label resultsLabel;
    private Label filterLabel;
    private Font filterLabelFont;

    /*
     * the array of team projects used by this dialog - this array backs the
     * team projects shown in the team project combo
     */
    private Project[] projects;

    /*
     * the array of work item types used by this dialog - this array backs the
     * work item types shown in the work item type combo
     */
    private WorkItemType[] workItemTypes;

    /*
     * The default search mode to use when we are creating the control.
     */
    private static final SearchMode DEFAULT_SEARCH_MODE = SearchMode.SAVED_QUERY;

    /*
     * The WorkItem that the user has selected
     */
    private WorkItem[] selectedWorkItems;

    /*
     * The currently selected query definition.
     */
    private QueryDefinition selectedQueryDefinition;

    /*
     * A WHERE clause snippet which includes the allowed work item types based
     * on the filter. The form of this string is "([System.WorkItemType] = "
     * <type >" OR [System.WorkItemType] = "<type2>"...)". This member will be
     * null if no filter is supplied or no if no work item types are exluded by
     * the filter.
     */
    private String workItemTypeFilterWhereClause = null;

    /*
     * True if any work item types are excluded based on the work item type
     * filter.
     */
    private boolean haveFilters = false;

    /*
     * True if multi-select should be allowed on this dialog.
     */
    private boolean allowMultiSelect = true;

    /*
     * A simple enum-style class. Values indicate different search modes that
     * the dialog can be in.
     */
    private static class SearchMode {
        private static final SearchMode SAVED_QUERY = new SearchMode();
        private static final SearchMode IDS = new SearchMode();
        private static final SearchMode TITLE = new SearchMode();

        private SearchMode() {
        }
    }

    public WorkItemPickerDialog(final Shell parentShell, final TFSServer server,
            final WorkItemClient workItemClient, final Project initialProject,
            final WIFormLinksControlWITypeFilters wiFilters, final boolean allowMultiSelect) {
        super(parentShell);

        Check.notNull(server, "server"); //$NON-NLS-1$
        Check.notNull(workItemClient, "workItemClient"); //$NON-NLS-1$
        Check.notNull(initialProject, "initialProject"); //$NON-NLS-1$

        this.server = server;
        this.workItemClient = workItemClient;
        this.initialProject = initialProject;
        this.wiFilters = wiFilters;
        this.allowMultiSelect = allowMultiSelect;

        haveFilters = wiFilters != null && wiFilters.getFilter() != WIFormLinksControlWITypeFilterEnum.INCLUDEALL;
    }

    @Override
    protected String provideDialogTitle() {
        return Messages.getString("WorkItemPickerDialog.DialogTitle"); //$NON-NLS-1$
    }

    @Override
    protected void hookAddToDialogArea(final Composite dialogArea) {
        final GridLayout layout = new GridLayout(2, false);
        dialogArea.setLayout(layout);

        Label label = new Label(dialogArea, SWT.NONE);
        label.setText(Messages.getString("WorkItemPickerDialog.ProjectLabelText")); //$NON-NLS-1$

        projectCombo = new Combo(dialogArea, SWT.READ_ONLY);
        projectCombo.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(final SelectionEvent e) {
                projectChanged();
            }
        });
        populateProjects();

        label = new Label(dialogArea, SWT.NONE);
        label.setText(Messages.getString("WorkItemPickerDialog.SelectMethodLabelText")); //$NON-NLS-1$
        label.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER, false, false, layout.numColumns, 1));

        savedQueryModeButton = new Button(dialogArea, SWT.RADIO);
        savedQueryModeButton.setText(Messages.getString("WorkItemPickerDialog.SaveQueryButtonText")); //$NON-NLS-1$
        savedQueryModeButton.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(final SelectionEvent e) {
                searchModeChanged();
            }
        });
        savedQueryModeButton.setSelection(DEFAULT_SEARCH_MODE == SearchMode.SAVED_QUERY);

        savedQueryArea = new Composite(dialogArea, SWT.NONE);
        final GridLayout queryAreaLayout = new GridLayout(2, false);
        queryAreaLayout.marginHeight = 0;
        queryAreaLayout.marginWidth = 0;
        savedQueryArea.setLayout(queryAreaLayout);
        savedQueryArea.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));

        savedQueryText = new Text(savedQueryArea, SWT.BORDER | SWT.READ_ONLY);
        savedQueryText.setText(Messages.getString("WorkItemPickerDialog.SavedQueryLabelText")); //$NON-NLS-1$
        savedQueryText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));

        savedQueryButton = new Button(savedQueryArea, SWT.NONE);
        savedQueryButton.setText(Messages.getString("WorkItemPickerDialog.SavedQueryButtonText")); //$NON-NLS-1$
        savedQueryButton.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(final SelectionEvent e) {
                selectQuery();
            }
        });

        idsModeButton = new Button(dialogArea, SWT.RADIO);
        idsModeButton.setText(Messages.getString("WorkItemPickerDialog.IdsButtonText")); //$NON-NLS-1$
        idsModeButton.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(final SelectionEvent e) {
                searchModeChanged();
            }
        });
        idsModeButton.setSelection(DEFAULT_SEARCH_MODE == SearchMode.IDS);

        idsText = new Text(dialogArea, SWT.BORDER);
        idsText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
        idsText.addModifyListener(new ModifyListener() {
            @Override
            public void modifyText(final ModifyEvent e) {
                idsTextModified();
            }
        });

        titleModeButton = new Button(dialogArea, SWT.RADIO);
        titleModeButton.setText(Messages.getString("WorkItemPickerDialog.TitleModeButtonText")); //$NON-NLS-1$
        titleModeButton.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(final SelectionEvent e) {
                searchModeChanged();
            }
        });
        titleModeButton.setSelection(DEFAULT_SEARCH_MODE == SearchMode.TITLE);

        titleText = new Text(dialogArea, SWT.BORDER);
        titleText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
        titleText.addModifyListener(new ModifyListener() {
            @Override
            public void modifyText(final ModifyEvent e) {
                titleTextModified();
            }
        });

        label = new Label(dialogArea, SWT.NONE);
        label.setText(Messages.getString("WorkItemPickerDialog.AndTypeLabelText")); //$NON-NLS-1$

        workItemTypeCombo = new Combo(dialogArea, SWT.READ_ONLY);
        workItemTypeCombo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
        populateWorkItemTypes();

        findButton = new Button(dialogArea, SWT.NONE);
        findButton.setText(Messages.getString("WorkItemPickerDialog.FindButtonText")); //$NON-NLS-1$
        findButton.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, layout.numColumns, 1));
        findButton.setEnabled(false); // find is disabled by default
        findButton.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(final SelectionEvent e) {
                find();
            }
        });

        label = new Label(dialogArea, SWT.NONE);
        label.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER, false, false, layout.numColumns, 1));
        label.setText(Messages.getString("WorkItemPickerDialog.SelectItemLabelText")); //$NON-NLS-1$

        resultsControl = new QueryResultsControl(dialogArea, allowMultiSelect ? SWT.MULTI : SWT.NONE);
        resultsControl.addSimpleSortingBehavior();
        resultsControl.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, layout.numColumns, 1));
        ControlSize.setCharHeightHint(resultsControl, 10);
        resultsControl.addSelectionChangedListener(new ISelectionChangedListener() {
            @Override
            public void selectionChanged(final SelectionChangedEvent event) {
                resultsControlSelectionChanged(event.getSelection());
            }
        });
        resultsControl.addDoubleClickListener(new DoubleClickListener() {
            @Override
            public void doubleClick(final DoubleClickEvent event) {
                final WorkItem workItem = (WorkItem) ((IStructuredSelection) event.getSelection())
                        .getFirstElement();
                doubleClicked(workItem);
            }
        });

        resultsComposite = new Composite(dialogArea, SWT.NONE);
        resultsComposite.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false, layout.numColumns, 1));
        SWTUtil.gridLayout(resultsComposite, 2, false, 0, 0);

        resultsLabel = new Label(resultsComposite, SWT.NONE);
        resultsLabel.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 1, 1));

        filterLabel = new Label(resultsComposite, SWT.NONE);
        filterLabel.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, false, 1, 1));
        filterLabel.setVisible(false);
        filterLabel.setText(getFilterLabelText());
        filterLabelFont = styleFont(filterLabel, SWT.ITALIC);
        filterLabel.setFont(filterLabelFont);

        final Button button = new Button(dialogArea, SWT.NONE);
        button.setText(Messages.getString("WorkItemPickerDialog.ResetButtonText")); //$NON-NLS-1$
        button.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, layout.numColumns, 1));
        button.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(final SelectionEvent e) {
                reset();
            }
        });

        /*
         * set initial enablement
         */
        setEnablementBasedOnSearchMode();
        setFindEnablement();

        reset();
    }

    @Override
    protected void hookDialogAboutToClose() {
        filterLabelFont.dispose();
    }

    public WorkItem[] getSelectedWorkItems() {
        return selectedWorkItems;
    }

    private void resultsControlSelectionChanged(final ISelection selection) {
        if (selection.isEmpty()) {
            selectedWorkItems = null;
        } else {
            final ArrayList selections = new ArrayList();
            final IStructuredSelection structuredSelection = (IStructuredSelection) selection;
            for (final Iterator it = structuredSelection.iterator(); it.hasNext();) {
                selections.add(it.next());
            }
            selectedWorkItems = (WorkItem[]) selections.toArray(new WorkItem[selections.size()]);
        }
        setOKEnablement();
    }

    private void doubleClicked(final WorkItem workItem) {
        okPressed();
    }

    /**
     * The title text control has had its contents changed.
     */
    private void titleTextModified() {
        setFindEnablement();
    }

    /**
     * The IDs text control has had its contents changed.
     */
    private void idsTextModified() {
        setFindEnablement();
    }

    /**
     * Sets the enablement state of controls on the dialog based on the search
     * mode that is currently active.
     */
    private void setEnablementBasedOnSearchMode() {
        final SearchMode searchMode = getSearchMode();

        savedQueryText.setEnabled(searchMode == SearchMode.SAVED_QUERY);
        savedQueryButton.setEnabled(searchMode == SearchMode.SAVED_QUERY);

        idsText.setEnabled(searchMode == SearchMode.IDS);
        titleText.setEnabled(searchMode == SearchMode.TITLE);
        workItemTypeCombo.setEnabled(searchMode == SearchMode.TITLE);
    }

    /**
     * Sets enablement of the Find button based on the data provided by the user
     * for the current search mode.
     */
    private void setFindEnablement() {
        final SearchMode searchMode = getSearchMode();

        if (searchMode == SearchMode.SAVED_QUERY) {
            findButton.setEnabled(selectedQueryDefinition != null);
        } else if (searchMode == SearchMode.IDS) {
            findButton.setEnabled(idsText.getText().trim().length() > 0);
        } else if (searchMode == SearchMode.TITLE) {
            findButton.setEnabled(titleText.getText().trim().length() > 0);
        } else {
            throw new IllegalStateException();
        }
    }

    @Override
    protected void createButtonsForButtonBar(final Composite parent) {
        super.createButtonsForButtonBar(parent);
        setOKEnablement();
    }

    private void setOKEnablement() {
        final Button button = getButton(IDialogConstants.OK_ID);

        /*
         * guard for when we are called from reset() before the buttons have
         * been created
         */
        if (button != null) {
            button.setEnabled(selectedWorkItems != null && selectedWorkItems.length > 0);
        }
    }

    /**
     * The select query button was clicked by the user.
     */
    private void selectQuery() {
        final SelectQueryItemDialog queryDefinitionDialog = new SelectQueryItemDialog(getShell(), server,
                workItemClient.getProjects().getProjects(), selectedQueryDefinition,
                QueryItemType.QUERY_DEFINITION);

        if (queryDefinitionDialog.open() == IDialogConstants.OK_ID) {
            selectedQueryDefinition = (QueryDefinition) queryDefinitionDialog.getSelectedQueryItem();
            final StoredQuery storedQuery = workItemClient.getStoredQuery(selectedQueryDefinition.getID());
            savedQueryText.setText(storedQuery.getName());
            setFindEnablement();
        }
    }

    /**
     * The find button was clicked by the user.
     */
    private void find() {
        final Query query = makeQuery();
        if (query == null) {
            /*
             * some sort of validation error - return without filling the
             * results
             */
            return;
        }
        fillResultsWithQuery(query, true);
    }

    /**
     * Make a query to run based on the current search mode. Called on the UI
     * thread.
     *
     * @return a Query to run, or null to indicate a validation error or
     *         cancellation occurred
     */
    private Query makeQuery() {
        final SearchMode searchMode = getSearchMode();

        if (searchMode == SearchMode.SAVED_QUERY) {
            return makeQueryQuery();
        } else if (searchMode == SearchMode.IDS) {
            return makeIDsQuery();
        } else if (searchMode == SearchMode.TITLE) {
            return makeTitleQuery();
        }

        throw new UnsupportedOperationException();
    }

    private Query makeIDsQuery() {

        int[] workItemIds = null;

        try {
            workItemIds = WorkItemLinkUtils.buildWorkItemIDListFromText(idsText.getText());
        } catch (final NumberFormatException e) {
            MessageBoxHelpers.errorMessageBox(getShell(),
                    Messages.getString("WorkItemPickerDialog.ErrorDialogTitle"), //$NON-NLS-1$
                    e.getLocalizedMessage());
            return null;
        } catch (final RuntimeException e) {
        }

        if (workItemIds == null || workItemIds.length == 0) {
            MessageBoxHelpers.errorMessageBox(getShell(),
                    Messages.getString("WorkItemPickerDialog.ErrorDialogTitle"), //$NON-NLS-1$
                    Messages.getString("WorkItemPickerDialog.IdErrorDialogText")); //$NON-NLS-1$
            return null;
        }

        final StringBuffer sb = new StringBuffer();

        sb.append("SELECT ["); //$NON-NLS-1$
        sb.append(CoreFieldReferenceNames.ID);
        sb.append("] From WorkItem WHERE ["); //$NON-NLS-1$

        sb.append(CoreFieldReferenceNames.ID);
        sb.append("] IN ("); //$NON-NLS-1$

        for (int i = 0; i < workItemIds.length; i++) {
            if (i > 0) {
                sb.append(","); //$NON-NLS-1$
            }
            sb.append(workItemIds[i]);
        }
        sb.append(")"); //$NON-NLS-1$

        final Project project = getSelectedProject();
        if (project != null) {
            sb.append(" AND ["); //$NON-NLS-1$
            sb.append(CoreFieldReferenceNames.AREA_PATH);
            sb.append("] UNDER \""); //$NON-NLS-1$
            sb.append(project.getName());
            sb.append("\""); //$NON-NLS-1$
        }

        if (workItemTypeFilterWhereClause != null) {
            sb.append(" AND "); //$NON-NLS-1$
            sb.append(workItemTypeFilterWhereClause);
        }

        sb.append(" ORDER BY ["); //$NON-NLS-1$
        sb.append(CoreFieldReferenceNames.ID);
        sb.append("]"); //$NON-NLS-1$

        final String wiql = sb.toString();
        if (log.isTraceEnabled()) {
            log.trace("ids query: [" + wiql + "]"); //$NON-NLS-1$ //$NON-NLS-2$
        }

        return workItemClient.createQuery(wiql);
    }

    private Query makeTitleQuery() {
        String containsText = titleText.getText().trim();
        containsText = containsText.replaceAll("\"", "\"\""); //$NON-NLS-1$ //$NON-NLS-2$

        final StringBuffer sb = new StringBuffer();

        sb.append("SELECT ["); //$NON-NLS-1$
        sb.append(CoreFieldReferenceNames.ID);
        sb.append("] From WorkItem WHERE ["); //$NON-NLS-1$
        sb.append(CoreFieldReferenceNames.TITLE);
        sb.append("] CONTAINS \""); //$NON-NLS-1$
        sb.append(containsText);
        sb.append("\""); //$NON-NLS-1$

        final WorkItemType workItemType = getSelectedWorkItemType();
        if (workItemType != null) {
            sb.append(" AND ["); //$NON-NLS-1$
            sb.append(CoreFieldReferenceNames.WORK_ITEM_TYPE);
            sb.append("] = \""); //$NON-NLS-1$
            sb.append(workItemType.getName());
            sb.append("\""); //$NON-NLS-1$
        } else if (workItemTypeFilterWhereClause != null) {
            sb.append(" AND "); //$NON-NLS-1$
            sb.append(workItemTypeFilterWhereClause);
        }

        final Project project = getSelectedProject();
        if (project != null) {
            sb.append(" AND ["); //$NON-NLS-1$
            sb.append(CoreFieldReferenceNames.AREA_PATH);
            sb.append("] UNDER \""); //$NON-NLS-1$
            sb.append(project.getName());
            sb.append("\""); //$NON-NLS-1$
        }

        sb.append(" ORDER BY ["); //$NON-NLS-1$
        sb.append(CoreFieldReferenceNames.ID);
        sb.append("]"); //$NON-NLS-1$

        final String wiql = sb.toString();

        if (log.isTraceEnabled()) {
            final String messageFormat = "title query: [{0}]"; //$NON-NLS-1$
            final String message = MessageFormat.format(messageFormat, wiql);
            log.trace(message);
        }

        return workItemClient.createQuery(wiql);
    }

    /**
     * Create a new query based of an existing query, which was selected by the
     * user, with the addition of work item type filtering. If filtering is
     * required, the WHERE clause of the existing query needs to be updated to
     * include on the valid work item types. To do this, the existing query is
     * parsed by the WIQL parser and the WIQL DOM is modified to include the
     * additional conditions on the WHERE clause.
     *
     *
     * @return A new query which contains required work item filtering.
     */
    private Query makeQueryQuery() {
        // Get the existing WIQL query chosen by the user.
        String wiql = selectedQueryDefinition.getQueryText();

        if (haveFilters && workItemTypes.length > 0) {
            // Parse the existing WIQL.
            final NodeSelect select = Parser.parseSyntax(wiql);
            final Node where = select.getWhere();
            Node addition;

            // Build the conditions that will produce filtering.
            if (workItemTypes.length == 1) {
                addition = new NodeCondition(Condition.EQUALS, new NodeFieldName("System.WorkItemType"), //$NON-NLS-1$
                        new NodeString(workItemTypes[0].getName()));
            } else {
                final NodeOrOperator nodeOr = new NodeOrOperator();
                for (int i = 0; i < workItemTypes.length; i++) {
                    nodeOr.add(new NodeCondition(Condition.EQUALS, new NodeFieldName("System.WorkItemType"), //$NON-NLS-1$
                            new NodeString(workItemTypes[i].getName())));
                }
                addition = nodeOr;
            }

            // Update the WHERE clause.
            if (where == null) {
                select.setWhere(addition);
            } else {
                final NodeAndOperator nodeAnd = new NodeAndOperator();
                nodeAnd.add(where);
                nodeAnd.add(addition);
                select.setWhere(nodeAnd);
            }

            wiql = select.toString();
        }

        // Create a query based on the potentially modified WIQL.
        return workItemClient.createQuery(wiql,
                WorkItemQueryUtils.makeContext(initialProject, WorkItemHelpers.getCurrentTeamName()));
    }

    /**
     * Builds a snippet of a WHERE clause that can be included when building a
     * query to support the ID and TITLE search modes.
     *
     *
     * @param workItemTypes
     *        The work item types to include.
     *
     * @return The snippet for a WIQL where clause. The string is in the format
     *         ([System.WorkItemType] = "type1" OR [System.WorkItemType] =
     *         "type2"...)
     */
    private String makeWorkItemTypeFilterWhereClause(final WorkItemType[] workItemTypes) {
        if (workItemTypes.length == 0) {
            return null;
        }

        final StringBuffer sb = new StringBuffer();
        sb.append("("); //$NON-NLS-1$

        for (int i = 0; i < workItemTypes.length; i++) {
            if (i > 0) {
                sb.append(" OR "); //$NON-NLS-1$
            }
            sb.append("["); //$NON-NLS-1$
            sb.append(CoreFieldReferenceNames.WORK_ITEM_TYPE);
            sb.append("] = \""); //$NON-NLS-1$
            sb.append(workItemTypes[i].getName());
            sb.append("\""); //$NON-NLS-1$
        }

        sb.append(")"); //$NON-NLS-1$
        return sb.toString();
    }

    /**
     * Fill the results control with the results made from running the given
     * Query.
     *
     * @param query
     *        the Query to run
     * @param updateResultsLabel
     *        true to update the results label
     */
    private void fillResultsWithQuery(final Query query, final boolean updateResultsLabel) {
        /*
         * modify the DisplayFieldList on the Query to have the fields we want
         * to display in this control
         */
        final DisplayFieldList displayFieldList = query.getDisplayFieldList();
        displayFieldList.clear();
        displayFieldList.add(CoreFieldReferenceNames.ID);
        displayFieldList.add(CoreFieldReferenceNames.WORK_ITEM_TYPE);
        displayFieldList.add(CoreFieldReferenceNames.TITLE);
        displayFieldList.add(CoreFieldReferenceNames.STATE);

        /*
         * run the query
         */
        final String projectName = (getSelectedProject() != null) ? getSelectedProject().getName() : null;
        final String queryName = (selectedQueryDefinition != null) ? selectedQueryDefinition.getName() : null;
        final QueryResultCommand command = new QueryResultCommand(query, projectName, queryName);

        final IStatus status = UICommandExecutorFactory.newBusyIndicatorCommandExecutor(getShell())
                .execute(command);
        if (status.getSeverity() != Status.OK) {
            return;
        }
        final QueryResultData results = command.getQueryResultData();

        /*
         * pass the results off to the results control
         */
        resultsControl.setServer(server);
        resultsControl.setWorkItems(results);

        /*
         * enable the results control if there is at least one result
         */
        resultsControl.setEnabled(results.getCount() > 0);

        /*
         * update the results label if needed
         */
        if (updateResultsLabel) {
            final String message = MessageFormat.format(Messages.getString("WorkItemPickerDialog.ResultsLabelText"), //$NON-NLS-1$
                    new Object[] { new Integer(results.getCount()) });
            resultsLabel.setText(message);
            filterLabel.setVisible(true);
        } else {
            resultsLabel.setText(""); //$NON-NLS-1$
            filterLabel.setVisible(false);
        }

        resultsComposite.layout(true);
    }

    /**
     * The reset button was clicked by the user.
     */
    private void reset() {
        // 1) does not change the selected search mode or team project
        // 2) clears any entered or selected data
        // 3) clears any results
        savedQueryText.setText(Messages.getString("WorkItemPickerDialog.SavedQueryLabelText")); //$NON-NLS-1$
        idsText.setText(""); //$NON-NLS-1$
        titleText.setText(""); //$NON-NLS-1$
        workItemTypeCombo.select(0);

        fillResultsWithQuery(workItemClient.createEmptyQuery(), false);
        selectedWorkItems = null;
        selectedQueryDefinition = null;

        setFindEnablement();
        setOKEnablement();
    }

    /**
     * The search mode has been changed by the user. Note: the new search mode
     * is available by calling the getSearchMode() method.
     */
    private void searchModeChanged() {
        setEnablementBasedOnSearchMode();
        reset();
    }

    /**
     * The selected project has been changed by the user. Note: The newly
     * selected project can be obtained by calling getSelectedProject().
     */
    private void projectChanged() {
        populateWorkItemTypes();
        reset();
    }

    /**
     * This method: 1) populates the "projects" field of this class 2) populates
     * the projects combo with project names 3) selects the initial choice in
     * the projects combo ("Any Project")
     */
    private void populateProjects() {
        projects = workItemClient.getProjects().getProjects();

        projectCombo.add(Messages.getString("WorkItemPickerDialog.AnyProjectChoice")); //$NON-NLS-1$
        int initialSelectIx = 0;

        for (int i = 0; i < projects.length; i++) {
            projectCombo.add(projects[i].getName());
            if (projects[i] == initialProject) {
                initialSelectIx = (i + 1);
            }
        }

        /*
         * select "Any Project" by default
         */
        projectCombo.select(initialSelectIx);
    }

    /**
     * Obtains the currently selected team project in the projects combo. This
     * method will return null to indicate that the "Any Project" choice is
     * currently selected.
     *
     * @return the current project or null
     */
    private Project getSelectedProject() {
        final int ix = projectCombo.getSelectionIndex();
        if (ix == 0) {
            return null;
        }
        return projects[ix - 1];
    }

    /**
     * @return the current SearchMode, as determined by the search mode radio
     *         buttons
     */
    private SearchMode getSearchMode() {
        if (savedQueryModeButton.getSelection()) {
            return SearchMode.SAVED_QUERY;
        } else if (idsModeButton.getSelection()) {
            return SearchMode.IDS;
        } else if (titleModeButton.getSelection()) {
            return SearchMode.TITLE;
        }
        throw new IllegalStateException();
    }

    /**
     * Populates the saved query combo based on the currently selected project
     * in the project combo and the specified work item type filters.
     */
    private void populateWorkItemTypes() {
        workItemTypeCombo.removeAll();
        workItemTypeCombo.add(Messages.getString("WorkItemPickerDialog.AllWorkItemTypesChoice")); //$NON-NLS-1$
        workItemTypeCombo.select(0);

        // Get the set of filtered work item types based on the project
        // selection.
        final Project selectedProject = getSelectedProject();
        if (selectedProject != null) {
            workItemTypes = getFilteredWorkItemTypes(selectedProject.getWorkItemTypes().getTypes());
        } else {
            workItemTypes = getFilteredWorkItemTypes(getAllProjectWorkItemTypes());
        }

        // Build the WHERE clause that would apply for the filtered work item
        // types.
        workItemTypeFilterWhereClause = null;
        if (haveFilters && workItemTypes != null && workItemTypes.length > 0) {
            workItemTypeFilterWhereClause = makeWorkItemTypeFilterWhereClause(workItemTypes);
        }

        // Populate the combobox.
        if (workItemTypes != null) {
            for (int i = 0; i < workItemTypes.length; i++) {
                workItemTypeCombo.add(workItemTypes[i].getName());
            }
        }

        // Set the minimum size of the drop down.
        ComboHelper.setVisibleItemCount(workItemTypeCombo);
    }

    /**
     * Iterate all projects and determine the union of work item types across
     * all projects.
     *
     * @return Array of work-item types representing the union of all work item
     *         types across all projects.
     */
    private WorkItemType[] getAllProjectWorkItemTypes() {
        final HashMap map = new HashMap();
        for (int i = 0; i < projects.length; i++) {
            final Project project = projects[i];
            final WorkItemTypeCollection collection = project.getWorkItemTypes();

            for (final Iterator it = collection.iterator(); it.hasNext();) {
                final WorkItemType type = (WorkItemType) it.next();
                final String typeName = type.getName();

                if (!map.containsKey(typeName)) {
                    map.put(typeName, type);
                }
            }
        }

        return (WorkItemType[]) map.values().toArray(new WorkItemType[map.size()]);
    }

    /**
     * Filters the specified array of work-item types based on the filter
     * options suppled to this class.
     *
     *
     * @param allWorkItemTypes
     * @return
     */
    private WorkItemType[] getFilteredWorkItemTypes(final WorkItemType[] allWorkItemTypes) {
        if (!haveFilters) {
            return allWorkItemTypes;
        }

        final ArrayList list = new ArrayList();
        for (int i = 0; i < allWorkItemTypes.length; i++) {
            if (wiFilters.includes(allWorkItemTypes[i].getName())) {
                list.add(allWorkItemTypes[i]);
            }
        }

        return (WorkItemType[]) list.toArray(new WorkItemType[list.size()]);
    }

    /**
     * Obtains the currently selected work item type in the work item types
     * combo. This method will return null to indicate that the
     * "All Work Item Types" choice is currently selected.
     *
     * @return the current work item type or null
     */
    private WorkItemType getSelectedWorkItemType() {
        final int ix = workItemTypeCombo.getSelectionIndex();
        if (ix == 0) {
            return null;
        }
        return workItemTypes[ix - 1];
    }

    /**
     * Returns the text to display when work items are being filtered by type.
     * An empty string is returned if there are no filters applied to this
     * dialog.
     */
    private String getFilterLabelText() {
        if (wiFilters != null && wiFilters.getFilter() != WIFormLinksControlWITypeFilterEnum.INCLUDEALL) {
            final WIFormLinksControlWITypeFilter[] filters = wiFilters.getFilters();

            if (filters.length > 0) {
                String messageFormat;
                if (wiFilters.getFilter() == WIFormLinksControlWITypeFilterEnum.EXCLUDE) {
                    messageFormat = Messages.getString("WorkItemPickerDialog.ExcludingWorkItemTypesFormat"); //$NON-NLS-1$
                } else {
                    messageFormat = Messages.getString("WorkItemPickerDialog.IncludingWorkItemTypesFormat"); //$NON-NLS-1$
                }

                final StringBuffer sb = new StringBuffer();
                for (int i = 0; i < filters.length; i++) {
                    if (i > 0) {
                        sb.append(Messages.getString("WorkItemPickerDialog.WorkItemTypeNameSeparator")); //$NON-NLS-1$
                    }
                    sb.append(filters[i].getWorkItemType());
                }

                return MessageFormat.format(messageFormat, sb.toString());
            }
        }

        return ""; //$NON-NLS-1$
    }

    /**
     * Create a new font based of the existing font of the specified label. Add
     * the additional style to the new font.
     *
     *
     * @param label
     *        The label whose font we want to use as a base.
     * @param style
     *        The styles to add to the base font.
     * @return A newly allocated font.
     */
    private Font styleFont(final Label label, final int style) {
        final FontData[] fontData = label.getFont().getFontData();
        for (int i = 0; i < fontData.length; i++) {
            fontData[i].setStyle(fontData[i].getStyle() | style);
        }

        return new Font(label.getDisplay(), fontData);
    }
}