Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'org.eclipse.mylyn.trac.core/src')
-rw-r--r--org.eclipse.mylyn.trac.core/src/org/eclipse/mylyn/internal/trac/core/TracAttachmentHandler.java110
-rw-r--r--org.eclipse.mylyn.trac.core/src/org/eclipse/mylyn/internal/trac/core/TracCorePlugin.java39
-rw-r--r--org.eclipse.mylyn.trac.core/src/org/eclipse/mylyn/internal/trac/core/TracOfflineTaskHandler.java275
-rw-r--r--org.eclipse.mylyn.trac.core/src/org/eclipse/mylyn/internal/trac/core/TracQueryHit.java81
-rw-r--r--org.eclipse.mylyn.trac.core/src/org/eclipse/mylyn/internal/trac/core/TracRepositoryConnector.java240
-rw-r--r--org.eclipse.mylyn.trac.core/src/org/eclipse/mylyn/internal/trac/core/TracRepositoryQuery.java88
-rw-r--r--org.eclipse.mylyn.trac.core/src/org/eclipse/mylyn/internal/trac/core/TracTask.java183
7 files changed, 1016 insertions, 0 deletions
diff --git a/org.eclipse.mylyn.trac.core/src/org/eclipse/mylyn/internal/trac/core/TracAttachmentHandler.java b/org.eclipse.mylyn.trac.core/src/org/eclipse/mylyn/internal/trac/core/TracAttachmentHandler.java
new file mode 100644
index 000000000..895d2c5db
--- /dev/null
+++ b/org.eclipse.mylyn.trac.core/src/org/eclipse/mylyn/internal/trac/core/TracAttachmentHandler.java
@@ -0,0 +1,110 @@
+/*******************************************************************************
+ * Copyright (c) 2004 - 2006 University Of British Columbia 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:
+ * University Of British Columbia - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylar.internal.trac.core;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Proxy;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.mylar.internal.trac.core.model.TracTicket;
+import org.eclipse.mylar.tasks.core.AbstractRepositoryTask;
+import org.eclipse.mylar.tasks.core.IAttachmentHandler;
+import org.eclipse.mylar.tasks.core.RepositoryAttachment;
+import org.eclipse.mylar.tasks.core.RepositoryTaskAttribute;
+import org.eclipse.mylar.tasks.core.TaskRepository;
+
+/**
+ * @author Steffen Pingel
+ */
+public class TracAttachmentHandler implements IAttachmentHandler {
+
+ private TracRepositoryConnector connector;
+
+ public TracAttachmentHandler(TracRepositoryConnector connector) {
+ this.connector = connector;
+ }
+
+ public void downloadAttachment(TaskRepository repository, AbstractRepositoryTask task, RepositoryAttachment attachment, File file, Proxy proxySettings) throws CoreException {
+ String filename = attachment.getAttributeValue(RepositoryTaskAttribute.ATTACHMENT_FILENAME);
+ if (filename == null) {
+ throw new CoreException(new Status(IStatus.ERROR, TracCorePlugin.PLUGIN_ID, IStatus.OK, "Attachment download from " + task.getRepositoryUrl() + " failed, missing attachment filename.", null));
+ }
+
+ try {
+ ITracClient client = connector.getClientManager().getRepository(repository);
+ int id = Integer.parseInt(AbstractRepositoryTask.getTaskId(task.getHandleIdentifier()));
+ byte[] data = client.getAttachmentData(id, filename);
+ writeData(file, data);
+ } catch (Exception e) {
+ throw new CoreException(new Status(IStatus.ERROR, TracCorePlugin.PLUGIN_ID, 0, "Attachment download from " + task.getRepositoryUrl() + " failed, please see details.", e ));
+ }
+ }
+
+ private void writeData(File file, byte[] data) throws IOException {
+ OutputStream out = new FileOutputStream(file);
+ try {
+ out.write(data);
+ } finally {
+ out.close();
+ }
+ }
+
+ public void uploadAttachment(TaskRepository repository, AbstractRepositoryTask task, String comment, String description, File file, String contentType, boolean isPatch, Proxy proxySettings) throws CoreException {
+ if (!connector.hasAttachmentSupport(repository, task)) {
+ throw new CoreException(new Status(IStatus.INFO, TracCorePlugin.PLUGIN_ID, IStatus.OK, "Attachments are not supported by this repository access type.", null));
+ }
+
+ try {
+ ITracClient client = connector.getClientManager().getRepository(repository);
+ int id = Integer.parseInt(AbstractRepositoryTask.getTaskId(task.getHandleIdentifier()));
+ byte[] data = readData(file);
+ client.putAttachmentData(id, file.getName(), description, data);
+ if (comment != null && comment.length() > 0) {
+ TracTicket ticket = new TracTicket(id);
+ client.updateTicket(ticket, comment);
+ }
+ } catch (Exception e) {
+ throw new CoreException(new Status(IStatus.ERROR, TracCorePlugin.PLUGIN_ID, 0, "Attachment upload to " + task.getRepositoryUrl() + " failed, please see details.", e ));
+ }
+ }
+
+ private byte[] readData(File file) throws IOException {
+ if (file.length() > Integer.MAX_VALUE) {
+ throw new IOException("Can not upload files larger than " + Integer.MAX_VALUE + " bytes");
+ }
+
+ InputStream in = new FileInputStream(file);
+ try {
+ byte[] data = new byte[(int) file.length()];
+ in.read(data, 0, (int) file.length());
+ return data;
+ } finally {
+ in.close();
+ }
+ }
+
+ public boolean canDownloadAttachment(TaskRepository repository, AbstractRepositoryTask task) {
+ return connector.hasAttachmentSupport(repository, task);
+ }
+
+ public boolean canUploadAttachment(TaskRepository repository, AbstractRepositoryTask task) {
+ return connector.hasAttachmentSupport(repository, task);
+ }
+
+}
diff --git a/org.eclipse.mylyn.trac.core/src/org/eclipse/mylyn/internal/trac/core/TracCorePlugin.java b/org.eclipse.mylyn.trac.core/src/org/eclipse/mylyn/internal/trac/core/TracCorePlugin.java
index a9a2389df..20f14607b 100644
--- a/org.eclipse.mylyn.trac.core/src/org/eclipse/mylyn/internal/trac/core/TracCorePlugin.java
+++ b/org.eclipse.mylyn.trac.core/src/org/eclipse/mylyn/internal/trac/core/TracCorePlugin.java
@@ -10,7 +10,9 @@
*******************************************************************************/
package org.eclipse.mylar.internal.trac.core;
+import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Plugin;
import org.eclipse.core.runtime.Status;
import org.osgi.framework.BundleContext;
@@ -29,6 +31,8 @@ public class TracCorePlugin extends Plugin {
private static TracCorePlugin plugin;
public final static String REPOSITORY_KIND = "trac";
+
+ private TracRepositoryConnector connector;
public TracCorePlugin() {
}
@@ -45,10 +49,45 @@ public class TracCorePlugin extends Plugin {
@Override
public void stop(BundleContext context) throws Exception {
+ if (connector != null) {
+ connector.stop();
+ connector = null;
+ }
+
plugin = null;
super.stop(context);
}
+ public TracRepositoryConnector getConnector() {
+ return connector;
+ }
+
+ void setConnector(TracRepositoryConnector connector) {
+ this.connector = connector;
+ }
+
+ /**
+ * Returns the path to the file caching repository attributes.
+ */
+ protected IPath getRepostioryAttributeCachePath() {
+ IPath stateLocation = Platform.getStateLocation(TracCorePlugin.getDefault().getBundle());
+ IPath cacheFile = stateLocation.append("repositoryConfigurations");
+ return cacheFile;
+ }
+
+ public static IStatus toStatus(Throwable e) {
+ if (e instanceof TracLoginException) {
+ return new Status(Status.ERROR, PLUGIN_ID, IStatus.INFO,
+ "Your login name or password is incorrect. Ensure proper repository configuration in Task Repositories View.", null);
+ } else if (e instanceof TracException) {
+ return new Status(Status.ERROR, PLUGIN_ID, IStatus.INFO, "Connection Error: " + e.getMessage(), e);
+ } else if (e instanceof ClassCastException) {
+ return new Status(Status.ERROR, PLUGIN_ID, IStatus.INFO, "Error parsing server response", e);
+ } else {
+ return new Status(Status.ERROR, PLUGIN_ID, IStatus.ERROR, "Unexpected error", e);
+ }
+ }
+
/**
* Convenience method for logging statuses to the plug-in log
*
diff --git a/org.eclipse.mylyn.trac.core/src/org/eclipse/mylyn/internal/trac/core/TracOfflineTaskHandler.java b/org.eclipse.mylyn.trac.core/src/org/eclipse/mylyn/internal/trac/core/TracOfflineTaskHandler.java
new file mode 100644
index 000000000..9c18d60e3
--- /dev/null
+++ b/org.eclipse.mylyn.trac.core/src/org/eclipse/mylyn/internal/trac/core/TracOfflineTaskHandler.java
@@ -0,0 +1,275 @@
+/*******************************************************************************
+ * Copyright (c) 2004 - 2006 University Of British Columbia 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:
+ * University Of British Columbia - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylar.internal.trac.core;
+
+import java.io.UnsupportedEncodingException;
+import java.net.Proxy;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.mylar.internal.trac.core.TracAttributeFactory.Attribute;
+import org.eclipse.mylar.internal.trac.core.model.TracAttachment;
+import org.eclipse.mylar.internal.trac.core.model.TracComment;
+import org.eclipse.mylar.internal.trac.core.model.TracTicket;
+import org.eclipse.mylar.internal.trac.core.util.TracUtils;
+import org.eclipse.mylar.tasks.core.AbstractAttributeFactory;
+import org.eclipse.mylar.tasks.core.AbstractRepositoryTask;
+import org.eclipse.mylar.tasks.core.IOfflineTaskHandler;
+import org.eclipse.mylar.tasks.core.RepositoryAttachment;
+import org.eclipse.mylar.tasks.core.RepositoryOperation;
+import org.eclipse.mylar.tasks.core.RepositoryTaskAttribute;
+import org.eclipse.mylar.tasks.core.RepositoryTaskData;
+import org.eclipse.mylar.tasks.core.TaskComment;
+import org.eclipse.mylar.tasks.core.TaskRepository;
+
+/**
+ * @author Steffen Pingel
+ */
+public class TracOfflineTaskHandler implements IOfflineTaskHandler {
+
+ private AbstractAttributeFactory attributeFactory = new TracAttributeFactory();
+
+ private TracRepositoryConnector connector;
+
+ public TracOfflineTaskHandler(TracRepositoryConnector connector) {
+ this.connector = connector;
+ }
+
+ public RepositoryTaskData downloadTaskData(final AbstractRepositoryTask task, TaskRepository repository, Proxy proxySettings) throws CoreException {
+ if (!connector.hasRichEditor(repository, task)) {
+ // offline mode is only supported for XML-RPC
+ return null;
+ }
+
+ try {
+ int id = Integer.parseInt(AbstractRepositoryTask.getTaskId(task.getHandleIdentifier()));
+ RepositoryTaskData data = new RepositoryTaskData(attributeFactory, TracCorePlugin.REPOSITORY_KIND,
+ repository.getUrl(), id + "");
+ ITracClient client = connector.getClientManager().getRepository(repository);
+ client.updateAttributes(new NullProgressMonitor(), false);
+ TracTicket ticket = client.getTicket(id);
+ createDefaultAttributes(attributeFactory, data, client);
+ updateTaskData(repository, attributeFactory, data, ticket);
+ return data;
+ } catch (Exception e) {
+ throw new CoreException(new Status(IStatus.ERROR, TracCorePlugin.PLUGIN_ID, 0, "Ticket download from "
+ + task.getRepositoryUrl() + " failed, please see details.", e));
+ }
+ }
+
+ public AbstractAttributeFactory getAttributeFactory() {
+ return attributeFactory;
+ }
+
+ public Date getDateForAttributeType(String attributeKey, String dateString) {
+ if (dateString == null || dateString.length() == 0) {
+ return null;
+ }
+
+ try {
+ String mappedKey = attributeFactory.mapCommonAttributeKey(attributeKey);
+ if (mappedKey.equals(Attribute.TIME.getTracKey()) || mappedKey.equals(Attribute.CHANGE_TIME.getTracKey())) {
+ return TracUtils.parseDate(Integer.valueOf(dateString));
+ }
+ } catch (Exception e) {
+ }
+ return null;
+ }
+
+ public static void updateTaskData(TaskRepository repository, AbstractAttributeFactory factory,
+ RepositoryTaskData data, TracTicket ticket) {
+ if (ticket.getCreated() != null) {
+ data.setAttributeValue(Attribute.TIME.getTracKey(), TracUtils.toTracTime(ticket.getCreated()) + "");
+ }
+ if (ticket.getLastChanged() != null) {
+ data.setAttributeValue(Attribute.CHANGE_TIME.getTracKey(), TracUtils.toTracTime(ticket.getLastChanged())
+ + "");
+ }
+ Map<String, String> valueByKey = ticket.getValues();
+ for (String key : valueByKey.keySet()) {
+ data.setAttributeValue(key, valueByKey.get(key));
+ }
+
+ TracComment[] comments = ticket.getComments();
+ if (comments != null) {
+ for (int i = 0; i < comments.length; i++) {
+ if (!"comment".equals(comments[i].getField()) || "".equals(comments[i].getNewValue())) {
+ continue;
+ }
+
+ TaskComment taskComment = new TaskComment(factory, data, data.getComments().size() + 1);
+ taskComment.setAttributeValue(RepositoryTaskAttribute.USER_OWNER, comments[i].getAuthor());
+ taskComment
+ .setAttributeValue(RepositoryTaskAttribute.COMMENT_DATE, comments[i].getCreated().toString());
+ taskComment.setAttributeValue(RepositoryTaskAttribute.COMMENT_TEXT, comments[i].getNewValue());
+ data.addComment(taskComment);
+ }
+ }
+
+ TracAttachment[] attachments = ticket.getAttachments();
+ if (attachments != null) {
+ for (int i = 0; i < attachments.length; i++) {
+ RepositoryAttachment taskAttachment = new RepositoryAttachment(factory);
+ taskAttachment.setCreator(attachments[i].getAuthor());
+ taskAttachment.setAttributeValue(Attribute.DESCRIPTION.getTracKey(), attachments[i].getDescription());
+ taskAttachment.setAttributeValue(RepositoryTaskAttribute.ATTACHMENT_FILENAME, attachments[i]
+ .getFilename());
+ taskAttachment.setAttributeValue(RepositoryTaskAttribute.USER_OWNER, attachments[i].getAuthor());
+ taskAttachment.setAttributeValue(RepositoryTaskAttribute.ATTACHMENT_DATE, attachments[i].getCreated()
+ .toString());
+ taskAttachment.setAttributeValue(RepositoryTaskAttribute.ATTACHMENT_URL, repository.getUrl()
+ + ITracClient.TICKET_ATTACHMENT_URL + ticket.getId() + "/" + attachments[i].getFilename());
+ taskAttachment.setAttributeValue(RepositoryTaskAttribute.ATTACHMENT_ID, i + "");
+ data.addAttachment(taskAttachment);
+ }
+ }
+
+ String[] actions = ticket.getActions();
+ if (actions != null) {
+ // add operations in a defined order
+ List<String> actionList = new ArrayList<String>(Arrays.asList(actions));
+ addOperation(repository, data, ticket, actionList, "leave");
+ addOperation(repository, data, ticket, actionList, "accept");
+ addOperation(repository, data, ticket, actionList, "resolve");
+ addOperation(repository, data, ticket, actionList, "reassign");
+ addOperation(repository, data, ticket, actionList, "reopen");
+ }
+ }
+
+ // TODO Reuse Labels from BugzillaServerFacade
+ private static void addOperation(TaskRepository repository, RepositoryTaskData data, TracTicket ticket,
+ List<String> actions, String action) {
+ if (!actions.remove(action)) {
+ return;
+ }
+
+ RepositoryOperation operation = null;
+ if ("leave".equals(action)) {
+ operation = new RepositoryOperation(action, "Leave as " + data.getStatus() + " " + data.getResolution());
+ operation.setChecked(true);
+ } else if ("accept".equals(action)) {
+ operation = new RepositoryOperation(action, "Accept");
+ } else if ("resolve".equals(action)) {
+ operation = new RepositoryOperation(action, "Resolve bug, changing resolution to");
+ operation.setUpOptions("resolution");
+ for (String resolution : ticket.getResolutions()) {
+ operation.addOption(resolution, resolution);
+ }
+ } else if ("reassign".equals(action)) {
+ operation = new RepositoryOperation(action, "Reassing bug to");
+ operation.setInputName("owner");
+ operation.setInputValue(TracRepositoryConnector.getDisplayUsername(repository));
+ } else if ("reopen".equals(action)) {
+ operation = new RepositoryOperation(action, "Reopen");
+ }
+
+ if (operation != null) {
+ data.addOperation(operation);
+ }
+ }
+
+ public static void createDefaultAttributes(AbstractAttributeFactory factory, RepositoryTaskData data,
+ ITracClient client) {
+ createAttribute(factory, data, Attribute.STATUS, client.getTicketStatus());
+ createAttribute(factory, data, Attribute.RESOLUTION, client.getTicketResolutions());
+
+ createAttribute(factory, data, Attribute.COMPONENT, client.getComponents());
+ createAttribute(factory, data, Attribute.VERSION, client.getVersions(), true);
+ createAttribute(factory, data, Attribute.PRIORITY, client.getPriorities());
+ createAttribute(factory, data, Attribute.SEVERITY, client.getSeverities());
+
+ createAttribute(factory, data, Attribute.TYPE, client.getTicketTypes());
+ createAttribute(factory, data, Attribute.OWNER);
+ createAttribute(factory, data, Attribute.MILESTONE, client.getMilestones(), true);
+ createAttribute(factory, data, Attribute.REPORTER);
+
+ createAttribute(factory, data, Attribute.CC);
+ createAttribute(factory, data, Attribute.KEYWORDS);
+ }
+
+ private static RepositoryTaskAttribute createAttribute(AbstractAttributeFactory factory, RepositoryTaskData data,
+ Attribute attribute, Object[] values, boolean allowEmtpy) {
+ RepositoryTaskAttribute attr = factory.createAttribute(attribute.getTracKey());
+ if (values != null && values.length > 0) {
+ if (allowEmtpy) {
+ attr.addOptionValue("", "");
+ }
+ for (int i = 0; i < values.length; i++) {
+ attr.addOptionValue(values[i].toString(), values[i].toString());
+ }
+ } else {
+ // attr.setHidden(true);
+ attr.setReadOnly(true);
+ }
+ data.addAttribute(attribute.getTracKey(), attr);
+ return attr;
+ }
+
+ private static RepositoryTaskAttribute createAttribute(AbstractAttributeFactory factory, RepositoryTaskData data,
+ Attribute attribute) {
+ RepositoryTaskAttribute attr = factory.createAttribute(attribute.getTracKey());
+ data.addAttribute(attribute.getTracKey(), attr);
+ return attr;
+ }
+
+ private static RepositoryTaskAttribute createAttribute(AbstractAttributeFactory factory, RepositoryTaskData data,
+ Attribute attribute, Object[] values) {
+ return createAttribute(factory, data, attribute, values, false);
+ }
+
+ public Set<AbstractRepositoryTask> getChangedSinceLastSync(TaskRepository repository,
+ Set<AbstractRepositoryTask> tasks, Proxy proxySettings) throws CoreException, UnsupportedEncodingException {
+ if (repository.getSyncTimeStamp() == null) {
+ return tasks;
+ }
+
+ if (!connector.hasChangedSince(repository)) {
+ // return an empty list to avoid causing all tasks to synchronized
+ return Collections.emptySet();
+ }
+
+ Date since = new Date(0);
+ try {
+ since = TracUtils.parseDate(Integer.parseInt(repository.getSyncTimeStamp()));
+ } catch (NumberFormatException e) {
+ }
+
+ ITracClient client;
+ try {
+ client = connector.getClientManager().getRepository(repository);
+ Set<Integer> ids = client.getChangedTickets(since);
+
+ Set<AbstractRepositoryTask> result = new HashSet<AbstractRepositoryTask>();
+ if (!ids.isEmpty()) {
+ for (AbstractRepositoryTask task : tasks) {
+ Integer id = Integer.parseInt(AbstractRepositoryTask.getTaskId(task.getHandleIdentifier()));
+ if (ids.contains(id)) {
+ result.add(task);
+ }
+ }
+ }
+ return result;
+ } catch (Exception e) {
+ throw new CoreException(new Status(IStatus.ERROR, TracCorePlugin.PLUGIN_ID, IStatus.OK, "could not determine changed tasks", e));
+ }
+ }
+}
diff --git a/org.eclipse.mylyn.trac.core/src/org/eclipse/mylyn/internal/trac/core/TracQueryHit.java b/org.eclipse.mylyn.trac.core/src/org/eclipse/mylyn/internal/trac/core/TracQueryHit.java
new file mode 100644
index 000000000..261312ed8
--- /dev/null
+++ b/org.eclipse.mylyn.trac.core/src/org/eclipse/mylyn/internal/trac/core/TracQueryHit.java
@@ -0,0 +1,81 @@
+/*******************************************************************************
+ * Copyright (c) 2006 - 2006 Mylar eclipse.org project 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:
+ * Mylar project committers - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylar.internal.trac.core;
+
+import org.eclipse.mylar.tasks.core.AbstractQueryHit;
+import org.eclipse.mylar.tasks.core.AbstractRepositoryTask;
+import org.eclipse.mylar.tasks.core.ITask;
+import org.eclipse.mylar.tasks.core.TaskList;
+
+/**
+ * @author Steffen Pingel
+ */
+public class TracQueryHit extends AbstractQueryHit {
+
+ private TracTask task;
+
+ private boolean completed;
+
+ private TaskList taskList;
+
+ public TracQueryHit(TaskList taskList, String repositoryUrl, String description, String id) {
+ super(repositoryUrl, description, id);
+
+ this.taskList = taskList;
+ }
+
+ public TracQueryHit(TaskList taskList, String handle) {
+ super(AbstractRepositoryTask.getRepositoryUrl(handle), "", AbstractRepositoryTask.getTaskId(handle));
+
+ this.taskList = taskList;
+ }
+
+ @Override
+ public AbstractRepositoryTask getCorrespondingTask() {
+ return task;
+ }
+
+ @Override
+ public AbstractRepositoryTask getOrCreateCorrespondingTask() {
+ ITask existingTask = taskList.getTask(getHandleIdentifier());
+ if (existingTask instanceof TracTask) {
+ this.task = (TracTask) existingTask;
+ } else {
+ this.task = new TracTask(getHandleIdentifier(), getDescription(), true);
+ task.setCompleted(completed);
+ task.setPriority(priority);
+ taskList.addTask(task);
+ }
+ return task;
+ }
+
+ @Override
+ public boolean isCompleted() {
+ return (task != null) ? task.isCompleted() : completed;
+ }
+
+ @Override
+ public void setCorrespondingTask(AbstractRepositoryTask task) {
+ if (task instanceof TracTask) {
+ this.task = (TracTask) task;
+ }
+ }
+
+ public String getUrl() {
+ return getRepositoryUrl() + ITracClient.TICKET_URL + getId();
+ }
+
+ public void setCompleted(boolean completed) {
+ this.completed = completed;
+ }
+
+}
diff --git a/org.eclipse.mylyn.trac.core/src/org/eclipse/mylyn/internal/trac/core/TracRepositoryConnector.java b/org.eclipse.mylyn.trac.core/src/org/eclipse/mylyn/internal/trac/core/TracRepositoryConnector.java
new file mode 100644
index 000000000..7c589748e
--- /dev/null
+++ b/org.eclipse.mylyn.trac.core/src/org/eclipse/mylyn/internal/trac/core/TracRepositoryConnector.java
@@ -0,0 +1,240 @@
+/*******************************************************************************
+ * Copyright (c) 2006 - 2006 Mylar eclipse.org project 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:
+ * Mylar project committers - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylar.internal.trac.core;
+
+import java.io.File;
+import java.net.Proxy;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.mylar.context.core.MylarStatusHandler;
+import org.eclipse.mylar.internal.trac.core.ITracClient.Version;
+import org.eclipse.mylar.internal.trac.core.TracTask.Kind;
+import org.eclipse.mylar.internal.trac.core.model.TracTicket;
+import org.eclipse.mylar.internal.trac.core.model.TracTicket.Key;
+import org.eclipse.mylar.tasks.core.AbstractRepositoryConnector;
+import org.eclipse.mylar.tasks.core.AbstractRepositoryQuery;
+import org.eclipse.mylar.tasks.core.AbstractRepositoryTask;
+import org.eclipse.mylar.tasks.core.IAttachmentHandler;
+import org.eclipse.mylar.tasks.core.IOfflineTaskHandler;
+import org.eclipse.mylar.tasks.core.IQueryHitCollector;
+import org.eclipse.mylar.tasks.core.ITask;
+import org.eclipse.mylar.tasks.core.TaskRepository;
+
+/**
+ * @author Steffen Pingel
+ */
+public class TracRepositoryConnector extends AbstractRepositoryConnector {
+
+ private final static String CLIENT_LABEL = "Trac (supports 0.9 and later or XML-RPC)";
+
+ private List<String> supportedVersions;
+
+ private TracClientManager clientManager;
+
+ private TracOfflineTaskHandler offlineTaskHandler = new TracOfflineTaskHandler(this);
+
+ private TracAttachmentHandler attachmentHandler = new TracAttachmentHandler(this);
+
+ public TracRepositoryConnector() {
+ TracCorePlugin.getDefault().setConnector(this);
+ }
+
+ @Override
+ public boolean canCreateNewTask(TaskRepository repository) {
+ return true;
+ }
+
+ @Override
+ public boolean canCreateTaskFromKey(TaskRepository repository) {
+ return true;
+ }
+
+ @Override
+ public String getLabel() {
+ return CLIENT_LABEL;
+ }
+
+ @Override
+ public String getRepositoryType() {
+ return TracCorePlugin.REPOSITORY_KIND;
+ }
+
+ @Override
+ public String getRepositoryUrlFromTaskUrl(String url) {
+ if (url == null) {
+ return null;
+ }
+ int i = url.lastIndexOf(ITracClient.TICKET_URL);
+ return (i != -1) ? url.substring(0, i) : null;
+ }
+
+ @Override
+ public List<String> getSupportedVersions() {
+ if (supportedVersions == null) {
+ supportedVersions = new ArrayList<String>();
+ for (Version version : Version.values()) {
+ supportedVersions.add(version.toString());
+ }
+ }
+ return supportedVersions;
+ }
+
+ @Override
+ public IAttachmentHandler getAttachmentHandler() {
+ return attachmentHandler;
+ }
+
+ @Override
+ public IOfflineTaskHandler getOfflineTaskHandler() {
+ return offlineTaskHandler;
+ }
+
+ @Override
+ public void updateTaskState(AbstractRepositoryTask repositoryTask) {
+ // TODO Auto-generated method stub
+ }
+
+ @Override
+ public IStatus performQuery(AbstractRepositoryQuery query, TaskRepository repository,
+ Proxy proxySettings, IProgressMonitor monitor, IQueryHitCollector resultCollector) {
+
+ final List<TracTicket> tickets = new ArrayList<TracTicket>();
+
+ ITracClient tracClient;
+ try {
+ tracClient = getClientManager().getRepository(repository);
+ if (query instanceof TracRepositoryQuery) {
+ tracClient.search(((TracRepositoryQuery) query).getTracSearch(), tickets);
+ }
+
+ for (TracTicket ticket : tickets) {
+ TracQueryHit hit = new TracQueryHit(taskList, query.getRepositoryUrl(), getTicketDescription(ticket), ticket
+ .getId()
+ + "");
+ hit.setCompleted(TracTask.isCompleted(ticket.getValue(Key.STATUS)));
+ hit.setPriority(TracTask.getMylarPriority(ticket.getValue(Key.PRIORITY)));
+ resultCollector.accept(hit);
+ }
+ } catch (Throwable e) {
+ return TracCorePlugin.toStatus(e);
+ }
+
+ return Status.OK_STATUS;
+ }
+
+ @Override
+ public ITask createTaskFromExistingKey(TaskRepository repository, String id, Proxy proxySettings) throws CoreException {
+ try {
+ ITracClient connection = getClientManager().getRepository(repository);
+ TracTicket ticket = connection.getTicket(Integer.parseInt(id));
+
+ String handleIdentifier = AbstractRepositoryTask.getHandle(repository.getUrl(), ticket.getId());
+ TracTask task = createTask(ticket, handleIdentifier);
+ updateTaskDetails(task, ticket, true);
+
+ return task;
+ } catch (Exception e) {
+ MylarStatusHandler.log(e, "Error creating task from key " + id);
+ }
+ return null;
+ }
+
+ public synchronized TracClientManager getClientManager() {
+ if (clientManager == null) {
+ File cacheFile = null;
+ if (TracCorePlugin.getDefault().getRepostioryAttributeCachePath() != null) {
+ cacheFile = TracCorePlugin.getDefault().getRepostioryAttributeCachePath().toFile();
+ }
+ clientManager = new TracClientManager(cacheFile);
+ }
+ return clientManager;
+ }
+
+ public TracTask createTask(TracTicket ticket, String handleIdentifier) {
+ TracTask task;
+ ITask existingTask = taskList.getTask(handleIdentifier);
+ if (existingTask instanceof TracTask) {
+ task = (TracTask) existingTask;
+ } else {
+ task = new TracTask(handleIdentifier, getTicketDescription(ticket), true);
+ taskList.addTask(task);
+ }
+ return task;
+ }
+
+ /**
+ * Updates fields of <code>task</code> from <code>ticket</code>.
+ */
+ public void updateTaskDetails(TracTask task, TracTicket ticket, boolean notify) {
+ if (ticket.getValue(Key.SUMMARY) != null) {
+ task.setDescription(getTicketDescription(ticket));
+ }
+ task.setCompleted(TracTask.isCompleted(ticket.getValue(Key.STATUS)));
+ task.setPriority(TracTask.getMylarPriority(ticket.getValue(Key.PRIORITY)));
+ if (ticket.getValue(Key.TYPE) != null) {
+ Kind kind = TracTask.Kind.fromType(ticket.getValue(Key.TYPE));
+ task.setKind((kind != null) ? kind.toString() : ticket.getValue(Key.TYPE));
+ }
+ if (ticket.getCreated() != null) {
+ task.setCreationDate(ticket.getCreated());
+ }
+
+ if (notify) {
+ taskList.notifyLocalInfoChanged(task);
+ }
+ }
+
+ private static String getTicketDescription(TracTicket ticket) {
+ return ticket.getId() + ": " + ticket.getValue(Key.SUMMARY);
+ }
+
+ public boolean hasChangedSince(TaskRepository repository) {
+ return Version.XML_RPC.name().equals(repository.getVersion());
+ }
+
+ public boolean hasRichEditor(TaskRepository repository, AbstractRepositoryTask task) {
+ return Version.XML_RPC.name().equals(repository.getVersion());
+ }
+
+ public boolean hasAttachmentSupport(TaskRepository repository, AbstractRepositoryTask task) {
+ return Version.XML_RPC.name().equals(repository.getVersion());
+ }
+
+ public void stop() {
+ if (clientManager != null) {
+ clientManager.writeCache();
+ }
+ }
+
+ @Override
+ public void updateAttributes(TaskRepository repository, Proxy proxySettings, IProgressMonitor monitor) throws CoreException {
+ try {
+ ITracClient client = getClientManager().getRepository(repository);
+ client.updateAttributes(monitor, true);
+ } catch (Exception e) {
+ MylarStatusHandler.fail(e, "Could not update attributes", false);
+ }
+ }
+
+ public static String getDisplayUsername(TaskRepository repository) {
+ if (!repository.hasCredentials()) {
+ return ITracClient.DEFAULT_USERNAME;
+ }
+ return repository.getUserName();
+ }
+
+} \ No newline at end of file
diff --git a/org.eclipse.mylyn.trac.core/src/org/eclipse/mylyn/internal/trac/core/TracRepositoryQuery.java b/org.eclipse.mylyn.trac.core/src/org/eclipse/mylyn/internal/trac/core/TracRepositoryQuery.java
new file mode 100644
index 000000000..03daac842
--- /dev/null
+++ b/org.eclipse.mylyn.trac.core/src/org/eclipse/mylyn/internal/trac/core/TracRepositoryQuery.java
@@ -0,0 +1,88 @@
+/*******************************************************************************
+ * Copyright (c) 2006 - 2006 Mylar eclipse.org project 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:
+ * Mylar project committers - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylar.internal.trac.core;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.util.StringTokenizer;
+
+import org.eclipse.mylar.context.core.MylarStatusHandler;
+import org.eclipse.mylar.internal.trac.core.model.TracSearch;
+import org.eclipse.mylar.tasks.core.AbstractRepositoryQuery;
+import org.eclipse.mylar.tasks.core.TaskList;
+
+/**
+ * @author Steffen Pingel
+ */
+public class TracRepositoryQuery extends AbstractRepositoryQuery {
+
+ public TracRepositoryQuery(String repositoryUrl, String queryUrl, String description, TaskList taskList) {
+ super(description, taskList);
+
+ assert queryUrl.startsWith(repositoryUrl + ITracClient.QUERY_URL);
+
+ setRepositoryUrl(repositoryUrl);
+ setUrl(queryUrl);
+ }
+
+ @Override
+ public String getRepositoryKind() {
+ return TracCorePlugin.REPOSITORY_KIND;
+ }
+
+ public String getQueryParameter() {
+ String url = getUrl();
+ int i = url.indexOf(ITracClient.QUERY_URL);
+ if (i == -1) {
+ return null;
+ }
+ return url.substring(i + ITracClient.QUERY_URL.length());
+ }
+
+ /**
+ * Creates a <code>TracSearch</code> object from this query.
+ */
+ public TracSearch getTracSearch() {
+ TracSearch list = new TracSearch();
+ String url = getQueryParameter();
+ if (url == null) {
+ return list;
+ }
+
+ StringTokenizer t = new StringTokenizer(url, "&");
+ while (t.hasMoreTokens()) {
+ String token = t.nextToken();
+ int i = token.indexOf("=");
+ if (i != -1) {
+ try {
+ String key = URLDecoder.decode(token.substring(0, i), ITracClient.CHARSET);
+ String value = URLDecoder.decode(token.substring(i + 1), ITracClient.CHARSET);
+
+ if ("order".equals(key)) {
+ list.setOrderBy(value);
+ } else if ("desc".equals(key)) {
+ list.setAscending(!"1".equals(value));
+ } else if ("group".equals(key) || "groupdesc".equals(key) || "verbose".equals(key)) {
+ // ignore these parameters
+ } else {
+ list.addFilter(key, value);
+ }
+ } catch (UnsupportedEncodingException e) {
+ MylarStatusHandler.log(e, "Unexpected exception while decoding URL");
+ }
+ }
+ }
+
+ return list;
+ }
+
+}
diff --git a/org.eclipse.mylyn.trac.core/src/org/eclipse/mylyn/internal/trac/core/TracTask.java b/org.eclipse.mylyn.trac.core/src/org/eclipse/mylyn/internal/trac/core/TracTask.java
new file mode 100644
index 000000000..fabd76cd2
--- /dev/null
+++ b/org.eclipse.mylyn.trac.core/src/org/eclipse/mylyn/internal/trac/core/TracTask.java
@@ -0,0 +1,183 @@
+/*******************************************************************************
+ * Copyright (c) 2006 - 2006 Mylar eclipse.org project 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:
+ * Mylar project committers - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylar.internal.trac.core;
+
+import org.eclipse.mylar.internal.trac.core.TracAttributeFactory.Attribute;
+import org.eclipse.mylar.tasks.core.AbstractRepositoryTask;
+import org.eclipse.mylar.tasks.core.RepositoryTaskAttribute;
+import org.eclipse.mylar.tasks.core.Task;
+
+/**
+ * @author Steffen Pingel
+ */
+public class TracTask extends AbstractRepositoryTask {
+
+ public enum PriorityLevel {
+ BLOCKER, CRITICAL, MAJOR, MINOR, TRIVIAL;
+
+ @Override
+ public String toString() {
+ switch (this) {
+ case BLOCKER:
+ return "P1";
+ case CRITICAL:
+ return "P2";
+ case MAJOR:
+ return "P3";
+ case MINOR:
+ return "P4";
+ case TRIVIAL:
+ return "P5";
+ default:
+ return "P5";
+ }
+ }
+
+ public static PriorityLevel fromPriority(String priority) {
+ if (priority == null)
+ return null;
+ if (priority.equals("blocker"))
+ return BLOCKER;
+ if (priority.equals("critical"))
+ return CRITICAL;
+ if (priority.equals("major"))
+ return MAJOR;
+ if (priority.equals("minor"))
+ return MINOR;
+ if (priority.equals("trivial"))
+ return TRIVIAL;
+ return null;
+ }
+ }
+
+ public enum Kind {
+ DEFECT, ENHANCEMENT, TASK;
+
+ @Override
+ public String toString() {
+ switch (this) {
+ case DEFECT:
+ return "Defect";
+ case ENHANCEMENT:
+ return "Enhancement";
+ case TASK:
+ return "Task";
+ default:
+ return "";
+ }
+ }
+
+ public static Kind fromType(String type) {
+ if (type == null)
+ return null;
+ if (type.equals("defect"))
+ return DEFECT;
+ if (type.equals("enhancement"))
+ return ENHANCEMENT;
+ if (type.equals("task"))
+ return TASK;
+ return null;
+ }
+
+ }
+
+ public enum Status {
+ NEW, ASSIGNED, REOPENED, CLOSED;
+
+ @Override
+ public String toString() {
+ switch (this) {
+ case NEW:
+ return "New";
+ case ASSIGNED:
+ return "Assigned";
+ case REOPENED:
+ return "Reopened";
+ case CLOSED:
+ return "Closed";
+ default:
+ return "";
+ }
+ }
+
+ public static Status fromStatus(String status) {
+ if (status == null)
+ return null;
+ if (status.equals("new"))
+ return NEW;
+ if (status.equals("assigned"))
+ return ASSIGNED;
+ if (status.equals("reopened"))
+ return REOPENED;
+ if (status.equals("closed"))
+ return CLOSED;
+ return null;
+ }
+
+ }
+
+ public TracTask(String handle, String label, boolean newTask) {
+ super(handle, label, newTask);
+
+ setUrl(AbstractRepositoryTask.getRepositoryUrl(handle) + ITracClient.TICKET_URL
+ + AbstractRepositoryTask.getTaskId(handle));
+ }
+
+ @Override
+ public boolean isCompleted() {
+ if (taskData != null) {
+ return isCompleted(taskData.getStatus());
+ } else {
+ return super.isCompleted();
+ }
+ }
+
+ @Override
+ public String getRepositoryKind() {
+ return TracCorePlugin.REPOSITORY_KIND;
+ }
+
+ @Override
+ public String getPriority() {
+ if (taskData != null && taskData.getAttribute(Attribute.PRIORITY.getTracKey()) != null) {
+ return getMylarPriority(taskData.getAttributeValue(Attribute.PRIORITY.getTracKey()));
+ } else {
+ return super.getPriority();
+ }
+ }
+
+ @Override
+ public String getOwner() {
+ if (taskData != null && taskData.getAttribute(RepositoryTaskAttribute.USER_OWNER) != null) {
+ return taskData.getAttributeValue(RepositoryTaskAttribute.USER_OWNER);
+ } else {
+ return super.getOwner();
+ }
+ }
+
+ // TODO use priority attributes from repository instead of hard coded enum
+ public static String getMylarPriority(String tracPriority) {
+ if (tracPriority != null) {
+ PriorityLevel priority = PriorityLevel.fromPriority(tracPriority);
+ if (priority != null) {
+ return priority.toString();
+ }
+ }
+ return Task.PriorityLevel.P3.toString();
+ }
+
+ public static boolean isCompleted(String tracStatus) {
+ TracTask.Status status = TracTask.Status.fromStatus(tracStatus);
+ return status == TracTask.Status.CLOSED;
+ }
+
+}

Back to the top