blob: 08d1b3e766cf43675883230bcdcb7998aaec3df7 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2004, 2008 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
* Ken Sueda - XML serialization support
*******************************************************************************/
package org.eclipse.mylyn.internal.tasks.core.externalization;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.ISafeRunnable;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.core.runtime.Status;
import org.eclipse.mylyn.internal.tasks.core.AbstractTask;
import org.eclipse.mylyn.internal.tasks.core.AbstractTaskCategory;
import org.eclipse.mylyn.internal.tasks.core.AbstractTaskContainer;
import org.eclipse.mylyn.internal.tasks.core.DayDateRange;
import org.eclipse.mylyn.internal.tasks.core.ITasksCoreConstants;
import org.eclipse.mylyn.internal.tasks.core.ITransferList;
import org.eclipse.mylyn.internal.tasks.core.LocalRepositoryConnector;
import org.eclipse.mylyn.internal.tasks.core.LocalTask;
import org.eclipse.mylyn.internal.tasks.core.RepositoryModel;
import org.eclipse.mylyn.internal.tasks.core.RepositoryQuery;
import org.eclipse.mylyn.internal.tasks.core.RepositoryTaskHandleUtil;
import org.eclipse.mylyn.internal.tasks.core.TaskActivityUtil;
import org.eclipse.mylyn.internal.tasks.core.TaskCategory;
import org.eclipse.mylyn.internal.tasks.core.TaskExternalizationException;
import org.eclipse.mylyn.internal.tasks.core.TaskTask;
import org.eclipse.mylyn.internal.tasks.core.UncategorizedTaskContainer;
import org.eclipse.mylyn.internal.tasks.core.WeekDateRange;
import org.eclipse.mylyn.tasks.core.AbstractTaskListMigrator;
import org.eclipse.mylyn.tasks.core.IAttributeContainer;
import org.eclipse.mylyn.tasks.core.IRepositoryManager;
import org.eclipse.mylyn.tasks.core.IRepositoryQuery;
import org.eclipse.mylyn.tasks.core.ITask;
import org.eclipse.mylyn.tasks.core.TaskRepository;
import org.eclipse.mylyn.tasks.core.ITask.PriorityLevel;
import org.eclipse.mylyn.tasks.core.ITask.SynchronizationState;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* Subclass externalizers must override the get*TagName() methods for the types of externalized items they support to
* ensure that their externalizer does not externalize tasks from other connectors incorrectly.
*
* These tag names uniquely identify the externalizer to be used to read the task from externalized form on disk.
*
* The canCreateElementFor methods specify which tasks the externalizer should write to disk.
*
* The TaskList is read on startup, so externalizers extending this should not perform any slow (i.e., network)
* operations when overriding methods.
*
* @author Mik Kersten
* @author Steffen Pingel
*/
public final class DelegatingTaskExternalizer {
static final String DEFAULT_PRIORITY = PriorityLevel.P3.toString();
static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss.S z"; //$NON-NLS-1$
static final String KEY_NOTIFIED_INCOMING = "NotifiedIncoming"; //$NON-NLS-1$
public static final String KEY_NAME = "Name"; //$NON-NLS-1$
public static final String KEY_LABEL = "Label"; //$NON-NLS-1$
public static final String KEY_QUERY = "Query"; //$NON-NLS-1$
public static final String KEY_QUERY_STRING = "QueryString"; //$NON-NLS-1$
static final String KEY_HANDLE = "Handle"; //$NON-NLS-1$
public static final String KEY_REPOSITORY_URL = "RepositoryUrl"; //$NON-NLS-1$
public static final String KEY_CATEGORY = "Category"; //$NON-NLS-1$
static final String VAL_ROOT = "Root"; //$NON-NLS-1$
static final String KEY_SUBTASK = "SubTask"; //$NON-NLS-1$
static final String KEY_KIND = "Kind"; //$NON-NLS-1$
static final String KEY_TASK_CATEGORY = "Task" + KEY_CATEGORY; //$NON-NLS-1$
static final String KEY_LINK = "Link"; //$NON-NLS-1$
static final String KEY_PLAN = "Plan"; //$NON-NLS-1$
static final String KEY_TIME_ESTIMATED = "Estimated"; //$NON-NLS-1$
static final String KEY_ISSUEURL = "IssueURL"; //$NON-NLS-1$
static final String KEY_NOTES = "Notes"; //$NON-NLS-1$
static final String KEY_ACTIVE = "Active"; //$NON-NLS-1$
static final String KEY_PRIORITY = "Priority"; //$NON-NLS-1$
static final String KEY_PATH = "Path"; //$NON-NLS-1$
static final String VAL_FALSE = "false"; //$NON-NLS-1$
static final String VAL_TRUE = "true"; //$NON-NLS-1$
static final String KEY_DATE_END = "EndDate"; //$NON-NLS-1$
static final String KEY_QUERY_HIT = "QueryHit"; //$NON-NLS-1$
static final String KEY_TASK_REFERENCE = "TaskReference"; //$NON-NLS-1$
static final String KEY_DATE_CREATION = "CreationDate"; //$NON-NLS-1$
static final String KEY_DATE_REMINDER = "ReminderDate"; //$NON-NLS-1$
static final String KEY_DATE_SCHEDULED_START = "ScheduledStartDate"; //$NON-NLS-1$
static final String KEY_DATE_SCHEDULED_END = "ScheduledEndDate"; //$NON-NLS-1$
static final String KEY_DATE_MODIFICATION = "ModificationDate"; //$NON-NLS-1$
static final String KEY_DATE_DUE = "DueDate"; //$NON-NLS-1$
static final String KEY_REMINDED = "Reminded"; //$NON-NLS-1$
static final String KEY_FLOATING = "Floating"; //$NON-NLS-1$
/**
* This element holds the date stamp recorded upon last transition to a synchronized state.
*/
static final String KEY_LAST_MOD_DATE = "LastModified"; //$NON-NLS-1$
static final String KEY_DIRTY = "Dirty"; //$NON-NLS-1$
static final String KEY_SYNC_STATE = "offlineSyncState"; //$NON-NLS-1$
static final String KEY_OWNER = "Owner"; //$NON-NLS-1$
static final String KEY_MARK_READ_PENDING = "MarkReadPending"; //$NON-NLS-1$
static final String KEY_STALE = "Stale"; //$NON-NLS-1$
static final String KEY_CONNECTOR_KIND = "ConnectorKind"; //$NON-NLS-1$
static final String KEY_TASK_ID = "TaskId"; //$NON-NLS-1$
public static final String KEY_LAST_REFRESH = "LastRefreshTimeStamp"; //$NON-NLS-1$
static final String NODE_ATTRIBUTE = "Attribute"; //$NON-NLS-1$
static final String NODE_QUERY = "Query"; //$NON-NLS-1$
static final String NODE_TASK = "Task"; //$NON-NLS-1$
static final String KEY_KEY = "Key"; //$NON-NLS-1$
// 2.0 -> 3.0 migration holds tasks to category handles
private final Map<AbstractTask, String> parentCategoryMap;
private final RepositoryModel repositoryModel;
private List<AbstractTaskListMigrator> migrators;
private boolean taskActivated;
private final IRepositoryManager repositoryManager;
private final List<IStatus> errors;
public DelegatingTaskExternalizer(RepositoryModel repositoryModel, IRepositoryManager repositoryManager) {
Assert.isNotNull(repositoryModel);
Assert.isNotNull(repositoryManager);
this.repositoryModel = repositoryModel;
this.repositoryManager = repositoryManager;
this.parentCategoryMap = new HashMap<AbstractTask, String>();
this.errors = new ArrayList<IStatus>();
this.migrators = Collections.emptyList();
}
public void initialize(List<AbstractTaskListMigrator> migrators) {
Assert.isNotNull(migrators);
this.migrators = migrators;
}
public Element createCategoryElement(AbstractTaskCategory category, Document doc, Element parent) {
Element node = doc.createElement(getCategoryTagName());
node.setAttribute(DelegatingTaskExternalizer.KEY_HANDLE, category.getHandleIdentifier());
node.setAttribute(DelegatingTaskExternalizer.KEY_NAME, category.getSummary());
parent.appendChild(node);
for (ITask task : category.getChildren()) {
createTaskReference(KEY_TASK_REFERENCE, task, doc, node);
}
return node;
}
@SuppressWarnings("deprecation")
public Element createTaskElement(final AbstractTask task, Document doc, Element parent) {
final Element node;
if (task.getClass() == TaskTask.class || task instanceof LocalTask) {
node = doc.createElement(NODE_TASK);
} else {
errors.add(new Status(IStatus.WARNING, ITasksCoreConstants.ID_PLUGIN, "No externalizer for task: " + task)); //$NON-NLS-1$
return null;
}
node.setAttribute(KEY_CONNECTOR_KIND, task.getConnectorKind());
node.setAttribute(KEY_REPOSITORY_URL, task.getRepositoryUrl());
node.setAttribute(KEY_TASK_ID, task.getTaskId());
if (task.getTaskKey() != null) {
node.setAttribute(KEY_KEY, task.getTaskKey());
}
node.setAttribute(KEY_HANDLE, task.getHandleIdentifier());
node.setAttribute(KEY_LABEL, stripControlCharacters(task.getSummary()));
node.setAttribute(KEY_PRIORITY, task.getPriority());
node.setAttribute(KEY_KIND, task.getTaskKind());
if (task.isActive()) {
node.setAttribute(KEY_ACTIVE, VAL_TRUE);
} else {
node.setAttribute(KEY_ACTIVE, VAL_FALSE);
}
if (task.getUrl() != null) {
node.setAttribute(KEY_ISSUEURL, task.getUrl());
}
node.setAttribute(KEY_NOTES, stripControlCharacters(task.getNotes()));
node.setAttribute(KEY_TIME_ESTIMATED, "" + task.getEstimatedTimeHours()); //$NON-NLS-1$
node.setAttribute(KEY_DATE_END, formatExternDate(task.getCompletionDate()));
node.setAttribute(KEY_DATE_CREATION, formatExternDate(task.getCreationDate()));
node.setAttribute(KEY_DATE_MODIFICATION, formatExternDate(task.getModificationDate()));
node.setAttribute(KEY_DATE_DUE, formatExternDate(task.getDueDate()));
if (task.getScheduledForDate() != null) {
node.setAttribute(KEY_DATE_SCHEDULED_START, formatExternCalendar(task.getScheduledForDate().getStartDate()));
node.setAttribute(KEY_DATE_SCHEDULED_END, formatExternCalendar(task.getScheduledForDate().getEndDate()));
}
if (task.isReminded()) {
node.setAttribute(KEY_REMINDED, VAL_TRUE);
} else {
node.setAttribute(KEY_REMINDED, VAL_FALSE);
}
if (task.isStale()) {
node.setAttribute(KEY_STALE, VAL_TRUE);
} else {
node.setAttribute(KEY_STALE, VAL_FALSE);
}
if (task.isMarkReadPending()) {
node.setAttribute(KEY_MARK_READ_PENDING, VAL_TRUE);
} else {
node.setAttribute(KEY_MARK_READ_PENDING, VAL_FALSE);
}
if (task.getLastReadTimeStamp() != null) {
node.setAttribute(KEY_LAST_MOD_DATE, task.getLastReadTimeStamp());
}
if (task.isNotified()) {
node.setAttribute(KEY_NOTIFIED_INCOMING, VAL_TRUE);
} else {
node.setAttribute(KEY_NOTIFIED_INCOMING, VAL_FALSE);
}
if (task.getSynchronizationState() != null) {
node.setAttribute(KEY_SYNC_STATE, task.getSynchronizationState().name());
} else {
node.setAttribute(KEY_SYNC_STATE, SynchronizationState.SYNCHRONIZED.name());
}
if (task.getOwner() != null) {
node.setAttribute(KEY_OWNER, task.getOwner());
}
createAttributes(task, doc, node);
for (ITask t : task.getChildren()) {
createTaskReference(KEY_SUBTASK, t, doc, node);
}
parent.appendChild(node);
return node;
}
private void createAttributes(IAttributeContainer container, Document doc, Element parent) {
Map<String, String> attributes = container.getAttributes();
for (Map.Entry<String, String> entry : attributes.entrySet()) {
Element node = doc.createElement(NODE_ATTRIBUTE);
node.setAttribute(KEY_KEY, entry.getKey());
node.setTextContent(entry.getValue());
parent.appendChild(node);
}
}
/**
* creates nested task reference nodes named nodeName which include a handle to the task
*
* @return
*/
public Element createTaskReference(String nodeName, ITask task, Document doc, Element parent) {
Element node = doc.createElement(nodeName);
node.setAttribute(KEY_HANDLE, task.getHandleIdentifier());
parent.appendChild(node);
return node;
}
/**
* create tasks from the nodes provided and places them within the given container
*/
public void readTaskReferences(AbstractTaskContainer task, NodeList nodes, ITransferList tasklist) {
for (int j = 0; j < nodes.getLength(); j++) {
Node child = nodes.item(j);
Element element = (Element) child;
if (element.hasAttribute(KEY_HANDLE)) {
String handle = element.getAttribute(KEY_HANDLE);
AbstractTask subTask = tasklist.getTask(handle);
if (subTask != null) {
tasklist.addTask(subTask, task);
} else {
errors.add(new Status(IStatus.WARNING, ITasksCoreConstants.ID_PLUGIN,
"Failed to add subtask with handle \"" + handle + "\" to \"" + task + "\"")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
}
}
}
@SuppressWarnings( { "deprecation", "restriction" })
private String stripControlCharacters(String text) {
if (text == null) {
return ""; //$NON-NLS-1$
}
return org.eclipse.mylyn.internal.commons.core.XmlStringConverter.cleanXmlString(text);
}
private String formatExternDate(Date date) {
if (date == null) {
return ""; //$NON-NLS-1$
}
String f = DATE_FORMAT;
SimpleDateFormat format = new SimpleDateFormat(f, Locale.ENGLISH);
return format.format(date);
}
private String formatExternCalendar(Calendar date) {
if (date == null) {
return ""; //$NON-NLS-1$
}
String f = DATE_FORMAT;
SimpleDateFormat format = new SimpleDateFormat(f, Locale.ENGLISH);
return format.format(date.getTime());
}
public void readCategory(Node node, ITransferList taskList) {
Element element = (Element) node;
AbstractTaskCategory category = null;
if (element.hasAttribute(KEY_NAME)) {
String name = element.getAttribute(KEY_NAME);
String handle = name;
if (element.hasAttribute(KEY_HANDLE)) {
handle = element.getAttribute(KEY_HANDLE);
}
category = taskList.getContainerForHandle(handle);
if (category == null) {
category = new TaskCategory(handle, name);
taskList.addCategory((TaskCategory) category);
} else if (!UncategorizedTaskContainer.HANDLE.equals(handle)) {
errors.add(new Status(IStatus.WARNING, ITasksCoreConstants.ID_PLUGIN, "Category with handle \"" + name //$NON-NLS-1$
+ "\" already exists in task list")); //$NON-NLS-1$
}
} else {
errors.add(new Status(IStatus.WARNING, ITasksCoreConstants.ID_PLUGIN, "Category is missing name attribute")); //$NON-NLS-1$
// LEGACY: registry categories did not have names
// category = taskList.getArchiveContainer();
// a null category will now go into appropriate orphaned category
}
NodeList list = node.getChildNodes();
readTaskReferences(category, list, taskList);
}
@SuppressWarnings("deprecation")
public final AbstractTask readTask(Node node, AbstractTaskCategory legacyCategory, ITask parent)
throws CoreException {
String handle;
String taskId;
String repositoryUrl;
String summary = ""; //$NON-NLS-1$
final Element element = (Element) node;
if (element.hasAttribute(KEY_REPOSITORY_URL) && element.hasAttribute(KEY_TASK_ID)
&& element.hasAttribute(KEY_HANDLE)) {
handle = element.getAttribute(KEY_HANDLE);
repositoryUrl = element.getAttribute(KEY_REPOSITORY_URL);
taskId = element.getAttribute(KEY_TASK_ID);
} else if (element.hasAttribute(KEY_HANDLE)) {
handle = element.getAttribute(KEY_HANDLE);
repositoryUrl = RepositoryTaskHandleUtil.getRepositoryUrl(handle);
taskId = RepositoryTaskHandleUtil.getTaskId(handle);
} else {
errors.add(new Status(IStatus.WARNING, ITasksCoreConstants.ID_PLUGIN, "Task is missing handle attribute")); //$NON-NLS-1$
return null;
}
if (element.hasAttribute(KEY_LABEL)) {
summary = element.getAttribute(KEY_LABEL);
}
AbstractTask task = null;
AbstractTaskListMigrator taskMigrator = null;
if (NODE_TASK.equals(node.getNodeName())) {
String connectorKind = element.getAttribute(DelegatingTaskExternalizer.KEY_CONNECTOR_KIND);
task = readDefaultTask(connectorKind, repositoryUrl, taskId, summary, element);
}
// attempt migration from < 3.0 task list
if (task == null) {
for (AbstractTaskListMigrator migrator : migrators) {
if (node.getNodeName().equals(migrator.getTaskElementName())) {
task = readDefaultTask(migrator.getConnectorKind(), repositoryUrl, taskId, summary, element);
taskMigrator = migrator;
break;
}
}
}
// populate common attributes
if (task != null) {
if (repositoryManager.getRepositoryConnector(task.getConnectorKind()) == null) {
errors.add(new Status(IStatus.WARNING, ITasksCoreConstants.ID_PLUGIN,
"Missing connector for task with kind \"" + task.getConnectorKind() + "\"")); //$NON-NLS-1$ //$NON-NLS-2$
return null;
}
readTaskInfo(task, element, parent, legacyCategory);
readAttributes(task, element);
if (taskMigrator != null) {
if (task.getSynchronizationState() == SynchronizationState.INCOMING
&& task.getLastReadTimeStamp() == null) {
task.setSynchronizationState(SynchronizationState.INCOMING_NEW);
}
task.setTaskKey(task.getTaskId());
final AbstractTaskListMigrator finalTaskMigrator = taskMigrator;
final AbstractTask finalTask = task;
SafeRunner.run(new ISafeRunnable() {
public void handleException(Throwable e) {
errors.add(new Status(IStatus.WARNING, ITasksCoreConstants.ID_PLUGIN,
"Task migration failed for task \"" + finalTask + "\"", e)); //$NON-NLS-1$ //$NON-NLS-2$
}
public void run() throws Exception {
finalTaskMigrator.migrateTask(finalTask, element);
}
});
}
return task;
} else {
errors.add(new Status(IStatus.WARNING, ITasksCoreConstants.ID_PLUGIN, "Missing connector for task node \"" //$NON-NLS-1$
+ node.getNodeName() + "\"")); //$NON-NLS-1$
return null;
}
}
private void readAttributes(IAttributeContainer container, Element parent) {
NodeList list = parent.getChildNodes();
for (int i = 0; i < list.getLength(); i++) {
Node child = list.item(i);
if (child instanceof Element && child.getNodeName().equals(NODE_ATTRIBUTE)) {
Element element = (Element) child;
String key = element.getAttribute(KEY_KEY);
if (key.length() > 0) {
container.setAttribute(key, element.getTextContent());
}
}
}
}
@SuppressWarnings("deprecation")
private void readTaskInfo(AbstractTask task, Element element, ITask parent, AbstractTaskCategory legacyCategory) {
if (element.hasAttribute(KEY_CATEGORY)) {
// Migration 2.0 -> 3.0 task list. Category no longer maintained on the task element but
// task handles held within category nodes similar to query children
String categoryHandle = element.getAttribute(KEY_CATEGORY);
if (categoryHandle.equals(VAL_ROOT)) {
categoryHandle = UncategorizedTaskContainer.HANDLE;
}
//task.setCategoryHandle(categoryHandle);
parentCategoryMap.put(task, categoryHandle);
}
if (element.hasAttribute(KEY_PRIORITY)) {
task.setPriority(element.getAttribute(KEY_PRIORITY));
} else {
task.setPriority(DEFAULT_PRIORITY);
}
if (element.hasAttribute(KEY_KIND)) {
task.setTaskKind(element.getAttribute(KEY_KIND));
}
if (!taskActivated && element.getAttribute(KEY_ACTIVE).compareTo(VAL_TRUE) == 0) {
task.setActive(true);
taskActivated = true;
} else {
task.setActive(false);
}
if (element.hasAttribute(KEY_ISSUEURL)) {
task.setUrl(element.getAttribute(KEY_ISSUEURL));
} else {
task.setUrl(""); //$NON-NLS-1$
}
if (element.hasAttribute(KEY_NOTES)) {
task.setNotes(element.getAttribute(KEY_NOTES));
} else {
task.setNotes(""); //$NON-NLS-1$
}
if (element.hasAttribute(KEY_TIME_ESTIMATED)) {
String est = element.getAttribute(KEY_TIME_ESTIMATED);
try {
int estimate = Integer.parseInt(est);
task.setEstimatedTimeHours(estimate);
} catch (Exception e) {
task.setEstimatedTimeHours(0);
}
} else {
task.setEstimatedTimeHours(0);
}
if (element.hasAttribute(KEY_DATE_END)) {
task.setCompletionDate(getDateFromString(element.getAttribute(KEY_DATE_END)));
} else {
task.setCompletionDate(null);
}
if (element.hasAttribute(KEY_DATE_CREATION)) {
task.setCreationDate(getDateFromString(element.getAttribute(KEY_DATE_CREATION)));
} else {
task.setCreationDate(null);
}
if (element.hasAttribute(KEY_DATE_MODIFICATION)) {
task.setModificationDate(getDateFromString(element.getAttribute(KEY_DATE_MODIFICATION)));
} else {
task.setModificationDate(null);
}
if (element.hasAttribute(KEY_DATE_DUE)) {
task.setDueDate(getDateFromString(element.getAttribute(KEY_DATE_DUE)));
} else {
task.setDueDate(null);
}
// Legacy 2.3.2 -> 3.0 migration of scheduled date
boolean isFloating = false;
if (element.hasAttribute(KEY_FLOATING) && element.getAttribute(KEY_FLOATING).compareTo(VAL_TRUE) == 0) {
isFloating = true;
} else {
isFloating = false;
}
if (element.hasAttribute(KEY_DATE_REMINDER)) {
Date date = getDateFromString(element.getAttribute(KEY_DATE_REMINDER));
if (date != null) {
if (isFloating) {
task.setScheduledForDate(TaskActivityUtil.getWeekOf(date));
} else {
task.setScheduledForDate(TaskActivityUtil.getDayOf(date));
}
}
}
// Scheduled date range (3.0)
if (element.hasAttribute(KEY_DATE_SCHEDULED_START) && element.hasAttribute(KEY_DATE_SCHEDULED_END)) {
Date startDate = getDateFromString(element.getAttribute(KEY_DATE_SCHEDULED_START));
Date endDate = getDateFromString(element.getAttribute(KEY_DATE_SCHEDULED_END));
if (startDate != null && endDate != null && startDate.compareTo(endDate) <= 0) {
Calendar calStart = TaskActivityUtil.getCalendar();
calStart.setTime(startDate);
Calendar calEnd = TaskActivityUtil.getCalendar();
calEnd.setTime(endDate);
if (DayDateRange.isDayRange(calStart, calEnd)) {
task.setScheduledForDate(new DayDateRange(calStart, calEnd));
} else if (WeekDateRange.isWeekRange(calStart, calEnd)) {
task.setScheduledForDate(new WeekDateRange(calStart, calEnd));
} else {
// Neither week nor day found, default to today
task.setScheduledForDate(TaskActivityUtil.getDayOf(new Date()));
}
}
}
if (element.hasAttribute(KEY_REMINDED) && element.getAttribute(KEY_REMINDED).compareTo(VAL_TRUE) == 0) {
task.setReminded(true);
} else {
task.setReminded(false);
}
if (element.hasAttribute(KEY_STALE) && element.getAttribute(KEY_STALE).compareTo(VAL_TRUE) == 0) {
task.setStale(true);
} else {
task.setStale(false);
}
if (element.hasAttribute(KEY_MARK_READ_PENDING)
&& element.getAttribute(KEY_MARK_READ_PENDING).compareTo(VAL_TRUE) == 0) {
task.setMarkReadPending(true);
} else {
task.setMarkReadPending(false);
}
task.setSynchronizing(false);
if (element.hasAttribute(KEY_REPOSITORY_URL)) {
task.setRepositoryUrl(element.getAttribute(KEY_REPOSITORY_URL));
}
if (element.hasAttribute(KEY_LAST_MOD_DATE) && !element.getAttribute(KEY_LAST_MOD_DATE).equals("")) { //$NON-NLS-1$
task.setLastReadTimeStamp(element.getAttribute(KEY_LAST_MOD_DATE));
}
if (element.hasAttribute(KEY_OWNER)) {
task.setOwner(element.getAttribute(KEY_OWNER));
}
if (VAL_TRUE.equals(element.getAttribute(KEY_NOTIFIED_INCOMING))) {
task.setNotified(true);
} else {
task.setNotified(false);
}
if (element.hasAttribute(KEY_SYNC_STATE)) {
try {
SynchronizationState state = SynchronizationState.valueOf(element.getAttribute(KEY_SYNC_STATE));
task.setSynchronizationState(state);
} catch (IllegalArgumentException e) {
// invalid sync state, ignore
// TODO log this to a multi-status
}
}
if (element.hasAttribute(KEY_KEY)) {
task.setTaskKey(element.getAttribute(KEY_KEY));
} else {
task.setTaskKey(null);
}
}
private Date getDateFromString(String dateString) {
Date date = null;
if ("".equals(dateString)) { //$NON-NLS-1$
return null;
}
String formatString = DATE_FORMAT;
SimpleDateFormat format = new SimpleDateFormat(formatString, Locale.ENGLISH);
try {
date = format.parse(dateString);
} catch (ParseException e) {
errors.add(new Status(IStatus.WARNING, ITasksCoreConstants.ID_PLUGIN, "Could not parse date \"" //$NON-NLS-1$
+ dateString + "\"", e)); //$NON-NLS-1$
}
return date;
}
private String getCategoryTagName() {
return KEY_TASK_CATEGORY;
}
public Element createQueryElement(final RepositoryQuery query, Document doc, Element parent) {
final Element node;
if (query.getClass() == RepositoryQuery.class) {
node = doc.createElement(NODE_QUERY);
} else {
errors.add(new Status(IStatus.WARNING, ITasksCoreConstants.ID_PLUGIN,
"Missing factory to externalize query \"" + query + "\"")); //$NON-NLS-1$ //$NON-NLS-2$
return null;
}
node.setAttribute(KEY_HANDLE, query.getHandleIdentifier());
node.setAttribute(KEY_CONNECTOR_KIND, query.getConnectorKind());
node.setAttribute(KEY_NAME, query.getSummary());
node.setAttribute(KEY_QUERY_STRING, query.getUrl());
node.setAttribute(KEY_REPOSITORY_URL, query.getRepositoryUrl());
if (query.getLastSynchronizedTimeStamp() != null) {
node.setAttribute(KEY_LAST_REFRESH, query.getLastSynchronizedTimeStamp());
}
createAttributes(query, doc, node);
for (ITask hit : query.getChildren()) {
createTaskReference(KEY_QUERY_HIT, hit, doc, node);
}
parent.appendChild(node);
return node;
}
public Map<AbstractTask, String> getLegacyParentCategoryMap() {
return parentCategoryMap;
}
/**
* Reads the Query from the specified Node. If taskList is not null, then also adds this query to the TaskList
*
* @throws TaskExternalizationException
*/
public RepositoryQuery readQuery(Node node) {
final Element element = (Element) node;
String repositoryUrl = element.getAttribute(DelegatingTaskExternalizer.KEY_REPOSITORY_URL);
String queryString = element.getAttribute(KEY_QUERY_STRING);
if (queryString.length() == 0) { // fall back for legacy
queryString = element.getAttribute(KEY_QUERY);
}
String label = element.getAttribute(DelegatingTaskExternalizer.KEY_NAME);
if (label.length() == 0) { // fall back for legacy
label = element.getAttribute(DelegatingTaskExternalizer.KEY_LABEL);
}
AbstractTaskListMigrator queryMigrator = null;
RepositoryQuery query = null;
if (NODE_QUERY.equals(node.getNodeName())) {
String connectorKind = element.getAttribute(DelegatingTaskExternalizer.KEY_CONNECTOR_KIND);
query = readDefaultQuery(connectorKind, repositoryUrl, queryString, label, element);
}
// attempt migration from < 3.0 task list
if (query == null) {
for (AbstractTaskListMigrator migrator : migrators) {
Set<String> queryTagNames = migrator.getQueryElementNames();
if (queryTagNames != null && queryTagNames.contains(node.getNodeName())) {
query = readDefaultQuery(migrator.getConnectorKind(), repositoryUrl, queryString, label, element);
queryMigrator = migrator;
break;
}
}
}
// populate common attributes
if (query != null) {
if (repositoryManager.getRepositoryConnector(query.getConnectorKind()) == null) {
errors.add(new Status(IStatus.WARNING, ITasksCoreConstants.ID_PLUGIN,
"Missing connector for query with kind \"" + query.getConnectorKind() + "\"")); //$NON-NLS-1$ //$NON-NLS-2$
return null;
}
if (element.getAttribute(DelegatingTaskExternalizer.KEY_LAST_REFRESH) != null
&& !element.getAttribute(DelegatingTaskExternalizer.KEY_LAST_REFRESH).equals("")) { //$NON-NLS-1$
query.setLastSynchronizedStamp(element.getAttribute(DelegatingTaskExternalizer.KEY_LAST_REFRESH));
}
String handle = element.getAttribute(DelegatingTaskExternalizer.KEY_HANDLE);
if (handle.length() > 0) {
query.setHandleIdentifier(handle);
}
readAttributes(query, element);
if (queryMigrator != null) {
query.setHandleIdentifier(label);
final AbstractTaskListMigrator finalQueryMigrator = queryMigrator;
final RepositoryQuery finalQuery = query;
SafeRunner.run(new ISafeRunnable() {
public void handleException(Throwable e) {
errors.add(new Status(IStatus.WARNING, ITasksCoreConstants.ID_PLUGIN,
"Query migration failed for query \"" + finalQuery + "\"", e)); //$NON-NLS-1$ //$NON-NLS-2$
}
public void run() throws Exception {
finalQueryMigrator.migrateQuery(finalQuery, element);
}
});
}
return query;
} else {
errors.add(new Status(IStatus.WARNING, ITasksCoreConstants.ID_PLUGIN, "Missing connector for query node \"" //$NON-NLS-1$
+ node.getNodeName() + "\"")); //$NON-NLS-1$
return null;
}
}
private RepositoryQuery readDefaultQuery(String connectorKind, String repositoryUrl, String queryString,
String label, Element childElement) {
TaskRepository taskRepository = repositoryModel.getTaskRepository(connectorKind, repositoryUrl);
IRepositoryQuery query = repositoryModel.createRepositoryQuery(taskRepository);
query.setSummary(label);
query.setUrl(queryString);
return (RepositoryQuery) query;
}
private AbstractTask readDefaultTask(String connectorKind, String repositoryUrl, String taskId, String summary,
Element element) {
TaskRepository taskRepository = repositoryModel.getTaskRepository(connectorKind, repositoryUrl);
if (repositoryUrl.equals(LocalRepositoryConnector.REPOSITORY_URL)) {
LocalTask task = new LocalTask(taskId, summary);
return task;
}
ITask task = repositoryModel.createTask(taskRepository, taskId);
task.setSummary(summary);
return (AbstractTask) task;
}
public void reset() {
parentCategoryMap.clear();
errors.clear();
}
public void clearErrorStatus() {
errors.clear();
}
public Status getErrorStatus() {
if (errors.size() > 0) {
return new MultiStatus(ITasksCoreConstants.ID_PLUGIN, 0, errors.toArray(new IStatus[0]),
"Problems encounted while externalizing task list", null); //$NON-NLS-1$
}
return null;
}
}