| /********************************************************************* |
| * Copyright (c) 2010, 2014 Sony Ericsson/ST Ericsson 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: |
| * Sony Ericsson/ST Ericsson - initial API and implementation |
| * Tasktop Technologies - improvements |
| * GitHub, Inc. - fixes for bug 354753 |
| * Sascha Scholz (SAP) - improvements |
| * Marc-Andre Laperle (Ericsson) - Add topic |
| *********************************************************************/ |
| package org.eclipse.mylyn.internal.gerrit.core; |
| |
| import java.util.Date; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.mylyn.internal.gerrit.core.client.GerritChange; |
| import org.eclipse.mylyn.internal.gerrit.core.client.GerritClient; |
| import org.eclipse.mylyn.internal.gerrit.core.client.GerritConfiguration; |
| import org.eclipse.mylyn.internal.gerrit.core.client.GerritException; |
| import org.eclipse.mylyn.internal.gerrit.core.client.compat.ChangeDetailX; |
| import org.eclipse.mylyn.internal.gerrit.core.client.data.GerritPerson; |
| import org.eclipse.mylyn.internal.gerrit.core.client.data.GerritQueryResult; |
| import org.eclipse.mylyn.internal.gerrit.core.client.rest.GerritReviewLabel; |
| import org.eclipse.mylyn.internal.gerrit.core.remote.GerritRemoteFactoryProvider; |
| import org.eclipse.mylyn.reviews.core.model.IRepository; |
| import org.eclipse.mylyn.reviews.core.model.IReview; |
| import org.eclipse.mylyn.reviews.core.spi.remote.emf.RemoteEmfConsumer; |
| import org.eclipse.mylyn.reviews.core.spi.remote.emf.RemoteEmfObserver; |
| import org.eclipse.mylyn.tasks.core.IRepositoryPerson; |
| import org.eclipse.mylyn.tasks.core.ITaskMapping; |
| import org.eclipse.mylyn.tasks.core.RepositoryResponse; |
| import org.eclipse.mylyn.tasks.core.TaskRepository; |
| import org.eclipse.mylyn.tasks.core.data.AbstractTaskDataHandler; |
| import org.eclipse.mylyn.tasks.core.data.AbstractTaskSchema.Field; |
| import org.eclipse.mylyn.tasks.core.data.TaskAttribute; |
| import org.eclipse.mylyn.tasks.core.data.TaskAttributeMapper; |
| import org.eclipse.mylyn.tasks.core.data.TaskCommentMapper; |
| import org.eclipse.mylyn.tasks.core.data.TaskData; |
| import org.eclipse.osgi.util.NLS; |
| |
| import com.google.gerrit.common.data.AccountInfo; |
| import com.google.gerrit.common.data.ApprovalDetail; |
| import com.google.gerrit.common.data.ChangeDetail; |
| import com.google.gerrit.common.data.ChangeInfo; |
| import com.google.gerrit.reviewdb.Account; |
| import com.google.gerrit.reviewdb.ApprovalCategory; |
| import com.google.gerrit.reviewdb.Change; |
| import com.google.gerrit.reviewdb.ChangeMessage; |
| import com.google.gerrit.reviewdb.PatchSetApproval; |
| import com.google.gerrit.reviewdb.Project; |
| |
| /** |
| * @author Mikael Kober |
| * @author Thomas Westling |
| * @author Steffen Pingel |
| * @author Kevin Sawicki |
| */ |
| public class GerritTaskDataHandler extends AbstractTaskDataHandler { |
| |
| private final GerritConnector connector; |
| |
| private final String ANONYMOUS = "Anonymous"; //$NON-NLS-1$ |
| |
| public GerritTaskDataHandler(GerritConnector connector) { |
| this.connector = connector; |
| } |
| |
| public TaskData createTaskData(TaskRepository repository, String taskId, IProgressMonitor monitor) { |
| TaskData data = new TaskData(getAttributeMapper(repository), GerritConnector.CONNECTOR_KIND, |
| repository.getRepositoryUrl(), taskId); |
| initializeTaskData(repository, data, null, monitor); |
| return data; |
| } |
| |
| public TaskData createPartialTaskData(TaskRepository repository, String taskId, IProgressMonitor monitor) { |
| TaskData data = new TaskData(getAttributeMapper(repository), GerritConnector.CONNECTOR_KIND, |
| repository.getRepositoryUrl(), taskId); |
| GerritQueryResultSchema.getDefault().initialize(data); |
| data.setPartial(true); |
| return data; |
| } |
| |
| @Override |
| public TaskAttributeMapper getAttributeMapper(TaskRepository repository) { |
| return new TaskAttributeMapper(repository); |
| } |
| |
| /** |
| * Retrieves task data for the given review from repository. |
| */ |
| public TaskData getTaskData(TaskRepository repository, String taskId, IProgressMonitor monitor) |
| throws CoreException { |
| ReviewObserver reviewObserver = new ReviewObserver(); |
| try { |
| GerritClient client = connector.getClient(repository); |
| boolean anonymous = client.isAnonymous(); |
| String id = null; |
| if (!anonymous) { |
| id = getAccountId(client, repository, monitor); |
| } |
| taskId = client.toReviewId(taskId, monitor); |
| TaskData taskData = createTaskData(repository, taskId, monitor); |
| |
| RemoteEmfConsumer<IRepository, IReview, String, GerritChange, String, Date> consumer = updateModelData( |
| repository, taskData, reviewObserver, monitor); |
| GerritChange gerritChange = consumer.getRemoteObject(); |
| if (gerritChange == null) { |
| throw new CoreException(connector.createErrorStatus(repository, |
| NLS.bind("Couldn't retrieve remote object for task: {0}. Check remote connection", taskId))); //$NON-NLS-1$ |
| } |
| Project.NameKey project = gerritChange.getChangeDetail().getChange().getProject(); |
| client.refreshConfigOnce(project, monitor); |
| if (!monitor.isCanceled()) { |
| updateTaskData(repository, taskData, gerritChange, !anonymous, id); |
| } |
| return taskData; |
| } catch (GerritException e) { |
| throw connector.toCoreException(repository, |
| NLS.bind("Problem retrieving task data for task: {0}", taskId), e); //$NON-NLS-1$ |
| } finally { |
| reviewObserver.dispose(); |
| } |
| } |
| |
| private RemoteEmfConsumer<IRepository, IReview, String, GerritChange, String, Date> updateModelData( |
| TaskRepository repository, TaskData taskData, ReviewObserver reviewObserver, IProgressMonitor monitor) |
| throws CoreException { |
| GerritClient client = connector.getClient(repository); |
| GerritRemoteFactoryProvider factoryProvider = (GerritRemoteFactoryProvider) client.getFactoryProvider(); |
| RemoteEmfConsumer<IRepository, IReview, String, GerritChange, String, Date> consumer = factoryProvider.getReviewFactory() |
| .getConsumerForLocalKey(factoryProvider.getRoot(), taskData.getTaskId()); |
| |
| consumer.addObserver(reviewObserver); |
| if (!consumer.isRetrieving()) { |
| if (monitor.isCanceled()) { |
| return consumer; |
| } |
| consumer.open(); |
| if (monitor.isCanceled()) { |
| return consumer; |
| } |
| consumer.setAsynchronous(false); |
| consumer.retrieve(true); |
| consumer.setAsynchronous(true); |
| } |
| |
| while (!reviewObserver.complete && !monitor.isCanceled()) { |
| try { |
| Thread.sleep(50); |
| } catch (InterruptedException e) { |
| reviewObserver.dispose(); |
| Thread.currentThread().interrupt(); |
| } |
| } |
| |
| consumer.save(); |
| if (!consumer.getStatus().isOK()) { |
| if (consumer.getStatus().getException() instanceof CoreException) { |
| throw ((CoreException) consumer.getStatus().getException()); |
| } |
| throw new CoreException(consumer.getStatus()); |
| } |
| return consumer; |
| } |
| |
| private class ReviewObserver extends RemoteEmfObserver<IRepository, IReview, String, Date> { |
| boolean complete; |
| |
| @Override |
| public void updated(boolean modified) { |
| complete = true; |
| } |
| } |
| |
| /** |
| * Get account id for repository |
| * |
| * @param client |
| * @param repository |
| * @param monitor |
| * @return account id or null if not found |
| * @throws GerritException |
| */ |
| protected String getAccountId(GerritClient client, TaskRepository repository, IProgressMonitor monitor) |
| throws GerritException { |
| String id = repository.getProperty(GerritConnector.KEY_REPOSITORY_ACCOUNT_ID); |
| if (id == null) { |
| Account account = client.getAccount(monitor); |
| if (account != null) { |
| id = account.getId().toString(); |
| repository.setProperty(GerritConnector.KEY_REPOSITORY_ACCOUNT_ID, id); |
| } |
| } |
| return id; |
| } |
| |
| @Override |
| public boolean initializeTaskData(TaskRepository repository, TaskData taskData, ITaskMapping initializationData, |
| IProgressMonitor monitor) { |
| GerritTaskSchema.getDefault().initialize(taskData); |
| return true; |
| } |
| |
| @Override |
| public RepositoryResponse postTaskData(TaskRepository repository, TaskData taskData, |
| Set<TaskAttribute> oldAttributes, IProgressMonitor monitor) throws CoreException { |
| throw new UnsupportedOperationException(); |
| } |
| |
| public void updateTaskData(TaskRepository repository, TaskData data, GerritChange gerritReview, boolean canPublish, |
| String accountId) { |
| GerritTaskSchema schema = GerritTaskSchema.getDefault(); |
| |
| ChangeDetail changeDetail = gerritReview.getChangeDetail(); |
| Change change = changeDetail.getChange(); |
| AccountInfo owner = changeDetail.getAccounts().get(change.getOwner()); |
| |
| updatePartialTaskData(repository, data, new GerritQueryResult(new ChangeInfo(change))); |
| setAttributeValue(data, schema.BRANCH, change.getDest().get()); |
| String userId = GerritUtil.getUserId(owner); |
| String userLabel = GerritUtil.getUserLabel(owner); |
| TaskAttribute ownerAttribute = setAttributeValue(data, schema.OWNER, userId); |
| if (ownerAttribute != null) { |
| ownerAttribute.putOption(userId, userLabel); |
| } |
| setAttributeValue(data, schema.UPLOADED, dateToString(((ChangeDetailX) changeDetail).getDateCreated())); |
| setAttributeValue(data, schema.UPDATED, dateToString(((ChangeDetailX) changeDetail).getLastModified())); |
| setAttributeValue(data, schema.DESCRIPTION, changeDetail.getDescription()); |
| int i = 1; |
| String accountName = repository.getUserName(); |
| for (ChangeMessage message : changeDetail.getMessages()) { |
| TaskCommentMapper mapper = new TaskCommentMapper(); |
| if (message.getAuthor() != null) { |
| AccountInfo author = changeDetail.getAccounts().get(message.getAuthor()); |
| String userName; |
| String id = author.getId().toString(); |
| if (id.equals(accountId) && accountName != null) { |
| userName = accountName; |
| } else { |
| String email = author.getPreferredEmail(); |
| userName = (email != null) ? email : id; |
| } |
| IRepositoryPerson person = repository.createPerson(userName); |
| person.setName(author.getFullName()); |
| mapper.setAuthor(person); |
| } else { |
| // messages without an author are from Gerrit itself |
| IRepositoryPerson person = repository.createPerson("Gerrit Code Review"); //$NON-NLS-1$ |
| mapper.setAuthor(person); |
| } |
| mapper.setText(message.getMessage()); |
| mapper.setCreationDate(message.getWrittenOn()); |
| mapper.setNumber(i); |
| TaskAttribute attribute = data.getRoot().createAttribute(TaskAttribute.PREFIX_COMMENT + i); |
| mapper.applyTo(attribute); |
| i++; |
| } |
| |
| setAttributeValue(data, schema.CAN_PUBLISH, Boolean.toString(canPublish)); |
| |
| // Retrieve the 'starred' state |
| setAttributeValue(data, schema.IS_STARRED, Boolean.toString(changeDetail.isStarred())); |
| |
| // Retrieve the approvals |
| Short reviewState = 0; |
| Short verifyState = 0; |
| for (ApprovalDetail approvals : changeDetail.getApprovals()) { |
| Map<ApprovalCategory.Id, PatchSetApproval> map = approvals.getApprovalMap(); |
| PatchSetApproval approval = map.get(new ApprovalCategory.Id("CRVW")); //$NON-NLS-1$ |
| if (approval != null) { |
| reviewState = getStateValue(approval.getValue(), reviewState); |
| } |
| approval = map.get(new ApprovalCategory.Id("VRIF")); //$NON-NLS-1$ |
| if (approval != null) { |
| verifyState = getStateValue(approval.getValue(), verifyState); |
| } |
| } |
| setAttributeValue(data, schema.REVIEW_STATE, reviewState.toString()); |
| setAttributeValue(data, schema.VERIFY_STATE, verifyState.toString()); |
| } |
| |
| private Short getStateValue(Short value, Short oldState) { |
| Short state = 0; |
| if (value < 0) { |
| state = (short) Math.min(oldState, value); |
| } else { |
| state = (short) Math.max(oldState, value); |
| } |
| return state; |
| } |
| |
| @Override |
| public void migrateTaskData(TaskRepository repository, TaskData taskData) { |
| super.migrateTaskData(repository, taskData); |
| //Support 1.1.0 commenting capability see https://bugs.eclipse.org/bugs/show_bug.cgi?id=344108 |
| if (taskData.getRoot().getAttribute(GerritTaskSchema.getDefault().NEW_COMMENT.getKey()) == null) { |
| taskData.getRoot().createAttribute(GerritTaskSchema.getDefault().NEW_COMMENT.getKey()); |
| } |
| } |
| |
| public void updatePartialTaskData(TaskRepository repository, TaskData data, GerritQueryResult queryResult) { |
| GerritQueryResultSchema schema = GerritQueryResultSchema.getDefault(); |
| setAttributeValue(data, schema.KEY, shortenChangeId(queryResult.getId())); |
| setAttributeValue(data, schema.PROJECT, queryResult.getProject()); |
| setAttributeValue(data, schema.SUMMARY, queryResult.getSubject()); |
| setAttributeValue(data, schema.STATUS, queryResult.getStatus()); |
| setAttributeValue(data, schema.URL, connector.getTaskUrl(repository.getUrl(), data.getTaskId())); |
| setAttributeValue(data, schema.UPDATED, dateToString(queryResult.getUpdated())); |
| setAttributeValue(data, schema.CHANGE_ID, queryResult.getId()); |
| if (GerritConnector.isClosed(queryResult.getStatus())) { |
| setAttributeValue(data, schema.COMPLETED, dateToString(queryResult.getUpdated())); |
| } |
| |
| GerritPerson owner = queryResult.getOwner(); |
| if (owner != null) { |
| String fullName = getFullNameFromAccount(repository); |
| if (fullName != null && fullName.equals(owner.getName())) { |
| // populate ITask.ownerId so that My Tasks filter works |
| String preferredEmail = getPreferredEmailFromAccount(repository); |
| TaskAttribute ownerAttribute = setAttributeValue(data, schema.OWNER, preferredEmail); |
| ownerAttribute.putOption(preferredEmail, fullName); |
| } else { |
| // we don't have the owner id and it could be expensive to include it in the query results |
| setAttributeValue(data, schema.OWNER, owner.getName()); |
| } |
| } else { |
| setAttributeValue(data, schema.OWNER, ANONYMOUS); |
| } |
| setAttributeValue(data, schema.BRANCH, queryResult.getBranch()); |
| setAttributeValue(data, schema.IS_STARRED, (queryResult.isStarred() ? Boolean.TRUE : Boolean.FALSE).toString()); |
| setAttributeValue(data, schema.TOPIC, queryResult.getTopic()); |
| |
| GerritReviewLabel reviewLabel = queryResult.getReviewLabel(); |
| if (reviewLabel != null) { |
| if (reviewLabel.getVerifyStatus() != null) { |
| setAttributeValue(data, schema.VERIFY_STATE, reviewLabel.getVerifyStatus().getStatus()); |
| } |
| if (reviewLabel.getCodeReviewStatus() != null) { |
| setAttributeValue(data, schema.REVIEW_STATE, reviewLabel.getCodeReviewStatus().getStatus()); |
| } |
| } |
| } |
| |
| private String getFullNameFromAccount(TaskRepository repository) { |
| GerritConfiguration config = connector.getConfiguration(repository); |
| if (config != null && config.getAccount() != null) { |
| return config.getAccount().getFullName(); |
| } |
| return null; |
| } |
| |
| private String getPreferredEmailFromAccount(TaskRepository repository) { |
| GerritConfiguration config = connector.getConfiguration(repository); |
| if (config != null && config.getAccount() != null) { |
| return config.getAccount().getPreferredEmail(); |
| } |
| return null; |
| } |
| |
| private String shortenChangeId(String changeId) { |
| changeId = GerritUtil.toChangeId(changeId); |
| return changeId.substring(0, Math.min(9, changeId.length())); |
| } |
| |
| /** |
| * Convenience method to set the value of a given Attribute in the given {@link TaskData}. |
| */ |
| private TaskAttribute setAttributeValue(TaskData data, Field gerritAttribute, String value) { |
| TaskAttribute attribute = data.getRoot().getAttribute(gerritAttribute.getKey()); |
| if (value != null) { |
| attribute.setValue(value); |
| } |
| return attribute; |
| } |
| |
| private static String dateToString(Date date) { |
| if (date == null) { |
| return ""; //$NON-NLS-1$ |
| } else { |
| return Long.toString(date.getTime()); |
| } |
| } |
| |
| } |