blob: 0f1776ef0614a2dd948a08e43962705a4129b22b [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2004, 2007 Mylyn project committers 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
*******************************************************************************/
package org.eclipse.mylyn.tasks.ui;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.Date;
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 org.eclipse.core.internal.net.ProxyManager;
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.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.Preferences.IPropertyChangeListener;
import org.eclipse.core.runtime.Preferences.PropertyChangeEvent;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.text.hyperlink.IHyperlinkDetector;
import org.eclipse.mylyn.context.core.ContextCorePlugin;
import org.eclipse.mylyn.internal.context.core.ContextPreferenceContstants;
import org.eclipse.mylyn.internal.tasks.core.LocalRepositoryConnector;
import org.eclipse.mylyn.internal.tasks.core.ScheduledTaskContainer;
import org.eclipse.mylyn.internal.tasks.core.TaskActivityManager;
import org.eclipse.mylyn.internal.tasks.core.TaskDataManager;
import org.eclipse.mylyn.internal.tasks.ui.IDynamicSubMenuContributor;
import org.eclipse.mylyn.internal.tasks.ui.ITaskHighlighter;
import org.eclipse.mylyn.internal.tasks.ui.ITaskListNotificationProvider;
import org.eclipse.mylyn.internal.tasks.ui.ITasksUiConstants;
import org.eclipse.mylyn.internal.tasks.ui.OfflineCachingStorage;
import org.eclipse.mylyn.internal.tasks.ui.OfflineFileStorage;
import org.eclipse.mylyn.internal.tasks.ui.RepositoryAwareStatusHandler;
import org.eclipse.mylyn.internal.tasks.ui.TaskEditorBloatMonitor;
import org.eclipse.mylyn.internal.tasks.ui.TaskListBackupManager;
import org.eclipse.mylyn.internal.tasks.ui.TaskListColorsAndFonts;
import org.eclipse.mylyn.internal.tasks.ui.TaskListNotificationManager;
import org.eclipse.mylyn.internal.tasks.ui.TaskListSynchronizationScheduler;
import org.eclipse.mylyn.internal.tasks.ui.TaskRepositoryUtil;
import org.eclipse.mylyn.internal.tasks.ui.TasksUiPreferenceConstants;
import org.eclipse.mylyn.internal.tasks.ui.notifications.AbstractNotification;
import org.eclipse.mylyn.internal.tasks.ui.notifications.TaskListNotification;
import org.eclipse.mylyn.internal.tasks.ui.notifications.TaskListNotificationQueryIncoming;
import org.eclipse.mylyn.internal.tasks.ui.notifications.TaskListNotificationReminder;
import org.eclipse.mylyn.internal.tasks.ui.util.TaskListSaveManager;
import org.eclipse.mylyn.internal.tasks.ui.util.TaskListWriter;
import org.eclipse.mylyn.internal.tasks.ui.util.TasksUiExtensionReader;
import org.eclipse.mylyn.internal.tasks.ui.views.TaskRepositoriesView;
import org.eclipse.mylyn.monitor.core.StatusHandler;
import org.eclipse.mylyn.tasks.core.AbstractAttributeFactory;
import org.eclipse.mylyn.tasks.core.AbstractRepositoryConnector;
import org.eclipse.mylyn.tasks.core.AbstractRepositoryQuery;
import org.eclipse.mylyn.tasks.core.AbstractTask;
import org.eclipse.mylyn.tasks.core.AbstractTaskContainer;
import org.eclipse.mylyn.tasks.core.AbstractTaskDataHandler;
import org.eclipse.mylyn.tasks.core.ITaskActivityListener;
import org.eclipse.mylyn.tasks.core.RepositoryTaskAttribute;
import org.eclipse.mylyn.tasks.core.RepositoryTaskData;
import org.eclipse.mylyn.tasks.core.RepositoryTemplate;
import org.eclipse.mylyn.tasks.core.TaskComment;
import org.eclipse.mylyn.tasks.core.TaskRepository;
import org.eclipse.mylyn.tasks.core.TaskRepositoryManager;
import org.eclipse.mylyn.tasks.core.AbstractTask.PriorityLevel;
import org.eclipse.mylyn.tasks.core.AbstractTask.RepositoryTaskSyncState;
import org.eclipse.mylyn.tasks.ui.editors.AbstractTaskEditorFactory;
import org.eclipse.mylyn.web.core.WebClientUtil;
import org.eclipse.swt.graphics.Image;
import org.eclipse.ui.IStartup;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.plugin.AbstractUIPlugin;
import org.eclipse.ui.progress.UIJob;
import org.osgi.framework.BundleContext;
/**
* Main entry point for the Tasks UI.
*
* @author Mik Kersten
* @since 2.0
*/
public class TasksUiPlugin extends AbstractUIPlugin {
private static final int MAX_CHANGED_ATTRIBUTES = 2;
private static final int LINK_PROVIDER_TIMEOUT_SECONDS = 5;
public static final String LABEL_VIEW_REPOSITORIES = "Task Repositories";
public static final String ID_PLUGIN = "org.eclipse.mylyn.tasks.ui";
private static final String FOLDER_OFFLINE = "offline";
private static final String DIRECTORY_METADATA = ".metadata";
private static final String NAME_DATA_DIR = ".mylyn";
private static final char DEFAULT_PATH_SEPARATOR = '/';
private static final int NOTIFICATION_DELAY = 5000;
private static TasksUiPlugin INSTANCE;
private static TaskListManager taskListManager;
private static TaskActivityManager taskActivityManager;
private static TaskRepositoryManager taskRepositoryManager;
private static TaskListSynchronizationScheduler synchronizationScheduler;
private static RepositorySynchronizationManager synchronizationManager;
private static Map<String, AbstractRepositoryConnectorUi> repositoryConnectorUiMap = new HashMap<String, AbstractRepositoryConnectorUi>();
private TaskListSaveManager taskListSaveManager;
private TaskListNotificationManager taskListNotificationManager;
private TaskListBackupManager taskListBackupManager;
private TaskDataManager taskDataManager;
private Set<AbstractTaskEditorFactory> taskEditorFactories = new HashSet<AbstractTaskEditorFactory>();
private Set<IHyperlinkDetector> hyperlinkDetectors = new HashSet<IHyperlinkDetector>();
private TreeSet<AbstractTaskRepositoryLinkProvider> repositoryLinkProviders = new TreeSet<AbstractTaskRepositoryLinkProvider>(
new OrderComparator());
private TaskListWriter taskListWriter;
private ITaskHighlighter highlighter;
private boolean initialized = false;
private Map<String, Image> brandingIcons = new HashMap<String, Image>();
private Map<String, ImageDescriptor> overlayIcons = new HashMap<String, ImageDescriptor>();
private Set<AbstractDuplicateDetector> duplicateDetectors = new HashSet<AbstractDuplicateDetector>();
private ISaveParticipant saveParticipant;
private TaskEditorBloatMonitor taskEditorBloatManager;
private static final boolean DEBUG_HTTPCLIENT = "true".equalsIgnoreCase(Platform.getDebugOption("org.eclipse.mylyn.tasks.ui/debug/httpclient"));
// API 3.0 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";
case THREE_HOURS:
return "3 hours";
case DAY:
return "1 day";
default:
return "3 hours";
}
}
public static TaskListSaveMode fromString(String string) {
if (string == null) {
return null;
}
if (string.equals("1 hour")) {
return ONE_HOUR;
}
if (string.equals("3 hours")) {
return THREE_HOURS;
}
if (string.equals("1 day")) {
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 ITaskActivityListener CONTEXT_TASK_ACTIVITY_LISTENER = new ITaskActivityListener() {
public void taskActivated(final AbstractTask task) {
ContextCorePlugin.getContextManager().activateContext(task.getHandleIdentifier());
}
public void taskDeactivated(final AbstractTask task) {
ContextCorePlugin.getContextManager().deactivateContext(task.getHandleIdentifier());
}
public void activityChanged(ScheduledTaskContainer week) {
// ignore
}
public void taskListRead() {
// ignore
}
};
private static ITaskListNotificationProvider REMINDER_NOTIFICATION_PROVIDER = new ITaskListNotificationProvider() {
public Set<AbstractNotification> getNotifications() {
Collection<AbstractTask> allTasks = TasksUiPlugin.getTaskListManager().getTaskList().getAllTasks();
Set<AbstractNotification> reminders = new HashSet<AbstractNotification>();
for (AbstractTask task : allTasks) {
if (task.isPastReminder() && !task.isReminded()) {
reminders.add(new TaskListNotificationReminder(task));
task.setReminded(true);
}
}
return reminders;
}
};
private static ITaskListNotificationProvider INCOMING_NOTIFICATION_PROVIDER = new ITaskListNotificationProvider() {
public Set<AbstractNotification> getNotifications() {
Set<AbstractNotification> notifications = new HashSet<AbstractNotification>();
// Incoming Changes
for (TaskRepository repository : getRepositoryManager().getAllRepositories()) {
AbstractRepositoryConnector connector = getRepositoryManager().getRepositoryConnector(
repository.getConnectorKind());
AbstractRepositoryConnectorUi connectorUi = getConnectorUi(repository.getConnectorKind());
if (connectorUi != null && !connectorUi.isCustomNotificationHandling()) {
for (AbstractTask repositoryTask : TasksUiPlugin.getTaskListManager()
.getTaskList()
.getRepositoryTasks(repository.getUrl())) {
if ((repositoryTask.getLastReadTimeStamp() == null || repositoryTask.getSynchronizationState() == RepositoryTaskSyncState.INCOMING)
&& repositoryTask.isNotified() == false) {
TaskListNotification notification = INSTANCE.getIncommingNotification(connector,
repositoryTask);
notifications.add(notification);
repositoryTask.setNotified(true);
}
}
}
}
// New query hits
for (AbstractRepositoryQuery query : TasksUiPlugin.getTaskListManager().getTaskList().getQueries()) {
AbstractRepositoryConnectorUi connectorUi = getConnectorUi(query.getRepositoryKind());
if (!connectorUi.isCustomNotificationHandling()) {
for (AbstractTask hit : query.getChildren()) {
if (hit.isNotified() == false) {
notifications.add(new TaskListNotificationQueryIncoming(hit));
hit.setNotified(true);
}
}
}
}
return notifications;
}
};
private final IPropertyChangeListener PREFERENCE_LISTENER = new IPropertyChangeListener() {
public void propertyChange(PropertyChangeEvent event) {
// TODO: do we ever get here?
if (event.getProperty().equals(ContextPreferenceContstants.PREF_DATA_DIR)) {
if (event.getOldValue() instanceof String) {
reloadDataDirectory(true);
}
}
}
};
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(ContextPreferenceContstants.PREF_DATA_DIR)) {
if (event.getOldValue() instanceof String) {
reloadDataDirectory(true);
}
}
}
};
private class TasksUiInitializationJob extends UIJob {
public TasksUiInitializationJob() {
super("Initializing Task List");
setSystem(true);
}
@Override
public IStatus runInUIThread(IProgressMonitor monitor) {
// NOTE: failure in one part of the initialization should
// not prevent others
monitor.beginTask("Initializing Task List", 5);
try {
// Needs to run after workbench is loaded because it
// relies on images.
TasksUiExtensionReader.initWorkbenchUiExtensions();
// Needs to happen asynchronously to avoid bug 159706
if (taskListManager.getTaskList().getActiveTask() != null) {
taskListManager.activateTask(taskListManager.getTaskList().getActiveTask());
}
taskListManager.initActivityHistory();
} catch (Throwable t) {
StatusHandler.log(new Status(IStatus.ERROR, TasksUiPlugin.ID_PLUGIN,
"Could not initialize task activity", t));
}
monitor.worked(1);
try {
taskListNotificationManager = new TaskListNotificationManager();
taskListNotificationManager.addNotificationProvider(REMINDER_NOTIFICATION_PROVIDER);
taskListNotificationManager.addNotificationProvider(INCOMING_NOTIFICATION_PROVIDER);
taskListNotificationManager.startNotification(NOTIFICATION_DELAY);
getPreferenceStore().addPropertyChangeListener(taskListNotificationManager);
} catch (Throwable t) {
StatusHandler.log(new Status(IStatus.ERROR, TasksUiPlugin.ID_PLUGIN,
"Could not initialize notifications", t));
}
monitor.worked(1);
try {
taskListBackupManager = new TaskListBackupManager();
getPreferenceStore().addPropertyChangeListener(taskListBackupManager);
synchronizationScheduler = new TaskListSynchronizationScheduler(true);
synchronizationScheduler.startSynchJob();
} catch (Throwable t) {
StatusHandler.log(new Status(IStatus.ERROR, TasksUiPlugin.ID_PLUGIN,
"Could not initialize task list backup and synchronization", t));
}
monitor.worked(1);
try {
taskListSaveManager = new TaskListSaveManager();
taskListManager.setTaskListSaveManager(taskListSaveManager);
ContextCorePlugin.getDefault().getPluginPreferences().addPropertyChangeListener(PREFERENCE_LISTENER);
getPreferenceStore().addPropertyChangeListener(PROPERTY_LISTENER);
getPreferenceStore().addPropertyChangeListener(synchronizationScheduler);
getPreferenceStore().addPropertyChangeListener(taskListManager);
// TODO: get rid of this, hack to make decorators show
// up on startup
TaskRepositoriesView repositoriesView = TaskRepositoriesView.getFromActivePerspective();
if (repositoriesView != null) {
repositoriesView.getViewer().refresh();
}
taskEditorBloatManager = new TaskEditorBloatMonitor();
taskEditorBloatManager.install(PlatformUI.getWorkbench());
} catch (Throwable t) {
StatusHandler.log(new Status(IStatus.ERROR, TasksUiPlugin.ID_PLUGIN,
"Could not finish Tasks UI initialization", t));
} finally {
monitor.done();
}
return new Status(IStatus.OK, TasksUiPlugin.ID_PLUGIN, IStatus.OK, "", null);
}
}
public TasksUiPlugin() {
super();
INSTANCE = this;
}
@Override
public void start(BundleContext context) throws Exception {
super.start(context);
// NOTE: startup order is very sensitive
try {
StatusHandler.setDefaultStatusHandler(new RepositoryAwareStatusHandler());
WebClientUtil.setLoggingEnabled(DEBUG_HTTPCLIENT);
initializeDefaultPreferences(getPreferenceStore());
taskListWriter = new TaskListWriter();
File dataDir = new File(getDataDirectory());
dataDir.mkdirs();
String path = getDataDirectory() + File.separator + ITasksUiConstants.DEFAULT_TASK_LIST_FILE;
File taskListFile = new File(path);
taskListManager = new TaskListManager(taskListWriter, taskListFile);
taskActivityManager = TaskActivityManager.getInstance();
taskRepositoryManager = new TaskRepositoryManager(taskListManager.getTaskList());
IProxyService proxyService = ProxyManager.getProxyManager();
IProxyChangeListener proxyChangeListener = new TasksUiProxyChangeListener(taskRepositoryManager);
proxyService.addProxyChangeListener(proxyChangeListener);
synchronizationManager = new RepositorySynchronizationManager();
// NOTE: initializing extensions in start(..) has caused race
// conditions previously
TasksUiExtensionReader.initStartupExtensions(taskListWriter);
taskRepositoryManager.readRepositories(getRepositoriesFilePath());
// instantiates taskDataManager
startOfflineStorageManager();
loadTemplateRepositories();
// NOTE: task list must be read before Task List view can be
// initialized
taskListManager.init();
taskListManager.addActivityListener(CONTEXT_TASK_ACTIVITY_LISTENER);
// readExistingOrCreateNewList() must be called after repositories have been read in
taskListManager.readExistingOrCreateNewList();
initialized = true;
// if the taskListManager didn't initialize do it here
if (!taskActivityManager.isInitialized()) {
taskActivityManager.init(taskRepositoryManager, taskListManager.getTaskList());
taskActivityManager.setEndHour(getPreferenceStore().getInt(TasksUiPreferenceConstants.PLANNING_ENDHOUR));
}
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) {
taskListManager.saveTaskList();
taskDataManager.stop();
}
}
};
ResourcesPlugin.getWorkspace().addSaveParticipant(this, saveParticipant);
new TasksUiInitializationJob().schedule();
} catch (Exception e) {
StatusHandler.log(new Status(IStatus.ERROR, TasksUiPlugin.ID_PLUGIN,
"Task list initialization failed", e));
}
}
private void loadTemplateRepositories() {
// Add standard local task repository
if (taskRepositoryManager.getRepository(LocalRepositoryConnector.REPOSITORY_URL) == null) {
TaskRepository localRepository = new TaskRepository(LocalRepositoryConnector.CONNECTOR_KIND,
LocalRepositoryConnector.REPOSITORY_URL, LocalRepositoryConnector.REPOSITORY_VERSION);
localRepository.setRepositoryLabel(LocalRepositoryConnector.REPOSITORY_LABEL);
localRepository.setAnonymous(true);
taskRepositoryManager.addRepository(localRepository, getRepositoriesFilePath());
}
// Add the automatically created templates
for (AbstractRepositoryConnector connector : taskRepositoryManager.getRepositoryConnectors()) {
connector.setTaskDataManager(taskDataManager);
for (RepositoryTemplate template : connector.getTemplates()) {
if (template.addAutomatically && !TaskRepositoryUtil.isAddAutomaticallyDisabled(template.repositoryUrl)) {
try {
TaskRepository taskRepository = taskRepositoryManager.getRepository(
connector.getConnectorKind(), template.repositoryUrl);
if (taskRepository == null) {
taskRepository = new TaskRepository(connector.getConnectorKind(), template.repositoryUrl,
template.version);
taskRepository.setRepositoryLabel(template.label);
taskRepository.setAnonymous(true);
taskRepositoryManager.addRepository(taskRepository, getRepositoriesFilePath());
}
} catch (Throwable t) {
StatusHandler.log(new Status(IStatus.ERROR, TasksUiPlugin.ID_PLUGIN,
"Could not load repository template", t));
}
}
}
}
}
@Override
public void stop(BundleContext context) throws Exception {
try {
if (ResourcesPlugin.getWorkspace() != null) {
ResourcesPlugin.getWorkspace().removeSaveParticipant(this);
}
if (PlatformUI.isWorkbenchRunning()) {
getPreferenceStore().removePropertyChangeListener(taskListNotificationManager);
getPreferenceStore().removePropertyChangeListener(taskListBackupManager);
getPreferenceStore().removePropertyChangeListener(taskListManager);
getPreferenceStore().removePropertyChangeListener(synchronizationScheduler);
getPreferenceStore().removePropertyChangeListener(PROPERTY_LISTENER);
taskListManager.getTaskList().removeChangeListener(taskListSaveManager);
taskListManager.dispose();
TaskListColorsAndFonts.dispose();
if (ContextCorePlugin.getDefault() != null) {
ContextCorePlugin.getDefault().getPluginPreferences().removePropertyChangeListener(
PREFERENCE_LISTENER);
}
taskEditorBloatManager.dispose(PlatformUI.getWorkbench());
INSTANCE = null;
}
} catch (Exception e) {
StatusHandler.log(new Status(IStatus.ERROR, TasksUiPlugin.ID_PLUGIN,
"Task list stop terminated abnormally", e));
} finally {
super.stop(context);
}
}
public String getDefaultDataDirectory() {
return ResourcesPlugin.getWorkspace().getRoot().getLocation().toString() + '/' + DIRECTORY_METADATA + '/'
+ NAME_DATA_DIR;
}
/**
* Only attempt once per startup.
*/
private boolean attemptMigration = true;
public synchronized String getDataDirectory() {
if (attemptMigration) {
migrateFromLegacyDirectory();
attemptMigration = false;
}
return getPreferenceStore().getString(ContextPreferenceContstants.PREF_DATA_DIR);
}
/**
* API-3.0: remove
*/
@Deprecated
private void migrateFromLegacyDirectory() {
// Migrate .mylar data folder to .metadata/.mylyn
String oldDefaultDataPath = ResourcesPlugin.getWorkspace().getRoot().getLocation().toString() + '/' + ".mylar";
File oldDefaultDataDir = new File(oldDefaultDataPath);
if (oldDefaultDataDir.exists()) { // && !newDefaultDataDir.exists()) {
File metadata = new File(ResourcesPlugin.getWorkspace().getRoot().getLocation().toString() + '/'
+ DIRECTORY_METADATA);
if (!metadata.exists()) {
if (!metadata.mkdirs()) {
StatusHandler.log("Unable to create metadata folder: " + metadata.getAbsolutePath(), this);
}
}
File newDefaultDataDir = new File(getPreferenceStore().getString(ContextPreferenceContstants.PREF_DATA_DIR));
if (metadata.exists()) {
if (!oldDefaultDataDir.renameTo(newDefaultDataDir)) {
StatusHandler.log("Could not migrate legacy data from " + oldDefaultDataDir.getAbsolutePath()
+ " to " + TasksUiPlugin.getDefault().getDefaultDataDirectory(), this);
} else {
StatusHandler.log("Migrated legacy task data from " + oldDefaultDataDir.getAbsolutePath() + " to "
+ TasksUiPlugin.getDefault().getDefaultDataDirectory(), this);
}
}
}
}
public void setDataDirectory(String newPath) {
getTaskListManager().saveTaskList();
ContextCorePlugin.getContextManager().saveActivityContext();
getPreferenceStore().setValue(ContextPreferenceContstants.PREF_DATA_DIR, newPath);
ContextCorePlugin.getDefault().getContextStore().contextStoreMoved();
}
/**
* Only support task data versions post 0.7
*
* @param withProgress
*/
public void reloadDataDirectory(boolean withProgress) {
getTaskListManager().getTaskActivationHistory().clear();
getRepositoryManager().readRepositories(getRepositoriesFilePath());
loadTemplateRepositories();
getTaskListManager().resetTaskList();
getTaskListManager().setTaskListFile(
new File(getDataDirectory() + File.separator + ITasksUiConstants.DEFAULT_TASK_LIST_FILE));
ContextCorePlugin.getContextManager().loadActivityMetaContext();
getTaskListManager().readExistingOrCreateNewList();
getTaskListManager().initActivityHistory();
}
@Override
protected void initializeDefaultPreferences(IPreferenceStore store) {
store.setDefault(ContextPreferenceContstants.PREF_DATA_DIR, getDefaultDataDirectory());
store.setDefault(TasksUiPreferenceConstants.GROUP_SUBTASKS, true);
store.setDefault(TasksUiPreferenceConstants.NOTIFICATIONS_ENABLED, true);
store.setDefault(TasksUiPreferenceConstants.FILTER_PRIORITY, PriorityLevel.P5.toString());
store.setDefault(TasksUiPreferenceConstants.EDITOR_TASKS_RICH, true);
store.setDefault(TasksUiPreferenceConstants.ACTIVATE_WHEN_OPENED, false);
store.setDefault(TasksUiPreferenceConstants.SHOW_TRIM, false);
store.setDefault(TasksUiPreferenceConstants.LOCAL_SUB_TASKS_ENABLED, false);
store.setDefault(TasksUiPreferenceConstants.REPOSITORY_SYNCH_SCHEDULE_ENABLED, true);
store.setDefault(TasksUiPreferenceConstants.REPOSITORY_SYNCH_SCHEDULE_MILISECONDS, "" + (20 * 60 * 1000));
store.setDefault(TasksUiPreferenceConstants.BACKUP_SCHEDULE, 1);
store.setDefault(TasksUiPreferenceConstants.BACKUP_MAXFILES, 20);
store.setDefault(TasksUiPreferenceConstants.BACKUP_LAST, 0f);
store.setDefault(TasksUiPreferenceConstants.FILTER_ARCHIVE_MODE, true);
store.setDefault(TasksUiPreferenceConstants.ACTIVATE_MULTIPLE, false);
store.setValue(TasksUiPreferenceConstants.ACTIVATE_MULTIPLE, false);
store.setDefault(TasksUiPreferenceConstants.PLANNING_STARTHOUR, 9);
store.setDefault(TasksUiPreferenceConstants.PLANNING_ENDHOUR, 18);
}
public static TaskListManager getTaskListManager() {
return taskListManager;
}
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(AbstractTaskContainer container) {
boolean groupSubtasks = TasksUiPlugin.getDefault().getPreferenceStore().getBoolean(
TasksUiPreferenceConstants.GROUP_SUBTASKS);
if (container instanceof AbstractTask) {
AbstractRepositoryConnectorUi connectorUi = TasksUiPlugin.getConnectorUi(((AbstractTask) container).getConnectorKind());
if (connectorUi != null) {
if (connectorUi.forceSubtaskHierarchy()) {
groupSubtasks = true;
}
}
}
if (container instanceof AbstractRepositoryQuery) {
AbstractRepositoryConnectorUi connectorUi = TasksUiPlugin.getConnectorUi(((AbstractRepositoryQuery) container).getRepositoryKind());
if (connectorUi != null) {
if (connectorUi.forceSubtaskHierarchy()) {
groupSubtasks = true;
}
}
}
return groupSubtasks;
}
private Map<String, List<IDynamicSubMenuContributor>> menuContributors = new HashMap<String, List<IDynamicSubMenuContributor>>();
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 + ITasksUiConstants.DEFAULT_BACKUP_FOLDER_NAME;
}
public ITaskHighlighter getHighlighter() {
return highlighter;
}
public void setHighlighter(ITaskHighlighter highlighter) {
this.highlighter = highlighter;
}
public Set<AbstractTaskEditorFactory> getTaskEditorFactories() {
return taskEditorFactories;
}
public void addContextEditor(AbstractTaskEditorFactory contextEditor) {
if (contextEditor != null) {
this.taskEditorFactories.add(contextEditor);
}
}
public static TaskRepositoryManager getRepositoryManager() {
return taskRepositoryManager;
}
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 boolean isInitialized() {
return initialized;
}
public IHyperlinkDetector[] getTaskHyperlinkDetectors() {
return hyperlinkDetectors.toArray(new IHyperlinkDetector[1]);
}
public void addTaskHyperlinkDetector(IHyperlinkDetector listener) {
if (listener != null) {
this.hyperlinkDetectors.add(listener);
}
}
public void addRepositoryLinkProvider(AbstractTaskRepositoryLinkProvider repositoryLinkProvider) {
if (repositoryLinkProvider != null) {
this.repositoryLinkProviders.add(repositoryLinkProvider);
}
}
public TaskListBackupManager getBackupManager() {
return taskListBackupManager;
}
private void startOfflineStorageManager() {
//IPath offlineReportsPath = Platform.getStateLocation(TasksUiPlugin.getDefault().getBundle());
File root = new File(this.getDataDirectory() + '/' + FOLDER_OFFLINE);
OfflineFileStorage storage = new OfflineFileStorage(root);
OfflineCachingStorage cachedStorage = new OfflineCachingStorage(storage);
taskDataManager = new TaskDataManager(taskRepositoryManager, cachedStorage);
taskDataManager.start();
}
public static TaskDataManager getTaskDataManager() {
if (INSTANCE == null || INSTANCE.taskDataManager == null) {
StatusHandler.fail(new Status(IStatus.ERROR, TasksUiPlugin.ID_PLUGIN,
"Offline reports file not created, try restarting."));
return null;
} else {
return INSTANCE.taskDataManager;
}
}
public void addRepositoryConnectorUi(AbstractRepositoryConnectorUi repositoryConnectorUi) {
if (!repositoryConnectorUiMap.values().contains(repositoryConnectorUi)) {
repositoryConnectorUiMap.put(repositoryConnectorUi.getConnectorKind(), repositoryConnectorUi);
}
}
public static AbstractRepositoryConnectorUi getConnectorUi(String kind) {
return repositoryConnectorUiMap.get(kind);
}
public static TaskListSynchronizationScheduler getSynchronizationScheduler() {
return synchronizationScheduler;
}
public static RepositorySynchronizationManager getSynchronizationManager() {
return synchronizationManager;
}
public void addDuplicateDetector(AbstractDuplicateDetector duplicateDetector) {
if (duplicateDetector != null) {
duplicateDetectors.add(duplicateDetector);
}
}
public Set<AbstractDuplicateDetector> getDuplicateSearchCollectorsList() {
return duplicateDetectors;
}
public String getRepositoriesFilePath() {
return getDataDirectory() + File.separator + TaskRepositoryManager.DEFAULT_REPOSITORIES_FILE;
}
public boolean canSetRepositoryForResource(IResource resource) {
if (resource == null) {
return false;
}
// find first provider that can link repository
for (AbstractTaskRepositoryLinkProvider linkProvider : repositoryLinkProviders) {
TaskRepository repository = linkProvider.getTaskRepository(resource, getRepositoryManager());
if (repository != null) {
return linkProvider.canSetTaskRepository(resource);
}
}
// find first provider that can set new repository
for (AbstractTaskRepositoryLinkProvider linkProvider : repositoryLinkProviders) {
if (linkProvider.canSetTaskRepository(resource)) {
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(IResource resource, TaskRepository repository) throws CoreException {
if (resource == null || repository == null) {
return;
}
for (AbstractTaskRepositoryLinkProvider linkProvider : repositoryLinkProviders) {
TaskRepository r = linkProvider.getTaskRepository(resource, getRepositoryManager());
boolean canSetRepository = linkProvider.canSetTaskRepository(resource);
if (r != null && !canSetRepository) {
return;
}
if (canSetRepository) {
linkProvider.setTaskRepository(resource, repository);
return;
}
}
}
/**
* 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.
*
* API-3.0: remove "silent" parameter
*/
public TaskRepository getRepositoryForResource(IResource resource, boolean silent) {
if (resource == null) {
return null;
}
Set<AbstractTaskRepositoryLinkProvider> defectiveLinkProviders = new HashSet<AbstractTaskRepositoryLinkProvider>();
for (AbstractTaskRepositoryLinkProvider linkProvider : repositoryLinkProviders) {
long startTime = System.nanoTime();
TaskRepository repository = linkProvider.getTaskRepository(resource, getRepositoryManager());
long elapsed = System.nanoTime() - startTime;
if (elapsed > LINK_PROVIDER_TIMEOUT_SECONDS * 1000 * 1000 * 1000) {
defectiveLinkProviders.add(linkProvider);
}
if (repository != null) {
return repository;
}
}
if (!defectiveLinkProviders.isEmpty()) {
repositoryLinkProviders.removeAll(defectiveLinkProviders);
StatusHandler.log(new Status(IStatus.WARNING, ID_PLUGIN,
"Repository link provider took over 5s to execute and was timed out: " + defectiveLinkProviders));
}
if (!silent) {
MessageDialog.openInformation(null, "No Repository Found",
"No repository was found. Associate a Task Repository with this project via the project's property page.");
}
return null;
}
/**
* Public for testing.
*/
public static TaskListSaveManager getTaskListSaveManager() {
return INSTANCE.taskListSaveManager;
}
public String getNextNewRepositoryTaskId() {
return getTaskDataManager().getNewRepositoryTaskId();
}
/**
* TODO: move, uses and exposes internal class.
*
* @Deprecated
*/
public TaskListNotification getIncommingNotification(AbstractRepositoryConnector connector, AbstractTask task) {
TaskListNotification notification = new TaskListNotification(task);
RepositoryTaskData newTaskData = getTaskDataManager().getNewTaskData(task.getRepositoryUrl(), task.getTaskId());
RepositoryTaskData oldTaskData = getTaskDataManager().getOldTaskData(task.getRepositoryUrl(), task.getTaskId());
try {
if (task.getSynchronizationState().equals(RepositoryTaskSyncState.INCOMING)
&& task.getLastReadTimeStamp() == null) {
notification.setDescription("New unread task ");
} else if (newTaskData != null && oldTaskData != null) {
StringBuilder description = new StringBuilder();
String changedDescription = getChangedDescription(newTaskData, oldTaskData);
String changedAttributes = getChangedAttributes(newTaskData, oldTaskData);
if (!"".equals(changedDescription.trim())) {
description.append(changedDescription);
if (!"".equals(changedAttributes)) {
description.append('\n');
}
}
if (!"".equals(changedAttributes)) {
description.append(changedAttributes);
}
notification.setDescription(description.toString());
if (connector != null) {
AbstractTaskDataHandler offlineHandler = connector.getTaskDataHandler();
if (offlineHandler != null && newTaskData.getLastModified() != null) {
Date modified = newTaskData.getAttributeFactory().getDateForAttributeType(
RepositoryTaskAttribute.DATE_MODIFIED, newTaskData.getLastModified());
notification.setDate(modified);
}
}
} else {
notification.setDescription("Unread task");
}
} catch (Throwable t) {
StatusHandler.log(new Status(IStatus.ERROR, TasksUiPlugin.ID_PLUGIN,
"Could not format notification for: " + task, t));
}
return notification;
}
private static String getChangedDescription(RepositoryTaskData newTaskData, RepositoryTaskData oldTaskData) {
String descriptionText = "";
if (newTaskData.getComments().size() > oldTaskData.getComments().size()) {
List<TaskComment> taskComments = newTaskData.getComments();
if (taskComments != null && taskComments.size() > 0) {
TaskComment lastComment = taskComments.get(taskComments.size() - 1);
if (lastComment != null) {
// descriptionText += "Comment by " + lastComment.getAuthor() + ":\n ";
descriptionText += lastComment.getAuthor() + ": ";
descriptionText += cleanValue(lastComment.getText());
}
}
}
return descriptionText;
}
private static String getChangedAttributes(RepositoryTaskData newTaskData, RepositoryTaskData oldTaskData) {
List<Change> changes = new ArrayList<Change>();
for (RepositoryTaskAttribute newAttribute : newTaskData.getAttributes()) {
if (ignoreAttribute(newTaskData, newAttribute)) {
continue;
}
List<String> newValues = newAttribute.getValues();
if (newValues != null) {
RepositoryTaskAttribute oldAttribute = oldTaskData.getAttribute(newAttribute.getId());
if (oldAttribute == null) {
changes.add(getDiff(newTaskData, newAttribute, null, newValues));
}
if (oldAttribute != null) {
List<String> oldValues = oldAttribute.getValues();
if (!oldValues.equals(newValues)) {
changes.add(getDiff(newTaskData, newAttribute, oldValues, newValues));
}
}
}
}
for (RepositoryTaskAttribute oldAttribute : oldTaskData.getAttributes()) {
if (ignoreAttribute(oldTaskData, oldAttribute)) {
continue;
}
RepositoryTaskAttribute attribute = newTaskData.getAttribute(oldAttribute.getId());
List<String> values = oldAttribute.getValues();
if (attribute == null && values != null && !values.isEmpty()) {
changes.add(getDiff(oldTaskData, oldAttribute, values, null));
}
}
if (changes.isEmpty()) {
return "";
}
String details = "";
String sep = "";
int n = 0;
for (Change change : changes) {
String removed = cleanValues(change.removed);
String added = cleanValues(change.added);
details += sep + " " + change.field + " " + removed;
if (removed.length() > 30) {
// details += "\n ";
details += "\n ";
}
details += " -> " + added;
sep = "\n";
if (++n == MAX_CHANGED_ATTRIBUTES) {
break;
}
}
// if (!details.equals("")) {
// return details;
// return "Attributes Changed:\n" + details;
// }
return details;
}
private static String cleanValues(List<String> values) {
StringBuilder sb = new StringBuilder();
boolean first = true;
for (String value : values) {
if (!first) {
sb.append(", ");
}
sb.append(cleanValue(value));
first = false;
}
return sb.toString();
}
private static String cleanValue(String value) {
String commentText = value.replaceAll("\\s", " ").trim();
if (commentText.length() > 60) {
commentText = commentText.substring(0, 55) + "...";
}
return commentText;
}
private static boolean ignoreAttribute(RepositoryTaskData taskData, RepositoryTaskAttribute attribute) {
AbstractAttributeFactory factory = taskData.getAttributeFactory();
return (attribute.getId().equals(factory.mapCommonAttributeKey(RepositoryTaskAttribute.DATE_MODIFIED))
|| attribute.getId().equals(factory.mapCommonAttributeKey(RepositoryTaskAttribute.DATE_CREATION))
|| "delta_ts".equals(attribute.getId()) || "longdesclength".equals(attribute.getId()));
}
private static Change getDiff(RepositoryTaskData taskData, RepositoryTaskAttribute attribute,
List<String> oldValues, List<String> newValues) {
// AbstractAttributeFactory factory = taskData.getAttributeFactory();
// if (attribute.getId().equals(factory.mapCommonAttributeKey(RepositoryTaskAttribute.DATE_MODIFIED))
// || attribute.getId().equals(factory.mapCommonAttributeKey(RepositoryTaskAttribute.DATE_CREATION))) {
// if (newValues != null && newValues.size() > 0) {
// for (int i = 0; i < newValues.size(); i++) {
// newValues.set(i, factory.getDateForAttributeType(attribute.getId(), newValues.get(i)).toString());
// }
// }
//
// Change change = new Change(attribute.getName(), newValues);
// if (oldValues != null) {
// for (String value : oldValues) {
// value = factory.getDateForAttributeType(attribute.getId(), value).toString();
// if (change.added.contains(value)) {
// change.added.remove(value);
// } else {
// change.removed.add(value);
// }
// }
// }
// return change;
// }
Change change = new Change(attribute.getName(), newValues);
if (oldValues != null) {
for (String value : oldValues) {
if (change.added.contains(value)) {
change.added.remove(value);
} else {
change.removed.add(value);
}
}
}
return change;
}
private static class Change {
final String field;
final List<String> added;
final List<String> removed = new ArrayList<String>();
public Change(String field, List<String> newValues) {
this.field = field;
if (newValues != null) {
this.added = new ArrayList<String>(newValues);
} else {
this.added = new ArrayList<String>();
}
}
}
}