/*******************************************************************************
* Copyright (c) 2011 Red Hat 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:
* David Green <david.green@tasktop.com> - initial contribution
* Christian Trutz <christian.trutz@gmail.com> - initial contribution
* Chris Aniszczyk <caniszczyk@gmail.com> - initial contribution
*******************************************************************************/
package org.eclipse.mylyn.internal.github.core.issue;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.egit.github.core.Comment;
import org.eclipse.egit.github.core.Issue;
import org.eclipse.egit.github.core.Label;
import org.eclipse.egit.github.core.Milestone;
import org.eclipse.egit.github.core.RepositoryId;
import org.eclipse.egit.github.core.User;
import org.eclipse.egit.github.core.client.GitHubClient;
import org.eclipse.egit.github.core.service.IssueService;
import org.eclipse.egit.github.core.service.LabelService;
import org.eclipse.mylyn.internal.github.core.GitHub;
import org.eclipse.mylyn.internal.github.core.GitHubTaskDataHandler;
import org.eclipse.mylyn.tasks.core.ITaskMapping;
import org.eclipse.mylyn.tasks.core.RepositoryResponse;
import org.eclipse.mylyn.tasks.core.RepositoryResponse.ResponseKind;
import org.eclipse.mylyn.tasks.core.TaskRepository;
import org.eclipse.mylyn.tasks.core.data.TaskAttribute;
import org.eclipse.mylyn.tasks.core.data.TaskData;
/**
* GitHub issue task data handler
*/
public class IssueTaskDataHandler extends GitHubTaskDataHandler {
private static final String DATA_VERSION = "1"; //$NON-NLS-1$
private static final String MILESTONE_NONE_KEY = "0"; //$NON-NLS-1$
private final IssueConnector connector;
/**
* Create GitHub issue task data handler for connector
*
* @param connector
*/
public IssueTaskDataHandler(IssueConnector connector) {
this.connector = connector;
}
/**
* Create task data
*
* @param repository
* @param monitor
* @param user
* @param project
* @param issue
* @return task data
*/
public TaskData createTaskData(TaskRepository repository,
IProgressMonitor monitor, String user, String project, Issue issue) {
String key = Integer.toString(issue.getNumber());
TaskData data = new TaskData(getAttributeMapper(repository),
IssueConnector.KIND, repository.getRepositoryUrl(), key);
data.setVersion(DATA_VERSION);
createOperations(data, issue);
createAttribute(data, IssueAttribute.KEY.getMetadata(), key);
createAttribute(data, IssueAttribute.TITLE.getMetadata(),
issue.getTitle());
createAttribute(data, IssueAttribute.BODY.getMetadata(),
issue.getBody());
createAttribute(data, IssueAttribute.STATUS.getMetadata(),
issue.getState());
createAttribute(data, IssueAttribute.CREATION_DATE.getMetadata(),
issue.getCreatedAt());
createAttribute(data, IssueAttribute.MODIFICATION_DATE.getMetadata(),
issue.getUpdatedAt());
createAttribute(data, IssueAttribute.CLOSED_DATE.getMetadata(),
issue.getClosedAt());
User reporter = issue.getUser();
createAttribute(data, IssueAttribute.REPORTER.getMetadata(), reporter,
repository);
String reporterGravatar = reporter != null ? reporter.getAvatarUrl()
: null;
createAttribute(data, IssueAttribute.REPORTER_GRAVATAR.getMetadata(),
reporterGravatar);
User assignee = issue.getAssignee();
createAttribute(data, IssueAttribute.ASSIGNEE.getMetadata(), assignee,
repository);
String assigneeGravatar = assignee != null ? assignee.getAvatarUrl()
: null;
createAttribute(data, IssueAttribute.ASSIGNEE_GRAVATAR.getMetadata(),
assigneeGravatar);
createAttribute(data, IssueAttribute.COMMENT_NEW.getMetadata());
createLabels(repository, data, issue);
createMilestones(repository, data, issue);
return data;
}
private void createMilestones(TaskRepository repository, TaskData data,
Issue issue) {
Milestone current = issue.getMilestone();
String number = current != null ? Integer.toString(current.getNumber())
: MILESTONE_NONE_KEY;
TaskAttribute milestoneAttribute = createAttribute(data,
IssueAttribute.MILESTONE.getMetadata(), number);
if (!connector.hasCachedMilestones(repository))
try {
connector.refreshMilestones(repository);
} catch (CoreException ignore) {
// Ignored
}
List<Milestone> cachedMilestones = connector.getMilestones(repository);
milestoneAttribute.putOption(MILESTONE_NONE_KEY,
Messages.IssueAttribute_MilestoneNone);
for (Milestone milestone : cachedMilestones)
milestoneAttribute.putOption(
Integer.toString(milestone.getNumber()),
milestone.getTitle());
}
private void createLabels(TaskRepository repository, TaskData data,
Issue issue) {
TaskAttribute labels = createAttribute(data, IssueAttribute.LABELS,
issue.getLabels());
if (!connector.hasCachedLabels(repository))
try {
connector.refreshLabels(repository);
} catch (CoreException ignore) {
// Ignored
}
List<Label> cachedLabels = connector.getLabels(repository);
for (Label label : cachedLabels)
labels.putOption(label.getName(), label.getName());
}
private void createOperations(TaskData data, Issue issue) {
createOperationAttribute(data);
if (!data.isNew()) {
String state = issue.getState();
if (state != null) {
addOperation(data, issue, IssueOperation.LEAVE, true);
if (state.equals(IssueService.STATE_OPEN))
addOperation(data, issue, IssueOperation.CLOSE, false);
else if (state.equals(IssueService.STATE_CLOSED))
addOperation(data, issue, IssueOperation.REOPEN, false);
}
}
}
private void addOperation(TaskData data, Issue issue,
IssueOperation operation, boolean isDefault) {
String id = operation.getId();
String label = createOperationLabel(issue, operation);
addOperation(data, id, label, isDefault);
}
private String createOperationLabel(Issue issue, IssueOperation operation) {
return operation == IssueOperation.LEAVE ? operation.getLabel()
+ issue.getState() : operation.getLabel();
}
/**
* Create task data for issue
*
* @param repository
* @param monitor
* @param user
* @param project
* @param issue
* @param comments
* @return task data
*/
public TaskData createTaskData(TaskRepository repository,
IProgressMonitor monitor, String user, String project, Issue issue,
List<Comment> comments) {
TaskData taskData = createTaskData(repository, monitor, user, project,
issue);
taskData.setPartial(false);
addComments(taskData.getRoot(), comments, repository);
return taskData;
}
private Issue createIssue(TaskData taskData) {
Issue issue = new Issue();
if (!taskData.isNew())
issue.setNumber(Integer.parseInt(taskData.getTaskId()));
issue.setBody(getAttributeValue(taskData,
IssueAttribute.BODY.getMetadata()));
issue.setTitle(getAttributeValue(taskData,
IssueAttribute.TITLE.getMetadata()));
String assigneeValue = getAttributeValue(taskData,
IssueAttribute.ASSIGNEE.getMetadata());
if (assigneeValue != null) {
if (assigneeValue.trim().length() == 0)
assigneeValue = null;
User assignee = new User().setLogin(assigneeValue);
issue.setAssignee(assignee);
}
String milestoneValue = getAttributeValue(taskData,
IssueAttribute.MILESTONE.getMetadata());
if (milestoneValue != null) {
Milestone milestone = new Milestone();
if (milestoneValue.length() > 0)
milestone.setNumber(Integer.parseInt(milestoneValue));
issue.setMilestone(milestone);
}
return issue;
}
private TaskAttribute createAttribute(TaskData data,
IssueAttribute attribute, List<Label> values) {
TaskAttribute attr = createAttribute(data, attribute.getMetadata());
if (values != null) {
List<String> labels = new ArrayList<String>(values.size());
for (Label label : values)
labels.add(label.getName());
data.getAttributeMapper().setValues(attr, labels);
}
return attr;
}
@Override
public boolean initializeTaskData(TaskRepository repository, TaskData data,
ITaskMapping initializationData, IProgressMonitor monitor)
throws CoreException {
data.setVersion(DATA_VERSION);
for (IssueAttribute attr : IssueAttribute.values())
if (attr.getMetadata().isInitTask())
createAttribute(data, attr.getMetadata(), (String) null);
Issue dummy = new Issue();
createLabels(repository, data, dummy);
createMilestones(repository, data, dummy);
return true;
}
/**
* Create any new labels that have been added to the issue and set the
* issues labels to the current value of labels attribute.
*
* @param user
* @param repo
* @param client
* @param repository
* @param data
* @param oldAttributes
* @param issue
*/
protected void updateLabels(String user, String repo, GitHubClient client,
TaskRepository repository, TaskData data,
Set<TaskAttribute> oldAttributes, Issue issue) {
TaskAttribute labelsAttribute = data.getRoot().getAttribute(
IssueAttribute.LABELS.getMetadata().getId());
if (oldAttributes.contains(labelsAttribute) || data.isNew()) {
LabelService labelService = new LabelService(client);
if (!connector.hasCachedLabels(repository))
try {
connector.refreshLabels(repository);
} catch (CoreException ignore) {
// Ignore
}
List<Label> currentLabels = connector.getLabels(repository);
List<Label> newLabels = new LinkedList<Label>();
List<Label> labels = new LinkedList<Label>();
for (String value : labelsAttribute.getValues()) {
Label label = new Label().setName(value);
if (!currentLabels.contains(label))
newLabels.add(label);
labels.add(label);
}
issue.setLabels(labels);
for (Label label : newLabels)
try {
labelService.createLabel(user, repo, label);
} catch (IOException e) {
// TODO detect failure and handle label already created
}
if (!newLabels.isEmpty())
try {
connector.refreshLabels(repository);
} catch (CoreException ignore) {
// Ignore
}
}
}
@Override
public RepositoryResponse postTaskData(TaskRepository repository,
TaskData taskData, Set<TaskAttribute> oldAttributes,
IProgressMonitor monitor) throws CoreException {
String taskId = taskData.getTaskId();
Issue issue = createIssue(taskData);
RepositoryId repo = GitHub.getRepository(repository.getRepositoryUrl());
try {
GitHubClient client = IssueConnector.createClient(repository);
boolean collaborator = isCollaborator(client, repo);
if (collaborator)
updateLabels(repo.getOwner(), repo.getName(), client,
repository, taskData, oldAttributes, issue);
IssueService service = new IssueService(client);
if (taskData.isNew()) {
issue.setState(IssueService.STATE_OPEN);
issue = service.createIssue(repo.getOwner(), repo.getName(),
issue);
taskId = Integer.toString(issue.getNumber());
} else {
// Handle new comment
String comment = getAttributeValue(taskData,
IssueAttribute.COMMENT_NEW.getMetadata());
if (comment != null && comment.length() > 0)
service.createComment(repo.getOwner(), repo.getName(),
taskId, comment);
boolean reporter = attributeMatchesUser(client,
IssueAttribute.REPORTER.getMetadata(), taskData);
if (collaborator || reporter) {
// Handle state change
TaskAttribute operationAttribute = taskData.getRoot()
.getAttribute(TaskAttribute.OPERATION);
if (operationAttribute != null) {
IssueOperation operation = IssueOperation
.fromId(operationAttribute.getValue());
if (operation == IssueOperation.REOPEN)
issue.setState(IssueService.STATE_OPEN);
else if (operation == IssueOperation.CLOSE)
issue.setState(IssueService.STATE_CLOSED);
}
service.editIssue(repo.getOwner(), repo.getName(), issue);
}
}
return new RepositoryResponse(
taskData.isNew() ? ResponseKind.TASK_CREATED
: ResponseKind.TASK_UPDATED, taskId);
} catch (IOException e) {
throw new CoreException(GitHub.createWrappedStatus(e));
}
}
}