blob: b50ed16079b5f4a3c19b1fb14b356d12c9ac337c [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2016.
* 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.reviews.internal.core;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collection;
import java.util.Set;
import org.eclipse.mylyn.internal.tasks.core.AbstractTask;
import org.eclipse.mylyn.internal.tasks.core.ITaskListChangeListener;
import org.eclipse.mylyn.internal.tasks.core.TaskContainerDelta;
import org.eclipse.mylyn.internal.tasks.core.TaskContainerDelta.Kind;
import org.eclipse.mylyn.internal.tasks.core.TaskList;
import org.eclipse.mylyn.internal.tasks.core.TaskRepositoryManager;
import org.eclipse.mylyn.internal.tasks.core.util.TasksCoreUtil;
import org.eclipse.mylyn.reviews.core.spi.ReviewsConnector;
import org.eclipse.mylyn.tasks.core.AbstractRepositoryConnector;
import org.eclipse.mylyn.tasks.core.IRepositoryElement;
import org.eclipse.mylyn.tasks.core.ITask;
import org.eclipse.mylyn.tasks.core.data.TaskAttribute;
import org.eclipse.mylyn.tasks.core.data.TaskData;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.Multimaps;
import com.google.common.collect.SetMultimap;
/**
* This class is used by review connectors to provide a mapping from tasks to reviews. These mappings are used by
* TaskEditorReviewPart to give a table of the reviews pertaining to a task. The class is limited as it maps one task to
* many reviews. It is however possible (albeit strange) to have multiple tasks for one review. This is a limitation by
* design.
*
* @author Blaine Lewis
*/
@SuppressWarnings("restriction")
public class TaskReviewsMappingsStore implements ITaskListChangeListener {
static final String ATTR_ASSOCIATED_TASK = "org.eclipse.mylyn.associated.task"; //$NON-NLS-1$
private final SetMultimap<String, String> taskReviewsMap;
private final TaskRepositoryManager repositoryManager;
private final TaskList taskList;
private static TaskReviewsMappingsStore instance;
public TaskReviewsMappingsStore(TaskList taskList, TaskRepositoryManager repositoryManager) {
this.taskList = taskList;
this.repositoryManager = repositoryManager;
taskReviewsMap = Multimaps.synchronizedSetMultimap(LinkedHashMultimap.<String, String> create());
}
public void readFromTaskList() {
for (AbstractTask review : taskList.getAllTasks()) {
String task = getTaskUrl(review);
if (task != null) {
taskReviewsMap.put(task, review.getUrl());
}
}
}
private void updateMapping(ITask review, String newTaskUrl) {
String reviewUrl = review.getUrl();
String oldTaskUrl = getTaskUrl(review);
if (oldTaskUrl != null && !oldTaskUrl.equals(newTaskUrl)) {
taskReviewsMap.remove(oldTaskUrl, reviewUrl);
}
taskReviewsMap.put(newTaskUrl, reviewUrl);
review.setAttribute(ATTR_ASSOCIATED_TASK, newTaskUrl);
}
/*
* This method of extracting URLs is deficient because if we have "(www.helloworld.com)" it will be
* a valid URL. This is difficult to format though so we won't handle that case.
*/
public void addTaskAssocation(ITask review, TaskData taskData) {
TaskAttribute attr = taskData.getRoot().getMappedAttribute(TaskAttribute.DESCRIPTION);
if (attr == null) {
return;
}
String oldTaskUrl = getTaskUrl(review);
String description = attr.getValue();
for (String token : description.split("\\s+")) { //$NON-NLS-1$
if (token.equals(oldTaskUrl)) {
return;// don't do expensive getTaskByUrl lookup if nothing changed
} else if (token.contains("://")) { //$NON-NLS-1$
try {
new URL(token);
ITask task = getTaskByUrl(token);
if (task != null) {
AbstractRepositoryConnector connector = repositoryManager
.getRepositoryConnector(task.getConnectorKind());
if (!(connector instanceof ReviewsConnector)) {
updateMapping(review, task.getUrl() != null ? task.getUrl() : token);
return;
}
}
} catch (MalformedURLException e) {
//Do nothing, this is expected behavior when there is no URL
}
}
}
deleteMappingsTo(review);
}
public String getTaskUrl(ITask review) {
return review.getAttribute(ATTR_ASSOCIATED_TASK);
}
public Collection<String> getReviewUrls(String taskUrl) {
synchronized (taskReviewsMap) {
return ImmutableSet.copyOf(taskReviewsMap.get(taskUrl));
}
}
ITask getTaskByUrl(String url) {
return TasksCoreUtil.getTaskByUrl(taskList, repositoryManager, url);
}
@Override
public void containersChanged(Set<TaskContainerDelta> containers) {
for (TaskContainerDelta delta : containers) {
if (delta.getKind() == Kind.DELETED) {
IRepositoryElement reviewRepoElement = delta.getElement();
if (!(reviewRepoElement instanceof ITask)) {
continue;
}
ITask review = (ITask) reviewRepoElement;
//We need to check it in case the mapping was removed from the task
AbstractRepositoryConnector connector = repositoryManager
.getRepositoryConnector(review.getConnectorKind());
if (connector instanceof ReviewsConnector) {
deleteMappingsTo(review);
}
}
}
}
private void deleteMappingsTo(ITask review) {
String taskUrl = getTaskUrl(review);
if (taskUrl != null) {
taskReviewsMap.remove(taskUrl, review.getUrl());
review.setAttribute(ATTR_ASSOCIATED_TASK, null);
}
}
public static void setInstance(TaskReviewsMappingsStore instance) {
TaskReviewsMappingsStore.instance = instance;
}
public static TaskReviewsMappingsStore getInstance() {
return instance;
}
}