/*******************************************************************************
 * Copyright (c) 2004, 2010 Tasktop Technologies and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Tasktop Technologies - initial API and implementation
 *     Steve Elsemore - fix for bug 296963
 *     Atlassian - fix for bug 319699
 *******************************************************************************/

package org.eclipse.mylyn.internal.tasks.ui.util;

import java.net.MalformedURLException;
import java.net.URL;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.eclipse.core.commands.Command;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.commands.NotEnabledException;
import org.eclipse.core.commands.NotHandledException;
import org.eclipse.core.commands.ParameterizedCommand;
import org.eclipse.core.commands.common.NotDefinedException;
import org.eclipse.core.expressions.EvaluationContext;
import org.eclipse.core.expressions.IEvaluationContext;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.ProgressMonitorWrapper;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.IJobChangeEvent;
import org.eclipse.core.runtime.jobs.IJobChangeListener;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.runtime.jobs.JobChangeAdapter;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.jface.dialogs.IInputValidator;
import org.eclipse.jface.dialogs.InputDialog;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITreeSelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.window.Window;
import org.eclipse.jface.wizard.IWizard;
import org.eclipse.jface.wizard.WizardDialog;
import org.eclipse.mylyn.commons.core.CoreUtil;
import org.eclipse.mylyn.commons.core.StatusHandler;
import org.eclipse.mylyn.commons.identity.Account;
import org.eclipse.mylyn.internal.monitor.ui.MonitorUiPlugin;
import org.eclipse.mylyn.internal.provisional.commons.ui.CommonImages;
import org.eclipse.mylyn.internal.provisional.commons.ui.CommonUiUtil;
import org.eclipse.mylyn.internal.provisional.commons.ui.WorkbenchUtil;
import org.eclipse.mylyn.internal.tasks.core.AbstractTask;
import org.eclipse.mylyn.internal.tasks.core.AbstractTaskCategory;
import org.eclipse.mylyn.internal.tasks.core.AbstractTaskContainer;
import org.eclipse.mylyn.internal.tasks.core.ITaskJobFactory;
import org.eclipse.mylyn.internal.tasks.core.ITaskList;
import org.eclipse.mylyn.internal.tasks.core.ITasksCoreConstants;
import org.eclipse.mylyn.internal.tasks.core.LocalRepositoryConnector;
import org.eclipse.mylyn.internal.tasks.core.LocalTask;
import org.eclipse.mylyn.internal.tasks.core.RepositoryQuery;
import org.eclipse.mylyn.internal.tasks.core.TaskCategory;
import org.eclipse.mylyn.internal.tasks.core.TaskList;
import org.eclipse.mylyn.internal.tasks.core.UncategorizedTaskContainer;
import org.eclipse.mylyn.internal.tasks.core.UnsubmittedTaskContainer;
import org.eclipse.mylyn.internal.tasks.core.data.TaskDataState;
import org.eclipse.mylyn.internal.tasks.core.sync.SynchronizationScheduler;
import org.eclipse.mylyn.internal.tasks.core.sync.SynchronizationScheduler.Synchronizer;
import org.eclipse.mylyn.internal.tasks.ui.ITasksUiPreferenceConstants;
import org.eclipse.mylyn.internal.tasks.ui.OpenRepositoryTaskJob;
import org.eclipse.mylyn.internal.tasks.ui.TasksUiPlugin;
import org.eclipse.mylyn.internal.tasks.ui.views.TaskListView;
import org.eclipse.mylyn.internal.tasks.ui.wizards.MultiRepositoryAwareWizard;
import org.eclipse.mylyn.internal.tasks.ui.wizards.NewAttachmentWizardDialog;
import org.eclipse.mylyn.internal.tasks.ui.wizards.NewTaskWizardInternal;
import org.eclipse.mylyn.internal.tasks.ui.wizards.TaskAttachmentWizard;
import org.eclipse.mylyn.tasks.core.AbstractRepositoryConnector;
import org.eclipse.mylyn.tasks.core.IRepositoryElement;
import org.eclipse.mylyn.tasks.core.IRepositoryManager;
import org.eclipse.mylyn.tasks.core.IRepositoryPerson;
import org.eclipse.mylyn.tasks.core.IRepositoryQuery;
import org.eclipse.mylyn.tasks.core.ITask;
import org.eclipse.mylyn.tasks.core.ITask.PriorityLevel;
import org.eclipse.mylyn.tasks.core.ITask.SynchronizationState;
import org.eclipse.mylyn.tasks.core.ITaskMapping;
import org.eclipse.mylyn.tasks.core.RepositoryStatus;
import org.eclipse.mylyn.tasks.core.TaskRepository;
import org.eclipse.mylyn.tasks.core.data.AbstractTaskAttachmentSource;
import org.eclipse.mylyn.tasks.core.data.AbstractTaskDataHandler;
import org.eclipse.mylyn.tasks.core.data.ITaskDataWorkingCopy;
import org.eclipse.mylyn.tasks.core.data.TaskAttachmentMapper;
import org.eclipse.mylyn.tasks.core.data.TaskAttribute;
import org.eclipse.mylyn.tasks.core.data.TaskAttributeMapper;
import org.eclipse.mylyn.tasks.core.data.TaskCommentMapper;
import org.eclipse.mylyn.tasks.core.data.TaskData;
import org.eclipse.mylyn.tasks.core.data.TaskHistory;
import org.eclipse.mylyn.tasks.core.data.TaskRevision;
import org.eclipse.mylyn.tasks.core.data.TaskRevision.Change;
import org.eclipse.mylyn.tasks.core.sync.SynchronizationJob;
import org.eclipse.mylyn.tasks.core.sync.TaskJob;
import org.eclipse.mylyn.tasks.ui.AbstractRepositoryConnectorUi;
import org.eclipse.mylyn.tasks.ui.TasksUi;
import org.eclipse.mylyn.tasks.ui.TasksUiImages;
import org.eclipse.mylyn.tasks.ui.TasksUiUtil;
import org.eclipse.mylyn.tasks.ui.editors.TaskEditor;
import org.eclipse.mylyn.tasks.ui.editors.TaskEditorInput;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorReference;
import org.eclipse.ui.ISources;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchPreferenceConstants;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.IWorkingSet;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.commands.ICommandService;
import org.eclipse.ui.handlers.IHandlerService;
import org.eclipse.ui.services.IServiceLocator;
import org.eclipse.ui.statushandlers.IStatusAdapterConstants;
import org.eclipse.ui.statushandlers.StatusAdapter;
import org.eclipse.ui.statushandlers.StatusManager;

/**
 * @author Steffen Pingel
 */
public class TasksUiInternal {

	private static SynchronizationScheduler synchronizationScheduler = new SynchronizationScheduler();

	// TODO e3.5 replace by SWT.SHEET
	public static final int SWT_SHEET = 1 << 28;

	public static final String ID_MENU_ACTIVE_TASK = "org.eclipse.mylyn.tasks.ui.menus.activeTask"; //$NON-NLS-1$

	public static MultiRepositoryAwareWizard createNewTaskWizard(ITaskMapping taskSelection) {
		return new NewTaskWizardInternal(taskSelection);
	}

	/**
	 * get the connector discovery wizard command. Calling code should check {@link Command#isEnabled()} on return.
	 * 
	 * @return the command, or null if it is not available.
	 */
	public static Command getConfiguredDiscoveryWizardCommand() {
		ICommandService service = (ICommandService) PlatformUI.getWorkbench().getService(ICommandService.class);
		final Command discoveryWizardCommand = service.getCommand("org.eclipse.mylyn.tasks.ui.discoveryWizardCommand"); //$NON-NLS-1$
		if (discoveryWizardCommand != null) {
			IHandlerService handlerService = (IHandlerService) PlatformUI.getWorkbench().getService(
					IHandlerService.class);
			EvaluationContext evaluationContext = createDiscoveryWizardEvaluationContext(handlerService);
			// update enabled state in case something has changed (ProxyHandler caches state)
			discoveryWizardCommand.setEnabled(evaluationContext);
		}
		return discoveryWizardCommand;
	}

	public static EvaluationContext createDiscoveryWizardEvaluationContext(IHandlerService handlerService) {
		EvaluationContext evaluationContext = new EvaluationContext(handlerService.getCurrentState(), Platform.class);
		// must specify this variable otherwise the PlatformPropertyTester won't work
		evaluationContext.addVariable("platform", Platform.class); //$NON-NLS-1$
		return evaluationContext;
	}

	public static ImageDescriptor getPriorityImage(ITask task) {
		if (task.isCompleted()) {
			return CommonImages.COMPLETE;
		} else {
			return TasksUiImages.getImageDescriptorForPriority(PriorityLevel.fromString(task.getPriority()));
		}
	}

	public static List<TaskEditor> getActiveRepositoryTaskEditors() {
		List<TaskEditor> repositoryTaskEditors = new ArrayList<TaskEditor>();
		IWorkbenchWindow[] windows = PlatformUI.getWorkbench().getWorkbenchWindows();
		for (IWorkbenchWindow window : windows) {
			IEditorReference[] editorReferences = window.getActivePage().getEditorReferences();
			for (IEditorReference editorReference : editorReferences) {
				try {
					if (editorReference.getEditorInput() instanceof TaskEditorInput) {
						TaskEditorInput input = (TaskEditorInput) editorReference.getEditorInput();
						if (input.getTask() != null) {
							IEditorPart editorPart = editorReference.getEditor(false);
							if (editorPart instanceof TaskEditor) {
								repositoryTaskEditors.add((TaskEditor) editorPart);
							}
						}
					}
				} catch (PartInitException e) {
					// ignore
				}
			}
		}
		return repositoryTaskEditors;
	}

	public static IProgressMonitor getUiMonitor(IProgressMonitor monitor) {
		return new ProgressMonitorWrapper(monitor) {
			@Override
			public void beginTask(final String name, final int totalWork) {
				PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
					public void run() {
						getWrappedProgressMonitor().beginTask(name, totalWork);
					}
				});
			}

			@Override
			public void done() {
				PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
					public void run() {
						getWrappedProgressMonitor().done();
					}
				});
			}

			@Override
			public void subTask(final String name) {
				PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
					public void run() {
						getWrappedProgressMonitor().subTask(name);
					}
				});
			}

			@Override
			public void worked(final int work) {
				PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
					public void run() {
						getWrappedProgressMonitor().worked(work);
					}
				});
			}
		};
	}

	public static void openEditor(TaskCategory category) {
		final String name = category.getSummary();
		InputDialog dialog = new InputDialog(WorkbenchUtil.getShell(), Messages.TasksUiInternal_Rename_Category_Title,
				Messages.TasksUiInternal_Rename_Category_Message, name, new IInputValidator() {
					public String isValid(String newName) {
						if (newName.trim().length() == 0 || newName.equals(name)) {
							return ""; //$NON-NLS-1$
						}
						Set<AbstractTaskCategory> categories = TasksUiPlugin.getTaskList().getCategories();
						for (AbstractTaskCategory category : categories) {
							if (newName.equals(category.getSummary())) {
								return Messages.TasksUiInternal_Rename_Category_Name_already_exists_Error;
							}
						}
						return null;
					}
				});
		if (dialog.open() == Window.OK) {
			TasksUiPlugin.getTaskList().renameContainer(category, dialog.getValue());
		}
	}

	public static void refreshAndOpenTaskListElement(IRepositoryElement element) {
		if (element instanceof ITask) {
			final AbstractTask task = (AbstractTask) element;

			if (task instanceof LocalTask) {
				TasksUiUtil.openTask(task);
			} else {
				String repositoryKind = task.getConnectorKind();
				final AbstractRepositoryConnector connector = TasksUi.getRepositoryManager().getRepositoryConnector(
						repositoryKind);

				TaskRepository repository = TasksUi.getRepositoryManager().getRepository(repositoryKind,
						task.getRepositoryUrl());
				if (repository == null) {
					displayStatus(Messages.TasksUiInternal_Failed_to_open_task, new Status(IStatus.ERROR,
							TasksUiPlugin.ID_PLUGIN, Messages.TasksUiInternal_No_repository_found));
					return;
				}

				if (connector != null) {
					boolean opened = false;
					if (TasksUiPlugin.getTaskDataManager().hasTaskData(task)) {
						opened = TasksUiUtil.openTask(task);
					}

					if (!opened) {
						if (connector.canSynchronizeTask(repository, task)) {
							// TODO consider moving this into the editor, i.e. have the editor refresh the task if task data is missing
							TasksUiInternal.synchronizeTask(connector, task, true, new JobChangeAdapter() {
								@Override
								public void done(IJobChangeEvent event) {
									PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
										public void run() {
											TasksUiUtil.openTask(task);
										}
									});
								}
							});
						} else {
							TasksUiUtil.openTask(task);
						}
					}
				}
			}
		} else if (element instanceof TaskCategory) {
			TasksUiInternal.openEditor((TaskCategory) element);
		} else if (element instanceof IRepositoryQuery) {
			RepositoryQuery query = (RepositoryQuery) element;
			AbstractRepositoryConnectorUi connectorUi = TasksUiPlugin.getConnectorUi(query.getConnectorKind());
			TasksUiInternal.openEditQueryDialog(connectorUi, query);
		}
	}

	public static TaskJob updateRepositoryConfiguration(final TaskRepository taskRepository) {
		synchronized (taskRepository) {
			taskRepository.setUpdating(true);
		}

		AbstractRepositoryConnector connector = TasksUi.getRepositoryManager().getRepositoryConnector(
				taskRepository.getConnectorKind());
		final TaskJob job = TasksUiInternal.getJobFactory().createUpdateRepositoryConfigurationJob(connector,
				taskRepository, null);
		job.addJobChangeListener(new JobChangeAdapter() {
			@Override
			public void done(IJobChangeEvent event) {
				synchronized (taskRepository) {
					taskRepository.setUpdating(false);
				}
				if (job.getStatus() != null) {
					TasksUiInternal.asyncLogAndDisplayStatus(Messages.TasksUiInternal_Configuration_Refresh_Failed,
							job.getStatus());
				}
			}
		});
		job.schedule();
		return job;
	}

	private static void joinIfInTestMode(SynchronizationJob job) {
		// FIXME the client code should join the job
		if (CoreUtil.TEST_MODE) {
			try {
				job.join();
			} catch (InterruptedException e) {
				throw new RuntimeException(e);
			}
		}
	}

	public static final Job synchronizeQueries(AbstractRepositoryConnector connector, TaskRepository repository,
			Set<RepositoryQuery> queries, IJobChangeListener listener, boolean force) {
		Assert.isTrue(queries.size() > 0);

		TaskList taskList = TasksUiPlugin.getTaskList();
		for (RepositoryQuery query : queries) {
			query.setSynchronizing(true);
		}
		taskList.notifySynchronizationStateChanged(queries);

		SynchronizationJob job = TasksUiPlugin.getTaskJobFactory().createSynchronizeQueriesJob(connector, repository,
				queries);
		job.setUser(force);
		if (force) {
			// show the progress in the system task bar if this is a user job (i.e. forced)
			job.setProperty(WorkbenchUtil.SHOW_IN_TASKBAR_ICON_PROPERTY, Boolean.TRUE);
		}
		if (listener != null) {
			job.addJobChangeListener(listener);
		}
		if (force) {
			final RepositoryQuery query = queries.iterator().next();
			job.addJobChangeListener(new JobChangeAdapter() {
				@Override
				public void done(IJobChangeEvent event) {
					if (query.getStatus() != null) {
						TasksUiInternal.asyncLogAndDisplayStatus(Messages.TasksUiInternal_Query_Synchronization_Failed,
								query.getStatus());
					}
				}
			});
		}
		job.schedule();
		joinIfInTestMode(job);
		return job;
	}

	/**
	 * For synchronizing a single query. Use synchronize(Set, IJobChangeListener) if synchronizing multiple queries at a
	 * time.
	 */
	public static final Job synchronizeQuery(AbstractRepositoryConnector connector, RepositoryQuery repositoryQuery,
			IJobChangeListener listener, boolean force) {
		TaskRepository repository = TasksUi.getRepositoryManager().getRepository(repositoryQuery.getConnectorKind(),
				repositoryQuery.getRepositoryUrl());
		return synchronizeQueries(connector, repository, Collections.singleton(repositoryQuery), listener, force);
	}

	public static SynchronizationJob synchronizeAllRepositories(boolean force) {
		SynchronizationJob job = TasksUiPlugin.getTaskJobFactory().createSynchronizeRepositoriesJob(null);
		job.setUser(force);
		if (force) {
			// show the progress in the system task bar if this is a user job (i.e. forced)
			job.setProperty(WorkbenchUtil.SHOW_IN_TASKBAR_ICON_PROPERTY, Boolean.TRUE);
		}
		job.schedule();
		joinIfInTestMode(job);
		return job;
	}

	public static void synchronizeRepositoryInBackground(final TaskRepository repository) {
		synchronizationScheduler.schedule(repository, new Synchronizer<SynchronizationJob>() {
			@Override
			public SynchronizationJob createJob() {
				return synchronizeRepositoryInternal(repository, false);
			}
		});
	}

	public static SynchronizationJob synchronizeRepository(TaskRepository repository, boolean force) {
		SynchronizationJob job = synchronizeRepositoryInternal(repository, force);
		synchronizationScheduler.cancel(repository);
		job.schedule();
		return job;
	}

	private static SynchronizationJob synchronizeRepositoryInternal(TaskRepository repository, boolean force) {
		SynchronizationJob job = TasksUiInternal.getJobFactory().createSynchronizeRepositoriesJob(
				Collections.singleton(repository));
		// do not show in progress view by default
		job.setSystem(true);
		job.setUser(force);
		if (force) {
			// show the progress in the system task bar if this is a user job (i.e. forced)
			job.setProperty(WorkbenchUtil.SHOW_IN_TASKBAR_ICON_PROPERTY, Boolean.TRUE);
		}
		job.setFullSynchronization(false);
		return job;
	}

	public static void synchronizeTaskInBackground(final AbstractRepositoryConnector connector, final ITask task) {
		synchronizationScheduler.schedule(task, new Synchronizer<Job>() {
			@Override
			public Job createJob() {
				SynchronizationJob job = TasksUiPlugin.getTaskJobFactory().createSynchronizeTasksJob(connector,
						Collections.singleton(task));
				job.setUser(false);
				job.setSystem(true);
				return job;
			}
		});
	}

	/**
	 * Synchronize a single task. Note that if you have a collection of tasks to synchronize with this connector then
	 * you should call synchronize(Set<Set<AbstractTask> repositoryTasks, ...)
	 * 
	 * @param listener
	 *            can be null
	 */
	public static Job synchronizeTask(AbstractRepositoryConnector connector, ITask task, boolean force,
			IJobChangeListener listener) {
		return synchronizeTasks(connector, Collections.singleton(task), force, listener);
	}

	/**
	 * @param listener
	 *            can be null
	 */
	public static Job synchronizeTasks(AbstractRepositoryConnector connector, Set<ITask> tasks, boolean force,
			IJobChangeListener listener) {
		ITaskList taskList = TasksUiInternal.getTaskList();
		for (ITask task : tasks) {
			((AbstractTask) task).setSynchronizing(true);
			synchronizationScheduler.cancel(task);
		}
		((TaskList) taskList).notifySynchronizationStateChanged(tasks);
		// TODO notify task list?

		SynchronizationJob job = TasksUiPlugin.getTaskJobFactory().createSynchronizeTasksJob(connector, tasks);
		job.setUser(force);
		job.setSystem(!force);
		job.setPriority(Job.DECORATE);
		if (listener != null) {
			job.addJobChangeListener(listener);
		}
		if (force && tasks.size() == 1) {
			final ITask task = tasks.iterator().next();
			job.addJobChangeListener(new JobChangeAdapter() {
				@Override
				public void done(IJobChangeEvent event) {
					if (task instanceof AbstractTask && ((AbstractTask) task).getStatus() != null) {
						TasksUiInternal.asyncLogAndDisplayStatus(Messages.TasksUiInternal_Task_Synchronization_Failed,
								((AbstractTask) task).getStatus());
					}
				}
			});
		}
		job.schedule();
		joinIfInTestMode(job);
		return job;
	}

	public static ITaskJobFactory getJobFactory() {
		return TasksUiPlugin.getTaskJobFactory();
	}

	public static NewAttachmentWizardDialog openNewAttachmentWizard(Shell shell, TaskRepository taskRepository,
			ITask task, TaskAttribute taskAttribute, TaskAttachmentWizard.Mode mode, AbstractTaskAttachmentSource source) {
		TaskAttachmentWizard attachmentWizard = new TaskAttachmentWizard(taskRepository, task, taskAttribute);
		attachmentWizard.setSource(source);
		attachmentWizard.setMode(mode);
		NewAttachmentWizardDialog dialog = new NewAttachmentWizardDialog(shell, attachmentWizard, false);
		dialog.setBlockOnOpen(false);
		dialog.create();
		dialog.open();
		return dialog;
	}

	private static MessageDialog createDialog(Shell shell, String title, String message, int type) {
		return new MessageDialog(shell, title, null, message, type, new String[] { IDialogConstants.OK_LABEL }, 0);
	}

	private static void displayStatus(Shell shell, final String title, final IStatus status, boolean showLinkToErrorLog) {
		// avoid blocking ui when in test mode
		if (CoreUtil.TEST_MODE) {
			StatusHandler.log(status);
			return;
		}

		if (status.getCode() == RepositoryStatus.ERROR_INTERNAL) {
			StatusHandler.fail(status);
		} else {
			if (status instanceof RepositoryStatus && ((RepositoryStatus) status).isHtmlMessage()) {
				WebBrowserDialog.openAcceptAgreement(shell, title, status.getMessage(),
						((RepositoryStatus) status).getHtmlMessage());
			} else {
				String message = status.getMessage();
				if (showLinkToErrorLog) {
					message += "\n\n" + Messages.TasksUiInternal_See_error_log_for_details; //$NON-NLS-1$
				}
				switch (status.getSeverity()) {
				case IStatus.CANCEL:
				case IStatus.INFO:
					createDialog(shell, title, message, MessageDialog.INFORMATION).open();
					break;
				case IStatus.WARNING:
					createDialog(shell, title, message, MessageDialog.WARNING).open();
					break;
				case IStatus.ERROR:
				default:
					createDialog(shell, title, message, MessageDialog.ERROR).open();
					break;
				}
			}
		}
	}

	public static void asyncDisplayStatus(final String title, final IStatus status) {
		Display display = PlatformUI.getWorkbench().getDisplay();
		if (!display.isDisposed()) {
			display.asyncExec(new Runnable() {
				public void run() {
					displayStatus(title, status);
				}
			});
		} else {
			StatusHandler.log(status);
		}
	}

	public static void asyncLogAndDisplayStatus(final String title, final IStatus status) {
		Display display = PlatformUI.getWorkbench().getDisplay();
		if (!display.isDisposed()) {
			display.asyncExec(new Runnable() {
				public void run() {
					logAndDisplayStatus(title, status);
				}
			});
		} else {
			StatusHandler.log(status);
		}
	}

	public static void logAndDisplayStatus(final String title, final IStatus status) {
		StatusHandler.log(status);
		IWorkbench workbench = PlatformUI.getWorkbench();
		if (workbench != null && !workbench.getDisplay().isDisposed()) {
			displayStatus(WorkbenchUtil.getShell(), title, status, false);
		}
	}

	public static void displayStatus(final String title, final IStatus status) {
		IWorkbench workbench = PlatformUI.getWorkbench();
		if (workbench != null && !workbench.getDisplay().isDisposed()) {
			displayStatus(WorkbenchUtil.getShell(), title, status, false);
		} else {
			StatusHandler.log(status);
		}
	}

	/**
	 * Creates a new local task and schedules for today
	 * 
	 * @param summary
	 *            if null DEFAULT_SUMMARY (New Task) used.
	 */
	public static LocalTask createNewLocalTask(String summary) {
		if (summary == null) {
			summary = LocalRepositoryConnector.DEFAULT_SUMMARY;
		}
		TaskList taskList = TasksUiPlugin.getTaskList();
		LocalTask newTask = new LocalTask("" + taskList.getNextLocalTaskId(), summary); //$NON-NLS-1$
		newTask.setPriority(PriorityLevel.P3.toString());
		TasksUiInternal.getTaskList().addTask(newTask);
		TasksUiPlugin.getTaskActivityManager().scheduleNewTask(newTask);

		TaskListView view = TaskListView.getFromActivePerspective();
		AbstractTaskCategory category = getSelectedCategory(view);
		if (view != null && view.getDrilledIntoCategory() != null && view.getDrilledIntoCategory() != category) {
			MessageDialog.openInformation(Display.getCurrent().getActiveShell(), Messages.TasksUiInternal_Create_Task,
					MessageFormat.format(Messages.TasksUiInternal_The_new_task_will_be_added_to_the_X_container,
							UncategorizedTaskContainer.LABEL));
		}
		taskList.addTask(newTask, category);
		return newTask;
	}

	public static AbstractTaskCategory getSelectedCategory(TaskListView view) {
		Object selectedObject = null;
		if (view != null) {
			selectedObject = ((IStructuredSelection) view.getViewer().getSelection()).getFirstElement();
		}
		if (selectedObject instanceof TaskCategory) {
			return (TaskCategory) selectedObject;
		} else if (selectedObject instanceof ITask) {
			ITask task = (ITask) selectedObject;
			AbstractTaskContainer container = TaskCategory.getParentTaskCategory(task);
			if (container instanceof TaskCategory) {
				return (TaskCategory) container;
			} else if (view != null && view.getDrilledIntoCategory() instanceof TaskCategory) {
				return (TaskCategory) view.getDrilledIntoCategory();
			}
		} else if (view != null && view.getDrilledIntoCategory() instanceof TaskCategory) {
			return (TaskCategory) view.getDrilledIntoCategory();
		}
		return TasksUiPlugin.getTaskList().getDefaultCategory();
	}

	public static Set<AbstractTaskContainer> getContainersFromWorkingSet(Set<IWorkingSet> containers) {

		Set<AbstractTaskContainer> allTaskContainersInWorkingSets = new HashSet<AbstractTaskContainer>();
		for (IWorkingSet workingSet : containers) {
			IAdaptable[] elements = workingSet.getElements();
			for (IAdaptable adaptable : elements) {
				if (adaptable instanceof AbstractTaskContainer) {
					allTaskContainersInWorkingSets.add(((AbstractTaskContainer) adaptable));
				}
			}
		}
		return allTaskContainersInWorkingSets;
	}

	/**
	 * @param connectorUi
	 *            - repository connector ui
	 * @param query
	 *            - repository query
	 * @return - true if dialog was opened successfully and not canceled, false otherwise
	 * @since 3.0
	 */
	public static boolean openEditQueryDialog(AbstractRepositoryConnectorUi connectorUi, final IRepositoryQuery query) {
		try {
			TaskRepository repository = TasksUi.getRepositoryManager().getRepository(query.getConnectorKind(),
					query.getRepositoryUrl());
			if (repository == null) {
				return false;
			}

			IWizard wizard = connectorUi.getQueryWizard(repository, query);

			Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
			if (wizard != null && shell != null && !shell.isDisposed()) {
				WizardDialog dialog = new WizardDialog(shell, wizard) {
					private static final String DIALOG_SETTINGS = "EditQueryWizardWizard"; //$NON-NLS-1$

					@Override
					protected IDialogSettings getDialogBoundsSettings() {
						IDialogSettings settings = TasksUiPlugin.getDefault().getDialogSettings();
						String settingsSectionId = DIALOG_SETTINGS + '.' + query.getRepositoryUrl();
						IDialogSettings section = settings.getSection(settingsSectionId);
						if (section == null) {
							section = settings.addNewSection(settingsSectionId);
						}
						return section;
					}
				};
				dialog.create();
				dialog.setBlockOnOpen(true);
				if (dialog.open() == Window.CANCEL) {
					return false;
				} else {
					return true;
				}
			}
		} catch (Exception e) {
			StatusHandler.log(new Status(IStatus.ERROR, TasksUiPlugin.ID_PLUGIN, "Failed to open query dialog", e)); //$NON-NLS-1$
		}
		return false;
	}

	public static ITaskList getTaskList() {
		return TasksUiPlugin.getTaskList();
	}

	public static boolean isAnimationsEnabled() {
		IPreferenceStore store = PlatformUI.getPreferenceStore();
		return store.getBoolean(IWorkbenchPreferenceConstants.ENABLE_ANIMATIONS);
	}

	public static boolean hasValidUrl(ITask task) {
		return isValidUrl(task.getUrl());
	}

	public static boolean isValidUrl(String url) {
		if (url != null && !url.equals("") && !url.equals("http://") && !url.equals("https://")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
			try {
				new URL(url);
				return true;
			} catch (MalformedURLException e) {
				return false;
			}
		}
		return false;
	}

	/**
	 * @deprecated use {@link #closeTaskEditorInAllPages(ITask, boolean)}
	 */
	@Deprecated
	public static void closeEditorInActivePage(ITask task, boolean save) {
		Assert.isNotNull(task);
		IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
		if (window == null) {
			return;
		}
		IWorkbenchPage page = window.getActivePage();
		if (page == null) {
			return;
		}
		TaskRepository taskRepository = TasksUi.getRepositoryManager().getRepository(task.getConnectorKind(),
				task.getRepositoryUrl());
		IEditorInput input = new TaskEditorInput(taskRepository, task);
		IEditorPart editor = page.findEditor(input);
		if (editor != null) {
			page.closeEditor(editor, save);
		}
	}

	public static void closeTaskEditorInAllPages(ITask task, boolean save) {
		Assert.isNotNull(task);
		TaskRepository taskRepository = TasksUi.getRepositoryManager().getRepository(task.getConnectorKind(),
				task.getRepositoryUrl());
		IEditorInput input = new TaskEditorInput(taskRepository, task);
		IWorkbenchWindow[] windows = PlatformUI.getWorkbench().getWorkbenchWindows();
		for (IWorkbenchWindow window : windows) {
			IWorkbenchPage[] pages = window.getPages();
			for (IWorkbenchPage page : pages) {
				IEditorPart editor = page.findEditor(input);
				if (editor != null) {
					page.closeEditor(editor, save);
				}
			}
		}
	}

	public static boolean hasLocalCompletionState(ITask task) {
		TaskRepository taskRepository = TasksUi.getRepositoryManager().getRepository(task.getConnectorKind(),
				task.getRepositoryUrl());
		AbstractRepositoryConnector connector = TasksUi.getRepositoryManager().getRepositoryConnector(
				task.getConnectorKind());
		return connector.hasLocalCompletionState(taskRepository, task);
	}

	/**
	 * Utility method to get the best parenting possible for a dialog. If there is a modal shell create it so as to
	 * avoid two modal dialogs. If not then return the shell of the active workbench window. If neither can be found
	 * return null.
	 * <p>
	 * <b>Note: Applied from patch on bug 99472.</b>
	 * 
	 * @return Shell or <code>null</code>
	 * @deprecated Use {@link WorkbenchUtil#getShell()} instead
	 */
	@Deprecated
	public static Shell getShell() {
		return WorkbenchUtil.getShell();
	}

	public static TaskData createTaskData(TaskRepository taskRepository, ITaskMapping initializationData,
			ITaskMapping selectionData, IProgressMonitor monitor) throws CoreException {
		AbstractRepositoryConnector connector = TasksUi.getRepositoryManager().getRepositoryConnector(
				taskRepository.getConnectorKind());
		AbstractTaskDataHandler taskDataHandler = connector.getTaskDataHandler();
		TaskAttributeMapper mapper = taskDataHandler.getAttributeMapper(taskRepository);
		TaskData taskData = new TaskData(mapper, taskRepository.getConnectorKind(), taskRepository.getRepositoryUrl(),
				""); //$NON-NLS-1$
		boolean result = taskDataHandler.initializeTaskData(taskRepository, taskData, initializationData, monitor);
		if (!result) {
			throw new CoreException(new Status(IStatus.ERROR, TasksUiPlugin.ID_PLUGIN,
					"Initialization of task failed. The provided data is insufficient.")); //$NON-NLS-1$
		}
		if (selectionData != null) {
			connector.getTaskMapping(taskData).merge(selectionData);
		}
		return taskData;
	}

	public static void createAndOpenNewTask(TaskData taskData) throws CoreException {
		ITask task = TasksUiUtil.createOutgoingNewTask(taskData.getConnectorKind(), taskData.getRepositoryUrl());
		AbstractRepositoryConnector connector = TasksUi.getRepositoryManager().getRepositoryConnector(
				taskData.getConnectorKind());
		ITaskMapping mapping = connector.getTaskMapping(taskData);
		String summary = mapping.getSummary();
		if (summary != null && summary.length() > 0) {
			task.setSummary(summary);
		}
		String taskKind = mapping.getTaskKind();
		if (taskKind != null && taskKind.length() > 0) {
			task.setTaskKind(taskKind);
		}
		UnsubmittedTaskContainer unsubmitted = TasksUiPlugin.getTaskList().getUnsubmittedContainer(
				taskData.getRepositoryUrl());
		if (unsubmitted != null) {
			TasksUiPlugin.getTaskList().addTask(task, unsubmitted);
		}
		ITaskDataWorkingCopy workingCopy = TasksUi.getTaskDataManager().createWorkingCopy(task, taskData);
		workingCopy.save(null, null);
		TaskRepository taskRepository = TasksUi.getRepositoryManager().getRepository(taskData.getConnectorKind(),
				taskData.getRepositoryUrl());
		connector.updateNewTaskFromTaskData(taskRepository, task, taskData);
		TaskRepository localTaskRepository = TasksUi.getRepositoryManager().getRepository(task.getConnectorKind(),
				task.getRepositoryUrl());
		TaskEditorInput editorInput = new TaskEditorInput(localTaskRepository, task);
		TasksUiUtil.openEditor(editorInput, TaskEditor.ID_EDITOR, null);
	}

	public static boolean openTask(TaskRepository repository, String taskId, TaskOpenListener listener) {
		Assert.isNotNull(repository);
		Assert.isNotNull(taskId);

		AbstractTask task = (AbstractTask) TasksUiInternal.getTaskList().getTask(repository.getRepositoryUrl(), taskId);
		if (task == null) {
			task = TasksUiPlugin.getTaskList().getTaskByKey(repository.getRepositoryUrl(), taskId);
		}
		if (task != null) {
			// task is known, open in task editor
			TaskOpenEvent event = TasksUiInternal.openTask(task, taskId);
			if (listener != null && event != null) {
				listener.taskOpened(event);
			}
			return event != null;
		} else {
			// search for task
			AbstractRepositoryConnectorUi connectorUi = TasksUiPlugin.getConnectorUi(repository.getConnectorKind());
			if (connectorUi != null) {
				try {
					return TasksUiInternal.openRepositoryTask(connectorUi.getConnectorKind(),
							repository.getRepositoryUrl(), taskId, listener);
				} catch (Exception e) {
					StatusHandler.log(new Status(IStatus.ERROR, TasksUiPlugin.ID_PLUGIN,
							"Internal error while opening repository task", e)); //$NON-NLS-1$
				}
			}
		}
		return false;
	}

	public static TaskOpenEvent openTask(ITask task, String taskId) {
		Assert.isNotNull(task);
		IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
		if (window != null) {
			TaskRepository taskRepository = TasksUi.getRepositoryManager().getRepository(task.getConnectorKind(),
					task.getRepositoryUrl());
			boolean openWithBrowser = !TasksUiPlugin.getDefault()
					.getPreferenceStore()
					.getBoolean(ITasksUiPreferenceConstants.EDITOR_TASKS_RICH);
			if (openWithBrowser) {
				TasksUiUtil.openWithBrowser(taskRepository, task);
				return new TaskOpenEvent(taskRepository, task, taskId, null, true);
			} else {
				IEditorInput editorInput = getTaskEditorInput(taskRepository, task);
				IEditorPart editor = refreshEditorContentsIfOpen(task, editorInput);
				if (editor != null) {
					synchronizeTask(taskRepository, task);
					return new TaskOpenEvent(taskRepository, task, taskId, editor, false);
				} else {
					IWorkbenchPage page = window.getActivePage();
					editor = TasksUiUtil.openEditor(editorInput, getTaskEditorId(task), page);
					if (editor != null) {
						synchronizeTask(taskRepository, task);
						return new TaskOpenEvent(taskRepository, task, taskId, editor, false);
					}
				}
			}
		} else {
			StatusHandler.log(new Status(IStatus.ERROR, TasksUiPlugin.ID_PLUGIN, "Unable to open editor for \"" //$NON-NLS-1$
					+ task.getSummary() + "\": no active workbench window")); //$NON-NLS-1$
		}
		return null;
	}

	private static IEditorInput getTaskEditorInput(TaskRepository repository, ITask task) {
		Assert.isNotNull(task);
		Assert.isNotNull(repository);
		AbstractRepositoryConnectorUi connectorUi = TasksUiPlugin.getConnectorUi(task.getConnectorKind());
		IEditorInput editorInput = connectorUi.getTaskEditorInput(repository, task);
		if (editorInput != null) {
			return editorInput;
		} else {
			return new TaskEditorInput(repository, task);
		}
	}

	private static String getTaskEditorId(final ITask task) {
		String taskEditorId = TaskEditor.ID_EDITOR;
		if (task != null) {
			ITask repositoryTask = task;
			AbstractRepositoryConnectorUi repositoryUi = TasksUiPlugin.getConnectorUi(repositoryTask.getConnectorKind());
			String customTaskEditorId = repositoryUi.getTaskEditorId(repositoryTask);
			if (customTaskEditorId != null) {
				taskEditorId = customTaskEditorId;
			}
		}
		return taskEditorId;
	}

	/**
	 * If task is already open and has incoming, must force refresh in place
	 */
	private static IEditorPart refreshEditorContentsIfOpen(ITask task, IEditorInput editorInput) {
		if (task != null) {
			if (task.getSynchronizationState() == SynchronizationState.INCOMING
					|| task.getSynchronizationState() == SynchronizationState.CONFLICT) {
				for (TaskEditor editor : TasksUiInternal.getActiveRepositoryTaskEditors()) {
					if (editor.getEditorInput().equals(editorInput)) {
						editor.refreshPages();
						editor.getEditorSite().getPage().activate(editor);
						return editor;
					}
				}
			}
		}
		return null;
	}

	private static void synchronizeTask(TaskRepository taskRepository, ITask task) {
		if (task instanceof LocalTask) {
			return;
		}

		AbstractRepositoryConnector connector = TasksUi.getRepositoryManager().getRepositoryConnector(
				task.getConnectorKind());
		if (connector.canSynchronizeTask(taskRepository, task)) {
			TasksUiInternal.synchronizeTaskInBackground(connector, task);
		}
	}

	public static boolean openRepositoryTask(String connectorKind, String repositoryUrl, String id) {
		return openRepositoryTask(connectorKind, repositoryUrl, id, null);
	}

	/**
	 * Only override if task should be opened by a custom editor, default behavior is to open with a rich editor,
	 * falling back to the web browser if not available.
	 * 
	 * @return true if the task was successfully opened
	 */
	public static boolean openRepositoryTask(String connectorKind, String repositoryUrl, String id,
			TaskOpenListener listener) {
		return openRepositoryTask(connectorKind, repositoryUrl, id, listener, 0);
	}

	/**
	 * Only override if task should be opened by a custom editor, default behavior is to open with a rich editor,
	 * falling back to the web browser if not available.
	 * 
	 * @return true if the task was successfully opened
	 */
	public static boolean openRepositoryTask(String connectorKind, String repositoryUrl, String id,
			TaskOpenListener listener, long timestamp) {
		IRepositoryManager repositoryManager = TasksUi.getRepositoryManager();
		AbstractRepositoryConnector connector = repositoryManager.getRepositoryConnector(connectorKind);
		String taskUrl = connector.getTaskUrl(repositoryUrl, id);
		if (taskUrl == null) {
			return false;
		}

		IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
		if (window == null) {
			IWorkbenchWindow[] windows = PlatformUI.getWorkbench().getWorkbenchWindows();
			if (windows != null && windows.length > 0) {
				window = windows[0];
			}
		}
		if (window == null) {
			return false;
		}
		IWorkbenchPage page = window.getActivePage();

		OpenRepositoryTaskJob job = new OpenRepositoryTaskJob(connectorKind, repositoryUrl, id, taskUrl, timestamp,
				page);
		job.setListener(listener);
		job.schedule();

		return true;
	}

	/**
	 * @since 3.0
	 */
	public static boolean openTaskInBackground(ITask task, boolean bringToTop) {
		IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
		if (window != null) {
			IEditorPart activeEditor = null;
			IWorkbenchPart activePart = null;
			IWorkbenchPage activePage = window.getActivePage();
			if (activePage != null) {
				activeEditor = activePage.getActiveEditor();
				activePart = activePage.getActivePart();
			}
			boolean opened = TasksUiUtil.openTask(task);
			if (opened && activePage != null) {
				if (!bringToTop && activeEditor != null) {
					activePage.bringToTop(activeEditor);
				}
				if (activePart != null) {
					activePage.activate(activePart);
				}
			}
			return opened;
		} else {
			StatusHandler.log(new Status(IStatus.ERROR, TasksUiPlugin.ID_PLUGIN, "Unable to open editor for \"" //$NON-NLS-1$
					+ task.getSummary() + "\": no active workbench window")); //$NON-NLS-1$
		}
		return false;
	}

	/**
	 * Returns text masking the &amp;-character from decoration as an accelerator in SWT labels.
	 * 
	 * @deprecated Use {@link CommonUiUtil#toLabel(String)} instead
	 */
	@Deprecated
	public static String escapeLabelText(String text) {
		return CommonUiUtil.toLabel(text);
	}

	public static void preservingSelection(final TreeViewer viewer, Runnable runnable) {
		final ISelection selection = viewer.getSelection();

		runnable.run();

		if (selection != null) {
			ISelection newSelection = viewer.getSelection();
			if ((newSelection == null || newSelection.isEmpty()) && !(selection == null || selection.isEmpty())) {
				// delay execution to ensure that any delayed tree updates such as expand all have been processed and the selection is revealed properly
				Display.getDefault().asyncExec(new Runnable() {
					public void run() {
						viewer.setSelection(selection, true);
					}
				});
			} else if (newSelection instanceof ITreeSelection && !newSelection.isEmpty()) {
				viewer.reveal(((ITreeSelection) newSelection).getFirstElement());
			}
		}
	}

	public static String getFormattedDuration(long duration, boolean includeSeconds) {
		long seconds = duration / 1000;
		long minutes = 0;
		long hours = 0;
		// final long SECOND = 1000;
		final long MIN = 60;
		final long HOUR = MIN * 60;
		String formatted = ""; //$NON-NLS-1$

		String hour = ""; //$NON-NLS-1$
		String min = ""; //$NON-NLS-1$
		String sec = ""; //$NON-NLS-1$
		if (seconds >= HOUR) {
			hours = seconds / HOUR;
			if (hours == 1) {
				hour = hours + Messages.TasksUiInternal__hour_;
			} else if (hours > 1) {
				hour = hours + Messages.TasksUiInternal__hours_;
			}
			seconds -= hours * HOUR;

			minutes = seconds / MIN;
			if (minutes == 1) {
				min = minutes + Messages.TasksUiInternal__minute_;
			} else if (minutes != 1) {
				min = minutes + Messages.TasksUiInternal__minutes_;
			}
			seconds -= minutes * MIN;
			if (seconds == 1) {
				sec = seconds + Messages.TasksUiInternal__second;
			} else if (seconds > 1) {
				sec = seconds + Messages.TasksUiInternal__seconds;
			}
			formatted += hour + min;
			if (includeSeconds) {
				formatted += sec;
			}
		} else if (seconds >= MIN) {
			minutes = seconds / MIN;
			if (minutes == 1) {
				min = minutes + Messages.TasksUiInternal__minute_;
			} else if (minutes != 1) {
				min = minutes + Messages.TasksUiInternal__minutes_;
			}
			seconds -= minutes * MIN;
			if (seconds == 1) {
				sec = seconds + Messages.TasksUiInternal__second;
			} else if (seconds > 1) {
				sec = seconds + Messages.TasksUiInternal__seconds;
			}
			formatted += min;
			if (includeSeconds) {
				formatted += sec;
			}
		} else {
			if (seconds == 1) {
				sec = seconds + Messages.TasksUiInternal__second;
			} else if (seconds > 1) {
				sec = seconds + Messages.TasksUiInternal__seconds;
			}
			if (includeSeconds) {
				formatted += sec;
			}
		}
		return formatted;
	}

	public static AbstractTask getTask(String repositoryUrl, String taskId, String fullUrl) {
		AbstractTask task = null;
		if (repositoryUrl != null && taskId != null) {
			task = (AbstractTask) TasksUiInternal.getTaskList().getTask(repositoryUrl, taskId);
		}
		if (task == null && fullUrl != null) {
			task = TasksUiInternal.getTaskByUrl(fullUrl);
		}
		if (task == null && repositoryUrl != null && taskId != null) {
			task = TasksUiPlugin.getTaskList().getTaskByKey(repositoryUrl, taskId);
		}
		return task;
	}

	/**
	 * Searches for a task whose URL matches
	 * 
	 * @return first task with a matching URL.
	 */
	public static AbstractTask getTaskByUrl(String taskUrl) {
		Collection<AbstractTask> tasks = TasksUiPlugin.getTaskList().getAllTasks();
		for (AbstractTask task : tasks) {
			String currUrl = task.getUrl();
			if (currUrl != null && !currUrl.equals("") && currUrl.equals(taskUrl)) { //$NON-NLS-1$
				return task;
			}
		}
		return null;
	}

	public static boolean isTaskUrl(String taskUrl) {
		Assert.isNotNull(taskUrl);
		List<TaskRepository> repositories = TasksUiPlugin.getRepositoryManager().getAllRepositories();
		for (TaskRepository repository : repositories) {
			if (taskUrl.startsWith(repository.getUrl())) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Cleans text for use as the text of an action to ensure that it is displayed properly.
	 * 
	 * @return the cleaned text
	 * @deprecated use {@link CommonUiUtil#toMenuLabel(String)} instead
	 */
	@Deprecated
	public static String cleanTextForAction(String label) {
		return CommonUiUtil.toMenuLabel(label);
	}

	public static void executeCommand(IServiceLocator serviceLocator, String commandId, String title, Object object,
			Event event) throws NotEnabledException {
		IHandlerService service = (IHandlerService) serviceLocator.getService(IHandlerService.class);
		if (service != null) {
			ICommandService commandService = (ICommandService) serviceLocator.getService(ICommandService.class);
			if (commandService != null) {
				Command command = commandService.getCommand(commandId);
				if (command != null) {
					try {
						if (object != null) {
							IEvaluationContext context = service.createContextSnapshot(false);
							context.addVariable(ISources.ACTIVE_CURRENT_SELECTION_NAME, new StructuredSelection(object));
							service.executeCommandInContext(new ParameterizedCommand(command, null), event, context);
						} else {
							service.executeCommand(commandId, event);
						}
					} catch (ExecutionException e) {
						TasksUiInternal.displayStatus(title, new Status(IStatus.ERROR, TasksUiPlugin.ID_PLUGIN,
								"Command execution failed", e)); //$NON-NLS-1$
					} catch (NotDefinedException e) {
						TasksUiInternal.displayStatus(
								title,
								new Status(IStatus.ERROR, TasksUiPlugin.ID_PLUGIN, NLS.bind(
										"The command with the id ''{0}'' is not defined.", commandId), e)); //$NON-NLS-1$
					} catch (NotHandledException e) {
						TasksUiInternal.displayStatus(
								title,
								new Status(IStatus.ERROR, TasksUiPlugin.ID_PLUGIN, NLS.bind(
										"The command with the id ''{0}'' is not bound.", commandId), e)); //$NON-NLS-1$
					}
				} else {
					TasksUiInternal.displayStatus(
							title,
							new Status(IStatus.ERROR, TasksUiPlugin.ID_PLUGIN, NLS.bind(
									"The command with the id ''{0}'' does not exist.", commandId))); //$NON-NLS-1$
				}
			} else {
				TasksUiInternal.displayStatus(
						title,
						new Status(
								IStatus.ERROR,
								TasksUiPlugin.ID_PLUGIN,
								NLS.bind(
										"Command service is not available to execute command with the id ''{0}''.", commandId), new Exception())); //$NON-NLS-1$
			}
		} else {
			TasksUiInternal.displayStatus(
					title,
					new Status(
							IStatus.ERROR,
							TasksUiPlugin.ID_PLUGIN,
							NLS.bind(
									"Handler service is not available to execute command with the id ''{0}''.", commandId), new Exception())); //$NON-NLS-1$
		}
	}

	public static void activateTaskThroughCommand(ITask task) {
		try {
			TasksUiInternal.executeCommand(
					PlatformUI.getWorkbench(),
					"org.eclipse.mylyn.tasks.ui.command.activateSelectedTask", Messages.TasksUiInternal_Activate_Task, task, null); //$NON-NLS-1$
		} catch (NotEnabledException e) {
			StatusHandler.log(new Status(IStatus.ERROR, TasksUiPlugin.ID_PLUGIN, NLS.bind(
					"Failed to activate task ''{0}''.", task.getSummary()), e)); //$NON-NLS-1$
		}
	}

	public static long getActiveTime(ITask task) {
		if (MonitorUiPlugin.getDefault().isActivityTrackingEnabled()) {
			return TasksUiPlugin.getTaskActivityManager().getElapsedTime(task);
		}
		return 0;
	}

	public static String getTaskPrefix(String connectorKind) {
		AbstractRepositoryConnector connector = TasksUiPlugin.getConnector(connectorKind);
		if (connector != null) {
			String prefix = connector.getTaskIdPrefix();
			// work around short prefixes which are not separated by space, e.g. "#" for Trac
			return (prefix.length() > 1) ? prefix + " " : prefix; //$NON-NLS-1$
		}
		return ""; //$NON-NLS-1$
	}

	public static void displayFrameworkError(String message) {
		RuntimeException exception = new RuntimeException(message);
		if (!CoreUtil.TEST_MODE) {
			StatusAdapter status = new StatusAdapter(new Status(IStatus.ERROR, TasksUiPlugin.ID_PLUGIN, message,
					exception));
			status.setProperty(IStatusAdapterConstants.TITLE_PROPERTY, "Framework Error"); //$NON-NLS-1$
			StatusManager.getManager().handle(status, StatusManager.SHOW | StatusManager.LOG | StatusManager.BLOCK);
		}
		throw exception;
	}

	public static String getAuthenticatedUrl(TaskRepository repository, IRepositoryElement element) {
		Assert.isNotNull(repository);
		Assert.isNotNull(element);
		IRepositoryManager repositoryManager = TasksUi.getRepositoryManager();
		AbstractRepositoryConnector connector = repositoryManager.getRepositoryConnector(repository.getConnectorKind());
		if (connector != null) {
			URL authenticatedUrl = connector.getAuthenticatedUrl(repository, element);
			if (authenticatedUrl != null) {
				return authenticatedUrl.toString();
			} else {
				String url = element.getUrl();
				if (TasksUiInternal.isValidUrl(url)) {
					return url;
				}
			}
		}
		return null;
	}

	public static TaskRepository getRepository(IRepositoryElement element) {
		IRepositoryManager repositoryManager = TasksUi.getRepositoryManager();
		TaskRepository repository = null;
		if (element instanceof ITask) {
			repository = repositoryManager.getRepository(((ITask) element).getConnectorKind(),
					((ITask) element).getRepositoryUrl());
		} else if (element instanceof IRepositoryQuery) {
			repository = repositoryManager.getRepository(((IRepositoryQuery) element).getConnectorKind(),
					((IRepositoryQuery) element).getRepositoryUrl());
		}
		return repository;
	}

	/**
	 * Returns all the tasks in the given selection, or an empty list if the selection contains no tasks.
	 */
	public static List<ITask> getTasksFromSelection(ISelection selection) {
		Assert.isNotNull(selection);
		if (selection.isEmpty()) {
			return Collections.emptyList();
		}

		List<ITask> tasks = new ArrayList<ITask>();
		if (selection instanceof IStructuredSelection) {
			for (Object element : ((IStructuredSelection) selection).toList()) {
				ITask task = null;
				if (element instanceof ITask) {
					task = (ITask) element;
				} else if (element instanceof IAdaptable) {
					IAdaptable adaptable = (IAdaptable) element;
					task = (ITask) adaptable.getAdapter(ITask.class);
				}
				if (task != null) {
					tasks.add(task);
				}
			}
		}
		return tasks;
	}

	public static boolean shouldShowIncoming(ITask task) {
		SynchronizationState state = task.getSynchronizationState();
		if ((state == SynchronizationState.INCOMING && !Boolean.valueOf(task.getAttribute(ITasksCoreConstants.ATTRIBUTE_TASK_SUPPRESS_INCOMING)))
				|| state == SynchronizationState.INCOMING_NEW || state == SynchronizationState.CONFLICT) {
			return true;
		}
		return false;
	}

	public static TaskData computeTaskData(TaskData taskData, TaskHistory history, String revisionId,
			IProgressMonitor monitor) throws CoreException {
		TaskData newTaskData = TaskDataState.createCopy(taskData);
		List<TaskRevision> revisions = history.getRevisions();
		Collections.reverse(revisions);
		TaskRevision lastRevision = null;
		for (TaskRevision revision : revisions) {
			for (Change change : revision.getChanges()) {
				TaskAttribute attribute = newTaskData.getRoot().getAttribute(change.getAttributeId());
				if (attribute != null) {
					attribute.setValue(change.getRemoved());
				}
			}
			// only apply changes up to this revision
			if (revisionId.equals(revision.getId())) {
				lastRevision = revision;
				break;
			}
		}

		if (lastRevision != null && lastRevision.getDate() != null) {
			// remove attachments and comments that are newer than lastRevision
			List<TaskAttribute> attributes = new ArrayList<TaskAttribute>(newTaskData.getRoot()
					.getAttributes()
					.values());
			for (TaskAttribute attribute : attributes) {
				if (TaskAttribute.TYPE_COMMENT.equals(attribute.getMetaData().getType())) {
					TaskCommentMapper mapper = TaskCommentMapper.createFrom(attribute);
					if (mapper.getCreationDate() != null && mapper.getCreationDate().after(lastRevision.getDate())) {
						newTaskData.getRoot().removeAttribute(attribute.getId());
					}
				} else if (TaskAttribute.TYPE_ATTACHMENT.equals(attribute.getMetaData().getType())) {
					TaskAttachmentMapper mapper = TaskAttachmentMapper.createFrom(attribute);
					if (mapper.getCreationDate() != null && mapper.getCreationDate().after(lastRevision.getDate())) {
						newTaskData.getRoot().removeAttribute(attribute.getId());
					}
				}
			}
		}

		return newTaskData;
	}

	public static boolean canGetTaskHistory(ITask task) {
		TaskRepository repository = TasksUi.getRepositoryManager().getRepository(task.getConnectorKind(),
				task.getRepositoryUrl());
		AbstractRepositoryConnector connector = TasksUi.getRepositoryManager().getRepositoryConnector(
				repository.getConnectorKind());
		return connector.canGetTaskHistory(repository, task);
	}

	public static Account getAccount(TaskAttribute attribute) {
		Account account;
		if (TaskAttribute.TYPE_PERSON.equals(attribute.getMetaData().getType())) {
			IRepositoryPerson person = attribute.getTaskData().getAttributeMapper().getRepositoryPerson(attribute);
			account = Account.id(person.getPersonId()).name(person.getName());
		} else {
			account = Account.id(attribute.getValue());
		}
		TaskRepository repository = attribute.getTaskData().getAttributeMapper().getTaskRepository();
		return account.kind(repository.getConnectorKind()).url(repository.getRepositoryUrl());
	}

}
