Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiles Parker2013-04-17 20:31:04 -0400
committerMiles Parker2013-04-18 16:23:43 -0400
commit5c470d1c419260c79c963b9a60b20210355911a5 (patch)
treeceb6b0619f3b04eb1419439ffa3f999cc803b96d
parenta4b2f2e773853d828cd440e0e0dc50d9db0bafc5 (diff)
downloadorg.eclipse.mylyn.reviews-5c470d1c419260c79c963b9a60b20210355911a5.tar.gz
org.eclipse.mylyn.reviews-5c470d1c419260c79c963b9a60b20210355911a5.tar.xz
org.eclipse.mylyn.reviews-5c470d1c419260c79c963b9a60b20210355911a5.zip
400168: implement gerrit Remote API
•Improve Remote API, separating consumer and observer concerns •Remote API now fully decouples remote access from UI •Editor and navigator interactions are now fully synchronized •Implement changes for Gerrit Remote API •Update Gerrit ChangeDetailX •Implement additional Gerrit Remote Factories applications •Fix update bugs affecting patch set and review detail sections •Additional testing Change-Id: I336942c1be7fbeeb9098b666b1371727df1bd776 Task-Url: https://bugs.eclipse.org/bugs/show_bug.cgi?id=400270
-rw-r--r--org.eclipse.mylyn.gerrit.core/.classpath1
-rw-r--r--org.eclipse.mylyn.gerrit.core/META-INF/MANIFEST.MF4
-rw-r--r--org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/GerritConnector.java10
-rw-r--r--org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/client/GerritClient.java11
-rw-r--r--org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/client/PatchSetContent.java10
-rw-r--r--org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/client/compat/ChangeDetailX.java11
-rw-r--r--org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/client/compat/SubmitRecord.java71
-rw-r--r--org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/operations/AbandonRequest.java2
-rw-r--r--org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/remote/GerritRemoteFactoryProvider.java56
-rw-r--r--org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/remote/GerritReviewRemoteFactory.java358
-rw-r--r--org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/remote/GerritUserRemoteFactory.java (renamed from org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/remote/UserRemoteFactory.java)48
-rw-r--r--org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/remote/PatchSetContentCompareRemoteFactory.java47
-rw-r--r--org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/remote/PatchSetContentIdRemoteFactory.java55
-rw-r--r--org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/remote/PatchSetContentRemoteFactory.java152
-rw-r--r--org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/remote/PatchSetDetailRemoteFactory.java (renamed from org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/remote/ReviewItemSetRemoteFactory.java)37
-rw-r--r--org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/remote/ReviewRemoteFactory.java126
-rw-r--r--org.eclipse.mylyn.gerrit.tests/META-INF/MANIFEST.MF4
-rw-r--r--org.eclipse.mylyn.gerrit.tests/src/org/eclipse/mylyn/internal/gerrit/core/remote/GerritRemoteFactoryTest.java270
-rw-r--r--org.eclipse.mylyn.gerrit.tests/src/org/eclipse/mylyn/internal/gerrit/core/remote/TestRemoteObserver.java105
-rw-r--r--org.eclipse.mylyn.gerrit.ui/META-INF/MANIFEST.MF3
-rw-r--r--org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/GerritConnectorUi.java2
-rw-r--r--org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/GerritUiPlugin.java1
-rw-r--r--org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/editor/GerritReviewDetailSection.java48
-rw-r--r--org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/editor/GerritTaskEditorPage.java17
-rw-r--r--org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/editor/ReviewSection.java257
-rw-r--r--org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/factories/AbstractPatchSetUiFactory.java12
-rw-r--r--org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/factories/AddReviewersUiFactory.java6
-rw-r--r--org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/factories/CompareWithUiFactory.java59
-rw-r--r--org.eclipse.mylyn.reviews.core.tests/src/org/eclipse/mylyn/reviews/core/remote/RemoteEmfFactoryTest.java625
-rw-r--r--org.eclipse.mylyn.reviews.core.tests/src/org/eclipse/mylyn/reviews/core/remote/RemoteEmfObserverTest.java80
-rw-r--r--org.eclipse.mylyn.reviews.core.tests/src/org/eclipse/mylyn/reviews/core/remote/RemoteServiceTest.java26
-rw-r--r--org.eclipse.mylyn.reviews.core.tests/src/org/eclipse/mylyn/reviews/core/remote/TestIRemoteEmfObserver.java104
-rw-r--r--org.eclipse.mylyn.reviews.core.tests/src/org/eclipse/mylyn/reviews/core/remote/TestRemoteEmfObserver.java106
-rw-r--r--org.eclipse.mylyn.reviews.core.tests/src/org/eclipse/mylyn/reviews/core/remote/TestRemoteFactory.java77
-rw-r--r--org.eclipse.mylyn.reviews.core.tests/src/org/eclipse/mylyn/reviews/core/remote/TestRemoteFactoryProvider.java21
-rw-r--r--org.eclipse.mylyn.reviews.core.tests/src/org/eclipse/mylyn/reviews/core/remote/TestRemoteObject.java26
-rw-r--r--org.eclipse.mylyn.reviews.core/META-INF/MANIFEST.MF9
-rw-r--r--org.eclipse.mylyn.reviews.core/src/org/eclipse/mylyn/reviews/core/spi/remote/AbstractRemoteConsumer.java19
-rw-r--r--org.eclipse.mylyn.reviews.core/src/org/eclipse/mylyn/reviews/core/spi/remote/AbstractRemoteFactoryProvider.java14
-rw-r--r--org.eclipse.mylyn.reviews.core/src/org/eclipse/mylyn/reviews/core/spi/remote/AbstractRemoteService.java33
-rw-r--r--org.eclipse.mylyn.reviews.core/src/org/eclipse/mylyn/reviews/core/spi/remote/JobRemoteService.java72
-rw-r--r--org.eclipse.mylyn.reviews.core/src/org/eclipse/mylyn/reviews/core/spi/remote/emf/AbstractRemoteEmfFactory.java416
-rw-r--r--org.eclipse.mylyn.reviews.core/src/org/eclipse/mylyn/reviews/core/spi/remote/emf/IRemoteEmfObserver.java72
-rw-r--r--org.eclipse.mylyn.reviews.core/src/org/eclipse/mylyn/reviews/core/spi/remote/emf/RemoteEmfConsumer.java422
-rw-r--r--org.eclipse.mylyn.reviews.core/src/org/eclipse/mylyn/reviews/core/spi/remote/emf/RemoteEmfObserver.java87
-rw-r--r--org.eclipse.mylyn.reviews.core/src/org/eclipse/mylyn/reviews/core/spi/remote/emf/ReviewsRemoteFactoryProvider.java47
-rw-r--r--org.eclipse.mylyn.reviews.core/src/org/eclipse/mylyn/reviews/core/spi/remote/review/IReviewRemoteFactoryProvider.java36
-rw-r--r--org.eclipse.mylyn.reviews.core/src/org/eclipse/mylyn/reviews/internal/core/ReviewsConnector.java36
-rw-r--r--org.eclipse.mylyn.reviews.edit/META-INF/MANIFEST.MF5
-rw-r--r--org.eclipse.mylyn.reviews.edit/icons/full/ctool16/CreateReview_reviewTask_TaskReference.gifbin223 -> 0 bytes
-rw-r--r--org.eclipse.mylyn.reviews.edit/plugin.xml1
-rw-r--r--org.eclipse.mylyn.reviews.edit/src/org/eclipse/mylyn/reviews/edit/ReviewsEditPlugin.java34
-rw-r--r--org.eclipse.mylyn.reviews.edit/src/org/eclipse/mylyn/reviews/edit/remote/AbstractRemoteEditFactoryProvider.java109
-rw-r--r--org.eclipse.mylyn.reviews.edit/src/org/eclipse/mylyn/reviews/edit/remote/ReviewsRemoteEditFactoryProvider.java31
-rw-r--r--org.eclipse.mylyn.reviews.ui/META-INF/MANIFEST.MF4
-rw-r--r--org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/internal/reviews/ui/Messages.java31
-rw-r--r--org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/internal/reviews/ui/ReviewsUiConstants.java8
-rw-r--r--org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/internal/reviews/ui/ReviewsUiPlugin.java22
-rw-r--r--org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/internal/reviews/ui/messages.properties3
-rw-r--r--org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/internal/reviews/ui/providers/RetrievingContentsNode.java40
-rw-r--r--org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/internal/reviews/ui/providers/ReviewsLabelProvider.java14
-rw-r--r--org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/internal/reviews/ui/providers/ReviewsTreeContentProvider.java12
-rw-r--r--org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/internal/reviews/ui/views/ReviewExplorer.java242
-rw-r--r--org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/reviews/ui/spi/editor/AbstractReviewSection.java96
-rw-r--r--org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/reviews/ui/spi/editor/AbstractReviewTaskEditorPage.java128
-rw-r--r--org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/reviews/ui/spi/editor/ReviewDetailSection.java208
-rw-r--r--org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/reviews/ui/spi/editor/ReviewSetContentSection.java144
-rw-r--r--org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/reviews/ui/spi/editor/ReviewSetSection.java24
-rw-r--r--org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/reviews/ui/spi/factories/AbstractUiFactory.java6
-rw-r--r--org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/reviews/ui/spi/factories/IUiContext.java4
-rw-r--r--org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/reviews/ui/spi/remote/RemoteUiService.java12
71 files changed, 3685 insertions, 1604 deletions
diff --git a/org.eclipse.mylyn.gerrit.core/.classpath b/org.eclipse.mylyn.gerrit.core/.classpath
index 32a814bb..6a5e6853 100644
--- a/org.eclipse.mylyn.gerrit.core/.classpath
+++ b/org.eclipse.mylyn.gerrit.core/.classpath
@@ -4,6 +4,7 @@
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins">
<accessrules>
<accessrule kind="accessible" pattern="org/eclipse/mylyn/reviews/core/**"/>
+ <accessrule kind="accessible" pattern="org/eclipse/mylyn/reviews/internal/core/model/ReviewsPackage*"/>
</accessrules>
</classpathentry>
<classpathentry kind="src" path="src"/>
diff --git a/org.eclipse.mylyn.gerrit.core/META-INF/MANIFEST.MF b/org.eclipse.mylyn.gerrit.core/META-INF/MANIFEST.MF
index 43edbaa0..b491fe37 100644
--- a/org.eclipse.mylyn.gerrit.core/META-INF/MANIFEST.MF
+++ b/org.eclipse.mylyn.gerrit.core/META-INF/MANIFEST.MF
@@ -8,12 +8,14 @@ Require-Bundle: org.eclipse.core.runtime,
org.eclipse.emf.common;bundle-version="2.5.0",
org.eclipse.emf.ecore;bundle-version="2.5.0",
org.eclipse.emf.ecore.xmi;bundle-version="2.5.0",
+ org.eclipse.emf.edit;bundle-version="2.5.0",
org.eclipse.mylyn.commons.core,
org.eclipse.mylyn.commons.net,
org.eclipse.mylyn.reviews.core,
org.eclipse.mylyn.tasks.core,
org.eclipse.jgit,
- org.eclipse.egit.core
+ org.eclipse.egit.core,
+ org.eclipse.mylyn.reviews.edit;bundle-version="1.1.0"
Bundle-ActivationPolicy: lazy
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Export-Package: org.eclipse.mylyn.internal.gerrit.core;x-friends:="org.eclipse.mylyn.gerrit.ui",
diff --git a/org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/GerritConnector.java b/org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/GerritConnector.java
index d08c4e7a..98c80f29 100644
--- a/org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/GerritConnector.java
+++ b/org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/GerritConnector.java
@@ -39,7 +39,9 @@ import org.eclipse.mylyn.internal.gerrit.core.client.GerritLoginException;
import org.eclipse.mylyn.internal.gerrit.core.client.GerritSystemInfo;
import org.eclipse.mylyn.internal.gerrit.core.client.JSonSupport;
import org.eclipse.mylyn.internal.gerrit.core.client.data.GerritQueryResult;
-import org.eclipse.mylyn.tasks.core.AbstractRepositoryConnector;
+import org.eclipse.mylyn.internal.gerrit.core.remote.GerritRemoteFactoryProvider;
+import org.eclipse.mylyn.reviews.core.spi.remote.AbstractRemoteFactoryProvider;
+import org.eclipse.mylyn.reviews.internal.core.ReviewsConnector;
import org.eclipse.mylyn.tasks.core.IRepositoryQuery;
import org.eclipse.mylyn.tasks.core.ITask;
import org.eclipse.mylyn.tasks.core.ITaskMapping;
@@ -63,7 +65,7 @@ import com.google.gwtorm.server.StandardKeyEncoder;
* @author Sascha Scholz
* @author Miles Parker
*/
-public class GerritConnector extends AbstractRepositoryConnector {
+public class GerritConnector extends ReviewsConnector {
static Logger logger = Logger.getLogger("com.google.gson.ParameterizedTypeHandlerMap"); //$NON-NLS-1$
@@ -404,4 +406,8 @@ public class GerritConnector extends AbstractRepositoryConnector {
return "MERGED".equals(status) || "ABANDONED".equals(status);
}
+ @Override
+ public AbstractRemoteFactoryProvider createFactoryProvider(TaskRepository repository) {
+ return new GerritRemoteFactoryProvider(getClient(repository));
+ }
}
diff --git a/org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/client/GerritClient.java b/org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/client/GerritClient.java
index cc719f7a..096e2a9f 100644
--- a/org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/client/GerritClient.java
+++ b/org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/client/GerritClient.java
@@ -44,7 +44,6 @@ import org.eclipse.mylyn.internal.gerrit.core.client.compat.PatchSetPublishDetai
import org.eclipse.mylyn.internal.gerrit.core.client.compat.ProjectAdminService;
import org.eclipse.mylyn.internal.gerrit.core.client.compat.ProjectDetailX;
import org.eclipse.mylyn.internal.gerrit.core.client.data.GerritQueryResult;
-import org.eclipse.mylyn.reviews.core.model.IReviewsFactory;
import org.eclipse.osgi.util.NLS;
import com.google.gerrit.common.data.AccountDashboardInfo;
@@ -57,7 +56,6 @@ import com.google.gerrit.common.data.PatchScript;
import com.google.gerrit.common.data.PatchSetDetail;
import com.google.gerrit.common.data.ReviewerResult;
import com.google.gerrit.common.data.SingleListChangeInfo;
-import com.google.gerrit.common.data.SystemInfoService;
import com.google.gerrit.reviewdb.Account;
import com.google.gerrit.reviewdb.AccountDiffPreference;
import com.google.gerrit.reviewdb.AccountDiffPreference.Whitespace;
@@ -86,8 +84,6 @@ import com.google.gwtjsonrpc.client.VoidResult;
*/
public class GerritClient {
- private static final IReviewsFactory FACTORY = IReviewsFactory.INSTANCE;
-
private abstract class Operation<T> implements AsyncCallback<T> {
private Throwable exception;
@@ -274,7 +270,7 @@ public class GerritClient {
});
}
- public ChangeDetail abondon(String reviewId, int patchSetId, final String message, IProgressMonitor monitor)
+ public ChangeDetail abandon(String reviewId, int patchSetId, final String message, IProgressMonitor monitor)
throws GerritException {
final PatchSet.Id id = new PatchSet.Id(new Change.Id(id(reviewId)), patchSetId);
return execute(monitor, new Operation<ChangeDetail>() {
@@ -818,10 +814,6 @@ public class GerritClient {
return getService(PatchDetailService.class);
}
- private SystemInfoService getSystemInfoService() {
- return getService(SystemInfoService.class);
- }
-
private List<Project> getVisibleProjects(IProgressMonitor monitor, GerritConfig gerritConfig)
throws GerritException {
List<Project> result = new ArrayList<Project>();
@@ -915,5 +907,4 @@ public class GerritClient {
}
return clazz.cast(service);
}
-
}
diff --git a/org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/client/PatchSetContent.java b/org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/client/PatchSetContent.java
index cba41d23..7f11974d 100644
--- a/org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/client/PatchSetContent.java
+++ b/org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/client/PatchSetContent.java
@@ -96,4 +96,14 @@ public class PatchSetContent {
return patchScriptByPatchKey.get(key);
}
+ public String getId() {
+ String id = "";
+ if (getBase() != null) {
+ id += getBase().getId() + "-";
+ }
+ if (getTarget() != null) {
+ id += getTarget().getId();
+ }
+ return id;
+ }
}
diff --git a/org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/client/compat/ChangeDetailX.java b/org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/client/compat/ChangeDetailX.java
index 7bb1169b..f7a49291 100644
--- a/org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/client/compat/ChangeDetailX.java
+++ b/org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/client/compat/ChangeDetailX.java
@@ -11,6 +11,8 @@
package org.eclipse.mylyn.internal.gerrit.core.client.compat;
+import java.util.List;
+
import com.google.gerrit.common.data.ChangeDetail;
/**
@@ -42,6 +44,8 @@ public class ChangeDetailX extends ChangeDetail {
private boolean canEdit;
+ protected List<SubmitRecord> submitRecords;
+
public boolean canRevert() {
return canRevert;
}
@@ -62,4 +66,11 @@ public class ChangeDetailX extends ChangeDetail {
return canDeleteDraft;
}
+ public List<SubmitRecord> getSubmitRecords() {
+ return submitRecords;
+ }
+
+ public void setSubmitRecords(List<SubmitRecord> submitRecords) {
+ this.submitRecords = submitRecords;
+ }
}
diff --git a/org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/client/compat/SubmitRecord.java b/org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/client/compat/SubmitRecord.java
new file mode 100644
index 00000000..e0555e68
--- /dev/null
+++ b/org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/client/compat/SubmitRecord.java
@@ -0,0 +1,71 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Tasktop Technologies 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:
+ * Tasktop Technologies - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylyn.internal.gerrit.core.client.compat;
+
+import java.util.List;
+
+import com.google.gerrit.reviewdb.Account;
+
+public class SubmitRecord {
+
+ public class Label {
+ private String label;
+
+ private String status;
+
+ private Account.Id appliedBy;
+
+ public String getLabel() {
+ return label;
+ }
+
+ public void setLabel(String label) {
+ this.label = label;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ public Account.Id getAppliedBy() {
+ return appliedBy;
+ }
+
+ public void setAppliedBy(Account.Id appliedBy) {
+ this.appliedBy = appliedBy;
+ }
+ }
+
+ String status;
+
+ private List<Label> labels;
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ public List<Label> getLabels() {
+ return labels;
+ }
+
+ public void setLabels(List<Label> labels) {
+ this.labels = labels;
+ }
+}
diff --git a/org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/operations/AbandonRequest.java b/org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/operations/AbandonRequest.java
index d8849117..c4d00168 100644
--- a/org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/operations/AbandonRequest.java
+++ b/org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/operations/AbandonRequest.java
@@ -43,7 +43,7 @@ public class AbandonRequest extends AbstractRequest<ChangeDetail> {
@Override
protected ChangeDetail execute(GerritClient client, IProgressMonitor monitor) throws GerritException {
- return client.abondon(getReviewId(), getPatchSetId(), getMessage(), monitor);
+ return client.abandon(getReviewId(), getPatchSetId(), getMessage(), monitor);
}
}
diff --git a/org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/remote/GerritRemoteFactoryProvider.java b/org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/remote/GerritRemoteFactoryProvider.java
index b317cd3f..385e7657 100644
--- a/org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/remote/GerritRemoteFactoryProvider.java
+++ b/org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/remote/GerritRemoteFactoryProvider.java
@@ -11,49 +11,77 @@
package org.eclipse.mylyn.internal.gerrit.core.remote;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.mylyn.internal.gerrit.core.client.GerritClient;
-import org.eclipse.mylyn.reviews.core.spi.remote.JobRemoteService;
-import org.eclipse.mylyn.reviews.core.spi.remote.emf.ReviewsRemoteFactoryProvider;
+import org.eclipse.mylyn.reviews.core.model.IReviewGroup;
+import org.eclipse.mylyn.reviews.core.model.IUser;
+import org.eclipse.mylyn.reviews.core.spi.remote.emf.RemoteEmfConsumer;
+import org.eclipse.mylyn.reviews.edit.remote.ReviewsRemoteEditFactoryProvider;
+import com.google.gerrit.common.data.AccountInfo;
import com.google.gerrit.common.data.AccountInfoCache;
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.Account.Id;
/**
* Implements a reviews factory provider for Gerrit remote API.
*
* @author Miles Parker
*/
-public class GerritRemoteFactoryProvider extends ReviewsRemoteFactoryProvider {
+public class GerritRemoteFactoryProvider extends ReviewsRemoteEditFactoryProvider {
private final GerritClient client;
- ReviewRemoteFactory reviewRemoteFactory = new ReviewRemoteFactory(this);
+ private final GerritReviewRemoteFactory gerritReviewRemoteFactory = new GerritReviewRemoteFactory(this);
- ReviewItemSetRemoteFactory reviewSetFactory = new ReviewItemSetRemoteFactory(this);
+ private final PatchSetDetailRemoteFactory reviewSetFactory = new PatchSetDetailRemoteFactory(this);
- PatchSetContentRemoteFactory reviewItemSetContentFactory = new PatchSetContentRemoteFactory(this);
+ private final PatchSetContentIdRemoteFactory reviewItemSetContentFactory = new PatchSetContentIdRemoteFactory(this);
- public GerritRemoteFactoryProvider(JobRemoteService service, GerritClient client) {
- super(service);
+ private final GerritUserRemoteFactory userFactory = new GerritUserRemoteFactory(this);
+
+ public GerritRemoteFactoryProvider(GerritClient client) {
this.client = client;
}
@Override
- public ReviewRemoteFactory getReviewFactory() {
- return reviewRemoteFactory;
+ public GerritReviewRemoteFactory getReviewFactory() {
+ return gerritReviewRemoteFactory;
}
@Override
- public ReviewItemSetRemoteFactory getReviewItemSetFactory() {
+ public PatchSetDetailRemoteFactory getReviewItemSetFactory() {
return reviewSetFactory;
}
@Override
- public PatchSetContentRemoteFactory getReviewItemSetContentFactory() {
+ public PatchSetContentIdRemoteFactory getReviewItemSetContentFactory() {
return reviewItemSetContentFactory;
}
- public UserRemoteFactory getUserFactory(AccountInfoCache cache) {
- return new UserRemoteFactory(this, cache);
+ public GerritUserRemoteFactory getUserFactory(AccountInfoCache cache) {
+ userFactory.getCache().merge(cache);
+ return userFactory;
+ }
+
+ void retrieveUser(IReviewGroup parent, AccountInfoCache cache, Id id, IProgressMonitor monitor)
+ throws CoreException {
+ if (id != null) {
+ final RemoteEmfConsumer<IReviewGroup, IUser, AccountInfo, Id, String> userConsumer = getUserFactory(cache).getConsumerForRemoteKey(
+ parent, id);
+ userConsumer.pull(false, monitor);
+ }
+ }
+
+ IUser createUser(IReviewGroup parent, AccountInfoCache cache, Account.Id id) {
+ if (id != null) {
+ final RemoteEmfConsumer<IReviewGroup, IUser, AccountInfo, Id, String> userConsumer = getUserFactory(cache).getConsumerForRemoteKey(
+ parent, id);
+ userConsumer.applyModel(false);
+ return userConsumer.getModelObject();
+ }
+ return null;
}
public GerritClient getClient() {
diff --git a/org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/remote/GerritReviewRemoteFactory.java b/org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/remote/GerritReviewRemoteFactory.java
new file mode 100644
index 00000000..a1b977d1
--- /dev/null
+++ b/org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/remote/GerritReviewRemoteFactory.java
@@ -0,0 +1,358 @@
+/*******************************************************************************
+ * Copyright (c) 2013 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:
+ * Miles Parker, Tasktop Technologies - initial API and implementation
+ * Steffen Pingel, Tasktop Technologies - original GerritUtil implementation
+ *******************************************************************************/
+
+package org.eclipse.mylyn.internal.gerrit.core.remote;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.mylyn.internal.gerrit.core.GerritConnector;
+import org.eclipse.mylyn.internal.gerrit.core.client.GerritChange;
+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.compat.SubmitRecord;
+import org.eclipse.mylyn.internal.gerrit.core.client.compat.SubmitRecord.Label;
+import org.eclipse.mylyn.reviews.core.model.IApprovalType;
+import org.eclipse.mylyn.reviews.core.model.IChange;
+import org.eclipse.mylyn.reviews.core.model.IComment;
+import org.eclipse.mylyn.reviews.core.model.IRepository;
+import org.eclipse.mylyn.reviews.core.model.IRequirementEntry;
+import org.eclipse.mylyn.reviews.core.model.IRequirementReviewState;
+import org.eclipse.mylyn.reviews.core.model.IReview;
+import org.eclipse.mylyn.reviews.core.model.IReviewGroup;
+import org.eclipse.mylyn.reviews.core.model.IReviewItemSet;
+import org.eclipse.mylyn.reviews.core.model.IReviewerEntry;
+import org.eclipse.mylyn.reviews.core.model.IReviewsFactory;
+import org.eclipse.mylyn.reviews.core.model.ISimpleReviewState;
+import org.eclipse.mylyn.reviews.core.model.ITopic;
+import org.eclipse.mylyn.reviews.core.model.IUser;
+import org.eclipse.mylyn.reviews.core.model.RequirementStatus;
+import org.eclipse.mylyn.reviews.core.spi.remote.emf.AbstractRemoteEmfFactory;
+import org.eclipse.mylyn.reviews.core.spi.remote.emf.RemoteEmfConsumer;
+import org.eclipse.mylyn.reviews.internal.core.model.ReviewsPackage;
+
+import com.google.gerrit.common.data.AccountInfo;
+import com.google.gerrit.common.data.ApprovalDetail;
+import com.google.gerrit.common.data.ApprovalType;
+import com.google.gerrit.common.data.ChangeInfo;
+import com.google.gerrit.common.data.PatchSetDetail;
+import com.google.gerrit.reviewdb.Change;
+import com.google.gerrit.reviewdb.ChangeMessage;
+import com.google.gerrit.reviewdb.PatchSetApproval;
+import com.google.gerrit.reviewdb.UserIdentity;
+
+/**
+ * Manages retrieval of Review information from Gerrit API. Also creates, adds, and updates contents of review sets for
+ * each patch set, but does not retrieve the patch set details or contents.
+ *
+ * @author Miles Parker
+ * @author Steffen Pingel
+ */
+public class GerritReviewRemoteFactory extends AbstractRemoteEmfFactory<IRepository, IReview, GerritChange, String, String> {
+
+ public GerritReviewRemoteFactory(GerritRemoteFactoryProvider gerritRemoteFactoryProvider) {
+ super(gerritRemoteFactoryProvider, ReviewsPackage.Literals.REVIEW_GROUP__REVIEWS,
+ ReviewsPackage.Literals.CHANGE__ID);
+ }
+
+ @Override
+ public boolean isPullNeeded(IRepository parent, IReview review, GerritChange remote) {
+ //We don't know if we need a pull until we actually retrieve the data
+ return true;
+ }
+
+ @Override
+ public GerritChange pull(IRepository parent, String remoteKey, IProgressMonitor monitor) throws CoreException {
+ try {
+ getGerritProvider().getClient().refreshConfig(new NullProgressMonitor());
+
+ GerritChange gerritChange = getGerritProvider().getClient().getChange(remoteKey, monitor);
+ final ChangeDetailX detail = gerritChange.getChangeDetail();
+
+ //We need to ensure we have all possible users for review in pull phase, as we can't do any async calls in apply phase
+ getGerritProvider().retrieveUser(parent, detail.getAccounts(), detail.getChange().getOwner(), monitor);
+ for (ChangeMessage message : detail.getMessages()) {
+ if (message.getAuthor() != null) {
+ getGerritProvider().retrieveUser(parent, detail.getAccounts(), message.getAuthor(), monitor);
+ }
+ }
+ for (PatchSetDetail patchSetDetail : gerritChange.getPatchSetDetails()) {
+ getGerritProvider().retrieveUser(parent, detail.getAccounts(),
+ patchSetDetail.getInfo().getAuthor().getAccount(), monitor);
+ getGerritProvider().retrieveUser(parent, detail.getAccounts(),
+ patchSetDetail.getInfo().getCommitter().getAccount(), monitor);
+ }
+ for (ApprovalDetail remoteApproval : detail.getApprovals()) {
+ getGerritProvider().retrieveUser(parent, detail.getAccounts(), remoteApproval.getAccount(), monitor);
+ }
+
+ if (detail.getSubmitRecords() != null) {
+ for (SubmitRecord record : detail.getSubmitRecords()) {
+ for (Label label : record.getLabels()) {
+ if (label.getAppliedBy() != null) {
+ getGerritProvider().retrieveUser(parent, detail.getAccounts(), label.getAppliedBy(),
+ monitor);
+ }
+ }
+ }
+ }
+
+ pull(parent, detail, detail.getDependsOn(), monitor);
+ pull(parent, detail, detail.getNeededBy(), monitor);
+ return gerritChange;
+ } catch (GerritException e) {
+ throw GerritConnector.toCoreException(null, e);
+ }
+ }
+
+ protected void pull(IReviewGroup parent, ChangeDetailX detail, List<ChangeInfo> remoteChanges,
+ IProgressMonitor monitor) throws CoreException {
+ for (ChangeInfo remoteChange : remoteChanges) {
+ AccountInfo remoteOwner = detail.getAccounts().get(remoteChange.getOwner());
+ getGerritProvider().retrieveUser(parent, detail.getAccounts(), remoteOwner.getId(), monitor);
+ }
+ }
+
+ @Override
+ public IReview createModel(IRepository parent, GerritChange gerritChange) {
+ final ChangeDetailX detail = gerritChange.getChangeDetail();
+ Change change = detail.getChange();
+
+ final IReview review = IReviewsFactory.INSTANCE.createReview();
+
+ //Immutable Data (?)
+ review.setKey(change.getKey().get());
+ review.setId(getLocalKeyForRemoteObject(gerritChange));
+ AccountInfo remoteOwner = detail.getAccounts().get(change.getOwner());
+ IUser owner = getGerritProvider().createUser(parent, detail.getAccounts(), remoteOwner.getId());
+ review.setOwner(owner);
+ review.setCreationDate(change.getCreatedOn());
+ parent.getReviews().add(review);
+
+ return review;
+ }
+
+ @Override
+ public boolean isUpdateModelNeeded(IRepository parent, IReview review, GerritChange gerritChange) {
+ Change change = gerritChange.getChangeDetail().getChange();
+ return review.getModificationDate() == null || review.getModificationDate().equals(change.getLastUpdatedOn());
+ }
+
+ @Override
+ public boolean updateModel(IRepository parent, IReview review, GerritChange gerritChange) {
+ ChangeDetailX detail = gerritChange.getChangeDetail();
+ Change change = detail.getChange();
+
+ //Mutable Data
+ review.setModificationDate(change.getLastUpdatedOn());
+ review.setSubject(change.getSubject());
+ review.setMessage(detail.getDescription());
+
+ updateComments(parent, review, detail);
+ updateEmptyPatchSets(parent, review, gerritChange);
+ updateApprovalsAndRequirements(parent, review, detail);
+ updateDependencies(parent, review, detail);
+ return true;
+ }
+
+ public void updateComments(IRepository parent, IReview review, ChangeDetailX detail) {
+ //Topics and Comments
+ int oldTopicCount = review.getTopics().size();
+ int topicIndex = 0;
+ for (ChangeMessage message : detail.getMessages()) {
+ if (topicIndex++ < oldTopicCount) {
+ continue;
+ }
+ ITopic topic = review.createTopicComment(null, message.getMessage());
+ topic.setDraft(false);
+ topic.setCreationDate(message.getWrittenOn());
+ for (IComment comment : topic.getComments()) {
+ comment.setDraft(false);
+ }
+ if (message.getAuthor() != null) {
+ IUser author = getGerritProvider().createUser(parent, detail.getAccounts(), message.getAuthor());
+ topic.setAuthor(author);
+ for (IComment comment : topic.getComments()) {
+ comment.setAuthor(topic.getAuthor());
+ }
+ }
+ }
+ }
+
+ public void updateEmptyPatchSets(IRepository parent, IReview review, GerritChange gerritChange) {
+ ChangeDetailX detail = gerritChange.getChangeDetail();
+ //Basic Patch Sets
+ int oldPatchCount = review.getSets().size();
+ int patchIndex = 0;
+ for (PatchSetDetail patchSetDetail : gerritChange.getPatchSetDetails()) {
+ if (patchIndex++ < oldPatchCount) {
+ continue;
+ }
+ PatchSetDetailRemoteFactory itemSetFactory = getGerritProvider().getReviewItemSetFactory();
+ RemoteEmfConsumer<IReview, IReviewItemSet, PatchSetDetail, PatchSetDetail, String> consumer = itemSetFactory.getConsumerForRemoteObject(
+ review, patchSetDetail);
+ consumer.applyModel(false);
+ IReviewItemSet itemSet = consumer.getModelObject();
+ IUser author = getGerritProvider().createUser(parent, detail.getAccounts(),
+ patchSetDetail.getInfo().getAuthor().getAccount());
+ itemSet.setAddedBy(author);
+ IUser committer = getGerritProvider().createUser(parent, detail.getAccounts(),
+ patchSetDetail.getInfo().getCommitter().getAccount());
+ itemSet.setCommittedBy(committer);
+ UserIdentity authorIdent = patchSetDetail.getInfo().getAuthor();
+ if (authorIdent != null) {
+ itemSet.setModificationDate(authorIdent.getDate());
+ }
+ review.getSets().add(itemSet);
+ }
+ }
+
+ public void updateApprovalsAndRequirements(IRepository parent, IReview review, ChangeDetailX detail) {
+ Map<String, IApprovalType> typeForKey = new HashMap<String, IApprovalType>();
+ Map<String, IApprovalType> typeForName = new HashMap<String, IApprovalType>();
+ for (IApprovalType type : parent.getApprovalTypes()) {
+ typeForKey.put(type.getKey(), type);
+ }
+ GerritConfiguration configuration = getGerritProvider().getClient().getConfiguration();
+ for (ApprovalType remoteType : configuration.getGerritConfig().getApprovalTypes().getApprovalTypes()) {
+ IApprovalType localApprovalType = typeForKey.get(remoteType.getCategory().getId().get());
+ if (localApprovalType == null) {
+ localApprovalType = IReviewsFactory.INSTANCE.createApprovalType();
+ localApprovalType.setKey(remoteType.getCategory().getId().get());
+ localApprovalType.setName(remoteType.getCategory().getName());
+ parent.getApprovalTypes().add(localApprovalType);
+ typeForKey.put(localApprovalType.getKey(), localApprovalType);
+ }
+ String approvalName = remoteType.getCategory().getName();
+ //Special case so we can match different label name for status records. (?!)
+ approvalName = approvalName.replace(" ", "-");
+ typeForName.put(approvalName, localApprovalType);
+ }
+
+ //Approvals
+ review.getReviewerApprovals().clear();
+ for (ApprovalDetail remoteApproval : detail.getApprovals()) {
+ IUser reviewer = getGerritProvider().createUser(parent, detail.getAccounts(), remoteApproval.getAccount());
+ if (reviewer == null) {
+ throw new RuntimeException("Internal Error, no reviewer found for: " + remoteApproval.getAccount());
+ }
+ IReviewerEntry reviewerEntry = review.getReviewerApprovals().get(reviewer);
+ if (reviewerEntry == null) {
+ reviewerEntry = IReviewsFactory.INSTANCE.createReviewerEntry();
+ review.getReviewerApprovals().put(reviewer, reviewerEntry);
+ }
+ for (Entry<com.google.gerrit.reviewdb.ApprovalCategory.Id, PatchSetApproval> remoteMap : remoteApproval.getApprovalMap()
+ .entrySet()) {
+ String remoteType = remoteMap.getValue().getCategoryId().get();
+ IApprovalType approvalType = typeForKey.get(remoteType);
+ if (approvalType == null) {
+ approvalType = IReviewsFactory.INSTANCE.createApprovalType();
+ approvalType.setKey(remoteType);
+ approvalType.setName(remoteType);
+ parent.getApprovalTypes().add(approvalType);
+ typeForKey.put(approvalType.getKey(), approvalType);
+ }
+ reviewerEntry.getApprovals().put(approvalType, (int) remoteMap.getValue().getValue());
+ }
+ }
+
+ //Requirements
+ review.getRequirements().clear();
+ if (detail.getSubmitRecords() != null) {
+ for (SubmitRecord record : detail.getSubmitRecords()) {
+ for (Label label : record.getLabels()) {
+ IApprovalType approvalType = typeForName.get(label.getLabel());
+ if (approvalType == null) {
+ throw new RuntimeException("Internal Error, no approval type found for: " + label.getLabel());
+ }
+ IRequirementEntry requirementEntry = IReviewsFactory.INSTANCE.createRequirementEntry();
+ if (label.getStatus().equals("OK")) {
+ requirementEntry.setStatus(RequirementStatus.SATISFIED);
+ } else if (label.getStatus().equals("NEED")) {
+ requirementEntry.setStatus(RequirementStatus.NOT_SATISFIED);
+ } else if (label.getStatus().equals("REJECT")) {
+ requirementEntry.setStatus(RequirementStatus.REJECTED);
+ } else if (label.getStatus().equals("MAY")) {
+ requirementEntry.setStatus(RequirementStatus.OPTIONAL);
+ } else if (label.getStatus().equals("IMPOSSIBLE")) {
+ requirementEntry.setStatus(RequirementStatus.ERROR);
+ }
+ if (label.getAppliedBy() != null) {
+ IUser approver = getGerritProvider().createUser(parent, detail.getAccounts(),
+ label.getAppliedBy());
+ requirementEntry.setBy(approver);
+ }
+ review.getRequirements().put(approvalType, requirementEntry);
+ }
+ IRequirementReviewState state = IReviewsFactory.INSTANCE.createRequirementReviewState();
+ if (record.getStatus().equals("OK")) {
+ state.setStatus(RequirementStatus.SATISFIED);
+ } else if (record.getStatus().equals("NOT_READY")) {
+ state.setStatus(RequirementStatus.NOT_SATISFIED);
+ } else if (record.getStatus().equals("CLOSED")) {
+ state.setStatus(RequirementStatus.CLOSED);
+ }
+ review.setState(state);
+ }
+ }
+ }
+
+ public void updateDependencies(IRepository parent, IReview review, ChangeDetailX detail) {
+ create(parent, review.getParents(), detail, detail.getDependsOn());
+ create(parent, review.getChildren(), detail, detail.getNeededBy());
+ }
+
+ protected void create(IReviewGroup group, List<IChange> localChanges, ChangeDetailX detail,
+ List<ChangeInfo> remoteChanges) {
+ localChanges.clear();
+ for (ChangeInfo remoteChange : remoteChanges) {
+ final IChange localChange = IReviewsFactory.INSTANCE.createChange();
+ localChange.setKey(remoteChange.getKey().get());
+ localChange.setId(remoteChange.getId().get() + "");
+ AccountInfo remoteOwner = detail.getAccounts().get(remoteChange.getOwner());
+ IUser owner = getGerritProvider().createUser(group, detail.getAccounts(), remoteOwner.getId());
+ localChange.setOwner(owner);
+ localChange.setModificationDate(remoteChange.getLastUpdatedOn());
+ localChange.setSubject(remoteChange.getSubject());
+ ISimpleReviewState state = IReviewsFactory.INSTANCE.createSimpleReviewState();
+ state.setName(remoteChange.getStatus().name());
+ localChange.setState(state);
+ ;
+ localChanges.add(localChange);
+ }
+ }
+
+ @Override
+ public String getRemoteKey(GerritChange remoteObject) {
+ return remoteObject.getChangeDetail().getChange().getId().get() + "";
+ }
+
+ @Override
+ public String getLocalKeyForRemoteObject(GerritChange remoteObject) {
+ return getRemoteKey(remoteObject);
+ }
+
+ @Override
+ public String getLocalKeyForRemoteKey(String remoteKey) {
+ return remoteKey;
+ }
+
+ public GerritRemoteFactoryProvider getGerritProvider() {
+ return (GerritRemoteFactoryProvider) getFactoryProvider();
+ }
+}
diff --git a/org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/remote/UserRemoteFactory.java b/org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/remote/GerritUserRemoteFactory.java
index ca9a6738..f401d71f 100644
--- a/org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/remote/UserRemoteFactory.java
+++ b/org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/remote/GerritUserRemoteFactory.java
@@ -11,10 +11,12 @@
package org.eclipse.mylyn.internal.gerrit.core.remote;
+import java.util.ArrayList;
+
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
-import org.eclipse.emf.ecore.EObject;
import org.eclipse.mylyn.internal.gerrit.core.GerritUtil;
+import org.eclipse.mylyn.reviews.core.model.IReviewGroup;
import org.eclipse.mylyn.reviews.core.model.IReviewsFactory;
import org.eclipse.mylyn.reviews.core.model.IUser;
import org.eclipse.mylyn.reviews.core.spi.remote.emf.AbstractRemoteEmfFactory;
@@ -30,38 +32,56 @@ import com.google.gerrit.reviewdb.Account.Id;
*
* @author Miles Parker
*/
-public class UserRemoteFactory extends AbstractRemoteEmfFactory<EObject, IUser, Account.Id, Account.Id, String> {
+public class GerritUserRemoteFactory extends AbstractRemoteEmfFactory<IReviewGroup, IUser, AccountInfo, Account.Id, String> {
- private final AccountInfoCache cache;
+ private final AccountInfoCache cache = new AccountInfoCache(new ArrayList<AccountInfo>());
- public UserRemoteFactory(GerritRemoteFactoryProvider gerritRemoteFactoryProvider, AccountInfoCache cache) {
- super(gerritRemoteFactoryProvider.getService(), null, ReviewsPackage.Literals.USER__ID);
- this.cache = cache;
+ public GerritUserRemoteFactory(GerritRemoteFactoryProvider gerritRemoteFactoryProvider) {
+ super(gerritRemoteFactoryProvider, ReviewsPackage.Literals.REVIEW_GROUP__USERS,
+ ReviewsPackage.Literals.USER__ID);
}
@Override
- public Id retrieve(Id remoteKey, IProgressMonitor monitor) throws CoreException {
- return remoteKey;
+ public AccountInfo pull(IReviewGroup parent, Id id, IProgressMonitor monitor) throws CoreException {
+ return cache.get(id);
}
@Override
- public IUser create(EObject item, Id id) {
- AccountInfo info = cache.get(id);
+ public IUser createModel(IReviewGroup group, AccountInfo info) {
IUser user = IReviewsFactory.INSTANCE.createUser();
user.setDisplayName(GerritUtil.getUserLabel(info));
- if (id != null) {
- user.setId(Integer.toString(id.get()));
- }
+ user.setId(info.getId() + "");
+ user.setEmail(info.getPreferredEmail());
+ group.getUsers().add(user);
return user;
}
@Override
+ public boolean isPullNeeded(IReviewGroup parent, IUser user, AccountInfo remote) {
+ return true;
+ }
+
+ @Override
public boolean isAsynchronous() {
return false;
}
@Override
- public boolean update(EObject item, IUser object, Id id) {
+ public boolean updateModel(IReviewGroup item, IUser object, AccountInfo info) {
return false;
}
+
+ @Override
+ public Id getRemoteKey(AccountInfo info) {
+ return info.getId();
+ }
+
+ @Override
+ public String getLocalKeyForRemoteKey(Id remoteKey) {
+ return Integer.toString(remoteKey.get());
+ }
+
+ public AccountInfoCache getCache() {
+ return cache;
+ }
} \ No newline at end of file
diff --git a/org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/remote/PatchSetContentCompareRemoteFactory.java b/org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/remote/PatchSetContentCompareRemoteFactory.java
new file mode 100644
index 00000000..b6dac1c9
--- /dev/null
+++ b/org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/remote/PatchSetContentCompareRemoteFactory.java
@@ -0,0 +1,47 @@
+/*******************************************************************************
+ * Copyright (c) 2013 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:
+ * Miles Parker, Tasktop Technologies - initial API and implementation
+ * Steffen Pingel, Tasktop Technologies - original GerritUtil implementation
+ *******************************************************************************/
+
+package org.eclipse.mylyn.internal.gerrit.core.remote;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.mylyn.internal.gerrit.core.client.PatchSetContent;
+import org.eclipse.mylyn.reviews.core.model.IReviewItemSet;
+
+/**
+ * Manages retrieval of patch set contents, including file revisions and associated comments, from Gerrit API,
+ * supporting arbitrary patch set contents, including comparisons.
+ *
+ * @author Miles Parker
+ */
+public class PatchSetContentCompareRemoteFactory extends PatchSetContentRemoteFactory<PatchSetContent> {
+
+ public PatchSetContentCompareRemoteFactory(GerritRemoteFactoryProvider gerritRemoteFactoryProvider) {
+ super(gerritRemoteFactoryProvider);
+ }
+
+ @Override
+ public PatchSetContent pull(IReviewItemSet parentObject, PatchSetContent content, IProgressMonitor monitor)
+ throws CoreException {
+ return super.pull(parentObject, content, monitor);
+ }
+
+ @Override
+ public PatchSetContent getRemoteKey(PatchSetContent remoteObject) {
+ return remoteObject;
+ }
+
+ @Override
+ public String getLocalKeyForRemoteKey(PatchSetContent content) {
+ return content.getId();
+ }
+}
diff --git a/org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/remote/PatchSetContentIdRemoteFactory.java b/org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/remote/PatchSetContentIdRemoteFactory.java
new file mode 100644
index 00000000..b32403cc
--- /dev/null
+++ b/org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/remote/PatchSetContentIdRemoteFactory.java
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * Copyright (c) 2013 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:
+ * Miles Parker, Tasktop Technologies - initial API and implementation
+ * Steffen Pingel, Tasktop Technologies - original GerritUtil implementation
+ *******************************************************************************/
+
+package org.eclipse.mylyn.internal.gerrit.core.remote;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.mylyn.internal.gerrit.core.client.PatchSetContent;
+import org.eclipse.mylyn.reviews.core.model.IReview;
+import org.eclipse.mylyn.reviews.core.model.IReviewItemSet;
+import org.eclipse.mylyn.reviews.core.spi.remote.emf.RemoteEmfConsumer;
+
+import com.google.gerrit.common.data.PatchSetDetail;
+import com.google.gerrit.reviewdb.PatchSet;
+
+/**
+ * Manages retrieval of patch set contents, including file revisions and associated comments, from Gerrit API,
+ * supporting key based retrieval of single patch sets.
+ *
+ * @author Miles Parker
+ */
+public class PatchSetContentIdRemoteFactory extends PatchSetContentRemoteFactory<String> {
+
+ public PatchSetContentIdRemoteFactory(GerritRemoteFactoryProvider gerritRemoteFactoryProvider) {
+ super(gerritRemoteFactoryProvider);
+ }
+
+ @Override
+ public PatchSetContent pull(IReviewItemSet parentObject, String key, IProgressMonitor monitor) throws CoreException {
+ RemoteEmfConsumer<IReview, IReviewItemSet, PatchSetDetail, PatchSetDetail, String> itemSetConsumer = getGerritProvider().getReviewItemSetFactory()
+ .getConsumerForLocalKey(parentObject.getReview(), key);
+ PatchSetDetail detail = itemSetConsumer.getRemoteObject();
+ PatchSetContent content = new PatchSetContent((PatchSet) null, detail);
+ return pull(parentObject, content, monitor);
+ }
+
+ @Override
+ public String getRemoteKey(PatchSetContent remoteObject) {
+ return remoteObject.getId();
+ }
+
+ @Override
+ public String getLocalKeyForRemoteKey(String remoteKey) {
+ return remoteKey;
+ }
+}
diff --git a/org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/remote/PatchSetContentRemoteFactory.java b/org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/remote/PatchSetContentRemoteFactory.java
index d62ff924..1fdc91b6 100644
--- a/org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/remote/PatchSetContentRemoteFactory.java
+++ b/org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/remote/PatchSetContentRemoteFactory.java
@@ -12,6 +12,7 @@
package org.eclipse.mylyn.internal.gerrit.core.remote;
+import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.runtime.CoreException;
@@ -39,7 +40,6 @@ import org.eclipse.osgi.util.NLS;
import com.google.gerrit.common.data.AccountInfoCache;
import com.google.gerrit.common.data.CommentDetail;
import com.google.gerrit.common.data.PatchScript;
-import com.google.gerrit.common.data.PatchSetDetail;
import com.google.gerrit.prettify.common.SparseFileContent;
import com.google.gerrit.reviewdb.Patch;
import com.google.gerrit.reviewdb.PatchLineComment;
@@ -50,42 +50,57 @@ import com.google.gerrit.reviewdb.PatchLineComment;
* @author Miles Parker
* @author Steffen Pingel
*/
-public class PatchSetContentRemoteFactory extends
- AbstractRemoteEmfFactory<IReviewItemSet, List<IFileItem>, PatchSetContent, PatchSetContent, String> {
+public abstract class PatchSetContentRemoteFactory<RemoteKeyType> extends
+ AbstractRemoteEmfFactory<IReviewItemSet, List<IFileItem>, PatchSetContent, RemoteKeyType, String> {
private final ReviewItemCache cache;
- private final GerritRemoteFactoryProvider gerritRemoteFactoryProvider;
+ private final GerritRemoteFactoryProvider gerritFactoryProvider;
public PatchSetContentRemoteFactory(GerritRemoteFactoryProvider gerritRemoteFactoryProvider) {
- super(gerritRemoteFactoryProvider.getService(), ReviewsPackage.Literals.REVIEW_ITEM_SET__ITEMS,
+ super(gerritRemoteFactoryProvider, ReviewsPackage.Literals.REVIEW_ITEM_SET__ITEMS,
ReviewsPackage.Literals.REVIEW_ITEM__ID);
- this.gerritRemoteFactoryProvider = gerritRemoteFactoryProvider;
+ this.gerritFactoryProvider = gerritRemoteFactoryProvider;
cache = new ReviewItemCache();
}
- @Override
- public PatchSetContent retrieve(PatchSetContent content, IProgressMonitor monitor) throws CoreException {
+ public PatchSetContent pull(IReviewItemSet parentObject, PatchSetContent content, IProgressMonitor monitor)
+ throws CoreException {
try {
- gerritRemoteFactoryProvider.getClient().loadPatchSetContent(content, monitor);
+ gerritFactoryProvider.getClient().loadPatchSetContent(content, monitor);
+
} catch (GerritException e) {
throw new CoreException(new Status(IStatus.ERROR, GerritCorePlugin.PLUGIN_ID,
"Problem while collecting patch set content", e));
}
+ for (Patch patch : content.getTargetDetail().getPatches()) {
+ PatchScript patchScript = content.getPatchScript(patch.getKey());
+ CommentDetail commentDetail = patchScript.getCommentDetail();
+ List<PatchLineComment> comments = new ArrayList<PatchLineComment>();
+ comments.addAll(commentDetail.getCommentsA());
+ comments.addAll(commentDetail.getCommentsB());
+ for (PatchLineComment comment : comments) {
+ gerritFactoryProvider.retrieveUser(getGerritProvider().getRoot(), patchScript.getCommentDetail()
+ .getAccounts(), comment.getAuthor(), monitor);
+ }
+ }
return content;
}
- @Override
- public PatchSetContent getRemoteKey(IReviewItemSet parentObject, List<IFileItem> items) {
- PatchSetDetail patchDetail = gerritRemoteFactoryProvider.getReviewItemSetFactory()
- .getRemoteObject(parentObject);
- return new PatchSetContent(null, patchDetail.getPatchSet());
- }
-
- void addComments(IFileVersion version, List<PatchLineComment> comments, AccountInfoCache accountInfoCache) {
- if (comments == null || comments.isEmpty()) {
- return;
+ boolean addComments(IReviewItemSet set, IFileVersion version, List<PatchLineComment> comments,
+ AccountInfoCache accountInfoCache) {
+ version.getTopics().clear();
+ if (version == null || comments == null || comments.isEmpty()) {
+ return false;
}
+ boolean changed = comments.size() != version.getTopics().size();
+ int oldDraftCount = version.getTopics().size();
+ for (ITopic topic : version.getTopics()) {
+ if (topic.isDraft()) {
+ oldDraftCount++;
+ }
+ }
+ int draftCount = 0;
for (PatchLineComment comment : comments) {
ILineRange line = IReviewsFactory.INSTANCE.createLineRange();
line.setStart(comment.getLine());
@@ -94,11 +109,15 @@ public class PatchSetContentRemoteFactory extends
location.getRanges().add(line);
IComment topicComment = IReviewsFactory.INSTANCE.createComment();
- IUser author = gerritRemoteFactoryProvider.getUserFactory(accountInfoCache).get(topicComment,
+ IUser author = getGerritProvider().createUser(getGerritProvider().getRoot(), accountInfoCache,
comment.getAuthor());
+
topicComment.setCreationDate(comment.getWrittenOn());
topicComment.setDescription(comment.getMessage());
topicComment.setDraft(PatchLineComment.Status.DRAFT == comment.getStatus());
+ if (topicComment.isDraft()) {
+ draftCount++;
+ }
topicComment.setAuthor(author);
ITopic topic = IReviewsFactory.INSTANCE.createTopic();
@@ -114,11 +133,18 @@ public class PatchSetContentRemoteFactory extends
version.getTopics().add(topic);
}
+ changed |= draftCount != oldDraftCount;
+ return changed;
+ }
+
+ @Override
+ public boolean isPullNeeded(IReviewItemSet parent, List<IFileItem> items, PatchSetContent remote) {
+ return items == null || items.size() == 0 || remote == null;
}
@Override
- public List<IFileItem> create(IReviewItemSet set, PatchSetContent content) {
- List<IFileItem> items = IReviewsFactory.INSTANCE.createReviewItemSet().getItems();
+ public List<IFileItem> createModel(IReviewItemSet set, PatchSetContent content) {
+ List<IFileItem> items = set.getItems();
for (Patch patch : content.getTargetDetail().getPatches()) {
String targetId = patch.getKey().toString();
String sourceFileName = (patch.getSourceFileName() != null)
@@ -128,14 +154,15 @@ public class PatchSetContentRemoteFactory extends
? new Patch.Key(content.getBase().getId(), sourceFileName).toString()
: "base-" + targetId;
String id = baseId + ":" + targetId; //$NON-NLS-1$
- IFileItem item = (IFileItem) cache.getItem(id);
+ IFileItem item = (IFileItem) getCache().getItem(id);
if (item == null) {
item = IReviewsFactory.INSTANCE.createFileItem();
item.setId(id);
item.setName(patch.getFileName());
item.setAddedBy(set.getAddedBy());
item.setCommittedBy(set.getCommittedBy());
- cache.put(item);
+ item.setReference(patch.getKey().getParentKey() + "," + patch.getFileName());
+ getCache().put(item);
}
items.add(item);
@@ -143,39 +170,38 @@ public class PatchSetContentRemoteFactory extends
if (patchScript != null) {
CommentDetail commentDetail = patchScript.getCommentDetail();
- IFileVersion versionA = (IFileVersion) cache.getItem(baseId);
- if (versionA == null) {
- versionA = IReviewsFactory.INSTANCE.createFileVersion();
- versionA.setId(baseId);
- versionA.setContent(patchScript.getA().asString());
- versionA.setPath(patchScript.getA().getPath());
- versionA.setDescription((content.getBase() != null) ? NLS.bind("Patch Set {0}", content.getBase()
- .getPatchSetId()) : "Base");
- versionA.setFile(item);
- versionA.setName(item.getName());
- addComments(versionA, commentDetail.getCommentsA(), commentDetail.getAccounts());
- cache.put(versionA);
+ IFileVersion baseVersion = (IFileVersion) getCache().getItem(baseId);
+ if (baseVersion == null) {
+ baseVersion = IReviewsFactory.INSTANCE.createFileVersion();
+ baseVersion.setId(baseId);
+ baseVersion.setContent(patchScript.getA().asString());
+ baseVersion.setPath(patchScript.getA().getPath());
+ baseVersion.setDescription((content.getBase() != null) ? NLS.bind("Patch Set {0}",
+ content.getBase().getPatchSetId()) : "Base");
+ baseVersion.setFile(item);
+ baseVersion.setName(item.getName());
+ getCache().put(baseVersion);
}
- item.setBase(versionA);
+ addComments(set, baseVersion, commentDetail.getCommentsA(), commentDetail.getAccounts());
+ item.setBase(baseVersion);
- IFileVersion versionB = (IFileVersion) cache.getItem(targetId);
- if (versionB == null) {
- versionB = IReviewsFactory.INSTANCE.createFileVersion();
- versionB.setId(targetId);
+ IFileVersion targetVersion = (IFileVersion) getCache().getItem(targetId);
+ if (targetVersion == null) {
+ targetVersion = IReviewsFactory.INSTANCE.createFileVersion();
+ targetVersion.setId(targetId);
SparseFileContent target = patchScript.getB().apply(patchScript.getA(), patchScript.getEdits());
- versionB.setContent(target.asString());
- versionB.setPath(patchScript.getB().getPath());
- versionB.setDescription(NLS.bind("Patch Set {0}", content.getTargetDetail()
+ targetVersion.setContent(target.asString());
+ targetVersion.setPath(patchScript.getB().getPath());
+ targetVersion.setDescription(NLS.bind("Patch Set {0}", content.getTargetDetail()
.getPatchSet()
.getPatchSetId()));
- versionB.setFile(item);
- versionB.setAddedBy(item.getAddedBy());
- versionB.setCommittedBy(item.getCommittedBy());
- versionB.setName(item.getName());
- addComments(versionB, commentDetail.getCommentsB(), commentDetail.getAccounts());
- cache.put(versionB);
+ targetVersion.setFile(item);
+ targetVersion.setAddedBy(item.getAddedBy());
+ targetVersion.setCommittedBy(item.getCommittedBy());
+ targetVersion.setName(item.getName());
+ getCache().put(targetVersion);
}
- item.setTarget(versionB);
+ item.setTarget(targetVersion);
}
}
return items;
@@ -185,7 +211,27 @@ public class PatchSetContentRemoteFactory extends
* Patch sets results never change.
*/
@Override
- public boolean update(IReviewItemSet set, List<IFileItem> items, PatchSetContent content) {
- return false;
+ public boolean updateModel(IReviewItemSet set, List<IFileItem> items, PatchSetContent content) {
+ boolean changed = false;
+ for (IFileItem item : items) {
+ IFileItem fileItem = item;
+ PatchScript patchScript = content.getPatchScript(Patch.Key.parse(item.getReference()));
+ if (patchScript != null) {
+ CommentDetail commentDetail = patchScript.getCommentDetail();
+ changed |= addComments(set, fileItem.getBase(), commentDetail.getCommentsA(),
+ commentDetail.getAccounts());
+ changed |= addComments(set, fileItem.getTarget(), commentDetail.getCommentsB(),
+ commentDetail.getAccounts());
+ }
+ }
+ return changed;
+ }
+
+ public GerritRemoteFactoryProvider getGerritProvider() {
+ return gerritFactoryProvider;
+ }
+
+ public ReviewItemCache getCache() {
+ return cache;
}
}
diff --git a/org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/remote/ReviewItemSetRemoteFactory.java b/org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/remote/PatchSetDetailRemoteFactory.java
index a8b3ba7a..22200939 100644
--- a/org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/remote/ReviewItemSetRemoteFactory.java
+++ b/org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/remote/PatchSetDetailRemoteFactory.java
@@ -25,22 +25,22 @@ import com.google.gerrit.common.data.PatchSetDetail;
import com.google.gerrit.reviewdb.PatchSet;
/**
- * Converts patch set details to review sets. Does not require a remote invocation, as the neccesary data is collected
- * as part of {@link ReviewRemoteFactory} API call.
+ * Converts patch set details to review sets. Does not retrive actual patch set content. Does not require a remote
+ * invocation, as the neccesary data is collected as part of {@link GerritReviewRemoteFactory} API call.
*
* @author Miles Parker
* @author Steffen Pingel
*/
-public class ReviewItemSetRemoteFactory extends
+public class PatchSetDetailRemoteFactory extends
AbstractRemoteEmfFactory<IReview, IReviewItemSet, PatchSetDetail, PatchSetDetail, String> {
- public ReviewItemSetRemoteFactory(GerritRemoteFactoryProvider gerritRemoteFactoryProvider) {
- super(gerritRemoteFactoryProvider.getService(), ReviewsPackage.Literals.REVIEW_ITEM_SET__ITEMS,
+ public PatchSetDetailRemoteFactory(GerritRemoteFactoryProvider gerritRemoteFactoryProvider) {
+ super(gerritRemoteFactoryProvider, ReviewsPackage.Literals.REVIEW__SETS,
ReviewsPackage.Literals.REVIEW_ITEM__ID);
}
@Override
- public PatchSetDetail retrieve(PatchSetDetail remoteKey, IProgressMonitor monitor) throws CoreException {
+ public PatchSetDetail pull(IReview parent, PatchSetDetail remoteKey, IProgressMonitor monitor) throws CoreException {
return remoteKey;
}
@@ -50,7 +50,12 @@ public class ReviewItemSetRemoteFactory extends
}
@Override
- public IReviewItemSet create(IReview review, PatchSetDetail patchSetDetail) {
+ public boolean isPullNeeded(IReview parent, IReviewItemSet object, PatchSetDetail remote) {
+ return object != null;
+ }
+
+ @Override
+ public IReviewItemSet createModel(IReview review, PatchSetDetail patchSetDetail) {
PatchSet patchSet = patchSetDetail.getPatchSet();
IReviewItemSet itemSet = IReviewsFactory.INSTANCE.createReviewItemSet();
itemSet.setName(NLS.bind("Patch Set {0}", patchSet.getPatchSetId()));
@@ -58,6 +63,22 @@ public class ReviewItemSetRemoteFactory extends
itemSet.setId(patchSet.getPatchSetId() + "");
itemSet.setReference(patchSet.getRefName());
itemSet.setRevision(patchSet.getRevision().get());
+ review.getSets().add(itemSet);
return itemSet;
}
-} \ No newline at end of file
+
+ @Override
+ public PatchSetDetail getRemoteKey(PatchSetDetail remoteObject) {
+ return remoteObject;
+ }
+
+ @Override
+ public String getLocalKeyForRemoteObject(PatchSetDetail remoteObject) {
+ return remoteObject.getPatchSet().getPatchSetId() + "";
+ }
+
+ @Override
+ public String getLocalKeyForRemoteKey(PatchSetDetail remoteKey) {
+ return getLocalKeyForRemoteObject(remoteKey);
+ }
+}
diff --git a/org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/remote/ReviewRemoteFactory.java b/org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/remote/ReviewRemoteFactory.java
deleted file mode 100644
index 6d8b5d73..00000000
--- a/org.eclipse.mylyn.gerrit.core/src/org/eclipse/mylyn/internal/gerrit/core/remote/ReviewRemoteFactory.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2013 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:
- * Miles Parker, Tasktop Technologies - initial API and implementation
- * Steffen Pingel, Tasktop Technologies - original GerritUtil implementation
- *******************************************************************************/
-
-package org.eclipse.mylyn.internal.gerrit.core.remote;
-
-import org.eclipse.core.runtime.CoreException;
-import org.eclipse.core.runtime.IProgressMonitor;
-import org.eclipse.mylyn.internal.gerrit.core.GerritConnector;
-import org.eclipse.mylyn.internal.gerrit.core.client.GerritChange;
-import org.eclipse.mylyn.internal.gerrit.core.client.GerritException;
-import org.eclipse.mylyn.internal.gerrit.core.client.compat.ChangeDetailX;
-import org.eclipse.mylyn.reviews.core.model.IComment;
-import org.eclipse.mylyn.reviews.core.model.IReview;
-import org.eclipse.mylyn.reviews.core.model.IReviewGroup;
-import org.eclipse.mylyn.reviews.core.model.IReviewItemSet;
-import org.eclipse.mylyn.reviews.core.model.IReviewsFactory;
-import org.eclipse.mylyn.reviews.core.model.ITopic;
-import org.eclipse.mylyn.reviews.core.model.IUser;
-import org.eclipse.mylyn.reviews.core.spi.remote.emf.AbstractRemoteEmfFactory;
-import org.eclipse.mylyn.reviews.internal.core.model.ReviewsPackage;
-
-import com.google.gerrit.common.data.AccountInfo;
-import com.google.gerrit.common.data.PatchSetDetail;
-import com.google.gerrit.reviewdb.Change;
-import com.google.gerrit.reviewdb.ChangeMessage;
-import com.google.gerrit.reviewdb.UserIdentity;
-
-/**
- * Manages retrieval of Review information from Gerrit API. Also creates, adds, and updates contents of review sets for
- * each patch set, but does not retrieve the patch set details or contents.
- *
- * @author Miles Parker
- * @author Steffen Pingel
- */
-public class ReviewRemoteFactory extends AbstractRemoteEmfFactory<IReviewGroup, IReview, GerritChange, String, String> {
-
- private final GerritRemoteFactoryProvider gerritFactoryProvider;
-
- public ReviewRemoteFactory(GerritRemoteFactoryProvider gerritRemoteFactoryProvider) {
- super(gerritRemoteFactoryProvider.getService(), ReviewsPackage.Literals.REVIEW_GROUP__REVIEWS,
- ReviewsPackage.Literals.CHANGE__ID);
- this.gerritFactoryProvider = gerritRemoteFactoryProvider;
- }
-
- @Override
- public GerritChange retrieve(String remoteKey, IProgressMonitor monitor) throws CoreException {
- try {
- return gerritFactoryProvider.getClient().getChange(remoteKey, monitor);
- } catch (GerritException e) {
- throw GerritConnector.toCoreException(null, e);
- }
- }
-
- @Override
- public IReview create(IReviewGroup parent, GerritChange gerritChange) {
- final ChangeDetailX detail = gerritChange.getChangeDetail();
- Change change = detail.getChange();
-
- IReview review = IReviewsFactory.INSTANCE.createReview();
- review.setId(detail.getChange().getId().get() + "");
- AccountInfo owner = detail.getAccounts().get(change.getOwner());
- IUser reviewAuthor = gerritFactoryProvider.getUserFactory(detail.getAccounts()).get(review, owner.getId());
- review.setOwner(reviewAuthor);
- review.setCreationDate(change.getCreatedOn());
- return review;
- }
-
- @Override
- public boolean update(IReviewGroup parent, IReview review, GerritChange gerritChange) {
- ChangeDetailX detail = getRemoteObject(review).getChangeDetail();
- Change change = detail.getChange();
-
- review.setModificationDate(change.getLastUpdatedOn());
-
- int oldTopicCount = review.getTopics().size();
- int topicIndex = 0;
- for (ChangeMessage message : detail.getMessages()) {
- if (topicIndex++ < oldTopicCount) {
- continue;
- }
- ITopic topic = review.createTopicComment(null, message.getMessage());
- topic.setDraft(false);
- topic.setCreationDate(message.getWrittenOn());
- for (IComment comment : topic.getComments()) {
- comment.setDraft(false);
- }
- if (message.getAuthor() != null) {
- topic.setAuthor(gerritFactoryProvider.getUserFactory(detail.getAccounts()).get(topic,
- message.getAuthor()));
- //TODO Should be handled by edit framework
- for (IComment comment : topic.getComments()) {
- comment.setAuthor(topic.getAuthor());
- }
- }
- }
-
- int oldPatchCount = review.getSets().size();
- int patchIndex = 0;
- for (PatchSetDetail patchSetDetail : gerritChange.getPatchSetDetails()) {
- if (patchIndex++ < oldPatchCount) {
- continue;
- }
- ReviewItemSetRemoteFactory itemSetFactory = gerritFactoryProvider.getReviewItemSetFactory();
- IReviewItemSet itemSet = itemSetFactory.get(review, patchSetDetail);
- UserIdentity author = patchSetDetail.getInfo().getAuthor();
- itemSet.setAddedBy(gerritFactoryProvider.getUserFactory(detail.getAccounts()).get(itemSet,
- author.getAccount()));
- UserIdentity committer = patchSetDetail.getInfo().getCommitter();
- itemSet.setCommittedBy(gerritFactoryProvider.getUserFactory(detail.getAccounts()).get(itemSet,
- committer.getAccount()));
- itemSet.setModificationDate(author.getDate());
- review.getSets().add(itemSet);
- }
-
- return review.getTopics().size() > oldTopicCount || review.getSets().size() > oldPatchCount;
- }
-} \ No newline at end of file
diff --git a/org.eclipse.mylyn.gerrit.tests/META-INF/MANIFEST.MF b/org.eclipse.mylyn.gerrit.tests/META-INF/MANIFEST.MF
index 32d99e15..d4e7577d 100644
--- a/org.eclipse.mylyn.gerrit.tests/META-INF/MANIFEST.MF
+++ b/org.eclipse.mylyn.gerrit.tests/META-INF/MANIFEST.MF
@@ -27,7 +27,9 @@ Require-Bundle: org.junit;bundle-version="4.5.0",
org.eclipse.mylyn.tests.util,
org.eclipse.mylyn.tasks.core;bundle-version="3.8.0",
org.eclipse.mylyn.tasks.ui;bundle-version="3.8.0",
- org.eclipse.mylyn.reviews.core;bundle-version="2.0.0"
+ org.eclipse.mylyn.reviews.core;bundle-version="2.0.0",
+ org.eclipse.mylyn.reviews.edit;bundle-version="2.0.0",
+ org.eclipse.emf.edit
Import-Package: com.google.gerrit.common.auth.userpass;version="2.1.5",
com.google.gerrit.common.data;version="2.1.5",
com.google.gerrit.prettify.common;version="2.1.5",
diff --git a/org.eclipse.mylyn.gerrit.tests/src/org/eclipse/mylyn/internal/gerrit/core/remote/GerritRemoteFactoryTest.java b/org.eclipse.mylyn.gerrit.tests/src/org/eclipse/mylyn/internal/gerrit/core/remote/GerritRemoteFactoryTest.java
index bea174b3..ca92707b 100644
--- a/org.eclipse.mylyn.gerrit.tests/src/org/eclipse/mylyn/internal/gerrit/core/remote/GerritRemoteFactoryTest.java
+++ b/org.eclipse.mylyn.gerrit.tests/src/org/eclipse/mylyn/internal/gerrit/core/remote/GerritRemoteFactoryTest.java
@@ -11,37 +11,48 @@
package org.eclipse.mylyn.internal.gerrit.core.remote;
+import static org.hamcrest.Matchers.endsWith;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
+import static org.hamcrest.Matchers.sameInstance;
+import static org.hamcrest.Matchers.startsWith;
import static org.junit.Assert.assertThat;
import java.util.Calendar;
import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
import junit.framework.TestCase;
import org.eclipse.core.runtime.CoreException;
-import org.eclipse.core.runtime.IStatus;
import org.eclipse.mylyn.gerrit.tests.support.GerritFixture;
import org.eclipse.mylyn.gerrit.tests.support.GerritHarness;
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.PatchSetContent;
+import org.eclipse.mylyn.reviews.core.model.IApprovalType;
+import org.eclipse.mylyn.reviews.core.model.IChange;
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.IRepository;
+import org.eclipse.mylyn.reviews.core.model.IRequirementEntry;
+import org.eclipse.mylyn.reviews.core.model.IRequirementReviewState;
import org.eclipse.mylyn.reviews.core.model.IReview;
-import org.eclipse.mylyn.reviews.core.model.IReviewGroup;
import org.eclipse.mylyn.reviews.core.model.IReviewItem;
import org.eclipse.mylyn.reviews.core.model.IReviewItemSet;
+import org.eclipse.mylyn.reviews.core.model.IReviewerEntry;
import org.eclipse.mylyn.reviews.core.model.IReviewsFactory;
import org.eclipse.mylyn.reviews.core.model.ITopic;
+import org.eclipse.mylyn.reviews.core.model.IUser;
+import org.eclipse.mylyn.reviews.core.model.RequirementStatus;
import org.eclipse.mylyn.reviews.core.spi.remote.JobRemoteService;
import org.eclipse.mylyn.reviews.core.spi.remote.emf.RemoteEmfConsumer;
-import org.eclipse.mylyn.tasks.core.TaskRepository;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -56,18 +67,17 @@ public class GerritRemoteFactoryTest extends TestCase {
private GerritClient client;
- private GerritRemoteFactoryProvider service;
+ private GerritRemoteFactoryProvider factoryProvider;
private GerritHarness harness;
@Override
@Before
public void setUp() throws Exception {
- //TODO -- we shouldn't be using eclipse as test host, but I can't find a test gerrit instance that is working
harness = GerritFixture.current().harness();
client = harness.client();
- TaskRepository repository = GerritFixture.current().singleRepository();
- service = new GerritRemoteFactoryProvider(new JobRemoteService(), client);
+ factoryProvider = new GerritRemoteFactoryProvider(client);
+ factoryProvider.setService(new JobRemoteService());
}
@Override
@@ -76,80 +86,25 @@ public class GerritRemoteFactoryTest extends TestCase {
harness.dispose();
}
- private final class TestListener<T> implements RemoteEmfConsumer.IObserver<T> {
-
- T createdObject;
-
- int updated;
-
- IStatus failure;
-
- @Override
- public void created(T object) {
- createdObject = object;
- }
-
- @Override
- public void responded(boolean modified) {
- updated++;
- }
-
- @Override
- public void failed(org.eclipse.core.runtime.IStatus status) {
- failure = status;
- }
-
- protected void waitForUpdate() {
- long delay;
- delay = 0;
- while (delay < 10000) {
- if (updated < 1) {
- try {
- Thread.sleep(10);
- delay += 10;
- } catch (InterruptedException e) {
- }
- } else {
- break;
- }
- }
- assertThat(updated, greaterThan(0));
- }
-
- protected void waitForFailure() {
- long delay = 0;
- while (delay < 10000) {
- if (failure == null) {
- try {
- Thread.sleep(10);
- delay += 10;
- } catch (InterruptedException e) {
- }
- } else {
- break;
- }
- }
- }
-
- void clear() {
- createdObject = null;
- updated = 0;
- failure = null;
- }
- }
-
@Test
public void testReviewFactory() throws CoreException {
- IReviewGroup group = IReviewsFactory.INSTANCE.createReviewGroup();
- TestListener<IReview> reviewListener = new TestListener<IReview>();
- RemoteEmfConsumer<IReviewGroup, IReview, GerritChange, String, String> consumer = service.getReviewFactory()
- .consume("Test Get Review", group, "2", "2", reviewListener);
- consumer.request();
- reviewListener.waitForUpdate();
+ IRepository group = IReviewsFactory.INSTANCE.createRepository();
+ TestRemoteObserver<IRepository, IReview> reviewListener = new TestRemoteObserver<IRepository, IReview>(
+ factoryProvider.getReviewFactory());
+ RemoteEmfConsumer<IRepository, IReview, GerritChange, String, String> consumer = factoryProvider.getReviewFactory()
+ .getConsumerForRemoteKey(group, "2");
+ consumer.addObserver(reviewListener);
+ consumer.retrieve(false);
+ reviewListener.waitForResponse(1, 1);
+
assertThat(group.getReviews().size(), is(1));
IReview review = group.getReviews().get(0);
assertThat(review, notNullValue());
assertThat(review.getId(), is("2"));
+ assertThat(review.getKey(), is("I2fcca48e6ae767e779cc428540cbe3fac6df1eb3"));
+ assertThat(review.getSubject(), is("Create Remote Test Review"));
+ assertThat(review.getMessage(), startsWith("Create Remote Test Review"));
+ assertThat(review.getMessage(), endsWith("<tests@mylyn.eclipse.org>"));
assertThat(review.getOwner().getDisplayName(), is("Mylyn Test User"));
Calendar reviewCreated = Calendar.getInstance();
reviewCreated.setTime(review.getCreationDate());
@@ -162,12 +117,12 @@ public class GerritRemoteFactoryTest extends TestCase {
List<ITopic> topics = review.getTopics();
assertThat(topics.size(), greaterThan(2));
ITopic topic = topics.get(0);
- assertThat(topic.getAuthor().getDisplayName(), is("Mylyn Test User"));
- assertThat(topic.getDescription(), is("Uploaded patch set 2."));
- assertThat(topic.getComments().size(), is(1));
IComment comment = topic.getComments().get(0);
assertThat(comment.getAuthor().getDisplayName(), is("Mylyn Test User"));
assertThat(comment.getDescription(), is("Uploaded patch set 2."));
+ assertThat(topic.getAuthor().getDisplayName(), is("Mylyn Test User"));
+ assertThat(topic.getDescription(), is("Uploaded patch set 2."));
+ assertThat(topic.getComments().size(), is(1));
List<IReviewItemSet> items = review.getSets();
assertThat(items.size(), greaterThan(1));
@@ -191,63 +146,136 @@ public class GerritRemoteFactoryTest extends TestCase {
//TODO -- we need to get update time here, not creation time. Not clear where gerrit API provides this
assertThat(patch2Created.get(Calendar.MINUTE), is(9));
assertThat(patchSet2.getReference(), is("refs/changes/02/2/2"));
+
+ assertThat(group.getUsers().size(), is(1));
+ assertThat(group.getUsers().get(0).getDisplayName(), is("Mylyn Test User"));
+
+ //Approvals
+ assertThat(group.getApprovalTypes().size(), is(2));
+ IApprovalType verifyApproval = group.getApprovalTypes().get(0);
+ assertThat(verifyApproval.getKey(), is("VRIF"));
+ assertThat(verifyApproval.getName(), is("Verified"));
+ IApprovalType codeReviewApproval = group.getApprovalTypes().get(1);
+ assertThat(codeReviewApproval.getKey(), is("CRVW"));
+ assertThat(codeReviewApproval.getName(), is("Code Review"));
+
+ assertThat(review.getReviewerApprovals().size(), is(1));
+ Entry<IUser, IReviewerEntry> reviewerEntry = review.getReviewerApprovals().entrySet().iterator().next();
+ Map<IApprovalType, Integer> reviewerApprovals = reviewerEntry.getValue().getApprovals();
+ assertThat(reviewerApprovals.size(), is(1));
+ Entry<IApprovalType, Integer> next = reviewerApprovals.entrySet().iterator().next();
+ assertThat(next.getKey(), sameInstance(codeReviewApproval));
+ assertThat(next.getValue(), is(1));
+
+ Set<Entry<IApprovalType, IRequirementEntry>> reviewApprovals = review.getRequirements().entrySet();
+ assertThat(reviewApprovals.size(), is(2));
+ IRequirementEntry codeReviewEntry = review.getRequirements().get(codeReviewApproval);
+ assertThat(codeReviewEntry, notNullValue());
+ assertThat(codeReviewEntry.getBy(), nullValue());
+ assertThat(codeReviewEntry.getStatus(), is(RequirementStatus.NOT_SATISFIED));
+ IRequirementEntry verifyEntry = review.getRequirements().get(verifyApproval);
+ assertThat(verifyEntry, notNullValue());
+ assertThat(verifyEntry.getBy(), nullValue());
+ assertThat(verifyEntry.getStatus(), is(RequirementStatus.NOT_SATISFIED));
+
+ assertThat(review.getState(), instanceOf(IRequirementReviewState.class));
+ assertThat(((IRequirementReviewState) review.getState()).getStatus(), is(RequirementStatus.NOT_SATISFIED));
+ assertThat(((IRequirementReviewState) review.getState()).getDescriptor(), is("NotSatisfied"));
+
+ //Dependencies
+ assertThat(review.getParents().size(), is(1));
+ IChange parentChange = review.getParents().get(0);
+ assertThat(parentChange.getId(), is("1"));
+ assertThat(parentChange.getKey(), is("I4c72e71a1bce68eff290c55b52b066b15a95a7b9"));
+ assertThat(parentChange.getSubject(), is("Test Review Commit"));
+ assertThat(parentChange.getMessage(), nullValue());
+ assertThat(parentChange.getOwner().getDisplayName(), is("Mylyn Test User"));
+
+ assertThat(review.getChildren().size(), is(1));
+ IChange childChange = review.getChildren().get(0);
+ assertThat(childChange.getId(), is("3"));
+ assertThat(childChange.getKey(), is("Id6475a8d1546943ed9d98139d5b553961bceb42b"));
+ assertThat(childChange.getSubject(), is("New Manual Test"));
+ assertThat(childChange.getMessage(), nullValue());
+ assertThat(childChange.getOwner().getDisplayName(), is("Mylyn Test User"));
}
@Test
public void testPatchSetContentFactory() throws CoreException {
- IReviewGroup group = IReviewsFactory.INSTANCE.createReviewGroup();
- TestListener<IReview> reviewListener = new TestListener<IReview>();
- RemoteEmfConsumer<IReviewGroup, IReview, GerritChange, String, String> consumer = service.getReviewFactory()
- .consume("Test Get Review", group, "2", "2", reviewListener);
- consumer.request();
- reviewListener.waitForUpdate();
+ IRepository group = IReviewsFactory.INSTANCE.createRepository();
+ TestRemoteObserver<IRepository, IReview> reviewListener = new TestRemoteObserver<IRepository, IReview>(
+ factoryProvider.getReviewFactory());
+ RemoteEmfConsumer<IRepository, IReview, GerritChange, String, String> reviewConsumer = factoryProvider.getReviewFactory()
+ .getConsumerForRemoteKey(group, "2");
+ reviewConsumer.addObserver(reviewListener);
+ reviewConsumer.retrieve(false);
+ reviewListener.waitForResponse(1, 1);
+
assertThat(group.getReviews().size(), is(1));
IReview review = group.getReviews().get(0);
+ assertThat(review.getId(), is("2"));
+ assertThat(review.getSubject(), is("Create Remote Test Review"));
+
+ IReviewItemSet testPatchSet = review.getSets().get(3);
+ RemoteEmfConsumer<IReview, IReviewItemSet, PatchSetDetail, PatchSetDetail, String> itemSetConsumer = factoryProvider.getReviewItemSetFactory()
+ .getConsumerForLocalKey(review, "4");
+ itemSetConsumer.retrieve(false);
+ PatchSetDetail detail = itemSetConsumer.getRemoteObject();
+ assertThat(detail.getInfo().getKey().get(), is(4));
- PatchSetContentRemoteFactory patchFactory = service.getReviewItemSetContentFactory();
- assertThat(review.getSets().size(), greaterThan(2));
- IReviewItemSet patchSet4 = review.getSets().get(3);
- PatchSetDetail detail = service.getReviewItemSetFactory().getRemoteObject(patchSet4);
PatchSetContent content = new PatchSetContent((PatchSet) null, detail);
- TestListener<List<IFileItem>> reviewItemListener = new TestListener<List<IFileItem>>();
- RemoteEmfConsumer<IReviewItemSet, List<IFileItem>, PatchSetContent, PatchSetContent, String> patchSetConsumer = patchFactory.consume(
- "CompareItems", patchSet4, content, "", reviewItemListener);
- patchSetConsumer.request();
- reviewItemListener.waitForUpdate();
- List<IFileItem> fileItems = patchSet4.getItems();
+
+ PatchSetContentIdRemoteFactory patchFactory = factoryProvider.getReviewItemSetContentFactory();
+ assertThat(review.getSets().size(), greaterThan(2));
+ List<IFileItem> fileItems = testPatchSet.getItems();
+ assertThat(fileItems.size(), is(0));
+ TestRemoteObserver<IReviewItemSet, List<IFileItem>> patchSetListener = new TestRemoteObserver<IReviewItemSet, List<IFileItem>>(
+ patchFactory);
+ RemoteEmfConsumer<IReviewItemSet, List<IFileItem>, PatchSetContent, String, String> patchSetConsumer = patchFactory.getConsumerForRemoteKey(
+ testPatchSet, "4");
+ patchSetConsumer.addObserver(patchSetListener);
+ patchSetConsumer.retrieve(false);
+ patchSetListener.waitForResponse(1, 1);
+
assertThat(fileItems.size(), is(3));
for (IReviewItem fileItem : fileItems) {
assertThat(fileItem, instanceOf(IFileItem.class));
+ assertThat(fileItem.getAddedBy().getDisplayName(), is("Mylyn Test User"));
+ assertThat(fileItem.getCommittedBy().getDisplayName(), is("Mylyn Test User"));
}
- IFileItem fileItem = fileItems.get(2);
- assertThat(fileItem.getAddedBy().getDisplayName(), is("Mylyn Test User"));
- assertThat(fileItem.getCommittedBy().getDisplayName(), is("Mylyn Test User"));
- //TODO Shouldn't name be last segment only?
- assertThat(fileItem.getName(), is("mylyn.test.files/item_remote_test_2.txt"));
-
- IFileVersion base = fileItem.getBase();
- assertThat(base.getAddedBy(), nullValue());
- assertThat(base.getCommittedBy(), nullValue());
- assertThat(base.getContent(), is(""));
- assertThat(base.getId(), is("base-2,4,mylyn.test.files/item_remote_test_2.txt"));
- assertThat(base.getName(), is("mylyn.test.files/item_remote_test_2.txt"));
- assertThat(base.getPath(), nullValue());
- assertThat(base.getReference(), nullValue());
- assertThat(base.getDescription(), is("Base"));
-
- IFileVersion target = fileItem.getTarget();
- assertThat(target.getAddedBy().getDisplayName(), is("Mylyn Test User"));
- assertThat(target.getCommittedBy().getDisplayName(), is("Mylyn Test User"));
- assertThat(target.getContent(), is("(Added for comment test review. V2)"));
- assertThat(target.getId(), is("2,4,mylyn.test.files/item_remote_test_2.txt"));
- assertThat(target.getName(), is("mylyn.test.files/item_remote_test_2.txt"));
- assertThat(target.getPath(), is("mylyn.test.files/item_remote_test_2.txt"));
- assertThat(target.getReference(), nullValue());
- assertThat(target.getDescription(), is("Patch Set 4"));
-
- List<IComment> allComments = target.getAllComments();
+ IFileItem fileItem0 = fileItems.get(0);
+ assertThat(fileItem0.getName(), is("/COMMIT_MSG"));
+
+ IFileItem fileItem1 = fileItems.get(1);
+ assertThat(fileItem1.getName(), is("mylyn.test.files/item_remote_test.txt"));
+
+ IFileItem fileItem2 = fileItems.get(2);
+ assertThat(fileItem2.getName(), is("mylyn.test.files/item_remote_test_2.txt"));
+
+ IFileVersion base2 = fileItem2.getBase();
+ assertThat(base2.getAddedBy(), nullValue());
+ assertThat(base2.getCommittedBy(), nullValue());
+ assertThat(base2.getContent(), is(""));
+ assertThat(base2.getId(), is("base-2,4,mylyn.test.files/item_remote_test_2.txt"));
+ assertThat(base2.getName(), is("mylyn.test.files/item_remote_test_2.txt"));
+ assertThat(base2.getPath(), nullValue());
+ assertThat(base2.getReference(), nullValue());
+ assertThat(base2.getDescription(), is("Base"));
+
+ IFileVersion target2 = fileItem2.getTarget();
+ assertThat(target2.getAddedBy().getDisplayName(), is("Mylyn Test User"));
+ assertThat(target2.getCommittedBy().getDisplayName(), is("Mylyn Test User"));
+ assertThat(target2.getContent(), is("(Added for comment test review. V2)"));
+ assertThat(target2.getId(), is("2,4,mylyn.test.files/item_remote_test_2.txt"));
+ assertThat(target2.getName(), is("mylyn.test.files/item_remote_test_2.txt"));
+ assertThat(target2.getPath(), is("mylyn.test.files/item_remote_test_2.txt"));
+ assertThat(target2.getReference(), nullValue());
+ assertThat(target2.getDescription(), is("Patch Set 4"));
+
+ List<IComment> allComments = target2.getAllComments();
assertThat(allComments.size(), is(1));
IComment fileComment = allComments.get(0);
+ assertThat(fileComment, notNullValue());
assertThat(fileComment.getAuthor().getDisplayName(), is("Mylyn Test User"));
assertThat(fileComment.getDescription(), is("Changed the version."));
}
diff --git a/org.eclipse.mylyn.gerrit.tests/src/org/eclipse/mylyn/internal/gerrit/core/remote/TestRemoteObserver.java b/org.eclipse.mylyn.gerrit.tests/src/org/eclipse/mylyn/internal/gerrit/core/remote/TestRemoteObserver.java
new file mode 100644
index 00000000..a60dc6be
--- /dev/null
+++ b/org.eclipse.mylyn.gerrit.tests/src/org/eclipse/mylyn/internal/gerrit/core/remote/TestRemoteObserver.java
@@ -0,0 +1,105 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Tasktop Technologies 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:
+ * Tasktop Technologies - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylyn.internal.gerrit.core.remote;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.mylyn.reviews.core.spi.remote.emf.AbstractRemoteEmfFactory;
+import org.eclipse.mylyn.reviews.core.spi.remote.emf.IRemoteEmfObserver;
+
+final class TestRemoteObserver<P extends EObject, T> implements IRemoteEmfObserver<P, T> {
+
+ static final int TEST_TIMEOUT = 10000;
+
+ T createdObject;
+
+ int updated;
+
+ int responded;
+
+ IStatus failure;
+
+ AbstractRemoteEmfFactory<?, ?, ?, ?, ?> factory;
+
+ public TestRemoteObserver(AbstractRemoteEmfFactory<?, ?, ?, ?, ?> factory) {
+ this.factory = factory;
+ }
+
+ public void created(P object, T child) {
+ createdObject = child;
+ }
+
+ public void updating(P parent, T object) {
+ }
+
+ public void updated(P object, T child, boolean modified) {
+ responded++;
+ if (modified) {
+ updated++;
+ }
+ }
+
+ public void failed(P object, T child, IStatus status) {
+ failure = status;
+ throw new RuntimeException(failure.getException());
+ }
+
+ protected void waitForResponse(int response, int update) {
+ long delay;
+ delay = 0;
+ while (delay < TEST_TIMEOUT) {
+ if (responded < response || updated < update) {
+ try {
+ Thread.sleep(10);
+ delay += 10;
+ } catch (InterruptedException e) {
+ }
+ } else {
+ break;
+ }
+ }
+ try {
+ //wait extra to ensure there aren't remaining jobs
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ }
+ assertThat("Wrong # responses", responded, is(response));
+ assertThat("Wrong # updates", updated, is(update));
+ if (factory != null) {
+ assertThat(factory.getService().isActive(), is(false));
+ }
+ }
+
+ protected void waitForFailure() {
+ long delay = 0;
+ while (delay < TEST_TIMEOUT) {
+ if (failure == null) {
+ try {
+ Thread.sleep(10);
+ delay += 10;
+ } catch (InterruptedException e) {
+ }
+ } else {
+ break;
+ }
+ }
+ }
+
+ void clear() {
+ createdObject = null;
+ updated = 0;
+ failure = null;
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.mylyn.gerrit.ui/META-INF/MANIFEST.MF b/org.eclipse.mylyn.gerrit.ui/META-INF/MANIFEST.MF
index 64acae89..38ca57dd 100644
--- a/org.eclipse.mylyn.gerrit.ui/META-INF/MANIFEST.MF
+++ b/org.eclipse.mylyn.gerrit.ui/META-INF/MANIFEST.MF
@@ -29,7 +29,8 @@ Require-Bundle: org.eclipse.core.runtime,
org.eclipse.mylyn.commons.net;bundle-version="3.8.0",
org.eclipse.mylyn.tasks.ui;bundle-version="3.8.0",
org.eclipse.ui.navigator,
- org.eclipse.team.core
+ org.eclipse.team.core,
+ org.eclipse.mylyn.reviews.edit;bundle-version="1.1.0"
Bundle-ActivationPolicy: lazy
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Bundle-Vendor: Eclipse Mylyn
diff --git a/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/GerritConnectorUi.java b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/GerritConnectorUi.java
index f04b3808..33947860 100644
--- a/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/GerritConnectorUi.java
+++ b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/GerritConnectorUi.java
@@ -45,6 +45,7 @@ import org.eclipse.mylyn.tasks.ui.wizards.RepositoryQueryWizard;
* @author Thomas Westling
* @author Steffen Pingel
* @author Kevin Sawicki
+ * @author Miles Parker
*/
public class GerritConnectorUi extends AbstractRepositoryConnectorUi {
@@ -134,5 +135,4 @@ public class GerritConnectorUi extends AbstractRepositoryConnectorUi {
}
return links != null ? links.toArray(new IHyperlink[links.size()]) : null;
}
-
}
diff --git a/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/GerritUiPlugin.java b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/GerritUiPlugin.java
index af3e7f49..54c990c4 100644
--- a/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/GerritUiPlugin.java
+++ b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/GerritUiPlugin.java
@@ -72,5 +72,4 @@ public class GerritUiPlugin extends AbstractUIPlugin {
}
return operationFactory;
}
-
}
diff --git a/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/editor/GerritReviewDetailSection.java b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/editor/GerritReviewDetailSection.java
new file mode 100644
index 00000000..ba43b865
--- /dev/null
+++ b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/editor/GerritReviewDetailSection.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * Copyright (c) 2010 Tasktop Technologies 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:
+ * Tasktop Technologies - initial API and implementation
+ * Jan Lohre (SAP) - improvements
+ * Sascha Scholz (SAP) - improvements
+ *******************************************************************************/
+
+package org.eclipse.mylyn.internal.gerrit.ui.editor;
+
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.mylyn.internal.gerrit.ui.factories.ReviewUiFactoryProvider;
+import org.eclipse.mylyn.reviews.ui.spi.editor.ReviewDetailSection;
+import org.eclipse.swt.widgets.Composite;
+
+import com.google.gerrit.reviewdb.Change;
+
+/**
+ * Displays basic information about a given review corresponding to top sections of Gerrit web interface.
+ *
+ * @author Steffen Pingel
+ * @author Miles Parker
+ */
+public class GerritReviewDetailSection extends ReviewDetailSection {
+
+ private final ReviewUiFactoryProvider reviewUiFactoryProvider = new ReviewUiFactoryProvider();
+
+ @Override
+ protected void createReviewersSubSection(Composite parent) {
+ if (getReview().getReviewerApprovals().isEmpty() && !canAddReviewers()) {
+ return;
+ }
+ super.createReviewersSubSection(parent);
+ Composite actionComposite = reviewUiFactoryProvider.createButtons(this, composite, getToolkit(), getReview());
+ GridDataFactory.fillDefaults().span(2, 1).applyTo(actionComposite);
+ }
+
+ @Override
+ protected boolean canAddReviewers() {
+ return getReview().getState().getDescriptor().equalsIgnoreCase(Change.Status.NEW.name())
+ || getReview().getState() == null;
+ }
+}
diff --git a/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/editor/GerritTaskEditorPage.java b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/editor/GerritTaskEditorPage.java
index 8a760b1f..338db249 100644
--- a/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/editor/GerritTaskEditorPage.java
+++ b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/editor/GerritTaskEditorPage.java
@@ -15,14 +15,10 @@ import java.util.Set;
import org.eclipse.mylyn.internal.gerrit.core.GerritConnector;
import org.eclipse.mylyn.internal.gerrit.core.GerritTaskSchema;
-import org.eclipse.mylyn.internal.gerrit.core.client.GerritClient;
-import org.eclipse.mylyn.internal.gerrit.core.remote.GerritRemoteFactoryProvider;
-import org.eclipse.mylyn.reviews.core.spi.remote.emf.ReviewsRemoteFactoryProvider;
import org.eclipse.mylyn.reviews.ui.spi.editor.AbstractReviewTaskEditorPage;
+import org.eclipse.mylyn.reviews.ui.spi.editor.ReviewDetailSection;
import org.eclipse.mylyn.reviews.ui.spi.editor.ReviewSetSection;
-import org.eclipse.mylyn.reviews.ui.spi.remote.RemoteUiService;
import org.eclipse.mylyn.tasks.core.data.TaskAttribute;
-import org.eclipse.mylyn.tasks.ui.TasksUi;
import org.eclipse.mylyn.tasks.ui.editors.AbstractAttributeEditor;
import org.eclipse.mylyn.tasks.ui.editors.AbstractTaskEditorPart;
import org.eclipse.mylyn.tasks.ui.editors.AttributeEditorFactory;
@@ -86,10 +82,10 @@ public class GerritTaskEditorPage extends AbstractReviewTaskEditorPage {
newCommentsDescriptor = descriptor;
}
}
- descriptors.add(new TaskEditorPartDescriptor(ReviewSection.class.getName()) {
+ descriptors.add(new TaskEditorPartDescriptor(ReviewDetailSection.class.getName()) {
@Override
public AbstractTaskEditorPart createPart() {
- return new ReviewSection();
+ return new ReviewDetailSection();
}
});
descriptors.add(new TaskEditorPartDescriptor(ReviewSetSection.class.getName()) {
@@ -106,11 +102,4 @@ public class GerritTaskEditorPage extends AbstractReviewTaskEditorPage {
}
return descriptors;
}
-
- @Override
- protected ReviewsRemoteFactoryProvider createRemoteFactory() {
- GerritConnector connector = (GerritConnector) TasksUi.getRepositoryConnector(getConnectorKind());
- GerritClient client = connector.getClient(getTaskRepository());
- return new GerritRemoteFactoryProvider(new RemoteUiService(), client);
- }
}
diff --git a/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/editor/ReviewSection.java b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/editor/ReviewSection.java
deleted file mode 100644
index 3503c90a..00000000
--- a/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/editor/ReviewSection.java
+++ /dev/null
@@ -1,257 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2010 Tasktop Technologies 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:
- * Tasktop Technologies - initial API and implementation
- * Jan Lohre (SAP) - improvements
- * Sascha Scholz (SAP) - improvements
- *******************************************************************************/
-
-package org.eclipse.mylyn.internal.gerrit.ui.editor;
-
-import java.util.Collections;
-import java.util.List;
-
-import org.eclipse.jface.layout.GridDataFactory;
-import org.eclipse.jface.layout.GridLayoutFactory;
-import org.eclipse.mylyn.internal.gerrit.core.GerritCorePlugin;
-import org.eclipse.mylyn.internal.gerrit.core.GerritUtil;
-import org.eclipse.mylyn.internal.gerrit.core.client.GerritChange;
-import org.eclipse.mylyn.internal.gerrit.core.client.compat.GerritConfigX;
-import org.eclipse.mylyn.internal.gerrit.core.remote.GerritRemoteFactoryProvider;
-import org.eclipse.mylyn.internal.gerrit.ui.factories.ReviewUiFactoryProvider;
-import org.eclipse.mylyn.reviews.ui.spi.editor.AbstractReviewSection;
-import org.eclipse.mylyn.tasks.ui.TasksUiUtil;
-import org.eclipse.osgi.util.NLS;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.SelectionAdapter;
-import org.eclipse.swt.events.SelectionEvent;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Label;
-import org.eclipse.swt.widgets.Link;
-import org.eclipse.ui.forms.IFormColors;
-import org.eclipse.ui.forms.widgets.ExpandableComposite;
-import org.eclipse.ui.forms.widgets.FormToolkit;
-import org.eclipse.ui.forms.widgets.Section;
-
-import com.google.gerrit.common.data.AccountInfo;
-import com.google.gerrit.common.data.ApprovalDetail;
-import com.google.gerrit.common.data.ApprovalType;
-import com.google.gerrit.common.data.ChangeDetail;
-import com.google.gerrit.common.data.ChangeInfo;
-import com.google.gerrit.common.data.GerritConfig;
-import com.google.gerrit.reviewdb.ApprovalCategory;
-import com.google.gerrit.reviewdb.ApprovalCategoryValue;
-import com.google.gerrit.reviewdb.Change;
-import com.google.gerrit.reviewdb.PatchSetApproval;
-
-/**
- * Displays basic information about a given review corresponding to top sections of Gerrit web interface.
- *
- * @author Steffen Pingel
- * @author Miles Parker
- */
-public class ReviewSection extends AbstractReviewSection {
-
- public ReviewSection() {
- setPartName("Review");
- }
-
- private void createReviewContent(ChangeDetail changeDetail) {
- GerritConfigX config = GerritCorePlugin.getGerritClient(getTaskEditorPage().getTaskRepository())
- .getGerritConfig();
- createPeopleSubSection(composite, changeDetail, config);
-
- if (changeDetail.getMissingApprovals() != null && changeDetail.getMissingApprovals().size() > 0) {
- createRequirementsSubSection(toolkit, composite, config, changeDetail);
- }
-
- if (changeDetail.getDependsOn() != null && changeDetail.getDependsOn().size() > 0) {
- createDependenciesSubSection(toolkit, composite, "Depends On", changeDetail, changeDetail.getDependsOn());
- }
- if (changeDetail.getNeededBy() != null && changeDetail.getNeededBy().size() > 0) {
- createDependenciesSubSection(toolkit, composite, "Needed By", changeDetail, changeDetail.getNeededBy());
- }
- }
-
- void createPeopleSubSection(Composite parent, ChangeDetail changeDetail, GerritConfig config) {
- if (changeDetail.getApprovals().isEmpty() && !canAddReviewers(changeDetail)) {
- return;
- }
-
- int style = ExpandableComposite.TWISTIE | ExpandableComposite.CLIENT_INDENT
- | ExpandableComposite.LEFT_TEXT_CLIENT_ALIGNMENT | ExpandableComposite.EXPANDED;
-
- final Section subSection = toolkit.createSection(parent, style);
- GridDataFactory.fillDefaults().grab(true, false).applyTo(subSection);
- subSection.setTitleBarForeground(toolkit.getColors().getColor(IFormColors.TITLE));
- subSection.setText("Reviewers");
-
- List<ApprovalType> approvalTypes;
- if (config != null && config.getApprovalTypes() != null && config.getApprovalTypes().getApprovalTypes() != null) {
- approvalTypes = config.getApprovalTypes().getApprovalTypes();
- } else {
- approvalTypes = Collections.emptyList();
- }
-
- Composite composite = toolkit.createComposite(subSection);
- int numColumns = approvalTypes.size() + 1;
- GridLayoutFactory.fillDefaults()
- .numColumns(numColumns)
- .extendedMargins(0, 0, 0, 5)
- .spacing(20, 5)
- .applyTo(composite);
- subSection.setClient(composite);
-
- if (changeDetail.getApprovals().size() > 0) {
- StringBuilder names = new StringBuilder();
-
- // column headers
- Label label = new Label(composite, SWT.NONE);
- label.setForeground(toolkit.getColors().getColor(IFormColors.TITLE));
- label.setText(" "); //$NON-NLS-1$
- for (ApprovalType approvalType : approvalTypes) {
- label = new Label(composite, SWT.NONE);
- label.setForeground(toolkit.getColors().getColor(IFormColors.TITLE));
- label.setText(approvalType.getCategory().getName());
- }
-
- for (ApprovalDetail approvalDetail : changeDetail.getApprovals()) {
- AccountInfo user = changeDetail.getAccounts().get(approvalDetail.getAccount());
-
- label = new Label(composite, SWT.NONE);
- label.setForeground(toolkit.getColors().getColor(IFormColors.TITLE));
- label.setText(GerritUtil.getUserLabel(user));
-
- for (ApprovalType approvalType : approvalTypes) {
- PatchSetApproval approval = approvalDetail.getApprovalMap().get(approvalType.getCategory().getId());
- if (approval != null && approval.getValue() != 0) {
- label = new Label(composite, SWT.NONE);
- GridDataFactory.fillDefaults().align(SWT.END, SWT.CENTER).applyTo(label);
-
- ApprovalCategoryValue approvalValue = approvalType.getValue(approval.getValue());
- if (approvalValue != null) {
- label.setText(approvalValue.formatValue());
- label.setToolTipText(approvalValue.format());
- } else {
- label.setText(approval.getValue() + ""); //$NON-NLS-1$
- }
- } else {
- label = new Label(composite, SWT.NONE);
- label.setText(" "); //$NON-NLS-1$
- }
- }
-
- if (names.length() > 0) {
- names.append(", "); //$NON-NLS-1$
- }
- names.append(GerritUtil.getUserLabel(user));
- }
-
- if (names.length() > 0) {
- addTextClient(toolkit, subSection, names.toString());
- }
- }
- Composite actionComposite = getUiFactoryProvider().createButtons(this, composite, getToolkit(), getReview());
- GridDataFactory.fillDefaults().span(2, 1).applyTo(actionComposite);
- }
-
- private boolean canAddReviewers(ChangeDetail changeDetail) {
- return changeDetail.getChange().getStatus() == Change.Status.NEW
- || GerritUtil.isDraft(changeDetail.getChange().getStatus());
- }
-
- private void createRequirementsSubSection(final FormToolkit toolkit, final Composite parent, GerritConfig config,
- ChangeDetail changeDetail) {
- if (config == null) {
- return;
- }
-
- int style = ExpandableComposite.TWISTIE | ExpandableComposite.CLIENT_INDENT
- | ExpandableComposite.LEFT_TEXT_CLIENT_ALIGNMENT;
-
- final Section subSection = toolkit.createSection(parent, style);
- GridDataFactory.fillDefaults().grab(true, false).applyTo(subSection);
- subSection.setTitleBarForeground(toolkit.getColors().getColor(IFormColors.TITLE));
- subSection.setText("Requirements");
-
- Composite composite = toolkit.createComposite(subSection);
- GridLayoutFactory.fillDefaults().numColumns(2).spacing(20, 5).extendedMargins(0, 0, 0, 5).applyTo(composite);
- subSection.setClient(composite);
-
- StringBuilder sb = new StringBuilder();
-
- for (ApprovalCategory.Id approvalCategoryId : changeDetail.getMissingApprovals()) {
- ApprovalType type = config.getApprovalTypes().getApprovalType(approvalCategoryId);
- if (type != null) {
- ApprovalCategoryValue approvalValue = type.getMax();
- if (approvalValue != null) {
- Label label1 = new Label(composite, SWT.NONE);
- label1.setText(type.getCategory().getName());
- label1.setForeground(toolkit.getColors().getColor(IFormColors.TITLE));
-
- Label label2 = new Label(composite, SWT.NONE);
- label2.setText(approvalValue.format());
- //label2.setForeground(toolkit.getColors().getColor(IFormColors.TITLE));
-
- if (sb.length() > 0) {
- sb.append(", "); //$NON-NLS-1$
- }
- sb.append(type.getCategory().getName());
- }
- }
- }
-
- addTextClient(toolkit, subSection, sb.toString());
- }
-
- private void createDependenciesSubSection(final FormToolkit toolkit, final Composite parent, String title,
- ChangeDetail changeDetail, List<ChangeInfo> changeInfos) {
- int style = ExpandableComposite.TWISTIE | ExpandableComposite.CLIENT_INDENT;
-
- final Section subSection = toolkit.createSection(parent, style);
- GridDataFactory.fillDefaults().grab(true, false).applyTo(subSection);
- subSection.setTitleBarForeground(toolkit.getColors().getColor(IFormColors.TITLE));
- subSection.setText(title);
-
- Composite composite = toolkit.createComposite(subSection);
- GridLayoutFactory.fillDefaults().extendedMargins(0, 0, 0, 5).applyTo(composite);
- subSection.setClient(composite);
-
- for (final ChangeInfo changeInfo : changeInfos) {
- AccountInfo user = changeDetail.getAccounts().get(changeInfo.getOwner());
- Link link = new Link(composite, SWT.NONE);
- link.setText(NLS.bind("<a>{0}</a>: {1} ({3}) by {2}", new String[] { changeInfo.getKey().abbreviate(),
- changeInfo.getSubject(), GerritUtil.getUserLabel(user), String.valueOf(changeInfo.getStatus()) }));
- link.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent e) {
- TasksUiUtil.openTask(getTaskEditorPage().getTaskRepository(), changeInfo.getId() + ""); //$NON-NLS-1$
- }
- });
- }
- }
-
- @Override
- public void createModelControls() {
- GerritChange change = ((GerritRemoteFactoryProvider) getRemoteFactoryProvider()).getReviewFactory()
- .getRemoteObject(getReview());
- if (change != null) {
- createReviewContent(change.getChangeDetail());
- }
- }
-
- @Override
- protected boolean shouldExpandOnCreate() {
- return true;
- }
-
- @Override
- protected ReviewUiFactoryProvider getUiFactoryProvider() {
- return new ReviewUiFactoryProvider();
- }
-}
diff --git a/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/factories/AbstractPatchSetUiFactory.java b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/factories/AbstractPatchSetUiFactory.java
index df47d64b..93cda7ce 100644
--- a/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/factories/AbstractPatchSetUiFactory.java
+++ b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/factories/AbstractPatchSetUiFactory.java
@@ -26,6 +26,7 @@ import org.eclipse.mylyn.internal.gerrit.core.egit.GerritToGitMapping;
import org.eclipse.mylyn.internal.gerrit.core.remote.GerritRemoteFactoryProvider;
import org.eclipse.mylyn.internal.gerrit.ui.GerritUiPlugin;
import org.eclipse.mylyn.internal.gerrit.ui.egit.EGitUiUtil;
+import org.eclipse.mylyn.reviews.core.model.IRepository;
import org.eclipse.mylyn.reviews.core.model.IReviewItemSet;
import org.eclipse.mylyn.reviews.ui.spi.factories.AbstractUiFactory;
import org.eclipse.mylyn.reviews.ui.spi.factories.IUiContext;
@@ -46,15 +47,20 @@ public abstract class AbstractPatchSetUiFactory extends AbstractUiFactory<IRevie
}
protected PatchSetDetail getPatchSetDetail() {
- return getGerritFactoryProvider().getReviewItemSetFactory().getRemoteObject(getModelObject());
+ return getGerritFactoryProvider().getReviewItemSetFactory()
+ .getConsumerForModel(getModelObject().getReview(), getModelObject())
+ .getRemoteObject();
}
protected GerritRemoteFactoryProvider getGerritFactoryProvider() {
- return (GerritRemoteFactoryProvider) getRemoteFactoryProvider();
+ return (GerritRemoteFactoryProvider) getFactoryProvider();
}
protected GerritChange getChange() {
- return getGerritFactoryProvider().getReviewFactory().getRemoteObject(getModelObject().getReview());
+ return getGerritFactoryProvider().getReviewFactory()
+ .getConsumerForModel((IRepository) getModelObject().getReview().getGroup(),
+ getModelObject().getReview())
+ .getRemoteObject();
}
protected final String getGerritProject(ChangeDetail changeDetail) {
diff --git a/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/factories/AddReviewersUiFactory.java b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/factories/AddReviewersUiFactory.java
index 35b43a86..2b58a69f 100644
--- a/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/factories/AddReviewersUiFactory.java
+++ b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/factories/AddReviewersUiFactory.java
@@ -15,6 +15,7 @@ import org.eclipse.mylyn.internal.gerrit.core.GerritUtil;
import org.eclipse.mylyn.internal.gerrit.core.client.GerritChange;
import org.eclipse.mylyn.internal.gerrit.core.remote.GerritRemoteFactoryProvider;
import org.eclipse.mylyn.internal.gerrit.ui.operations.AddReviewersDialog;
+import org.eclipse.mylyn.reviews.core.model.IRepository;
import org.eclipse.mylyn.reviews.core.model.IReview;
import org.eclipse.mylyn.reviews.ui.spi.factories.AbstractUiFactory;
import org.eclipse.mylyn.reviews.ui.spi.factories.IUiContext;
@@ -38,8 +39,9 @@ public class AddReviewersUiFactory extends AbstractUiFactory<IReview> {
}
protected GerritChange getChange() {
- return ((GerritRemoteFactoryProvider) getRemoteFactoryProvider()).getReviewFactory().getRemoteObject(
- getModelObject());
+ return ((GerritRemoteFactoryProvider) getFactoryProvider()).getReviewFactory()
+ .getConsumerForModel((IRepository) getModelObject().getGroup(), getModelObject())
+ .getRemoteObject();
}
@Override
diff --git a/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/factories/CompareWithUiFactory.java b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/factories/CompareWithUiFactory.java
index e882ce5b..8d3ef7c4 100644
--- a/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/factories/CompareWithUiFactory.java
+++ b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/factories/CompareWithUiFactory.java
@@ -23,6 +23,7 @@ import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.mylyn.commons.core.StatusHandler;
import org.eclipse.mylyn.internal.gerrit.core.client.PatchSetContent;
import org.eclipse.mylyn.internal.gerrit.core.client.compat.ChangeDetailX;
+import org.eclipse.mylyn.internal.gerrit.core.remote.PatchSetContentCompareRemoteFactory;
import org.eclipse.mylyn.internal.gerrit.ui.GerritReviewBehavior;
import org.eclipse.mylyn.internal.gerrit.ui.GerritUiPlugin;
import org.eclipse.mylyn.internal.reviews.ui.compare.ReviewItemSetCompareEditorInput;
@@ -30,6 +31,7 @@ import org.eclipse.mylyn.reviews.core.model.IFileItem;
import org.eclipse.mylyn.reviews.core.model.IReviewItemSet;
import org.eclipse.mylyn.reviews.core.model.IReviewsFactory;
import org.eclipse.mylyn.reviews.core.spi.remote.emf.RemoteEmfConsumer;
+import org.eclipse.mylyn.reviews.core.spi.remote.emf.RemoteEmfObserver;
import org.eclipse.mylyn.reviews.ui.spi.factories.IUiContext;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
@@ -39,6 +41,7 @@ import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.ui.forms.widgets.FormToolkit;
@@ -55,6 +58,37 @@ import com.google.gerrit.reviewdb.PatchSet;
*/
public class CompareWithUiFactory extends AbstractPatchSetUiFactory {
+ private final class CompareObserver extends RemoteEmfObserver<IReviewItemSet, List<IFileItem>> {
+ private final IReviewItemSet compareSet;
+
+ private final RemoteEmfConsumer<IReviewItemSet, List<IFileItem>, PatchSetContent, PatchSetContent, String> consumer;
+
+ private CompareObserver(IReviewItemSet compareSet,
+ RemoteEmfConsumer<IReviewItemSet, List<IFileItem>, PatchSetContent, PatchSetContent, String> consumer) {
+ this.compareSet = compareSet;
+ this.consumer = consumer;
+ }
+
+ @Override
+ public void updated(IReviewItemSet parentObject, List<IFileItem> modelObject, boolean modified) {
+ CompareConfiguration configuration = new CompareConfiguration();
+ CompareUI.openCompareEditor(new ReviewItemSetCompareEditorInput(configuration, compareSet, null,
+ new GerritReviewBehavior(getTask(), resolveGitRepository())));
+ Display.getCurrent().asyncExec(new Runnable() {
+ @Override
+ public void run() {
+ consumer.removeObserver(CompareObserver.this);
+ }
+ });
+ }
+
+ @Override
+ public void failed(IReviewItemSet parentObject, List<IFileItem> modelObject, IStatus status) {
+ StatusHandler.log(new Status(IStatus.ERROR, GerritUiPlugin.PLUGIN_ID,
+ "Couldn't load task content for review", status.getException())); //$NON-NLS-1$
+ }
+ }
+
private PatchSet baseSet;
private PatchSet targetSet;
@@ -131,28 +165,17 @@ public class CompareWithUiFactory extends AbstractPatchSetUiFactory {
String basePatchSetLabel = content.getBase() != null ? content.getBase().getPatchSetId() + "" : "Base";
compareSet.setName(NLS.bind("Compare Patch Set {0} and {1}", content.getTarget().getPatchSetId(),
basePatchSetLabel));
- RemoteEmfConsumer<IReviewItemSet, List<IFileItem>, PatchSetContent, PatchSetContent, String> consumer = getGerritFactoryProvider().getReviewItemSetContentFactory()
- .consume("Compare Items", compareSet, content, "", new RemoteEmfConsumer.IObserver<List<IFileItem>>() {
- public void responded(boolean modified) {
- CompareConfiguration configuration = new CompareConfiguration();
- CompareUI.openCompareEditor(new ReviewItemSetCompareEditorInput(configuration, compareSet,
- null, new GerritReviewBehavior(getTask(), resolveGitRepository())));
- }
-
- public void failed(IStatus status) {
- StatusHandler.log(new Status(IStatus.ERROR, GerritUiPlugin.PLUGIN_ID,
- "Couldn't load task content for review", status.getException())); //$NON-NLS-1$
- }
-
- public void created(List<IFileItem> object) {
- }
- });
- consumer.request();
+ PatchSetContentCompareRemoteFactory remoteFactory = new PatchSetContentCompareRemoteFactory(
+ getGerritFactoryProvider());
+ final RemoteEmfConsumer<IReviewItemSet, List<IFileItem>, PatchSetContent, PatchSetContent, String> consumer = remoteFactory.getConsumerForRemoteObject(
+ compareSet, content);
+ consumer.addObserver(new CompareObserver(compareSet, consumer));
+ consumer.retrieve(false);
}
@Override
public boolean isExecutable() {
ChangeDetailX changeDetail = getChange().getChangeDetail();
- return changeDetail != null && changeDetail.getPatchSets().size() > 1;
+ return changeDetail != null && changeDetail.getPatchSets().size() > 0;
}
}
diff --git a/org.eclipse.mylyn.reviews.core.tests/src/org/eclipse/mylyn/reviews/core/remote/RemoteEmfFactoryTest.java b/org.eclipse.mylyn.reviews.core.tests/src/org/eclipse/mylyn/reviews/core/remote/RemoteEmfFactoryTest.java
index 47bd65ab..10df9609 100644
--- a/org.eclipse.mylyn.reviews.core.tests/src/org/eclipse/mylyn/reviews/core/remote/RemoteEmfFactoryTest.java
+++ b/org.eclipse.mylyn.reviews.core.tests/src/org/eclipse/mylyn/reviews/core/remote/RemoteEmfFactoryTest.java
@@ -17,10 +17,9 @@ import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.Matchers.sameInstance;
import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
@@ -31,7 +30,6 @@ import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EcoreFactory;
import org.eclipse.emf.ecore.EcorePackage;
-import org.eclipse.mylyn.reviews.core.spi.remote.JobRemoteService;
import org.eclipse.mylyn.reviews.core.spi.remote.emf.AbstractRemoteEmfFactory;
import org.eclipse.mylyn.reviews.core.spi.remote.emf.RemoteEmfConsumer;
import org.junit.Test;
@@ -41,146 +39,296 @@ import org.junit.Test;
*/
public class RemoteEmfFactoryTest {
- private static final int TEST_TIMEOUT = 100;
+ class TestRemoteObjectManyObject {
+ String name;
- private final class TestListener<T> implements RemoteEmfConsumer.IObserver<T> {
+ String data;
- T createdObject;
+ private TestRemoteObjectManyObject(String name) {
+ this.name = name;
+ }
+ }
- int updated;
+ class TestRemoteFactoryNoUpdate extends TestRemoteFactory {
- int responded;
+ @Override
+ public boolean updateModel(EPackage parent, EClass object, TestRemoteObject remoteObject) {
+ return false;
+ }
+ }
- IStatus failure;
+ EPackage parent = EcoreFactory.eINSTANCE.createEPackage();
- AbstractRemoteEmfFactory<?, ?, ?, ?, ?> factory;
+ class TestManagerHarness {
- private TestListener(AbstractRemoteEmfFactory<?, ?, ?, ?, ?> factory) {
- this.factory = factory;
- }
+ TestRemoteFactory factory;
- public void created(T object) {
- createdObject = object;
- }
+ RemoteEmfConsumer<EPackage, EClass, TestRemoteObject, String, String> consumer;
- public void responded(boolean modified) {
- responded++;
- if (modified) {
- updated++;
- }
- }
+ TestIRemoteEmfObserver<EPackage, EClass> listener;
- public void failed(org.eclipse.core.runtime.IStatus status) {
- failure = status;
+ TestManagerHarness(TestRemoteFactory factory) {
+ this.factory = factory;
+ listener = new TestIRemoteEmfObserver<EPackage, EClass>(factory);
+ consumer = createConsumer();
+ consumer.addObserver(listener);
}
- protected void waitForResponse(int response, int update) {
- long delay;
- delay = 0;
- while (delay < TEST_TIMEOUT) {
- if (responded < response) {
- try {
- Thread.sleep(10);
- delay += 10;
- } catch (InterruptedException e) {
- }
- } else {
- break;
- }
- }
- try {
- //wait extra to ensure there aren't remaining jobs
- Thread.sleep(100);
- } catch (InterruptedException e) {
- }
- assertThat("Wrong # responses", responded, is(response));
- assertThat("Wrong # updates", updated, is(update));
- if (factory != null) {
- assertThat(factory.getService().isActive(), is(false));
- }
+ TestManagerHarness() {
+ this(new TestRemoteFactory());
}
- protected void waitForFailure() {
- long delay = 0;
- while (delay < TEST_TIMEOUT) {
- if (failure == null) {
- try {
- Thread.sleep(10);
- delay += 10;
- } catch (InterruptedException e) {
- }
- } else {
- break;
- }
- }
+ RemoteEmfConsumer<EPackage, EClass, TestRemoteObject, String, String> createConsumer() {
+ return factory.getConsumerForRemoteKey(parent, "remoteKeyFor Object 1");
}
- void clear() {
- createdObject = null;
- updated = 0;
- failure = null;
+ void basicTest() {
+ consumer.retrieve(false);
+ listener.waitForResponse(1, 1);
+ checkConsumer(consumer, "remoteKeyFor Object 1", "Remote Object 1", "localKeyFor Object 1",
+ "Local Object 1");
}
}
- class TestRemoteObject {
- String name;
+ IStatus errorStatus = new Status(IStatus.ERROR, "blah", "Blah");
- String data;
+ class TestFailureFactory extends TestRemoteFactory {
+ @Override
+ public TestRemoteObject pull(EPackage parent, String remoteKey, IProgressMonitor monitor) throws CoreException {
+ throw new CoreException(errorStatus);
+ }
+ }
- private TestRemoteObject(String name) {
- this.name = name;
+ protected void checkConsumer(RemoteEmfConsumer<EPackage, EClass, TestRemoteObject, String, String> manager,
+ String remoteKey, String remoteObject, String localKey, String localObject) {
+ if (remoteKey != null) {
+ assertThat("Bad Remote Key", manager.getRemoteKey(), is(remoteKey));
+ } else {
+ assertThat("Bad Remote Key", manager.getRemoteKey(), nullValue());
+ }
+ if (remoteObject != null) {
+ assertThat("Bad Remote Object", manager.getRemoteObject(), notNullValue());
+ assertThat(manager.getRemoteObject().getName(), is(remoteObject));
+ } else {
+ assertThat("Bad Remote Object", manager.getRemoteObject(), nullValue());
+ }
+ if (localKey != null) {
+ assertThat("Bad Local Key", manager.getLocalKey(), is(localKey));
+ } else {
+ assertThat("Bad Local Key", manager.getLocalKey(), nullValue());
+ }
+ if (localObject != null) {
+ assertThat("Bad Local Object", manager.getModelObject(), notNullValue());
+ assertThat(manager.getModelObject().getName(), is(localObject));
+ } else {
+ assertThat("Bad Local Object", manager.getModelObject(), nullValue());
}
}
- class TestRemoteObjectManyObject {
- String name;
+ @Test
+ public void testRemoteProcessCreate() {
+ TestRemoteFactory testRemoteFactory = new TestRemoteFactory();
+ EPackage parent = EcoreFactory.eINSTANCE.createEPackage();
+ EClass create1 = testRemoteFactory.createModel(parent, TestRemoteFactory.remote1);
+ assertThat(create1.getName(), is("Local Object 1"));
+ EClass create2 = testRemoteFactory.createModel(parent, TestRemoteFactory.remote2);
+ assertThat(create2.getName(), is("Local Object 2"));
+ }
- String data;
+ @Test
+ public void testGetConsumerForRemoteKey() throws CoreException {
+ TestManagerHarness harness = new TestManagerHarness() {
+ @Override
+ RemoteEmfConsumer<EPackage, EClass, TestRemoteObject, String, String> createConsumer() {
+ return factory.getConsumerForRemoteKey(parent, "remoteKeyFor Object 1");
+ }
+ };
+ checkConsumer(harness.consumer, "remoteKeyFor Object 1", null, "localKeyFor Object 1", null);
+ harness.basicTest();
+ }
- private TestRemoteObjectManyObject(String name) {
- this.name = name;
- }
+ @Test
+ public void testGetConsumerForRemoteKeyUpdate() throws CoreException {
+ TestManagerHarness harness = new TestManagerHarness() {
+ @Override
+ RemoteEmfConsumer<EPackage, EClass, TestRemoteObject, String, String> createConsumer() {
+ return factory.getConsumerForRemoteKey(parent, "remoteKeyFor Object 1");
+ }
+ };
+ checkConsumer(harness.consumer, "remoteKeyFor Object 1", null, "localKeyFor Object 1", null);
+ harness.basicTest();
+ TestRemoteFactory.remote1.data = "new";
+ harness.consumer.retrieve(false);
+ harness.listener.waitForResponse(2, 2);
+ EClass modelObject = harness.consumer.getModelObject();
+ assertThat(modelObject.getInstanceTypeName(), is("new"));
}
- TestRemoteObject remote1 = new TestRemoteObject("Object 1");
+ @Test
+ public void testGetConsumerForDifferentParentSameLocalKey() throws CoreException {
+ EPackage parent1 = EcoreFactory.eINSTANCE.createEPackage();
+ TestRemoteFactory factory = new TestRemoteFactory();
+ TestIRemoteEmfObserver<EPackage, EClass> listener1 = new TestIRemoteEmfObserver<EPackage, EClass>(factory);
+ RemoteEmfConsumer<EPackage, EClass, TestRemoteObject, String, String> consumer1 = factory.getConsumerForRemoteKey(
+ parent1, "remoteKeyFor Object 1");
- TestRemoteObject remote2 = new TestRemoteObject("Object 2");
+ EPackage parent2 = EcoreFactory.eINSTANCE.createEPackage();
+ RemoteEmfConsumer<EPackage, EClass, TestRemoteObject, String, String> consumer2 = factory.getConsumerForRemoteKey(
+ parent2, "remoteKeyFor Object 1");
- Map<String, TestRemoteObject> remoteForKey = new HashMap<String, TestRemoteObject>();
+ assertThat(consumer1, not(sameInstance(consumer2)));
+ }
- class TestRemoteFactory extends AbstractRemoteEmfFactory<EPackage, EClass, TestRemoteObject, String, String> {
+ @Test
+ public void testGetConsumerForLocalKey() throws CoreException {
+ TestManagerHarness harness = new TestManagerHarness() {
+ @Override
+ RemoteEmfConsumer<EPackage, EClass, TestRemoteObject, String, String> createConsumer() {
+ return factory.getConsumerForLocalKey(parent, "localKeyFor Object 1");
+ }
+ };
+ checkConsumer(harness.consumer, null, null, "localKeyFor Object 1", null);
+ harness.consumer.retrieve(false);
+ harness.listener.waitForResponse(0, 0);
+ checkConsumer(harness.consumer, null, null, "localKeyFor Object 1", null);
+ }
- public TestRemoteFactory() {
- super(new JobRemoteService(), EcorePackage.Literals.EPACKAGE__ECLASSIFIERS,
- EcorePackage.Literals.ENAMED_ELEMENT__NAME);
- remoteForKey.put("object1", remote1);
- remoteForKey.put("object2", remote2);
- }
+ @Test
+ public void testGetConsumerForRemote() throws CoreException {
+ TestManagerHarness harness = new TestManagerHarness() {
+ @Override
+ RemoteEmfConsumer<EPackage, EClass, TestRemoteObject, String, String> createConsumer() {
+ return factory.getConsumerForRemoteObject(parent, TestRemoteFactory.remote1);
+ }
+ };
+ checkConsumer(harness.consumer, "remoteKeyFor Object 1", "Remote Object 1", "localKeyFor Object 1", null);
+ harness.consumer.retrieve(false);
+ harness.listener.waitForResponse(1, 1);
+ checkConsumer(harness.consumer, "remoteKeyFor Object 1", "Remote Object 1", "localKeyFor Object 1",
+ "Local Object 1");
+ }
- @Override
- public TestRemoteObject retrieve(String remoteKey, IProgressMonitor monitor) throws CoreException {
- return remoteForKey.get(remoteKey);
- }
+ @Test
+ public void testRemoteKeyThenRemoteObject() throws CoreException {
+ TestRemoteFactory testRemoteFactory = new TestRemoteFactory();
+ TestManagerHarness keyHarness = new TestManagerHarness(testRemoteFactory) {
+ @Override
+ RemoteEmfConsumer<EPackage, EClass, TestRemoteObject, String, String> createConsumer() {
+ return factory.getConsumerForRemoteKey(parent, "remoteKeyFor Object 2");
+ }
+ };
+ checkConsumer(keyHarness.consumer, "remoteKeyFor Object 2", null, "localKeyFor Object 2", null);
+ TestManagerHarness remoteHarness = new TestManagerHarness(testRemoteFactory) {
+ @Override
+ RemoteEmfConsumer<EPackage, EClass, TestRemoteObject, String, String> createConsumer() {
+ return factory.getConsumerForRemoteObject(parent, TestRemoteFactory.remote2);
+ }
+ };
+ assertThat(keyHarness.consumer, sameInstance(remoteHarness.consumer));
+ checkConsumer(remoteHarness.consumer, "remoteKeyFor Object 2", "Remote Object 2", "localKeyFor Object 2", null);
+ }
- @Override
- protected EClass create(EPackage parent, TestRemoteObject remoteObject) {
- EClass clazz = EcoreFactory.eINSTANCE.createEClass();
- clazz.setName(remoteObject.name);
- return clazz;
- }
+ @Test
+ public void testRemoteObjectThenRemoteKey() throws CoreException {
+ TestRemoteFactory testRemoteFactory = new TestRemoteFactory();
+ TestManagerHarness remoteHarness = new TestManagerHarness(testRemoteFactory) {
+ @Override
+ RemoteEmfConsumer<EPackage, EClass, TestRemoteObject, String, String> createConsumer() {
+ return factory.getConsumerForRemoteObject(parent, TestRemoteFactory.remote2);
+ }
+ };
+ checkConsumer(remoteHarness.consumer, "remoteKeyFor Object 2", "Remote Object 2", "localKeyFor Object 2", null);
+ TestManagerHarness keyHarness = new TestManagerHarness(testRemoteFactory) {
+ @Override
+ RemoteEmfConsumer<EPackage, EClass, TestRemoteObject, String, String> createConsumer() {
+ return factory.getConsumerForRemoteKey(parent, "remoteKeyFor Object 2");
+ }
+ };
+ assertThat(keyHarness.consumer, sameInstance(remoteHarness.consumer));
+ checkConsumer(keyHarness.consumer, "remoteKeyFor Object 2", "Remote Object 2", "localKeyFor Object 2", null);
+ }
- @Override
- public boolean update(EPackage parent, EClass object, TestRemoteObject remoteObject) {
- object.setInstanceTypeName(remoteObject.data);
- return true;
- }
+ @Test
+ public void testLocalKeyThenRemoteKeyAndObject() throws CoreException {
+ TestRemoteFactory testRemoteFactory = new TestRemoteFactory();
+ TestManagerHarness remoteHarness = new TestManagerHarness(testRemoteFactory) {
+ @Override
+ RemoteEmfConsumer<EPackage, EClass, TestRemoteObject, String, String> createConsumer() {
+ return factory.getConsumerForLocalKey(parent, "localKeyFor Object 2");
+ }
+ };
+ checkConsumer(remoteHarness.consumer, null, null, "localKeyFor Object 2", null);
+ TestManagerHarness keyHarness = new TestManagerHarness(testRemoteFactory) {
+ @Override
+ RemoteEmfConsumer<EPackage, EClass, TestRemoteObject, String, String> createConsumer() {
+ return factory.getConsumerForRemoteKey(parent, "remoteKeyFor Object 2");
+ }
+ };
+ assertThat(keyHarness.consumer, sameInstance(remoteHarness.consumer));
+ checkConsumer(keyHarness.consumer, "remoteKeyFor Object 2", null, "localKeyFor Object 2", null);
+ keyHarness.consumer.retrieve(false);
+ keyHarness.listener.waitForResponse(1, 1);
+ checkConsumer(keyHarness.consumer, "remoteKeyFor Object 2", "Remote Object 2", "localKeyFor Object 2",
+ "Local Object 2");
}
- class TestRemoteFactoryNoUpdate extends TestRemoteFactory {
+ @Test
+ public void testLocalObjectThenRemoteKeyAndObject() {
+ TestRemoteFactory factory = new TestRemoteFactory();
+ EClass create1 = EcoreFactory.eINSTANCE.createEClass();
+ create1.setName("Object 1");
+ create1.setInstanceClassName("localKeyFor Object 1");
+ parent.getEClassifiers().add(create1);
+ EClass create2 = EcoreFactory.eINSTANCE.createEClass();
+ create2.setName("Object 2");
+ create2.setInstanceClassName("localKeyFor Object 2");
+ parent.getEClassifiers().add(create2);
+ TestIRemoteEmfObserver<EPackage, EClass> testListener1 = new TestIRemoteEmfObserver<EPackage, EClass>(factory);
+ RemoteEmfConsumer<EPackage, EClass, TestRemoteObject, String, String> createConsumer = factory.getConsumerForLocalKey(
+ parent, "localKeyFor Object 2");
+ createConsumer.addObserver(testListener1);
+ createConsumer.retrieve(false);
+ testListener1.waitForResponse(1, 0);
+ assertThat(testListener1.createdObject, nullValue());
+ checkConsumer(createConsumer, null, null, "localKeyFor Object 2", "Object 2");
+
+ TestManagerHarness keyHarness = new TestManagerHarness(factory) {
+ @Override
+ RemoteEmfConsumer<EPackage, EClass, TestRemoteObject, String, String> createConsumer() {
+ return factory.getConsumerForRemoteKey(parent, "remoteKeyFor Object 2");
+ }
+ };
+ assertThat(keyHarness.consumer, sameInstance(createConsumer));
+ checkConsumer(keyHarness.consumer, "remoteKeyFor Object 2", null, "localKeyFor Object 2", "Object 2");
+ createConsumer.retrieve(false);
+ testListener1.waitForResponse(2, 1);
+ checkConsumer(keyHarness.consumer, "remoteKeyFor Object 2", "Remote Object 2", "localKeyFor Object 2",
+ "Object 2");
+ }
- @Override
- public boolean update(EPackage parent, EClass object, TestRemoteObject remoteObject) {
- return false;
+ @Test
+ public void testRemoteProcessFailure() throws CoreException {
+ EPackage parent = EcoreFactory.eINSTANCE.createEPackage();
+ TestRemoteFactory factory = new TestFailureFactory();
+ TestIRemoteEmfObserver<EPackage, EClass> testListener = new TestIRemoteEmfObserver<EPackage, EClass>(factory);
+ RemoteEmfConsumer<EPackage, EClass, TestRemoteObject, String, String> consumer1 = factory.getConsumerForRemoteKey(
+ parent, "object1");
+ consumer1.addObserver(testListener);
+ consumer1.retrieve(false);
+ testListener.waitForFailure();
+ assertThat(testListener.failure, is(errorStatus));
+ }
+
+ @Test
+ public void testMultipleConsumers() throws CoreException {
+ TestRemoteFactory factory = new TestRemoteFactory();
+ TestManagerHarness[] harnesses = new TestManagerHarness[5];
+ for (int i = 0; i < harnesses.length; i++) {
+ harnesses[i] = new TestManagerHarness(factory);
+ harnesses[i].basicTest();
+ }
+ for (int i = 1; i < harnesses.length; i++) {
+ assertThat(harnesses[i].consumer, sameInstance(harnesses[0].consumer));
}
}
@@ -188,211 +336,162 @@ public class RemoteEmfFactoryTest {
AbstractRemoteEmfFactory<EPackage, List<EClassifier>, TestRemoteObject, String, String> {
public TestRemoteFactoryCollectionObject() {
- super(new JobRemoteService(), EcorePackage.Literals.EPACKAGE__ECLASSIFIERS,
+ super(new TestRemoteFactoryProvider(), EcorePackage.Literals.EPACKAGE__ECLASSIFIERS,
EcorePackage.Literals.ENAMED_ELEMENT__NAME);
- remoteForKey.put("object1", remote1);
- remoteForKey.put("object2", remote2);
}
@Override
- public TestRemoteObject retrieve(String remoteKey, IProgressMonitor monitor) throws CoreException {
- return remoteForKey.get(remoteKey);
+ public TestRemoteObject pull(EPackage parent, String remoteKey, IProgressMonitor monitor) throws CoreException {
+ return TestRemoteFactory.remoteForKey.get(remoteKey);
}
@Override
- protected List<EClassifier> create(EPackage parent, TestRemoteObject remoteObject) {
+ protected List<EClassifier> createModel(EPackage parent, TestRemoteObject remoteObject) {
List<EClassifier> classifiers = parent.getEClassifiers();
EClass class1 = EcoreFactory.eINSTANCE.createEClass();
- class1.setName("Many Class 1");
+ class1.setName("Many " + remoteObject.getName() + "_1");
classifiers.add(class1);
EClass class2 = EcoreFactory.eINSTANCE.createEClass();
- class2.setName("Many Class 2");
+ class2.setName("Many " + remoteObject.getName() + "_2");
classifiers.add(class2);
return classifiers;
}
@Override
- public boolean update(EPackage parent, List<EClassifier> classifiers, TestRemoteObject remoteObject) {
+ public boolean updateModel(EPackage parent, List<EClassifier> classifiers, TestRemoteObject remoteObject) {
EClass class2 = EcoreFactory.eINSTANCE.createEClass();
- class2.setName("Many Class " + (classifiers.size() + 1));
+ class2.setName("Many " + remoteObject.getName() + "_" + (classifiers.size() + 1));
classifiers.add(class2);
return true;
}
- }
- IStatus errorStatus = new Status(IStatus.ERROR, "blah", "Blah");
-
- class TestFailureFactory extends TestRemoteFactory {
@Override
- public TestRemoteObject retrieve(String remoteKey, IProgressMonitor monitor) throws CoreException {
- throw new CoreException(errorStatus);
+ public String getRemoteKey(TestRemoteObject remoteObject) {
+ return "remoteKeyFor" + remoteObject.getName();
}
- }
-
- @Test
- public void testRemoteProcessCreate() {
- TestRemoteFactory testRemoteFactory = new TestRemoteFactory();
- EPackage parent = EcoreFactory.eINSTANCE.createEPackage();
- EClass create1 = testRemoteFactory.create(parent, remote1);
- assertThat(create1.getName(), is("Object 1"));
- EClass create2 = testRemoteFactory.create(parent, remote2);
- assertThat(create2.getName(), is("Object 2"));
- }
-
- @Test
- public void testRemoteProcessGet() {
- TestRemoteFactory testRemoteFactory = new TestRemoteFactory();
- EPackage parent = EcoreFactory.eINSTANCE.createEPackage();
- EClass get2 = testRemoteFactory.get(parent, remote2);
- assertThat(get2.getName(), is("Object 2"));
- EClass get1 = testRemoteFactory.get(parent, remote1);
- assertThat(get1.getName(), is("Object 1"));
-
- EClass get1Again = testRemoteFactory.get(parent, remote1);
- assertThat(get1Again.getName(), is("Object 1"));
- }
-
- @Test
- public void testRemoteProcessRequest() throws CoreException {
- EPackage parent = EcoreFactory.eINSTANCE.createEPackage();
-
- TestRemoteFactory factory = new TestRemoteFactory();
- TestListener<EClass> testListener = new TestListener<EClass>(factory);
- RemoteEmfConsumer<EPackage, EClass, TestRemoteObject, String, String> consume = factory.consume("Test Process",
- parent, "object1", null, testListener);
- consume.request();
- testListener.waitForResponse(1, 1);
- assertThat(testListener.createdObject, notNullValue());
- EClass modelObject = factory.getModelObject(remote1);
- assertThat(modelObject.getName(), is("Object 1"));
- TestRemoteObject remoteObject = factory.getRemoteObject(modelObject);
- assertThat(remoteObject, sameInstance(remote1));
- assertThat(testListener.createdObject.getInstanceTypeName(), nullValue());
- }
-
- @Test
- public void testRemoteProcessUpdate() throws CoreException {
- EPackage parent = EcoreFactory.eINSTANCE.createEPackage();
- TestRemoteFactory factory = new TestRemoteFactory();
- TestListener<EClass> testListener = new TestListener<EClass>(factory);
- RemoteEmfConsumer<EPackage, EClass, TestRemoteObject, String, String> consume = factory.consume("Test Process",
- parent, "object1", null, testListener);
- consume.request();
- testListener.waitForResponse(1, 1);
- remote1.data = "new";
- consume.request();
- testListener.waitForResponse(2, 2);
- EClass modelObject = factory.getModelObject(remote1);
- assertThat(modelObject, sameInstance(consume.getModelObject()));
- assertThat(modelObject.getInstanceTypeName(), is("new"));
- }
+ @Override
+ public String getLocalKeyForRemoteObject(TestRemoteObject remoteObject) {
+ return "localKeyFor" + remoteObject.getName();
+ }
- @Test
- public void testRemoteProcessRequestUpdateNoModification() throws CoreException {
- EPackage parent = EcoreFactory.eINSTANCE.createEPackage();
- TestRemoteFactory factory = new TestRemoteFactoryNoUpdate();
- TestListener<EClass> testListener = new TestListener<EClass>(factory);
- RemoteEmfConsumer<EPackage, EClass, TestRemoteObject, String, String> consume = factory.consume("Test Process",
- parent, "object1", null, testListener);
- consume.request();
- testListener.waitForResponse(1, 1);
- assertThat(testListener.createdObject, notNullValue());
- EClass modelObject = factory.getModelObject(remote1);
- assertThat(modelObject.getName(), is("Object 1"));
- TestRemoteObject remoteObject = factory.getRemoteObject(modelObject);
- assertThat(remoteObject, sameInstance(remote1));
- assertThat(testListener.createdObject.getInstanceTypeName(), nullValue());
- consume.request();
- testListener.waitForResponse(2, 1);
+ @Override
+ public String getLocalKeyForRemoteKey(String remoteKey) {
+ return remoteKey.replace("remote", "local");
+ }
}
@Test
public void testRemoteProcessCollectionRequestAndUpdate() throws CoreException {
EPackage parent = EcoreFactory.eINSTANCE.createEPackage();
TestRemoteFactoryCollectionObject factory = new TestRemoteFactoryCollectionObject();
- TestListener<List<EClassifier>> testListener = new TestListener<List<EClassifier>>(factory);
- RemoteEmfConsumer<EPackage, List<EClassifier>, TestRemoteObject, String, String> consume = factory.consume(
- "Test Process", parent, null, null, testListener);
- consume.request();
+ TestIRemoteEmfObserver<EPackage, List<EClassifier>> testListener = new TestIRemoteEmfObserver<EPackage, List<EClassifier>>(
+ factory);
+ RemoteEmfConsumer<EPackage, List<EClassifier>, TestRemoteObject, String, String> consumer1 = factory.getConsumerForRemoteKey(
+ parent, "remoteKeyFor Object 1");
+ consumer1.addObserver(testListener);
+ consumer1.retrieve(false);
testListener.waitForResponse(1, 1);
- assertThat(testListener.createdObject, notNullValue());
- List<EClassifier> modelObject = consume.getModelObject();
+ List<EClassifier> modelObject = consumer1.getModelObject();
assertThat(modelObject.size(), is(3));
- assertThat(modelObject.get(0).getName(), is("Many Class 1"));
- assertThat(modelObject.get(1).getName(), is("Many Class 2"));
- assertThat(modelObject.get(2).getName(), is("Many Class 3"));
+ assertThat(modelObject.get(0).getName(), is("Many Remote Object 1_1"));
+ assertThat(modelObject.get(1).getName(), is("Many Remote Object 1_2"));
+ assertThat(modelObject.get(2).getName(), is("Many Remote Object 1_3"));
- consume.request();
+ consumer1.retrieve(false);
testListener.waitForResponse(2, 2);
- consume.request();
+ consumer1.retrieve(false);
testListener.waitForResponse(3, 3);
assertThat(modelObject.size(), is(5));
- assertThat(modelObject.get(3).getName(), is("Many Class 4"));
- assertThat(modelObject.get(4).getName(), is("Many Class 5"));
+ assertThat(modelObject.get(3).getName(), is("Many Remote Object 1_4"));
+ assertThat(modelObject.get(4).getName(), is("Many Remote Object 1_5"));
}
- @Test
- public void testRemoteProcessConsumerVsSubscriber() throws CoreException {
- EPackage parent = EcoreFactory.eINSTANCE.createEPackage();
+ class TestNoPullFactory extends TestRemoteFactory {
+ @Override
+ public TestRemoteObject pull(EPackage parent, String remoteKey, IProgressMonitor monitor) throws CoreException {
+ fail("No retrieve call expected.");
+ return null;
+ }
- TestRemoteFactory factory = new TestRemoteFactory();
- TestListener<EClass> testListener1 = new TestListener<EClass>(factory);
- RemoteEmfConsumer<EPackage, EClass, TestRemoteObject, String, String> consumer1 = factory.consume(
- "Test Process", parent, "object1", null, testListener1);
- TestListener<EClass> testListener2 = new TestListener<EClass>(factory);
- RemoteEmfConsumer<EPackage, EClass, TestRemoteObject, String, String> consumer2 = factory.consume(
- "Test Process", parent, "object1", null, testListener2);
- consumer1.request();
- testListener1.waitForResponse(1, 1);
- assertThat(testListener1.createdObject.getName(), is("Object 1"));
- assertThat(consumer1.getModelObject().getName(), is("Object 1"));
- assertThat(consumer2.getModelObject(), nullValue());
-
- consumer2.subscribe();
- assertThat(testListener2.createdObject, nullValue());
- assertThat(consumer2.getModelObject().getName(), is("Object 1"));
+ @Override
+ public boolean isPullNeeded(EPackage parent, EClass object, TestRemoteObject remote) {
+ return false;
+ }
}
@Test
- public void testRemoteProcessMultipleConsumers() throws CoreException {
- EPackage parent = EcoreFactory.eINSTANCE.createEPackage();
+ public void testRemoteKeyNoPull() throws CoreException {
+ TestManagerHarness harness = new TestManagerHarness(new TestNoPullFactory()) {
+ @Override
+ RemoteEmfConsumer<EPackage, EClass, TestRemoteObject, String, String> createConsumer() {
+ return factory.getConsumerForRemoteKey(parent, "remoteKeyFor Object 1");
+ }
+ };
+ checkConsumer(harness.consumer, "remoteKeyFor Object 1", null, "localKeyFor Object 1", null);
+ harness.consumer.retrieve(false);
+ harness.listener.waitForResponse(0, 0);
+ checkConsumer(harness.consumer, "remoteKeyFor Object 1", null, "localKeyFor Object 1", null);
+ }
- TestRemoteFactory factory = new TestRemoteFactoryNoUpdate();
- TestListener<EClass> testListener1 = new TestListener<EClass>(factory);
- RemoteEmfConsumer<EPackage, EClass, TestRemoteObject, String, String> consumer1 = factory.consume(
- "Test Process", parent, "object1", null, testListener1);
- consumer1.request();
- testListener1.waitForResponse(1, 1);
- assertThat(testListener1.createdObject.getName(), is("Object 1"));
- assertThat(consumer1.getModelObject().getName(), is("Object 1"));
- assertThat(factory.getService().isActive(), not(true));
-
- TestListener<EClass> testListener2 = new TestListener<EClass>(factory);
- RemoteEmfConsumer<EPackage, EClass, TestRemoteObject, String, String> consumer2 = factory.consume(
- "Test Process", parent, "object1", null, testListener2);
- assertThat(consumer2.getModelObject(), nullValue());
- consumer2.request();
- testListener1.waitForResponse(2, 1);
- testListener2.waitForResponse(1, 0);
- assertThat(factory.getService().isActive(), not(true));
- assertThat(testListener2.createdObject, nullValue());
- assertThat(consumer2.getModelObject(), sameInstance(consumer1.getModelObject()));
- consumer1.request();
- testListener1.waitForResponse(3, 1);
- testListener2.waitForResponse(2, 0);
+ class TestNoPullForceOnlyFactory extends TestRemoteFactory {
+
+ @Override
+ public boolean isPullNeeded(EPackage parent, EClass object, TestRemoteObject remote) {
+ return false;
+ }
}
@Test
- public void testRemoteProcessFailure() throws CoreException {
- EPackage parent = EcoreFactory.eINSTANCE.createEPackage();
- TestRemoteFactory factory = new TestFailureFactory();
- TestListener<EClass> testListener = new TestListener<EClass>(factory);
- RemoteEmfConsumer<EPackage, EClass, TestRemoteObject, String, String> consume = factory.consume("Test Process",
- parent, "object1", null, testListener);
- consume.request();
- testListener.waitForFailure();
- assertThat(testListener.failure, is(errorStatus));
+ public void testRemoteKeyNoPullForceOnly() throws CoreException {
+ TestManagerHarness harness = new TestManagerHarness(new TestNoPullForceOnlyFactory()) {
+ @Override
+ RemoteEmfConsumer<EPackage, EClass, TestRemoteObject, String, String> createConsumer() {
+ return factory.getConsumerForRemoteKey(parent, "remoteKeyFor Object 1");
+ }
+ };
+ checkConsumer(harness.consumer, "remoteKeyFor Object 1", null, "localKeyFor Object 1", null);
+ harness.consumer.retrieve(false);
+ harness.listener.waitForResponse(0, 0);
+ checkConsumer(harness.consumer, "remoteKeyFor Object 1", null, "localKeyFor Object 1", null);
+ harness.consumer.retrieve(true);
+ harness.listener.waitForResponse(1, 1);
+ checkConsumer(harness.consumer, "remoteKeyFor Object 1", "Remote Object 1", "localKeyFor Object 1",
+ "Local Object 1");
+ }
+
+ class TestPullCreateOnlyFactory extends TestRemoteFactory {
+
+ @Override
+ public boolean isUpdateModelNeeded(EPackage parent, EClass object, TestRemoteObject remote) {
+ return false;
+ }
}
+ @Test
+ public void testRemoteKeyNoUpdate() throws CoreException {
+ TestManagerHarness harness = new TestManagerHarness(new TestPullCreateOnlyFactory()) {
+ @Override
+ RemoteEmfConsumer<EPackage, EClass, TestRemoteObject, String, String> createConsumer() {
+ return factory.getConsumerForRemoteKey(parent, "remoteKeyFor Object 1");
+ }
+ };
+ checkConsumer(harness.consumer, "remoteKeyFor Object 1", null, "localKeyFor Object 1", null);
+ harness.consumer.retrieve(false);
+ harness.listener.waitForResponse(1, 1);
+ checkConsumer(harness.consumer, "remoteKeyFor Object 1", "Remote Object 1", "localKeyFor Object 1",
+ "Local Object 1");
+ TestRemoteFactory.remote1.data = "newData";
+ harness.consumer.retrieve(false);
+ harness.listener.waitForResponse(2, 1);
+ EClass modelObject = harness.consumer.getModelObject();
+ checkConsumer(harness.consumer, "remoteKeyFor Object 1", "Remote Object 1", "localKeyFor Object 1",
+ "Local Object 1");
+ assertThat(modelObject.getInstanceTypeName(), is("localKeyFor Object 1"));
+ harness.consumer.retrieve(true);
+ harness.listener.waitForResponse(3, 2);
+ checkConsumer(harness.consumer, "remoteKeyFor Object 1", "Remote Object 1", "localKeyFor Object 1",
+ "Local Object 1");
+ assertThat(modelObject.getInstanceTypeName(), is("newData"));
+ }
}
diff --git a/org.eclipse.mylyn.reviews.core.tests/src/org/eclipse/mylyn/reviews/core/remote/RemoteEmfObserverTest.java b/org.eclipse.mylyn.reviews.core.tests/src/org/eclipse/mylyn/reviews/core/remote/RemoteEmfObserverTest.java
new file mode 100644
index 00000000..fb9d7857
--- /dev/null
+++ b/org.eclipse.mylyn.reviews.core.tests/src/org/eclipse/mylyn/reviews/core/remote/RemoteEmfObserverTest.java
@@ -0,0 +1,80 @@
+/*******************************************************************************
+ * Copyright (c) 2011 GitHub Inc. 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:
+ * GitHub Inc. - initial API and implementation
+ * Tasktop Technologies - improvements
+ *******************************************************************************/
+package org.eclipse.mylyn.reviews.core.remote;
+
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.nullValue;
+import static org.hamcrest.Matchers.sameInstance;
+import static org.junit.Assert.assertThat;
+
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.EPackage;
+import org.eclipse.emf.ecore.EcoreFactory;
+import org.eclipse.mylyn.reviews.core.spi.remote.emf.RemoteEmfConsumer;
+import org.junit.Test;
+
+/**
+ * @author Miles Parker
+ */
+public class RemoteEmfObserverTest {
+ @Test
+ public void testListeners() {
+ TestRemoteFactory factory = new TestRemoteFactory();
+ EPackage parent = EcoreFactory.eINSTANCE.createEPackage();
+ RemoteEmfConsumer<EPackage, EClass, TestRemoteObject, String, String> consumer1 = factory.getConsumerForRemoteKey(
+ parent, "remoteKeyFor Object 1");
+ RemoteEmfConsumer<EPackage, EClass, TestRemoteObject, String, String> consumer2 = factory.getConsumerForRemoteKey(
+ parent, "remoteKeyFor Object 2");
+ assertThat(consumer1, not(sameInstance(consumer2)));
+ TestRemoteEmfObserver<EPackage, EClass> listener1 = new TestRemoteEmfObserver<EPackage, EClass>(consumer1);
+ TestRemoteEmfObserver<EPackage, EClass> listener2 = new TestRemoteEmfObserver<EPackage, EClass>();
+ consumer1.retrieve(false);
+ listener1.waitForResponse(1, 1);
+ assertThat(listener2.responded, is(0));
+ assertThat(listener2.updated, is(0));
+ listener2.setConsumer(consumer1);
+ consumer1.retrieve(false);
+ listener1.waitForResponse(2, 2);
+ assertThat(listener2.responded, is(1));
+ assertThat(listener2.updated, is(1));
+
+ consumer1.removeObserver(listener1);
+ assertThat(listener1.getConsumer(), nullValue());
+ assertThat(listener2.getConsumer() == consumer1, is(true));
+ consumer1.retrieve(false);
+ listener2.waitForResponse(2, 2);
+ assertThat(listener1.responded, is(2));
+ assertThat(listener1.updated, is(2));
+
+ consumer2.addObserver(listener1);
+ assertThat(listener1.getConsumer() == consumer2, is(true));
+ assertThat(listener2.getConsumer() == consumer1, is(true));
+ consumer1.retrieve(false);
+ listener2.waitForResponse(3, 3);
+ assertThat(listener1.responded, is(2));
+ assertThat(listener1.updated, is(2));
+ consumer2.retrieve(false);
+ listener1.waitForResponse(3, 3);
+ assertThat(listener1.responded, is(3));
+ assertThat(listener1.updated, is(3));
+
+ listener2.setConsumer(consumer2);
+ assertThat(listener1.getConsumer() == consumer2, is(true));
+ assertThat(listener2.getConsumer() == consumer2, is(true));
+ consumer2.retrieve(false);
+ listener2.waitForResponse(4, 4);
+ assertThat(listener1.responded, is(4));
+ assertThat(listener1.updated, is(4));
+ }
+
+}
diff --git a/org.eclipse.mylyn.reviews.core.tests/src/org/eclipse/mylyn/reviews/core/remote/RemoteServiceTest.java b/org.eclipse.mylyn.reviews.core.tests/src/org/eclipse/mylyn/reviews/core/remote/RemoteServiceTest.java
index a64ad858..64781413 100644
--- a/org.eclipse.mylyn.reviews.core.tests/src/org/eclipse/mylyn/reviews/core/remote/RemoteServiceTest.java
+++ b/org.eclipse.mylyn.reviews.core.tests/src/org/eclipse/mylyn/reviews/core/remote/RemoteServiceTest.java
@@ -42,12 +42,12 @@ public class RemoteServiceTest {
IStatus status;
@Override
- protected void retrieve(IProgressMonitor monitor) throws CoreException {
+ protected void pull(boolean force, IProgressMonitor monitor) throws CoreException {
retrieve = true;
}
@Override
- protected void apply() {
+ protected void applyModel(boolean force) {
apply = true;
}
@@ -88,7 +88,7 @@ public class RemoteServiceTest {
public void testExecute() throws CoreException {
JobRemoteService remoteService = new JobRemoteService();
Consumer consumer = new Consumer();
- remoteService.execute(consumer);
+ remoteService.retrieve(consumer, false);
consumer.waitForDone();
assertThat(consumer.status.getSeverity(), is(IStatus.OK));
assertThat(consumer.retrieve, is(true));
@@ -100,7 +100,7 @@ public class RemoteServiceTest {
JobRemoteService remoteService = new JobRemoteService();
Consumer consumer = new Consumer();
consumer.async = false;
- remoteService.execute(consumer);
+ remoteService.retrieve(consumer, false);
consumer.waitForDone();
assertThat(consumer.status.getSeverity(), is(IStatus.OK));
assertThat(consumer.retrieve, is(true));
@@ -109,7 +109,7 @@ public class RemoteServiceTest {
class BrokenConsumer extends Consumer {
@Override
- protected void retrieve(IProgressMonitor monitor) throws CoreException {
+ protected void pull(boolean force, IProgressMonitor monitor) throws CoreException {
throw new CoreException(new Status(IStatus.ERROR, "blah", "Whoops!"));
}
}
@@ -118,7 +118,7 @@ public class RemoteServiceTest {
public void testExecuteCoreException() throws CoreException {
JobRemoteService remoteService = new JobRemoteService();
Consumer consumer = new BrokenConsumer();
- remoteService.execute(consumer);
+ remoteService.retrieve(consumer, false);
consumer.waitForDone();
assertThat(consumer.status.getSeverity(), is(IStatus.WARNING));
assertThat(consumer.retrieve, is(false));
@@ -130,7 +130,7 @@ public class RemoteServiceTest {
JobRemoteService remoteService = new JobRemoteService();
Consumer consumer = new BrokenConsumer();
consumer.async = false;
- remoteService.execute(consumer);
+ remoteService.retrieve(consumer, false);
consumer.waitForDone();
assertThat(consumer.status.getSeverity(), is(IStatus.ERROR));
assertThat(consumer.retrieve, is(false));
@@ -155,15 +155,15 @@ public class RemoteServiceTest {
Thread retrieveThread;
@Override
- protected void retrieve(IProgressMonitor monitor) throws CoreException {
+ protected void pull(boolean force, IProgressMonitor monitor) throws CoreException {
retrieveThread = Thread.currentThread();
- super.retrieve(monitor);
+ super.pull(force, monitor);
}
@Override
- protected void apply() {
+ protected void applyModel(boolean force) {
modelThread = Thread.currentThread();
- super.apply();
+ super.applyModel(force);
}
}
@@ -171,7 +171,7 @@ public class RemoteServiceTest {
public void testExecuteModelThread() throws CoreException {
JobRemoteService remoteService = new ThreadedService();
ModelThreadConsumer consumer = new ModelThreadConsumer();
- remoteService.execute(consumer);
+ remoteService.retrieve(consumer, false);
consumer.waitForDone();
assertThat(consumer.modelThread.getName(), is("Test Thread"));
assertThat(consumer.retrieveThread.getName(), not("Test Thread"));
@@ -187,7 +187,7 @@ public class RemoteServiceTest {
JobRemoteService remoteService = new ThreadedService();
ModelThreadConsumer consumer = new ModelThreadConsumer();
consumer.async = false;
- remoteService.execute(consumer);
+ remoteService.retrieve(consumer, false);
consumer.waitForDone();
assertThat(consumer.modelThread.getName(), is("Test Thread"));
assertThat(consumer.retrieveThread.getName(), not("Test Thread"));
diff --git a/org.eclipse.mylyn.reviews.core.tests/src/org/eclipse/mylyn/reviews/core/remote/TestIRemoteEmfObserver.java b/org.eclipse.mylyn.reviews.core.tests/src/org/eclipse/mylyn/reviews/core/remote/TestIRemoteEmfObserver.java
new file mode 100644
index 00000000..cff1dc10
--- /dev/null
+++ b/org.eclipse.mylyn.reviews.core.tests/src/org/eclipse/mylyn/reviews/core/remote/TestIRemoteEmfObserver.java
@@ -0,0 +1,104 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Tasktop Technologies 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:
+ * Tasktop Technologies - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylyn.reviews.core.remote;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.mylyn.reviews.core.spi.remote.emf.AbstractRemoteEmfFactory;
+import org.eclipse.mylyn.reviews.core.spi.remote.emf.IRemoteEmfObserver;
+
+final class TestIRemoteEmfObserver<P extends EObject, T> implements IRemoteEmfObserver<P, T> {
+
+ static final int TEST_TIMEOUT = 100;
+
+ T createdObject;
+
+ int updated;
+
+ int responded;
+
+ IStatus failure;
+
+ AbstractRemoteEmfFactory<?, ?, ?, ?, ?> factory;
+
+ TestIRemoteEmfObserver(AbstractRemoteEmfFactory<?, ?, ?, ?, ?> factory) {
+ this.factory = factory;
+ }
+
+ public void created(P object, T child) {
+ createdObject = child;
+ }
+
+ public void updating(P parent, T object) {
+ }
+
+ public void updated(P object, T child, boolean modified) {
+ responded++;
+ if (modified) {
+ updated++;
+ }
+ }
+
+ public void failed(P object, T child, IStatus status) {
+ failure = status;
+ }
+
+ protected void waitForResponse(int response, int update) {
+ long delay;
+ delay = 0;
+ while (delay < TEST_TIMEOUT) {
+ if (responded < response) {
+ try {
+ Thread.sleep(10);
+ delay += 10;
+ } catch (InterruptedException e) {
+ }
+ } else {
+ break;
+ }
+ }
+ try {
+ //wait extra to ensure there aren't remaining jobs
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ }
+ assertThat("Wrong # responses", responded, is(response));
+ assertThat("Wrong # updates", updated, is(update));
+ if (factory != null) {
+ assertThat(factory.getService().isActive(), is(false));
+ }
+ }
+
+ protected void waitForFailure() {
+ long delay = 0;
+ while (delay < TEST_TIMEOUT) {
+ if (failure == null) {
+ try {
+ Thread.sleep(10);
+ delay += 10;
+ } catch (InterruptedException e) {
+ }
+ } else {
+ break;
+ }
+ }
+ }
+
+ void clear() {
+ createdObject = null;
+ updated = 0;
+ failure = null;
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.mylyn.reviews.core.tests/src/org/eclipse/mylyn/reviews/core/remote/TestRemoteEmfObserver.java b/org.eclipse.mylyn.reviews.core.tests/src/org/eclipse/mylyn/reviews/core/remote/TestRemoteEmfObserver.java
new file mode 100644
index 00000000..5385e5f5
--- /dev/null
+++ b/org.eclipse.mylyn.reviews.core.tests/src/org/eclipse/mylyn/reviews/core/remote/TestRemoteEmfObserver.java
@@ -0,0 +1,106 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Tasktop Technologies 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:
+ * Tasktop Technologies - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylyn.reviews.core.remote;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.mylyn.reviews.core.spi.remote.emf.RemoteEmfConsumer;
+import org.eclipse.mylyn.reviews.core.spi.remote.emf.RemoteEmfObserver;
+
+final class TestRemoteEmfObserver<P extends EObject, T> extends RemoteEmfObserver<P, T> {
+
+ static final int TEST_TIMEOUT = 100;
+
+ T createdObject;
+
+ int updated;
+
+ int responded;
+
+ IStatus failure;
+
+ public TestRemoteEmfObserver() {
+ }
+
+ public TestRemoteEmfObserver(RemoteEmfConsumer<P, T, ?, ?, ?> consumer) {
+ super(consumer);
+ }
+
+ @Override
+ public void created(P object, T child) {
+ createdObject = child;
+ }
+
+ @Override
+ public void updating(P parent, T object) {
+ }
+
+ @Override
+ public void updated(P object, T child, boolean modified) {
+ responded++;
+ if (modified) {
+ updated++;
+ }
+ }
+
+ @Override
+ public void failed(P object, T child, IStatus status) {
+ failure = status;
+ }
+
+ protected void waitForResponse(int response, int update) {
+ long delay;
+ delay = 0;
+ while (delay < TEST_TIMEOUT) {
+ if (responded < response) {
+ try {
+ Thread.sleep(10);
+ delay += 10;
+ } catch (InterruptedException e) {
+ }
+ } else {
+ break;
+ }
+ }
+ try {
+ //wait extra to ensure there aren't remaining jobs
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ }
+ assertThat("Wrong # responses", responded, is(response));
+ assertThat("Wrong # updates", updated, is(update));
+ }
+
+ protected void waitForFailure() {
+ long delay = 0;
+ while (delay < TEST_TIMEOUT) {
+ if (failure == null) {
+ try {
+ Thread.sleep(10);
+ delay += 10;
+ } catch (InterruptedException e) {
+ }
+ } else {
+ break;
+ }
+ }
+ }
+
+ void clear() {
+ createdObject = null;
+ updated = 0;
+ failure = null;
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.mylyn.reviews.core.tests/src/org/eclipse/mylyn/reviews/core/remote/TestRemoteFactory.java b/org.eclipse.mylyn.reviews.core.tests/src/org/eclipse/mylyn/reviews/core/remote/TestRemoteFactory.java
new file mode 100644
index 00000000..d28bc0eb
--- /dev/null
+++ b/org.eclipse.mylyn.reviews.core.tests/src/org/eclipse/mylyn/reviews/core/remote/TestRemoteFactory.java
@@ -0,0 +1,77 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Tasktop Technologies 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:
+ * Tasktop Technologies - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylyn.reviews.core.remote;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.EPackage;
+import org.eclipse.emf.ecore.EcoreFactory;
+import org.eclipse.emf.ecore.EcorePackage;
+import org.eclipse.mylyn.reviews.core.spi.remote.emf.AbstractRemoteEmfFactory;
+
+class TestRemoteFactory extends AbstractRemoteEmfFactory<EPackage, EClass, TestRemoteObject, String, String> {
+
+ static TestRemoteObject remote1 = new TestRemoteObject("Remote Object 1");
+
+ static TestRemoteObject remote2 = new TestRemoteObject("Remote Object 2");
+
+ static Map<String, TestRemoteObject> remoteForKey = new HashMap<String, TestRemoteObject>();
+
+ {
+ remoteForKey.put("remoteKeyFor Object 1", remote1);
+ remoteForKey.put("remoteKeyFor Object 2", remote2);
+ }
+
+ public TestRemoteFactory() {
+ super(new TestRemoteFactoryProvider(), EcorePackage.Literals.EPACKAGE__ECLASSIFIERS,
+ EcorePackage.Literals.ECLASSIFIER__INSTANCE_CLASS_NAME);
+ }
+
+ @Override
+ public TestRemoteObject pull(EPackage parent, String remoteKey, IProgressMonitor monitor) throws CoreException {
+ return remoteForKey.get(remoteKey);
+ }
+
+ @Override
+ protected EClass createModel(EPackage parent, TestRemoteObject remoteObject) {
+ EClass clazz = EcoreFactory.eINSTANCE.createEClass();
+ clazz.setName(remoteObject.name.replaceAll("Remote", "Local"));
+ return clazz;
+ }
+
+ @Override
+ public boolean updateModel(EPackage parent, EClass object, TestRemoteObject remoteObject) {
+ if (remoteObject != null) {
+ object.setInstanceTypeName(remoteObject.data);
+ }
+ return true;
+ }
+
+ @Override
+ public String getRemoteKey(TestRemoteObject remoteObject) {
+ if (remoteObject == remote1) {
+ return "remoteKeyFor Object 1";
+ } else if (remoteObject == remote2) {
+ return "remoteKeyFor Object 2";
+ }
+ throw new RuntimeException();
+ }
+
+ @Override
+ public String getLocalKeyForRemoteKey(String remoteKey) {
+ return remoteKey.replace("remote", "local");
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.mylyn.reviews.core.tests/src/org/eclipse/mylyn/reviews/core/remote/TestRemoteFactoryProvider.java b/org.eclipse.mylyn.reviews.core.tests/src/org/eclipse/mylyn/reviews/core/remote/TestRemoteFactoryProvider.java
new file mode 100644
index 00000000..5b1abdd4
--- /dev/null
+++ b/org.eclipse.mylyn.reviews.core.tests/src/org/eclipse/mylyn/reviews/core/remote/TestRemoteFactoryProvider.java
@@ -0,0 +1,21 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Tasktop Technologies 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:
+ * Tasktop Technologies - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylyn.reviews.core.remote;
+
+import org.eclipse.mylyn.reviews.core.spi.remote.AbstractRemoteFactoryProvider;
+import org.eclipse.mylyn.reviews.core.spi.remote.JobRemoteService;
+
+class TestRemoteFactoryProvider extends AbstractRemoteFactoryProvider {
+ public TestRemoteFactoryProvider() {
+ setService(new JobRemoteService());
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.mylyn.reviews.core.tests/src/org/eclipse/mylyn/reviews/core/remote/TestRemoteObject.java b/org.eclipse.mylyn.reviews.core.tests/src/org/eclipse/mylyn/reviews/core/remote/TestRemoteObject.java
new file mode 100644
index 00000000..36a3ca11
--- /dev/null
+++ b/org.eclipse.mylyn.reviews.core.tests/src/org/eclipse/mylyn/reviews/core/remote/TestRemoteObject.java
@@ -0,0 +1,26 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Tasktop Technologies 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:
+ * Tasktop Technologies - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylyn.reviews.core.remote;
+
+class TestRemoteObject {
+ String name;
+
+ String data;
+
+ TestRemoteObject(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.mylyn.reviews.core/META-INF/MANIFEST.MF b/org.eclipse.mylyn.reviews.core/META-INF/MANIFEST.MF
index 4e4a358c..ea1fa845 100644
--- a/org.eclipse.mylyn.reviews.core/META-INF/MANIFEST.MF
+++ b/org.eclipse.mylyn.reviews.core/META-INF/MANIFEST.MF
@@ -9,12 +9,13 @@ Require-Bundle: org.eclipse.core.runtime,
org.eclipse.mylyn.tasks.core;bundle-version="3.9.0",
org.eclipse.emf.common;bundle-version="2.5.0",
org.eclipse.emf.ecore;bundle-version="2.5.0",
- org.eclipse.emf.ecore.xmi;bundle-version="2.5.0",
- org.eclipse.emf.edit;bundle-version="2.5.0"
+ org.eclipse.emf.ecore.xmi;bundle-version="2.5.0"
Bundle-ActivationPolicy: lazy
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Export-Package: org.eclipse.mylyn.reviews.core.model;x-friends:="org.eclipse.mylyn.reviews.edit,org.eclipse.mylyn.reviews.ui",
- org.eclipse.mylyn.reviews.core.spi.remote;x-friends:="org.eclipse.mylyn.reviews.ui",
- org.eclipse.mylyn.reviews.core.spi.remote.emf;x-friends:="org.eclipse.mylyn.reviews.ui",
+ org.eclipse.mylyn.reviews.core.spi.remote;x-friends:="org.eclipse.mylyn.reviews.ui,org.eclipse.mylyn.reviews.edit",
+ org.eclipse.mylyn.reviews.core.spi.remote.emf;x-friends:="org.eclipse.mylyn.reviews.ui,org.eclipse.mylyn.reviews.edit",
+ org.eclipse.mylyn.reviews.core.spi.remote.review;x-friends:="org.eclipse.mylyn.reviews.ui,org.eclipse.mylyn.reviews.edit",
+ org.eclipse.mylyn.reviews.internal.core;x-friends:="org.eclipse.mylyn.reviews.ui",
org.eclipse.mylyn.reviews.internal.core.model;x-friends:="org.eclipse.mylyn.reviews.edit"
Bundle-Vendor: Eclipse Mylyn
diff --git a/org.eclipse.mylyn.reviews.core/src/org/eclipse/mylyn/reviews/core/spi/remote/AbstractRemoteConsumer.java b/org.eclipse.mylyn.reviews.core/src/org/eclipse/mylyn/reviews/core/spi/remote/AbstractRemoteConsumer.java
index 78841461..31323b04 100644
--- a/org.eclipse.mylyn.reviews.core/src/org/eclipse/mylyn/reviews/core/spi/remote/AbstractRemoteConsumer.java
+++ b/org.eclipse.mylyn.reviews.core/src/org/eclipse/mylyn/reviews/core/spi/remote/AbstractRemoteConsumer.java
@@ -28,21 +28,26 @@ public abstract class AbstractRemoteConsumer {
* Override to perform the request against remote API, storing the results of that request as state (e.g. as
* member(s) field of an implementing class). May be long-running and should be able to safely fail.
*
+ * @param force
+ * pull from remote even when API doesn't require
* @param monitor
* @throws CoreException
*/
- protected abstract void retrieve(IProgressMonitor monitor) throws CoreException;
+ protected abstract void pull(boolean force, IProgressMonitor monitor) throws CoreException;
/**
* Override to apply the remotely obtained state to a local model object. This method is expected to execute
* <em>very</em> quickly, as the typical implementation will occur on the UI thread.
*
+ * @param force
+ * apply the changes even when API doesn't require
* @throws CoreException
*/
- protected abstract void apply();
+ protected abstract void applyModel(boolean force);
/**
- * Provides notification of failure. See {@link AbstractRemoteService#execute(AbstractRemoteProcess)} for details.
+ * Provides notification of failure. See {@link AbstractRemoteService#retrieve(AbstractRemoteProcess, boolean)} for
+ * details.
*/
public abstract void notifyDone(IStatus status);
@@ -64,4 +69,12 @@ public abstract class AbstractRemoteConsumer {
* @return
*/
public abstract String getDescription();
+
+ /**
+ * Returns {@link #getDescription()}.
+ */
+ @Override
+ public String toString() {
+ return getDescription();
+ }
}
diff --git a/org.eclipse.mylyn.reviews.core/src/org/eclipse/mylyn/reviews/core/spi/remote/AbstractRemoteFactoryProvider.java b/org.eclipse.mylyn.reviews.core/src/org/eclipse/mylyn/reviews/core/spi/remote/AbstractRemoteFactoryProvider.java
index 10f8ef0a..ab6be542 100644
--- a/org.eclipse.mylyn.reviews.core/src/org/eclipse/mylyn/reviews/core/spi/remote/AbstractRemoteFactoryProvider.java
+++ b/org.eclipse.mylyn.reviews.core/src/org/eclipse/mylyn/reviews/core/spi/remote/AbstractRemoteFactoryProvider.java
@@ -18,17 +18,21 @@ package org.eclipse.mylyn.reviews.core.spi.remote;
*/
public abstract class AbstractRemoteFactoryProvider {
- private final AbstractRemoteService service;
+ private AbstractRemoteService service;
- public AbstractRemoteFactoryProvider(JobRemoteService service) {
- this.service = service;
+ public void modelExec(Runnable runnable, boolean block) {
+ if (service != null) {
+ service.modelExec(runnable, block);
+ } else {
+ throw new RuntimeException("Internal Error: Connector must supply a service for execution.");
+ }
}
public AbstractRemoteService getService() {
return service;
}
- public void dispose() {
- service.dispose();
+ public void setService(AbstractRemoteService service) {
+ this.service = service;
}
}
diff --git a/org.eclipse.mylyn.reviews.core/src/org/eclipse/mylyn/reviews/core/spi/remote/AbstractRemoteService.java b/org.eclipse.mylyn.reviews.core/src/org/eclipse/mylyn/reviews/core/spi/remote/AbstractRemoteService.java
index 212b5454..a557fe07 100644
--- a/org.eclipse.mylyn.reviews.core/src/org/eclipse/mylyn/reviews/core/spi/remote/AbstractRemoteService.java
+++ b/org.eclipse.mylyn.reviews.core/src/org/eclipse/mylyn/reviews/core/spi/remote/AbstractRemoteService.java
@@ -21,17 +21,17 @@ public abstract class AbstractRemoteService {
/**
* Implementors should invoke the the
- * {@link AbstractRemoteConsumer#retrieve(org.eclipse.core.runtime.IProgressMonitor)} and
- * {@link AbstractRemoteConsumer#apply()} methods for the supplied process in the following well defined way. This
- * method is expected to return very quickly and <em>must not block</em> under any circumstances, as it must be
- * safely callable from the UI thread. Implementors should support one full cycle of invocation:
+ * {@link AbstractRemoteConsumer#pull(boolean, org.eclipse.core.runtime.IProgressMonitor)} and
+ * {@link AbstractRemoteConsumer#applyModel(boolean)} methods for the supplied process in the following well defined
+ * way. This method is expected to return very quickly and <em>must not block</em> under any circumstances, as it
+ * must be safely callable from the UI thread. Implementors should support one full cycle of invocation:
* <ol>
- * <li>The retrieve phase of the process is invoked. This invocation must be asynchronous if
+ * <li>The pull phase of the process is invoked. This invocation must be asynchronous if
* {@link AbstractRemoteConsumer#isAsynchronous()} is true.</li>
* <li>If a failure occurs or a core exception is thrown during the request phase,
* {@link AbstractRemoteConsumer#notifyDone(org.eclipse.core.runtime.IStatus)} is invoked with the exception.</li>
- * <li>Otherwise, when the request process returns, the {@link AbstractRemoteConsumer#apply()} phase of the process
- * is invoked. (In the case of the UI implementations, this might occur on the UI thread.)</li>
+ * <li>Otherwise, when the request process returns, the {@link AbstractRemoteConsumer#applyModel(boolean)} phase of
+ * the process is invoked. (In the case of the UI implementations, this might occur on the UI thread.)</li>
* <li>If a failure occurs during the create phase, notifyDone may optionally be invoked to report the failure.</li>
* <li>If both phases complete successfully,
* {@link AbstractRemoteConsumer#notifyDone(org.eclipse.core.runtime.IStatus)} is invoked on the process with an OK
@@ -39,16 +39,31 @@ public abstract class AbstractRemoteService {
* </ol>
*
* @param process
+ * The consumer process to execute
+ * @param force
+ * Invoke the pull and apply processes even if the relevant APIs indicate that they are not needed.
*/
- public abstract void execute(final AbstractRemoteConsumer process);
+ public abstract void retrieve(final AbstractRemoteConsumer process, boolean force);
/**
* Supports apply and notification services executed against a specific thread. (For example, the Remote Ui Service
* overrides this to force all model update events to occur on the UI thread, as best EMF practices require.)
*
* @param runnable
+ * @param block
+ * true if the model execution should block until complete, false if it can complete in seperate thread
*/
- public abstract void modelExec(Runnable runnable);
+ public abstract void modelExec(Runnable runnable, boolean block);
+
+ /**
+ * Supports apply and notification services executed against a specific thread. (For example, the Remote Ui Service
+ * overrides this to force all model update events to occur on the UI thread, as best EMF practices require.)
+ *
+ * @param runnable
+ */
+ public void modelExec(Runnable runnable) {
+ modelExec(runnable, true);
+ }
/**
* Returns true if any consumers are currently being managed.
diff --git a/org.eclipse.mylyn.reviews.core/src/org/eclipse/mylyn/reviews/core/spi/remote/JobRemoteService.java b/org.eclipse.mylyn.reviews.core/src/org/eclipse/mylyn/reviews/core/spi/remote/JobRemoteService.java
index 6ee9859a..ac55eea7 100644
--- a/org.eclipse.mylyn.reviews.core/src/org/eclipse/mylyn/reviews/core/spi/remote/JobRemoteService.java
+++ b/org.eclipse.mylyn.reviews.core/src/org/eclipse/mylyn/reviews/core/spi/remote/JobRemoteService.java
@@ -39,61 +39,65 @@ public class JobRemoteService extends AbstractRemoteService {
}
/**
- * Fully implements the {@link AbstractRemoteService#execute(AbstractRemoteConsumer)} contract:
+ * Fully implements the {@link AbstractRemoteService#retrieve(AbstractRemoteConsumer, boolean)} contract:
* <ol>
* <li>If {@link AbstractRemoteConsumer#isAsynchronous()}, creates and runs a job to
- * {@link AbstractRemoteConsumer#retrieve(org.eclipse.core.runtime.IProgressMonitor)} the remote API data.
+ * {@link AbstractRemoteConsumer#pull(boolean, org.eclipse.core.runtime.IProgressMonitor)} the remote API data.
* Otherwise, simply calls retrieve.</li>
* <li>If a failure occurs, calls {@link AbstractRemoteConsumer#notifyDone(org.eclipse.core.runtime.IStatus)}.</li>
- * <li>Invokes {@link AbstractRemoteConsumer#apply()} inside of a modelExec call, so that extending classes can
- * manage thread context.</li>
+ * <li>Invokes {@link AbstractRemoteConsumer#applyModel(boolean)} inside of a modelExec call, so that extending
+ * classes can manage thread context.</li>
* <li>(No notification occurs in the case of an error while applying.)</li>
* </ol>
*/
@Override
- public void execute(final AbstractRemoteConsumer process) {
+ public void retrieve(final AbstractRemoteConsumer process, final boolean force) {
if (process.isAsynchronous()) {
- final Job job = new Job(process.getDescription()) {
+ new Thread() { //We create a new temporary thread here just to ensure that retrieve returns as quickly as possible
@Override
- protected IStatus run(IProgressMonitor monitor) {
- try {
- process.retrieve(monitor);
- } catch (CoreException e) {
-// return e.getStatus();
- return new Status(IStatus.WARNING, "org.eclipse.mylyn.reviews.core", "Couldn't update model.",
- e);
- } catch (OperationCanceledException e) {
- return Status.CANCEL_STATUS;
- }
- return Status.OK_STATUS;
- }
- };
- job.addJobChangeListener(new JobChangeAdapter() {
- @Override
- public void done(final IJobChangeEvent event) {
- modelExec(new Runnable() {
- public void run() {
- final IStatus result = event.getResult();
- if (result.isOK()) {
- process.apply();
+ public void run() {
+ final Job job = new Job(process.getDescription()) {
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ try {
+ process.pull(force, monitor);
+ } catch (CoreException e) {
+ return new Status(IStatus.WARNING, "org.eclipse.mylyn.reviews.core",
+ "Couldn't update model.", e);
+ } catch (OperationCanceledException e) {
+ return Status.CANCEL_STATUS;
}
- process.notifyDone(event.getResult());
+ return Status.OK_STATUS;
+ }
+ };
+ job.addJobChangeListener(new JobChangeAdapter() {
+ @Override
+ public void done(final IJobChangeEvent event) {
+ modelExec(new Runnable() {
+ public void run() {
+ final IStatus result = event.getResult();
+ if (result.isOK()) {
+ process.applyModel(force);
+ }
+ process.notifyDone(event.getResult());
+ }
+ });
}
});
+ addJob(job);
+ job.schedule();
}
- });
- addJob(job);
- job.schedule();
+ }.start();
} else {
try {
- process.retrieve(new NullProgressMonitor());
+ process.pull(force, new NullProgressMonitor());
} catch (CoreException e) {
process.notifyDone(e.getStatus());
return;
}
modelExec(new Runnable() {
public void run() {
- process.apply();
+ process.applyModel(force);
process.notifyDone(Status.OK_STATUS);
}
});
@@ -144,7 +148,7 @@ public class JobRemoteService extends AbstractRemoteService {
}
@Override
- public void modelExec(Runnable runnable) {
+ public void modelExec(Runnable runnable, boolean block) {
runnable.run();
}
}
diff --git a/org.eclipse.mylyn.reviews.core/src/org/eclipse/mylyn/reviews/core/spi/remote/emf/AbstractRemoteEmfFactory.java b/org.eclipse.mylyn.reviews.core/src/org/eclipse/mylyn/reviews/core/spi/remote/emf/AbstractRemoteEmfFactory.java
index 3648f851..44a949c9 100644
--- a/org.eclipse.mylyn.reviews.core/src/org/eclipse/mylyn/reviews/core/spi/remote/emf/AbstractRemoteEmfFactory.java
+++ b/org.eclipse.mylyn.reviews.core/src/org/eclipse/mylyn/reviews/core/spi/remote/emf/AbstractRemoteEmfFactory.java
@@ -12,124 +12,268 @@
package org.eclipse.mylyn.reviews.core.spi.remote.emf;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
-import org.eclipse.emf.common.notify.impl.AdapterImpl;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
+import org.eclipse.mylyn.reviews.core.spi.remote.AbstractRemoteFactoryProvider;
import org.eclipse.mylyn.reviews.core.spi.remote.AbstractRemoteService;
-import org.eclipse.mylyn.reviews.core.spi.remote.emf.RemoteEmfConsumer.IObserver;
/**
* Manages a set of model objects representing remote API analogs. While the factory can be accessed directly, generally
* factory services should be requested from a consumer as this ensures that remote and local calls are handled
* appropriately.
* <p>
- * Factory users should usually obtain a fully managed {@link RemoteEmfConsumer} by calling the
- * {@link #consume(String, EObject, Object, IObserver)} method(s). They can then request pulls from the remote API and
- * be notified whenever the model objects are updated. A factory can support unlimited consumers.
+ * Factory users should usually obtain a fully managed {@link RemoteEmfConsumer} by calling the {@link #getConsumer()}
+ * method(s). They can then request model object creation, updates and retrieval from the consumer. Every model object
+ * can have one and only one consumer, even if that consumer was first obtained from the factory using only a remote key
+ * or object. This allows consumers to safely use a single consumer throughout the remote and local model object
+ * life-cycle and to obtain a consumer at any point. Consumers do not need to be disposed or managed explicitly.
* </p>
* <p>
- * Factory implementors should override the {@link AbstractRemoteEmfFactory#retrieve(Object, IProgressMonitor)},
- * {@link #create(EObject, Object)} and {@link #update(EObject, Object, Object)} methods as appropriate.
+ * Factory implementors should override the {@link AbstractRemoteEmfFactory#pull(EObject, Object, IProgressMonitor)},
+ * {@link #createModel(EObject, Object)} and {@link #updateModel(EObject, Object, Object)} methods as appropriate.
* </p>
* <p>
- * Typically, model objects are created using the {@link AbstractRemoteEmfFactory#retrieve(Object, IProgressMonitor)}
- * method. EMF objects can be also be obtained synchronously from an existing remote object using the
+ * Typically, model objects are created using the {@link AbstractRemoteEmfFactory#createModel(EObject, Object)} method.
+ * EMF objects can be also be obtained synchronously from an existing remote object using the
* {@link #get(EObject, Object)} method. Remote objects can be obtained synchronously for appropriate remote keys using
- * {@link #retrieve(Object, IProgressMonitor)}.
+ * {@link #pull(EObject, Object, IProgressMonitor)}.
* </p>
*
* @author Miles Parker
*/
public abstract class AbstractRemoteEmfFactory<EParentObjectType extends EObject, EObjectType, RemoteType, RemoteKeyType, LocalKeyType> {
- Map<EObjectType, RemoteType> remoteForObject = new HashMap<EObjectType, RemoteType>();
+ class UniqueLocalReference<P, L> {
+ P parent;
- Map<EObjectType, RemoteKeyType> remoteKeyForObject = new HashMap<EObjectType, RemoteKeyType>();
+ L localKey;
- Map<RemoteType, EObjectType> objectForRemote = new HashMap<RemoteType, EObjectType>();
+ UniqueLocalReference(P parent, L localKey) {
+ if (parent == null || localKey == null) {
+ throw new RuntimeException("Internal Exception: Parent and local keys must be specified.");
+ }
+ this.parent = parent;
+ this.localKey = localKey;
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ if (object instanceof AbstractRemoteEmfFactory.UniqueLocalReference) {
+ @SuppressWarnings("rawtypes")
+ UniqueLocalReference reference = (UniqueLocalReference) object; //Cannot test for generic types because of erasure
+ return parent.equals(reference.parent) && localKey.equals(reference.localKey);
+ }
+ return false;
+ }
- Map<LocalKeyType, EObjectType> objectForLocalKey = new HashMap<LocalKeyType, EObjectType>();
+ @Override
+ public int hashCode() {
+ return parent.hashCode() + 31 * localKey.hashCode();
+ }
+ }
- Map<RemoteEmfConsumer.IObserver<EObjectType>, AdapterImpl> adapterForListener = new HashMap<IObserver<EObjectType>, AdapterImpl>();
+ private final Map<UniqueLocalReference<EParentObjectType, LocalKeyType>, RemoteEmfConsumer<EParentObjectType, EObjectType, RemoteType, RemoteKeyType, LocalKeyType>> consumerForLocalKey = new HashMap<UniqueLocalReference<EParentObjectType, LocalKeyType>, RemoteEmfConsumer<EParentObjectType, EObjectType, RemoteType, RemoteKeyType, LocalKeyType>>();
- AbstractRemoteService service;
+ private final EReference parentReference;
- EReference parentReference;
+ private final EAttribute localKeyAttribute;
- EAttribute localAttribute;
+ private final AbstractRemoteFactoryProvider factoryProvider;
- public AbstractRemoteEmfFactory(AbstractRemoteService service, EReference parentReference, EAttribute localAttribute) {
- this.service = service;
+ /**
+ * Constructs the factory.
+ *
+ * @param factoryProvider
+ * The associated factory provider
+ * @param parentReference
+ * The EMF reference in the parent object that points to model objects; must be available for all parent
+ * object instances, but need not be a containment reference assuming persistence is managed separately
+ * @param localKeyAttribute
+ * The EMF attribute specifying the local key; must be available for all model object instance
+ */
+ public AbstractRemoteEmfFactory(AbstractRemoteFactoryProvider factoryProvider, EReference parentReference,
+ EAttribute localKeyAttribute) {
+ this.factoryProvider = factoryProvider;
this.parentReference = parentReference;
- this.localAttribute = localAttribute;
+ this.localKeyAttribute = localKeyAttribute;
}
/**
- * Returns the remote object that corresponds to a given model object.
+ * Returns a unique consumer for a model object that corresponds to a given remote API key. May be called from any
+ * thread.
*
- * @param object
- * A model object
- * @return An object containing remotely derived state
+ * @param parentObject
+ * The object that contains or will contain the remote object type
+ * @return A key used for locating the remote object from remote API. That object does not have to exist on the
+ * remote API yet, provided appropriate remote key to local key mappings are provided.
*/
- public RemoteType getRemoteObject(EObjectType object) {
- return remoteForObject.get(object);
+ public synchronized RemoteEmfConsumer<EParentObjectType, EObjectType, RemoteType, RemoteKeyType, LocalKeyType> getConsumerForRemoteKey(
+ EParentObjectType parentObject, RemoteKeyType remoteKey) {
+ LocalKeyType localKey = getLocalKeyForRemoteKey(remoteKey);
+ RemoteEmfConsumer<EParentObjectType, EObjectType, RemoteType, RemoteKeyType, LocalKeyType> consumer = findConsumer(
+ parentObject, localKey);
+ if (consumer == null) {
+ consumer = new RemoteEmfConsumer<EParentObjectType, EObjectType, RemoteType, RemoteKeyType, LocalKeyType>(
+ this, parentObject, null, localKey, null, remoteKey);
+ assignConsumer(parentObject, localKey, consumer);
+ } else {
+ consumer.setRemoteKey(remoteKey);
+ }
+ return consumer;
}
/**
- * Returns the model object that corresponds to a given remote API object.
+ * Returns unique consumer for a model object that corresponds to a given remote API object. May be called from any
+ * thread.
*
- * @param object
- * A model object
+ * @param parentObject
+ * The object that contains or will contain the remote object type
* @return An object containing remotely derived state
*/
- public EObjectType getModelObject(RemoteType remote) {
- return objectForRemote.get(remote);
+ public synchronized RemoteEmfConsumer<EParentObjectType, EObjectType, RemoteType, RemoteKeyType, LocalKeyType> getConsumerForRemoteObject(
+ EParentObjectType parentObject, RemoteType remoteObject) {
+ RemoteKeyType remoteKey = getRemoteKey(remoteObject);
+ LocalKeyType localKey = getLocalKeyForRemoteKey(remoteKey);
+ RemoteEmfConsumer<EParentObjectType, EObjectType, RemoteType, RemoteKeyType, LocalKeyType> consumer = findConsumer(
+ parentObject, localKey);
+ if (consumer == null) {
+ consumer = new RemoteEmfConsumer<EParentObjectType, EObjectType, RemoteType, RemoteKeyType, LocalKeyType>(
+ this, parentObject, null, localKey, remoteObject, remoteKey);
+ assignConsumer(parentObject, localKey, consumer);
+ } else {
+ consumer.setRemoteObject(remoteObject);
+ }
+ return consumer;
}
/**
- * Returns the remote object that matches a given model object within a given parent.
+ * Returns unique consumer for a model object that matches a given local key.
+ * <em>Must be called from EMF safe (e.g. UI) thread.</em>
*
- * @param object
- * A model object
+ * @param parentObject
+ * The object that contains or will contain the remote object type
+ * @return A key used for locating the model object from within the model parent object (Typically an EMF id). The
+ * actual matching model object does not have to exist yet. It might for example be created as part of a
+ * subsequent retrieval based on a matching remote key.
* @return An object containing remotely derived state
*/
- public RemoteKeyType getRemoteKey(EParentObjectType parentObject, EObjectType object) {
- return remoteKeyForObject.get(object);
+ public synchronized RemoteEmfConsumer<EParentObjectType, EObjectType, RemoteType, RemoteKeyType, LocalKeyType> getConsumerForLocalKey(
+ EParentObjectType parentObject, LocalKeyType localKey) {
+ RemoteEmfConsumer<EParentObjectType, EObjectType, RemoteType, RemoteKeyType, LocalKeyType> consumer = findConsumer(
+ parentObject, localKey);
+ if (consumer == null) {
+ Object parentField = parentObject.eGet(parentReference);
+ if (parentField instanceof List<?>) {
+ List<?> members = (List<?>) parentField;
+ for (Object object : members) {
+ if (object instanceof EObject) {
+ @SuppressWarnings("unchecked")
+ EObjectType eo = (EObjectType) object;
+ LocalKeyType currentKey = getLocalKey(parentObject, eo);
+ if (currentKey != null && localKey.equals(currentKey)) {
+ EObjectType modelObject = eo;
+ consumer = new RemoteEmfConsumer<EParentObjectType, EObjectType, RemoteType, RemoteKeyType, LocalKeyType>(
+ this, parentObject, modelObject, localKey, null, null);
+ assignConsumer(parentObject, localKey, consumer);
+ break;
+ }
+ }
+ }
+ }
+ if (consumer == null) {
+ consumer = new RemoteEmfConsumer<EParentObjectType, EObjectType, RemoteType, RemoteKeyType, LocalKeyType>(
+ this, parentObject, null, localKey, null, null);
+ assignConsumer(parentObject, localKey, consumer);
+ }
+ }
+ return consumer;
}
/**
- * Returns the local object that matches a given local key. (This will be used to support local sharing of model
- * resources.)
+ * Returns a unique consumer for a model object. <em>Must be called from EMF safe (e.g. UI) thread.</em>
*
- * @param object
- * A model object
+ * @param parentObject
+ * The object that contains the model object
+ * @param modelObject
+ * The model object itself. Must currently exist in the parent.
+ * @return A key used for locating the model object from within the model parent object (Typically an EMF id)
* @return An object containing remotely derived state
*/
- public EObjectType getLocalModelObject(LocalKeyType localKey) {
- return objectForLocalKey.get(localKey);
+ public synchronized RemoteEmfConsumer<EParentObjectType, EObjectType, RemoteType, RemoteKeyType, LocalKeyType> getConsumerForModel(
+ EParentObjectType parentObject, EObjectType modelObject) {
+ RemoteEmfConsumer<EParentObjectType, EObjectType, RemoteType, RemoteKeyType, LocalKeyType> consumer = null;
+ LocalKeyType localKey = getLocalKey(parentObject, modelObject);
+ consumer = findConsumer(parentObject, localKey);
+ if (consumer == null) {
+ consumer = new RemoteEmfConsumer<EParentObjectType, EObjectType, RemoteType, RemoteKeyType, LocalKeyType>(
+ this, parentObject, modelObject, localKey, null, null);
+ assignConsumer(parentObject, localKey, consumer);
+ }
+ return consumer;
+ }
+
+ private RemoteEmfConsumer<EParentObjectType, EObjectType, RemoteType, RemoteKeyType, LocalKeyType> findConsumer(
+ EParentObjectType parentObject, LocalKeyType localKey) {
+ UniqueLocalReference<EParentObjectType, LocalKeyType> key = new UniqueLocalReference<EParentObjectType, LocalKeyType>(
+ parentObject, localKey);
+ return consumerForLocalKey.get(key);
+ }
+
+ private RemoteEmfConsumer<EParentObjectType, EObjectType, RemoteType, RemoteKeyType, LocalKeyType> assignConsumer(
+ EParentObjectType parentObject, LocalKeyType localKey,
+ RemoteEmfConsumer<EParentObjectType, EObjectType, RemoteType, RemoteKeyType, LocalKeyType> consumer) {
+ UniqueLocalReference<EParentObjectType, LocalKeyType> key = new UniqueLocalReference<EParentObjectType, LocalKeyType>(
+ parentObject, localKey);
+ return consumerForLocalKey.put(key, consumer);
+ }
+
+ @SuppressWarnings("unchecked")
+ public LocalKeyType getLocalKey(EParentObjectType parentObject, EObjectType modelObject) {
+ if (modelObject instanceof EObject) {
+ EObject eObject = (EObject) modelObject;
+ return (LocalKeyType) eObject.eGet(getLocalKeyAttribute()); //Cannot test for type because of erasure
+ }
+ return null;
}
/**
- * Associates a remote object to it's model object. See {@link RemoteEmfConsumer#apply()}.
+ * Override to infer a local key from the remote object. Should not usually need to be overridden -- by default
+ * returns the local key matching the remote key for the supplied object.
*
- * @param object
- * @param remote
+ * @param remoteObject
+ * The remote object to obtain the local key from
*/
- void associateObjects(EObjectType object, RemoteType remote, LocalKeyType localKey, RemoteKeyType remoteKey) {
- remoteForObject.put(object, remote);
- objectForRemote.put(remote, object);
- objectForLocalKey.put(localKey, object);
- remoteKeyForObject.put(object, remoteKey);
+ public LocalKeyType getLocalKeyForRemoteObject(RemoteType remoteObject) {
+ return getLocalKeyForRemoteKey(getRemoteKey(remoteObject));
}
/**
- * Does the creation of an object require a call to the remote API? If false, no request job is created. True by
- * default (safe case).
+ * Override to infer a local key from a remote key. This method must be properly implemented with a one to one
+ * mapping in order for consumers to function correctly.
+ *
+ * @param remoteKey
+ * The remote key to obtain the local key from
+ */
+ public abstract LocalKeyType getLocalKeyForRemoteKey(RemoteKeyType remoteKey);
+
+ /**
+ * Returns the remote object that matches a given model object within a given parent.
+ *
+ * @param object
+ * A model object
+ * @return An object containing remotely derived state
+ */
+ public abstract RemoteKeyType getRemoteKey(RemoteType remoteObject);
+
+ /**
+ * Returns true if the creation of an object requires a call to the remote API. If false, no request job is created.
+ * True by default (safe case).
*
* @return true by default
*/
@@ -140,137 +284,143 @@ public abstract class AbstractRemoteEmfFactory<EParentObjectType extends EObject
/**
* Override to perform request to remote API. This request is fully managed by remote service and could be invoked
* directly, but is typically invoked through a consumer.
+ * <em>This method may block or fail, and must not be called from UI thread.</em>
*
+ * @param parentObject
+ * The object that contains the model object
* @param remoteKey
* A unique identifier in the target API
* @param monitor
- * @return An object containing remotely derived state. (This might be a remote API object or a local object
- * containing remotly obtained data.)
+ * @return An object containing remotely derived state. (This might be a remote API object itself or any other
+ * object containing remotely obtained data.)
* @throws CoreException
*/
- protected abstract RemoteType retrieve(RemoteKeyType remoteKey, IProgressMonitor monitor) throws CoreException;
+ public abstract RemoteType pull(EParentObjectType parent, RemoteKeyType remoteKey, IProgressMonitor monitor)
+ throws CoreException;
+
+ /**
+ * Override to return true if the remote object state should be requested from the remote API. Override to return
+ * true if there is no way to check the remote model object state without retrieving the whole object. The default
+ * implementation is sufficient if the remote state is immutable -- that is, if the update method is not implemented
+ * at all.
+ *
+ * @param parentObject
+ * The object that contains the model object
+ * @param modelObject
+ * The model object to test
+ * @param remoteObject
+ * A unique identifier in the target API
+ * @param monitor
+ * @return
+ */
+ public boolean isPullNeeded(EParentObjectType parent, EObjectType object, RemoteType remote) {
+ return object == null || remote == null;
+ }
/**
* Override to create an EObject from remote object. (Consumers should use
* {@link #get(EParentObjectType parent, RemoteType remoteObject)}, which ensures that any cached objects will be
* returned instead.)
+ * <em>Must be called from EMF safe (e.g. UI) thread and should have very fast execution time.</em>
*
- * @param parent
- * the parent EMF object that the new child object will be made a member of.
+ * @param parentObject
+ * the parent EMF object that the new child object will be referenced from
* @param remoteObject
* the object representing the remote API request response
* @return a model object
*/
- protected abstract EObjectType create(EParentObjectType parent, RemoteType remoteObject);
+ protected abstract EObjectType createModel(EParentObjectType parentObject, RemoteType remoteObject);
/**
* Updates the values for the supplied EMF object based on any values that have changed in the remote object since
* the last call to {@link #retrieve(String, EObject, EReference, Object)} or {@link #update(Object)}. The object
* must have been previously retrieved using this factory.
+ * <em>Must be called from EMF safe (e.g. UI) thread and should have very fast execution time.</em>
*
- * @param object
- * a model object
- * @return true if the object has changed, false if not
+ * @param parentObject
+ * the parent EMF object that the new child object is referenced from
+ * @param modelObject
+ * The model object to update -- must currently exist in the parent
+ * @return true if the object has changed or the object delta is unknown, false otherwise
*/
- public boolean update(EParentObjectType parent, EObjectType object, RemoteType remoteObject) {
+ public boolean updateModel(EParentObjectType parentObject, EObjectType modelObject, RemoteType remoteObject) {
return false;
}
/**
- * Returns a local object for the remote object, optionally creating a new object if one does not already exist.
- * (Override {@link #create(EParentObjectType parent, RemoteType remoteObject)} to provide implementation for object
- * creation.)
+ * Override to return true if a remote object to model object update should occur, e.g. when the remote object state
+ * is more recent then the model object state. Return true by default and generally doesn't need to be overridden as
+ * most update operations should be inexpensive.
*
+ * @param parentObject
+ * The object that contains the model object
+ * @param modelObject
+ * The model object to test
* @param remoteObject
- * the object representing the remote API request response
- * @return a model object
+ * A unique identifier in the target API
+ * @param monitor
+ * @return
*/
- public final EObjectType get(EParentObjectType parent, RemoteType remoteObject, LocalKeyType localKey,
- boolean create) {
- EObjectType object = getModelObject(remoteObject);
- if (object == null && localKey != null) {
- object = getLocalModelObject(localKey);
- }
- if (object == null && create) {
- object = create(parent, remoteObject);
- associateObjects(object, remoteObject, localKey, null);
- }
- return object;
+ public boolean isUpdateModelNeeded(EParentObjectType parentObject, EObjectType modelObject, RemoteType remote) {
+ return true;
}
/**
- * Returns a local object for the remote object. (Override
- * {@link #create(EParentObjectType parent, RemoteType remoteObject)} to provide implementation for object
- * creation.)
+ * Returns the model object for the supplied key(s), assuming that model object has already been created. This
+ * method can be called from any thread, and does not require any interaction with the remote server or local model
+ * object.
*
* @param remoteObject
* the object representing the remote API request response
* @return a model object
*/
- public final EObjectType get(EParentObjectType parent, RemoteType remoteObject, LocalKeyType localKey) {
- return get(parent, remoteObject, localKey, true);
+ public synchronized final EObjectType get(EParentObjectType parentObject, LocalKeyType localKey,
+ RemoteKeyType remoteKey) {
+ RemoteEmfConsumer<EParentObjectType, EObjectType, RemoteType, RemoteKeyType, LocalKeyType> consumer = null;
+ if (localKey != null) {
+ consumer = getConsumerForLocalKey(parentObject, localKey);
+ }
+ if (consumer == null && remoteKey != null) {
+ consumer = getConsumerForRemoteKey(parentObject, remoteKey);
+ }
+ if (consumer != null) {
+ return consumer.getModelObject();
+ }
+ return null;
}
/**
- * Returns a local object for the remote object, optionally creating a new object if one does not already exist.
- * (Override {@link #create(EParentObjectType parent, RemoteType remoteObject)} to provide implementation.)
+ * Returns the EMF reference in the parent object that refers to model objects. Returns the EMF attribute specifying
+ * the local key.
*
- * @param remoteObject
- * the object representing the remote API request response
- * @return a model object
+ * @return
*/
- public final EObjectType get(EParentObjectType parent, RemoteType remoteObject) {
- return get(parent, remoteObject, null, true);
+ public EReference getParentReference() {
+ return parentReference;
}
/**
- * Factory method to create a new EMF object factor. This is the method that most factory consumers will be
- * interested in. The method will asynchronously (as defined by remote service implementation):<li>
- * <ol>
- * Call the remote API, retrieving the results into a local (e.g. proxy) object representing the contents of the
- * remote object
- * </ol>
- * <ol>
- * Create a new EMF object and reference it from the supplied parent object.
- * </ol>
- * <ol>
- * Notify the parent object via the EMF notification mechanism. (As a by-product of the above step.)
- * </ol>
- * </li>
+ * Returns the EMF attribute specifying the local key.
*
- * @param description
- * a description of the purpose or context of this retrieval
- * @param parent
- * the object to which the retrieved object will be added
- * @param reference
- * the reference feature to use -- need not be a containment relation
- * @param key
- * the key used by the remote API to identify the object
- * @throws CoreException
+ * @return
*/
- public RemoteEmfConsumer<EParentObjectType, EObjectType, RemoteType, RemoteKeyType, LocalKeyType> consume(
- String description, EParentObjectType parent, RemoteKeyType remoteKey, LocalKeyType localKey,
- RemoteEmfConsumer.IObserver<EObjectType> consumer) {
- return new RemoteEmfConsumer<EParentObjectType, EObjectType, RemoteType, RemoteKeyType, LocalKeyType>(
- description, this, parent, remoteKey, localKey, consumer);
- }
-
- public RemoteEmfConsumer<EParentObjectType, EObjectType, RemoteType, RemoteKeyType, LocalKeyType> consume(
- String description, EParentObjectType parent, EObjectType modelObject,
- RemoteEmfConsumer.IObserver<EObjectType> consumer) {
- return new RemoteEmfConsumer<EParentObjectType, EObjectType, RemoteType, RemoteKeyType, LocalKeyType>(
- description, this, parent, modelObject, consumer);
- }
-
- public EReference getParentReference() {
- return parentReference;
+ public EAttribute getLocalKeyAttribute() {
+ return localKeyAttribute;
}
- public EAttribute getLocalAttribute() {
- return localAttribute;
+ /**
+ * Returns the service used to execute model operations as supplied by the factory provider.
+ *
+ * @return
+ */
+ public AbstractRemoteService getService() {
+ return getFactoryProvider().getService();
}
- public AbstractRemoteService getService() {
- return service;
+ /**
+ * Returns the parent factory provider that provides this factory.
+ */
+ public AbstractRemoteFactoryProvider getFactoryProvider() {
+ return factoryProvider;
}
}
diff --git a/org.eclipse.mylyn.reviews.core/src/org/eclipse/mylyn/reviews/core/spi/remote/emf/IRemoteEmfObserver.java b/org.eclipse.mylyn.reviews.core/src/org/eclipse/mylyn/reviews/core/spi/remote/emf/IRemoteEmfObserver.java
new file mode 100644
index 00000000..8d71fef5
--- /dev/null
+++ b/org.eclipse.mylyn.reviews.core/src/org/eclipse/mylyn/reviews/core/spi/remote/emf/IRemoteEmfObserver.java
@@ -0,0 +1,72 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Tasktop Technologies 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:
+ * Tasktop Technologies - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylyn.reviews.core.spi.remote.emf;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.emf.ecore.EObject;
+
+/**
+ * The observer receives notification of events that affect the consumer model objects. This is like an Asynchronous
+ * callback, except that the notification occurs every time a remote change to the object occurs. Notifications can
+ * occur across factories or through other remote update notifications.
+ * <p>
+ * Note that the observer notifications provide different semantics not supported by EMF notifications, such as a parent
+ * add.
+ * </p>
+ */
+public interface IRemoteEmfObserver<EParentObjectType extends EObject, EObjectType> {
+
+ /**
+ * Called whenever a model object has been created from a remote object and added to a parent object.
+ *
+ * @param parentObject
+ * The parent of the supplied object
+ * @param modelObject
+ * The newly created object
+ */
+ void created(EParentObjectType parentObject, EObjectType modelObject);
+
+ /**
+ * Called whenever a model object's value has been updated from the remote object. Unlike with Set and Add EMF
+ * notifications, updates are batched so that one and only one notification occurs for a model change after all
+ * values have been updated from the remote API. Updated is also called for newly created objects, so it typically
+ * isn't neccesary to listen for {@link #created(EObject, Object)} events.
+ *
+ * @param parentObject
+ * The parent of the supplied object
+ * @param modelObject
+ * The updated object
+ */
+ void updated(EParentObjectType parentObject, EObjectType modelObject, boolean modified);
+
+ /**
+ * Called whenever a model object begins the update process, that is, after a call to
+ * {@link RemoteEmfConsumer#retrieve(boolean)}.
+ *
+ * @param parentObject
+ * The parent of the supplied object
+ * @param modelObject
+ * The updating object
+ */
+ void updating(EParentObjectType parentObject, EObjectType modelObject);
+
+ /**
+ * Called whenever a failure has occurred while attempting to retrieve a remote object.
+ *
+ * @param parentObject
+ * The parent of the supplied object
+ * @param modelObject
+ * The object for which the failure occurred
+ */
+ void failed(EParentObjectType parentObject, EObjectType modelObject, IStatus status);
+
+} \ No newline at end of file
diff --git a/org.eclipse.mylyn.reviews.core/src/org/eclipse/mylyn/reviews/core/spi/remote/emf/RemoteEmfConsumer.java b/org.eclipse.mylyn.reviews.core/src/org/eclipse/mylyn/reviews/core/spi/remote/emf/RemoteEmfConsumer.java
index 735cbda6..221c47c0 100644
--- a/org.eclipse.mylyn.reviews.core/src/org/eclipse/mylyn/reviews/core/spi/remote/emf/RemoteEmfConsumer.java
+++ b/org.eclipse.mylyn.reviews.core/src/org/eclipse/mylyn/reviews/core/spi/remote/emf/RemoteEmfConsumer.java
@@ -11,6 +11,7 @@
package org.eclipse.mylyn.reviews.core.spi.remote.emf;
+import java.util.ArrayList;
import java.util.Collection;
import org.eclipse.core.runtime.CoreException;
@@ -27,48 +28,19 @@ import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.mylyn.reviews.core.spi.remote.AbstractRemoteConsumer;
/**
- * Manages the interaction between a remote API and a local EMF object.
+ * Manages the interaction between a remote API and a local EMF object. There can be only one instance of a consumer for
+ * a given model object or remote object per factory.
* <p>
- * After obtaining a consumer using one of the
- * {@link AbstractRemoteEmfFactory#consume(String, EObject, Object, IObserver)} methods, users can then
- * {@link RemoteEmfConsumer#request()} the model state to be updated from the remote API. Consumers that have not
- * directly requested an object can attach to it by calling is no longer needed in order to avoid excessive EMF
- * notification overhead.
+ * After obtaining a consumer using one of the {@link RemoteEmfConsumer} <i>AbstractRemoteEmfFactory#getConsumer()</i>
+ * methods, call {@link RemoteEmfConsumer#retrieve(boolean)} to request an update. Any registered
+ * {@link IRemoteEmfObserver}s will then receive an {@link IRemoteEmfObserver#updated(EObject, Object, boolean)} event
+ * regardless of whether or not the actual state changed.
*
* @author Miles Parker
*/
public class RemoteEmfConsumer<EParentObjectType extends EObject, EObjectType, RemoteType, RemoteKeyType, LocalKeyType>
extends AbstractRemoteConsumer {
- /**
- * The observer receives notification of events that affect the consumer model objects. This is like an Asynchronous
- * callback, except that the notification occurs every time a remote change to the object occurs. Notifications
- * occur regardless of the source consumer, and even across factories.
- */
- public interface IObserver<EObjectType> {
-
- void created(EObjectType object);
-
- void responded(boolean modified);
-
- void failed(IStatus status);
-
- }
-
- public class ObserverImpl<ObjectType> implements IObserver<EObjectType> {
-
- public void created(EObjectType object) {
- }
-
- public void responded(boolean changed) {
- }
-
- public void failed(IStatus status) {
- }
- }
-
- private final String description;
-
private final AbstractRemoteEmfFactory<EParentObjectType, EObjectType, RemoteType, RemoteKeyType, LocalKeyType> factory;
private RemoteKeyType remoteKey;
@@ -81,122 +53,193 @@ public class RemoteEmfConsumer<EParentObjectType extends EObject, EObjectType, R
private LocalKeyType localKey;
- private final IObserver<EObjectType> listener;
+ Collection<IRemoteEmfObserver<EParentObjectType, EObjectType>> remoteEmfObservers;
+
+ private boolean pulling;
+
+ private boolean retrieving;
- class ConsumerAdapter extends AdapterImpl {
+ private class ConsumerAdapter extends AdapterImpl {
@Override
public void notifyChanged(Notification msg) {
- if (msg.getEventType() == RemoteNotification.REMOTE_MEMBER_CREATE) {
- if (msg.getNewValue() == modelObject) {
- listener.created(modelObject);
+ if (msg instanceof RemoteNotification) {
+ RemoteNotification remoteMessage = (RemoteNotification) msg;
+ boolean notifyParent = remoteMessage.isMember()
+ && msg.getNotifier() == parentObject
+ && ((msg.getNewValue() == modelObject && (msg.getEventType() == RemoteNotification.REMOTE_MEMBER_CREATE || msg.getEventType() == RemoteNotification.REMOTE_MEMBER_FAILURE)) || modelObject instanceof Collection);
+ boolean notifyChild = !remoteMessage.isMember() && msg.getNotifier() == modelObject;
+ if (notifyParent || notifyChild) {
+ synchronized (remoteEmfObservers) {
+ for (IRemoteEmfObserver<EParentObjectType, EObjectType> listener : remoteEmfObservers) {
+ switch (msg.getEventType()) {
+ case RemoteNotification.REMOTE_MEMBER_CREATE:
+ listener.created(parentObject, modelObject);
+ break;
+ case RemoteNotification.REMOTE_MEMBER_UPDATING:
+ case RemoteNotification.REMOTE_UPDATING:
+ listener.updating(parentObject, modelObject);
+ break;
+ case RemoteNotification.REMOTE_MEMBER_UPDATE:
+ case RemoteNotification.REMOTE_UPDATE:
+ listener.updated(parentObject, modelObject, remoteMessage.isModification());
+ break;
+ case RemoteNotification.REMOTE_MEMBER_FAILURE:
+ case RemoteNotification.REMOTE_FAILURE:
+ listener.failed(parentObject, modelObject, remoteMessage.getStatus());
+ }
+ }
+ }
}
}
- if ((msg.getEventType() == RemoteNotification.REMOTE_MEMBER_UPDATE && msg.getNotifier() == parentObject && (!(modelObject instanceof EObject)))
- || (msg.getEventType() == RemoteNotification.REMOTE_UPDATE && msg.getNotifier() == modelObject)) {
- listener.responded(((RemoteNotification) msg).isModification());
- }
- if ((msg.getEventType() == RemoteNotification.REMOTE_MEMBER_FAILURE && msg.getNotifier() == parentObject && msg.getNewValue() == modelObject)
- || (msg.getEventType() == RemoteNotification.REMOTE_FAILURE && msg.getNotifier() == modelObject)) {
- listener.failed(((RemoteNotification) msg).getStatus());
- }
}
}
ConsumerAdapter adapter = new ConsumerAdapter();
- RemoteEmfConsumer(String description,
+ RemoteEmfConsumer(
AbstractRemoteEmfFactory<EParentObjectType, EObjectType, RemoteType, RemoteKeyType, LocalKeyType> factory,
- EParentObjectType parent, RemoteKeyType remoteKey, LocalKeyType localKey, IObserver<EObjectType> consumer) {
+ EParentObjectType parent, EObjectType modelObject, LocalKeyType localKey, RemoteType remoteObject,
+ RemoteKeyType remoteKey) {
this.parentObject = parent;
- this.factory = factory;
- this.description = description;
+ this.modelObject = modelObject;
+ this.remoteObject = remoteObject;
this.remoteKey = remoteKey;
this.localKey = localKey;
- this.listener = consumer;
- parent.eAdapters().add(adapter);
- }
-
- RemoteEmfConsumer(String description,
- AbstractRemoteEmfFactory<EParentObjectType, EObjectType, RemoteType, RemoteKeyType, LocalKeyType> factory,
- EParentObjectType parent, EObjectType object, IObserver<EObjectType> consumer) {
- this.description = description;
- this.parentObject = parent;
- this.modelObject = object;
this.factory = factory;
- this.listener = consumer;
- if (object instanceof EObject) {
- ((EObject) object).eAdapters().add(adapter);
+ if (remoteKey == null && remoteObject != null) {
+ remoteKey = factory.getRemoteKey(remoteObject);
+ }
+ if (localKey == null && modelObject != null) {
+ localKey = factory.getLocalKey(null, modelObject);
+ }
+ if (modelObject instanceof EObject) {
+ ((EObject) modelObject).eAdapters().add(adapter);
} else if (parent != null) {
parent.eAdapters().add(adapter);
}
+ remoteEmfObservers = new ArrayList<IRemoteEmfObserver<EParentObjectType, EObjectType>>();
}
+ /**
+ * Pulls the results from the factory, populating the remote object with the latest state from the remote API.
+ * Blocks until the remote API call completes. Does nothing if a retrieval is already occurring.
+ * <em>This method must not be called from the UI thread.</em>
+ *
+ * @param force
+ * pull from remote even when API doesn't require
+ * @param monitor
+ * @throws CoreException
+ */
@Override
- protected void retrieve(IProgressMonitor monitor) throws CoreException {
- parentObject.eNotify(new RemoteENotificationImpl((InternalEObject) parentObject,
- RemoteNotification.REMOTE_MEMBER_UPDATING, factory.getParentReference(), null));
- if (modelObject instanceof EObject) {
- ((EObject) modelObject).eNotify(new RemoteENotificationImpl((InternalEObject) modelObject,
- RemoteNotification.REMOTE_MEMBER_UPDATING, null, null));
+ public void pull(boolean force, IProgressMonitor monitor) throws CoreException {
+ pulling = true;
+ if (remoteObject != null && remoteKey == null) {
+ remoteKey = factory.getRemoteKey(remoteObject);
}
- if (remoteKey == null && modelObject != null) {
- remoteKey = factory.getRemoteKey(parentObject, modelObject);
- }
- try {
- remoteObject = factory.retrieve(remoteKey, monitor);
- } catch (final CoreException e) {
+ //Pull when "needed" or forced, but not when we don't have a remote key as that would be pointless.
+ if ((factory.isPullNeeded(parentObject, modelObject, remoteObject) || force == true) && remoteKey != null) {
+
getFactory().getService().modelExec(new Runnable() {
public void run() {
+ parentObject.eNotify(new RemoteENotificationImpl((InternalEObject) parentObject,
+ RemoteNotification.REMOTE_MEMBER_UPDATING, factory.getParentReference(), modelObject));
if (modelObject instanceof EObject) {
((EObject) modelObject).eNotify(new RemoteENotificationImpl((InternalEObject) modelObject,
- RemoteNotification.REMOTE_FAILURE, null, null, e.getStatus()));
- } else {
+ RemoteNotification.REMOTE_MEMBER_UPDATING, null, null));
+ }
+ }
+ }, false);
+ try {
+ remoteObject = factory.pull(parentObject, remoteKey, monitor);
+ if (localKey == null) {
+ localKey = factory.getLocalKeyForRemoteObject(remoteObject);
+ }
+ pulling = false;
+ } catch (final CoreException e) {
+ getFactory().getService().modelExec(new Runnable() {
+ public void run() {
parentObject.eNotify(new RemoteENotificationImpl((InternalEObject) parentObject,
RemoteNotification.REMOTE_MEMBER_FAILURE, factory.getParentReference(), null,
e.getStatus()));
+ if (modelObject instanceof EObject) {
+ ((EObject) modelObject).eNotify(new RemoteENotificationImpl((InternalEObject) modelObject,
+ RemoteNotification.REMOTE_FAILURE, null, null, e.getStatus()));
+ }
}
- }
- });
- throw e;
+ }, false);
+ throw e;
+ }
}
+ pulling = false;
}
+ /**
+ * Returns true whenever the consumer is pulling from Remote API to update the remote state, that is after
+ * {@link #retrieve(boolean)} has been called but before the {@link RemoteEmfConsumer#applyModel(boolean)} call has
+ * occurred.
+ */
+ public boolean isPulling() {
+ return pulling;
+ }
+
+ /**
+ * Apply the remote object to the local model object.
+ * <em>This method must be called from the EMF managed (e.g.) UI thread.</em>
+ *
+ * @param force
+ * apply the changes even when API doesn't require
+ * @throws CoreException
+ */
@Override
- protected void apply() {
+ public void applyModel(boolean force) {
+ //We may not have been retrieving the model at this point
NotificationChain msgs = new NotificationChainImpl();
- subscribe();
EReference reference = factory.getParentReference();
- boolean created = false;
- if (modelObject == null || reference.isMany() && (((Collection<?>) parentObject.eGet(reference)).size() == 0)) {
- created = true;
- modelObject = factory.create(parentObject, remoteObject);
- factory.associateObjects(modelObject, remoteObject, localKey, remoteKey);
- if (reference.isMany()) {
- if (modelObject instanceof Collection) {
- ((EList<EObjectType>) parentObject.eGet(reference)).addAll((Collection<EObjectType>) modelObject);
+ boolean modified = false;
+ if (remoteObject != null) {
+ if (modelObject == null
+ || (reference.isMany() && (((Collection<?>) parentObject.eGet(reference)).size() == 0))) {
+ modified = true;
+ modelObject = factory.createModel(parentObject, remoteObject);
+ if (reference.isMany()) {
+ if (modelObject instanceof Collection) {
+ ((EList<EObjectType>) parentObject.eGet(reference)).addAll((Collection<EObjectType>) modelObject);
+ } else {
+ ((EList<EObjectType>) parentObject.eGet(reference)).add(modelObject);
+ }
} else {
- ((EList<EObjectType>) parentObject.eGet(reference)).add(modelObject);
+ parentObject.eSet(reference, modelObject);
+ }
+ if (modelObject instanceof EObject) {
+ ((EObject) modelObject).eSet(factory.getLocalKeyAttribute(),
+ factory.getLocalKeyForRemoteObject(remoteObject));
+ ((EObject) modelObject).eAdapters().add(adapter);
}
- } else {
- parentObject.eSet(reference, modelObject);
+ msgs.add(new RemoteENotificationImpl((InternalEObject) parentObject,
+ RemoteNotification.REMOTE_MEMBER_CREATE, reference, modelObject));
}
- if (modelObject instanceof EObject) {
- ((EObject) modelObject).eAdapters().add(adapter);
+ if (factory.isUpdateModelNeeded(parentObject, modelObject, remoteObject) || force) {
+ modified |= factory.updateModel(parentObject, modelObject, remoteObject);
}
- msgs.add(new RemoteENotificationImpl((InternalEObject) parentObject,
- RemoteNotification.REMOTE_MEMBER_CREATE, reference, modelObject));
}
- boolean update = factory.update(parentObject, modelObject, remoteObject);
- update |= created;
msgs.add(new RemoteENotificationImpl((InternalEObject) parentObject, RemoteNotification.REMOTE_MEMBER_UPDATE,
- reference, modelObject, update));
+ reference, modelObject, modified));
if (modelObject instanceof EObject) {
msgs.add(new RemoteENotificationImpl((InternalEObject) modelObject, RemoteNotification.REMOTE_UPDATE, null,
- null, update));
+ null, modified));
}
+ retrieving = false;
msgs.dispatch();
+ }
+ /**
+ * Returns true whenever the consumer is updating model state, that is after a {@link #retrieve(boolean)} has been
+ * called and until immediately after the {@link IRemoteEmfObserver#updated(EObject, Object, boolean)} has been
+ * called.
+ */
+ public boolean isRetrieving() {
+ return retrieving;
}
/**
@@ -204,49 +247,48 @@ public class RemoteEmfConsumer<EParentObjectType extends EObject, EObjectType, R
* method primary factory consumers will be interested in. The method will asynchronously (as defined by remote
* service implementation):<li>
* <ol>
+ * Notify any registered {@link IRemoteEmfObserver}s that the object is
+ * {@link IRemoteEmfObserver#updating(EObject, Object)}.
+ * </ol>
+ * <ol>
* Call the remote API, retrieving the results into a local object representing the contents of the remote object.
- * If the object does not yet exist, one will be created.
* </ol>
* <ol>
- * Create a new EMF object and reference it from the supplied parent object.
+ * If the object does not yet exist, one will be created and added to the appropriate parent object.
* </ol>
* <ol>
- * Notify the parent object via the EMF notification mechanism. (As a by-product of the above step.)
+ * Notify objects of any changes via the standard EMF notification mechanisms. (As a by-product of the above step.)
+ * </ol>
+ * <ol>
+ * Notify any registered {@link IRemoteEmfObserver}s of object creation or update. (An update is notified even if
+ * object state does not change.)
* </ol>
* </li>
+ *
+ * @param force
+ * Forces pull and update, even if factory methods
+ * {@link AbstractRemoteEmfFactory#isPullNeeded(EObject, Object, Object)} and/or
+ * {@link AbstractRemoteEmfFactory#isUpdateModelNeeded(EObject, Object, Object)} return false.
*/
- public void request() {
- getFactory().getService().execute(this);
- }
-
- /**
- * Registers the consumer with the factory and obtains a model object representing the remote object. If a model
- * object does not yet exist, it will not be created. This is the method that factory observers such as views will
- * interested in. This method may be called at any time and does not trigger a remote invocation.
- */
- public void subscribe() {
- boolean modelWasNull = modelObject == null;
- if (modelObject == null) {
- modelObject = factory.getModelObject(remoteObject);
- }
- if (modelObject == null) {
- modelObject = factory.getLocalModelObject(localKey);
- }
- if (modelWasNull && modelObject instanceof EObject) {
- ((EObject) modelObject).eAdapters().add(adapter);
+ public void retrieve(boolean force) {
+ if (retrieving) {
+ return;
}
+ retrieving = true;
+ getFactory().getService().retrieve(this, force);
}
/**
- * A no-op. Typically, consumers should handle failure notifications through the {@link IObserver#failed(IStatus)}
- * method.
+ * Notifies the consumer that a failure has occurred while performing a retrieval. (Consumers should generally
+ * handle update and failure notifications through the {@link IRemoteEmfObserver#failed(IStatus)} method instead.)
*/
@Override
public void notifyDone(IStatus status) {
+ retrieving = false;
}
/**
- * Unregisters all listeners.
+ * Unregisters all listeners and adapters.
*/
@Override
public void dispose() {
@@ -254,35 +296,143 @@ public class RemoteEmfConsumer<EParentObjectType extends EObject, EObjectType, R
if (modelObject instanceof EObject) {
((EObject) modelObject).eAdapters().remove(adapter);
}
+ synchronized (remoteEmfObservers) {
+ remoteEmfObservers.clear();
+ }
}
- @Override
- public String getDescription() {
- return description;
+ /**
+ * Adds an observer to this consumer. Updates the consumer field for {@link RemoteEmfObserver}s.
+ *
+ * @param observer
+ * The observer to add
+ */
+ public void addObserver(IRemoteEmfObserver<EParentObjectType, EObjectType> observer) {
+ if (observer instanceof RemoteEmfObserver) {
+ RemoteEmfObserver<EParentObjectType, EObjectType> remoteEmfObserver = (RemoteEmfObserver<EParentObjectType, EObjectType>) observer;
+ if (remoteEmfObserver.getConsumer() != null && remoteEmfObserver.getConsumer() != this) {
+ remoteEmfObserver.getConsumer().removeObserver(remoteEmfObserver);
+ }
+ remoteEmfObserver.internalSetConsumer(this);
+ }
+ synchronized (remoteEmfObservers) {
+ remoteEmfObservers.add(observer);
+ }
+ }
+
+ /**
+ * Adds an observer to this consumer. Updates the consumer field for {@link RemoteEmfObserver}s.
+ *
+ * @param observer
+ * The observer to remove
+ */
+ public void removeObserver(IRemoteEmfObserver<EParentObjectType, EObjectType> observer) {
+ if (observer instanceof RemoteEmfObserver) {
+ RemoteEmfObserver<EParentObjectType, EObjectType> remoteEmfObserver = (RemoteEmfObserver<EParentObjectType, EObjectType>) observer;
+ if (remoteEmfObserver.getConsumer() == this) {
+ remoteEmfObserver.internalSetConsumer(null);
+ }
+ }
+ synchronized (remoteEmfObservers) {
+ remoteEmfObservers.remove(observer);
+ }
}
+ /**
+ * Returns the factory that providing services and objects for this consumer.
+ */
public AbstractRemoteEmfFactory<EParentObjectType, EObjectType, RemoteType, RemoteKeyType, LocalKeyType> getFactory() {
return factory;
}
+ /**
+ * Returns true if the factory is asynchronous, false otherwise.
+ */
@Override
public boolean isAsynchronous() {
- return factory.isAsynchronous();
+ return getFactory().isAsynchronous();
}
+ /**
+ * Returns the parent object for this consumer.
+ */
+ public EParentObjectType getParentObject() {
+ return parentObject;
+ }
+
+ /**
+ * Returns the model object for this consumer, if one has been obtained through the {@link #retrieve(boolean)}
+ * method or supplied when any object obtained this consumer.
+ */
public EObjectType getModelObject() {
return modelObject;
}
- public RemoteType getRemoteObject() {
- return remoteObject;
+ /**
+ * Returns the local key supplied by the consumer, or the local remote key if it can be inferred from the remote key
+ * or remote object.
+ */
+ public LocalKeyType getLocalKey() {
+ if (localKey != null) {
+ return localKey;
+ } else if (remoteKey != null) {
+ return getFactory().getLocalKeyForRemoteKey(remoteKey);
+ } else if (remoteObject != null) {
+ return getFactory().getLocalKeyForRemoteObject(remoteObject);
+ }
+ return null;
}
+ /**
+ * Returns the remote key for this consumer.
+ */
public RemoteKeyType getRemoteKey() {
return remoteKey;
}
- public LocalKeyType getLocalKey() {
- return localKey;
+ /**
+ * Returns the remote object that maps to this consumer's model object, local key or remote key, if one has been
+ * supplied or obtained using the remote key.
+ *
+ * @return
+ */
+ public RemoteType getRemoteObject() {
+ return remoteObject;
+ }
+
+ /**
+ * Should only be called by RemoteEmfFactory.
+ *
+ * @param remoteObject
+ */
+ void setRemoteObject(RemoteType remoteObject) {
+ if (!factory.getLocalKeyForRemoteObject(remoteObject).equals(getLocalKey())) {
+ throw new RuntimeException(
+ "Internal Error. Tried to set a remote object that doesn't match existing local key or object.");
+ }
+ this.remoteObject = remoteObject;
+ }
+
+ /**
+ * Should only be called by RemoteEmfFactory.
+ *
+ * @param remoteObject
+ */
+ void setRemoteKey(RemoteKeyType remoteKey) {
+ if (!factory.getLocalKeyForRemoteKey(remoteKey).equals(getLocalKey())) {
+ throw new RuntimeException(
+ "Internal Error. Tried to set a remote object that doesn't match existing local key or object.");
+ }
+ this.remoteKey = remoteKey;
+ }
+
+ @Override
+ public String getDescription() {
+ return "Retrieving "
+ + factory.getParentReference().getEReferenceType().getName()
+ + " "
+ + (localKey != null ? localKey.toString() : (remoteKey != null
+ ? ("Remote Key: " + remoteKey)
+ : "Unknown"));
}
}
diff --git a/org.eclipse.mylyn.reviews.core/src/org/eclipse/mylyn/reviews/core/spi/remote/emf/RemoteEmfObserver.java b/org.eclipse.mylyn.reviews.core/src/org/eclipse/mylyn/reviews/core/spi/remote/emf/RemoteEmfObserver.java
new file mode 100644
index 00000000..c5dd9075
--- /dev/null
+++ b/org.eclipse.mylyn.reviews.core/src/org/eclipse/mylyn/reviews/core/spi/remote/emf/RemoteEmfObserver.java
@@ -0,0 +1,87 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Tasktop Technologies 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:
+ * Tasktop Technologies - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylyn.reviews.core.spi.remote.emf;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.emf.ecore.EObject;
+
+/**
+ * A concrete implementation of {@link IRemoteEmfObserver}, providing a number of convenience methods for managing
+ * consumer interaction with observers.
+ *
+ * @author Miles Parker
+ */
+public class RemoteEmfObserver<EParentObjectType extends EObject, EObjectType> implements
+ IRemoteEmfObserver<EParentObjectType, EObjectType> {
+
+ RemoteEmfConsumer<EParentObjectType, EObjectType, ?, ?, ?> consumer;
+
+ /**
+ * Constructs an observer that listens to the supplied consumer.
+ *
+ * @param consumer
+ */
+ public RemoteEmfObserver(RemoteEmfConsumer<EParentObjectType, EObjectType, ?, ?, ?> consumer) {
+ setConsumer(consumer);
+ }
+
+ /**
+ * Constructs an observer.
+ */
+ public RemoteEmfObserver() {
+ }
+
+ public void created(EParentObjectType parentObject, EObjectType modelObject) {
+ }
+
+ public void updating(EParentObjectType parent, EObjectType object) {
+ }
+
+ public void updated(EParentObjectType parentObject, EObjectType modelObject, boolean modified) {
+ }
+
+ public void failed(EParentObjectType parentObject, EObjectType modelObject, IStatus status) {
+ }
+
+ /**
+ * Returns the consumer the observer is listening to. This value may be null if the observer was added directly to
+ * the consumer.
+ */
+ public RemoteEmfConsumer<EParentObjectType, EObjectType, ?, ?, ?> getConsumer() {
+ return consumer;
+ }
+
+ /**
+ * Sets the consumer for the given observer, adding itself to the supplied consumer and removing it from an existing
+ * consumer if any. This supports reuse of an observer when the underlying model object changes.
+ *
+ * @param consumer
+ */
+ public void setConsumer(RemoteEmfConsumer<EParentObjectType, EObjectType, ?, ?, ?> consumer) {
+ if (this.consumer != consumer) {
+ consumer.addObserver(this);
+ }
+ }
+
+ /**
+ * Non-API. Intended for use by consumer only.
+ */
+ void internalSetConsumer(RemoteEmfConsumer<EParentObjectType, EObjectType, ?, ?, ?> consumer) {
+ this.consumer = consumer;
+ }
+
+ public void dispose() {
+ if (consumer != null) {
+ consumer.removeObserver(this);
+ }
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.mylyn.reviews.core/src/org/eclipse/mylyn/reviews/core/spi/remote/emf/ReviewsRemoteFactoryProvider.java b/org.eclipse.mylyn.reviews.core/src/org/eclipse/mylyn/reviews/core/spi/remote/emf/ReviewsRemoteFactoryProvider.java
deleted file mode 100644
index 9e89565d..00000000
--- a/org.eclipse.mylyn.reviews.core/src/org/eclipse/mylyn/reviews/core/spi/remote/emf/ReviewsRemoteFactoryProvider.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2013 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:
- * Miles Parker (Tasktop Technologies) - initial API and implementation
- *******************************************************************************/
-
-package org.eclipse.mylyn.reviews.core.spi.remote.emf;
-
-import java.util.List;
-
-import org.eclipse.mylyn.reviews.core.model.IFileItem;
-import org.eclipse.mylyn.reviews.core.model.IReview;
-import org.eclipse.mylyn.reviews.core.model.IReviewGroup;
-import org.eclipse.mylyn.reviews.core.model.IReviewItemSet;
-import org.eclipse.mylyn.reviews.core.model.IReviewsFactory;
-import org.eclipse.mylyn.reviews.core.spi.remote.AbstractRemoteFactoryProvider;
-import org.eclipse.mylyn.reviews.core.spi.remote.JobRemoteService;
-
-/**
- * Supports decoupling of Reviews from remote API as well as job management.
- *
- * @author Miles Parker
- */
-public abstract class ReviewsRemoteFactoryProvider extends AbstractRemoteFactoryProvider {
-
- private final IReviewGroup reviews;
-
- public ReviewsRemoteFactoryProvider(JobRemoteService service) {
- super(service);
- this.reviews = IReviewsFactory.INSTANCE.createReviewGroup();
- }
-
- public abstract AbstractRemoteEmfFactory<IReviewGroup, IReview, ?, String, String> getReviewFactory();
-
- public abstract AbstractRemoteEmfFactory<IReview, IReviewItemSet, ?, ?, String> getReviewItemSetFactory();
-
- public abstract AbstractRemoteEmfFactory<IReviewItemSet, List<IFileItem>, ?, ?, String> getReviewItemSetContentFactory();
-
- public IReviewGroup getGroup() {
- return reviews;
- }
-}
diff --git a/org.eclipse.mylyn.reviews.core/src/org/eclipse/mylyn/reviews/core/spi/remote/review/IReviewRemoteFactoryProvider.java b/org.eclipse.mylyn.reviews.core/src/org/eclipse/mylyn/reviews/core/spi/remote/review/IReviewRemoteFactoryProvider.java
new file mode 100644
index 00000000..8c66a57d
--- /dev/null
+++ b/org.eclipse.mylyn.reviews.core/src/org/eclipse/mylyn/reviews/core/spi/remote/review/IReviewRemoteFactoryProvider.java
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * Copyright (c) 2013 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:
+ * Miles Parker (Tasktop Technologies) - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylyn.reviews.core.spi.remote.review;
+
+import java.util.List;
+
+import org.eclipse.mylyn.reviews.core.model.IFileItem;
+import org.eclipse.mylyn.reviews.core.model.IRepository;
+import org.eclipse.mylyn.reviews.core.model.IReview;
+import org.eclipse.mylyn.reviews.core.model.IReviewItemSet;
+import org.eclipse.mylyn.reviews.core.spi.remote.emf.AbstractRemoteEmfFactory;
+
+/**
+ * Supports decoupling of Reviews from remote API.
+ *
+ * @author Miles Parker
+ */
+public interface IReviewRemoteFactoryProvider {
+
+ AbstractRemoteEmfFactory<IRepository, IReview, ?, String, String> getReviewFactory();
+
+ AbstractRemoteEmfFactory<IReview, IReviewItemSet, ?, ?, String> getReviewItemSetFactory();
+
+ AbstractRemoteEmfFactory<IReviewItemSet, List<IFileItem>, ?, String, String> getReviewItemSetContentFactory();
+
+ IRepository getRoot();
+}
diff --git a/org.eclipse.mylyn.reviews.core/src/org/eclipse/mylyn/reviews/internal/core/ReviewsConnector.java b/org.eclipse.mylyn.reviews.core/src/org/eclipse/mylyn/reviews/internal/core/ReviewsConnector.java
new file mode 100644
index 00000000..91c1391c
--- /dev/null
+++ b/org.eclipse.mylyn.reviews.core/src/org/eclipse/mylyn/reviews/internal/core/ReviewsConnector.java
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Tasktop Technologies 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:
+ * Tasktop Technologies - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylyn.reviews.internal.core;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.mylyn.reviews.core.spi.remote.AbstractRemoteFactoryProvider;
+import org.eclipse.mylyn.tasks.core.AbstractRepositoryConnector;
+import org.eclipse.mylyn.tasks.core.TaskRepository;
+
+public abstract class ReviewsConnector extends AbstractRepositoryConnector {
+
+ Map<TaskRepository, AbstractRemoteFactoryProvider> factoryForRepository = new HashMap<TaskRepository, AbstractRemoteFactoryProvider>();
+
+ public abstract AbstractRemoteFactoryProvider createFactoryProvider(TaskRepository repository);
+
+ public AbstractRemoteFactoryProvider getFactoryProvider(TaskRepository repository) {
+ AbstractRemoteFactoryProvider factoryProvider = factoryForRepository.get(repository);
+ if (factoryProvider == null) {
+ factoryProvider = createFactoryProvider(repository);
+ factoryForRepository.put(repository, factoryProvider);
+ }
+ return factoryProvider;
+ }
+
+}
diff --git a/org.eclipse.mylyn.reviews.edit/META-INF/MANIFEST.MF b/org.eclipse.mylyn.reviews.edit/META-INF/MANIFEST.MF
index 76ad24fd..d2bbe4db 100644
--- a/org.eclipse.mylyn.reviews.edit/META-INF/MANIFEST.MF
+++ b/org.eclipse.mylyn.reviews.edit/META-INF/MANIFEST.MF
@@ -14,4 +14,7 @@ Require-Bundle: org.eclipse.core.runtime,
Bundle-ActivationPolicy: lazy
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Bundle-Vendor: Eclipse Mylyn
-Export-Package: org.eclipse.mylyn.reviews.edit.provider;x-internal:=true
+Export-Package: org.eclipse.mylyn.reviews.edit,
+ org.eclipse.mylyn.reviews.edit.provider,
+ org.eclipse.mylyn.reviews.edit.remote
+Bundle-Activator: org.eclipse.mylyn.reviews.edit.ReviewsEditPlugin
diff --git a/org.eclipse.mylyn.reviews.edit/icons/full/ctool16/CreateReview_reviewTask_TaskReference.gif b/org.eclipse.mylyn.reviews.edit/icons/full/ctool16/CreateReview_reviewTask_TaskReference.gif
deleted file mode 100644
index c06fe361..00000000
--- a/org.eclipse.mylyn.reviews.edit/icons/full/ctool16/CreateReview_reviewTask_TaskReference.gif
+++ /dev/null
Binary files differ
diff --git a/org.eclipse.mylyn.reviews.edit/plugin.xml b/org.eclipse.mylyn.reviews.edit/plugin.xml
index 249b448a..4beb523d 100644
--- a/org.eclipse.mylyn.reviews.edit/plugin.xml
+++ b/org.eclipse.mylyn.reviews.edit/plugin.xml
@@ -26,5 +26,4 @@
org.eclipse.emf.edit.provider.IItemLabelProvider
org.eclipse.emf.edit.provider.IItemPropertySource"/>
</extension>
-
</plugin>
diff --git a/org.eclipse.mylyn.reviews.edit/src/org/eclipse/mylyn/reviews/edit/ReviewsEditPlugin.java b/org.eclipse.mylyn.reviews.edit/src/org/eclipse/mylyn/reviews/edit/ReviewsEditPlugin.java
new file mode 100644
index 00000000..db2215b1
--- /dev/null
+++ b/org.eclipse.mylyn.reviews.edit/src/org/eclipse/mylyn/reviews/edit/ReviewsEditPlugin.java
@@ -0,0 +1,34 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Tasktop Technologies 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:
+ * Tasktop Technologies - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylyn.reviews.edit;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+
+public class ReviewsEditPlugin implements BundleActivator {
+
+ public static final String PLUGIN_ID = "org.eclipse.mylyn.reviews.edit"; //$NON-NLS-1$
+
+ private static ReviewsEditPlugin plugin;
+
+ public void start(BundleContext context) throws Exception {
+ plugin = this;
+ }
+
+ public void stop(BundleContext context) throws Exception {
+ plugin = null;
+ }
+
+ public static ReviewsEditPlugin getDefault() {
+ return plugin;
+ }
+}
diff --git a/org.eclipse.mylyn.reviews.edit/src/org/eclipse/mylyn/reviews/edit/remote/AbstractRemoteEditFactoryProvider.java b/org.eclipse.mylyn.reviews.edit/src/org/eclipse/mylyn/reviews/edit/remote/AbstractRemoteEditFactoryProvider.java
new file mode 100644
index 00000000..ea4e7f6a
--- /dev/null
+++ b/org.eclipse.mylyn.reviews.edit/src/org/eclipse/mylyn/reviews/edit/remote/AbstractRemoteEditFactoryProvider.java
@@ -0,0 +1,109 @@
+/*******************************************************************************
+ * Copyright (c) 2013 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:
+ * Miles Parker (Tasktop Technologies) - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylyn.reviews.edit.remote;
+
+import java.util.HashMap;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.emf.common.command.AbstractCommand;
+import org.eclipse.emf.common.command.BasicCommandStack;
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.EFactory;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.emf.ecore.resource.impl.ResourceImpl;
+import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain;
+import org.eclipse.emf.edit.domain.EditingDomain;
+import org.eclipse.emf.edit.provider.ComposedAdapterFactory;
+import org.eclipse.emf.edit.provider.resource.ResourceItemProviderAdapterFactory;
+import org.eclipse.mylyn.commons.core.StatusHandler;
+import org.eclipse.mylyn.reviews.core.model.IReviewGroup;
+import org.eclipse.mylyn.reviews.core.spi.remote.AbstractRemoteFactoryProvider;
+import org.eclipse.mylyn.reviews.edit.ReviewsEditPlugin;
+import org.eclipse.mylyn.reviews.edit.provider.ReviewsItemProviderAdapterFactory;
+
+/**
+ * Supports decoupling of Reviews from remote API as well as job management.
+ *
+ * @author Miles Parker
+ */
+public abstract class AbstractRemoteEditFactoryProvider<ERootObject extends EObject> extends
+ AbstractRemoteFactoryProvider {
+
+ private final EditingDomain editingDomain;
+
+ ERootObject rootObject;
+
+ public AbstractRemoteEditFactoryProvider(EFactory emfFactory, EClass rootClass) {
+ ComposedAdapterFactory adapterFactory = new ComposedAdapterFactory(
+ ComposedAdapterFactory.Descriptor.Registry.INSTANCE);
+
+ adapterFactory.addAdapterFactory(new ResourceItemProviderAdapterFactory());
+ adapterFactory.addAdapterFactory(new ReviewsItemProviderAdapterFactory());
+
+ BasicCommandStack commandStack = new BasicCommandStack();
+ editingDomain = new AdapterFactoryEditingDomain(adapterFactory, commandStack, new HashMap<Resource, Boolean>());
+
+ Resource resource = new ResourceImpl();
+
+ if (resource.getContents().size() > 0 && resource.getContents().get(0) instanceof IReviewGroup) {
+ try {
+ rootObject = (ERootObject) resource.getContents().get(0);
+ } catch (ClassCastException e) {
+ StatusHandler.log(new Status(IStatus.ERROR, ReviewsEditPlugin.PLUGIN_ID,
+ "Problem creating editing domain. Unexpected root model content.", e));
+ }
+
+ } else {
+ try {
+ rootObject = (ERootObject) emfFactory.create(rootClass);
+ resource.getContents().add(rootObject);
+ } catch (ClassCastException e) {
+ StatusHandler.log(new Status(IStatus.ERROR, ReviewsEditPlugin.PLUGIN_ID,
+ "Problem creating editing domain. Root remote class must match remote editing domain type.", e));
+ }
+ }
+ }
+
+ @Override
+ public void modelExec(final Runnable runnable, boolean block) {
+ super.modelExec(new Runnable() { //Run in UI thread
+ public void run() {
+ editingDomain.getCommandStack().execute(new AbstractCommand() {
+
+ public void redo() {
+ // noop
+ }
+
+ public void execute() {
+ runnable.run();
+ }
+
+ @Override
+ protected boolean prepare() {
+ return true;
+ }
+
+ @Override
+ public boolean canUndo() {
+ return false;
+ }
+ });
+ }
+ }, block);
+ }
+
+ public ERootObject getRoot() {
+ return rootObject;
+ }
+}
diff --git a/org.eclipse.mylyn.reviews.edit/src/org/eclipse/mylyn/reviews/edit/remote/ReviewsRemoteEditFactoryProvider.java b/org.eclipse.mylyn.reviews.edit/src/org/eclipse/mylyn/reviews/edit/remote/ReviewsRemoteEditFactoryProvider.java
new file mode 100644
index 00000000..a4974b9f
--- /dev/null
+++ b/org.eclipse.mylyn.reviews.edit/src/org/eclipse/mylyn/reviews/edit/remote/ReviewsRemoteEditFactoryProvider.java
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * Copyright (c) 2013 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:
+ * Miles Parker (Tasktop Technologies) - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylyn.reviews.edit.remote;
+
+import org.eclipse.emf.ecore.EFactory;
+import org.eclipse.mylyn.reviews.core.model.IRepository;
+import org.eclipse.mylyn.reviews.core.model.IReviewsFactory;
+import org.eclipse.mylyn.reviews.core.spi.remote.review.IReviewRemoteFactoryProvider;
+import org.eclipse.mylyn.reviews.internal.core.model.ReviewsPackage;
+
+/**
+ * Supports decoupling of Reviews from remote API as well as job management.
+ *
+ * @author Miles Parker
+ */
+public abstract class ReviewsRemoteEditFactoryProvider extends AbstractRemoteEditFactoryProvider<IRepository> implements
+ IReviewRemoteFactoryProvider {
+
+ public ReviewsRemoteEditFactoryProvider() {
+ super((EFactory) IReviewsFactory.INSTANCE, ReviewsPackage.Literals.REPOSITORY);
+ }
+}
diff --git a/org.eclipse.mylyn.reviews.ui/META-INF/MANIFEST.MF b/org.eclipse.mylyn.reviews.ui/META-INF/MANIFEST.MF
index 948acf84..23de8c02 100644
--- a/org.eclipse.mylyn.reviews.ui/META-INF/MANIFEST.MF
+++ b/org.eclipse.mylyn.reviews.ui/META-INF/MANIFEST.MF
@@ -22,7 +22,9 @@ Require-Bundle: org.eclipse.ui,
org.eclipse.mylyn.tasks.ui;bundle-version="3.8.0",
org.eclipse.mylyn.tasks.core;bundle-version="3.8.0",
org.eclipse.mylyn.reviews.core;bundle-version="2.0.0",
- org.eclipse.emf.edit,
+ org.eclipse.mylyn.reviews.edit;bundle-version="2.0.0",
+ org.eclipse.emf.edit;bundle-version="2.5.0",
+ org.eclipse.emf.ecore.xmi;bundle-version="2.5.0",
org.eclipse.team.core
Bundle-ActivationPolicy: lazy
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
diff --git a/org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/internal/reviews/ui/Messages.java b/org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/internal/reviews/ui/Messages.java
new file mode 100644
index 00000000..7d839e36
--- /dev/null
+++ b/org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/internal/reviews/ui/Messages.java
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Tasktop Technologies 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:
+ * Tasktop Technologies - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylyn.internal.reviews.ui;
+
+import org.eclipse.osgi.util.NLS;
+
+public class Messages extends NLS {
+ private static final String BUNDLE_NAME = "org.eclipse.mylyn.internal.reviews.ui.messages"; //$NON-NLS-1$
+
+ public static String Reviews_GeneralCommentsText;
+
+ public static String Reviews_RetrievingContents;
+
+ public static String Reviews_RetrievingDetails;
+ static {
+ // initialize resource bundle
+ NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+ }
+
+ private Messages() {
+ }
+}
diff --git a/org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/internal/reviews/ui/ReviewsUiConstants.java b/org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/internal/reviews/ui/ReviewsUiConstants.java
index f196fddd..62378259 100644
--- a/org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/internal/reviews/ui/ReviewsUiConstants.java
+++ b/org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/internal/reviews/ui/ReviewsUiConstants.java
@@ -16,11 +16,11 @@ package org.eclipse.mylyn.internal.reviews.ui;
*/
public interface ReviewsUiConstants {
- public static final String REVIEW_EXPLORER_ID = "org.eclipse.mylyn.reviews.Explorer";
+ public static final String REVIEW_EXPLORER_ID = "org.eclipse.mylyn.reviews.Explorer"; //$NON-NLS-1$
- public static final String REVIEW_CONTENT_ID = "org.eclipse.mylyn.reviews.ui.ReviewContent";
+ public static final String REVIEW_CONTENT_ID = "org.eclipse.mylyn.reviews.ui.ReviewContent"; //$NON-NLS-1$
- public static final String REVIEW_FLAT_CONTENT_ID = "org.eclipse.mylyn.reviews.ui.ReviewFlatContent";
+ public static final String REVIEW_FLAT_CONTENT_ID = "org.eclipse.mylyn.reviews.ui.ReviewFlatContent"; //$NON-NLS-1$
- public static final String REVIEW_FILTER_FOR_COMMENTS = "org.eclipse.mylyn.reviews.ui.CommonFilter";
+ public static final String REVIEW_FILTER_FOR_COMMENTS = "org.eclipse.mylyn.reviews.ui.CommonFilter"; //$NON-NLS-1$
}
diff --git a/org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/internal/reviews/ui/ReviewsUiPlugin.java b/org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/internal/reviews/ui/ReviewsUiPlugin.java
index 6e2a44cd..f8c5204a 100644
--- a/org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/internal/reviews/ui/ReviewsUiPlugin.java
+++ b/org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/internal/reviews/ui/ReviewsUiPlugin.java
@@ -11,6 +11,13 @@
package org.eclipse.mylyn.internal.reviews.ui;
import org.eclipse.mylyn.commons.workbench.CommonImageManger;
+import org.eclipse.mylyn.reviews.core.spi.remote.AbstractRemoteFactoryProvider;
+import org.eclipse.mylyn.reviews.core.spi.remote.AbstractRemoteService;
+import org.eclipse.mylyn.reviews.core.spi.remote.review.IReviewRemoteFactoryProvider;
+import org.eclipse.mylyn.reviews.internal.core.ReviewsConnector;
+import org.eclipse.mylyn.reviews.ui.spi.remote.RemoteUiService;
+import org.eclipse.mylyn.tasks.core.TaskRepository;
+import org.eclipse.mylyn.tasks.ui.TasksUi;
import org.eclipse.ui.plugin.AbstractUIPlugin;
import org.osgi.framework.BundleContext;
@@ -22,6 +29,8 @@ public class ReviewsUiPlugin extends AbstractUIPlugin {
CommonImageManger imageManager;
+ AbstractRemoteService service;
+
public ReviewsUiPlugin() {
}
@@ -46,4 +55,17 @@ public class ReviewsUiPlugin extends AbstractUIPlugin {
public CommonImageManger getImageManager() {
return imageManager;
}
+
+ public IReviewRemoteFactoryProvider getFactoryProvider(String connectorKind, TaskRepository repository) {
+ ReviewsConnector connector = (ReviewsConnector) TasksUi.getRepositoryConnector(connectorKind);
+ AbstractRemoteFactoryProvider factoryProvider = connector.getFactoryProvider(repository);
+ if (factoryProvider instanceof IReviewRemoteFactoryProvider) {
+ if (service == null) {
+ service = new RemoteUiService();
+ }
+ factoryProvider.setService(service);
+ return (IReviewRemoteFactoryProvider) factoryProvider;
+ }
+ throw new RuntimeException("The connector factory propvider must implement IReviewRemoteFactoryProvider");
+ }
}
diff --git a/org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/internal/reviews/ui/messages.properties b/org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/internal/reviews/ui/messages.properties
new file mode 100644
index 00000000..507aabf0
--- /dev/null
+++ b/org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/internal/reviews/ui/messages.properties
@@ -0,0 +1,3 @@
+Reviews_GeneralCommentsText=Global
+Reviews_RetrievingContents=[Retrieving contents...]
+Reviews_RetrievingDetails=[Retrieving details...]
diff --git a/org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/internal/reviews/ui/providers/RetrievingContentsNode.java b/org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/internal/reviews/ui/providers/RetrievingContentsNode.java
new file mode 100644
index 00000000..d4f871ef
--- /dev/null
+++ b/org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/internal/reviews/ui/providers/RetrievingContentsNode.java
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright (c) 2012 Ericsson
+ * 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:
+ * Miles Parker (Tasktop Technologies) - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylyn.internal.reviews.ui.providers;
+
+import org.apache.commons.lang.ObjectUtils;
+import org.eclipse.mylyn.reviews.core.model.IReviewItemSet;
+
+/**
+ * A child node used to indicate that a node's children are being retrieved.
+ *
+ * @author Miles Parker
+ */
+class RetrievingContentsNode {
+
+ private final IReviewItemSet parent;
+
+ public RetrievingContentsNode(IReviewItemSet parent) {
+ this.parent = parent;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return (other instanceof RetrievingContentsNode)
+ && ObjectUtils.equals(this.parent, ((RetrievingContentsNode) other).parent);
+ }
+
+ @Override
+ public int hashCode() {
+ return parent != null ? parent.hashCode() : 1;
+ }
+}
diff --git a/org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/internal/reviews/ui/providers/ReviewsLabelProvider.java b/org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/internal/reviews/ui/providers/ReviewsLabelProvider.java
index b5e6d335..f89b2f8c 100644
--- a/org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/internal/reviews/ui/providers/ReviewsLabelProvider.java
+++ b/org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/internal/reviews/ui/providers/ReviewsLabelProvider.java
@@ -59,8 +59,6 @@ import org.eclipse.swt.widgets.Display;
*/
public abstract class ReviewsLabelProvider extends TableStyledLabelProvider {
- private static final String GLOBAL_COMMENTS_NAME = "Global";
-
private static final int TOOLTIP_CHAR_WIDTH = 100;
static final int LINE_NUMBER_WIDTH = 4;
@@ -153,7 +151,10 @@ public abstract class ReviewsLabelProvider extends TableStyledLabelProvider {
@Override
public String getText(Object element) {
if (element instanceof GlobalCommentsNode) {
- return GLOBAL_COMMENTS_NAME;
+ return org.eclipse.mylyn.internal.reviews.ui.Messages.Reviews_GeneralCommentsText;
+ }
+ if (element instanceof RetrievingContentsNode) {
+ return org.eclipse.mylyn.internal.reviews.ui.Messages.Reviews_RetrievingContents;
}
if (element instanceof Collection) {
Collection<?> collection = (Collection<?>) element;
@@ -205,7 +206,7 @@ public abstract class ReviewsLabelProvider extends TableStyledLabelProvider {
@Override
public StyledString getStyledText(Object element) {
Styler styler = null;
- if (element instanceof IComment) {
+ if (element instanceof IComment || element instanceof RetrievingContentsNode) {
styler = COMMENT_STYLE;
}
StyledString styledString = new StyledString(getText(element), styler);
@@ -381,7 +382,7 @@ public abstract class ReviewsLabelProvider extends TableStyledLabelProvider {
@Override
public String getText(Object element) {
if (element instanceof GlobalCommentsNode) {
- return GLOBAL_COMMENTS_NAME;
+ return org.eclipse.mylyn.internal.reviews.ui.Messages.Reviews_GeneralCommentsText;
}
if (element instanceof Collection) {
Collection<?> collection = (Collection<?>) element;
@@ -600,6 +601,9 @@ public abstract class ReviewsLabelProvider extends TableStyledLabelProvider {
}
public static String getStatsText(ITopicContainer container) {
+ if (container instanceof IReviewItemSet && ((IReviewItemSet) container).getItems().size() == 0) {
+ return " [?]";
+ }
List<? extends IComment> comments;
if (container instanceof IReview) {
comments = container.getTopics();
diff --git a/org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/internal/reviews/ui/providers/ReviewsTreeContentProvider.java b/org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/internal/reviews/ui/providers/ReviewsTreeContentProvider.java
index 68f0c7d5..7f36d82b 100644
--- a/org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/internal/reviews/ui/providers/ReviewsTreeContentProvider.java
+++ b/org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/internal/reviews/ui/providers/ReviewsTreeContentProvider.java
@@ -50,8 +50,12 @@ public class ReviewsTreeContentProvider extends GenericTreeContentProvider {
}
if (element instanceof IReviewItemSet) {
IReviewItemSet itemSet = (IReviewItemSet) element;
- children.addAll(itemSet.getTopics());
- children.addAll(itemSet.getItems());
+ if (itemSet.getItems().size() > 0) {
+ children.addAll(itemSet.getTopics());
+ children.addAll(itemSet.getItems());
+ } else {
+ children.add(new RetrievingContentsNode(itemSet));
+ }
}
if (element instanceof ITopic) {
ITopic topic = (ITopic) element;
@@ -84,8 +88,8 @@ public class ReviewsTreeContentProvider extends GenericTreeContentProvider {
}
return ((element instanceof ITopicContainer) && ((ITopicContainer) element).getAllComments().size() > 0)
|| (element instanceof IReview && ((IReview) element).getSets().size() > 0)
- || (element instanceof GlobalCommentsNode && hasChildren(((GlobalCommentsNode) element).getReview()) || (element instanceof IReviewItemSet && ((IReviewItemSet) element).getItems()
- .size() > 0)) || hasCollectionChildren(element);
+ || (element instanceof GlobalCommentsNode && hasChildren(((GlobalCommentsNode) element).getReview()) || (element instanceof IReviewItemSet))
+ || hasCollectionChildren(element);
}
@Override
diff --git a/org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/internal/reviews/ui/views/ReviewExplorer.java b/org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/internal/reviews/ui/views/ReviewExplorer.java
index e59ea3c9..14b31004 100644
--- a/org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/internal/reviews/ui/views/ReviewExplorer.java
+++ b/org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/internal/reviews/ui/views/ReviewExplorer.java
@@ -14,9 +14,10 @@ package org.eclipse.mylyn.internal.reviews.ui.views;
import java.util.Arrays;
import java.util.Collection;
-import java.util.Collections;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IToolBarManager;
@@ -25,39 +26,44 @@ import org.eclipse.jface.viewers.ColumnViewerToolTipSupport;
import org.eclipse.jface.viewers.ColumnWeightData;
import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider;
import org.eclipse.jface.viewers.ILabelProvider;
-import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.ITreeViewerListener;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TableLayout;
+import org.eclipse.jface.viewers.TreeExpansionEvent;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.TreeViewerColumn;
import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.mylyn.internal.reviews.ui.ReviewsImages;
import org.eclipse.mylyn.internal.reviews.ui.ReviewsUiConstants;
+import org.eclipse.mylyn.internal.reviews.ui.ReviewsUiPlugin;
import org.eclipse.mylyn.internal.reviews.ui.providers.ReviewsLabelProvider;
import org.eclipse.mylyn.internal.reviews.ui.providers.TableStyledLabelProvider;
import org.eclipse.mylyn.internal.reviews.ui.providers.TableStyledLabelProvider.TableColumnProvider;
+import org.eclipse.mylyn.reviews.core.model.IFileItem;
+import org.eclipse.mylyn.reviews.core.model.IRepository;
import org.eclipse.mylyn.reviews.core.model.IReview;
+import org.eclipse.mylyn.reviews.core.model.IReviewItemSet;
+import org.eclipse.mylyn.reviews.core.spi.remote.emf.RemoteEmfConsumer;
+import org.eclipse.mylyn.reviews.core.spi.remote.emf.RemoteEmfObserver;
+import org.eclipse.mylyn.reviews.core.spi.remote.review.IReviewRemoteFactoryProvider;
import org.eclipse.mylyn.reviews.ui.spi.editor.AbstractReviewTaskEditorPage;
import org.eclipse.mylyn.tasks.core.ITask;
+import org.eclipse.mylyn.tasks.ui.editors.AbstractTaskEditorPage;
import org.eclipse.mylyn.tasks.ui.editors.TaskEditor;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeColumn;
+import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.ui.IActionBars;
-import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.IPageListener;
import org.eclipse.ui.IPartListener;
-import org.eclipse.ui.ISelectionListener;
-import org.eclipse.ui.IViewSite;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
-import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.forms.editor.IFormPage;
import org.eclipse.ui.navigator.CommonNavigator;
@@ -69,7 +75,7 @@ import org.eclipse.ui.navigator.INavigatorFilterService;
/**
* @author Miles Parker
*/
-public class ReviewExplorer extends CommonNavigator implements ISelectionListener {
+public class ReviewExplorer extends CommonNavigator {
public static final String SHOW_VIEW_LIST = "showViewList";
@@ -87,7 +93,7 @@ public class ReviewExplorer extends CommonNavigator implements ISelectionListene
private boolean filterForComments;
- private List<IReview> reviews = Collections.emptyList();
+ private IReview review = null;
private TaskEditor currentPart;
@@ -97,6 +103,30 @@ public class ReviewExplorer extends CommonNavigator implements ISelectionListene
private TableStyledLabelProvider currentProvider;
+ private RemoteEmfConsumer<IRepository, IReview, ?, String, String> reviewConsumer;
+
+ private IReviewRemoteFactoryProvider factoryProvider;
+
+ private String taskId;
+
+ private final RemoteEmfObserver<IRepository, IReview> reviewObserver = new RemoteEmfObserver<IRepository, IReview>() {
+ @Override
+ public void created(IRepository parentObject, IReview modelObject) {
+ if (modelObject.getId().equals(taskId) && modelObject != ReviewExplorer.this.review) {
+ setReview(modelObject);
+ }
+ }
+
+ @Override
+ public void updated(IRepository parentObject, IReview modelObject, boolean modified) {
+ if (modified) {
+ updatePerservingSelection();
+ }
+ }
+ };
+
+ private final Map<IReviewItemSet, RemoteEmfObserver<IReviewItemSet, List<IFileItem>>> patchSetObservers = new HashMap<IReviewItemSet, RemoteEmfObserver<IReviewItemSet, List<IFileItem>>>();
+
class ShowListAction extends Action {
public ShowListAction() {
super("", AS_RADIO_BUTTON); //$NON-NLS-1$
@@ -161,7 +191,7 @@ public class ReviewExplorer extends CommonNavigator implements ISelectionListene
*/
@Override
public void run() {
- updatePerservingSelection();
+ refresh();
}
}
@@ -194,6 +224,20 @@ public class ReviewExplorer extends CommonNavigator implements ISelectionListene
treeLabelProvider = new ReviewsLabelProvider.Tree();
final CommonViewer viewer = super.createCommonViewer(parent);
updateTreeViewer(viewer);
+ viewer.addTreeListener(new ITreeViewerListener() {
+ public void treeExpanded(TreeExpansionEvent event) {
+ if (event.getElement() instanceof IReviewItemSet) {
+ IReviewItemSet set = (IReviewItemSet) event.getElement();
+ if (set.getItems().size() == 0) {
+ RemoteEmfObserver<IReviewItemSet, List<IFileItem>> observer = patchSetObservers.get(set);
+ observer.getConsumer().retrieve(false);
+ }
+ }
+ }
+
+ public void treeCollapsed(TreeExpansionEvent event) {
+ }
+ });
return viewer;
}
@@ -294,14 +338,6 @@ public class ReviewExplorer extends CommonNavigator implements ISelectionListene
return matches;
}
- public void refreshView() {
- Display.getDefault().asyncExec(new Runnable() {
- public void run() {
- updatePerservingSelection();
- }
- });
- }
-
/**
* @see org.eclipse.ui.navigator.CommonNavigator#createPartControl(org.eclipse.swt.widgets.Composite)
*/
@@ -347,27 +383,6 @@ public class ReviewExplorer extends CommonNavigator implements ISelectionListene
updateActivations();
- getViewSite().getPage().addPartListener(new IPartListener() {
-
- public void partOpened(IWorkbenchPart part) {
- }
-
- public void partDeactivated(IWorkbenchPart part) {
- }
-
- public void partClosed(IWorkbenchPart part) {
- }
-
- public void partBroughtToTop(IWorkbenchPart part) {
- }
-
- public void partActivated(IWorkbenchPart part) {
- if (part == ReviewExplorer.this) {
- activated();
- }
- }
- });
-
IWorkbenchPage activePage = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
if (activePage != null) {
handleActivePage(activePage);
@@ -389,84 +404,74 @@ public class ReviewExplorer extends CommonNavigator implements ISelectionListene
private void handleActivePage(IWorkbenchPage page) {
if (page != null) {
- IEditorPart activeEditor = page.getActiveEditor();
- if (activeEditor instanceof TaskEditor) {
- currentPart = (TaskEditor) activeEditor;
- }
page.addPartListener(new IPartListener() {
-
public void partOpened(IWorkbenchPart part) {
- // ignore
-
}
public void partDeactivated(IWorkbenchPart part) {
- // ignore
-
}
public void partClosed(IWorkbenchPart part) {
if (part == currentPart) {
- clear();
+ currentPart = null;
+ review = null;
+ taskId = null;
+ disposeObservers();
+ update();
}
}
public void partBroughtToTop(IWorkbenchPart part) {
- // ignore
-
}
public void partActivated(IWorkbenchPart part) {
- // ignore
-
+ partSelected(part);
}
});
- activated();
+ partSelected(page.getActiveEditor());
}
}
protected void updateContentDescription() {
String title = "(No Selection)";
- Object input = getCommonViewer().getInput();
- if (input != null && currentPart != null) {
+ if (currentPart != null && currentPart.getTaskEditorInput() != null) {
ITask task = currentPart.getTaskEditorInput().getTask();
title = "Change " + task.getTaskId() + ": " + task.getSummary();
}
setContentDescription(title);
}
- /* (non-Javadoc)
- * Method declared on ISelectionListener.
- * Notify the current page that the selection has changed.
- */
- public void selectionChanged(IWorkbenchPart part, ISelection sel) {
- List<IReview> lastReviews = reviews;
- if (part instanceof TaskEditor) {
+ protected void partSelected(IWorkbenchPart part) {
+ if (part instanceof TaskEditor && currentPart != part) {
TaskEditor editor = (TaskEditor) part;
IFormPage page = editor.getActivePageInstance();
if (page instanceof AbstractReviewTaskEditorPage) {
- IReview review = ((AbstractReviewTaskEditorPage) page).getReview();
- if (review != null) {
- reviews = Collections.singletonList(review);
- } else {
- reviews = Collections.emptyList();
- }
+ AbstractTaskEditorPage reviewPage = (AbstractTaskEditorPage) page;
+ factoryProvider = ReviewsUiPlugin.getDefault().getFactoryProvider(reviewPage.getConnectorKind(),
+ reviewPage.getTaskRepository());
currentPart = (TaskEditor) part;
-
- if (!reviews.equals(lastReviews)) {
- update();
- }
+ setReviewId(reviewPage.getTask().getTaskId());
+ updateContentDescription();
}
}
}
- private void update() {
- getCommonViewer().setInput(reviews);
- refreshAction.setEnabled(!reviews.isEmpty());
+ protected void update() {
+ refreshAction.setEnabled(review != null);
+ getCommonViewer().setInput(review);
updateContentDescription();
getCommonViewer().refresh();
}
+ protected void refresh() {
+ if (reviewConsumer != null) {
+ reviewConsumer.retrieve(true);
+ for (RemoteEmfObserver<IReviewItemSet, List<IFileItem>> observer : patchSetObservers.values()) {
+ observer.getConsumer().retrieve(true);
+ }
+ }
+ }
+
protected void updatePerservingSelection() {
//Because we don't necessarily have the same backing EMF objects, we need to test on equality. We'd also like to restore selection even if the tree structure changes. In this case, using labels will be most consistent with user expectations..
Object[] priorExpanded = getCommonViewer().getExpandedElements();
@@ -477,10 +482,10 @@ public class ReviewExplorer extends CommonNavigator implements ISelectionListene
getCommonViewer().getControl().setRedraw(false);
update();
Collection<Object> newExpanded = matchingElements(
- (ITreeContentProvider) getCommonViewer().getContentProvider(), reviews,
+ (ITreeContentProvider) getCommonViewer().getContentProvider(), review,
new HashSet<Object>(Arrays.asList(priorExpanded)), true);
Collection<Object> newSelection = matchingElements(
- (ITreeContentProvider) getCommonViewer().getContentProvider(), reviews,
+ (ITreeContentProvider) getCommonViewer().getContentProvider(), review,
new HashSet<Object>(Arrays.asList(priorSelection)), false);
getCommonViewer().setExpandedElements(newExpanded.toArray());
getCommonViewer().setSelection(new StructuredSelection(newSelection.toArray()), true);
@@ -488,10 +493,58 @@ public class ReviewExplorer extends CommonNavigator implements ISelectionListene
getCommonViewer().getControl().redraw();
}
- private void clear() {
- reviews = Collections.emptyList();
- currentPart = null;
- update();
+ public void setReview(IReview newReview) {
+ if (review != newReview) {
+ review = newReview;
+ update();
+ if (review != null) {
+ reviewConsumer = factoryProvider.getReviewFactory().getConsumerForModel(factoryProvider.getRoot(),
+ newReview);
+ reviewConsumer.retrieve(false);
+ for (IReviewItemSet newSet : review.getSets()) {
+ RemoteEmfConsumer<IReviewItemSet, List<IFileItem>, ?, String, String> contentConsumer = factoryProvider.getReviewItemSetContentFactory()
+ .getConsumerForLocalKey(newSet, newSet.getId());
+ RemoteEmfObserver<IReviewItemSet, List<IFileItem>> patchSetObserver = new RemoteEmfObserver<IReviewItemSet, List<IFileItem>>(
+ contentConsumer) {
+ @Override
+ public void updated(IReviewItemSet parent, List<IFileItem> items, boolean modified) {
+ if (showList) {
+ getCommonViewer().refresh(true);
+ } else {
+ //Tree, so we can just refresh the parent set, assuming it's currently displayed
+ TreeItem[] rootItems = getCommonViewer().getTree().getItems();
+ boolean parentDisplayed = false;
+ for (TreeItem treeItem : rootItems) {
+ Object data = treeItem.getData();
+ if (data == parent) {
+ parentDisplayed = true;
+ break;
+ }
+ }
+ if (parentDisplayed) {
+ getCommonViewer().refresh(parent, true);
+ } else { //treeitem is currently being filtered (as a non-commented set, for example)
+ getCommonViewer().refresh(true);
+ }
+ }
+ }
+ };
+ patchSetObservers.put(newSet, patchSetObserver);
+ }
+ }
+ }
+ }
+
+ protected void setReviewId(String newTaskId) {
+ if (!newTaskId.equals(taskId)) {
+ taskId = newTaskId;
+ disposeObservers();
+ reviewConsumer = factoryProvider.getReviewFactory().getConsumerForLocalKey(factoryProvider.getRoot(),
+ newTaskId);
+ reviewConsumer.addObserver(reviewObserver);
+ reviewConsumer.retrieve(false);
+ setReview(reviewConsumer.getModelObject());
+ }
}
/**
@@ -506,13 +559,12 @@ public class ReviewExplorer extends CommonNavigator implements ISelectionListene
}
}
- /**
- * @see org.eclipse.ui.navigator.CommonNavigator#init(org.eclipse.ui.IViewSite, org.eclipse.ui.IMemento)
- */
- @Override
- public void init(IViewSite site, IMemento memento) throws PartInitException {
- site.getPage().addPostSelectionListener(this);
- super.init(site, memento);
+ private void disposeObservers() {
+ for (RemoteEmfObserver<IReviewItemSet, List<IFileItem>> observer : patchSetObservers.values()) {
+ observer.dispose();
+ }
+ patchSetObservers.clear();
+ reviewObserver.dispose();
}
/* (non-Javadoc)
@@ -521,15 +573,11 @@ public class ReviewExplorer extends CommonNavigator implements ISelectionListene
@Override
public void dispose() {
super.dispose();
- getSite().getPage().removePostSelectionListener(this);
//Don't hang on to references
flatLabelProvider.doDispose();
treeLabelProvider.doDispose();
currentPart = null;
- }
-
- protected void refreshAll() {
- refreshView();
+ disposeObservers();
}
public boolean isFlat() {
@@ -602,12 +650,6 @@ public class ReviewExplorer extends CommonNavigator implements ISelectionListene
getCommonViewer().refresh();
}
- protected void activated() {
- if (currentPart instanceof IEditorPart) {
- selectionChanged(currentPart, StructuredSelection.EMPTY);
- }
- }
-
public IWorkbenchPart getCurrentPart() {
return currentPart;
}
diff --git a/org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/reviews/ui/spi/editor/AbstractReviewSection.java b/org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/reviews/ui/spi/editor/AbstractReviewSection.java
index 44fb6df4..c9252ff3 100644
--- a/org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/reviews/ui/spi/editor/AbstractReviewSection.java
+++ b/org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/reviews/ui/spi/editor/AbstractReviewSection.java
@@ -11,15 +11,15 @@
package org.eclipse.mylyn.reviews.ui.spi.editor;
-import org.eclipse.emf.common.notify.Notification;
-import org.eclipse.emf.common.notify.impl.AdapterImpl;
+import org.eclipse.core.runtime.IStatus;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.mylyn.commons.ui.CommonUiUtil;
import org.eclipse.mylyn.internal.tasks.ui.editors.AbstractTaskEditorSection;
+import org.eclipse.mylyn.reviews.core.model.IRepository;
import org.eclipse.mylyn.reviews.core.model.IReview;
-import org.eclipse.mylyn.reviews.core.model.IReviewGroup;
-import org.eclipse.mylyn.reviews.core.spi.remote.emf.ReviewsRemoteFactoryProvider;
-import org.eclipse.mylyn.reviews.ui.spi.factories.AbstractUiFactoryProvider;
+import org.eclipse.mylyn.reviews.core.spi.remote.emf.IRemoteEmfObserver;
+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.spi.factories.IUiContext;
import org.eclipse.mylyn.tasks.core.ITask;
import org.eclipse.mylyn.tasks.core.TaskRepository;
@@ -40,29 +40,23 @@ import org.eclipse.ui.forms.widgets.Section;
* @author Miles Parker
* @author Steffen Pingel
*/
-public abstract class AbstractReviewSection extends AbstractTaskEditorSection implements IUiContext {
+public abstract class AbstractReviewSection extends AbstractTaskEditorSection implements IUiContext,
+ IRemoteEmfObserver<IRepository, IReview> {
protected Composite composite;
protected FormToolkit toolkit;
- protected boolean createdModelControls;
+ protected boolean modelContentsCurrent;
+
+ protected RemoteEmfConsumer<IRepository, IReview, ?, String, String> consumer;
@Override
public void initialize(AbstractTaskEditorPage taskEditorPage) {
super.initialize(taskEditorPage);
- final IReviewGroup group = getReviewEditorPage().getRemoteFactory().getGroup();
- group.eAdapters().add(new AdapterImpl() {
- @Override
- public void notifyChanged(Notification notification) {
- if (notification.getNotifier() == group) {
- Object newValue = notification.getNewValue();
- if (newValue instanceof IReview && ((IReview) newValue).getId().equals(getTaskData().getTaskId())) {
- checkCreateModelControls();
- }
- }
- }
- });
+ consumer = getFactoryProvider().getReviewFactory().getConsumerForLocalKey(getFactoryProvider().getRoot(),
+ getTask().getTaskId());
+ consumer.addObserver(this);
}
@Override
@@ -70,33 +64,39 @@ public abstract class AbstractReviewSection extends AbstractTaskEditorSection im
this.toolkit = toolkit;
composite = toolkit.createComposite(parent);
GridLayoutFactory.fillDefaults().extendedMargins(0, 0, 0, 5).applyTo(composite);
- checkCreateModelControls();
+ checkCreateModelContent();
return composite;
}
/**
* We don't know whether the model or the controls will be available first, so we handle both cases here.
*/
- private void checkCreateModelControls() {
- if (composite != null && !createdModelControls) {
+ private void checkCreateModelContent() {
+ if (composite != null) {
if (!composite.isDisposed()) {
- if (getReviewEditorPage().getReview() != null) {
- createdModelControls = true;
- createModelControls();
- getSection().setText(CommonUiUtil.toLabel(getPartName()));
- } else {
- getSection().setText(CommonUiUtil.toLabel(getPartName()) + " [Loading contents...]");
+ if (!modelContentsCurrent && getReview() != null) {
+ modelContentsCurrent = true;
+ createModelContent();
}
+ updateMessage();
}
}
}
- @Override
- public void refresh() {
- super.refresh();
+ @SuppressWarnings("restriction")
+ private void updateMessage() {
+ if (composite != null) {
+ if (consumer != null && consumer.getModelObject() != null) {
+ getSection().setText(CommonUiUtil.toLabel(getPartName()));
+ } else {
+ getSection().setText(
+ CommonUiUtil.toLabel(getPartName()) + " "
+ + org.eclipse.mylyn.internal.reviews.ui.Messages.Reviews_RetrievingDetails);
+ }
+ }
}
- protected void createModelControls() {
+ protected void createModelContent() {
}
public Label addTextClient(final FormToolkit toolkit, final Section section, String text) {
@@ -151,20 +151,44 @@ public abstract class AbstractReviewSection extends AbstractTaskEditorSection im
}
public IReview getReview() {
- return getReviewEditorPage().getReview();
+ if (consumer != null) {
+ return consumer.getModelObject();
+ }
+ return null;
}
public TaskRepository getTaskRepository() {
return getReviewEditorPage().getTaskRepository();
}
- public ReviewsRemoteFactoryProvider getRemoteFactoryProvider() {
- return getReviewEditorPage().getRemoteFactory();
+ public IRepository getModelRepository() {
+ return (IRepository) getReview().getGroup();
+ }
+
+ public IReviewRemoteFactoryProvider getFactoryProvider() {
+ return getReviewEditorPage().getFactoryProvider();
}
public AbstractReviewTaskEditorPage getReviewEditorPage() {
return (AbstractReviewTaskEditorPage) getTaskEditorPage();
}
- protected abstract AbstractUiFactoryProvider<?> getUiFactoryProvider();
+ public void created(IRepository parent, IReview object) {
+ //ignore
+ }
+
+ public void updating(IRepository parent, IReview object) {
+ //ignore
+ }
+
+ public void updated(IRepository parent, IReview object, boolean modified) {
+ checkCreateModelContent();
+ if (modified) {
+ refresh();
+ }
+ }
+
+ public void failed(IRepository parent, IReview object, IStatus status) {
+ // ignore
+ }
}
diff --git a/org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/reviews/ui/spi/editor/AbstractReviewTaskEditorPage.java b/org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/reviews/ui/spi/editor/AbstractReviewTaskEditorPage.java
index 64bb0cd3..99f9a6b3 100644
--- a/org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/reviews/ui/spi/editor/AbstractReviewTaskEditorPage.java
+++ b/org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/reviews/ui/spi/editor/AbstractReviewTaskEditorPage.java
@@ -15,21 +15,21 @@ import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.dialogs.IMessageProvider;
import org.eclipse.mylyn.commons.core.StatusHandler;
-import org.eclipse.mylyn.internal.reviews.ui.ReviewsUiConstants;
-import org.eclipse.mylyn.internal.reviews.ui.views.ReviewExplorer;
+import org.eclipse.mylyn.internal.reviews.ui.ReviewsUiPlugin;
+import org.eclipse.mylyn.internal.tasks.core.AbstractTask;
import org.eclipse.mylyn.internal.tasks.ui.TasksUiPlugin;
import org.eclipse.mylyn.internal.tasks.ui.util.TasksUiInternal;
+import org.eclipse.mylyn.reviews.core.model.IRepository;
import org.eclipse.mylyn.reviews.core.model.IReview;
-import org.eclipse.mylyn.reviews.core.model.IReviewGroup;
+import org.eclipse.mylyn.reviews.core.spi.remote.emf.IRemoteEmfObserver;
import org.eclipse.mylyn.reviews.core.spi.remote.emf.RemoteEmfConsumer;
-import org.eclipse.mylyn.reviews.core.spi.remote.emf.ReviewsRemoteFactoryProvider;
-import org.eclipse.mylyn.reviews.internal.core.model.ReviewsFactory;
+import org.eclipse.mylyn.reviews.core.spi.remote.review.IReviewRemoteFactoryProvider;
+import org.eclipse.mylyn.tasks.core.ITask;
import org.eclipse.mylyn.tasks.ui.editors.AbstractTaskEditorPage;
import org.eclipse.mylyn.tasks.ui.editors.TaskEditor;
+import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorSite;
-import org.eclipse.ui.IViewPart;
-import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.forms.events.HyperlinkAdapter;
import org.eclipse.ui.forms.events.HyperlinkEvent;
@@ -38,86 +38,84 @@ import org.eclipse.ui.forms.events.HyperlinkEvent;
*
* @author Miles Parker
*/
-public abstract class AbstractReviewTaskEditorPage extends AbstractTaskEditorPage {
+public abstract class AbstractReviewTaskEditorPage extends AbstractTaskEditorPage implements
+ IRemoteEmfObserver<IRepository, IReview> {
- //TODO Much of this class will not be neccesary at all once we have workspace model support and notification working.
- private ReviewsRemoteFactoryProvider remoteFactory;
+ private RemoteEmfConsumer<IRepository, IReview, ?, String, String> consumer;
- private IReview review;
+ private IReviewRemoteFactoryProvider factoryProvider;
- private RemoteEmfConsumer<IReviewGroup, IReview, ?, String, String> consumer;
+ private boolean refreshRequested;
public AbstractReviewTaskEditorPage(TaskEditor editor, String id, String label, String connectorKind) {
super(editor, id, label, connectorKind);
}
@Override
- public void init(IEditorSite site, IEditorInput input) {
- super.init(site, input);
- IReviewGroup group = ReviewsFactory.eINSTANCE.createReviewGroup();
- remoteFactory = createRemoteFactory();
- consumer = remoteFactory.getReviewFactory().consume("Manage", remoteFactory.getGroup(), getTask().getTaskId(),
- getTask().getTaskId(), new RemoteEmfConsumer.IObserver<IReview>() {
-
- public void created(IReview review) {
- AbstractReviewTaskEditorPage.this.review = review;
- }
-
- public void responded(boolean modified) {
- }
-
- public void failed(final IStatus status) {
- StatusHandler.log(new Status(IStatus.ERROR, TasksUiPlugin.ID_PLUGIN,
- "Error loading task", status.getException())); //$NON-NLS-1$
- getTaskEditor().setMessage(
- org.eclipse.mylyn.internal.tasks.ui.editors.Messages.AbstractTaskEditorPage_Error_opening_task,
- IMessageProvider.ERROR, new HyperlinkAdapter() {
- @Override
- public void linkActivated(HyperlinkEvent event) {
- TasksUiInternal.displayStatus(
- org.eclipse.mylyn.internal.tasks.ui.editors.Messages.AbstractTaskEditorPage_Open_failed,
- status);
- }
- });
- }
- });
- consumer.request();
+ public void init(final IEditorSite site, final IEditorInput input) {
+ AbstractReviewTaskEditorPage.super.init(site, input);
+ if (getFactoryProvider() != null) {
+ consumer = getFactoryProvider().getReviewFactory().getConsumerForRemoteKey(getFactoryProvider().getRoot(),
+ getTask().getTaskId());
+ consumer.addObserver(AbstractReviewTaskEditorPage.this);
+ consumer.retrieve(false);
+ }
}
@Override
public void refresh() {
- super.refresh();
- consumer.request();
+ refreshRequested = true;
+ //We defer the actual refresh until the model is also refreshed. See updated method.
+ consumer.retrieve(true);
}
- public void refreshExplorer() {
- //TODO, we really shouldn't be updating the view directly, it should be listening for model change, but that needs to be implemented with care given EMF update and UI threading concerns, etc.
- IViewPart view = PlatformUI.getWorkbench()
- .getActiveWorkbenchWindow()
- .getActivePage()
- .findView(ReviewsUiConstants.REVIEW_EXPLORER_ID);
- if (view instanceof ReviewExplorer) {
- ((ReviewExplorer) view).refreshView();
+ public IReviewRemoteFactoryProvider getFactoryProvider() {
+ if (factoryProvider == null) {
+ factoryProvider = ReviewsUiPlugin.getDefault().getFactoryProvider(getConnectorKind(), getTaskRepository());
}
+ return factoryProvider;
}
- protected abstract ReviewsRemoteFactoryProvider createRemoteFactory();
+ public void created(IRepository parent, IReview object) {
+ //ignore
+ }
- public ReviewsRemoteFactoryProvider getRemoteFactory() {
- return remoteFactory;
+ public void updating(IRepository parent, IReview object) {
+ //ignore
}
- /**
- * Returns the current review. All instances should provide one open, accessible review model instance at init time,
- * and that review should be constant throughout the editor life-cycle.
- */
- public IReview getReview() {
- return review;
+ public void updated(IRepository parent, IReview object, boolean modified) {
+ if (refreshRequested) {
+ getTask().getSynchronizationState().isIncoming();
+ //Prevent CME from observer and allow other UI processes time to execute
+ Display.getCurrent().asyncExec(new Runnable() {
+ @Override
+ public void run() {
+ AbstractReviewTaskEditorPage.super.refresh();
+ }
+ });
+ refreshRequested = false;
+ } else if (modified) {
+ if (getTask() instanceof AbstractTask) {
+ //This is a little awkward, as if we have another update source, we'll need to re-refresh from here as well,
+ //but there isn't an obvious way to force the update to occur at this point w/o creating a cycle
+ ((AbstractTask) getTask()).setSynchronizationState(ITask.SynchronizationState.INCOMING);
+ }
+ }
}
- @Override
- public void dispose() {
- super.dispose();
- remoteFactory.dispose();
+ public void failed(IRepository parent, IReview object, final IStatus status) {
+ StatusHandler.log(new Status(IStatus.ERROR, TasksUiPlugin.ID_PLUGIN,
+ "Error loading task", status.getException())); //$NON-NLS-1$
+ getTaskEditor().setMessage(
+ org.eclipse.mylyn.internal.tasks.ui.editors.Messages.AbstractTaskEditorPage_Error_opening_task,
+ IMessageProvider.ERROR, new HyperlinkAdapter() {
+ @Override
+ public void linkActivated(HyperlinkEvent event) {
+ TasksUiInternal.displayStatus(
+ org.eclipse.mylyn.internal.tasks.ui.editors.Messages.AbstractTaskEditorPage_Open_failed,
+ status);
+ }
+ });
}
}
diff --git a/org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/reviews/ui/spi/editor/ReviewDetailSection.java b/org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/reviews/ui/spi/editor/ReviewDetailSection.java
new file mode 100644
index 00000000..d359dbc2
--- /dev/null
+++ b/org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/reviews/ui/spi/editor/ReviewDetailSection.java
@@ -0,0 +1,208 @@
+/*******************************************************************************
+ * Copyright (c) 2010 Tasktop Technologies 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:
+ * Tasktop Technologies - initial API and implementation
+ * Jan Lohre (SAP) - improvements
+ * Sascha Scholz (SAP) - improvements
+ *******************************************************************************/
+
+package org.eclipse.mylyn.reviews.ui.spi.editor;
+
+import java.util.List;
+import java.util.Map.Entry;
+
+import org.apache.commons.lang.StringUtils;
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.jface.layout.GridLayoutFactory;
+import org.eclipse.mylyn.reviews.core.model.IApprovalType;
+import org.eclipse.mylyn.reviews.core.model.IChange;
+import org.eclipse.mylyn.reviews.core.model.IRequirementEntry;
+import org.eclipse.mylyn.reviews.core.model.IReviewerEntry;
+import org.eclipse.mylyn.reviews.core.model.IUser;
+import org.eclipse.mylyn.tasks.ui.TasksUiUtil;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Link;
+import org.eclipse.ui.forms.IFormColors;
+import org.eclipse.ui.forms.widgets.ExpandableComposite;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.Section;
+
+/**
+ * Displays basic information about a given review corresponding to top sections of Gerrit web interface.
+ *
+ * @author Steffen Pingel
+ * @author Miles Parker
+ */
+public class ReviewDetailSection extends AbstractReviewSection {
+
+ public ReviewDetailSection() {
+ setPartName("Review");
+ }
+
+ @Override
+ public void createModelContent() {
+ createReviewersSubSection(composite);
+ createRequirementsSubSection(toolkit, composite);
+ createDependenciesSubSection(toolkit, composite, "Depends On", getReview().getParents());
+ createDependenciesSubSection(toolkit, composite, "Needed By", getReview().getChildren());
+ }
+
+ protected void createReviewersSubSection(Composite parent) {
+ if (getReview().getReviewerApprovals().isEmpty() && !canAddReviewers()) {
+ return;
+ }
+
+ int style = ExpandableComposite.TWISTIE | ExpandableComposite.CLIENT_INDENT
+ | ExpandableComposite.LEFT_TEXT_CLIENT_ALIGNMENT | ExpandableComposite.EXPANDED;
+
+ final Section subSection = toolkit.createSection(parent, style);
+ GridDataFactory.fillDefaults().grab(true, false).applyTo(subSection);
+ subSection.setTitleBarForeground(toolkit.getColors().getColor(IFormColors.TITLE));
+ subSection.setText("Reviewers");
+
+ Composite composite = toolkit.createComposite(subSection);
+ int numColumns = getModelRepository().getApprovalTypes().size() + 1;
+ GridLayoutFactory.fillDefaults()
+ .numColumns(numColumns)
+ .extendedMargins(0, 0, 0, 5)
+ .spacing(20, 5)
+ .applyTo(composite);
+ subSection.setClient(composite);
+
+ if (!getReview().getReviewerApprovals().isEmpty()) {
+ StringBuilder names = new StringBuilder();
+
+ Label headerLabel = new Label(composite, SWT.NONE);
+ headerLabel.setText(" "); //$NON-NLS-1$
+ for (IApprovalType approvalType : getModelRepository().getApprovalTypes()) {
+ Label approvalHeaderLabel = new Label(composite, SWT.NONE);
+ approvalHeaderLabel.setForeground(toolkit.getColors().getColor(IFormColors.TITLE));
+ approvalHeaderLabel.setText(approvalType.getName());
+ }
+
+ for (Entry<IUser, IReviewerEntry> entry : getReview().getReviewerApprovals().entrySet()) {
+
+ Label reviewerRowLabel = new Label(composite, SWT.NONE);
+ reviewerRowLabel.setForeground(toolkit.getColors().getColor(IFormColors.TITLE));
+ reviewerRowLabel.setText(entry.getKey().getDisplayName());
+
+ for (IApprovalType approvalType : getModelRepository().getApprovalTypes()) {
+ Integer value = entry.getValue().getApprovals().get(approvalType);
+ Label approvalValueLabel = new Label(composite, SWT.NONE);
+ GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(approvalValueLabel);
+ String rankingText = " ";
+ if (value != null && value != 0) {
+ if (value > 0) {
+ rankingText += "+";
+ approvalValueLabel.setForeground(Display.getCurrent().getSystemColor(SWT.COLOR_DARK_GREEN));
+ } else if (value < 0) {
+ approvalValueLabel.setForeground(Display.getCurrent().getSystemColor(SWT.COLOR_RED));
+ }
+ rankingText += value;
+ approvalValueLabel.setToolTipText(value + " " + approvalType.getName());
+ }
+ approvalValueLabel.setText(rankingText);
+ }
+ if (names.length() > 0) {
+ names.append(", "); //$NON-NLS-1$
+ }
+
+ if (names.length() > 0) {
+ names.append(", "); //$NON-NLS-1$
+ }
+ names.append(entry.getKey().getDisplayName());
+ }
+
+ if (names.length() > 0) {
+ addTextClient(toolkit, subSection, names.toString());
+ }
+ }
+ }
+
+ protected boolean canAddReviewers() {
+ return true;
+ }
+
+ protected void createRequirementsSubSection(final FormToolkit toolkit, final Composite parent) {
+ if (getReview().getRequirements().isEmpty()) {
+ return;
+ }
+
+ int style = ExpandableComposite.TWISTIE | ExpandableComposite.CLIENT_INDENT
+ | ExpandableComposite.LEFT_TEXT_CLIENT_ALIGNMENT;
+
+ final Section subSection = toolkit.createSection(parent, style);
+ GridDataFactory.fillDefaults().grab(true, false).applyTo(subSection);
+ subSection.setTitleBarForeground(toolkit.getColors().getColor(IFormColors.TITLE));
+ subSection.setText("Requirements");
+
+ Composite composite = toolkit.createComposite(subSection);
+ GridLayoutFactory.fillDefaults().numColumns(2).spacing(20, 5).extendedMargins(0, 0, 0, 5).applyTo(composite);
+ subSection.setClient(composite);
+
+ StringBuilder sb = new StringBuilder();
+
+ for (Entry<IApprovalType, IRequirementEntry> requirement : getReview().getRequirements().entrySet()) {
+ Label label1 = new Label(composite, SWT.NONE);
+ IApprovalType key = requirement.getKey();
+ label1.setText(key.getName());
+ label1.setForeground(toolkit.getColors().getColor(IFormColors.TITLE));
+
+ if (sb.length() > 0) {
+ sb.append(", "); //$NON-NLS-1$
+ }
+ sb.append(key.getName());
+ }
+
+ addTextClient(toolkit, subSection, sb.toString());
+ }
+
+ protected void createDependenciesSubSection(final FormToolkit toolkit, final Composite parent, String title,
+ List<IChange> changes) {
+ if (changes.isEmpty()) {
+ return;
+ }
+
+ int style = ExpandableComposite.TWISTIE | ExpandableComposite.CLIENT_INDENT;
+
+ final Section subSection = toolkit.createSection(parent, style);
+ GridDataFactory.fillDefaults().grab(true, false).applyTo(subSection);
+ subSection.setTitleBarForeground(toolkit.getColors().getColor(IFormColors.TITLE));
+ subSection.setText(title);
+
+ Composite composite = toolkit.createComposite(subSection);
+ GridLayoutFactory.fillDefaults().extendedMargins(0, 0, 0, 5).applyTo(composite);
+ subSection.setClient(composite);
+
+ for (final IChange change : changes) {
+ Link link = new Link(composite, SWT.NONE);
+ String changeStatus = change.getState() != null ? NLS.bind(" ({0})",
+ String.valueOf(change.getState().getDescriptor())) : " ";
+ String ownerName = change.getOwner().getDisplayName();
+ link.setText(NLS.bind("<a>{0}</a>: {1} {3} by {2}", new String[] { StringUtils.left(change.getKey(), 9),
+ change.getSubject(), ownerName, changeStatus }));
+ link.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ TasksUiUtil.openTask(getTaskEditorPage().getTaskRepository(), change.getId() + ""); //$NON-NLS-1$
+ }
+ });
+ }
+ }
+
+ @Override
+ protected boolean shouldExpandOnCreate() {
+ return true;
+ }
+}
diff --git a/org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/reviews/ui/spi/editor/ReviewSetContentSection.java b/org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/reviews/ui/spi/editor/ReviewSetContentSection.java
index 69ace417..e83d0851 100644
--- a/org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/reviews/ui/spi/editor/ReviewSetContentSection.java
+++ b/org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/reviews/ui/spi/editor/ReviewSetContentSection.java
@@ -13,7 +13,6 @@
package org.eclipse.mylyn.reviews.ui.spi.editor;
import java.text.DateFormat;
-import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -38,11 +37,13 @@ import org.eclipse.mylyn.internal.tasks.ui.editors.EditorUtil;
import org.eclipse.mylyn.reviews.core.model.IFileItem;
import org.eclipse.mylyn.reviews.core.model.IReviewItem;
import org.eclipse.mylyn.reviews.core.model.IReviewItemSet;
+import org.eclipse.mylyn.reviews.core.spi.remote.emf.IRemoteEmfObserver;
import org.eclipse.mylyn.reviews.core.spi.remote.emf.RemoteEmfConsumer;
import org.eclipse.mylyn.reviews.internal.core.model.ReviewsPackage;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.forms.FormColors;
@@ -60,7 +61,7 @@ import org.eclipse.ui.forms.widgets.Section;
* @author Miles Parker
* @author Sam Davis
*/
-public class ReviewSetContentSection {
+public class ReviewSetContentSection implements IRemoteEmfObserver<IReviewItemSet, List<IFileItem>> {
private static final int MAXIMUM_ITEMS_SHOWN = 30;
@@ -78,7 +79,11 @@ public class ReviewSetContentSection {
private boolean createdContentSection;
- private boolean retrievedModelContents;
+ private boolean requestedModelContents;
+
+ private boolean modelContentsCurrent;
+
+ private Composite tableContainer;
public ReviewSetContentSection(ReviewSetSection parentSection, final IReviewItemSet set) {
this.parentSection = parentSection;
@@ -86,55 +91,35 @@ public class ReviewSetContentSection {
int style = ExpandableComposite.TWISTIE | ExpandableComposite.CLIENT_INDENT
| ExpandableComposite.LEFT_TEXT_CLIENT_ALIGNMENT;
//We assume that the last item is also the "current" item
- List<IReviewItem> items = new ArrayList<IReviewItem>(set.getReview().getSets());
+ List<IReviewItemSet> items = set.getReview().getSets();
if (items.get(items.size() - 1) == set) {
style |= ExpandableComposite.EXPANDED;
}
consumer = getParentSection().getReviewEditorPage()
- .getRemoteFactory()
+ .getFactoryProvider()
.getReviewItemSetContentFactory()
- .consume("Managing Review Set", set, set.getItems(),
- new RemoteEmfConsumer.IObserver<List<IFileItem>>() {
-
- public void responded(boolean modified) {
- retrievedModelContents = true;
- checkCreateModelControls();
- }
-
- public void failed(IStatus status) {
- StatusHandler.log(new Status(IStatus.ERROR, TasksUiPlugin.ID_PLUGIN,
- "Error loading patch set", status.getException())); //$NON-NLS-1$
- AbstractReviewSection.appendMessage(getParentSection().getSection(),
- "Couldn't load patch set.");
- }
-
- public void created(List<IFileItem> object) {
- }
- });
+ .getConsumerForRemoteKey(set, set.getId());
+ consumer.addObserver(this);
section = parentSection.getToolkit().createSection(parentSection.getComposite(), style);
GridDataFactory.fillDefaults().grab(true, false).applyTo(section);
section.setText(set.getName());
section.setTitleBarForeground(parentSection.getToolkit().getColors().getColor(IFormColors.TITLE));
parentSection.addTextClient(parentSection.getToolkit(), section, "", false); //$NON-NLS-1$
- updateControls(false);
+ updateMessage();
if (section.isExpanded()) {
- createContents();
- checkCreateModelControls();
+ onExpanded();
}
section.addExpansionListener(new ExpansionAdapter() {
@Override
public void expansionStateChanged(ExpansionEvent e) {
- if (section.getClient() == null) {
- createContents();
- checkCreateModelControls();
- }
+ onExpanded();
}
});
}
- public void updateControls(boolean cachingInProgress) {
+ public void updateMessage() {
String message;
String time = DateFormat.getDateTimeInstance().format(set.getCreationDate());
@@ -145,21 +130,20 @@ public class ReviewSetContentSection {
message = NLS.bind("{0}", time);
}
- if (cachingInProgress) {
- message += " [Caching contents...]";
+ if (consumer.isRetrieving()) {
+ message += " " + org.eclipse.mylyn.internal.reviews.ui.Messages.Reviews_RetrievingContents;
}
AbstractReviewSection.appendMessage(getSection(), message);
}
protected void onExpanded() {
- //We assume that if we have any items that the review has been updated, and that all review sets have at least one review item (e.g. commit message)
- boolean contentRetrieved = set.getItems().size() > 0;
- updateControls(!contentRetrieved);
- if (!contentRetrieved) {
- contentRetrieved = true;
- consumer.request();
+ updateMessage();
+ if (!requestedModelContents) {
+ consumer.retrieve(false);
+ requestedModelContents = true;
}
+ checkCreateModelControls();
}
void createContents() {
@@ -173,14 +157,22 @@ public class ReviewSetContentSection {
authorLabel.setText("Author");
Text authorText = new Text(composite, SWT.READ_ONLY);
- authorText.setText(set.getAddedBy().getDisplayName());
+ if (set.getAddedBy() != null) {
+ authorText.setText(set.getAddedBy().getDisplayName());
+ } else {
+ authorText.setText("Unspecified");
+ }
Label committerLabel = new Label(composite, SWT.NONE);
committerLabel.setForeground(colors.getColor(IFormColors.TITLE));
committerLabel.setText("Committer");
Text committerText = new Text(composite, SWT.READ_ONLY);
- committerText.setText(set.getCommittedBy().getDisplayName());
+ if (set.getCommittedBy() != null) {
+ committerText.setText(set.getCommittedBy().getDisplayName());
+ } else {
+ committerText.setText("Unspecified");
+ }
Label commitLabel = new Label(composite, SWT.NONE);
commitLabel.setForeground(colors.getColor(IFormColors.TITLE));
@@ -204,6 +196,19 @@ public class ReviewSetContentSection {
Text refText = new Text(composite, SWT.READ_ONLY);
refText.setText(set.getReference());
+ tableContainer = new Composite(composite, SWT.NONE);
+ tableContainer.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_DARK_GRAY));
+ GridDataFactory.fillDefaults().span(2, 1).grab(true, true).applyTo(tableContainer);
+ GridLayoutFactory.fillDefaults().numColumns(2).applyTo(tableContainer);
+
+ Composite actionComposite = getParentSection().getUiFactoryProvider().createButtons(getParentSection(),
+ composite, parentSection.getToolkit(), set);
+ GridDataFactory.fillDefaults().span(2, 1).applyTo(actionComposite);
+
+ parentSection.getTaskEditorPage().reflow();
+ }
+
+ public void createItemSetTable(Composite composite) {
boolean fixedViewerSize = set.getItems().size() > MAXIMUM_ITEMS_SHOWN;
int heightHint = fixedViewerSize ? 300 : SWT.DEFAULT;
int style = SWT.SINGLE | SWT.BORDER | SWT.VIRTUAL;
@@ -274,35 +279,28 @@ public class ReviewSetContentSection {
}
}
});
-
- Composite actionComposite = getParentSection().getUiFactoryProvider().createButtons(getParentSection(),
- composite, parentSection.getToolkit(), set);
- GridDataFactory.fillDefaults().span(2, 1).applyTo(actionComposite);
-
- onExpanded();
EditorUtil.addScrollListener(viewer.getTable());
-
- parentSection.getTaskEditorPage().reflow();
}
/**
* We don't know whether the model or the controls will be available first, so we handle both cases here.
*/
private void checkCreateModelControls() {
- if (retrievedModelContents && !createdContentSection && viewer != null && !viewer.getControl().isDisposed()
- && section.isExpanded()) {
- createdContentSection = true;
- createModelControls();
- }
- }
-
- private void createModelControls() {
- viewer.setInput(set);
- if (getParentSection().getTaskEditorPage() instanceof AbstractReviewTaskEditorPage) {
- ((AbstractReviewTaskEditorPage) getParentSection().getTaskEditorPage()).refreshExplorer();
+ if (section.isExpanded()) {
+ if (!createdContentSection) {
+ createdContentSection = true;
+ createContents();
+ }
+ if (requestedModelContents && !set.getItems().isEmpty() && ((viewer == null || !modelContentsCurrent))) {
+ modelContentsCurrent = true;
+ if (viewer == null) {
+ createItemSetTable(tableContainer);
+ }
+ viewer.setInput(set);
+ getParentSection().getTaskEditorPage().reflow();
+ }
}
- updateControls(false);
- getParentSection().getTaskEditorPage().reflow();
+ updateMessage();
}
public Section getSection() {
@@ -313,18 +311,26 @@ public class ReviewSetContentSection {
return parentSection;
}
- public void updated() {
- // ignore
-
+ public void created(IReviewItemSet parent, List<IFileItem> object) {
}
- public void failure(IStatus status) {
- // ignore
+ public void updating(IReviewItemSet parent, List<IFileItem> object) {
+ updateMessage();
+ }
+ public void updated(IReviewItemSet parent, List<IFileItem> object, boolean modified) {
+ modelContentsCurrent &= !modified;
+ checkCreateModelControls();
}
- public void created(List<IReviewItem> object) {
- // ignore
+ public void failed(IReviewItemSet parent, List<IFileItem> object, IStatus status) {
+ StatusHandler.log(new Status(IStatus.ERROR, TasksUiPlugin.ID_PLUGIN,
+ "Error loading patch set", status.getException())); //$NON-NLS-1$
+ AbstractReviewSection.appendMessage(getParentSection().getSection(), "Couldn't load patch set.");
+ }
+ public void dispose() {
+ consumer.removeObserver(this);
+ section.dispose();
}
}
diff --git a/org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/reviews/ui/spi/editor/ReviewSetSection.java b/org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/reviews/ui/spi/editor/ReviewSetSection.java
index 0ac7ebb4..1bcd15e6 100644
--- a/org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/reviews/ui/spi/editor/ReviewSetSection.java
+++ b/org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/reviews/ui/spi/editor/ReviewSetSection.java
@@ -31,8 +31,6 @@ import org.eclipse.mylyn.tasks.ui.editors.AbstractTaskEditorPage;
*/
public abstract class ReviewSetSection extends AbstractReviewSection {
- private static final int MAXIMUM_ITEMS_SHOWN = 30;
-
protected ReviewsLabelProvider labelProvider;
private List<ReviewSetContentSection> reviewSetSections;
@@ -45,14 +43,6 @@ public abstract class ReviewSetSection extends AbstractReviewSection {
}
@Override
- public void dispose() {
- super.dispose();
- if (labelProvider != null) {
- labelProvider.dispose();
- }
- }
-
- @Override
public void initialize(AbstractTaskEditorPage taskEditorPage) {
reviewSetSections = new ArrayList<ReviewSetContentSection>();
super.initialize(taskEditorPage);
@@ -70,7 +60,7 @@ public abstract class ReviewSetSection extends AbstractReviewSection {
}
@Override
- public void createModelControls() {
+ public void createModelContent() {
for (IReviewItem item : getReview().getSets()) {
if (item instanceof IReviewItemSet) {
IReviewItemSet set = (IReviewItemSet) item;
@@ -78,6 +68,7 @@ public abstract class ReviewSetSection extends AbstractReviewSection {
reviewSetSections.add(subSection);
}
}
+ getTaskEditorPage().reflow();
}
protected ReviewSetContentSection createContentSubSection(IReviewItemSet set) {
@@ -89,7 +80,6 @@ public abstract class ReviewSetSection extends AbstractReviewSection {
return true;
}
- @Override
protected abstract AbstractReviewItemSetUiFactoryProvider getUiFactoryProvider();
@Override
@@ -97,4 +87,14 @@ public abstract class ReviewSetSection extends AbstractReviewSection {
return getEditor().getTaskEditorInput().getTaskRepository();
}
+ @Override
+ public void dispose() {
+ super.dispose();
+ if (labelProvider != null) {
+ labelProvider.dispose();
+ }
+ for (ReviewSetContentSection section : reviewSetSections) {
+ section.dispose();
+ }
+ }
}
diff --git a/org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/reviews/ui/spi/factories/AbstractUiFactory.java b/org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/reviews/ui/spi/factories/AbstractUiFactory.java
index a3b3955e..283657dc 100644
--- a/org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/reviews/ui/spi/factories/AbstractUiFactory.java
+++ b/org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/reviews/ui/spi/factories/AbstractUiFactory.java
@@ -11,7 +11,7 @@
package org.eclipse.mylyn.reviews.ui.spi.factories;
-import org.eclipse.mylyn.reviews.core.spi.remote.AbstractRemoteFactoryProvider;
+import org.eclipse.mylyn.reviews.core.spi.remote.review.IReviewRemoteFactoryProvider;
import org.eclipse.mylyn.reviews.ui.spi.editor.AbstractReviewSection;
import org.eclipse.mylyn.tasks.core.ITask;
import org.eclipse.mylyn.tasks.core.TaskRepository;
@@ -98,8 +98,8 @@ public abstract class AbstractUiFactory<EObjectType> implements IUiContext {
return context.getTaskRepository();
}
- public AbstractRemoteFactoryProvider getRemoteFactoryProvider() {
- return context.getRemoteFactoryProvider();
+ public IReviewRemoteFactoryProvider getFactoryProvider() {
+ return context.getFactoryProvider();
}
public IUiContext getContext() {
diff --git a/org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/reviews/ui/spi/factories/IUiContext.java b/org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/reviews/ui/spi/factories/IUiContext.java
index 93a01575..941532a2 100644
--- a/org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/reviews/ui/spi/factories/IUiContext.java
+++ b/org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/reviews/ui/spi/factories/IUiContext.java
@@ -11,7 +11,7 @@
package org.eclipse.mylyn.reviews.ui.spi.factories;
-import org.eclipse.mylyn.reviews.core.spi.remote.AbstractRemoteFactoryProvider;
+import org.eclipse.mylyn.reviews.core.spi.remote.review.IReviewRemoteFactoryProvider;
import org.eclipse.mylyn.tasks.core.ITask;
import org.eclipse.mylyn.tasks.core.TaskRepository;
import org.eclipse.mylyn.tasks.core.data.TaskData;
@@ -42,5 +42,5 @@ public interface IUiContext {
TaskRepository getTaskRepository();
- AbstractRemoteFactoryProvider getRemoteFactoryProvider();
+ IReviewRemoteFactoryProvider getFactoryProvider();
}
diff --git a/org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/reviews/ui/spi/remote/RemoteUiService.java b/org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/reviews/ui/spi/remote/RemoteUiService.java
index 1a828ddf..6a4b9f00 100644
--- a/org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/reviews/ui/spi/remote/RemoteUiService.java
+++ b/org.eclipse.mylyn.reviews.ui/src/org/eclipse/mylyn/reviews/ui/spi/remote/RemoteUiService.java
@@ -22,12 +22,12 @@ import org.eclipse.swt.widgets.Display;
public class RemoteUiService extends JobRemoteService {
@Override
- public void modelExec(final Runnable runnable) {
- getDisplayThread().asyncExec(new Runnable() {
- public void run() {
- runnable.run();
- }
- });
+ public void modelExec(final Runnable runnable, boolean block) {
+ if (block) {
+ getDisplayThread().syncExec(runnable);
+ } else {
+ getDisplayThread().asyncExec(runnable);
+ }
}
private Display getDisplayThread() {

Back to the top