org.eclipse.mylyn.internal.tasks.ui.TasksUiPlugin.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.mylyn.internal.tasks.ui.TasksUiPlugin.java

Source

/*******************************************************************************
 * Copyright (c) 2004, 2013 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
 *******************************************************************************/

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

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.atomic.AtomicInteger;

import org.eclipse.core.net.proxy.IProxyChangeEvent;
import org.eclipse.core.net.proxy.IProxyChangeListener;
import org.eclipse.core.net.proxy.IProxyService;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ISaveContext;
import org.eclipse.core.resources.ISaveParticipant;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.ISafeRunnable;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.util.SafeRunnable;
import org.eclipse.mylyn.commons.core.StatusHandler;
import org.eclipse.mylyn.commons.identity.core.IIdentityService;
import org.eclipse.mylyn.commons.net.WebUtil;
import org.eclipse.mylyn.commons.notifications.core.NotificationEnvironment;
import org.eclipse.mylyn.commons.notifications.feed.ServiceMessageManager;
import org.eclipse.mylyn.commons.notifications.ui.AbstractUiNotification;
import org.eclipse.mylyn.commons.ui.compatibility.CommonColors;
import org.eclipse.mylyn.commons.ui.compatibility.CommonFonts;
import org.eclipse.mylyn.commons.workbench.TaskBarManager;
import org.eclipse.mylyn.internal.commons.notifications.feed.ServiceMessage;
import org.eclipse.mylyn.internal.discovery.ui.DiscoveryUi;
import org.eclipse.mylyn.internal.monitor.ui.MonitorUiPlugin;
import org.eclipse.mylyn.internal.tasks.core.AbstractSearchHandler;
import org.eclipse.mylyn.internal.tasks.core.AbstractTask;
import org.eclipse.mylyn.internal.tasks.core.IRepositoryModelListener;
import org.eclipse.mylyn.internal.tasks.core.ITasksCoreConstants;
import org.eclipse.mylyn.internal.tasks.core.LocalRepositoryConnector;
import org.eclipse.mylyn.internal.tasks.core.RepositoryExternalizationParticipant;
import org.eclipse.mylyn.internal.tasks.core.RepositoryModel;
import org.eclipse.mylyn.internal.tasks.core.RepositoryTemplateManager;
import org.eclipse.mylyn.internal.tasks.core.TaskActivityManager;
import org.eclipse.mylyn.internal.tasks.core.TaskActivityUtil;
import org.eclipse.mylyn.internal.tasks.core.TaskList;
import org.eclipse.mylyn.internal.tasks.core.TaskRepositoryDelta;
import org.eclipse.mylyn.internal.tasks.core.TaskRepositoryDelta.Type;
import org.eclipse.mylyn.internal.tasks.core.TaskRepositoryManager;
import org.eclipse.mylyn.internal.tasks.core.data.SynchronizationManger;
import org.eclipse.mylyn.internal.tasks.core.data.TaskDataManager;
import org.eclipse.mylyn.internal.tasks.core.data.TaskDataStore;
import org.eclipse.mylyn.internal.tasks.core.externalization.ExternalizationManager;
import org.eclipse.mylyn.internal.tasks.core.externalization.IExternalizationParticipant;
import org.eclipse.mylyn.internal.tasks.core.externalization.TaskActivationExternalizationParticipant;
import org.eclipse.mylyn.internal.tasks.core.externalization.TaskListExternalizationParticipant;
import org.eclipse.mylyn.internal.tasks.core.externalization.TaskListExternalizer;
import org.eclipse.mylyn.internal.tasks.core.util.RepositoryConnectorLoader;
import org.eclipse.mylyn.internal.tasks.core.util.TaskRepositoryKeyringMigrator;
import org.eclipse.mylyn.internal.tasks.core.util.TaskRepositorySecureStoreMigrator;
import org.eclipse.mylyn.internal.tasks.core.util.TasksCoreExtensionReader;
import org.eclipse.mylyn.internal.tasks.ui.actions.ActivateTaskDialogAction;
import org.eclipse.mylyn.internal.tasks.ui.actions.NewTaskAction;
import org.eclipse.mylyn.internal.tasks.ui.notifications.TaskListNotificationReminder;
import org.eclipse.mylyn.internal.tasks.ui.notifications.TaskListNotifier;
import org.eclipse.mylyn.internal.tasks.ui.util.TasksUiExtensionReader;
import org.eclipse.mylyn.internal.tasks.ui.views.TaskListView;
import org.eclipse.mylyn.internal.tasks.ui.views.TaskRepositoriesView;
import org.eclipse.mylyn.tasks.core.AbstractDuplicateDetector;
import org.eclipse.mylyn.tasks.core.AbstractRepositoryConnector;
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.ITaskContainer;
import org.eclipse.mylyn.tasks.core.RepositoryTemplate;
import org.eclipse.mylyn.tasks.core.TaskRepository;
import org.eclipse.mylyn.tasks.core.activity.AbstractTaskActivityMonitor;
import org.eclipse.mylyn.tasks.core.context.AbstractTaskContextStore;
import org.eclipse.mylyn.tasks.ui.AbstractRepositoryConnectorUi;
import org.eclipse.mylyn.tasks.ui.AbstractTaskRepositoryLinkProvider;
import org.eclipse.mylyn.tasks.ui.TasksUi;
import org.eclipse.mylyn.tasks.ui.TasksUiImages;
import org.eclipse.mylyn.tasks.ui.editors.AbstractTaskEditorPageFactory;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IStartup;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.forms.FormColors;
import org.eclipse.ui.plugin.AbstractUIPlugin;
import org.eclipse.ui.progress.IProgressService;
import org.eclipse.ui.progress.UIJob;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.util.tracker.ServiceTracker;

/**
 * Main entry point for the Tasks UI.
 *
 * @author Mik Kersten
 * @since 3.0
 */
public class TasksUiPlugin extends AbstractUIPlugin {

    private static final int DELAY_QUERY_REFRESH_ON_STARTUP = 20 * 1000;

    private static final int DEFAULT_LINK_PROVIDER_TIMEOUT = 5 * 1000;

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

    private static final String DIRECTORY_METADATA = ".metadata"; //$NON-NLS-1$

    private static final String NAME_DATA_DIR = ".mylyn"; //$NON-NLS-1$

    private static final char DEFAULT_PATH_SEPARATOR = '/';

    private static final int NOTIFICATION_DELAY = 5000;

    private static final String PREF_MIGRATED_TASK_REPOSITORIES_FROM_SECURE_STORE = "migrated.task.repositories.secure.store"; //$NON-NLS-1$

    private static final String PREF_MIGRATED_TASK_REPOSITORIES_FROM_KEYRING = "migrated.task.repositories.keyring"; //$NON-NLS-1$

    private static final String PROP_FORCE_CREDENTIALS_MIGRATION = "org.eclipse.mylyn.tasks.force.credentials.migration"; //$NON-NLS-1$

    private static TasksUiPlugin INSTANCE;

    private static ExternalizationManager externalizationManager;

    private static TaskActivityManager taskActivityManager;

    private static TaskRepositoryManager repositoryManager;

    private static TaskListSynchronizationScheduler synchronizationScheduler;

    private static TaskDataManager taskDataManager;

    private static Map<String, AbstractRepositoryConnectorUi> repositoryConnectorUiMap = new HashMap<String, AbstractRepositoryConnectorUi>();

    private TaskListNotificationManager taskListNotificationManager;

    private TaskListBackupManager taskListBackupManager;

    private RepositoryTemplateManager repositoryTemplateManager;

    private ServiceMessageManager serviceMessageManager;

    private final Set<AbstractTaskEditorPageFactory> taskEditorPageFactories = new HashSet<AbstractTaskEditorPageFactory>();

    private final TreeSet<AbstractTaskRepositoryLinkProvider> repositoryLinkProviders = new TreeSet<AbstractTaskRepositoryLinkProvider>(
            new OrderComparator());

    private TaskListExternalizer taskListExternalizer;

    private final Map<String, Image> brandingIcons = new HashMap<String, Image>();

    private final Map<String, ImageDescriptor> overlayIcons = new HashMap<String, ImageDescriptor>();

    private final Set<AbstractDuplicateDetector> duplicateDetectors = new HashSet<AbstractDuplicateDetector>();

    private ISaveParticipant saveParticipant;

    private TaskJobFactory taskJobFactory;

    // shared colors for all forms
    private FormColors formColors;

    private static AbstractTaskContextStore contextStore;

    private final List<AbstractSearchHandler> searchHandlers = new ArrayList<AbstractSearchHandler>();

    private static final boolean DEBUG_HTTPCLIENT = "true" //$NON-NLS-1$
            .equalsIgnoreCase(Platform.getDebugOption("org.eclipse.mylyn.tasks.ui/debug/httpclient")); //$NON-NLS-1$

    // XXX reconsider if this is necessary
    public static class TasksUiStartup implements IStartup {

        public void earlyStartup() {
            // ignore
        }
    }

    private static final class OrderComparator implements Comparator<AbstractTaskRepositoryLinkProvider> {
        public int compare(AbstractTaskRepositoryLinkProvider p1, AbstractTaskRepositoryLinkProvider p2) {
            return p1.getOrder() - p2.getOrder();
        }
    }

    public enum TaskListSaveMode {
        ONE_HOUR, THREE_HOURS, DAY;
        @Override
        public String toString() {
            switch (this) {
            case ONE_HOUR:
                return "1 hour"; //$NON-NLS-1$
            case THREE_HOURS:
                return "3 hours"; //$NON-NLS-1$
            case DAY:
                return "1 day"; //$NON-NLS-1$
            default:
                return "3 hours"; //$NON-NLS-1$
            }
        }

        public static TaskListSaveMode fromString(String string) {
            if (string == null) {
                return null;
            }
            if (string.equals("1 hour")) { //$NON-NLS-1$
                return ONE_HOUR;
            }
            if (string.equals("3 hours")) { //$NON-NLS-1$
                return THREE_HOURS;
            }
            if (string.equals("1 day")) { //$NON-NLS-1$
                return DAY;
            }
            return null;
        }

        public static long fromStringToLong(String string) {
            long hour = 3600 * 1000;
            switch (fromString(string)) {
            case ONE_HOUR:
                return hour;
            case THREE_HOURS:
                return hour * 3;
            case DAY:
                return hour * 24;
            default:
                return hour * 3;
            }
        }
    }

    public enum ReportOpenMode {
        EDITOR, INTERNAL_BROWSER, EXTERNAL_BROWSER;
    }

    private static ITaskListNotificationProvider REMINDER_NOTIFICATION_PROVIDER = new ITaskListNotificationProvider() {

        public Set<AbstractUiNotification> getNotifications() {
            Collection<AbstractTask> allTasks = TasksUiPlugin.getTaskList().getAllTasks();
            Set<AbstractUiNotification> reminders = new HashSet<AbstractUiNotification>();
            for (AbstractTask task : allTasks) {
                if (TasksUiPlugin.getTaskActivityManager().isPastReminder(task) && !task.isReminded()) {
                    reminders.add(new TaskListNotificationReminder(task));
                    task.setReminded(true);
                }
            }
            return reminders;
        }
    };

    private final org.eclipse.jface.util.IPropertyChangeListener PROPERTY_LISTENER = new org.eclipse.jface.util.IPropertyChangeListener() {

        public void propertyChange(org.eclipse.jface.util.PropertyChangeEvent event) {
            if (event.getProperty().equals(ITasksUiPreferenceConstants.PLANNING_ENDHOUR)
                    || event.getProperty().equals(ITasksUiPreferenceConstants.WEEK_START_DAY)) {
                updateTaskActivityManager();
            }

            if (event.getProperty().equals(ITasksUiPreferenceConstants.REPOSITORY_SYNCH_SCHEDULE_ENABLED) || event
                    .getProperty().equals(ITasksUiPreferenceConstants.REPOSITORY_SYNCH_SCHEDULE_MILISECONDS)) {
                updateSynchronizationScheduler(false);
            }

            if (event.getProperty().equals(ITasksUiPreferenceConstants.SERVICE_MESSAGES_ENABLED)) {
                if (getPreferenceStore().getBoolean(ITasksUiPreferenceConstants.SERVICE_MESSAGES_ENABLED)) {
                    serviceMessageManager.start();
                } else {
                    serviceMessageManager.stop();
                }
            }
        }
    };

    private AbstractTaskActivityMonitor taskActivityMonitor;

    @SuppressWarnings("rawtypes")
    private ServiceReference proxyServiceReference;

    private IProxyChangeListener proxyChangeListener;

    private static TaskListExternalizationParticipant taskListExternalizationParticipant;

    private final Set<IRepositoryModelListener> listeners = new HashSet<IRepositoryModelListener>();

    private File activationHistoryFile;

    private static TaskList taskList;

    private static RepositoryModel repositoryModel;

    private static TasksUiFactory uiFactory;

    @Deprecated
    public static String LABEL_VIEW_REPOSITORIES = Messages.TasksUiPlugin_Task_Repositories;

    private final AtomicInteger initializationCount = new AtomicInteger();

    private SynchronizationManger synchronizationManger;

    private RepositoryConnectorLoader connectorLoader;

    private class TasksUiInitializationJob extends UIJob {

        public TasksUiInitializationJob() {
            super(Messages.TasksUiPlugin_Initializing_Task_List);
            setSystem(true);
        }

        @SuppressWarnings("restriction")
        @Override
        public IStatus runInUIThread(IProgressMonitor monitor) {
            // NOTE: failure in one part of the initialization should
            // not prevent others
            monitor.beginTask("Initializing Task List", 5); //$NON-NLS-1$
            try {
                // Needs to run after workbench is loaded because it
                // relies on images.
                TasksUiExtensionReader.initWorkbenchUiExtensions(connectorLoader.getBlackList());

                if (externalizationManager.getLoadStatus() != null) {
                    // XXX: recovery from task list load failure  (Rendered in task list)
                }

                List<String> commandLineArgs = Arrays.asList(Platform.getCommandLineArgs());
                boolean activateTask = !commandLineArgs.contains(ITasksCoreConstants.COMMAND_LINE_NO_ACTIVATE_TASK);
                if (activateTask) {
                    try {
                        Field field = org.eclipse.core.internal.resources.Workspace.class
                                .getDeclaredField("crashed"); //$NON-NLS-1$
                        field.setAccessible(true);
                        Object value = field.get(ResourcesPlugin.getWorkspace());
                        if (value instanceof Boolean) {
                            activateTask = !(Boolean) value;
                        }
                    } catch (Throwable t) {
                        t.printStackTrace();
                        // ignore
                    }
                }
                // Needs to happen asynchronously to avoid bug 159706
                for (AbstractTask task : taskList.getAllTasks()) {
                    if (task.isActive()) {
                        // the externalizer might set multiple tasks active
                        task.setActive(false);
                        if (activateTask) {
                            // make sure only one task is activated
                            taskActivityManager.activateTask(task);
                            activateTask = false;
                        }
                    }
                }

                //taskActivityMonitor.reloadActivityTime();
            } catch (Throwable t) {
                StatusHandler.log(new Status(IStatus.ERROR, TasksUiPlugin.ID_PLUGIN,
                        "Could not initialize task activity", t)); //$NON-NLS-1$
            }
            monitor.worked(1);

            initializeNotificationsAndSynchronization();

            addSystemTaskBarActions();

            try {
                getPreferenceStore().addPropertyChangeListener(PROPERTY_LISTENER);

                // TODO: get rid of this, hack to make decorators show
                // up on startup
                TaskRepositoriesView repositoriesView = TaskRepositoriesView.getFromActivePerspective();
                if (repositoriesView != null) {
                    repositoriesView.getViewer().refresh();
                }
            } catch (Throwable t) {
                StatusHandler.log(new Status(IStatus.ERROR, TasksUiPlugin.ID_PLUGIN,
                        "Could not finish Tasks UI initialization", t)); //$NON-NLS-1$
            } finally {
                monitor.done();
            }
            hideNonMatchingSubtasks();
            return new Status(IStatus.OK, TasksUiPlugin.ID_PLUGIN, IStatus.OK, "", null); //$NON-NLS-1$
        }

        /**
         * hide non-matching subtasks, or display a message about it if this is not a fresh installation
         */
        protected void hideNonMatchingSubtasks() {
            final String HIDE_SUBTASKS = "hide-subtasks"; //$NON-NLS-1$
            if (!getPreferenceStore().getBoolean(ITasksUiPreferenceConstants.FILTER_NON_MATCHING)
                    && !getPreferenceStore()
                            .getBoolean(ITasksUiPreferenceConstants.ENCOURAGED_FILTER_NON_MATCHING)) {
                if (taskList.getQueries().isEmpty()) {
                    getPreferenceStore().setValue(ITasksUiPreferenceConstants.FILTER_NON_MATCHING, true);
                } else {
                    TaskListView view = TaskListView.getFromActivePerspective();
                    if (view != null && view.getServiceMessageControl() != null) {
                        ServiceMessage message = new ServiceMessage("") { //$NON-NLS-1$
                            @Override
                            public boolean openLink(String link) {
                                if (HIDE_SUBTASKS.equals(link)) {
                                    getPreferenceStore().setValue(ITasksUiPreferenceConstants.FILTER_NON_MATCHING,
                                            true);
                                    savePluginPreferences();
                                    return true;
                                }
                                return false;
                            }
                        };
                        message.setId("hide.nonmatching.subtasks"); //$NON-NLS-1$
                        message.setImage(Dialog.DLG_IMG_MESSAGE_INFO);
                        message.setTitle(Messages.TasksUiPlugin_Hide_Irrelevant_Subtasks);
                        message.setDescription(
                                NLS.bind(Messages.TasksUiPlugin_Hide_Irrelevant_Subtasks_Message, HIDE_SUBTASKS));
                        view.getServiceMessageControl().setMessage(message);
                    }
                }
                // never do this again
                getPreferenceStore().setValue(ITasksUiPreferenceConstants.ENCOURAGED_FILTER_NON_MATCHING, true);
                savePluginPreferences();
            }
        }
    }

    public TasksUiPlugin() {
        super();
        INSTANCE = this;
    }

    private void addSystemTaskBarActions() {
        MenuManager taskBarMenuManager = TaskBarManager.getTaskBarMenuManager();
        if (taskBarMenuManager != null) {
            NewTaskAction newTaskAction = new NewTaskAction(Messages.TasksUiPlugin_New_Task, true);
            taskBarMenuManager.add(newTaskAction);

            Action activateTaskAction = new Action() {
                @Override
                public void run() {
                    ActivateTaskDialogAction activateTaskDialogAction = new ActivateTaskDialogAction();
                    IWorkbenchWindow window = getWorkbench().getActiveWorkbenchWindow();
                    if (window == null && getWorkbench().getWorkbenchWindows().length > 0) {
                        window = getWorkbench().getWorkbenchWindows()[0];
                    }
                    activateTaskDialogAction.init(window);
                    activateTaskDialogAction.run(null);
                }
            };
            activateTaskAction.setImageDescriptor(TasksUiImages.CONTEXT_ACTIVE_CENTERED);
            activateTaskAction.setText(Messages.TasksUiPlugin_Activate_Task);
            taskBarMenuManager.add(activateTaskAction);
            taskBarMenuManager.update(true);
        }
    }

    private void updateSynchronizationScheduler(boolean initial) {
        if (synchronizationScheduler == null) {
            return;
        }

        boolean enabled = TasksUiPlugin.getDefault().getPreferenceStore()
                .getBoolean(ITasksUiPreferenceConstants.REPOSITORY_SYNCH_SCHEDULE_ENABLED);
        if (enabled) {
            long interval = TasksUiPlugin.getDefault().getPreferenceStore()
                    .getLong(ITasksUiPreferenceConstants.REPOSITORY_SYNCH_SCHEDULE_MILISECONDS);
            if (initial) {
                synchronizationScheduler.setInterval(DELAY_QUERY_REFRESH_ON_STARTUP, interval);
            } else {
                synchronizationScheduler.setInterval(interval);
            }
        } else {
            synchronizationScheduler.setInterval(0);
        }
    }

    @SuppressWarnings("restriction")
    @Override
    public void start(BundleContext context) throws Exception {
        super.start(context);
        // NOTE: startup order is very sensitive
        try {
            // initialize framework and settings
            if (DEBUG_HTTPCLIENT) {
                // do this before anything else, once commons logging is initialized and an instance
                // of Log has been created it's too late
                initHttpLogging();
            }
            WebUtil.init();
            initializePreferences(getPreferenceStore());

            // initialize CommonFonts from UI thread: bug 240076
            if (CommonFonts.BOLD == null) {
                // ignore
            }

            File dataDir = new File(getDataDirectory());
            dataDir.mkdirs();

            // create data model
            externalizationManager = new ExternalizationManager(getDataDirectory());

            repositoryManager = new TaskRepositoryManager();
            IExternalizationParticipant repositoryParticipant = new RepositoryExternalizationParticipant(
                    externalizationManager, repositoryManager);
            externalizationManager.addParticipant(repositoryParticipant);

            taskList = new TaskList();
            repositoryModel = new RepositoryModel(taskList, repositoryManager);
            taskListExternalizer = new TaskListExternalizer(repositoryModel, repositoryManager);

            taskListExternalizationParticipant = new TaskListExternalizationParticipant(repositoryModel, taskList,
                    taskListExternalizer, externalizationManager, repositoryManager);
            //externalizationManager.load(taskListSaveParticipant);
            externalizationManager.addParticipant(taskListExternalizationParticipant);
            taskList.addChangeListener(taskListExternalizationParticipant);

            taskActivityManager = new TaskActivityManager(repositoryManager, taskList);

            taskActivityManager.addActivationListener(taskListExternalizationParticipant);

            // initialize
            updateTaskActivityManager();

            proxyServiceReference = context.getServiceReference(IProxyService.class.getName());
            if (proxyServiceReference != null) {
                @SuppressWarnings("unchecked")
                IProxyService proxyService = (IProxyService) context.getService(proxyServiceReference);
                if (proxyService != null) {
                    proxyChangeListener = new IProxyChangeListener() {
                        public void proxyInfoChanged(IProxyChangeEvent event) {
                            List<TaskRepository> repositories = repositoryManager.getAllRepositories();
                            for (TaskRepository repository : repositories) {
                                if (repository.isDefaultProxyEnabled()) {
                                    repositoryManager.notifyRepositorySettingsChanged(repository,
                                            new TaskRepositoryDelta(Type.PROYX));
                                }
                            }
                        }
                    };
                    proxyService.addProxyChangeListener(proxyChangeListener);
                }
            }

            repositoryTemplateManager = new RepositoryTemplateManager();

            // NOTE: initializing extensions in start(..) has caused race
            // conditions previously
            connectorLoader = new RepositoryConnectorLoader();
            connectorLoader.registerConnectors(repositoryManager, taskListExternalizer);
            connectorLoader.registerTemplates(repositoryManager, repositoryTemplateManager);
            TasksUiExtensionReader.initStartupExtensions(connectorLoader.getBlackList());

            // instantiate taskDataManager
            TaskDataStore taskDataStore = new TaskDataStore(repositoryManager);
            synchronizationManger = new SynchronizationManger(repositoryModel);
            taskDataManager = new TaskDataManager(taskDataStore, repositoryManager, taskList, taskActivityManager,
                    synchronizationManger);

            taskJobFactory = new TaskJobFactory(taskList, taskDataManager, repositoryManager, repositoryModel);

            taskActivityMonitor = TasksCoreExtensionReader.loadTaskActivityMonitor();
            taskActivityMonitor.start(taskActivityManager);

            saveParticipant = new ISaveParticipant() {

                public void doneSaving(ISaveContext context) {
                }

                public void prepareToSave(ISaveContext context) throws CoreException {
                }

                public void rollback(ISaveContext context) {
                }

                public void saving(ISaveContext context) throws CoreException {
                    if (context.getKind() == ISaveContext.FULL_SAVE) {
                        externalizationManager.stop();
                    }
                }
            };
            ResourcesPlugin.getWorkspace().addSaveParticipant(this, saveParticipant);

            // initialize externalization for task activation history
            IPath stateLocation = Platform.getStateLocation(getBundle());
            activationHistoryFile = stateLocation.append("TaskActivationHistory.xml").toFile(); //$NON-NLS-1$
            TaskActivationExternalizationParticipant taskActivationExternalizationParticipant = new TaskActivationExternalizationParticipant(
                    externalizationManager, taskList, taskActivityManager.getTaskActivationHistory(),
                    activationHistoryFile);
            taskActivityManager.addActivationListener(taskActivationExternalizationParticipant);
            externalizationManager.addParticipant(taskActivationExternalizationParticipant);

            // initialize managers
            initializeDataSources();

            migrateCredentials(repositoryManager.getAllRepositories());

            // make this available early for clients that are not initialized through tasks ui but need access
            taskListNotificationManager = new TaskListNotificationManager();

            String lastMod = getPreferenceStore()
                    .getString(ITasksUiPreferenceConstants.LAST_SERVICE_MESSAGE_LAST_MODIFIED);
            String etag = getPreferenceStore().getString(ITasksUiPreferenceConstants.LAST_SERVICE_MESSAGE_ETAG);
            String serviceMessageUrl = getPreferenceStore()
                    .getString(ITasksUiPreferenceConstants.SERVICE_MESSAGE_URL);

            Long checktime = getPreferenceStore()
                    .getLong(ITasksUiPreferenceConstants.LAST_SERVICE_MESSAGE_CHECKTIME);

            serviceMessageManager = new ServiceMessageManager(serviceMessageUrl, lastMod, etag, checktime,
                    new NotificationEnvironment() {
                        private Set<String> installedFeatures;

                        @Override
                        public Set<String> getInstalledFeatures(IProgressMonitor monitor) {
                            if (installedFeatures == null) {
                                installedFeatures = DiscoveryUi.createInstallJob().getInstalledFeatures(monitor);
                            }
                            return installedFeatures;
                        }
                    });

            // Disabled for initial 3.4 release as per bug#263528
            if (getPreferenceStore().getBoolean(ITasksUiPreferenceConstants.SERVICE_MESSAGES_ENABLED)) {
                serviceMessageManager.start();
            }

            // trigger lazy initialization
            new TasksUiInitializationJob().schedule();
        } catch (Exception e) {
            StatusHandler
                    .log(new Status(IStatus.ERROR, TasksUiPlugin.ID_PLUGIN, "Task list initialization failed", e)); //$NON-NLS-1$
        }
    }

    /**
     * Migrate credentials from the old secure store location, and from the deprecated keyring if the compatibility.auth
     * bundle is present.
     */
    @SuppressWarnings("deprecation")
    private void migrateCredentials(final List<TaskRepository> repositories) {
        final boolean force = Boolean.parseBoolean(System.getProperty(PROP_FORCE_CREDENTIALS_MIGRATION));
        final boolean migrateFromSecureStore = force
                || !getPluginPreferences().getBoolean(PREF_MIGRATED_TASK_REPOSITORIES_FROM_SECURE_STORE);
        final boolean migrateFromKeyring = (force
                || !getPluginPreferences().getBoolean(PREF_MIGRATED_TASK_REPOSITORIES_FROM_KEYRING))
                && isKeyringInstalled();
        if (!migrateFromSecureStore && !migrateFromKeyring) {
            return;
        }
        // Use a UI job to ensure the UI has loaded
        new UIJob("Credential Migration UI Job") { //$NON-NLS-1$
            @Override
            public IStatus runInUIThread(IProgressMonitor monitor) {
                // use a Job to ensure we do not access the secure store on the UI thread
                new Job("Credential Migration") { //$NON-NLS-1$
                    @Override
                    protected IStatus run(IProgressMonitor monitor) {
                        if (force) {
                            StatusHandler.log(new Status(IStatus.INFO, ITasksCoreConstants.ID_PLUGIN, NLS.bind(
                                    "Forcing task repository credential migration because system property {0} is set.", //$NON-NLS-1$
                                    PROP_FORCE_CREDENTIALS_MIGRATION)));
                        }
                        if (migrateFromSecureStore) {
                            new TaskRepositorySecureStoreMigrator().migrateCredentials(repositories);
                        }
                        if (migrateFromKeyring) {
                            new TaskRepositoryKeyringMigrator("", "Basic").migrateCredentials(repositories); //$NON-NLS-1$ //$NON-NLS-2$
                        }
                        return Status.OK_STATUS;
                    }

                }.schedule();
                return Status.OK_STATUS;
            }
        }.schedule();
    }

    private boolean isKeyringInstalled() {
        return Platform.getBundle("org.eclipse.core.runtime.compatibility.auth") != null; //$NON-NLS-1$
    }

    private void initHttpLogging() {
        System.setProperty("org.apache.commons.logging.Log", "org.apache.commons.logging.impl.SimpleLog"); //$NON-NLS-1$ //$NON-NLS-2$
        System.setProperty("org.apache.commons.logging.simplelog.showdatetime", "true"); //$NON-NLS-1$ //$NON-NLS-2$
        System.setProperty("org.apache.commons.logging.simplelog.log.httpclient.wire", "debug"); //$NON-NLS-1$ //$NON-NLS-2$
        System.setProperty("org.apache.commons.logging.simplelog.log.httpclient.wire.header", "debug"); //$NON-NLS-1$ //$NON-NLS-2$
        System.setProperty("org.apache.commons.logging.simplelog.log.org.apache.commons.httpclient", "debug"); //$NON-NLS-1$ //$NON-NLS-2$
        System.setProperty("org.apache.commons.logging.simplelog.log.org.apache.commons.httpclient.HttpConnection", //$NON-NLS-1$
                "trace"); //$NON-NLS-1$
        System.setProperty("org.apache.commons.logging.simplelog.log.org.apache.axis.message", "debug"); //$NON-NLS-1$ //$NON-NLS-2$
    }

    private void updateTaskActivityManager() {
        int endHour = getPreferenceStore().getInt(ITasksUiPreferenceConstants.PLANNING_ENDHOUR);
        //      if (taskActivityManager.getEndHour() != endHour) {
        //         taskActivityManager.setEndHour(endHour);
        TaskActivityUtil.setEndHour(endHour);
        //      }

        int newWeekStartDay = getPreferenceStore().getInt(ITasksUiPreferenceConstants.WEEK_START_DAY);
        int oldWeekStartDay = taskActivityManager.getWeekStartDay();
        if (oldWeekStartDay != newWeekStartDay) {
            taskActivityManager.setWeekStartDay(newWeekStartDay);
            //         taskActivityManager.setStartTime(new Date());
        }

        // event.getProperty().equals(TaskListPreferenceConstants.PLANNING_STARTDAY)
        // scheduledStartHour =
        // TasksUiPlugin.getDefault().getPreferenceStore().getInt(
        // TaskListPreferenceConstants.PLANNING_STARTHOUR);
    }

    private void loadTemplateRepositories() {
        // Add standard local task repository
        TaskRepository local = getLocalTaskRepository();
        repositoryManager.applyMigrators(local);

        // Add the automatically created templates
        for (AbstractRepositoryConnector connector : repositoryManager.getRepositoryConnectors()) {
            for (RepositoryTemplate template : repositoryTemplateManager
                    .getTemplates(connector.getConnectorKind())) {
                if (template.addAutomatically
                        && !TaskRepositoryUtil.isAddAutomaticallyDisabled(template.repositoryUrl)) {
                    try {
                        String repositoryUrl = TaskRepositoryManager.stripSlashes(template.repositoryUrl);
                        TaskRepository taskRepository = repositoryManager
                                .getRepository(connector.getConnectorKind(), repositoryUrl);
                        if (taskRepository == null) {
                            taskRepository = new TaskRepository(connector.getConnectorKind(), repositoryUrl);
                            taskRepository.setVersion(template.version);
                            taskRepository.setRepositoryLabel(template.label);
                            taskRepository.setCharacterEncoding(template.characterEncoding);
                            if (template.anonymous) {
                                taskRepository.setProperty("org.eclipse.mylyn.tasklist.repositories.enabled", //$NON-NLS-1$
                                        String.valueOf(false));
                                // bug 332747: avoid reseting password in shared keystore
                                //taskRepository.setCredentials(AuthenticationType.REPOSITORY, null, true);
                            }
                            taskRepository.setCreatedFromTemplate(true);
                            repositoryManager.addRepository(taskRepository);
                            repositoryManager.applyMigrators(taskRepository);
                        }
                    } catch (Throwable t) {
                        StatusHandler.log(new Status(IStatus.WARNING, TasksUiPlugin.ID_PLUGIN,
                                NLS.bind("Could not load repository template for repository {0}", //$NON-NLS-1$
                                        template.repositoryUrl),
                                t));
                    }
                }
            }
        }
    }

    /**
     * Returns the local task repository. If the repository does not exist it is created and added to the task
     * repository manager.
     *
     * @return the local task repository; never <code>null</code>
     * @since 3.0
     */
    public TaskRepository getLocalTaskRepository() {
        TaskRepository localRepository = repositoryManager.getRepository(LocalRepositoryConnector.CONNECTOR_KIND,
                LocalRepositoryConnector.REPOSITORY_URL);
        if (localRepository == null) {
            localRepository = new TaskRepository(LocalRepositoryConnector.CONNECTOR_KIND,
                    LocalRepositoryConnector.REPOSITORY_URL);
            localRepository.setVersion(LocalRepositoryConnector.REPOSITORY_VERSION);
            localRepository.setRepositoryLabel(LocalRepositoryConnector.REPOSITORY_LABEL);
            repositoryManager.addRepository(localRepository);
        }
        return localRepository;
    }

    @Override
    public void stop(BundleContext context) throws Exception {
        try {
            Job.getJobManager().cancel(ITasksCoreConstants.JOB_FAMILY_SYNCHRONIZATION);
            if (formColors != null) {
                formColors.dispose();
                formColors = null;
            }
            if (taskActivityMonitor != null) {
                taskActivityMonitor.stop();
            }

            if (ResourcesPlugin.getWorkspace() != null) {
                ResourcesPlugin.getWorkspace().removeSaveParticipant(this);
            }

            if (proxyServiceReference != null) {
                @SuppressWarnings("unchecked")
                IProxyService proxyService = (IProxyService) context.getService(proxyServiceReference);
                if (proxyService != null) {
                    proxyService.removeProxyChangeListener(proxyChangeListener);
                }
                context.ungetService(proxyServiceReference);
            }
            if (identityServiceTracker != null) {
                identityServiceTracker.close();
                identityServiceTracker = null;
            }

            // wait until stop() to set these to reduce chance of crash after setting them but before creds are persisted
            getPluginPreferences().setValue(PREF_MIGRATED_TASK_REPOSITORIES_FROM_SECURE_STORE,
                    Boolean.toString(true));
            if (isKeyringInstalled()) {
                getPluginPreferences().setValue(PREF_MIGRATED_TASK_REPOSITORIES_FROM_KEYRING,
                        Boolean.toString(true));
            }

            if (PlatformUI.isWorkbenchRunning()) {
                if (taskListNotificationManager != null) {
                    getPreferenceStore().removePropertyChangeListener(taskListNotificationManager);
                }
                if (taskListBackupManager != null) {
                    getPreferenceStore().removePropertyChangeListener(taskListBackupManager);
                }
                getPreferenceStore().removePropertyChangeListener(PROPERTY_LISTENER);
                //taskListManager.getTaskList().removeChangeListener(taskListSaveManager);
                CommonColors.dispose();
                //            if (ContextCorePlugin.getDefault() != null) {
                //               ContextCorePlugin.getDefault().getPluginPreferences().removePropertyChangeListener(
                //                     PREFERENCE_LISTENER);
                //            }
                serviceMessageManager.stop();
                INSTANCE = null;
            }
        } catch (Exception e) {
            StatusHandler.log(
                    new Status(IStatus.ERROR, TasksUiPlugin.ID_PLUGIN, "Task list stop terminated abnormally", e)); //$NON-NLS-1$
        } finally {
            super.stop(context);
        }
    }

    public String getDefaultDataDirectory() {
        return ResourcesPlugin.getWorkspace().getRoot().getLocation().toString() + '/' + DIRECTORY_METADATA + '/'
                + NAME_DATA_DIR;
    }

    public String getDataDirectory() {
        return getPreferenceStore().getString(ITasksUiPreferenceConstants.PREF_DATA_DIR);
    }

    /**
     * Persist <code>path</code> as data directory and loads data from <code>path</code>. This method may block if other
     * jobs are running that modify tasks data. This method will only execute after all conflicting jobs have been
     * completed.
     *
     * @throws CoreException
     *             in case setting of the data directory did not complete normally
     * @throws OperationCanceledException
     *             if the operation is cancelled by the user
     */
    public void setDataDirectory(final String path) throws CoreException {
        Assert.isNotNull(path);
        IRunnableWithProgress runner = new IRunnableWithProgress() {
            public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
                try {
                    monitor.beginTask(Messages.TasksUiPlugin_Load_Data_Directory, IProgressMonitor.UNKNOWN);
                    if (monitor.isCanceled()) {
                        throw new InterruptedException();
                    }

                    TasksUi.getTaskActivityManager().deactivateActiveTask();

                    // set new preference in case of a change
                    if (!path.equals(getDataDirectory())) {
                        getPreferenceStore().setValue(ITasksUiPreferenceConstants.PREF_DATA_DIR, path);
                    }

                    // reload data from new directory
                    initializeDataSources();
                } finally {
                    // FIXME roll back preferences change in case of an error?
                    monitor.done();
                }
            }
        };

        IProgressService service = PlatformUI.getWorkbench().getProgressService();
        try {
            service.runInUI(service, runner, ITasksCoreConstants.ROOT_SCHEDULING_RULE);
        } catch (InvocationTargetException e) {
            throw new CoreException(
                    new Status(IStatus.ERROR, TasksUiPlugin.ID_PLUGIN, "Failed to set data directory", //$NON-NLS-1$
                            e.getCause()));
        } catch (InterruptedException e) {
            throw new OperationCanceledException();
        }
    }

    public void reloadDataDirectory() throws CoreException {
        // no save just load what is there
        setDataDirectory(getDataDirectory());
    }

    /**
     * Invoked on startup and when data is loaded from disk or when the data directory changes.
     * <p>
     * Public for testing.
     */
    @SuppressWarnings("restriction")
    public void initializeDataSources() {
        // ensure that context directory exists
        File storeFile = new File(getDataDirectory(), ITasksCoreConstants.CONTEXTS_DIRECTORY);
        if (!storeFile.exists()) {
            storeFile.mkdirs();
        }

        taskDataManager.setDataPath(getDataDirectory());
        externalizationManager.setRootFolderPath(getDataDirectory());
        getContextStore().setDirectory(new File(getDataDirectory(), "tasks")); //$NON-NLS-1$

        externalizationManager.load();
        // TODO: Move management of template repositories to TaskRepositoryManager
        loadTemplateRepositories();

        taskActivityManager.clear();
        taskActivityMonitor.loadActivityTime();
        taskActivityManager.reloadPlanningData();

        if (!activationHistoryFile.exists() && taskActivityManager.getTaskActivationHistory().getSize() == 0) {
            // fall back to activity history
            List<ITask> tasks = taskActivityMonitor.getActivationHistory();
            for (ITask task : tasks) {
                taskActivityManager.getTaskActivationHistory().addTask((AbstractTask) task);
            }
        }

        if (!MonitorUiPlugin.getDefault().getPreferenceStore()
                .getBoolean(MonitorUiPlugin.ACTIVITY_TRACKING_ENABLED + ".checked")) { //$NON-NLS-1$
            if (!taskActivityMonitor.getActivationHistory().isEmpty()) {
                // tasks have been active before so fore preference enabled
                MonitorUiPlugin.getDefault().getPreferenceStore()
                        .setValue(MonitorUiPlugin.ACTIVITY_TRACKING_ENABLED, true);
            }
            MonitorUiPlugin.getDefault().getPreferenceStore()
                    .setValue(MonitorUiPlugin.ACTIVITY_TRACKING_ENABLED + ".checked", true); //$NON-NLS-1$
            MonitorUiPlugin.getDefault().savePluginPreferences();
        }

        // inform listeners that initialization is complete
        for (final IRepositoryModelListener listener : listeners) {
            SafeRunner.run(new ISafeRunnable() {

                public void handleException(Throwable exception) {
                    StatusHandler.log(new Status(IStatus.WARNING, TasksUiPlugin.ID_PLUGIN, "Listener failed: " //$NON-NLS-1$
                            + listener.getClass(), exception));
                }

                public void run() throws Exception {
                    listener.loaded();
                }
            });
        }
    }

    @SuppressWarnings("deprecation")
    private void initializePreferences(IPreferenceStore store) {
        store.setDefault(ITasksUiPreferenceConstants.PREF_DATA_DIR, getDefaultDataDirectory());
        store.setDefault(ITasksUiPreferenceConstants.GROUP_SUBTASKS, true);
        store.setDefault(ITasksUiPreferenceConstants.NOTIFICATIONS_ENABLED, true);
        store.setDefault(ITasksUiPreferenceConstants.SERVICE_MESSAGES_ENABLED, false);
        store.setDefault(ITasksUiPreferenceConstants.FILTER_PRIORITY, PriorityLevel.P5.toString());
        store.setDefault(ITasksUiPreferenceConstants.EDITOR_TASKS_RICH, true);
        store.setDefault(ITasksUiPreferenceConstants.EDITOR_CURRENT_LINE_HIGHLIGHT, false);
        store.setDefault(ITasksUiPreferenceConstants.ACTIVATE_WHEN_OPENED, false);
        store.setDefault(ITasksUiPreferenceConstants.SHOW_TRIM, false);
        // remove preference
        store.setToDefault(ITasksUiPreferenceConstants.LOCAL_SUB_TASKS_ENABLED);
        store.setDefault(ITasksUiPreferenceConstants.USE_STRIKETHROUGH_FOR_COMPLETED, true);

        store.setDefault(ITasksUiPreferenceConstants.REPOSITORY_SYNCH_SCHEDULE_ENABLED, true);
        store.setDefault(ITasksUiPreferenceConstants.REPOSITORY_SYNCH_SCHEDULE_MILISECONDS, "" + (20 * 60 * 1000)); //$NON-NLS-1$

        //store.setDefault(TasksUiPreferenceConstants.BACKUP_SCHEDULE, 1);
        store.setDefault(ITasksUiPreferenceConstants.BACKUP_MAXFILES, 20);
        store.setDefault(ITasksUiPreferenceConstants.BACKUP_LAST, 0f);

        store.setDefault(ITasksUiPreferenceConstants.FILTER_HIDDEN, true);
        store.setDefault(ITasksUiPreferenceConstants.FILTER_ARCHIVE_MODE, true);
        store.setDefault(ITasksUiPreferenceConstants.ACTIVATE_MULTIPLE, false);
        store.setValue(ITasksUiPreferenceConstants.ACTIVATE_MULTIPLE, false);

        store.setDefault(ITasksUiPreferenceConstants.WEEK_START_DAY, Calendar.getInstance().getFirstDayOfWeek());
        //store.setDefault(TasksUiPreferenceConstants.PLANNING_STARTHOUR, 9);
        store.setDefault(ITasksUiPreferenceConstants.PLANNING_ENDHOUR, 18);

        store.setDefault(ITasksUiPreferenceConstants.AUTO_EXPAND_TASK_LIST, true);
        store.setDefault(ITasksUiPreferenceConstants.TASK_LIST_TOOL_TIPS_ENABLED, true);

        store.setDefault(ITasksUiPreferenceConstants.SERVICE_MESSAGE_URL, "http://eclipse.org/mylyn/updates.xml"); //$NON-NLS-1$
    }

    public static TaskActivityManager getTaskActivityManager() {
        return taskActivityManager;
    }

    public static TaskListNotificationManager getTaskListNotificationManager() {
        return INSTANCE.taskListNotificationManager;
    }

    /**
     * Returns the shared instance.
     */
    public static TasksUiPlugin getDefault() {
        return INSTANCE;
    }

    public boolean groupSubtasks(ITaskContainer element) {
        boolean groupSubtasks = TasksUiPlugin.getDefault().getPreferenceStore()
                .getBoolean(ITasksUiPreferenceConstants.GROUP_SUBTASKS);

        if (element instanceof ITask) {
            AbstractRepositoryConnectorUi connectorUi = TasksUiPlugin
                    .getConnectorUi(((ITask) element).getConnectorKind());
            if (connectorUi != null) {
                if (connectorUi.hasStrictSubtaskHierarchy()) {
                    groupSubtasks = true;
                }
            }
        }

        if (element instanceof IRepositoryQuery) {
            AbstractRepositoryConnectorUi connectorUi = TasksUiPlugin
                    .getConnectorUi(((IRepositoryQuery) element).getConnectorKind());
            if (connectorUi != null) {
                if (connectorUi.hasStrictSubtaskHierarchy()) {
                    groupSubtasks = true;
                }
            }
        }

        return groupSubtasks;
    }

    private final Map<String, List<IDynamicSubMenuContributor>> menuContributors = new HashMap<String, List<IDynamicSubMenuContributor>>();

    @SuppressWarnings("rawtypes")
    private ServiceTracker identityServiceTracker;

    public Map<String, List<IDynamicSubMenuContributor>> getDynamicMenuMap() {
        return menuContributors;
    }

    public void addDynamicPopupContributor(String menuPath, IDynamicSubMenuContributor contributor) {
        List<IDynamicSubMenuContributor> contributors = menuContributors.get(menuPath);
        if (contributors == null) {
            contributors = new ArrayList<IDynamicSubMenuContributor>();
            menuContributors.put(menuPath, contributors);
        }
        contributors.add(contributor);
    }

    public String[] getSaveOptions() {
        String[] options = { TaskListSaveMode.ONE_HOUR.toString(), TaskListSaveMode.THREE_HOURS.toString(),
                TaskListSaveMode.DAY.toString() };
        return options;
    }

    public String getBackupFolderPath() {
        return getDataDirectory() + DEFAULT_PATH_SEPARATOR + ITasksCoreConstants.DEFAULT_BACKUP_FOLDER_NAME;
    }

    /**
     * @since 3.0
     */
    public AbstractTaskEditorPageFactory[] getTaskEditorPageFactories() {
        return taskEditorPageFactories.toArray(new AbstractTaskEditorPageFactory[0]);
    }

    /**
     * @since 3.0
     */
    public void addTaskEditorPageFactory(AbstractTaskEditorPageFactory factory) {
        Assert.isNotNull(factory);
        taskEditorPageFactories.add(factory);
    }

    /**
     * @since 3.0
     */
    public void removeTaskEditorPageFactory(AbstractTaskEditorPageFactory factory) {
        Assert.isNotNull(factory);
        taskEditorPageFactories.remove(factory);
    }

    public static TaskRepositoryManager getRepositoryManager() {
        return repositoryManager;
    }

    /**
     * @since 3.0
     */
    public static RepositoryTemplateManager getRepositoryTemplateManager() {
        return INSTANCE.repositoryTemplateManager;
    }

    public void addBrandingIcon(String repositoryType, Image icon) {
        brandingIcons.put(repositoryType, icon);
    }

    public Image getBrandingIcon(String repositoryType) {
        return brandingIcons.get(repositoryType);
    }

    public void addOverlayIcon(String repositoryType, ImageDescriptor icon) {
        overlayIcons.put(repositoryType, icon);
    }

    public ImageDescriptor getOverlayIcon(String repositoryType) {
        return overlayIcons.get(repositoryType);
    }

    public void addRepositoryLinkProvider(AbstractTaskRepositoryLinkProvider repositoryLinkProvider) {
        if (repositoryLinkProvider != null) {
            this.repositoryLinkProviders.add(repositoryLinkProvider);
        }
    }

    public static synchronized TaskListBackupManager getBackupManager() {
        if (INSTANCE.taskListBackupManager == null) {
            INSTANCE.taskListBackupManager = new TaskListBackupManager(INSTANCE.getBackupFolderPath());
            INSTANCE.getPreferenceStore().addPropertyChangeListener(INSTANCE.taskListBackupManager);
        }
        return INSTANCE.taskListBackupManager;
    }

    public void addRepositoryConnectorUi(AbstractRepositoryConnectorUi repositoryConnectorUi) {
        if (!repositoryConnectorUiMap.values().contains(repositoryConnectorUi)) {
            repositoryConnectorUiMap.put(repositoryConnectorUi.getConnectorKind(), repositoryConnectorUi);
        }
    }

    /**
     * @since 3.0
     */
    public static AbstractRepositoryConnector getConnector(String kind) {
        return getRepositoryManager().getRepositoryConnector(kind);
    }

    public static AbstractRepositoryConnectorUi getConnectorUi(String kind) {
        return repositoryConnectorUiMap.get(kind);
    }

    @Deprecated
    public static TaskListSynchronizationScheduler getSynchronizationScheduler() {
        return synchronizationScheduler;
    }

    /**
     * @since 3.0
     */
    public static TaskDataManager getTaskDataManager() {
        return taskDataManager;
    }

    /**
     * @since 3.0
     */
    public static TaskJobFactory getTaskJobFactory() {
        return INSTANCE.taskJobFactory;
    }

    public void addDuplicateDetector(AbstractDuplicateDetector duplicateDetector) {
        Assert.isNotNull(duplicateDetector);
        duplicateDetectors.add(duplicateDetector);
    }

    public Set<AbstractDuplicateDetector> getDuplicateSearchCollectorsList() {
        return duplicateDetectors;
    }

    public String getRepositoriesFilePath() {
        return getDataDirectory() + File.separator + TaskRepositoryManager.DEFAULT_REPOSITORIES_FILE;
    }

    public void addModelListener(IRepositoryModelListener listener) {
        listeners.add(listener);
    }

    public void removeModelListener(IRepositoryModelListener listener) {
        listeners.remove(listener);
    }

    public boolean canSetRepositoryForResource(final IResource resource) {
        if (resource == null) {
            return false;
        }

        // if a repository has already been linked only that provider should be queried to ensure that it is the same
        // provider that is used by getRepositoryForResource()
        final boolean result[] = new boolean[1];
        final boolean found[] = new boolean[1];
        for (final AbstractTaskRepositoryLinkProvider linkProvider : repositoryLinkProviders) {
            SafeRunner.run(new ISafeRunnable() {
                public void handleException(Throwable e) {
                    StatusHandler.log(new Status(IStatus.ERROR, TasksUiPlugin.ID_PLUGIN,
                            "Task repository link provider failed: \"" + linkProvider.getId() + "\"", e)); //$NON-NLS-1$ //$NON-NLS-2$
                }

                public void run() throws Exception {
                    if (linkProvider.getTaskRepository(resource, getRepositoryManager()) != null) {
                        found[0] = true;
                        result[0] = linkProvider.canSetTaskRepository(resource);
                    }
                }
            });
            if (found[0]) {
                return result[0];
            }
        }

        // find a provider that can set new repository
        for (final AbstractTaskRepositoryLinkProvider linkProvider : repositoryLinkProviders) {
            SafeRunner.run(new ISafeRunnable() {
                public void handleException(Throwable e) {
                    StatusHandler.log(new Status(IStatus.ERROR, TasksUiPlugin.ID_PLUGIN,
                            "Task repository link provider failed: \"" + linkProvider.getId() + "\"", e)); //$NON-NLS-1$ //$NON-NLS-2$
                }

                public void run() throws Exception {
                    if (linkProvider.canSetTaskRepository(resource)) {
                        result[0] = true;
                    }
                }
            });
            if (result[0]) {
                return true;
            }
        }
        return false;
    }

    /**
     * Associate a Task Repository with a workbench project
     *
     * @param resource
     *            project or resource belonging to a project
     * @param repository
     *            task repository to associate with given project
     * @throws CoreException
     */
    public void setRepositoryForResource(final IResource resource, final TaskRepository repository) {
        Assert.isNotNull(resource);
        for (final AbstractTaskRepositoryLinkProvider linkProvider : repositoryLinkProviders) {
            SafeRunner.run(new ISafeRunnable() {
                public void handleException(Throwable e) {
                    StatusHandler.log(new Status(IStatus.ERROR, TasksUiPlugin.ID_PLUGIN,
                            "Task repository link provider failed: \"" + linkProvider.getId() + "\"", e)); //$NON-NLS-1$ //$NON-NLS-2$
                }

                public void run() throws Exception {
                    boolean canSetRepository = linkProvider.canSetTaskRepository(resource);
                    if (canSetRepository) {
                        linkProvider.setTaskRepository(resource, repository);
                    }
                }
            });
        }
    }

    /**
     * Retrieve the task repository that has been associated with the given project (or resource belonging to a project)
     * NOTE: if call does not return in LINK_PROVIDER_TIMEOUT_SECONDS, the provide will be disabled until the next time
     * that the Workbench starts.
     */
    public TaskRepository getRepositoryForResource(final IResource resource) {
        Assert.isNotNull(resource);
        long timeout;
        try {
            timeout = Long.parseLong(System.getProperty(ITasksCoreConstants.PROPERTY_LINK_PROVIDER_TIMEOUT,
                    DEFAULT_LINK_PROVIDER_TIMEOUT + "")); //$NON-NLS-1$
        } catch (NumberFormatException e) {
            timeout = DEFAULT_LINK_PROVIDER_TIMEOUT;
        }
        Set<AbstractTaskRepositoryLinkProvider> defectiveLinkProviders = new HashSet<AbstractTaskRepositoryLinkProvider>();
        for (final AbstractTaskRepositoryLinkProvider linkProvider : repositoryLinkProviders) {
            long startTime = System.currentTimeMillis();
            final TaskRepository[] repository = new TaskRepository[1];
            SafeRunnable.run(new ISafeRunnable() {
                public void handleException(Throwable e) {
                    StatusHandler
                            .log(new Status(IStatus.ERROR, ID_PLUGIN, "Task repository link provider failed: \"" //$NON-NLS-1$
                                    + linkProvider.getId() + "\"", e)); //$NON-NLS-1$
                }

                public void run() throws Exception {
                    repository[0] = linkProvider.getTaskRepository(resource, getRepositoryManager());
                }
            });
            long elapsed = System.currentTimeMillis() - startTime;
            if (timeout >= 0 && elapsed > timeout) {
                defectiveLinkProviders.add(linkProvider);
            }
            if (repository[0] != null) {
                return repository[0];
            }
        }
        if (!defectiveLinkProviders.isEmpty()) {
            repositoryLinkProviders.removeAll(defectiveLinkProviders);
            StatusHandler.log(new Status(IStatus.WARNING, ID_PLUGIN, "Repository link provider took over " + timeout //$NON-NLS-1$
                    + " ms to execute and was timed out: \"" + defectiveLinkProviders + "\"")); //$NON-NLS-1$ //$NON-NLS-2$
        }
        return null;
    }

    public static ExternalizationManager getExternalizationManager() {
        return externalizationManager;
    }

    public static AbstractTaskActivityMonitor getTaskActivityMonitor() {
        return INSTANCE.taskActivityMonitor;
    }

    public static TaskList getTaskList() {
        return taskList;
    }

    public static RepositoryModel getRepositoryModel() {
        return repositoryModel;
    }

    /**
     * Note: This is provisional API that is used by connectors.
     * <p>
     * DO NOT CHANGE.
     */
    public void addSearchHandler(AbstractSearchHandler searchHandler) {
        searchHandlers.add(searchHandler);
    }

    /**
     * Note: This is provisional API that is used by connectors.
     * <p>
     * DO NOT CHANGE.
     */
    public void removeSearchHandler(AbstractSearchHandler searchHandler) {
        searchHandlers.remove(searchHandler);
    }

    public AbstractSearchHandler getSearchHandler(String connectorKind) {
        Assert.isNotNull(connectorKind);
        for (AbstractSearchHandler searchHandler : searchHandlers) {
            if (searchHandler.getConnectorKind().equals(connectorKind)) {
                return searchHandler;
            }
        }
        return null;
    }

    public FormColors getFormColors(Display display) {
        if (formColors == null) {
            formColors = new FormColors(display);
            formColors.markShared();
        }
        return formColors;
    }

    public void removeRepositoryLinkProvider(AbstractTaskRepositoryLinkProvider provider) {
        repositoryLinkProviders.remove(provider);
    }

    public TaskListExternalizer createTaskListExternalizer() {
        return new TaskListExternalizer(repositoryModel, repositoryManager);
    }

    public static TaskListExternalizationParticipant getTaskListExternalizationParticipant() {
        return taskListExternalizationParticipant;
    }

    public static TasksUiFactory getUiFactory() {
        if (uiFactory == null) {
            uiFactory = new TasksUiFactory();
        }
        return uiFactory;
    }

    public void initializeNotificationsAndSynchronization() {
        // this method is invoked by the tasks ui initialization job and the task list view
        // only proceed if both calls have been made
        if (initializationCount.incrementAndGet() != 2) {
            return;
        }

        try {
            taskListNotificationManager.addNotificationProvider(REMINDER_NOTIFICATION_PROVIDER);
            //            taskListNotificationManager.addNotificationProvider(INCOMING_NOTIFICATION_PROVIDER);
            TaskListNotifier taskListNotifier = new TaskListNotifier(getTaskDataManager(),
                    getSynchronizationManger());
            getTaskDataManager().addListener(taskListNotifier);
            taskListNotificationManager.addNotificationProvider(taskListNotifier);
            taskListNotificationManager.startNotification(NOTIFICATION_DELAY);
            getPreferenceStore().addPropertyChangeListener(taskListNotificationManager);
        } catch (Throwable t) {
            StatusHandler.log(
                    new Status(IStatus.ERROR, TasksUiPlugin.ID_PLUGIN, "Could not initialize notifications", t)); //$NON-NLS-1$
        }

        try {
            // trigger backup scheduler
            getBackupManager();

            synchronizationScheduler = new TaskListSynchronizationScheduler(taskJobFactory);
            MonitorUiPlugin.getDefault().getActivityContextManager().addListener(synchronizationScheduler);
            updateSynchronizationScheduler(true);
        } catch (Throwable t) {
            StatusHandler.log(new Status(IStatus.ERROR, TasksUiPlugin.ID_PLUGIN,
                    "Could not initialize task list backup and synchronization", t)); //$NON-NLS-1$
        }
    }

    @SuppressWarnings("restriction")
    public ServiceMessageManager getServiceMessageManager() {
        return serviceMessageManager;
    }

    public SynchronizationManger getSynchronizationManger() {
        return synchronizationManger;
    }

    @SuppressWarnings({ "rawtypes", "unchecked", "restriction" })
    public IIdentityService getIdentityService() {
        if (identityServiceTracker == null) {
            identityServiceTracker = new ServiceTracker(getBundle().getBundleContext(),
                    IIdentityService.class.getName(), null);
            identityServiceTracker.open();
        }
        return (IIdentityService) identityServiceTracker.getService();
    }

    public static synchronized AbstractTaskContextStore getContextStore() {
        if (contextStore == null) {
            contextStore = TasksCoreExtensionReader.loadTaskContextStore();
        }
        return contextStore;
    }

}