/*******************************************************************************
* Copyright (c) 2014, 2015 Ericsson AB 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:
* Guy Perron - original version
*
******************************************************************************/
package org.eclipse.mylyn.internal.reviews.ui.annotations;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.commons.lang.StringUtils;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.text.source.LineRange;
import org.eclipse.mylyn.internal.reviews.ui.annotations.InlineCommentEditor.CommentEditorState;
import org.eclipse.mylyn.internal.tasks.ui.TasksUiPlugin;
import org.eclipse.mylyn.reviews.core.model.IComment;
import org.eclipse.mylyn.reviews.core.model.IFileItem;
import org.eclipse.mylyn.reviews.core.model.IFileVersion;
import org.eclipse.mylyn.reviews.core.model.ILineLocation;
import org.eclipse.mylyn.reviews.core.model.ILineRange;
import org.eclipse.mylyn.reviews.core.model.IReviewItem;
import org.eclipse.mylyn.reviews.core.model.IReviewItemSet;
import org.eclipse.mylyn.reviews.core.model.IReviewsFactory;
import org.eclipse.mylyn.reviews.core.model.IUser;
import org.eclipse.mylyn.reviews.core.spi.ReviewsConnector;
import org.eclipse.mylyn.reviews.core.spi.remote.emf.RemoteEmfConsumer;
import org.eclipse.mylyn.reviews.core.spi.remote.review.IReviewRemoteFactoryProvider;
import org.eclipse.mylyn.reviews.ui.ReviewBehavior;
import org.eclipse.mylyn.tasks.core.TaskRepository;
import org.eclipse.mylyn.tasks.ui.TasksUi;
import org.eclipse.swt.widgets.Display;
public class InlineCommentSubmitter {
public static final String JOB_FAMILY = "CommentPopupDialogJobFamily"; // For Testing purposes //$NON-NLS-1$
private final IReviewItem reviewitem;
private final LineRange range;
private final CommentAnnotationHoverInput annotationInput;
private final InlineCommentEditor commentEditor;
/**
* Creates a comment submitter
*
* @param reviewitem
* the submitted comment's review item
* @param range
* the submitted comment's line range
* @param annotationInput
* the {@link CommentAnnotationHoverInput} associated with the dialog
* @param commentEditor
* the comment editor that this submitter is dependent on
*/
public InlineCommentSubmitter(IReviewItem reviewitem, LineRange range, CommentAnnotationHoverInput annotationInput,
InlineCommentEditor commentEditor) {
this.reviewitem = reviewitem;
this.range = range;
this.annotationInput = annotationInput;
this.commentEditor = commentEditor;
}
/**
* Creates a new comment, sets the comment's parameters based on the editor's current comment and text, and submits
* the comment
*/
public void saveComment() {
if (!commentEditor.getState().equals(CommentEditorState.VIEW)
&& (commentEditor.getState().equals(CommentEditorState.DISCARD) || StringUtils.isNotEmpty(commentEditor.getCommentEditorText()
.getText()
.trim()))) {
IComment comment = IReviewsFactory.INSTANCE.createComment();
comment.setDescription(commentEditor.getCommentEditorText().getText().trim());
if (commentEditor.getCurrentComment().isDraft()) {
comment.setId(commentEditor.getCurrentComment().getId());
}
comment.setDraft(true);
comment.setAuthor(getCurrentUser());
comment.setCreationDate(new Date());
ILineLocation location = getSelectedLineLocation();
if (location != null) {
comment.getLocations().add(location);
}
performOperation(comment);
commentEditor.forceDispose();
}
}
/**
* Performs a save or discard action
*
* @param comment
* the comment draft that will be added, edited or discarded from the comment thread
*/
private void performOperation(final IComment comment) {
final IReviewItem item = reviewitem;
final AtomicReference<IStatus> result = new AtomicReference<IStatus>();
final ReviewBehavior reviewBehavior = annotationInput.getBehavior();
if (!commentEditor.getState().equals(CommentEditorState.VIEW)) {
final Job job = new Job(Messages.CommandServerOperation) {
@Override
public boolean belongsTo(Object family) {
return family.equals(JOB_FAMILY);
}
@Override
protected IStatus run(IProgressMonitor monitor) {
IStatus status = null;
switch (commentEditor.getState()) {
case REPLY:
case EDIT:
status = reviewBehavior.addComment(item, comment, monitor);
if (status.isOK()) {
result.set(status);
updateClient(comment, item);
return Status.OK_STATUS;
}
break;
case DISCARD:
status = reviewBehavior.discardComment(item, comment, monitor);
if (status.isOK()) {
result.set(status);
updateClient(comment, item);
return Status.OK_STATUS;
}
break;
default:
Assert.isTrue(false, "Unknown state " + commentEditor.getState()); //$NON-NLS-1$
}
processServerError(status.getMessage());
return Status.OK_STATUS;
}
};
job.setUser(true);
job.schedule();
}
}
/**
* Updates the UI with the result of the comment operation (either a new comment draft, draft comment edit or
* discard comment action)
*
* @param comment
* the comment that the operation was done on
* @param item
* the item that will be updated
*/
private void updateClient(final IComment comment, final IReviewItem item) {
comment.setAuthor(getCurrentUser());
if (commentEditor.getState().equals(CommentEditorState.REPLY)) {
item.getComments().add(comment);
} else if (commentEditor.getState().equals(CommentEditorState.DISCARD)) {
List<IComment> commentlist = item.getComments();
for (Iterator<IComment> iter = commentlist.iterator(); iter.hasNext();) {
IComment element = iter.next();
if (element.getId() != null && element.getId().equals(commentEditor.getCurrentComment().getId())) {
iter.remove();
break;
}
}
} else {
List<IComment> commentlist = item.getComments();
for (int i = 0; i < commentlist.size(); i++) {
if (commentlist.get(i).getId() != null
&& commentlist.get(i).getId().equals(commentEditor.getCurrentComment().getId())) {
item.getComments().set(i, comment);
break;
}
}
}
IFileItem file = null;
if (item instanceof IFileItem) {
file = (IFileItem) item;
} else if (item instanceof IFileVersion) {
file = ((IFileVersion) item).getFile();
}
if (file != null && file.getReview() != null) {
// Update any review item set observers IFF we belong to a review. (The set might represent a compare,
// in which case we won't have a relevant model object.)
updateConsumer(file);
}
}
/**
* Creates an error dialog for unsuccessful comment actions
*
* @param message
* the message that will be displayed to the user
*/
private void processServerError(final String message) {
Display.getDefault().syncExec(new Runnable() {
public void run() {
final MessageDialog dialog = new MessageDialog(null, Messages.CommentPopupDialog_ServerError, null,
message, MessageDialog.ERROR, new String[] { IDialogConstants.CANCEL_LABEL }, 0);
dialog.open();
}
});
}
/**
* Updates the file with the new changes
*
* @param file
* the file that will be updated
*/
private void updateConsumer(IFileItem file) {
final ReviewBehavior reviewBehavior = annotationInput.getBehavior();
TaskRepository taskRepository = TasksUi.getRepositoryManager().getRepository(
reviewBehavior.getTask().getConnectorKind(), reviewBehavior.getTask().getRepositoryUrl());
@SuppressWarnings("restriction")
ReviewsConnector connector = (ReviewsConnector) TasksUiPlugin.getConnector(reviewBehavior.getTask()
.getConnectorKind());
IReviewRemoteFactoryProvider factoryProvider = (IReviewRemoteFactoryProvider) connector.getReviewClient(
taskRepository).getFactoryProvider();
RemoteEmfConsumer<IReviewItemSet, List<IFileItem>, String, ?, ?, Long> consumer = factoryProvider.getReviewItemSetContentFactory()
.getConsumerForLocalKey(file.getSet(), file.getSet().getId());
consumer.updateObservers();
consumer.release();
}
/**
* @return the current {@link IUser} for this repository
*/
private IUser getCurrentUser() {
if (reviewitem != null && reviewitem.getReview() != null && reviewitem.getReview().getRepository() != null) {
return reviewitem.getReview().getRepository().getAccount();
} else {
return null;
}
}
/**
* @return a {@ILineLocation} created from the {@link ILineRange} provided in the constructor
*/
protected ILineLocation getSelectedLineLocation() {
ILineLocation location = IReviewsFactory.INSTANCE.createLineLocation();
ILineRange lineRange = IReviewsFactory.INSTANCE.createLineRange();
lineRange.setStart(range.getStartLine());
lineRange.setEnd(range.getStartLine() + range.getNumberOfLines());
location.getRanges().add(lineRange);
return location;
}
}