Merge "Asynchronous diff rendering"
diff --git a/Documentation/dev-plugins.txt b/Documentation/dev-plugins.txt
index 2a58041..0b275c1 100644
--- a/Documentation/dev-plugins.txt
+++ b/Documentation/dev-plugins.txt
@@ -633,7 +633,7 @@
@Singleton
public class SampleOperator
implements ChangeQueryBuilder.ChangeOperatorFactory {
- public static class MyPredicate extends OperatorPredicate<ChangeData> {
+ public static class MyPredicate extends OperatorChangePredicate<ChangeData> {
...
}
diff --git a/Documentation/rest-api-accounts.txt b/Documentation/rest-api-accounts.txt
index 73bb72d..59fb282 100644
--- a/Documentation/rest-api-accounts.txt
+++ b/Documentation/rest-api-accounts.txt
@@ -7,19 +7,71 @@
[[account-endpoints]]
== Account Endpoints
-[[suggest-account]]
-=== Suggest Account
+[[query-account]]
+=== Query Account
--
'GET /accounts/'
--
-Suggest users for a given query `q` and result limit `n`. If result
-limit is not passed, then the default 10 is used. Returns a list of
-matching link:#account-info[AccountInfo] entities.
+Queries accounts visible to the caller. The
+link:user-search-accounts.html#_search_operators[query string] must be
+provided by the `q` parameter. The `n` parameter can be used to limit
+the returned results.
+
+As result a list of link:#account-info[AccountInfo] entities is
+returned.
.Request
----
- GET /accounts/?q=John HTTP/1.0
+ GET /accounts/?q=name:John+email:example.com&n=2 HTTP/1.0
+----
+
+.Response
+----
+ HTTP/1.1 200 OK
+ Content-Disposition: attachment
+ Content-Type: application/json; charset=UTF-8
+
+ )]}'
+ [
+ {
+ "_account_id": 1000096,
+ },
+ {
+ "_account_id": 1001439,
+ "_more_accounts": true
+ }
+ ]
+----
+
+If the number of accounts matching the query exceeds either the
+internal limit or a supplied `n` query parameter, the last account
+object has a `_more_accounts: true` JSON field set.
+
+The `S` or `start` query parameter can be supplied to skip a number
+of accounts from the list.
+
+Additional fields can be obtained by adding `o` parameters, each
+option slows down the query response time to the client so they are
+generally disabled by default. Optional fields are:
+
+[[details]]
+--
+* `DETAILS`: Includes full name, preferred email, username and avatars
+for each account.
+--
+
+[[suggest-account]]
+To get account suggestions set the parameter `suggest` and provide the
+typed substring as query `q`. If a result limit `n` is not specified,
+then the default 10 is used.
+
+For account suggestions link:#details[account details] are always
+returned.
+
+.Request
+----
+ GET /accounts/?suggest&q=John HTTP/1.0
----
.Response
@@ -1879,7 +1931,7 @@
[[account-detail-info]]
=== AccountDetailInfo
-The `AccountDetailInfo` entity contains detailled information about an
+The `AccountDetailInfo` entity contains detailed information about an
account.
`AccountDetailInfo` has the same fields as link:#account-info[
@@ -1898,20 +1950,29 @@
The `AccountInfo` entity contains information about an account.
[options="header",cols="1,^1,5"]
-|===========================
-|Field Name ||Description
-|`_account_id` ||The numeric ID of the account.
-|`name` |optional|The full name of the user. +
-Only set if link:rest-api-changes.html#detailed-accounts[detailed
-account information] is requested.
-|`email` |optional|
+|=============================
+|Field Name ||Description
+|`_account_id` ||The numeric ID of the account.
+|`name` |optional|The full name of the user. +
+Only set if detailed account information is requested. +
+See option link:rest-api-changes.html#detailed-accounts[
+DETAILED_ACCOUNTS] for change queries +
+and option link:#detaileds[DETAILS] for account queries.
+|`email` |optional|
The email address the user prefers to be contacted through. +
-Only set if link:rest-api-changes.html#detailed-accounts[detailed
-account information] is requested.
-|`username` |optional|The username of the user. +
-Only set if link:rest-api-changes.html#detailed-accounts[detailed
-account information] is requested.
-|===========================
+Only set if detailed account information is requested. +
+See option link:rest-api-changes.html#detailed-accounts[
+DETAILED_ACCOUNTS] for change queries +
+and option link:#detaileds[DETAILS] for account queries.
+|`username` |optional|The username of the user. +
+Only set if detailed account information is requested. +
+See option link:rest-api-changes.html#detailed-accounts[
+DETAILED_ACCOUNTS] for change queries +
+and option link:#detaileds[DETAILS] for account queries.
+|`_more_accounts`|optional, not set if `false`|
+Whether the query would deliver more results if not limited. +
+Only set on the last account that is returned.
+|=============================
[[account-input]]
=== AccountInput
diff --git a/Documentation/rest-api-changes.txt b/Documentation/rest-api-changes.txt
index 039a05f..aad6113 100644
--- a/Documentation/rest-api-changes.txt
+++ b/Documentation/rest-api-changes.txt
@@ -353,6 +353,7 @@
"current_revision": "184ebe53805e102605d11f6b143486d15c23a09c",
"revisions": {
"184ebe53805e102605d11f6b143486d15c23a09c": {
+ "kind": "REWORK",
"_number": 1,
"ref": "refs/changes/97/97/1",
"fetch": {
@@ -936,6 +937,7 @@
"current_revision": "27cc4558b5a3d3387dd11ee2df7a117e7e581822",
"revisions": {
"27cc4558b5a3d3387dd11ee2df7a117e7e581822": {
+ "kind": "REWORK",
"_number": 2,
"ref": "refs/changes/99/4799/2",
"fetch": {
@@ -1314,6 +1316,7 @@
"current_revision": "9adb9f4c7b40eeee0646e235de818d09164d7379",
"revisions": {
"9adb9f4c7b40eeee0646e235de818d09164d7379": {
+ "kind": "REWORK",
"_number": 1,
"created": "2015-05-01 15:39:57.979000000",
"uploader": {
@@ -1432,6 +1435,7 @@
"current_revision": "1bd7c12a38854a2c6de426feec28800623f492c4",
"revisions": {
"1bd7c12a38854a2c6de426feec28800623f492c4": {
+ "kind": "REWORK",
"_number": 1,
"created": "2015-05-01 15:39:57.979000000",
"uploader": {
@@ -2659,12 +2663,14 @@
"current_revision": "674ac754f91e64a0efb8087e59a176484bd534d1",
"revisions": {
"674ac754f91e64a0efb8087e59a176484bd534d1": {
- "_number": 2,
- "ref": "refs/changes/65/3965/2",
- "fetch": {
- "http": {
- "url": "http://gerrit/myProject",
- "ref": "refs/changes/65/3965/2"
+ "kind": "REWORK",
+ "_number": 2,
+ "ref": "refs/changes/65/3965/2",
+ "fetch": {
+ "http": {
+ "url": "http://gerrit/myProject",
+ "ref": "refs/changes/65/3965/2"
+ }
}
}
}
@@ -2863,6 +2869,7 @@
"current_revision": "27cc4558b5a3d3387dd11ee2df7a117e7e581822",
"revisions": {
"27cc4558b5a3d3387dd11ee2df7a117e7e581822": {
+ "kind": "REWORK",
"_number": 2,
"ref": "refs/changes/99/4799/2",
"fetch": {
@@ -4974,6 +4981,8 @@
|===========================
|Field Name ||Description
|`draft` |not set if `false`|Whether the patch set is a draft.
+|`kind` ||The change kind. Valid values are `REWORK`, `TRIVIAL_REBASE`,
+`MERGE_FIRST_PARENT_UPDATE`, `NO_CODE_CHANGE`, and `NO_CHANGE`.
|`_number` ||The patch set number.
|`created` ||
The link:rest-api.html#timestamp[timestamp] of when the patch set was
diff --git a/Documentation/user-search-accounts.txt b/Documentation/user-search-accounts.txt
new file mode 100644
index 0000000..15d87b0
--- /dev/null
+++ b/Documentation/user-search-accounts.txt
@@ -0,0 +1,83 @@
+= Gerrit Code Review - Searching Accounts
+
+== Basic Change Search
+
+Similar to many popular search engines on the web, just enter some
+text and let Gerrit figure out the meaning:
+
+[options="header"]
+|=============================================================
+|Description | Examples
+|Name | John
+|Email address | jdoe@example.com
+|Username | jdoe
+|Account-Id | 1000096
+|Own account | self
+|=============================================================
+
+[[search-operators]]
+== Search Operators
+
+Operators act as restrictions on the search. As more operators
+are added to the same query string, they further restrict the
+returned results. Search can also be performed by typing only a
+text with no operator, which will match against a variety of fields.
+
+[[email]]
+email:'EMAIL'::
++
+Matches accounts that have the email address 'EMAIL' or an email
+address that starts with 'EMAIL'.
+
+[[is]]
+[[is-active]]
+is:active::
++
+Matches accounts that are active.
+
+[[is-inactive]]
+is:inactive::
++
+Matches accounts that are inactive.
+
+[[name]]
+name:'NAME'::
++
+Matches accounts that have any name part 'NAME'. The name parts consist
+of any part of the full name and the email addresses.
+
+[[username]]
+username:'USERNAME'::
++
+Matches accounts that have the username 'USERNAME'.
+
+== Magical Operators
+
+[[is-visible]]
+is:visible::
++
+Magical internal flag to prove the current user has access to read
+the change. This flag is always added to any query.
+
+[[is-active-magic]]
+is:active::
++
+Matches accounts that are active. If neither link:#is-active[is:active]
+nor link:#is-inactive[is:inactive] is contained in a query, `is:active`
+is automatically added so that by default only active accounts are
+matched.
+
+[[limit]]
+limit:'CNT'::
++
+Limit the returned results to no more than 'CNT' records. This is
+automatically set to the page size configured in the current user's
+preferences. Including it in a web query may lead to unpredictable
+results with regards to pagination.
+
+GERRIT
+------
+Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
index 1b325ec..488f9f9 100644
--- a/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
+++ b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
@@ -867,6 +867,11 @@
}
}
+ protected RevCommit getRemoteHead(String project, String branch)
+ throws Exception {
+ return getRemoteHead(new Project.NameKey(project), branch);
+ }
+
protected RevCommit getRemoteHead() throws Exception {
return getRemoteHead(project, "master");
}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/ChangeIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/ChangeIT.java
index 1a17305..b12b9be 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/ChangeIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/ChangeIT.java
@@ -653,6 +653,35 @@
}
@Test
+ public void removeReviewerNoVotes() throws Exception {
+ PushOneCommit.Result r = createChange();
+ String changeId = r.getChangeId();
+ gApi.changes()
+ .id(changeId)
+ .addReviewer(user.getId().toString());
+
+ // ReviewerState will vary between ReviewDb and NoteDb; we just care that it
+ // shows up somewhere.
+ Iterable<AccountInfo> reviewers = Iterables.concat(
+ gApi.changes().id(changeId).get().reviewers.values());
+ assertThat(reviewers).hasSize(1);
+ assertThat(reviewers.iterator().next()._accountId)
+ .isEqualTo(user.getId().get());
+
+ gApi.changes()
+ .id(changeId)
+ .reviewer(user.getId().toString())
+ .remove();
+ assertThat(gApi.changes().id(changeId).get().reviewers.isEmpty());
+
+ exception.expect(ResourceNotFoundException.class);
+ gApi.changes()
+ .id(changeId)
+ .reviewer(user.getId().toString())
+ .remove();
+ }
+
+ @Test
public void removeReviewer() throws Exception {
PushOneCommit.Result r = createChange();
String changeId = r.getChangeId();
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/project/ProjectIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/project/ProjectIT.java
index 4be4d52..a3de2b4 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/project/ProjectIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/project/ProjectIT.java
@@ -36,23 +36,58 @@
public class ProjectIT extends AbstractDaemonTest {
@Test
- public void createProjectFoo() throws Exception {
+ public void createProject() throws Exception {
String name = name("foo");
assertThat(name).isEqualTo(
gApi.projects()
.create(name)
.get()
.name);
+
+ RevCommit head = getRemoteHead(name, "refs/meta/config");
+ eventRecorder.assertRefUpdatedEvents(name, "refs/meta/config",
+ null, head);
+
+ eventRecorder.assertRefUpdatedEvents(name, "refs/heads/master",
+ new String[]{});
}
@Test
- public void createProjectFooWithGitSuffix() throws Exception {
+ public void createProjectWithGitSuffix() throws Exception {
String name = name("foo");
assertThat(name).isEqualTo(
gApi.projects()
.create(name + ".git")
.get()
.name);
+
+ RevCommit head = getRemoteHead(name, "refs/meta/config");
+ eventRecorder.assertRefUpdatedEvents(name, "refs/meta/config",
+ null, head);
+
+ eventRecorder.assertRefUpdatedEvents(name, "refs/heads/master",
+ new String[]{});
+ }
+
+ @Test
+ public void createProjectWithInitialCommit() throws Exception {
+ String name = name("foo");
+ ProjectInput input = new ProjectInput();
+ input.name = name;
+ input.createEmptyCommit = true;
+ assertThat(name).isEqualTo(
+ gApi.projects()
+ .create(input)
+ .get()
+ .name);
+
+ RevCommit head = getRemoteHead(name, "refs/meta/config");
+ eventRecorder.assertRefUpdatedEvents(name, "refs/meta/config",
+ null, head);
+
+ head = getRemoteHead(name, "refs/heads/master");
+ eventRecorder.assertRefUpdatedEvents(name, "refs/heads/master",
+ null, head);
}
@Test
@@ -67,6 +102,15 @@
}
@Test
+ public void createProjectNoNameInInput() throws Exception {
+ ProjectInput in = new ProjectInput();
+ exception.expect(BadRequestException.class);
+ exception.expectMessage("input.name is required");
+ gApi.projects()
+ .create(in);
+ }
+
+ @Test
public void createProjectDuplicate() throws Exception {
ProjectInput in = new ProjectInput();
in.name = name("baz");
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/AccessIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/AccessIT.java
index 0fc0712..975dc2b 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/AccessIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/AccessIT.java
@@ -16,6 +16,8 @@
import static com.google.common.truth.Truth.assertThat;
import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.GitUtil;
+import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.common.data.AccessSection;
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.common.data.Permission;
@@ -33,6 +35,9 @@
import com.google.gerrit.server.config.AllProjectsNameProvider;
import com.google.gerrit.server.group.SystemGroupBackend;
+import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
+import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.revwalk.RevCommit;
import org.junit.Before;
@@ -355,6 +360,61 @@
.containsNoneIn(accessSectionInfo.permissions.keySet());
}
+ @Test
+ public void unknownPermissionRemainsUnchanged() throws Exception {
+ String access = "access";
+ String unknownPermission = "unknownPermission";
+ String registeredUsers = "group Registered Users";
+ String refsFor = "refs/for/*";
+ // Clone repository to forcefully add permission
+ TestRepository<InMemoryRepository> allProjectsRepo =
+ cloneProject(allProjects, admin);
+
+ // Fetch permission ref
+ GitUtil.fetch(allProjectsRepo, "refs/meta/config:cfg");
+ allProjectsRepo.reset("cfg");
+
+ // Load current permissions
+ String config = gApi.projects()
+ .name(allProjects.get())
+ .branch("refs/meta/config").file("project.config").asString();
+
+ // Append and push unknown permission
+ Config cfg = new Config();
+ cfg.fromText(config);
+ cfg.setString(access, refsFor, unknownPermission, registeredUsers);
+ config = cfg.toText();
+ PushOneCommit push = pushFactory.create(
+ db, admin.getIdent(), allProjectsRepo, "Subject", "project.config",
+ config);
+ push.to("refs/meta/config").assertOkStatus();
+
+ // Verify that unknownPermission is present
+ config = gApi.projects()
+ .name(allProjects.get())
+ .branch("refs/meta/config").file("project.config").asString();
+ cfg.fromText(config);
+ assertThat(cfg.getString(access, refsFor, unknownPermission))
+ .isEqualTo(registeredUsers);
+
+ // Make permission change through API
+ ProjectAccessInput accessInput = newProjectAccessInput();
+ AccessSectionInfo accessSectionInfo = createDefaultAccessSectionInfo();
+ accessInput.add.put(refsFor, accessSectionInfo);
+ gApi.projects().name(allProjects.get()).access(accessInput);
+ accessInput.add.clear();
+ accessInput.remove.put(refsFor, accessSectionInfo);
+ gApi.projects().name(allProjects.get()).access(accessInput);
+
+ // Verify that unknownPermission is still present
+ config = gApi.projects()
+ .name(allProjects.get())
+ .branch("refs/meta/config").file("project.config").asString();
+ cfg.fromText(config);
+ assertThat(cfg.getString(access, refsFor, unknownPermission))
+ .isEqualTo(registeredUsers);
+ }
+
private ProjectAccessInput newProjectAccessInput() {
ProjectAccessInput p = new ProjectAccessInput();
p.add = new HashMap<>();
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/notedb/ChangeRebuilderIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/notedb/ChangeRebuilderIT.java
index d5f9b6e..d8f2885 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/notedb/ChangeRebuilderIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/notedb/ChangeRebuilderIT.java
@@ -45,6 +45,7 @@
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.client.RevId;
import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.reviewdb.server.ReviewDbUtil;
import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.PatchLineCommentsUtil;
import com.google.gerrit.server.change.PostReview;
@@ -60,7 +61,6 @@
import com.google.gerrit.server.notedb.NoteDbChangeState;
import com.google.gerrit.server.notedb.NoteDbUpdateManager;
import com.google.gerrit.server.notedb.TestChangeRebuilderWrapper;
-import com.google.gerrit.server.schema.DisabledChangesReviewDbWrapper;
import com.google.gerrit.testutil.ConfigSuite;
import com.google.gerrit.testutil.NoteDbChecker;
import com.google.gerrit.testutil.NoteDbMode;
@@ -264,13 +264,13 @@
// First write doesn't create the ref, but rebuilding works.
checker.assertNoChangeRef(project, id);
- assertThat(unwrapDb().changes().get(id).getNoteDbState()).isNull();
+ assertThat(getUnwrappedDb().changes().get(id).getNoteDbState()).isNull();
checker.rebuildAndCheckChanges(id);
// Now that there is a ref, writes are "turned on" for this change, and
// NoteDb stays up to date without explicit rebuilding.
gApi.changes().id(id.get()).topic(name("new-topic"));
- assertThat(unwrapDb().changes().get(id).getNoteDbState()).isNotNull();
+ assertThat(getUnwrappedDb().changes().get(id).getNoteDbState()).isNotNull();
checker.checkChanges(id);
}
@@ -323,13 +323,13 @@
Change.Id id = r.getPatchSetId().getParentKey();
ObjectId changeMetaId = getMetaRef(project, changeMetaRef(id));
- assertThat(unwrapDb().changes().get(id).getNoteDbState()).isEqualTo(
+ assertThat(getUnwrappedDb().changes().get(id).getNoteDbState()).isEqualTo(
changeMetaId.name());
putDraft(user, id, 1, "comment by user");
ObjectId userDraftsId = getMetaRef(
allUsers, refsDraftComments(id, user.getId()));
- assertThat(unwrapDb().changes().get(id).getNoteDbState()).isEqualTo(
+ assertThat(getUnwrappedDb().changes().get(id).getNoteDbState()).isEqualTo(
changeMetaId.name()
+ "," + user.getId() + "=" + userDraftsId.name());
@@ -337,7 +337,7 @@
ObjectId adminDraftsId = getMetaRef(
allUsers, refsDraftComments(id, admin.getId()));
assertThat(admin.getId().get()).isLessThan(user.getId().get());
- assertThat(unwrapDb().changes().get(id).getNoteDbState()).isEqualTo(
+ assertThat(getUnwrappedDb().changes().get(id).getNoteDbState()).isEqualTo(
changeMetaId.name()
+ "," + admin.getId() + "=" + adminDraftsId.name()
+ "," + user.getId() + "=" + userDraftsId.name());
@@ -345,7 +345,7 @@
putDraft(admin, id, 2, "revised comment by admin");
adminDraftsId = getMetaRef(
allUsers, refsDraftComments(id, admin.getId()));
- assertThat(unwrapDb().changes().get(id).getNoteDbState()).isEqualTo(
+ assertThat(getUnwrappedDb().changes().get(id).getNoteDbState()).isEqualTo(
changeMetaId.name()
+ "," + admin.getId() + "=" + adminDraftsId.name()
+ "," + user.getId() + "=" + userDraftsId.name());
@@ -374,7 +374,7 @@
// Check that the bundles are equal.
ChangeBundle actual = ChangeBundle.fromNotes(
plcUtil, notesFactory.create(dbProvider.get(), project, id));
- ChangeBundle expected = ChangeBundle.fromReviewDb(unwrapDb(), id);
+ ChangeBundle expected = ChangeBundle.fromReviewDb(getUnwrappedDb(), id);
assertThat(actual.differencesFrom(expected)).isEmpty();
}
@@ -425,7 +425,7 @@
// Check that the bundles are equal.
ChangeNotes notes = notesFactory.create(dbProvider.get(), project, id);
ChangeBundle actual = ChangeBundle.fromNotes(plcUtil, notes);
- ChangeBundle expected = ChangeBundle.fromReviewDb(unwrapDb(), id);
+ ChangeBundle expected = ChangeBundle.fromReviewDb(getUnwrappedDb(), id);
assertThat(actual.differencesFrom(expected)).isEmpty();
assertThat(
Iterables.transform(
@@ -464,7 +464,7 @@
// Check that the bundles are equal.
ChangeBundle actual = ChangeBundle.fromNotes(
plcUtil, notesFactory.create(dbProvider.get(), project, id));
- ChangeBundle expected = ChangeBundle.fromReviewDb(unwrapDb(), id);
+ ChangeBundle expected = ChangeBundle.fromReviewDb(getUnwrappedDb(), id);
assertThat(actual.differencesFrom(expected)).isEmpty();
}
@@ -494,7 +494,7 @@
assertChangeUpToDate(false, id);
assertThat(getMetaRef(project, changeMetaRef(id))).isEqualTo(oldMetaId);
ChangeBundle actual = ChangeBundle.fromNotes(plcUtil, notes);
- ChangeBundle expected = ChangeBundle.fromReviewDb(unwrapDb(), id);
+ ChangeBundle expected = ChangeBundle.fromReviewDb(getUnwrappedDb(), id);
assertThat(actual.differencesFrom(expected)).isEmpty();
assertChangeUpToDate(false, id);
@@ -536,7 +536,7 @@
// Not up to date, but the actual returned state matches anyway.
assertDraftsUpToDate(false, id, user);
ChangeBundle actual = ChangeBundle.fromNotes(plcUtil, notes);
- ChangeBundle expected = ChangeBundle.fromReviewDb(unwrapDb(), id);
+ ChangeBundle expected = ChangeBundle.fromReviewDb(getUnwrappedDb(), id);
assertThat(actual.differencesFrom(expected)).isEmpty();
// Another rebuild attempt succeeds
@@ -564,7 +564,7 @@
setNotesMigration(false, false);
putDraft(user, id, 1, "second comment by user");
- ReviewDb db = unwrapDb();
+ ReviewDb db = getUnwrappedDb();
Change c = db.changes().get(id);
// Leave change meta ID alone so DraftCommentNotes does the rebuild.
NoteDbChangeState bogusState = new NoteDbChangeState(
@@ -591,7 +591,7 @@
assertChangeUpToDate(true, id);
assertDraftsUpToDate(false, id, user);
ChangeBundle actual = ChangeBundle.fromNotes(plcUtil, notes);
- ChangeBundle expected = ChangeBundle.fromReviewDb(unwrapDb(), id);
+ ChangeBundle expected = ChangeBundle.fromReviewDb(getUnwrappedDb(), id);
assertThat(actual.differencesFrom(expected)).isEmpty();
// Another rebuild attempt succeeds
@@ -950,7 +950,7 @@
}
private void setInvalidNoteDbState(Change.Id id) throws Exception {
- ReviewDb db = unwrapDb();
+ ReviewDb db = getUnwrappedDb();
Change c = db.changes().get(id);
// In reality we would have NoteDb writes enabled, which would write a real
// state into this field. For tests however, we turn NoteDb writes off, so
@@ -963,7 +963,7 @@
private void assertChangeUpToDate(boolean expected, Change.Id id)
throws Exception {
try (Repository repo = repoManager.openRepository(project)) {
- Change c = unwrapDb().changes().get(id);
+ Change c = getUnwrappedDb().changes().get(id);
assertThat(c).isNotNull();
assertThat(c.getNoteDbState()).isNotNull();
assertThat(NoteDbChangeState.parse(c).isChangeUpToDate(
@@ -975,7 +975,7 @@
private void assertDraftsUpToDate(boolean expected, Change.Id changeId,
TestAccount account) throws Exception {
try (Repository repo = repoManager.openRepository(allUsers)) {
- Change c = unwrapDb().changes().get(changeId);
+ Change c = getUnwrappedDb().changes().get(changeId);
assertThat(c).isNotNull();
assertThat(c.getNoteDbState()).isNotNull();
NoteDbChangeState state = NoteDbChangeState.parse(c);
@@ -1052,11 +1052,8 @@
return msg;
}
- private ReviewDb unwrapDb() {
+ private ReviewDb getUnwrappedDb() {
ReviewDb db = dbProvider.get();
- if (db instanceof DisabledChangesReviewDbWrapper) {
- db = ((DisabledChangesReviewDbWrapper) db).unsafeGetDelegate();
- }
- return db;
+ return ReviewDbUtil.unwrapDb(db);
}
}
diff --git a/gerrit-cache-h2/src/main/java/com/google/gerrit/server/cache/h2/H2CacheImpl.java b/gerrit-cache-h2/src/main/java/com/google/gerrit/server/cache/h2/H2CacheImpl.java
index 726a5f1..838f42c 100644
--- a/gerrit-cache-h2/src/main/java/com/google/gerrit/server/cache/h2/H2CacheImpl.java
+++ b/gerrit-cache-h2/src/main/java/com/google/gerrit/server/cache/h2/H2CacheImpl.java
@@ -14,11 +14,13 @@
package com.google.gerrit.server.cache.h2;
+import com.google.common.base.Throwables;
import com.google.common.cache.AbstractLoadingCache;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.CacheStats;
import com.google.common.cache.LoadingCache;
+import com.google.common.collect.ImmutableSet;
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnel;
import com.google.common.hash.Funnels;
@@ -81,6 +83,9 @@
PersistentCache {
private static final Logger log = LoggerFactory.getLogger(H2CacheImpl.class);
+ private static final ImmutableSet<String> OLD_CLASS_NAMES = ImmutableSet.of(
+ "com.google.gerrit.server.change.ChangeKind");
+
private final Executor executor;
private final SqlStore<K, V> store;
private final TypeLiteral<K> keyType;
@@ -472,7 +477,9 @@
c.get.clearParameters();
}
} catch (SQLException e) {
- log.warn("Cannot read cache " + url + " for " + key, e);
+ if (!isOldClassNameError(e)) {
+ log.warn("Cannot read cache " + url + " for " + key, e);
+ }
c = close(c);
return null;
} finally {
@@ -480,6 +487,16 @@
}
}
+ private static boolean isOldClassNameError(Throwable t) {
+ for (Throwable c : Throwables.getCausalChain(t)) {
+ if (c instanceof ClassNotFoundException
+ && OLD_CLASS_NAMES.contains(c.getMessage())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private boolean expired(Timestamp created) {
if (expireAfterWrite == 0) {
return false;
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/accounts/Accounts.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/accounts/Accounts.java
index 0d2e025..fe8a1d8 100644
--- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/accounts/Accounts.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/accounts/Accounts.java
@@ -14,10 +14,13 @@
package com.google.gerrit.extensions.api.accounts;
+import com.google.gerrit.extensions.client.ListAccountsOption;
import com.google.gerrit.extensions.common.AccountInfo;
import com.google.gerrit.extensions.restapi.NotImplementedException;
import com.google.gerrit.extensions.restapi.RestApiException;
+import java.util.Arrays;
+import java.util.EnumSet;
import java.util.List;
public interface Accounts {
@@ -70,6 +73,25 @@
throws RestApiException;
/**
+ * Queries users.
+ * <p>
+ * Example code:
+ * {@code query().withQuery("name:John email:example.com").withLimit(5).get()}
+ *
+ * @return API for setting parameters and getting result.
+ */
+ QueryRequest query() throws RestApiException;
+
+ /**
+ * Queries users.
+ * <p>
+ * Shortcut API for {@code query().withQuery(String)}.
+ *
+ * @see #query()
+ */
+ QueryRequest query(String query) throws RestApiException;
+
+ /**
* API for setting parameters and getting result.
* Used for {@code suggestAccounts()}.
*
@@ -113,6 +135,84 @@
}
/**
+ * API for setting parameters and getting result.
+ * Used for {@code query()}.
+ *
+ * @see #query()
+ */
+ abstract class QueryRequest {
+ private String query;
+ private int limit;
+ private int start;
+ private EnumSet<ListAccountsOption> options =
+ EnumSet.noneOf(ListAccountsOption.class);
+
+ /**
+ * Executes query and returns a list of accounts.
+ */
+ public abstract List<AccountInfo> get() throws RestApiException;
+
+ /**
+ * Set query.
+ *
+ * @param query needs to be in human-readable form.
+ */
+ public QueryRequest withQuery(String query) {
+ this.query = query;
+ return this;
+ }
+
+ /**
+ * Set limit for returned list of accounts.
+ * Optional; server-default is used when not provided.
+ */
+ public QueryRequest withLimit(int limit) {
+ this.limit = limit;
+ return this;
+ }
+
+ /**
+ * Set number of accounts to skip.
+ * Optional; no accounts are skipped when not provided.
+ */
+ public QueryRequest withStart(int start) {
+ this.start = start;
+ return this;
+ }
+
+ public QueryRequest withOption(ListAccountsOption options) {
+ this.options.add(options);
+ return this;
+ }
+
+ public QueryRequest withOptions(ListAccountsOption... options) {
+ this.options.addAll(Arrays.asList(options));
+ return this;
+ }
+
+ public QueryRequest withOptions(EnumSet<ListAccountsOption> options) {
+ this.options = options;
+ return this;
+ }
+
+ public String getQuery() {
+ return query;
+ }
+
+ public int getLimit() {
+ return limit;
+ }
+
+ public int getStart() {
+ return start;
+ }
+
+ public EnumSet<ListAccountsOption> getOptions() {
+ return options;
+ }
+ }
+
+ /**
* A default implementation which allows source compatibility
* when adding new methods to the interface.
**/
@@ -142,5 +242,15 @@
throws RestApiException {
throw new NotImplementedException();
}
+
+ @Override
+ public QueryRequest query() throws RestApiException {
+ throw new NotImplementedException();
+ }
+
+ @Override
+ public QueryRequest query(String query) throws RestApiException {
+ throw new NotImplementedException();
+ }
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeKind.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/client/ChangeKind.java
similarity index 95%
rename from gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeKind.java
rename to gerrit-extension-api/src/main/java/com/google/gerrit/extensions/client/ChangeKind.java
index 63f6095..e58e005 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeKind.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/client/ChangeKind.java
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.server.change;
+package com.google.gerrit.extensions.client;
/** Operation performed by a change relative to its parent. */
public enum ChangeKind {
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/client/ListAccountsOption.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/client/ListAccountsOption.java
new file mode 100644
index 0000000..bc3679c
--- /dev/null
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/client/ListAccountsOption.java
@@ -0,0 +1,59 @@
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.extensions.client;
+
+import java.util.EnumSet;
+import java.util.Set;
+
+/** Output options available for retrieval of account details. */
+public enum ListAccountsOption {
+ /** Return detailed account properties. */
+ DETAILS(0);
+
+ private final int value;
+
+ ListAccountsOption(int v) {
+ this.value = v;
+ }
+
+ public int getValue() {
+ return value;
+ }
+
+ public static EnumSet<ListAccountsOption> fromBits(int v) {
+ EnumSet<ListAccountsOption> r = EnumSet.noneOf(ListAccountsOption.class);
+ for (ListAccountsOption o : ListAccountsOption.values()) {
+ if ((v & (1 << o.value)) != 0) {
+ r.add(o);
+ v &= ~(1 << o.value);
+ }
+ if (v == 0) {
+ return r;
+ }
+ }
+ if (v != 0) {
+ throw new IllegalArgumentException("unknown " + Integer.toHexString(v));
+ }
+ return r;
+ }
+
+ public static int toBits(Set<ListAccountsOption> set) {
+ int r = 0;
+ for (ListAccountsOption o : set) {
+ r |= 1 << o.value;
+ }
+ return r;
+ }
+}
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/AccountInfo.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/AccountInfo.java
index 39d98de..8322eaf 100644
--- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/AccountInfo.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/AccountInfo.java
@@ -22,6 +22,7 @@
public String email;
public String username;
public List<AvatarInfo> avatars;
+ public Boolean _moreAccounts;
public AccountInfo(Integer id) {
this._accountId = id;
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/RevisionInfo.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/RevisionInfo.java
index 025c623..34a1e63 100644
--- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/RevisionInfo.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/RevisionInfo.java
@@ -14,12 +14,15 @@
package com.google.gerrit.extensions.common;
+import com.google.gerrit.extensions.client.ChangeKind;
+
import java.sql.Timestamp;
import java.util.Map;
public class RevisionInfo {
public transient boolean isCurrent;
public Boolean draft;
+ public ChangeKind kind;
public int _number;
public Timestamp created;
public AccountInfo uploader;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountApi.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountApi.java
index a822cfc..acd2e78 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountApi.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountApi.java
@@ -53,6 +53,7 @@
public static void suggest(String query, int limit,
AsyncCallback<JsArray<AccountInfo>> cb) {
new RestApi("/accounts/")
+ .addParameterTrue("suggest")
.addParameter("q", query)
.addParameter("n", limit)
.background()
diff --git a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java
index bc8efc6..4747455 100644
--- a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java
+++ b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java
@@ -451,6 +451,12 @@
cd.setChangedLines(
added.numericValue().intValue(),
deleted.numericValue().intValue());
+ } else {
+ // No ChangedLines stored, likely due to failure during reindexing, for
+ // example due to LargeObjectException. But we know the field was
+ // requested, so update ChangeData to prevent callers from trying to
+ // lazily load it, as that would probably also fail.
+ cd.setNoChangedLines();
}
}
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/RebuildNoteDb.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/RebuildNoteDb.java
index 9991a76..0adb1af 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/RebuildNoteDb.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/RebuildNoteDb.java
@@ -14,6 +14,7 @@
package com.google.gerrit.pgm;
+import static com.google.gerrit.reviewdb.server.ReviewDbUtil.unwrapDb;
import static com.google.gerrit.server.schema.DataSourceProvider.Context.MULTI_USER;
import com.google.common.base.Function;
@@ -48,7 +49,6 @@
import com.google.gerrit.server.index.change.ReindexAfterUpdate;
import com.google.gerrit.server.notedb.ChangeRebuilder;
import com.google.gerrit.server.notedb.NotesMigration;
-import com.google.gerrit.server.schema.DisabledChangesReviewDbWrapper;
import com.google.gwtorm.server.OrmException;
import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.Inject;
@@ -153,7 +153,7 @@
new Callable<Boolean>() {
@Override
public Boolean call() {
- try (ReviewDb db = unwrap(schemaFactory.open())) {
+ try (ReviewDb db = unwrapDb(schemaFactory.open())) {
return rebuilder.rebuildProject(
db, changesByProject, project, allUsersRepo);
} catch (Exception e) {
@@ -234,7 +234,7 @@
ArrayListMultimap.create();
try (ReviewDb db = schemaFactory.open()) {
if (projects.isEmpty() && !changes.isEmpty()) {
- Iterable<Change> todo = unwrap(db).changes().get(
+ Iterable<Change> todo = unwrapDb(db).changes().get(
Iterables.transform(changes, new Function<Integer, Change.Id>() {
@Override
public Change.Id apply(Integer in) {
@@ -245,7 +245,7 @@
changesByProject.put(c.getProject(), c.getId());
}
} else {
- for (Change c : unwrap(db).changes().all()) {
+ for (Change c : unwrapDb(db).changes().all()) {
boolean include = false;
if (projects.isEmpty() && changes.isEmpty()) {
include = true;
@@ -263,11 +263,4 @@
return ImmutableMultimap.copyOf(changesByProject);
}
}
-
- private static ReviewDb unwrap(ReviewDb db) {
- if (db instanceof DisabledChangesReviewDbWrapper) {
- db = ((DisabledChangesReviewDbWrapper) db).unsafeGetDelegate();
- }
- return db;
- }
}
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/BaseInit.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/BaseInit.java
index 540ba0b..36163b6 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/BaseInit.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/BaseInit.java
@@ -14,6 +14,7 @@
package com.google.gerrit.pgm.init;
+import static com.google.gerrit.reviewdb.server.ReviewDbUtil.unwrapDb;
import static com.google.gerrit.server.schema.DataSourceProvider.Context.SINGLE_USER;
import static com.google.inject.Scopes.SINGLETON;
import static com.google.inject.Stage.PRODUCTION;
@@ -399,7 +400,7 @@
System.err.flush();
} else if (ui.yesno(true, "%s\nExecute now", msg)) {
- try (JdbcSchema db = (JdbcSchema) schema.open();
+ try (JdbcSchema db = (JdbcSchema) unwrapDb(schema.open());
JdbcExecutor e = new JdbcExecutor(db)) {
for (String sql : pruneList) {
e.execute(sql);
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitPluginStepsLoader.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitPluginStepsLoader.java
index 3476de5..ee20d99 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitPluginStepsLoader.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitPluginStepsLoader.java
@@ -15,7 +15,7 @@
package com.google.gerrit.pgm.init;
import com.google.common.base.MoreObjects;
-import com.google.common.collect.Ordering;
+import com.google.common.collect.ImmutableList;
import com.google.gerrit.extensions.annotations.PluginName;
import com.google.gerrit.pgm.init.api.ConsoleUI;
import com.google.gerrit.pgm.init.api.InitStep;
@@ -30,12 +30,9 @@
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
-import java.nio.file.DirectoryStream;
-import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
import java.util.List;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
@@ -116,23 +113,12 @@
}
private List<Path> scanJarsInPluginsDirectory() {
- if (pluginsDir == null || !Files.isDirectory(pluginsDir)) {
- return Collections.emptyList();
- }
- DirectoryStream.Filter<Path> filter = new DirectoryStream.Filter<Path>() {
- @Override
- public boolean accept(Path entry) throws IOException {
- return entry.getFileName().toString().endsWith(".jar")
- && Files.isRegularFile(entry);
- }
- };
- try (DirectoryStream<Path> paths =
- Files.newDirectoryStream(pluginsDir, filter)) {
- return Ordering.natural().sortedCopy(paths);
+ try {
+ return PluginLoader.listPlugins(pluginsDir, ".jar");
} catch (IOException e) {
ui.message("WARN: Cannot list %s: %s", pluginsDir.toAbsolutePath(),
e.getMessage());
- return Collections.emptyList();
+ return ImmutableList.of();
}
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/DisabledChangesReviewDbWrapper.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/server/DisabledChangesReviewDbWrapper.java
similarity index 93%
rename from gerrit-server/src/main/java/com/google/gerrit/server/schema/DisabledChangesReviewDbWrapper.java
rename to gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/server/DisabledChangesReviewDbWrapper.java
index 658f3bb..b70778e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/DisabledChangesReviewDbWrapper.java
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/server/DisabledChangesReviewDbWrapper.java
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.server.schema;
+package com.google.gerrit.reviewdb.server;
import com.google.common.util.concurrent.CheckedFuture;
import com.google.gerrit.reviewdb.client.Account;
@@ -21,13 +21,6 @@
import com.google.gerrit.reviewdb.client.PatchLineComment;
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.reviewdb.client.PatchSetApproval;
-import com.google.gerrit.reviewdb.server.ChangeAccess;
-import com.google.gerrit.reviewdb.server.ChangeMessageAccess;
-import com.google.gerrit.reviewdb.server.PatchLineCommentAccess;
-import com.google.gerrit.reviewdb.server.PatchSetAccess;
-import com.google.gerrit.reviewdb.server.PatchSetApprovalAccess;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.reviewdb.server.ReviewDbWrapper;
import com.google.gwtorm.server.OrmException;
import com.google.gwtorm.server.ResultSet;
@@ -40,7 +33,7 @@
private final DisabledPatchSetAccess patchSets;
private final DisabledPatchLineCommentAccess patchComments;
- DisabledChangesReviewDbWrapper(ReviewDb db) {
+ public DisabledChangesReviewDbWrapper(ReviewDb db) {
super(db);
changes = new DisabledChangeAccess(delegate.changes());
patchSetApprovals =
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/server/ReviewDbUtil.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/server/ReviewDbUtil.java
index 5d782dd..42d0993 100644
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/server/ReviewDbUtil.java
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/server/ReviewDbUtil.java
@@ -49,6 +49,13 @@
return CHANGE_ID_FUNCTION;
}
+ public static ReviewDb unwrapDb(ReviewDb db) {
+ if (db instanceof DisabledChangesReviewDbWrapper) {
+ return ((DisabledChangesReviewDbWrapper) db).unsafeGetDelegate();
+ }
+ return db;
+ }
+
private ReviewDbUtil() {
}
}
diff --git a/gerrit-server/BUCK b/gerrit-server/BUCK
index 6aa9e2c..4fc578c 100644
--- a/gerrit-server/BUCK
+++ b/gerrit-server/BUCK
@@ -190,6 +190,7 @@
['src/test/java/**/*.java'],
excludes = TESTUTIL + PROLOG_TESTS + PROLOG_TEST_CASE + QUERY_TESTS
),
+ resources = glob(['src/test/resources/com/google/gerrit/server/mail/*']),
deps = TESTUTIL_DEPS + [
':testutil',
'//gerrit-antlr:query_exception',
@@ -202,6 +203,7 @@
'//lib:guava',
'//lib:guava-retrying',
'//lib:protobuf',
+ '//lib/commons:validator',
'//lib/dropwizard:dropwizard-core',
'//lib/guice:guice-assistedinject',
'//lib/prolog:runtime',
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/ApprovalCopier.java b/gerrit-server/src/main/java/com/google/gerrit/server/ApprovalCopier.java
index f4aa4c2..a3a7734 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/ApprovalCopier.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/ApprovalCopier.java
@@ -21,11 +21,11 @@
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Table;
import com.google.gerrit.common.data.LabelType;
+import com.google.gerrit.extensions.client.ChangeKind;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.reviewdb.client.PatchSetApproval;
import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.change.ChangeKind;
import com.google.gerrit.server.change.ChangeKindCache;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.LabelNormalizer;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountsCollection.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountsCollection.java
index d54a999..06cf255 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountsCollection.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountsCollection.java
@@ -40,7 +40,7 @@
private final AccountResolver resolver;
private final AccountControl.Factory accountControlFactory;
private final IdentifiedUser.GenericFactory userFactory;
- private final Provider<SuggestAccounts> list;
+ private final Provider<QueryAccounts> list;
private final DynamicMap<RestView<AccountResource>> views;
private final CreateAccount.Factory createAccountFactory;
@@ -49,7 +49,7 @@
AccountResolver resolver,
AccountControl.Factory accountControlFactory,
IdentifiedUser.GenericFactory userFactory,
- Provider<SuggestAccounts> list,
+ Provider<QueryAccounts> list,
DynamicMap<RestView<AccountResource>> views,
CreateAccount.Factory createAccountFactory) {
this.self = self;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/QueryAccounts.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/QueryAccounts.java
new file mode 100644
index 0000000..7c3ef1e
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/QueryAccounts.java
@@ -0,0 +1,265 @@
+// Copyright (C) 2014 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.account;
+
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableList;
+import com.google.gerrit.extensions.client.ListAccountsOption;
+import com.google.gerrit.extensions.common.AccountInfo;
+import com.google.gerrit.extensions.restapi.BadRequestException;
+import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
+import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.extensions.restapi.TopLevelResource;
+import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.reviewdb.client.AccountExternalId;
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.api.accounts.AccountInfoComparator;
+import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.gerrit.server.index.account.AccountIndex;
+import com.google.gerrit.server.index.account.AccountIndexCollection;
+import com.google.gerrit.server.query.Predicate;
+import com.google.gerrit.server.query.QueryParseException;
+import com.google.gerrit.server.query.QueryResult;
+import com.google.gerrit.server.query.account.AccountQueryBuilder;
+import com.google.gerrit.server.query.account.AccountQueryProcessor;
+import com.google.gwtorm.server.OrmException;
+import com.google.inject.Inject;
+
+import org.eclipse.jgit.lib.Config;
+import org.kohsuke.args4j.Option;
+
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+public class QueryAccounts implements RestReadView<TopLevelResource> {
+ private static final int MAX_SUGGEST_RESULTS = 100;
+ private static final String MAX_SUFFIX = "\u9fa5";
+
+ private final AccountControl accountControl;
+ private final AccountLoader.Factory accountLoaderFactory;
+ private final AccountCache accountCache;
+ private final AccountIndexCollection indexes;
+ private final AccountQueryBuilder queryBuilder;
+ private final AccountQueryProcessor queryProcessor;
+ private final ReviewDb db;
+ private final boolean suggestConfig;
+ private final int suggestFrom;
+
+ private AccountLoader accountLoader;
+ private boolean suggest;
+ private int suggestLimit = 10;
+ private String query;
+ private Integer start;
+ private EnumSet<ListAccountsOption> options;
+
+ @Option(name = "--suggest", metaVar = "SUGGEST", usage = "suggest users")
+ public void setSuggest(boolean suggest) {
+ this.suggest = suggest;
+ }
+
+ @Option(name = "--limit", aliases = {"-n"}, metaVar = "CNT", usage = "maximum number of users to return")
+ public void setLimit(int n) {
+ queryProcessor.setLimit(n);
+
+ if (n < 0) {
+ suggestLimit = 10;
+ } else if (n == 0) {
+ suggestLimit = MAX_SUGGEST_RESULTS;
+ } else {
+ suggestLimit = Math.min(n, MAX_SUGGEST_RESULTS);
+ }
+ }
+
+ @Option(name = "-o", usage = "Output options per account")
+ public void addOption(ListAccountsOption o) {
+ options.add(o);
+ }
+
+ @Option(name = "-O", usage = "Output option flags, in hex")
+ void setOptionFlagsHex(String hex) {
+ options.addAll(ListAccountsOption.fromBits(Integer.parseInt(hex, 16)));
+ }
+
+ @Option(name = "--query", aliases = {"-q"}, metaVar = "QUERY", usage = "match users")
+ public void setQuery(String query) {
+ this.query = query;
+ }
+
+ @Option(name = "--start", aliases = {"-S"}, metaVar = "CNT",
+ usage = "Number of accounts to skip")
+ public void setStart(int start) {
+ this.start = start;
+ }
+
+ @Inject
+ QueryAccounts(AccountControl.Factory accountControlFactory,
+ AccountLoader.Factory accountLoaderFactory,
+ AccountCache accountCache,
+ AccountIndexCollection indexes,
+ AccountQueryBuilder queryBuilder,
+ AccountQueryProcessor queryProcessor,
+ ReviewDb db,
+ @GerritServerConfig Config cfg) {
+ this.accountControl = accountControlFactory.get();
+ this.accountLoaderFactory = accountLoaderFactory;
+ this.accountCache = accountCache;
+ this.indexes = indexes;
+ this.queryBuilder = queryBuilder;
+ this.queryProcessor = queryProcessor;
+ this.db = db;
+ this.suggestFrom = cfg.getInt("suggest", null, "from", 0);
+ this.options = EnumSet.noneOf(ListAccountsOption.class);
+
+ if ("off".equalsIgnoreCase(cfg.getString("suggest", null, "accounts"))) {
+ suggestConfig = false;
+ } else {
+ boolean suggest;
+ try {
+ AccountVisibility av =
+ cfg.getEnum("suggest", null, "accounts", AccountVisibility.ALL);
+ suggest = (av != AccountVisibility.NONE);
+ } catch (IllegalArgumentException err) {
+ suggest = cfg.getBoolean("suggest", null, "accounts", true);
+ }
+ this.suggestConfig = suggest;
+ }
+ }
+
+ @Override
+ public List<AccountInfo> apply(TopLevelResource rsrc)
+ throws OrmException, BadRequestException, MethodNotAllowedException {
+ if (Strings.isNullOrEmpty(query)) {
+ throw new BadRequestException("missing query field");
+ }
+
+ if (suggest && (!suggestConfig || query.length() < suggestFrom)) {
+ return Collections.emptyList();
+ }
+
+ accountLoader = accountLoaderFactory
+ .create(suggest || options.contains(ListAccountsOption.DETAILS));
+
+ AccountIndex searchIndex = indexes.getSearchIndex();
+ if (searchIndex != null) {
+ return queryFromIndex();
+ }
+
+ if (!suggest) {
+ throw new MethodNotAllowedException();
+ }
+ if (start != null) {
+ throw new MethodNotAllowedException("option start not allowed");
+ }
+ return queryFromDb();
+ }
+
+ public List<AccountInfo> queryFromIndex()
+ throws BadRequestException, MethodNotAllowedException, OrmException {
+ if (queryProcessor.isDisabled()) {
+ throw new MethodNotAllowedException("query disabled");
+ }
+
+ if (start != null) {
+ queryProcessor.setStart(start);
+ }
+
+ Map<Account.Id, AccountInfo> matches = new LinkedHashMap<>();
+ try {
+ Predicate<AccountState> queryPred;
+ if (suggest) {
+ queryPred = queryBuilder.defaultField(query);
+ queryProcessor.setLimit(suggestLimit);
+ } else {
+ queryPred = queryBuilder.parse(query);
+ }
+ QueryResult<AccountState> result = queryProcessor.query(queryPred);
+ for (AccountState accountState : result.entities()) {
+ Account.Id id = accountState.getAccount().getId();
+ matches.put(id, accountLoader.get(id));
+ }
+
+ accountLoader.fill();
+
+ List<AccountInfo> sorted =
+ AccountInfoComparator.ORDER_NULLS_LAST.sortedCopy(matches.values());
+ if (!sorted.isEmpty() && result.more()) {
+ sorted.get(sorted.size() - 1)._moreAccounts = true;
+ }
+ return sorted;
+ } catch (QueryParseException e) {
+ if (suggest) {
+ return ImmutableList.of();
+ }
+ throw new BadRequestException(e.getMessage());
+ }
+ }
+
+ public List<AccountInfo> queryFromDb() throws OrmException {
+ String a = query;
+ String b = a + MAX_SUFFIX;
+
+ Map<Account.Id, AccountInfo> matches = new LinkedHashMap<>();
+ Map<Account.Id, String> queryEmail = new HashMap<>();
+
+ for (Account p : db.accounts().suggestByFullName(a, b, suggestLimit)) {
+ addSuggestion(matches, p);
+ }
+ if (matches.size() < suggestLimit) {
+ for (Account p : db.accounts()
+ .suggestByPreferredEmail(a, b, suggestLimit - matches.size())) {
+ addSuggestion(matches, p);
+ }
+ }
+ if (matches.size() < suggestLimit) {
+ for (AccountExternalId e : db.accountExternalIds()
+ .suggestByEmailAddress(a, b, suggestLimit - matches.size())) {
+ if (addSuggestion(matches, e.getAccountId())) {
+ queryEmail.put(e.getAccountId(), e.getEmailAddress());
+ }
+ }
+ }
+
+ accountLoader.fill();
+ for (Map.Entry<Account.Id, String> p : queryEmail.entrySet()) {
+ AccountInfo info = matches.get(p.getKey());
+ if (info != null) {
+ info.email = p.getValue();
+ }
+ }
+
+ return AccountInfoComparator.ORDER_NULLS_LAST.sortedCopy(matches.values());
+ }
+
+ private boolean addSuggestion(Map<Account.Id, AccountInfo> map, Account a) {
+ if (!a.isActive()) {
+ return false;
+ }
+ Account.Id id = a.getId();
+ if (!map.containsKey(id) && accountControl.canSee(id)) {
+ map.put(id, accountLoader.get(id));
+ return true;
+ }
+ return false;
+ }
+
+ private boolean addSuggestion(Map<Account.Id, AccountInfo> map, Account.Id id) {
+ Account a = accountCache.get(id).getAccount();
+ return addSuggestion(map, a);
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/SuggestAccounts.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/SuggestAccounts.java
deleted file mode 100644
index 1cc3c94..0000000
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/SuggestAccounts.java
+++ /dev/null
@@ -1,161 +0,0 @@
-// Copyright (C) 2014 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.account;
-
-import com.google.common.base.Strings;
-import com.google.gerrit.extensions.common.AccountInfo;
-import com.google.gerrit.extensions.restapi.BadRequestException;
-import com.google.gerrit.extensions.restapi.RestReadView;
-import com.google.gerrit.extensions.restapi.TopLevelResource;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountExternalId;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.api.accounts.AccountInfoComparator;
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-
-import org.eclipse.jgit.lib.Config;
-import org.kohsuke.args4j.Option;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-
-public class SuggestAccounts implements RestReadView<TopLevelResource> {
- private static final int MAX_RESULTS = 100;
- private static final String MAX_SUFFIX = "\u9fa5";
-
- private final AccountControl accountControl;
- private final AccountLoader accountLoader;
- private final AccountCache accountCache;
- private final ReviewDb db;
- private final boolean suggest;
- private final int suggestFrom;
-
- private int limit = 10;
- private String query;
-
- @Option(name = "--limit", aliases = {"-n"}, metaVar = "CNT", usage = "maximum number of users to return")
- public void setLimit(int n) {
- if (n < 0) {
- limit = 10;
- } else if (n == 0) {
- limit = MAX_RESULTS;
- } else {
- limit = Math.min(n, MAX_RESULTS);
- }
- }
-
- @Option(name = "--query", aliases = {"-q"}, metaVar = "QUERY", usage = "match users")
- public void setQuery(String query) {
- this.query = query;
- }
-
- @Inject
- SuggestAccounts(AccountControl.Factory accountControlFactory,
- AccountLoader.Factory accountLoaderFactory,
- AccountCache accountCache,
- ReviewDb db,
- @GerritServerConfig Config cfg) {
- accountControl = accountControlFactory.get();
- accountLoader = accountLoaderFactory.create(true);
- this.accountCache = accountCache;
- this.db = db;
- this.suggestFrom = cfg.getInt("suggest", null, "from", 0);
-
- if ("off".equalsIgnoreCase(cfg.getString("suggest", null, "accounts"))) {
- suggest = false;
- } else {
- boolean suggest;
- try {
- AccountVisibility av =
- cfg.getEnum("suggest", null, "accounts", AccountVisibility.ALL);
- suggest = (av != AccountVisibility.NONE);
- } catch (IllegalArgumentException err) {
- suggest = cfg.getBoolean("suggest", null, "accounts", true);
- }
- this.suggest = suggest;
- }
- }
-
- @Override
- public List<AccountInfo> apply(TopLevelResource rsrc)
- throws OrmException, BadRequestException {
- if (Strings.isNullOrEmpty(query)) {
- throw new BadRequestException("missing query field");
- }
-
- if (!suggest || query.length() < suggestFrom) {
- return Collections.emptyList();
- }
-
- String a = query;
- String b = a + MAX_SUFFIX;
-
- Map<Account.Id, AccountInfo> matches = new LinkedHashMap<>();
- Map<Account.Id, String> queryEmail = new HashMap<>();
-
- for (Account p : db.accounts().suggestByFullName(a, b, limit)) {
- addSuggestion(matches, p);
- }
- if (matches.size() < limit) {
- for (Account p : db.accounts()
- .suggestByPreferredEmail(a, b, limit - matches.size())) {
- addSuggestion(matches, p);
- }
- }
- if (matches.size() < limit) {
- for (AccountExternalId e : db.accountExternalIds()
- .suggestByEmailAddress(a, b, limit - matches.size())) {
- if (addSuggestion(matches, e.getAccountId())) {
- queryEmail.put(e.getAccountId(), e.getEmailAddress());
- }
- }
- }
-
- accountLoader.fill();
- for (Map.Entry<Account.Id, String> p : queryEmail.entrySet()) {
- AccountInfo info = matches.get(p.getKey());
- if (info != null) {
- info.email = p.getValue();
- }
- }
-
- List<AccountInfo> m = new ArrayList<>(matches.values());
- Collections.sort(m, AccountInfoComparator.ORDER_NULLS_LAST);
- return m;
- }
-
- private boolean addSuggestion(Map<Account.Id, AccountInfo> map, Account a) {
- if (!a.isActive()) {
- return false;
- }
- Account.Id id = a.getId();
- if (!map.containsKey(id) && accountControl.canSee(id)) {
- map.put(id, accountLoader.get(id));
- return true;
- }
- return false;
- }
-
- private boolean addSuggestion(Map<Account.Id, AccountInfo> map, Account.Id id) {
- Account a = accountCache.get(id).getAccount();
- return addSuggestion(map, a);
- }
-}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/accounts/AccountsImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/accounts/AccountsImpl.java
index 3bd7634..39e8b51 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/api/accounts/AccountsImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/accounts/AccountsImpl.java
@@ -16,6 +16,7 @@
import com.google.gerrit.extensions.api.accounts.AccountApi;
import com.google.gerrit.extensions.api.accounts.Accounts;
+import com.google.gerrit.extensions.client.ListAccountsOption;
import com.google.gerrit.extensions.common.AccountInfo;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.IdString;
@@ -24,7 +25,7 @@
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.account.AccountResource;
import com.google.gerrit.server.account.AccountsCollection;
-import com.google.gerrit.server.account.SuggestAccounts;
+import com.google.gerrit.server.account.QueryAccounts;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
@@ -37,17 +38,17 @@
private final AccountsCollection accounts;
private final AccountApiImpl.Factory api;
private final Provider<CurrentUser> self;
- private final Provider<SuggestAccounts> suggestAccountsProvider;
+ private final Provider<QueryAccounts> queryAccountsProvider;
@Inject
AccountsImpl(AccountsCollection accounts,
AccountApiImpl.Factory api,
Provider<CurrentUser> self,
- Provider<SuggestAccounts> suggestAccountsProvider) {
+ Provider<QueryAccounts> queryAccountsProvider) {
this.accounts = accounts;
this.api = api;
this.self = self;
- this.suggestAccountsProvider = suggestAccountsProvider;
+ this.queryAccountsProvider = queryAccountsProvider;
}
@Override
@@ -92,10 +93,42 @@
private List<AccountInfo> suggestAccounts(SuggestAccountsRequest r)
throws RestApiException {
try {
- SuggestAccounts mySuggestAccounts = suggestAccountsProvider.get();
- mySuggestAccounts.setQuery(r.getQuery());
- mySuggestAccounts.setLimit(r.getLimit());
- return mySuggestAccounts.apply(TopLevelResource.INSTANCE);
+ QueryAccounts myQueryAccounts = queryAccountsProvider.get();
+ myQueryAccounts.setSuggest(true);
+ myQueryAccounts.setQuery(r.getQuery());
+ myQueryAccounts.setLimit(r.getLimit());
+ return myQueryAccounts.apply(TopLevelResource.INSTANCE);
+ } catch (OrmException e) {
+ throw new RestApiException("Cannot retrieve suggested accounts", e);
+ }
+ }
+
+ @Override
+ public QueryRequest query() throws RestApiException {
+ return new QueryRequest() {
+ @Override
+ public List<AccountInfo> get() throws RestApiException {
+ return AccountsImpl.this.query(this);
+ }
+ };
+ }
+
+ @Override
+ public QueryRequest query(String query) throws RestApiException {
+ return query().withQuery(query);
+ }
+
+ private List<AccountInfo> query(QueryRequest r)
+ throws RestApiException {
+ try {
+ QueryAccounts myQueryAccounts = queryAccountsProvider.get();
+ myQueryAccounts.setQuery(r.getQuery());
+ myQueryAccounts.setLimit(r.getLimit());
+ myQueryAccounts.setStart(r.getStart());
+ for (ListAccountsOption option : r.getOptions()) {
+ myQueryAccounts.addOption(option);
+ }
+ return myQueryAccounts.apply(TopLevelResource.INSTANCE);
} catch (OrmException e) {
throw new RestApiException("Cannot retrieve suggested accounts", e);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/ProjectsImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/ProjectsImpl.java
index db31d42..5d21c45 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/ProjectsImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/ProjectsImpl.java
@@ -66,6 +66,9 @@
@Override
public ProjectApi create(ProjectInput in) throws RestApiException {
+ if (in.name == null) {
+ throw new BadRequestException("input.name is required");
+ }
return name(in.name).create(in);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java
index 322d455..ec145d5 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java
@@ -163,6 +163,7 @@
private final GpgApiAdapter gpgApi;
private final ChangeNotes.Factory notesFactory;
private final ChangeResource.Factory changeResourceFactory;
+ private final ChangeKindCache changeKindCache;
private AccountLoader accountLoader;
private Map<Change.Id, List<SubmitRecord>> submitRecords;
@@ -190,6 +191,7 @@
GpgApiAdapter gpgApi,
ChangeNotes.Factory notesFactory,
ChangeResource.Factory changeResourceFactory,
+ ChangeKindCache changeKindCache,
@Assisted Set<ListChangesOption> options) {
this.db = db;
this.labelNormalizer = ln;
@@ -211,6 +213,7 @@
this.gpgApi = gpgApi;
this.notesFactory = notesFactory;
this.changeResourceFactory = changeResourceFactory;
+ this.changeKindCache = changeKindCache;
this.options = options.isEmpty()
? EnumSet.noneOf(ListChangesOption.class)
: EnumSet.copyOf(options);
@@ -409,10 +412,10 @@
out.mergeable = cd.isMergeable();
}
out.submittable = Submit.submittable(cd);
- ChangedLines changedLines = cd.changedLines();
- if (changedLines != null) {
- out.insertions = changedLines.insertions;
- out.deletions = changedLines.deletions;
+ Optional<ChangedLines> changedLines = cd.changedLines();
+ if (changedLines.isPresent()) {
+ out.insertions = changedLines.get().insertions;
+ out.deletions = changedLines.get().deletions;
}
out.subject = in.getSubject();
out.status = in.getStatus().asChangeStatus();
@@ -472,7 +475,7 @@
finish(out);
if (needRevisions) {
- out.revisions = revisions(ctl, src);
+ out.revisions = revisions(ctl, cd, src);
if (out.revisions != null) {
for (Map.Entry<String, RevisionInfo> entry : out.revisions.entrySet()) {
if (entry.getValue().isCurrent) {
@@ -887,18 +890,21 @@
.toSortedList(AccountInfoComparator.ORDER_NULLS_FIRST);
}
- private Map<String, RevisionInfo> revisions(ChangeControl ctl,
+ private Map<String, RevisionInfo> revisions(ChangeControl ctl, ChangeData cd,
Map<PatchSet.Id, PatchSet> map) throws PatchListNotAvailableException,
GpgException, OrmException, IOException {
Map<String, RevisionInfo> res = new LinkedHashMap<>();
- for (PatchSet in : map.values()) {
- if ((has(ALL_REVISIONS)
- || in.getId().equals(ctl.getChange().currentPatchSetId()))
- && ctl.isPatchVisible(in, db.get())) {
- res.put(in.getRevision().get(), toRevisionInfo(ctl, in));
+ try (Repository repo =
+ repoManager.openRepository(ctl.getProject().getNameKey())) {
+ for (PatchSet in : map.values()) {
+ if ((has(ALL_REVISIONS)
+ || in.getId().equals(ctl.getChange().currentPatchSetId()))
+ && ctl.isPatchVisible(in, db.get())) {
+ res.put(in.getRevision().get(), toRevisionInfo(ctl, cd, in, repo));
+ }
}
+ return res;
}
- return res;
}
private Map<PatchSet.Id, PatchSet> loadPatchSets(ChangeData cd,
@@ -933,12 +939,17 @@
throws PatchListNotAvailableException, GpgException, OrmException,
IOException {
accountLoader = accountLoaderFactory.create(has(DETAILED_ACCOUNTS));
- RevisionInfo rev = toRevisionInfo(ctl, in);
- accountLoader.fill();
- return rev;
+ try (Repository repo =
+ repoManager.openRepository(ctl.getProject().getNameKey())) {
+ RevisionInfo rev = toRevisionInfo(
+ ctl, changeDataFactory.create(db.get(), ctl), in, repo);
+ accountLoader.fill();
+ return rev;
+ }
}
- private RevisionInfo toRevisionInfo(ChangeControl ctl, PatchSet in)
+ private RevisionInfo toRevisionInfo(ChangeControl ctl, ChangeData cd,
+ PatchSet in, Repository repo)
throws PatchListNotAvailableException, GpgException, OrmException,
IOException {
Change c = ctl.getChange();
@@ -950,14 +961,14 @@
out.uploader = accountLoader.get(in.getUploader());
out.draft = in.isDraft() ? true : null;
out.fetch = makeFetchMap(ctl, in);
+ out.kind = changeKindCache.getChangeKind(repo, cd, in);
boolean setCommit = has(ALL_COMMITS)
|| (out.isCurrent && has(CURRENT_COMMIT));
boolean addFooters = out.isCurrent && has(COMMIT_FOOTERS);
if (setCommit || addFooters) {
Project.NameKey project = c.getProject();
- try (Repository repo = repoManager.openRepository(project);
- RevWalk rw = new RevWalk(repo)) {
+ try (RevWalk rw = new RevWalk(repo)) {
String rev = in.getRevision().get();
RevCommit commit = rw.parseCommit(ObjectId.fromString(rev));
rw.parseBody(commit);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeKindCache.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeKindCache.java
index 5148f9a..2302b70 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeKindCache.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeKindCache.java
@@ -14,10 +14,12 @@
package com.google.gerrit.server.change;
+import com.google.gerrit.extensions.client.ChangeKind;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.project.ProjectState;
+import com.google.gerrit.server.query.change.ChangeData;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
@@ -33,4 +35,6 @@
ObjectId prior, ObjectId next);
ChangeKind getChangeKind(ReviewDb db, Change change, PatchSet patch);
+
+ ChangeKind getChangeKind(Repository repo, ChangeData cd, PatchSet patch);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeKindCacheImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeKindCacheImpl.java
index 2f660c3..edc1b12 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeKindCacheImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeKindCacheImpl.java
@@ -22,6 +22,7 @@
import com.google.common.cache.Cache;
import com.google.common.cache.Weigher;
import com.google.common.collect.FluentIterable;
+import com.google.gerrit.extensions.client.ChangeKind;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.reviewdb.server.ReviewDb;
@@ -117,6 +118,12 @@
return getChangeKindInternal(this, db, change, patch, changeDataFactory,
projectCache, repoManager);
}
+
+ @Override
+ public ChangeKind getChangeKind(Repository repo, ChangeData cd,
+ PatchSet patch) {
+ return getChangeKindInternal(this, repo, cd, patch, projectCache);
+ }
}
public static class Key implements Serializable {
@@ -332,23 +339,25 @@
projectCache, repoManager);
}
+ @Override
+ public ChangeKind getChangeKind(Repository repo, ChangeData cd,
+ PatchSet patch) {
+ return getChangeKindInternal(this, repo, cd, patch, projectCache);
+ }
+
private static ChangeKind getChangeKindInternal(
ChangeKindCache cache,
- ReviewDb db,
- Change change,
+ Repository repo,
+ ChangeData change,
PatchSet patch,
- ChangeData.Factory changeDataFactory,
- ProjectCache projectCache,
- GitRepositoryManager repoManager) {
- // TODO - dborowitz: add NEW_CHANGE type for default.
+ ProjectCache projectCache) {
ChangeKind kind = ChangeKind.REWORK;
- // Trivial case: if we're on the first patch, we don't need to open
+ // Trivial case: if we're on the first patch, we don't need to use
// the repository.
if (patch.getId().get() > 1) {
- try (Repository repo = repoManager.openRepository(change.getProject())) {
- ProjectState projectState = projectCache.checkedGet(change.getProject());
- ChangeData cd = changeDataFactory.create(db, change);
- Collection<PatchSet> patchSetCollection = cd.patchSets();
+ try {
+ ProjectState projectState = projectCache.checkedGet(change.project());
+ Collection<PatchSet> patchSetCollection = change.patchSets();
PatchSet priorPs = patch;
for (PatchSet ps : patchSetCollection) {
if (ps.getId().get() < patch.getId().get() &&
@@ -372,6 +381,32 @@
} catch (IOException | OrmException e) {
// Do nothing; assume we have a complex change
log.warn("Unable to get change kind for patchSet " + patch.getPatchSetId() +
+ "of change " + change.getId(), e);
+ }
+ }
+ return kind;
+ }
+
+ private static ChangeKind getChangeKindInternal(
+ ChangeKindCache cache,
+ ReviewDb db,
+ Change change,
+ PatchSet patch,
+ ChangeData.Factory changeDataFactory,
+ ProjectCache projectCache,
+ GitRepositoryManager repoManager) {
+ // TODO - dborowitz: add NEW_CHANGE type for default.
+ ChangeKind kind = ChangeKind.REWORK;
+ // Trivial case: if we're on the first patch, we don't need to open
+ // the repository.
+ if (patch.getId().get() > 1) {
+ try (Repository repo = repoManager.openRepository(change.getProject())) {
+ kind = getChangeKindInternal(cache, repo,
+ changeDataFactory.create(db, change), patch,
+ projectCache);
+ } catch (IOException e) {
+ // Do nothing; assume we have a complex change
+ log.warn("Unable to get change kind for patchSet " + patch.getPatchSetId() +
"of change " + change.getChangeId(), e);
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraftChangeOp.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraftChangeOp.java
index 9f8411f..3ca0e1b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraftChangeOp.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraftChangeOp.java
@@ -25,6 +25,7 @@
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.reviewdb.server.ReviewDbUtil;
import com.google.gerrit.server.PatchSetUtil;
import com.google.gerrit.server.StarredChangesUtil;
import com.google.gerrit.server.config.GerritServerConfig;
@@ -33,7 +34,6 @@
import com.google.gerrit.server.git.BatchUpdate.RepoContext;
import com.google.gerrit.server.git.BatchUpdateReviewDb;
import com.google.gerrit.server.project.NoSuchChangeException;
-import com.google.gerrit.server.schema.DisabledChangesReviewDbWrapper;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
@@ -57,10 +57,7 @@
if (db instanceof BatchUpdateReviewDb) {
db = ((BatchUpdateReviewDb) db).unsafeGetDelegate();
}
- if (db instanceof DisabledChangesReviewDbWrapper) {
- db = ((DisabledChangesReviewDbWrapper) db).unsafeGetDelegate();
- }
- return db;
+ return ReviewDbUtil.unwrapDb(db);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraftPatchSet.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraftPatchSet.java
index 93cb01b..1cd8726 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraftPatchSet.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraftPatchSet.java
@@ -146,6 +146,7 @@
psUtil.delete(ctx.getDb(), ctx.getUpdate(patchSet.getId()), patchSet);
accountPatchReviewStore.get().clearReviewed(psId);
+ // Use the unwrap from DeleteDraftChangeOp to handle BatchUpdateReviewDb.
ReviewDb db = DeleteDraftChangeOp.unwrap(ctx.getDb());
db.changeMessages().delete(db.changeMessages().byPatchSet(psId));
db.patchComments().delete(db.patchComments().byPatchSet(psId));
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteReviewer.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteReviewer.java
index 28d7dcd8..a2a59e9 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteReviewer.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteReviewer.java
@@ -128,6 +128,10 @@
public boolean updateChange(ChangeContext ctx)
throws AuthException, ResourceNotFoundException, OrmException {
Account.Id reviewerId = reviewer.getId();
+ if (!approvalsUtil.getReviewers(ctx.getDb(), ctx.getNotes()).all()
+ .contains(reviewerId)) {
+ throw new ResourceNotFoundException();
+ }
currChange = ctx.getChange();
currPs = psUtil.current(dbProvider.get(), ctx.getNotes());
@@ -156,9 +160,6 @@
throw new AuthException("delete not permitted");
}
}
- if (del.isEmpty()) {
- throw new ResourceNotFoundException();
- }
ctx.getDb().patchSetApprovals().delete(del);
ChangeUpdate update = ctx.getUpdate(currPs.getId());
update.removeReviewer(reviewerId);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Mergeable.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Mergeable.java
index 842e8bb..08ef76e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Mergeable.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Mergeable.java
@@ -24,6 +24,7 @@
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.reviewdb.server.ReviewDbUtil;
import com.google.gerrit.server.git.BranchOrderSection;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.MergeUtil;
@@ -32,7 +33,6 @@
import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.project.SubmitRuleEvaluator;
import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gerrit.server.schema.DisabledChangesReviewDbWrapper;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
@@ -186,13 +186,10 @@
throws OrmException {
// Empty update of Change to bump rowVersion, changing its ETag.
// TODO(dborowitz): Include cache info in ETag somehow instead.
- if (db instanceof DisabledChangesReviewDbWrapper) {
- db = ((DisabledChangesReviewDbWrapper) db).unsafeGetDelegate();
- }
+ db = ReviewDbUtil.unwrapDb(db);
Change c = db.changes().get(id);
if (c != null) {
db.changes().update(Collections.singleton(c));
}
}
-
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/PluginConfigFactory.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/PluginConfigFactory.java
index 58a6c60..95a0f36 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/PluginConfigFactory.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/PluginConfigFactory.java
@@ -22,6 +22,7 @@
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectState;
import com.google.inject.Inject;
+import com.google.inject.Provider;
import com.google.inject.Singleton;
import org.eclipse.jgit.errors.ConfigInvalidException;
@@ -45,7 +46,7 @@
private static final String EXTENSION = ".config";
private final SitePaths site;
- private final GerritServerConfigProvider cfgProvider;
+ private final Provider<Config> cfgProvider;
private final ProjectCache projectCache;
private final ProjectState.Factory projectStateFactory;
private final Map<String, Config> pluginConfigs;
@@ -54,8 +55,11 @@
private volatile Config cfg;
@Inject
- PluginConfigFactory(SitePaths site, GerritServerConfigProvider cfgProvider,
- ProjectCache projectCache, ProjectState.Factory projectStateFactory) {
+ PluginConfigFactory(
+ SitePaths site,
+ @GerritServerConfig Provider<Config> cfgProvider,
+ ProjectCache projectCache,
+ ProjectState.Factory projectStateFactory) {
this.site = site;
this.cfgProvider = cfgProvider;
this.projectCache = projectCache;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/data/PatchSetAttribute.java b/gerrit-server/src/main/java/com/google/gerrit/server/data/PatchSetAttribute.java
index c669e26..824d800 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/data/PatchSetAttribute.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/data/PatchSetAttribute.java
@@ -14,7 +14,7 @@
package com.google.gerrit.server.data;
-import com.google.gerrit.server.change.ChangeKind;
+import com.google.gerrit.extensions.client.ChangeKind;
import java.util.List;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/edit/ChangeEditUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/edit/ChangeEditUtil.java
index a2145e6..6811056 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/edit/ChangeEditUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/edit/ChangeEditUtil.java
@@ -18,6 +18,7 @@
import com.google.common.base.Optional;
import com.google.gerrit.common.TimeUtil;
+import com.google.gerrit.extensions.client.ChangeKind;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.RestApiException;
@@ -30,7 +31,6 @@
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.PatchSetUtil;
-import com.google.gerrit.server.change.ChangeKind;
import com.google.gerrit.server.change.ChangeKindCache;
import com.google.gerrit.server.change.PatchSetInserter;
import com.google.gerrit.server.git.BatchUpdate;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/BatchUpdate.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/BatchUpdate.java
index eb76350..598ed71 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/BatchUpdate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/BatchUpdate.java
@@ -50,7 +50,6 @@
import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.project.NoSuchRefException;
-import com.google.gerrit.server.schema.DisabledChangesReviewDbWrapper;
import com.google.gwtorm.server.OrmConcurrencyException;
import com.google.gwtorm.server.OrmException;
import com.google.gwtorm.server.SchemaFactory;
@@ -856,7 +855,7 @@
RevWalk rw, Change.Id id) throws Exception {
Change c = newChanges.get(id);
if (c == null) {
- c = unwrap(db).changes().get(id);
+ c = ReviewDbUtil.unwrapDb(db).changes().get(id);
}
// Pass in preloaded change to controlFor, to avoid:
// - reading from a db that does not belong to this update
@@ -903,11 +902,4 @@
op.postUpdate(ctx);
}
}
-
- private static ReviewDb unwrap(ReviewDb db) {
- if (db instanceof DisabledChangesReviewDbWrapper) {
- db = ((DisabledChangesReviewDbWrapper) db).unsafeGetDelegate();
- }
- return db;
- }
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectConfig.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectConfig.java
index b94efce..6d9d759 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectConfig.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectConfig.java
@@ -177,6 +177,7 @@
private long maxObjectSizeLimit;
private Map<String, Config> pluginConfigs;
private boolean checkReceivedObjects;
+ private Set<String> sectionsWithUnknownPermissions;
public static ProjectConfig read(MetaDataUpdate update) throws IOException,
ConfigInvalidException {
@@ -281,7 +282,13 @@
public void remove(AccessSection section) {
if (section != null) {
- accessSections.remove(section.getName());
+ String name = section.getName();
+ if (sectionsWithUnknownPermissions.contains(name)) {
+ AccessSection a = accessSections.get(name);
+ a.setPermissions(new ArrayList<Permission>());
+ } else {
+ accessSections.remove(name);
+ }
}
}
@@ -609,6 +616,7 @@
private void loadAccessSections(
Config rc, Map<String, GroupReference> groupsByName) {
accessSections = new HashMap<>();
+ sectionsWithUnknownPermissions = new HashSet<>();
for (String refName : rc.getSubsections(ACCESS)) {
if (RefConfigSection.isValid(refName) && isValidRegex(refName)) {
AccessSection as = getAccessSection(refName, true);
@@ -626,6 +634,8 @@
Permission perm = as.getPermission(varName, true);
loadPermissionRules(rc, ACCESS, refName, varName, groupsByName,
perm, Permission.hasRange(varName));
+ } else {
+ sectionsWithUnknownPermissions.add(as.getName());
}
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
index bbb325e..66637fb 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
@@ -37,6 +37,7 @@
import com.google.common.base.Strings;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.BiMap;
+import com.google.common.collect.Collections2;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.HashMultimap;
@@ -50,8 +51,6 @@
import com.google.common.collect.SetMultimap;
import com.google.common.collect.Sets;
import com.google.common.collect.SortedSetMultimap;
-import com.google.common.util.concurrent.CheckedFuture;
-import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.TimeUtil;
import com.google.gerrit.common.data.Capable;
@@ -291,7 +290,6 @@
private final TagCache tagCache;
private final AccountCache accountCache;
private final ChangeInserter.Factory changeInserterFactory;
- private final ListeningExecutorService changeUpdateExector;
private final RequestScopePropagator requestScopePropagator;
private final SshInfo sshInfo;
private final AllProjectsName allProjectsName;
@@ -355,7 +353,6 @@
ChangeInserter.Factory changeInserterFactory,
CommitValidators.Factory commitValidatorsFactory,
@CanonicalWebUrl String canonicalWebUrl,
- @ChangeUpdateExecutor ListeningExecutorService changeUpdateExector,
RequestScopePropagator requestScopePropagator,
SshInfo sshInfo,
AllProjectsName allProjectsName,
@@ -392,7 +389,6 @@
this.accountCache = accountCache;
this.changeInserterFactory = changeInserterFactory;
this.commitValidatorsFactory = commitValidatorsFactory;
- this.changeUpdateExector = changeUpdateExector;
this.requestScopePropagator = requestScopePropagator;
this.sshInfo = sshInfo;
this.allProjectsName = allProjectsName;
@@ -1892,17 +1888,19 @@
}
private void readChangesForReplace() throws OrmException {
- List<CheckedFuture<ChangeNotes, OrmException>> futures =
- Lists.newArrayListWithCapacity(replaceByChange.size());
- for (ReplaceRequest request : replaceByChange.values()) {
- futures.add(notesFactory.createAsync(changeUpdateExector, db,
- project.getNameKey(), request.ontoChange));
- }
- for (CheckedFuture<ChangeNotes, OrmException> f : futures) {
- ChangeNotes notes = f.checkedGet();
- if (notes.getChange() != null) {
- replaceByChange.get(notes.getChangeId()).notes = notes;
- }
+ Collection<ChangeNotes> allNotes =
+ notesFactory.create(
+ db,
+ Collections2.transform(
+ replaceByChange.values(),
+ new Function<ReplaceRequest, Change.Id>() {
+ @Override
+ public Change.Id apply(ReplaceRequest in) {
+ return in.ontoChange;
+ }
+ }));
+ for (ChangeNotes notes : allNotes) {
+ replaceByChange.get(notes.getChangeId()).notes = notes;
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReplaceOp.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReplaceOp.java
index ef785f2..c0bfccb 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReplaceOp.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReplaceOp.java
@@ -22,6 +22,7 @@
import com.google.common.collect.ImmutableList;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.LabelType;
+import com.google.gerrit.extensions.client.ChangeKind;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.Branch;
import com.google.gerrit.reviewdb.client.Change;
@@ -35,7 +36,6 @@
import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.PatchSetUtil;
import com.google.gerrit.server.account.AccountResolver;
-import com.google.gerrit.server.change.ChangeKind;
import com.google.gerrit.server.change.ChangeKindCache;
import com.google.gerrit.server.extensions.events.CommentAdded;
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexPredicate.java
index d3b9e95..ff9ff03 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexPredicate.java
@@ -20,7 +20,7 @@
public abstract class IndexPredicate<I> extends OperatorPredicate<I> {
private final FieldDef<I, ?> def;
- public IndexPredicate(FieldDef<I, ?> def, String value) {
+ protected IndexPredicate(FieldDef<I, ?> def, String value) {
super(def.getName(), value);
this.def = def;
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexedQuery.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexedQuery.java
index 3040ca6..65097b4 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexedQuery.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexedQuery.java
@@ -14,10 +14,8 @@
package com.google.gerrit.server.index;
-import com.google.common.base.Function;
import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
import com.google.gerrit.server.query.DataSource;
import com.google.gerrit.server.query.Paginated;
import com.google.gerrit.server.query.Predicate;
@@ -26,10 +24,7 @@
import com.google.gwtorm.server.ResultSet;
import java.util.Collection;
-import java.util.HashMap;
-import java.util.Iterator;
import java.util.List;
-import java.util.Map;
/**
* Wrapper combining an {@link IndexPredicate} together with a
@@ -48,8 +43,7 @@
private QueryOptions opts;
private final Predicate<T> pred;
- private DataSource<T> source;
- private final Map<T, DataSource<T>> fromSource;
+ protected DataSource<T> source;
public IndexedQuery(Index<I, T> index, Predicate<T> pred,
QueryOptions opts) throws QueryParseException {
@@ -57,7 +51,6 @@
this.opts = opts;
this.pred = pred;
this.source = index.getSource(pred, this.opts);
- this.fromSource = new HashMap<>();
}
@Override
@@ -90,38 +83,7 @@
@Override
public ResultSet<T> read() throws OrmException {
- final DataSource<T> currSource = source;
- final ResultSet<T> rs = currSource.read();
-
- return new ResultSet<T>() {
- @Override
- public Iterator<T> iterator() {
- return Iterables.transform(
- rs,
- new Function<T, T>() {
- @Override
- public
- T apply(T t) {
- fromSource.put(t, currSource);
- return t;
- }
- }).iterator();
- }
-
- @Override
- public List<T> toList() {
- List<T> r = rs.toList();
- for (T t : r) {
- fromSource.put(t, currSource);
- }
- return r;
- }
-
- @Override
- public void close() {
- rs.close();
- }
- };
+ return source.read();
}
@Override
@@ -146,19 +108,6 @@
}
@Override
- public boolean match(T t) throws OrmException {
- return (source != null && fromSource.get(t) == source) || pred.match(t);
- }
-
- @Override
- public int getCost() {
- // Index queries are assumed to be cheaper than any other type of query, so
- // so try to make sure they get picked. Note that pred's cost may be higher
- // because it doesn't know whether it's being used in an index query or not.
- return 1;
- }
-
- @Override
public int hashCode() {
return pred.hashCode();
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/IntegerRangePredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/IntegerRangePredicate.java
index 1259951..e62a685 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/IntegerRangePredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/IntegerRangePredicate.java
@@ -31,11 +31,13 @@
}
}
- protected abstract int getValueInt(T object) throws OrmException;
+ protected abstract Integer getValueInt(T object) throws OrmException;
- @Override
public boolean match(T object) throws OrmException {
- int valueInt = getValueInt(object);
+ Integer valueInt = getValueInt(object);
+ if (valueInt == null) {
+ return false;
+ }
return valueInt >= range.min && valueInt <= range.max;
}
@@ -48,9 +50,4 @@
public int getMaximumValue() {
return range.max;
}
-
- @Override
- public int getCost() {
- return 1;
- }
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/TimestampRangePredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/TimestampRangePredicate.java
index 8ba7df9..1e2e80b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/TimestampRangePredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/TimestampRangePredicate.java
@@ -38,9 +38,4 @@
public abstract Date getMinTimestamp();
public abstract Date getMaxTimestamp();
-
- @Override
- public int getCost() {
- return 1;
- }
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/account/AccountField.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/account/AccountField.java
index 8627e3a..0de17d3 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/account/AccountField.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/account/AccountField.java
@@ -77,7 +77,7 @@
// Additional values not currently added by getPersonParts.
// TODO(dborowitz): Move to getPersonParts and remove this hack.
if (fullName != null) {
- parts.add(fullName);
+ parts.add(fullName.toLowerCase());
}
return parts;
}
@@ -114,7 +114,8 @@
public String apply(String in) {
return in.toLowerCase();
}
- });
+ })
+ .toSet();
}
};
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/account/AccountIndexRewriter.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/account/AccountIndexRewriter.java
index 74ba1c3..65e9b09 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/account/AccountIndexRewriter.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/account/AccountIndexRewriter.java
@@ -21,6 +21,7 @@
import com.google.gerrit.server.index.QueryOptions;
import com.google.gerrit.server.query.Predicate;
import com.google.gerrit.server.query.QueryParseException;
+import com.google.gerrit.server.query.account.AccountPredicates;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -37,6 +38,9 @@
@Override
public Predicate<AccountState> rewrite(Predicate<AccountState> in,
QueryOptions opts) throws QueryParseException {
+ if (!AccountPredicates.hasActive(in)) {
+ in = Predicate.and(in, AccountPredicates.isActive());
+ }
AccountIndex index = indexes.getSearchIndex();
checkNotNull(index, "no active search index configured for accounts");
return new IndexedAccountQuery(index, in, opts);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/change/ChangeField.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/change/ChangeField.java
index 074c81a..fe448c6 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/change/ChangeField.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/change/ChangeField.java
@@ -19,6 +19,7 @@
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
+import com.google.common.base.Optional;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
@@ -595,9 +596,8 @@
@Override
public Integer get(ChangeData input, FillArgs args)
throws OrmException {
-
- return input.changedLines() != null
- ? input.changedLines().insertions
+ return input.changedLines().isPresent()
+ ? input.changedLines().get().insertions
: null;
}
};
@@ -609,8 +609,8 @@
@Override
public Integer get(ChangeData input, FillArgs args)
throws OrmException {
- return input.changedLines() != null
- ? input.changedLines().deletions
+ return input.changedLines().isPresent()
+ ? input.changedLines().get().deletions
: null;
}
};
@@ -622,9 +622,9 @@
@Override
public Integer get(ChangeData input, FillArgs args)
throws OrmException {
- ChangedLines changedLines = input.changedLines();
- return changedLines != null
- ? changedLines.insertions + changedLines.deletions
+ Optional<ChangedLines> changedLines = input.changedLines();
+ return changedLines.isPresent()
+ ? changedLines.get().insertions + changedLines.get().deletions
: null;
}
};
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/change/IndexedChangeQuery.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/change/IndexedChangeQuery.java
index 8fe02f4..996caa7 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/change/IndexedChangeQuery.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/change/IndexedChangeQuery.java
@@ -14,22 +14,33 @@
package com.google.gerrit.server.index.change;
+import static com.google.common.base.Preconditions.checkState;
import static com.google.gerrit.server.index.change.ChangeField.CHANGE;
import static com.google.gerrit.server.index.change.ChangeField.PROJECT;
import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Function;
import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.index.IndexConfig;
import com.google.gerrit.server.index.IndexPredicate;
import com.google.gerrit.server.index.IndexedQuery;
import com.google.gerrit.server.index.QueryOptions;
+import com.google.gerrit.server.query.DataSource;
+import com.google.gerrit.server.query.Matchable;
import com.google.gerrit.server.query.Predicate;
import com.google.gerrit.server.query.QueryParseException;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.ChangeDataSource;
+import com.google.gwtorm.server.OrmException;
+import com.google.gwtorm.server.ResultSet;
+import java.util.HashMap;
import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
import java.util.Set;
/**
@@ -41,7 +52,7 @@
* {@link ChangeDataSource} to be chosen by the query processor.
*/
public class IndexedChangeQuery extends IndexedQuery<Change.Id, ChangeData>
- implements ChangeDataSource {
+ implements ChangeDataSource, Matchable<ChangeData> {
public static QueryOptions oneResult() {
return createOptions(IndexConfig.createDefault(), 0, 1,
ImmutableSet.<String> of());
@@ -65,9 +76,68 @@
opts.limit(), opts.fields());
}
+ private final Map<ChangeData, DataSource<ChangeData>> fromSource;
+
public IndexedChangeQuery(ChangeIndex index, Predicate<ChangeData> pred,
QueryOptions opts) throws QueryParseException {
super(index, pred, convertOptions(opts));
+ this.fromSource = new HashMap<>();
+ }
+
+ @Override
+ public ResultSet<ChangeData> read() throws OrmException {
+ final DataSource<ChangeData> currSource = source;
+ final ResultSet<ChangeData> rs = currSource.read();
+
+ return new ResultSet<ChangeData>() {
+ @Override
+ public Iterator<ChangeData> iterator() {
+ return Iterables.transform(
+ rs,
+ new Function<ChangeData, ChangeData>() {
+ @Override
+ public ChangeData apply(ChangeData cd) {
+ fromSource.put(cd, currSource);
+ return cd;
+ }
+ }).iterator();
+ }
+
+ @Override
+ public List<ChangeData> toList() {
+ List<ChangeData> r = rs.toList();
+ for (ChangeData cd : r) {
+ fromSource.put(cd, currSource);
+ }
+ return r;
+ }
+
+ @Override
+ public void close() {
+ rs.close();
+ }
+ };
+ }
+
+ @Override
+ public boolean match(ChangeData cd) throws OrmException {
+ if (source != null && fromSource.get(cd) == source) {
+ return true;
+ }
+
+ Predicate<ChangeData> pred = getChild(0);
+ checkState(pred.isMatchable(),
+ "match invoked, but child predicate %s " + "doesn't implement %s", pred,
+ Matchable.class.getName());
+ return pred.asMatchable().match(cd);
+ }
+
+ @Override
+ public int getCost() {
+ // Index queries are assumed to be cheaper than any other type of query, so
+ // so try to make sure they get picked. Note that pred's cost may be higher
+ // because it doesn't know whether it's being used in an index query or not.
+ return 1;
}
@Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/change/ReindexAfterUpdate.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/change/ReindexAfterUpdate.java
index 5db8f64..e446f9a 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/change/ReindexAfterUpdate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/change/ReindexAfterUpdate.java
@@ -151,10 +151,14 @@
throws OrmException, IOException, NoSuchChangeException {
// Reload change, as some time may have passed since GetChanges.
ReviewDb db = ctx.getReviewDbProvider().get();
- Change c = notesFactory
- .createChecked(db, new Project.NameKey(event.getProjectName()), id)
- .getChange();
- indexerFactory.create(executor, indexes).index(db, c);
+ try {
+ Change c = notesFactory
+ .createChecked(db, new Project.NameKey(event.getProjectName()), id)
+ .getChange();
+ indexerFactory.create(executor, indexes).index(db, c);
+ } catch (NoSuchChangeException e) {
+ indexerFactory.create(executor, indexes).delete(id);
+ }
return null;
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/ProjectWatch.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/ProjectWatch.java
index 8a80bfe..6e4e1d4 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/ProjectWatch.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/ProjectWatch.java
@@ -211,6 +211,6 @@
p = Predicate.and(filterPredicate, p);
}
}
- return p == null || p.match(changeData);
+ return p == null || p.asMatchable().match(changeData);
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNotes.java b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNotes.java
index 60c7d25..82ed02a 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNotes.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNotes.java
@@ -34,11 +34,6 @@
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
import com.google.common.collect.Ordering;
-import com.google.common.util.concurrent.AsyncFunction;
-import com.google.common.util.concurrent.CheckedFuture;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
-import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.SubmitRecord;
import com.google.gerrit.metrics.Timer1;
@@ -52,6 +47,7 @@
import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.reviewdb.client.RevId;
import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.reviewdb.server.ReviewDbUtil;
import com.google.gerrit.server.ReviewerSet;
import com.google.gerrit.server.git.RefCache;
import com.google.gerrit.server.git.RepoRefCache;
@@ -59,7 +55,6 @@
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.InternalChangeQuery;
-import com.google.gerrit.server.schema.DisabledChangesReviewDbWrapper;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
@@ -80,7 +75,6 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.concurrent.Callable;
/** View of a single {@link Change} based on the log of its notes branch. */
public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
@@ -128,20 +122,16 @@
public ChangeNotes createChecked(ReviewDb db, Change c)
throws OrmException, NoSuchChangeException {
- ChangeNotes notes = create(db, c.getProject(), c.getId());
- if (notes.getChange() == null) {
- throw new NoSuchChangeException(c.getId());
- }
- return notes;
+ return createChecked(db, c.getProject(), c.getId());
}
public ChangeNotes createChecked(ReviewDb db, Project.NameKey project,
Change.Id changeId) throws OrmException, NoSuchChangeException {
- ChangeNotes notes = create(db, project, changeId);
- if (notes.getChange() == null) {
+ Change change = ReviewDbUtil.unwrapDb(db).changes().get(changeId);
+ if (change == null || !change.getProject().equals(project)) {
throw new NoSuchChangeException(changeId);
}
- return notes;
+ return new ChangeNotes(args, change).load();
}
public ChangeNotes createChecked(Change.Id changeId)
@@ -161,7 +151,7 @@
public ChangeNotes create(ReviewDb db, Project.NameKey project,
Change.Id changeId) throws OrmException {
- Change change = unwrap(db).changes().get(changeId);
+ Change change = ReviewDbUtil.unwrapDb(db).changes().get(changeId);
checkNotNull(change,
"change %s not found in ReviewDb", changeId);
checkArgument(change.getProject().equals(project),
@@ -194,7 +184,7 @@
ReviewDb db, Change.Id changeId) throws OrmException {
checkState(!args.migration.readChanges(), "do not call"
+ " createFromIdOnlyWhenNoteDbDisabled when NoteDb is enabled");
- Change change = unwrap(db).changes().get(changeId);
+ Change change = ReviewDbUtil.unwrapDb(db).changes().get(changeId);
checkNotNull(change,
"change %s not found in ReviewDb", changeId);
return new ChangeNotes(args, change).load();
@@ -217,41 +207,6 @@
return new ChangeNotes(args, change).load();
}
- public CheckedFuture<ChangeNotes, OrmException> createAsync(
- final ListeningExecutorService executorService, final ReviewDb db,
- final Project.NameKey project, final Change.Id changeId) {
- return Futures.makeChecked(
- Futures.transformAsync(unwrap(db).changes().getAsync(changeId),
- new AsyncFunction<Change, ChangeNotes>() {
- @Override
- public ListenableFuture<ChangeNotes> apply(
- final Change change) {
- return executorService.submit(new Callable<ChangeNotes>() {
- @Override
- public ChangeNotes call() throws Exception {
- checkArgument(change.getProject().equals(project),
- "passed project %s when creating ChangeNotes for %s,"
- + " but actual project is %s",
- project, changeId, change.getProject());
- // Disable auto-rebuilding. This may be called async for a
- // large number of changes in one project, increasing
- // write contention. Plus, we haven't propagated the
- // request scope for the rebuilder to open the db.
- return new ChangeNotes(args, change, false, null).load();
- }
- });
- }
- }), new Function<Exception, OrmException>() {
- @Override
- public OrmException apply(Exception e) {
- if (e instanceof OrmException) {
- return (OrmException) e;
- }
- return new OrmException(e);
- }
- });
- }
-
public List<ChangeNotes> create(ReviewDb db,
Collection<Change.Id> changeIds) throws OrmException {
List<ChangeNotes> notes = new ArrayList<>();
@@ -266,7 +221,7 @@
return notes;
}
- for (Change c : unwrap(db).changes().get(changeIds)) {
+ for (Change c : ReviewDbUtil.unwrapDb(db).changes().get(changeIds)) {
notes.add(createFromChangeOnlyWhenNoteDbDisabled(c));
}
return notes;
@@ -286,7 +241,7 @@
return notes;
}
- for (Change c : unwrap(db).changes().get(changeIds)) {
+ for (Change c : ReviewDbUtil.unwrapDb(db).changes().get(changeIds)) {
if (c != null && project.equals(c.getDest().getParentKey())) {
ChangeNotes cn = createFromChangeOnlyWhenNoteDbDisabled(c);
if (predicate.apply(cn)) {
@@ -312,7 +267,7 @@
}
}
} else {
- for (Change change : unwrap(db).changes().all()) {
+ for (Change change : ReviewDbUtil.unwrapDb(db).changes().all()) {
ChangeNotes notes = createFromChangeOnlyWhenNoteDbDisabled(change);
if (predicate.apply(notes)) {
m.put(change.getProject(), notes);
@@ -338,7 +293,7 @@
// A batch size of N may overload get(Iterable), so use something smaller,
// but still >1.
for (List<Change.Id> batch : Iterables.partition(ids, 30)) {
- for (Change change : unwrap(db).changes().get(batch)) {
+ for (Change change : ReviewDbUtil.unwrapDb(db).changes().get(batch)) {
notes.add(createFromChangeOnlyWhenNoteDbDisabled(change));
}
}
@@ -349,7 +304,7 @@
Project.NameKey project) throws OrmException, IOException {
Set<Change.Id> ids = scan(repo);
List<ChangeNotes> changeNotes = new ArrayList<>(ids.size());
- db = unwrap(db);
+ db = ReviewDbUtil.unwrapDb(db);
for (Change.Id id : ids) {
Change change = db.changes().get(id);
if (change == null) {
@@ -385,13 +340,6 @@
}
}
- private static ReviewDb unwrap(ReviewDb db) {
- if (db instanceof DisabledChangesReviewDbWrapper) {
- db = ((DisabledChangesReviewDbWrapper) db).unsafeGetDelegate();
- }
- return db;
- }
-
private final RefCache refs;
private Change change;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeRebuilderImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeRebuilderImpl.java
index 8f805d9..a181431 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeRebuilderImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeRebuilderImpl.java
@@ -61,7 +61,6 @@
import com.google.gerrit.server.patch.PatchListCache;
import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.project.ProjectCache;
-import com.google.gerrit.server.schema.DisabledChangesReviewDbWrapper;
import com.google.gwtorm.server.AtomicUpdate;
import com.google.gwtorm.server.OrmException;
import com.google.gwtorm.server.OrmRuntimeException;
@@ -157,7 +156,7 @@
public Result rebuild(ReviewDb db, Change.Id changeId)
throws NoSuchChangeException, IOException, OrmException,
ConfigInvalidException {
- db = unwrapDb(db);
+ db = ReviewDbUtil.unwrapDb(db);
Change change = db.changes().get(changeId);
if (change == null) {
throw new NoSuchChangeException(changeId);
@@ -188,7 +187,7 @@
@Override
public NoteDbUpdateManager stage(ReviewDb db, Change.Id changeId)
throws NoSuchChangeException, IOException, OrmException {
- db = unwrapDb(db);
+ db = ReviewDbUtil.unwrapDb(db);
Change change = db.changes().get(changeId);
if (change == null) {
throw new NoSuchChangeException(changeId);
@@ -204,7 +203,7 @@
public Result execute(ReviewDb db, Change.Id changeId,
NoteDbUpdateManager manager) throws NoSuchChangeException, OrmException,
IOException {
- db = unwrapDb(db);
+ db = ReviewDbUtil.unwrapDb(db);
Change change = db.changes().get(changeId);
if (change == null) {
throw new NoSuchChangeException(changeId);
@@ -1021,11 +1020,4 @@
}
}
}
-
- private ReviewDb unwrapDb(ReviewDb db) {
- if (db instanceof DisabledChangesReviewDbWrapper) {
- db = ((DisabledChangesReviewDbWrapper) db).unsafeGetDelegate();
- }
- return db;
- }
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginLoader.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginLoader.java
index 4e8d2a0..e170510 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginLoader.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginLoader.java
@@ -18,6 +18,7 @@
import com.google.common.base.Joiner;
import com.google.common.base.MoreObjects;
import com.google.common.base.Predicate;
+import com.google.common.base.Strings;
import com.google.common.collect.ComparisonChain;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
@@ -25,6 +26,7 @@
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
+import com.google.common.collect.Ordering;
import com.google.common.collect.Sets;
import com.google.common.io.ByteStreams;
import com.google.gerrit.extensions.annotations.PluginName;
@@ -58,7 +60,6 @@
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
@@ -135,6 +136,34 @@
}
}
+ public static List<Path> listPlugins(Path pluginsDir, final String suffix)
+ throws IOException {
+ if (pluginsDir == null || !Files.exists(pluginsDir)) {
+ return ImmutableList.of();
+ }
+ DirectoryStream.Filter<Path> filter = new DirectoryStream.Filter<Path>() {
+ @Override
+ public boolean accept(Path entry) throws IOException {
+ String n = entry.getFileName().toString();
+ boolean accept = !n.startsWith(".last_")
+ && !n.startsWith(".next_")
+ && Files.isRegularFile(entry);
+ if (!Strings.isNullOrEmpty(suffix)) {
+ accept &= n.endsWith(suffix);
+ }
+ return accept;
+ }
+ };
+ try (DirectoryStream<Path> files = Files.newDirectoryStream(
+ pluginsDir, filter)) {
+ return Ordering.natural().sortedCopy(files);
+ }
+ }
+
+ public static List<Path> listPlugins(Path pluginsDir) throws IOException {
+ return listPlugins(pluginsDir, null);
+ }
+
public boolean isRemoteAdminEnabled() {
return remoteAdmin;
}
@@ -681,20 +710,8 @@
}
private List<Path> scanPathsInPluginsDirectory(Path pluginsDir) {
- if (pluginsDir == null || !Files.exists(pluginsDir)) {
- return Collections.emptyList();
- }
- DirectoryStream.Filter<Path> filter = new DirectoryStream.Filter<Path>() {
- @Override
- public boolean accept(Path entry) throws IOException {
- String n = entry.getFileName().toString();
- return !n.startsWith(".last_")
- && !n.startsWith(".next_");
- }
- };
- try (DirectoryStream<Path> files
- = Files.newDirectoryStream(pluginsDir, filter)) {
- return ImmutableList.copyOf(files);
+ try {
+ return listPlugins(pluginsDir);
} catch (IOException e) {
log.error("Cannot list " + pluginsDir.toAbsolutePath(), e);
return ImmutableList.of();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/AndPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/AndPredicate.java
index 39b0fa3..899e789 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/AndPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/AndPredicate.java
@@ -14,6 +14,8 @@
package com.google.gerrit.server.query;
+import static com.google.common.base.Preconditions.checkState;
+
import com.google.gwtorm.server.OrmException;
import java.util.ArrayList;
@@ -23,7 +25,7 @@
import java.util.List;
/** Requires all predicates to be true. */
-public class AndPredicate<T> extends Predicate<T> {
+public class AndPredicate<T> extends Predicate<T> implements Matchable<T> {
private final List<Predicate<T>> children;
private final int cost;
@@ -39,11 +41,11 @@
if (getClass() == p.getClass()) {
for (Predicate<T> gp : p.getChildren()) {
t.add(gp);
- c += gp.getCost();
+ c += gp.estimateCost();
}
} else {
t.add(p);
- c += p.getCost();
+ c += p.estimateCost();
}
}
children = t;
@@ -71,9 +73,21 @@
}
@Override
+ public boolean isMatchable() {
+ for (Predicate<T> c : children) {
+ if (!c.isMatchable()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
public boolean match(final T object) throws OrmException {
- for (final Predicate<T> c : children) {
- if (!c.match(object)) {
+ for (Predicate<T> c : children) {
+ checkState(c.isMatchable(), "match invoked, but child predicate %s "
+ + "doesn't implement %s", c, Matchable.class.getName());
+ if (!c.asMatchable().match(object)) {
return false;
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/AndSource.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/AndSource.java
index 4b62333..168be5d 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/AndSource.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/AndSource.java
@@ -36,17 +36,29 @@
implements DataSource<T>, Comparator<Predicate<T>> {
protected final DataSource<T> source;
+ private final IsVisibleToPredicate<T> isVisibleToPredicate;
private final int start;
private final int cardinality;
-
public AndSource(Collection<? extends Predicate<T>> that) {
- this(that, 0);
+ this(that, null, 0);
}
- public AndSource(Collection<? extends Predicate<T>> that, int start) {
+ public AndSource(Predicate<T> that,
+ IsVisibleToPredicate<T> isVisibleToPredicate) {
+ this(that, isVisibleToPredicate, 0);
+ }
+
+ public AndSource(Predicate<T> that,
+ IsVisibleToPredicate<T> isVisibleToPredicate, int start) {
+ this(ImmutableList.of(that), isVisibleToPredicate, start);
+ }
+
+ public AndSource(Collection<? extends Predicate<T>> that,
+ IsVisibleToPredicate<T> isVisibleToPredicate, int start) {
super(that);
checkArgument(start >= 0, "negative start: %s", start);
+ this.isVisibleToPredicate = isVisibleToPredicate;
this.start = start;
int c = Integer.MAX_VALUE;
@@ -56,9 +68,10 @@
if (p instanceof DataSource) {
c = Math.min(c, ((DataSource<?>) p).getCardinality());
- if (p.getCost() < minCost) {
+ int cost = p.estimateCost();
+ if (cost < minCost) {
s = toDataSource(p);
- minCost = p.getCost();
+ minCost = cost;
}
}
}
@@ -85,7 +98,7 @@
int nextStart = 0;
boolean skipped = false;
for (T data : buffer(source.read())) {
- if (match(data)) {
+ if (!isMatchable() || match(data)) {
r.add(data);
} else {
skipped = true;
@@ -124,6 +137,24 @@
return new ListResultSet<>(r);
}
+ @Override
+ public boolean isMatchable() {
+ return isVisibleToPredicate != null || super.isMatchable();
+ }
+
+ @Override
+ public boolean match(T object) throws OrmException {
+ if (isVisibleToPredicate != null && !isVisibleToPredicate.match(object)) {
+ return false;
+ }
+
+ if (super.isMatchable() && !super.match(object)) {
+ return false;
+ }
+
+ return true;
+ }
+
private Iterable<T> buffer(ResultSet<T> scanner) {
return FluentIterable.from(Iterables.partition(scanner, 50))
.transformAndConcat(new Function<List<T>, List<T>>() {
@@ -156,7 +187,7 @@
int cmp = ai - bi;
if (cmp == 0) {
- cmp = a.getCost() - b.getCost();
+ cmp = a.estimateCost() - b.estimateCost();
}
if (cmp == 0
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/IsVisibleToPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/IsVisibleToPredicate.java
new file mode 100644
index 0000000..38411e3
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/IsVisibleToPredicate.java
@@ -0,0 +1,22 @@
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.query;
+
+public abstract class IsVisibleToPredicate<T> extends OperatorPredicate<T>
+ implements Matchable<T> {
+ public IsVisibleToPredicate(String name, String value) {
+ super(name, value);
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/LimitPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/LimitPredicate.java
index d4e7440..7c38e5a8 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/LimitPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/LimitPredicate.java
@@ -14,7 +14,7 @@
package com.google.gerrit.server.query;
-public class LimitPredicate<T> extends IntPredicate<T> {
+public class LimitPredicate<T> extends IntPredicate<T> implements Matchable<T> {
@SuppressWarnings("unchecked")
public static Integer getLimit(String fieldName, Predicate<?> p) {
IntPredicate<?> ip = QueryBuilder.find(p, IntPredicate.class, fieldName);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/Matchable.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/Matchable.java
new file mode 100644
index 0000000..b37e112
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/Matchable.java
@@ -0,0 +1,29 @@
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.query;
+
+import com.google.gwtorm.server.OrmException;
+
+public interface Matchable<T> {
+ /**
+ * Does this predicate match this object?
+ *
+ * @throws OrmException
+ */
+ boolean match(T object) throws OrmException;
+
+ /** @return a cost estimate to run this predicate, higher figures cost more. */
+ int getCost();
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/NotPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/NotPredicate.java
index 248fb9c..8ffba72 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/NotPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/NotPredicate.java
@@ -14,6 +14,8 @@
package com.google.gerrit.server.query;
+import static com.google.common.base.Preconditions.checkState;
+
import com.google.gwtorm.server.OrmException;
import java.util.Collection;
@@ -21,7 +23,7 @@
import java.util.List;
/** Negates the result of another predicate. */
-public class NotPredicate<T> extends Predicate<T> {
+public class NotPredicate<T> extends Predicate<T> implements Matchable<T> {
private final Predicate<T> that;
protected NotPredicate(final Predicate<T> that) {
@@ -58,13 +60,20 @@
}
@Override
+ public boolean isMatchable() {
+ return that.isMatchable();
+ }
+
+ @Override
public boolean match(final T object) throws OrmException {
- return !that.match(object);
+ checkState(that.isMatchable(), "match invoked, but child predicate %s "
+ + "doesn't implement %s", that, Matchable.class.getName());
+ return !that.asMatchable().match(object);
}
@Override
public int getCost() {
- return that.getCost();
+ return that.estimateCost();
}
@Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/OperatorPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/OperatorPredicate.java
index e9fea02..2cb70af 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/OperatorPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/OperatorPredicate.java
@@ -22,7 +22,7 @@
private final String name;
private final String value;
- public OperatorPredicate(final String name, final String value) {
+ protected OperatorPredicate(final String name, final String value) {
this.name = name;
this.value = value;
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/OrPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/OrPredicate.java
index 2432a41..ad15286 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/OrPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/OrPredicate.java
@@ -14,6 +14,8 @@
package com.google.gerrit.server.query;
+import static com.google.common.base.Preconditions.checkState;
+
import com.google.gwtorm.server.OrmException;
import java.util.ArrayList;
@@ -23,7 +25,7 @@
import java.util.List;
/** Requires one predicate to be true. */
-public class OrPredicate<T> extends Predicate<T> {
+public class OrPredicate<T> extends Predicate<T> implements Matchable<T> {
private final List<Predicate<T>> children;
private final int cost;
@@ -39,11 +41,11 @@
if (getClass() == p.getClass()) {
for (Predicate<T> gp : p.getChildren()) {
t.add(gp);
- c += gp.getCost();
+ c += gp.estimateCost();
}
} else {
t.add(p);
- c += p.getCost();
+ c += p.estimateCost();
}
}
children = t;
@@ -71,9 +73,21 @@
}
@Override
+ public boolean isMatchable() {
+ for (Predicate<T> c : children) {
+ if (!c.isMatchable()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
public boolean match(final T object) throws OrmException {
for (final Predicate<T> c : children) {
- if (c.match(object)) {
+ checkState(c.isMatchable(), "match invoked, but child predicate %s "
+ + "doesn't implement %s", c, Matchable.class.getName());
+ if (c.asMatchable().match(object)) {
return true;
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/Predicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/Predicate.java
index f4be013..3a38da6 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/Predicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/Predicate.java
@@ -14,8 +14,9 @@
package com.google.gerrit.server.query;
+import static com.google.common.base.Preconditions.checkState;
+
import com.google.common.collect.Iterables;
-import com.google.gwtorm.server.OrmException;
import java.util.Collection;
import java.util.Collections;
@@ -113,15 +114,23 @@
/** Create a copy of this predicate, with new children. */
public abstract Predicate<T> copy(Collection<? extends Predicate<T>> children);
- /**
- * Does this predicate match this object?
- *
- * @throws OrmException
- */
- public abstract boolean match(T object) throws OrmException;
+ public boolean isMatchable() {
+ return this instanceof Matchable;
+ }
+
+ @SuppressWarnings("unchecked")
+ public Matchable<T> asMatchable() {
+ checkState(isMatchable(), "not matchable");
+ return (Matchable<T>) this;
+ }
/** @return a cost estimate to run this predicate, higher figures cost more. */
- public abstract int getCost();
+ public int estimateCost() {
+ if (!isMatchable()) {
+ return 1;
+ }
+ return asMatchable().getCost();
+ }
@Override
public abstract int hashCode();
@@ -129,7 +138,7 @@
@Override
public abstract boolean equals(Object other);
- private static class Any<T> extends Predicate<T> {
+ private static class Any<T> extends Predicate<T> implements Matchable<T> {
private static final Any<Object> INSTANCE = new Any<>();
private Any() {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/account/AccountIdPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/account/AccountIdPredicate.java
deleted file mode 100644
index 659f5d8..0000000
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/account/AccountIdPredicate.java
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright (C) 2016 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.query.account;
-
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.server.account.AccountState;
-import com.google.gerrit.server.index.IndexPredicate;
-import com.google.gerrit.server.index.account.AccountField;
-import com.google.gwtorm.server.OrmException;
-
-public class AccountIdPredicate extends IndexPredicate<AccountState> {
- private final Account.Id accountId;
-
- public AccountIdPredicate(Account.Id accountId) {
- super(AccountField.ID, AccountQueryBuilder.FIELD_ACCOUNT,
- accountId.toString());
- this.accountId = accountId;
- }
-
- @Override
- public boolean match(AccountState accountState) throws OrmException {
- return accountId.equals(accountState.getAccount().getId());
- }
-
- @Override
- public int getCost() {
- return 1;
- }
-
-}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/account/IsVisibleToPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/account/AccountIsVisibleToPredicate.java
similarity index 88%
rename from gerrit-server/src/main/java/com/google/gerrit/server/query/account/IsVisibleToPredicate.java
rename to gerrit-server/src/main/java/com/google/gerrit/server/query/account/AccountIsVisibleToPredicate.java
index 8fbe4cf..dc68a61 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/account/IsVisibleToPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/account/AccountIsVisibleToPredicate.java
@@ -17,11 +17,12 @@
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.account.AccountControl;
import com.google.gerrit.server.account.AccountState;
-import com.google.gerrit.server.query.OperatorPredicate;
+import com.google.gerrit.server.query.IsVisibleToPredicate;
import com.google.gerrit.server.query.change.SingleGroupUser;
import com.google.gwtorm.server.OrmException;
-public class IsVisibleToPredicate extends OperatorPredicate<AccountState> {
+public class AccountIsVisibleToPredicate
+ extends IsVisibleToPredicate<AccountState> {
private static String describe(CurrentUser user) {
if (user.isIdentifiedUser()) {
return user.getAccountId().toString();
@@ -35,7 +36,7 @@
private final AccountControl accountControl;
- IsVisibleToPredicate(AccountControl accountControl) {
+ AccountIsVisibleToPredicate(AccountControl accountControl) {
super(AccountQueryBuilder.FIELD_VISIBLETO,
describe(accountControl.getUser()));
this.accountControl = accountControl;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/account/AccountPredicates.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/account/AccountPredicates.java
new file mode 100644
index 0000000..7a9f5bd
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/account/AccountPredicates.java
@@ -0,0 +1,71 @@
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.query.account;
+
+import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.server.account.AccountState;
+import com.google.gerrit.server.index.FieldDef;
+import com.google.gerrit.server.index.IndexPredicate;
+import com.google.gerrit.server.index.account.AccountField;
+import com.google.gerrit.server.query.Predicate;
+import com.google.gerrit.server.query.QueryBuilder;
+
+public class AccountPredicates {
+ public static boolean hasActive(Predicate<AccountState> p) {
+ return QueryBuilder.find(p, AccountPredicate.class,
+ AccountField.ACTIVE.getName()) != null;
+ }
+
+ static Predicate<AccountState> id(Account.Id accountId) {
+ return new AccountPredicate(AccountField.ID,
+ AccountQueryBuilder.FIELD_ACCOUNT, accountId.toString());
+ }
+
+ static Predicate<AccountState> email(String email) {
+ return new AccountPredicate(AccountField.EMAIL,
+ AccountQueryBuilder.FIELD_EMAIL, email.toLowerCase());
+ }
+
+ static Predicate<AccountState> equalsName(String name) {
+ return new AccountPredicate(AccountField.NAME_PART,
+ AccountQueryBuilder.FIELD_NAME, name.toLowerCase());
+ }
+
+ public static Predicate<AccountState> isActive() {
+ return new AccountPredicate(AccountField.ACTIVE, "1");
+ }
+
+ static Predicate<AccountState> isInactive() {
+ return new AccountPredicate(AccountField.ACTIVE, "0");
+ }
+
+ static Predicate<AccountState> username(String username) {
+ return new AccountPredicate(AccountField.USERNAME,
+ AccountQueryBuilder.FIELD_USERNAME, username.toLowerCase());
+ }
+
+ static class AccountPredicate extends IndexPredicate<AccountState> {
+ AccountPredicate(FieldDef<AccountState, ?> def, String value) {
+ super(def, value);
+ }
+
+ AccountPredicate(FieldDef<AccountState, ?> def, String name, String value) {
+ super(def, name, value);
+ }
+ }
+
+ private AccountPredicates() {
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/account/AccountQueryBuilder.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/account/AccountQueryBuilder.java
index 4082e08..b10d4c5 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/account/AccountQueryBuilder.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/account/AccountQueryBuilder.java
@@ -14,6 +14,7 @@
package com.google.gerrit.server.query.account;
+import com.google.common.collect.Lists;
import com.google.common.primitives.Ints;
import com.google.gerrit.common.errors.NotSignedInException;
import com.google.gerrit.reviewdb.client.Account;
@@ -28,6 +29,8 @@
import com.google.inject.Provider;
import com.google.inject.ProvisionException;
+import java.util.List;
+
/**
* Parses a query string meant to be applied to account objects.
*/
@@ -37,7 +40,10 @@
}
public static final String FIELD_ACCOUNT = "account";
+ public static final String FIELD_EMAIL = "email";
public static final String FIELD_LIMIT = "limit";
+ public static final String FIELD_NAME = "name";
+ public static final String FIELD_USERNAME = "username";
public static final String FIELD_VISIBLETO = "visibleto";
private static final QueryBuilder.Definition<AccountState, AccountQueryBuilder> mydef =
@@ -81,15 +87,19 @@
}
@Operator
- public Predicate<AccountState> account(String query)
- throws QueryParseException {
- if ("self".equals(query)) {
- return new AccountIdPredicate(self());
+ public Predicate<AccountState> email(String email) {
+ return AccountPredicates.email(email);
+ }
+
+ @Operator
+ public Predicate<AccountState> is(String value) throws QueryParseException {
+ if ("active".equalsIgnoreCase(value)) {
+ return AccountPredicates.isActive();
}
- if (query.matches("^[1-9][0-9]*$")) {
- return new AccountIdPredicate(Account.Id.parse(query));
+ if ("inactive".equalsIgnoreCase(value)) {
+ return AccountPredicates.isInactive();
}
- throw error("User " + query + " not found");
+ throw error("Invalid query");
}
@Operator
@@ -102,10 +112,31 @@
return new LimitPredicate<>(FIELD_LIMIT, limit);
}
+ @Operator
+ public Predicate<AccountState> name(String name) {
+ return AccountPredicates.equalsName(name);
+ }
+
+ @Operator
+ public Predicate<AccountState> username(String username) {
+ return AccountPredicates.username(username);
+ }
+
@Override
- protected Predicate<AccountState> defaultField(String query)
+ public Predicate<AccountState> defaultField(String query)
throws QueryParseException {
- return account(query);
+ if ("self".equalsIgnoreCase(query)) {
+ return AccountPredicates.id(self());
+ }
+
+ List<Predicate<AccountState>> preds = Lists.newArrayListWithCapacity(3);
+ Integer id = Ints.tryParse(query);
+ if (id != null) {
+ preds.add(AccountPredicates.id(new Account.Id(id)));
+ }
+ preds.add(name(query));
+ preds.add(username(query));
+ return Predicate.or(preds);
}
private Account.Id self() throws QueryParseException {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/account/AccountQueryProcessor.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/account/AccountQueryProcessor.java
index b2be7b5..48d0897 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/account/AccountQueryProcessor.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/account/AccountQueryProcessor.java
@@ -14,13 +14,14 @@
package com.google.gerrit.server.query.account;
+import static com.google.common.base.Preconditions.checkState;
import static com.google.gerrit.server.query.account.AccountQueryBuilder.FIELD_LIMIT;
-import com.google.common.collect.ImmutableList;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.account.AccountControl;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.index.IndexConfig;
+import com.google.gerrit.server.index.IndexPredicate;
import com.google.gerrit.server.index.account.AccountIndexCollection;
import com.google.gerrit.server.index.account.AccountIndexRewriter;
import com.google.gerrit.server.index.account.AccountSchemaDefinitions;
@@ -33,6 +34,13 @@
public class AccountQueryProcessor extends QueryProcessor<AccountState> {
private final AccountControl.Factory accountControlFactory;
+ static {
+ // It is assumed that basic rewrites do not touch visibleto predicates.
+ checkState(
+ !AccountIsVisibleToPredicate.class.isAssignableFrom(IndexPredicate.class),
+ "AccountQueryProcessor assumes visibleto is not used by the index rewriter.");
+ }
+
@Inject
protected AccountQueryProcessor(Provider<CurrentUser> userProvider,
Metrics metrics,
@@ -48,7 +56,7 @@
@Override
protected Predicate<AccountState> enforceVisibility(
Predicate<AccountState> pred) {
- return new AndSource<>(ImmutableList.of(pred,
- new IsVisibleToPredicate(accountControlFactory.get())));
+ return new AndSource<>(pred,
+ new AccountIsVisibleToPredicate(accountControlFactory.get()), start);
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/AddedPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/AddedPredicate.java
index b20a194..b3cdd6a 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/AddedPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/AddedPredicate.java
@@ -14,18 +14,17 @@
package com.google.gerrit.server.query.change;
-import com.google.gerrit.server.index.IntegerRangePredicate;
import com.google.gerrit.server.index.change.ChangeField;
import com.google.gerrit.server.query.QueryParseException;
import com.google.gwtorm.server.OrmException;
-public class AddedPredicate extends IntegerRangePredicate<ChangeData> {
+public class AddedPredicate extends IntegerRangeChangePredicate {
AddedPredicate(String value) throws QueryParseException {
super(ChangeField.ADDED, value);
}
@Override
- protected int getValueInt(ChangeData changeData) throws OrmException {
- return changeData.changedLines().insertions;
+ protected Integer getValueInt(ChangeData changeData) throws OrmException {
+ return ChangeField.ADDED.get(changeData, null);
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/AfterPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/AfterPredicate.java
index 5ed871a..477bf16 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/AfterPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/AfterPredicate.java
@@ -14,14 +14,13 @@
package com.google.gerrit.server.query.change;
-import com.google.gerrit.server.index.TimestampRangePredicate;
import com.google.gerrit.server.index.change.ChangeField;
import com.google.gerrit.server.query.QueryParseException;
import com.google.gwtorm.server.OrmException;
import java.util.Date;
-public class AfterPredicate extends TimestampRangePredicate<ChangeData> {
+public class AfterPredicate extends TimestampRangeChangePredicate {
private final Date cut;
AfterPredicate(String value) throws QueryParseException {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/AgePredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/AgePredicate.java
index 2b140d3..fd6cbee 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/AgePredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/AgePredicate.java
@@ -20,13 +20,12 @@
import com.google.gerrit.common.TimeUtil;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.config.ConfigUtil;
-import com.google.gerrit.server.index.TimestampRangePredicate;
import com.google.gerrit.server.index.change.ChangeField;
import com.google.gwtorm.server.OrmException;
import java.sql.Timestamp;
-public class AgePredicate extends TimestampRangePredicate<ChangeData> {
+public class AgePredicate extends TimestampRangeChangePredicate {
private final long cut;
AgePredicate(String value) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/AndChangeSource.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/AndChangeSource.java
index d40e53f..bd7daed 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/AndChangeSource.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/AndChangeSource.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.query.change;
import com.google.gerrit.server.query.AndSource;
+import com.google.gerrit.server.query.IsVisibleToPredicate;
import com.google.gerrit.server.query.Predicate;
import com.google.gwtorm.server.OrmException;
import com.google.gwtorm.server.OrmRuntimeException;
@@ -25,13 +26,13 @@
public class AndChangeSource extends AndSource<ChangeData>
implements ChangeDataSource {
- public AndChangeSource(Collection<? extends Predicate<ChangeData>> that) {
- super(that, 0);
+ public AndChangeSource(Collection<Predicate<ChangeData>> that) {
+ super(that);
}
- public AndChangeSource(Collection<? extends Predicate<ChangeData>> that,
- int start) {
- super(that, start);
+ public AndChangeSource(Predicate<ChangeData> that,
+ IsVisibleToPredicate<ChangeData> isVisibleToPredicate, int start) {
+ super(that, isVisibleToPredicate, start);
}
@Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/AuthorPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/AuthorPredicate.java
index ae192ab..ebaaab9 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/AuthorPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/AuthorPredicate.java
@@ -17,11 +17,10 @@
import static com.google.gerrit.server.index.change.ChangeField.AUTHOR;
import static com.google.gerrit.server.query.change.ChangeQueryBuilder.FIELD_AUTHOR;
-import com.google.gerrit.server.index.IndexPredicate;
import com.google.gerrit.server.index.change.ChangeField;
import com.google.gwtorm.server.OrmException;
-public class AuthorPredicate extends IndexPredicate<ChangeData> {
+public class AuthorPredicate extends ChangeIndexPredicate {
AuthorPredicate(String value) {
super(AUTHOR, FIELD_AUTHOR, value.toLowerCase());
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/BeforePredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/BeforePredicate.java
index 0618cc2..f36a1631 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/BeforePredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/BeforePredicate.java
@@ -14,14 +14,13 @@
package com.google.gerrit.server.query.change;
-import com.google.gerrit.server.index.TimestampRangePredicate;
import com.google.gerrit.server.index.change.ChangeField;
import com.google.gerrit.server.query.QueryParseException;
import com.google.gwtorm.server.OrmException;
import java.util.Date;
-public class BeforePredicate extends TimestampRangePredicate<ChangeData> {
+public class BeforePredicate extends TimestampRangeChangePredicate {
private final Date cut;
BeforePredicate(String value) throws QueryParseException {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java
index 1794ee3..1929833 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java
@@ -331,13 +331,14 @@
private Collection<PatchSet> patchSets;
private ListMultimap<PatchSet.Id, PatchSetApproval> allApprovals;
private List<PatchSetApproval> currentApprovals;
- private Map<Integer, List<String>> files = new HashMap<>();
+ private Map<Integer, List<String>> files;
+ private Map<Integer, Optional<PatchList>> patchLists;
private Collection<PatchLineComment> publishedComments;
private CurrentUser visibleTo;
private ChangeControl changeControl;
private List<ChangeMessage> messages;
private List<SubmitRecord> submitRecords;
- private ChangedLines changedLines;
+ private Optional<ChangedLines> changedLines;
private SubmitTypeRecord submitTypeRecord;
private Boolean mergeable;
private Set<String> hashtags;
@@ -549,10 +550,17 @@
return db;
}
+ private Map<Integer, List<String>> initFiles() {
+ if (files == null) {
+ files = new HashMap<>();
+ }
+ return files;
+ }
+
public void setCurrentFilePaths(List<String> filePaths) throws OrmException {
PatchSet ps = currentPatchSet();
if (ps != null) {
- files.put(ps.getPatchSetId(), ImmutableList.copyOf(filePaths));
+ initFiles().put(ps.getPatchSetId(), ImmutableList.copyOf(filePaths));
}
}
@@ -565,23 +573,23 @@
}
public List<String> filePaths(PatchSet ps) throws OrmException {
- if (!files.containsKey(ps.getPatchSetId())) {
+ Integer psId = ps.getPatchSetId();
+ List<String> r = initFiles().get(psId);
+ if (r == null) {
Change c = change();
if (c == null) {
return null;
}
- PatchList p;
- try {
- p = patchListCache.get(c, ps);
- } catch (PatchListNotAvailableException e) {
+ Optional<PatchList> p = getPatchList(c, ps);
+ if (!p.isPresent()) {
List<String> emptyFileList = Collections.emptyList();
files.put(ps.getPatchSetId(), emptyFileList);
return emptyFileList;
}
- List<String> r = new ArrayList<>(p.getPatches().size());
- for (PatchListEntry e : p.getPatches()) {
+ r = new ArrayList<>(p.get().getPatches().size());
+ for (PatchListEntry e : p.get().getPatches()) {
if (Patch.COMMIT_MSG.equals(e.getNewName())) {
continue;
}
@@ -601,37 +609,59 @@
}
}
Collections.sort(r);
- files.put(ps.getPatchSetId(), Collections.unmodifiableList(r));
+ r = Collections.unmodifiableList(r);
+ files.put(psId, r);
}
- return files.get(ps.getPatchSetId());
+ return r;
}
- public ChangedLines changedLines() throws OrmException {
- if (changedLines == null) {
- Change c = change();
- if (c == null) {
- return null;
- }
-
- PatchSet ps = currentPatchSet();
- if (ps == null) {
- return null;
- }
-
- PatchList p;
+ private Optional<PatchList> getPatchList(Change c, PatchSet ps) {
+ Integer psId = ps.getId().get();
+ if (patchLists == null) {
+ patchLists = new HashMap<>();
+ }
+ Optional<PatchList> r = patchLists.get(psId);
+ if (r == null) {
try {
- p = patchListCache.get(c, ps);
+ r = Optional.of(patchListCache.get(c, ps));
} catch (PatchListNotAvailableException e) {
- return null;
+ r = Optional.absent();
}
+ patchLists.put(psId, r);
+ }
+ return r;
+ }
- changedLines = new ChangedLines(p.getInsertions(), p.getDeletions());
+ private Optional<ChangedLines> computeChangedLines() throws OrmException {
+ Change c = change();
+ if (c == null) {
+ return Optional.absent();
+ }
+ PatchSet ps = currentPatchSet();
+ if (ps == null) {
+ return Optional.absent();
+ }
+ Optional<PatchList> p = getPatchList(c, ps);
+ if (!p.isPresent()) {
+ return Optional.absent();
+ }
+ return Optional.of(
+ new ChangedLines(p.get().getInsertions(), p.get().getDeletions()));
+ }
+
+ public Optional<ChangedLines> changedLines() throws OrmException {
+ if (changedLines == null) {
+ changedLines = computeChangedLines();
}
return changedLines;
}
public void setChangedLines(int insertions, int deletions) {
- changedLines = new ChangedLines(insertions, deletions);
+ changedLines = Optional.of(new ChangedLines(insertions, deletions));
+ }
+
+ public void setNoChangedLines() {
+ changedLines = Optional.absent();
}
public Change.Id getId() {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeIdPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeIdPredicate.java
index 1fbeb86..85d433a 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeIdPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeIdPredicate.java
@@ -15,12 +15,11 @@
package com.google.gerrit.server.query.change;
import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.server.index.IndexPredicate;
import com.google.gerrit.server.index.change.ChangeField;
import com.google.gwtorm.server.OrmException;
/** Predicate over Change-Id strings (aka Change.Key). */
-class ChangeIdPredicate extends IndexPredicate<ChangeData> {
+class ChangeIdPredicate extends ChangeIndexPredicate {
ChangeIdPredicate(String id) {
super(ChangeField.ID, ChangeQueryBuilder.FIELD_CHANGE, id);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeIndexPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeIndexPredicate.java
new file mode 100644
index 0000000..80951fd
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeIndexPredicate.java
@@ -0,0 +1,31 @@
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.query.change;
+
+import com.google.gerrit.server.index.FieldDef;
+import com.google.gerrit.server.index.IndexPredicate;
+import com.google.gerrit.server.query.Matchable;
+
+public abstract class ChangeIndexPredicate extends IndexPredicate<ChangeData>
+ implements Matchable<ChangeData> {
+ protected ChangeIndexPredicate(FieldDef<ChangeData, ?> def, String value) {
+ super(def, value);
+ }
+
+ protected ChangeIndexPredicate(FieldDef<ChangeData, ?> def, String name,
+ String value) {
+ super(def, name, value);
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/IsVisibleToPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeIsVisibleToPredicate.java
similarity index 91%
rename from gerrit-server/src/main/java/com/google/gerrit/server/query/change/IsVisibleToPredicate.java
rename to gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeIsVisibleToPredicate.java
index c4214f2..303c9f8 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/IsVisibleToPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeIsVisibleToPredicate.java
@@ -20,11 +20,11 @@
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.project.NoSuchChangeException;
-import com.google.gerrit.server.query.OperatorPredicate;
+import com.google.gerrit.server.query.IsVisibleToPredicate;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Provider;
-class IsVisibleToPredicate extends OperatorPredicate<ChangeData> {
+class ChangeIsVisibleToPredicate extends IsVisibleToPredicate<ChangeData> {
private static String describe(CurrentUser user) {
if (user.isIdentifiedUser()) {
return user.getAccountId().toString();
@@ -41,7 +41,8 @@
private final ChangeControl.GenericFactory changeControl;
private final CurrentUser user;
- IsVisibleToPredicate(Provider<ReviewDb> db, ChangeNotes.Factory notesFactory,
+ ChangeIsVisibleToPredicate(Provider<ReviewDb> db,
+ ChangeNotes.Factory notesFactory,
ChangeControl.GenericFactory changeControlFactory, CurrentUser user) {
super(ChangeQueryBuilder.FIELD_VISIBLETO, describe(user));
this.db = db;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeOperatorPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeOperatorPredicate.java
new file mode 100644
index 0000000..6bec598
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeOperatorPredicate.java
@@ -0,0 +1,26 @@
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.query.change;
+
+import com.google.gerrit.server.query.Matchable;
+import com.google.gerrit.server.query.OperatorPredicate;
+
+public abstract class ChangeOperatorPredicate extends
+ OperatorPredicate<ChangeData> implements Matchable<ChangeData> {
+
+ protected ChangeOperatorPredicate(String name, String value) {
+ super(name, value);
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
index b99f024..a150c93 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
@@ -479,7 +479,7 @@
// not status: alias?
}
- throw new IllegalArgumentException();
+ throw error("Invalid query");
}
@Operator
@@ -768,7 +768,7 @@
}
public Predicate<ChangeData> visibleto(CurrentUser user) {
- return new IsVisibleToPredicate(args.db, args.notesFactory,
+ return new ChangeIsVisibleToPredicate(args.db, args.notesFactory,
args.changeControlGenericFactory, user);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryProcessor.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryProcessor.java
index 4e534c8..44e5e7e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryProcessor.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryProcessor.java
@@ -17,7 +17,6 @@
import static com.google.common.base.Preconditions.checkState;
import static com.google.gerrit.server.query.change.ChangeQueryBuilder.FIELD_LIMIT;
-import com.google.common.collect.ImmutableList;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.index.IndexConfig;
@@ -44,7 +43,7 @@
static {
// It is assumed that basic rewrites do not touch visibleto predicates.
checkState(
- !IsVisibleToPredicate.class.isAssignableFrom(IndexPredicate.class),
+ !ChangeIsVisibleToPredicate.class.isAssignableFrom(IndexPredicate.class),
"ChangeQueryProcessor assumes visibleto is not used by the index rewriter.");
}
@@ -80,7 +79,7 @@
@Override
protected Predicate<ChangeData> enforceVisibility(
Predicate<ChangeData> pred) {
- return new AndChangeSource(ImmutableList.of(pred, new IsVisibleToPredicate(db,
- notesFactory, changeControlFactory, userProvider.get())), start);
+ return new AndChangeSource(pred, new ChangeIsVisibleToPredicate(db,
+ notesFactory, changeControlFactory, userProvider.get()), start);
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeRegexPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeRegexPredicate.java
new file mode 100644
index 0000000..747d72d
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeRegexPredicate.java
@@ -0,0 +1,31 @@
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.query.change;
+
+import com.google.gerrit.server.index.FieldDef;
+import com.google.gerrit.server.index.RegexPredicate;
+import com.google.gerrit.server.query.Matchable;
+
+public abstract class ChangeRegexPredicate extends RegexPredicate<ChangeData>
+ implements Matchable<ChangeData> {
+ protected ChangeRegexPredicate(FieldDef<ChangeData, ?> def, String value) {
+ super(def, value);
+ }
+
+ protected ChangeRegexPredicate(FieldDef<ChangeData, ?> def, String name,
+ String value) {
+ super(def, name, value);
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeStatusPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeStatusPredicate.java
index 4d42c33..1c92ecf 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeStatusPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeStatusPredicate.java
@@ -16,7 +16,6 @@
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.Change.Status;
-import com.google.gerrit.server.index.IndexPredicate;
import com.google.gerrit.server.index.change.ChangeField;
import com.google.gerrit.server.query.Predicate;
import com.google.gwtorm.server.OrmException;
@@ -36,7 +35,7 @@
* <p>
* Status names are looked up by prefix case-insensitively.
*/
-public final class ChangeStatusPredicate extends IndexPredicate<ChangeData> {
+public final class ChangeStatusPredicate extends ChangeIndexPredicate {
private static final TreeMap<String, Predicate<ChangeData>> PREDICATES;
private static final Predicate<ChangeData> CLOSED;
private static final Predicate<ChangeData> OPEN;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/CommentByPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/CommentByPredicate.java
index 1967a06..48d6e05 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/CommentByPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/CommentByPredicate.java
@@ -17,13 +17,12 @@
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.ChangeMessage;
import com.google.gerrit.reviewdb.client.PatchLineComment;
-import com.google.gerrit.server.index.IndexPredicate;
import com.google.gerrit.server.index.change.ChangeField;
import com.google.gwtorm.server.OrmException;
import java.util.Objects;
-class CommentByPredicate extends IndexPredicate<ChangeData> {
+class CommentByPredicate extends ChangeIndexPredicate {
private final Account.Id id;
CommentByPredicate(Account.Id id) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/CommentPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/CommentPredicate.java
index 5e3fa3d..b351740 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/CommentPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/CommentPredicate.java
@@ -14,7 +14,6 @@
package com.google.gerrit.server.query.change;
-import com.google.gerrit.server.index.IndexPredicate;
import com.google.gerrit.server.index.change.ChangeField;
import com.google.gerrit.server.index.change.ChangeIndex;
import com.google.gerrit.server.index.change.IndexedChangeQuery;
@@ -22,7 +21,7 @@
import com.google.gerrit.server.query.QueryParseException;
import com.google.gwtorm.server.OrmException;
-class CommentPredicate extends IndexPredicate<ChangeData> {
+class CommentPredicate extends ChangeIndexPredicate {
private final ChangeIndex index;
CommentPredicate(ChangeIndex index, String value) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/CommitPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/CommitPredicate.java
index 91b2c58..aa3dde3 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/CommitPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/CommitPredicate.java
@@ -20,10 +20,9 @@
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.server.index.FieldDef;
-import com.google.gerrit.server.index.IndexPredicate;
import com.google.gwtorm.server.OrmException;
-class CommitPredicate extends IndexPredicate<ChangeData> {
+class CommitPredicate extends ChangeIndexPredicate {
static FieldDef<ChangeData, ?> commitField(String id) {
if (id.length() == OBJECT_ID_STRING_LENGTH) {
return EXACT_COMMIT;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/CommitterPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/CommitterPredicate.java
index f923d00..06f5379 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/CommitterPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/CommitterPredicate.java
@@ -17,11 +17,10 @@
import static com.google.gerrit.server.index.change.ChangeField.COMMITTER;
import static com.google.gerrit.server.query.change.ChangeQueryBuilder.FIELD_COMMITTER;
-import com.google.gerrit.server.index.IndexPredicate;
import com.google.gerrit.server.index.change.ChangeField;
import com.google.gwtorm.server.OrmException;
-public class CommitterPredicate extends IndexPredicate<ChangeData> {
+public class CommitterPredicate extends ChangeIndexPredicate {
CommitterPredicate(String value) {
super(COMMITTER, FIELD_COMMITTER, value.toLowerCase());
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ConflictsPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ConflictsPredicate.java
index 74865ec..69bc2ca 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ConflictsPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ConflictsPredicate.java
@@ -25,7 +25,6 @@
import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectState;
-import com.google.gerrit.server.query.OperatorPredicate;
import com.google.gerrit.server.query.OrPredicate;
import com.google.gerrit.server.query.Predicate;
import com.google.gerrit.server.query.change.ChangeQueryBuilder.Arguments;
@@ -84,7 +83,7 @@
predicatesForOneChange.add(or(or(filePredicates),
new IsMergePredicate(args, value)));
- predicatesForOneChange.add(new OperatorPredicate<ChangeData>(
+ predicatesForOneChange.add(new ChangeOperatorPredicate(
ChangeQueryBuilder.FIELD_CONFLICTS, value) {
@Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/DeletedPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/DeletedPredicate.java
index 8e9ac73..9e49269 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/DeletedPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/DeletedPredicate.java
@@ -14,18 +14,17 @@
package com.google.gerrit.server.query.change;
-import com.google.gerrit.server.index.IntegerRangePredicate;
import com.google.gerrit.server.index.change.ChangeField;
import com.google.gerrit.server.query.QueryParseException;
import com.google.gwtorm.server.OrmException;
-public class DeletedPredicate extends IntegerRangePredicate<ChangeData> {
+public class DeletedPredicate extends IntegerRangeChangePredicate {
DeletedPredicate(String value) throws QueryParseException {
super(ChangeField.DELETED, value);
}
@Override
- protected int getValueInt(ChangeData changeData) throws OrmException {
- return changeData.changedLines().deletions;
+ protected Integer getValueInt(ChangeData changeData) throws OrmException {
+ return ChangeField.DELETED.get(changeData, null);
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/DeltaPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/DeltaPredicate.java
index a3eaa8a..ce33225 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/DeltaPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/DeltaPredicate.java
@@ -14,20 +14,17 @@
package com.google.gerrit.server.query.change;
-import com.google.gerrit.server.index.IntegerRangePredicate;
import com.google.gerrit.server.index.change.ChangeField;
import com.google.gerrit.server.query.QueryParseException;
-import com.google.gerrit.server.query.change.ChangeData.ChangedLines;
import com.google.gwtorm.server.OrmException;
-public class DeltaPredicate extends IntegerRangePredicate<ChangeData> {
+public class DeltaPredicate extends IntegerRangeChangePredicate {
DeltaPredicate(String value) throws QueryParseException {
super(ChangeField.DELTA, value);
}
@Override
- protected int getValueInt(ChangeData changeData) throws OrmException {
- ChangedLines changedLines = changeData.changedLines();
- return changedLines.insertions + changedLines.deletions;
+ protected Integer getValueInt(ChangeData changeData) throws OrmException {
+ return ChangeField.DELTA.get(changeData, null);
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/DestinationPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/DestinationPredicate.java
index 25fa09f..7e573dc 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/DestinationPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/DestinationPredicate.java
@@ -16,12 +16,11 @@
import com.google.gerrit.reviewdb.client.Branch;
import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.server.query.OperatorPredicate;
import com.google.gwtorm.server.OrmException;
import java.util.Set;
-class DestinationPredicate extends OperatorPredicate<ChangeData> {
+class DestinationPredicate extends ChangeOperatorPredicate {
Set<Branch.NameKey> destinations;
DestinationPredicate(Set<Branch.NameKey> destinations, String value) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/EditByPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/EditByPredicate.java
index f1fa000..8be5235 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/EditByPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/EditByPredicate.java
@@ -15,11 +15,10 @@
package com.google.gerrit.server.query.change;
import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.server.index.IndexPredicate;
import com.google.gerrit.server.index.change.ChangeField;
import com.google.gwtorm.server.OrmException;
-class EditByPredicate extends IndexPredicate<ChangeData> {
+class EditByPredicate extends ChangeIndexPredicate {
private final Account.Id id;
EditByPredicate(Account.Id id) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/EqualsFilePredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/EqualsFilePredicate.java
index 8c98b6f..6877761 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/EqualsFilePredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/EqualsFilePredicate.java
@@ -14,13 +14,12 @@
package com.google.gerrit.server.query.change;
-import com.google.gerrit.server.index.IndexPredicate;
import com.google.gerrit.server.index.change.ChangeField;
import com.google.gerrit.server.query.Predicate;
import com.google.gerrit.server.query.change.ChangeQueryBuilder.Arguments;
import com.google.gwtorm.server.OrmException;
-class EqualsFilePredicate extends IndexPredicate<ChangeData> {
+class EqualsFilePredicate extends ChangeIndexPredicate {
static Predicate<ChangeData> create(Arguments args, String value) {
Predicate<ChangeData> eqPath =
new EqualsPathPredicate(ChangeQueryBuilder.FIELD_FILE, value);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/EqualsLabelPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/EqualsLabelPredicate.java
index b01fdbe..e752b05 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/EqualsLabelPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/EqualsLabelPredicate.java
@@ -23,7 +23,6 @@
import com.google.gerrit.reviewdb.client.PatchSetApproval;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.index.IndexPredicate;
import com.google.gerrit.server.index.change.ChangeField;
import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.project.NoSuchChangeException;
@@ -32,7 +31,7 @@
import com.google.gwtorm.server.OrmException;
import com.google.inject.Provider;
-class EqualsLabelPredicate extends IndexPredicate<ChangeData> {
+class EqualsLabelPredicate extends ChangeIndexPredicate {
private final ProjectCache projectCache;
private final ChangeControl.GenericFactory ccFactory;
private final IdentifiedUser.GenericFactory userFactory;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/EqualsPathPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/EqualsPathPredicate.java
index 85c3cd5..5edd06c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/EqualsPathPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/EqualsPathPredicate.java
@@ -14,14 +14,13 @@
package com.google.gerrit.server.query.change;
-import com.google.gerrit.server.index.IndexPredicate;
import com.google.gerrit.server.index.change.ChangeField;
import com.google.gwtorm.server.OrmException;
import java.util.Collections;
import java.util.List;
-class EqualsPathPredicate extends IndexPredicate<ChangeData> {
+class EqualsPathPredicate extends ChangeIndexPredicate {
private final String value;
EqualsPathPredicate(String fieldName, String value) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ExactTopicPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ExactTopicPredicate.java
index 6658577..510910e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ExactTopicPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ExactTopicPredicate.java
@@ -17,10 +17,9 @@
import static com.google.gerrit.server.index.change.ChangeField.EXACT_TOPIC;
import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.server.index.IndexPredicate;
import com.google.gwtorm.server.OrmException;
-class ExactTopicPredicate extends IndexPredicate<ChangeData> {
+class ExactTopicPredicate extends ChangeIndexPredicate {
ExactTopicPredicate(String topic) {
super(EXACT_TOPIC, topic);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/FuzzyTopicPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/FuzzyTopicPredicate.java
index 23b3ee6..5651544 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/FuzzyTopicPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/FuzzyTopicPredicate.java
@@ -18,14 +18,13 @@
import com.google.common.collect.Iterables;
import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.server.index.IndexPredicate;
import com.google.gerrit.server.index.change.ChangeIndex;
import com.google.gerrit.server.index.change.IndexedChangeQuery;
import com.google.gerrit.server.query.Predicate;
import com.google.gerrit.server.query.QueryParseException;
import com.google.gwtorm.server.OrmException;
-class FuzzyTopicPredicate extends IndexPredicate<ChangeData> {
+class FuzzyTopicPredicate extends ChangeIndexPredicate {
private final ChangeIndex index;
FuzzyTopicPredicate(String topic, ChangeIndex index) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/GroupPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/GroupPredicate.java
index ff9c544..9e9bc8d 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/GroupPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/GroupPredicate.java
@@ -15,13 +15,12 @@
package com.google.gerrit.server.query.change;
import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.server.index.IndexPredicate;
import com.google.gerrit.server.index.change.ChangeField;
import com.google.gwtorm.server.OrmException;
import java.util.List;
-class GroupPredicate extends IndexPredicate<ChangeData> {
+class GroupPredicate extends ChangeIndexPredicate {
GroupPredicate(String group) {
super(ChangeField.GROUP, group);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/HasDraftByLegacyPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/HasDraftByLegacyPredicate.java
index f85a9ed..45a00c6 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/HasDraftByLegacyPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/HasDraftByLegacyPredicate.java
@@ -17,7 +17,6 @@
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.PatchLineComment;
-import com.google.gerrit.server.query.OperatorPredicate;
import com.google.gerrit.server.query.change.ChangeQueryBuilder.Arguments;
import com.google.gwtorm.server.ListResultSet;
import com.google.gwtorm.server.OrmException;
@@ -29,8 +28,8 @@
import java.util.Set;
@Deprecated
-class HasDraftByLegacyPredicate extends OperatorPredicate<ChangeData> implements
- ChangeDataSource {
+class HasDraftByLegacyPredicate extends ChangeOperatorPredicate
+ implements ChangeDataSource {
private final Arguments args;
private final Account.Id accountId;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/HasDraftByPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/HasDraftByPredicate.java
index c18e19c..244589c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/HasDraftByPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/HasDraftByPredicate.java
@@ -15,11 +15,10 @@
package com.google.gerrit.server.query.change;
import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.server.index.IndexPredicate;
import com.google.gerrit.server.index.change.ChangeField;
import com.google.gwtorm.server.OrmException;
-class HasDraftByPredicate extends IndexPredicate<ChangeData> {
+class HasDraftByPredicate extends ChangeIndexPredicate {
private final Account.Id accountId;
HasDraftByPredicate(Account.Id accountId) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/HasStarsPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/HasStarsPredicate.java
index 83990bc..eb3a137 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/HasStarsPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/HasStarsPredicate.java
@@ -15,11 +15,10 @@
package com.google.gerrit.server.query.change;
import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.server.index.IndexPredicate;
import com.google.gerrit.server.index.change.ChangeField;
import com.google.gwtorm.server.OrmException;
-public class HasStarsPredicate extends IndexPredicate<ChangeData> {
+public class HasStarsPredicate extends ChangeIndexPredicate {
private final Account.Id accountId;
HasStarsPredicate(Account.Id accountId) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/HashtagPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/HashtagPredicate.java
index 3f952d3..185a539 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/HashtagPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/HashtagPredicate.java
@@ -15,11 +15,10 @@
package com.google.gerrit.server.query.change;
import com.google.gerrit.server.change.HashtagsUtil;
-import com.google.gerrit.server.index.IndexPredicate;
import com.google.gerrit.server.index.change.ChangeField;
import com.google.gwtorm.server.OrmException;
-class HashtagPredicate extends IndexPredicate<ChangeData> {
+class HashtagPredicate extends ChangeIndexPredicate {
HashtagPredicate(String hashtag) {
super(ChangeField.HASHTAG, HashtagsUtil.cleanupHashtag(hashtag));
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/IntegerRangeChangePredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/IntegerRangeChangePredicate.java
new file mode 100644
index 0000000..a272fbb
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/IntegerRangeChangePredicate.java
@@ -0,0 +1,34 @@
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.query.change;
+
+import com.google.gerrit.server.index.FieldDef;
+import com.google.gerrit.server.index.IntegerRangePredicate;
+import com.google.gerrit.server.query.Matchable;
+import com.google.gerrit.server.query.QueryParseException;
+
+public abstract class IntegerRangeChangePredicate
+ extends IntegerRangePredicate<ChangeData> implements Matchable<ChangeData> {
+
+ protected IntegerRangeChangePredicate(FieldDef<ChangeData, Integer> type,
+ String value) throws QueryParseException {
+ super(type, value);
+ }
+
+ @Override
+ public int getCost() {
+ return 1;
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/IsMergePredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/IsMergePredicate.java
index 3c02bab..376ad84 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/IsMergePredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/IsMergePredicate.java
@@ -15,7 +15,6 @@
package com.google.gerrit.server.query.change;
import com.google.gerrit.server.git.CodeReviewCommit;
-import com.google.gerrit.server.query.OperatorPredicate;
import com.google.gerrit.server.query.change.ChangeQueryBuilder.Arguments;
import com.google.gwtorm.server.OrmException;
@@ -26,7 +25,7 @@
import java.io.IOException;
-public class IsMergePredicate extends OperatorPredicate<ChangeData> {
+public class IsMergePredicate extends ChangeOperatorPredicate {
private final Arguments args;
public IsMergePredicate(Arguments args, String value) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/IsMergeablePredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/IsMergeablePredicate.java
index 5dd7dd2..d998fa3 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/IsMergeablePredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/IsMergeablePredicate.java
@@ -15,11 +15,10 @@
package com.google.gerrit.server.query.change;
import com.google.gerrit.server.index.FieldDef.FillArgs;
-import com.google.gerrit.server.index.IndexPredicate;
import com.google.gerrit.server.index.change.ChangeField;
import com.google.gwtorm.server.OrmException;
-class IsMergeablePredicate extends IndexPredicate<ChangeData> {
+class IsMergeablePredicate extends ChangeIndexPredicate {
private final FillArgs args;
IsMergeablePredicate(FillArgs args) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/IsReviewedPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/IsReviewedPredicate.java
index 604f84b..24fcd6b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/IsReviewedPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/IsReviewedPredicate.java
@@ -17,7 +17,6 @@
import static com.google.gerrit.server.index.change.ChangeField.REVIEWEDBY;
import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.server.index.IndexPredicate;
import com.google.gerrit.server.index.change.ChangeField;
import com.google.gerrit.server.query.Predicate;
import com.google.gwtorm.server.OrmException;
@@ -27,7 +26,7 @@
import java.util.List;
import java.util.Set;
-class IsReviewedPredicate extends IndexPredicate<ChangeData> {
+class IsReviewedPredicate extends ChangeIndexPredicate {
private static final Account.Id NOT_REVIEWED =
new Account.Id(ChangeField.NOT_REVIEWED);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/IsStarredByPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/IsStarredByPredicate.java
index 634bc4a6..929ed18 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/IsStarredByPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/IsStarredByPredicate.java
@@ -15,12 +15,11 @@
package com.google.gerrit.server.query.change;
import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.server.index.IndexPredicate;
import com.google.gerrit.server.index.change.ChangeField;
import com.google.gwtorm.server.OrmException;
@Deprecated
-class IsStarredByPredicate extends IndexPredicate<ChangeData> {
+class IsStarredByPredicate extends ChangeIndexPredicate {
private final Account.Id accountId;
IsStarredByPredicate(Account.Id accountId) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/LegacyChangeIdPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/LegacyChangeIdPredicate.java
index 656eca0..425eb00 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/LegacyChangeIdPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/LegacyChangeIdPredicate.java
@@ -17,10 +17,9 @@
import static com.google.gerrit.server.index.change.ChangeField.LEGACY_ID;
import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.server.index.IndexPredicate;
/** Predicate over change number (aka legacy ID or Change.Id). */
-public class LegacyChangeIdPredicate extends IndexPredicate<ChangeData> {
+public class LegacyChangeIdPredicate extends ChangeIndexPredicate {
private final Change.Id id;
LegacyChangeIdPredicate(Change.Id id) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/LegacyReviewerPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/LegacyReviewerPredicate.java
index db10670..cd93ed3 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/LegacyReviewerPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/LegacyReviewerPredicate.java
@@ -15,12 +15,11 @@
package com.google.gerrit.server.query.change;
import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.server.index.IndexPredicate;
import com.google.gerrit.server.index.change.ChangeField;
import com.google.gwtorm.server.OrmException;
@Deprecated
-class LegacyReviewerPredicate extends IndexPredicate<ChangeData> {
+class LegacyReviewerPredicate extends ChangeIndexPredicate {
private final Account.Id id;
LegacyReviewerPredicate(Account.Id id) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/MessagePredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/MessagePredicate.java
index 021b6d7..722a8ad 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/MessagePredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/MessagePredicate.java
@@ -14,7 +14,6 @@
package com.google.gerrit.server.query.change;
-import com.google.gerrit.server.index.IndexPredicate;
import com.google.gerrit.server.index.change.ChangeField;
import com.google.gerrit.server.index.change.ChangeIndex;
import com.google.gerrit.server.index.change.IndexedChangeQuery;
@@ -26,7 +25,7 @@
* Predicate to match changes that contains specified text in commit messages
* body.
*/
-class MessagePredicate extends IndexPredicate<ChangeData> {
+class MessagePredicate extends ChangeIndexPredicate {
private final ChangeIndex index;
MessagePredicate(ChangeIndex index, String value) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/OwnerPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/OwnerPredicate.java
index bb7cb403a..dfaac08 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/OwnerPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/OwnerPredicate.java
@@ -16,11 +16,10 @@
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.server.index.IndexPredicate;
import com.google.gerrit.server.index.change.ChangeField;
import com.google.gwtorm.server.OrmException;
-class OwnerPredicate extends IndexPredicate<ChangeData> {
+class OwnerPredicate extends ChangeIndexPredicate {
private final Account.Id id;
OwnerPredicate(Account.Id id) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/OwnerinPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/OwnerinPredicate.java
index 467e4c5..72327ba 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/OwnerinPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/OwnerinPredicate.java
@@ -17,10 +17,9 @@
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.query.OperatorPredicate;
import com.google.gwtorm.server.OrmException;
-class OwnerinPredicate extends OperatorPredicate<ChangeData> {
+class OwnerinPredicate extends ChangeOperatorPredicate {
private final IdentifiedUser.GenericFactory userFactory;
private final AccountGroup.UUID uuid;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ProjectPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ProjectPredicate.java
index 0bb5650..644870d 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ProjectPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ProjectPredicate.java
@@ -16,11 +16,10 @@
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.server.index.IndexPredicate;
import com.google.gerrit.server.index.change.ChangeField;
import com.google.gwtorm.server.OrmException;
-class ProjectPredicate extends IndexPredicate<ChangeData> {
+class ProjectPredicate extends ChangeIndexPredicate {
ProjectPredicate(String id) {
super(ChangeField.PROJECT, id);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ProjectPrefixPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ProjectPrefixPredicate.java
index 400a204..4c06d1b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ProjectPrefixPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ProjectPrefixPredicate.java
@@ -15,11 +15,10 @@
package com.google.gerrit.server.query.change;
import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.server.index.IndexPredicate;
import com.google.gerrit.server.index.change.ChangeField;
import com.google.gwtorm.server.OrmException;
-class ProjectPrefixPredicate extends IndexPredicate<ChangeData> {
+class ProjectPrefixPredicate extends ChangeIndexPredicate {
ProjectPrefixPredicate(String prefix) {
super(ChangeField.PROJECTS, prefix);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/RefPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/RefPredicate.java
index e62855f..491aed9 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/RefPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/RefPredicate.java
@@ -15,11 +15,10 @@
package com.google.gerrit.server.query.change;
import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.server.index.IndexPredicate;
import com.google.gerrit.server.index.change.ChangeField;
import com.google.gwtorm.server.OrmException;
-class RefPredicate extends IndexPredicate<ChangeData> {
+class RefPredicate extends ChangeIndexPredicate {
RefPredicate(String ref) {
super(ChangeField.REF, ref);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/RegexPathPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/RegexPathPredicate.java
index d1e0f02..67efd69 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/RegexPathPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/RegexPathPredicate.java
@@ -14,14 +14,13 @@
package com.google.gerrit.server.query.change;
-import com.google.gerrit.server.index.RegexPredicate;
import com.google.gerrit.server.index.change.ChangeField;
import com.google.gerrit.server.util.RegexListSearcher;
import com.google.gwtorm.server.OrmException;
import java.util.List;
-class RegexPathPredicate extends RegexPredicate<ChangeData> {
+class RegexPathPredicate extends ChangeRegexPredicate {
RegexPathPredicate(String re) {
super(ChangeField.PATH, re);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/RegexProjectPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/RegexProjectPredicate.java
index 48c815f..007566e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/RegexProjectPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/RegexProjectPredicate.java
@@ -16,14 +16,13 @@
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.server.index.RegexPredicate;
import com.google.gerrit.server.index.change.ChangeField;
import com.google.gwtorm.server.OrmException;
import dk.brics.automaton.RegExp;
import dk.brics.automaton.RunAutomaton;
-class RegexProjectPredicate extends RegexPredicate<ChangeData> {
+class RegexProjectPredicate extends ChangeRegexPredicate {
private final RunAutomaton pattern;
RegexProjectPredicate(String re) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/RegexRefPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/RegexRefPredicate.java
index 00c1dfe..c6d1577 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/RegexRefPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/RegexRefPredicate.java
@@ -15,14 +15,13 @@
package com.google.gerrit.server.query.change;
import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.server.index.RegexPredicate;
import com.google.gerrit.server.index.change.ChangeField;
import com.google.gwtorm.server.OrmException;
import dk.brics.automaton.RegExp;
import dk.brics.automaton.RunAutomaton;
-class RegexRefPredicate extends RegexPredicate<ChangeData> {
+class RegexRefPredicate extends ChangeRegexPredicate {
private final RunAutomaton pattern;
RegexRefPredicate(String re) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/RegexTopicPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/RegexTopicPredicate.java
index d51aaa4..2d65670 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/RegexTopicPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/RegexTopicPredicate.java
@@ -17,13 +17,12 @@
import static com.google.gerrit.server.index.change.ChangeField.FUZZY_TOPIC;
import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.server.index.RegexPredicate;
import com.google.gwtorm.server.OrmException;
import dk.brics.automaton.RegExp;
import dk.brics.automaton.RunAutomaton;
-class RegexTopicPredicate extends RegexPredicate<ChangeData> {
+class RegexTopicPredicate extends ChangeRegexPredicate {
private final RunAutomaton pattern;
RegexTopicPredicate(String re) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ReviewerPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ReviewerPredicate.java
index d655a5f..1c4fbbb 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ReviewerPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ReviewerPredicate.java
@@ -16,7 +16,6 @@
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.server.index.IndexPredicate;
import com.google.gerrit.server.index.change.ChangeField;
import com.google.gerrit.server.notedb.ReviewerStateInternal;
import com.google.gerrit.server.query.Predicate;
@@ -26,7 +25,7 @@
import java.util.ArrayList;
import java.util.List;
-class ReviewerPredicate extends IndexPredicate<ChangeData> {
+class ReviewerPredicate extends ChangeIndexPredicate {
@SuppressWarnings("deprecation")
static Predicate<ChangeData> create(Arguments args, Account.Id id) {
List<Predicate<ChangeData>> and = new ArrayList<>(2);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ReviewerinPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ReviewerinPredicate.java
index 76a02432..34c10e3 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ReviewerinPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ReviewerinPredicate.java
@@ -17,10 +17,9 @@
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.query.OperatorPredicate;
import com.google.gwtorm.server.OrmException;
-class ReviewerinPredicate extends OperatorPredicate<ChangeData> {
+class ReviewerinPredicate extends ChangeOperatorPredicate {
private final IdentifiedUser.GenericFactory userFactory;
private final AccountGroup.UUID uuid;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/StarPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/StarPredicate.java
index 2facdb7..a31254f 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/StarPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/StarPredicate.java
@@ -16,11 +16,10 @@
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.StarredChangesUtil;
-import com.google.gerrit.server.index.IndexPredicate;
import com.google.gerrit.server.index.change.ChangeField;
import com.google.gwtorm.server.OrmException;
-public class StarPredicate extends IndexPredicate<ChangeData> {
+public class StarPredicate extends ChangeIndexPredicate {
private final Account.Id accountId;
private final String label;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/SubmissionIdPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/SubmissionIdPredicate.java
index 35a5a29..d8d5258 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/SubmissionIdPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/SubmissionIdPredicate.java
@@ -15,11 +15,10 @@
package com.google.gerrit.server.query.change;
import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.server.index.IndexPredicate;
import com.google.gerrit.server.index.change.ChangeField;
import com.google.gwtorm.server.OrmException;
-class SubmissionIdPredicate extends IndexPredicate<ChangeData> {
+class SubmissionIdPredicate extends ChangeIndexPredicate {
SubmissionIdPredicate(String changeSet) {
super(ChangeField.SUBMISSIONID, changeSet);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/TimestampRangeChangePredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/TimestampRangeChangePredicate.java
new file mode 100644
index 0000000..9242d9d
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/TimestampRangeChangePredicate.java
@@ -0,0 +1,34 @@
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.query.change;
+
+import com.google.gerrit.server.index.FieldDef;
+import com.google.gerrit.server.index.TimestampRangePredicate;
+import com.google.gerrit.server.query.Matchable;
+
+import java.sql.Timestamp;
+
+public abstract class TimestampRangeChangePredicate extends
+ TimestampRangePredicate<ChangeData> implements Matchable<ChangeData> {
+ protected TimestampRangeChangePredicate(FieldDef<ChangeData, Timestamp> def,
+ String name, String value) {
+ super(def, name, value);
+ }
+
+ @Override
+ public int getCost() {
+ return 1;
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/TrackingIdPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/TrackingIdPredicate.java
index aed8831..e9be4cd 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/TrackingIdPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/TrackingIdPredicate.java
@@ -16,7 +16,6 @@
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.config.TrackingFooters;
-import com.google.gerrit.server.index.IndexPredicate;
import com.google.gerrit.server.index.change.ChangeField;
import com.google.gwtorm.server.OrmException;
@@ -27,7 +26,7 @@
import java.io.IOException;
import java.util.List;
-class TrackingIdPredicate extends IndexPredicate<ChangeData> {
+class TrackingIdPredicate extends ChangeIndexPredicate {
private static final Logger log = LoggerFactory.getLogger(TrackingIdPredicate.class);
private final TrackingFooters trackingFooters;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/NotesMigrationSchemaFactory.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/NotesMigrationSchemaFactory.java
index 6f39a12..f38ddfa 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/NotesMigrationSchemaFactory.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/NotesMigrationSchemaFactory.java
@@ -14,6 +14,7 @@
package com.google.gerrit.server.schema;
+import com.google.gerrit.reviewdb.server.DisabledChangesReviewDbWrapper;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.notedb.NotesMigration;
import com.google.gwtorm.server.OrmException;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaUpdater.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaUpdater.java
index cc3dc5d..0b7e8b0 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaUpdater.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaUpdater.java
@@ -17,6 +17,7 @@
import com.google.gerrit.reviewdb.client.CurrentSchemaVersion;
import com.google.gerrit.reviewdb.client.SystemConfig;
import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.reviewdb.server.ReviewDbUtil;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.config.AllUsersName;
@@ -92,7 +93,7 @@
}
public void update(final UpdateUI ui) throws OrmException {
- try (ReviewDb db = unwrap(schema.open())) {
+ try (ReviewDb db = ReviewDbUtil.unwrapDb(schema.open())) {
final SchemaVersion u = updater.get();
final CurrentSchemaVersion version = getSchemaVersion(db);
@@ -115,13 +116,6 @@
}
}
- private static ReviewDb unwrap(ReviewDb db) {
- if (db instanceof DisabledChangesReviewDbWrapper) {
- db = ((DisabledChangesReviewDbWrapper) db).unsafeGetDelegate();
- }
- return db;
- }
-
private CurrentSchemaVersion getSchemaVersion(final ReviewDb db) {
try {
return db.schemaVersion().get(new CurrentSchemaVersion.Key());
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/index/change/FakeQueryBuilder.java b/gerrit-server/src/test/java/com/google/gerrit/server/index/change/FakeQueryBuilder.java
index 5316bcb..0b5ed32 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/index/change/FakeQueryBuilder.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/index/change/FakeQueryBuilder.java
@@ -18,7 +18,6 @@
import com.google.gerrit.server.query.Predicate;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.ChangeQueryBuilder;
-import com.google.gwtorm.server.OrmException;
import org.junit.Ignore;
@@ -44,16 +43,6 @@
}
private Predicate<ChangeData> predicate(String name, String value) {
- return new OperatorPredicate<ChangeData>(name, value) {
- @Override
- public boolean match(ChangeData object) throws OrmException {
- return false;
- }
-
- @Override
- public int getCost() {
- return 0;
- }
- };
+ return new OperatorPredicate<ChangeData>(name, value) {};
}
}
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/mail/ValidatorTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/mail/ValidatorTest.java
new file mode 100644
index 0000000..6707c9f
--- /dev/null
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/mail/ValidatorTest.java
@@ -0,0 +1,61 @@
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.mail;
+
+import static com.google.common.truth.Truth.assert_;
+
+import org.apache.commons.validator.routines.EmailValidator;
+import org.junit.Test;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+
+public class ValidatorTest {
+ private static final String UNSUPPORTED_PREFIX = "#! ";
+
+ @Test
+ public void validateTopLevelDomains() throws Exception {
+ try (InputStream in =
+ this.getClass().getResourceAsStream("tlds-alpha-by-domain.txt")) {
+ if (in == null) {
+ throw new Exception("TLD list not found");
+ }
+ BufferedReader r = new BufferedReader(new InputStreamReader(in));
+ String tld;
+ EmailValidator validator = EmailValidator.getInstance();
+ while ((tld = r.readLine()) != null) {
+ if (tld.startsWith("# ") || tld.startsWith("XN--")) {
+ // Ignore comments and non-latin domains
+ continue;
+ }
+ if (tld.startsWith(UNSUPPORTED_PREFIX)) {
+ String test = "test@example."
+ + tld.toLowerCase().substring(UNSUPPORTED_PREFIX.length());
+ assert_()
+ .withFailureMessage("expected invalid TLD \"" + test + "\"")
+ .that(validator.isValid(test))
+ .isFalse();
+ } else {
+ String test = "test@example." + tld.toLowerCase();
+ assert_()
+ .withFailureMessage("failed to validate TLD \"" + test + "\"")
+ .that(validator.isValid(test))
+ .isTrue();
+ }
+ }
+ }
+ }
+}
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/query/PredicateTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/query/PredicateTest.java
index a0a6e40..7762e50 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/query/PredicateTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/query/PredicateTest.java
@@ -24,16 +24,6 @@
protected TestPredicate(String name, String value) {
super(name, value);
}
-
- @Override
- public boolean match(String object) {
- return false;
- }
-
- @Override
- public int getCost() {
- return 0;
- }
}
protected static TestPredicate f(String name, String value) {
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/query/account/AbstractQueryAccountsTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/query/account/AbstractQueryAccountsTest.java
new file mode 100644
index 0000000..355edaf
--- /dev/null
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/query/account/AbstractQueryAccountsTest.java
@@ -0,0 +1,427 @@
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.query.account;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.fail;
+
+import com.google.common.base.Function;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableList;
+import com.google.gerrit.extensions.api.GerritApi;
+import com.google.gerrit.extensions.api.accounts.Accounts.QueryRequest;
+import com.google.gerrit.extensions.client.ListAccountsOption;
+import com.google.gerrit.extensions.common.AccountInfo;
+import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.RestApiException;
+import com.google.gerrit.lifecycle.LifecycleManager;
+import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.account.AccountCache;
+import com.google.gerrit.server.account.AccountManager;
+import com.google.gerrit.server.account.AuthRequest;
+import com.google.gerrit.server.query.change.InternalChangeQuery;
+import com.google.gerrit.server.schema.SchemaCreator;
+import com.google.gerrit.server.util.RequestContext;
+import com.google.gerrit.server.util.ThreadLocalRequestContext;
+import com.google.gerrit.testutil.ConfigSuite;
+import com.google.gerrit.testutil.GerritServerTests;
+import com.google.gerrit.testutil.InMemoryDatabase;
+import com.google.inject.Inject;
+import com.google.inject.Injector;
+import com.google.inject.Provider;
+import com.google.inject.util.Providers;
+
+import org.eclipse.jgit.lib.Config;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestName;
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+@Ignore
+public abstract class AbstractQueryAccountsTest extends GerritServerTests {
+ @ConfigSuite.Default
+ public static Config defaultConfig() {
+ Config cfg = new Config();
+ cfg.setInt("index", null, "maxPages", 10);
+ return cfg;
+ }
+
+ @Rule
+ public final TestName testName = new TestName();
+
+ @Inject
+ protected AccountCache accountCache;
+
+ @Inject
+ protected AccountManager accountManager;
+
+ @Inject
+ protected GerritApi gApi;
+
+ @Inject
+ protected IdentifiedUser.GenericFactory userFactory;
+
+ @Inject
+ protected InMemoryDatabase schemaFactory;
+
+ @Inject
+ protected InternalChangeQuery internalChangeQuery;
+
+ @Inject
+ protected SchemaCreator schemaCreator;
+
+ @Inject
+ protected ThreadLocalRequestContext requestContext;
+
+ protected LifecycleManager lifecycle;
+ protected ReviewDb db;
+ protected AccountInfo currentUserInfo;
+ protected CurrentUser user;
+
+ protected abstract Injector createInjector();
+
+ @Before
+ public void setUpInjector() throws Exception {
+ lifecycle = new LifecycleManager();
+ Injector injector = createInjector();
+ lifecycle.add(injector);
+ injector.injectMembers(this);
+ lifecycle.start();
+
+ db = schemaFactory.open();
+ schemaCreator.create(db);
+
+ Account.Id userId = createAccount("user", "User", "user@example.com", true);
+ user = userFactory.create(userId);
+ requestContext.setContext(newRequestContext(userId));
+ currentUserInfo = gApi.accounts().id(userId.get()).get();
+ }
+
+ protected RequestContext newRequestContext(Account.Id requestUserId) {
+ final CurrentUser requestUser =
+ userFactory.create(requestUserId);
+ return new RequestContext() {
+ @Override
+ public CurrentUser getUser() {
+ return requestUser;
+ }
+
+ @Override
+ public Provider<ReviewDb> getReviewDbProvider() {
+ return Providers.of(db);
+ }
+ };
+ }
+
+ @After
+ public void tearDownInjector() {
+ if (lifecycle != null) {
+ lifecycle.stop();
+ }
+ requestContext.setContext(null);
+ if (db != null) {
+ db.close();
+ }
+ InMemoryDatabase.drop(schemaFactory);
+ }
+
+ @Test
+ public void byId() throws Exception {
+ AccountInfo user = newAccount("user");
+
+ assertQuery("9999999");
+ assertQuery(currentUserInfo._accountId, currentUserInfo);
+ assertQuery(user._accountId, user);
+ }
+
+ @Test
+ public void bySelf() throws Exception {
+ assertQuery("self", currentUserInfo);
+ }
+
+ @Test
+ public void byEmail() throws Exception {
+ AccountInfo user1 = newAccountWithEmail("user1", name("user1@example.com"));
+
+ String domain = name("test.com");
+ AccountInfo user2 = newAccountWithEmail("user2", "user2@" + domain);
+ AccountInfo user3 = newAccountWithEmail("user3", "user3@" + domain);
+
+ String prefix = name("prefix");
+ AccountInfo user4 =
+ newAccountWithEmail("user4", prefix + "user4@example.com");
+
+ AccountInfo user5 =
+ newAccountWithEmail("user5", name("user5MixedCase@example.com"));
+
+ assertQuery("notexisting@test.com");
+
+ assertQuery(currentUserInfo.email, currentUserInfo);
+ assertQuery("email:" + currentUserInfo.email, currentUserInfo);
+
+ assertQuery(user1.email, user1);
+ assertQuery("email:" + user1.email, user1);
+
+ assertQuery(domain, user2, user3);
+
+ assertQuery("email:" + prefix, user4);
+
+ assertQuery(user5.email, user5);
+ assertQuery("email:" + user5.email, user5);
+ assertQuery("email:" + user5.email.toUpperCase(), user5);
+ }
+
+ @Test
+ public void byUsername() throws Exception {
+ AccountInfo user1 = newAccount("myuser");
+
+ assertQuery("notexisting");
+ assertQuery("Not Existing");
+
+ assertQuery(user1.username, user1);
+ assertQuery("username:" + user1.username, user1);
+ assertQuery("username:" + user1.username.toUpperCase(), user1);
+ }
+
+ @Test
+ public void isActive() throws Exception {
+ String domain = name("test.com");
+ AccountInfo user1 = newAccountWithEmail("user1", "user1@" + domain);
+ AccountInfo user2 = newAccountWithEmail("user2", "user2@" + domain);
+ AccountInfo user3 = newAccount("user3", "user3@" + domain, false);
+ AccountInfo user4 = newAccount("user4", "user4@" + domain, false);
+
+ // by default only active accounts are returned
+ assertQuery(domain, user1, user2);
+ assertQuery("name:" + domain, user1, user2);
+
+ assertQuery("is:active name:" + domain, user1, user2);
+
+ assertQuery("is:inactive name:" + domain, user3, user4);
+ }
+
+ @Test
+ public void byName() throws Exception {
+ AccountInfo user1 = newAccountWithFullName("jdoe", "John Doe");
+ AccountInfo user2 = newAccountWithFullName("jroe", "Jane Roe");
+
+ assertQuery("notexisting");
+ assertQuery("Not Existing");
+
+ assertQuery(quote(user1.name), user1);
+ assertQuery("name:" + quote(user1.name), user1);
+ assertQuery("John", user1);
+ assertQuery("john", user1);
+ assertQuery("Doe", user1);
+ assertQuery("doe", user1);
+ assertQuery("DOE", user1);
+ assertQuery("name:John", user1);
+ assertQuery("name:john", user1);
+ assertQuery("name:Doe", user1);
+ assertQuery("name:doe", user1);
+ assertQuery("name:DOE", user1);
+
+ assertQuery(quote(user2.name), user2);
+ assertQuery("name:" + quote(user2.name), user2);
+ }
+
+ @Test
+ public void withLimit() throws Exception {
+ String domain = name("test.com");
+ AccountInfo user1 = newAccountWithEmail("user1", "user1@" + domain);
+ AccountInfo user2 = newAccountWithEmail("user2", "user2@" + domain);
+ AccountInfo user3 = newAccountWithEmail("user3", "user3@" + domain);
+
+ List<AccountInfo> result = assertQuery(domain, user1, user2, user3);
+ assertThat(result.get(result.size() - 1)._moreAccounts).isNull();
+
+ result = assertQuery(newQuery(domain).withLimit(2), user1, user2);
+ assertThat(result.get(result.size() - 1)._moreAccounts).isTrue();
+ }
+
+ @Test
+ public void withStart() throws Exception {
+ String domain = name("test.com");
+ AccountInfo user1 = newAccountWithEmail("user1", "user1@" + domain);
+ AccountInfo user2 = newAccountWithEmail("user2", "user2@" + domain);
+ AccountInfo user3 = newAccountWithEmail("user3", "user3@" + domain);
+
+ assertQuery(domain, user1, user2, user3);
+ assertQuery(newQuery(domain).withStart(1), user2, user3);
+ }
+
+ @Test
+ public void withDetails() throws Exception {
+ AccountInfo user1 =
+ newAccount("myuser", "My User", "my.user@example.com", true);
+
+ List<AccountInfo> result = assertQuery(user1.username, user1);
+ AccountInfo ai = result.get(0);
+ assertThat(ai._accountId).isEqualTo(user1._accountId);
+ assertThat(ai.name).isNull();
+ assertThat(ai.username).isNull();
+ assertThat(ai.email).isNull();
+ assertThat(ai.avatars).isNull();
+
+ result = assertQuery(
+ newQuery(user1.username).withOption(ListAccountsOption.DETAILS), user1);
+ ai = result.get(0);
+ assertThat(ai._accountId).isEqualTo(user1._accountId);
+ assertThat(ai.name).isEqualTo(user1.name);
+ assertThat(ai.username).isEqualTo(user1.username);
+ assertThat(ai.email).isEqualTo(user1.email);
+ assertThat(ai.avatars).isNull();
+ }
+
+ protected AccountInfo newAccount(String username) throws Exception {
+ return newAccountWithEmail(username, null);
+ }
+
+ protected AccountInfo newAccountWithEmail(String username, String email)
+ throws Exception {
+ return newAccount(username, email, true);
+ }
+
+ protected AccountInfo newAccountWithFullName(String username, String fullName)
+ throws Exception {
+ return newAccount(username, fullName, null, true);
+ }
+
+ protected AccountInfo newAccount(String username, String email,
+ boolean active) throws Exception {
+ return newAccount(username, null, email, active);
+ }
+
+ protected AccountInfo newAccount(String username, String fullName,
+ String email, boolean active) throws Exception {
+ String uniqueName = name(username);
+
+ try {
+ gApi.accounts().id(uniqueName).get();
+ fail("user " + uniqueName + " already exists");
+ } catch (ResourceNotFoundException e) {
+ // expected: user does not exist yet
+ }
+
+ Account.Id id = createAccount(uniqueName, fullName, email, active);
+ return gApi.accounts().id(id.get()).get();
+ }
+
+ protected String quote(String s) {
+ return "\"" + s + "\"";
+ }
+
+ protected String name(String name) {
+ if (name == null) {
+ return null;
+ }
+ String suffix = testName.getMethodName().toLowerCase();
+ if (name.contains("@")) {
+ return name + "." + suffix;
+ }
+ return name + "_" + suffix;
+ }
+
+ private Account.Id createAccount(String username, String fullName,
+ String email, boolean active) throws Exception {
+ Account.Id id =
+ accountManager.authenticate(AuthRequest.forUser(username)).getAccountId();
+ if (email != null) {
+ accountManager.link(id, AuthRequest.forEmail(email));
+ }
+ Account a = db.accounts().get(id);
+ a.setFullName(fullName);
+ a.setPreferredEmail(email);
+ a.setActive(active);
+ db.accounts().update(ImmutableList.of(a));
+ accountCache.evict(id);
+ return id;
+ }
+
+ protected QueryRequest newQuery(Object query) throws RestApiException {
+ return gApi.accounts().query(query.toString());
+ }
+
+ protected List<AccountInfo> assertQuery(Object query, AccountInfo... accounts)
+ throws Exception {
+ return assertQuery(newQuery(query), accounts);
+ }
+
+ protected List<AccountInfo> assertQuery(QueryRequest query, AccountInfo... accounts)
+ throws Exception {
+ List<AccountInfo> result = query.get();
+ Iterable<Integer> ids = ids(result);
+ assertThat(ids).named(format(query, result, accounts))
+ .containsExactlyElementsIn(ids(accounts)).inOrder();
+ return result;
+ }
+
+ private String format(QueryRequest query, Iterable<AccountInfo> actualIds,
+ AccountInfo... expectedAccounts) {
+ StringBuilder b = new StringBuilder();
+ b.append("query '").append(query.getQuery())
+ .append("' with expected accounts ");
+ b.append(format(Arrays.asList(expectedAccounts)));
+ b.append(" and result ");
+ b.append(format(actualIds));
+ return b.toString();
+ }
+
+ private String format(Iterable<AccountInfo> accounts) {
+ StringBuilder b = new StringBuilder();
+ b.append("[");
+ Iterator<AccountInfo> it = accounts.iterator();
+ while (it.hasNext()) {
+ AccountInfo a = it.next();
+ b.append("{").append(a._accountId).append(", ").append("name=")
+ .append(a.name).append(", ").append("email=").append(a.email)
+ .append(", ").append("username=").append(a.username).append("}");
+ if (it.hasNext()) {
+ b.append(", ");
+ }
+ }
+ b.append("]");
+ return b.toString();
+ }
+
+ protected static Iterable<Integer> ids(AccountInfo... accounts) {
+ return FluentIterable.from(Arrays.asList(accounts)).transform(
+ new Function<AccountInfo, Integer>() {
+ @Override
+ public Integer apply(AccountInfo in) {
+ return in._accountId;
+ }
+ });
+ }
+
+ protected static Iterable<Integer> ids(Iterable<AccountInfo> accounts) {
+ return FluentIterable.from(accounts).transform(
+ new Function<AccountInfo, Integer>() {
+ @Override
+ public Integer apply(AccountInfo in) {
+ return in._accountId;
+ }
+ });
+ }
+}
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/query/account/LuceneQueryAccountsTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/query/account/LuceneQueryAccountsTest.java
new file mode 100644
index 0000000..857b661
--- /dev/null
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/query/account/LuceneQueryAccountsTest.java
@@ -0,0 +1,31 @@
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.query.account;
+
+import com.google.gerrit.testutil.InMemoryModule;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+
+import org.eclipse.jgit.lib.Config;
+
+public class LuceneQueryAccountsTest extends AbstractQueryAccountsTest {
+ @Override
+ protected Injector createInjector() {
+ Config luceneConfig = new Config(config);
+ InMemoryModule.setDefaults(luceneConfig);
+ return Guice.createInjector(
+ new InMemoryModule(luceneConfig, notesMigration));
+ }
+}
diff --git a/gerrit-server/src/test/java/com/google/gerrit/testutil/NoteDbChecker.java b/gerrit-server/src/test/java/com/google/gerrit/testutil/NoteDbChecker.java
index 66eed8b..61bfe78 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/testutil/NoteDbChecker.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/testutil/NoteDbChecker.java
@@ -28,7 +28,6 @@
import com.google.gerrit.server.notedb.ChangeBundle;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.notedb.ChangeRebuilder;
-import com.google.gerrit.server.schema.DisabledChangesReviewDbWrapper;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -71,7 +70,7 @@
public void rebuildAndCheckAllChanges() throws Exception {
rebuildAndCheckChanges(
Iterables.transform(
- unwrapDb().changes().all(),
+ getUnwrappedDb().changes().all(),
ReviewDbUtil.changeIdFunction()));
}
@@ -81,7 +80,7 @@
public void rebuildAndCheckChanges(Iterable<Change.Id> changeIds)
throws Exception {
- ReviewDb db = unwrapDb();
+ ReviewDb db = getUnwrappedDb();
List<ChangeBundle> allExpected = readExpected(changeIds);
@@ -124,7 +123,7 @@
private List<ChangeBundle> readExpected(Iterable<Change.Id> changeIds)
throws Exception {
- ReviewDb db = unwrapDb();
+ ReviewDb db = getUnwrappedDb();
boolean old = notesMigration.readChanges();
try {
notesMigration.setReadChanges(false);
@@ -142,7 +141,7 @@
private void checkActual(List<ChangeBundle> allExpected, List<String> msgs)
throws Exception {
- ReviewDb db = unwrapDb();
+ ReviewDb db = getUnwrappedDb();
boolean oldRead = notesMigration.readChanges();
boolean oldWrite = notesMigration.writeChanges();
try {
@@ -179,11 +178,8 @@
}
}
- private ReviewDb unwrapDb() {
+ private ReviewDb getUnwrappedDb() {
ReviewDb db = dbProvider.get();
- if (db instanceof DisabledChangesReviewDbWrapper) {
- db = ((DisabledChangesReviewDbWrapper) db).unsafeGetDelegate();
- }
- return db;
+ return ReviewDbUtil.unwrapDb(db);
}
}
diff --git a/gerrit-server/src/test/resources/com/google/gerrit/server/mail/tlds-alpha-by-domain.txt b/gerrit-server/src/test/resources/com/google/gerrit/server/mail/tlds-alpha-by-domain.txt
new file mode 100644
index 0000000..9edf6a4
--- /dev/null
+++ b/gerrit-server/src/test/resources/com/google/gerrit/server/mail/tlds-alpha-by-domain.txt
@@ -0,0 +1,1329 @@
+# Version 2016060601, Last Updated Tue Jun 7 07:07:01 2016 UTC
+# From http://data.iana.org/TLD/tlds-alpha-by-domain.txt
+AAA
+AARP
+ABB
+ABBOTT
+ABBVIE
+ABOGADO
+ABUDHABI
+AC
+ACADEMY
+ACCENTURE
+ACCOUNTANT
+ACCOUNTANTS
+ACO
+ACTIVE
+ACTOR
+AD
+ADAC
+ADS
+ADULT
+AE
+AEG
+AERO
+#! AETNA
+AF
+AFL
+AG
+AGAKHAN
+AGENCY
+AI
+AIG
+AIRFORCE
+AIRTEL
+AKDN
+AL
+ALIBABA
+ALIPAY
+ALLFINANZ
+ALLY
+ALSACE
+AM
+AMICA
+AMSTERDAM
+ANALYTICS
+ANDROID
+ANQUAN
+AO
+APARTMENTS
+APP
+APPLE
+AQ
+AQUARELLE
+AR
+ARAMCO
+ARCHI
+ARMY
+ARPA
+ARTE
+AS
+ASIA
+ASSOCIATES
+AT
+ATTORNEY
+AU
+AUCTION
+AUDI
+AUDIO
+AUTHOR
+AUTO
+AUTOS
+AVIANCA
+AW
+AWS
+AX
+AXA
+AZ
+AZURE
+BA
+BABY
+BAIDU
+BAND
+BANK
+BAR
+BARCELONA
+BARCLAYCARD
+BARCLAYS
+BAREFOOT
+BARGAINS
+BAUHAUS
+BAYERN
+BB
+BBC
+BBVA
+BCG
+BCN
+BD
+BE
+BEATS
+BEER
+BENTLEY
+BERLIN
+BEST
+BET
+BF
+BG
+BH
+BHARTI
+BI
+BIBLE
+BID
+BIKE
+BING
+BINGO
+BIO
+BIZ
+BJ
+BLACK
+BLACKFRIDAY
+#! BLOG
+BLOOMBERG
+BLUE
+BM
+BMS
+BMW
+BN
+BNL
+BNPPARIBAS
+BO
+BOATS
+BOEHRINGER
+BOM
+BOND
+BOO
+BOOK
+BOOTS
+BOSCH
+BOSTIK
+BOT
+BOUTIQUE
+BR
+BRADESCO
+BRIDGESTONE
+BROADWAY
+BROKER
+BROTHER
+BRUSSELS
+BS
+BT
+BUDAPEST
+BUGATTI
+BUILD
+BUILDERS
+BUSINESS
+BUY
+BUZZ
+BV
+BW
+BY
+BZ
+BZH
+CA
+CAB
+CAFE
+CAL
+CALL
+CAMERA
+CAMP
+CANCERRESEARCH
+CANON
+CAPETOWN
+CAPITAL
+CAR
+CARAVAN
+CARDS
+CARE
+CAREER
+CAREERS
+CARS
+CARTIER
+CASA
+CASH
+CASINO
+CAT
+CATERING
+CBA
+CBN
+CC
+CD
+CEB
+CENTER
+CEO
+CERN
+CF
+CFA
+CFD
+CG
+CH
+CHANEL
+CHANNEL
+CHASE
+CHAT
+CHEAP
+CHLOE
+CHRISTMAS
+CHROME
+CHURCH
+CI
+CIPRIANI
+CIRCLE
+CISCO
+CITIC
+CITY
+CITYEATS
+CK
+CL
+CLAIMS
+CLEANING
+CLICK
+CLINIC
+CLINIQUE
+CLOTHING
+CLOUD
+CLUB
+CLUBMED
+CM
+CN
+CO
+COACH
+CODES
+COFFEE
+COLLEGE
+COLOGNE
+COM
+COMMBANK
+COMMUNITY
+COMPANY
+COMPARE
+COMPUTER
+COMSEC
+CONDOS
+CONSTRUCTION
+CONSULTING
+CONTACT
+CONTRACTORS
+COOKING
+COOL
+COOP
+CORSICA
+COUNTRY
+COUPON
+COUPONS
+COURSES
+CR
+CREDIT
+CREDITCARD
+CREDITUNION
+CRICKET
+CROWN
+CRS
+CRUISES
+CSC
+CU
+CUISINELLA
+CV
+CW
+CX
+CY
+CYMRU
+CYOU
+CZ
+DABUR
+DAD
+DANCE
+DATE
+DATING
+DATSUN
+DAY
+DCLK
+#! DDS
+DE
+DEALER
+DEALS
+DEGREE
+DELIVERY
+DELL
+DELOITTE
+DELTA
+DEMOCRAT
+DENTAL
+DENTIST
+DESI
+DESIGN
+DEV
+#! DHL
+DIAMONDS
+DIET
+DIGITAL
+DIRECT
+DIRECTORY
+DISCOUNT
+DJ
+DK
+DM
+DNP
+DO
+DOCS
+DOG
+DOHA
+DOMAINS
+#! DOT
+DOWNLOAD
+DRIVE
+#! DTV
+DUBAI
+DURBAN
+DVAG
+DZ
+EARTH
+EAT
+EC
+EDEKA
+EDU
+EDUCATION
+EE
+EG
+EMAIL
+EMERCK
+ENERGY
+ENGINEER
+ENGINEERING
+ENTERPRISES
+EPSON
+EQUIPMENT
+ER
+ERNI
+ES
+ESQ
+ESTATE
+ET
+EU
+EUROVISION
+EUS
+EVENTS
+EVERBANK
+EXCHANGE
+EXPERT
+EXPOSED
+EXPRESS
+EXTRASPACE
+FAGE
+FAIL
+FAIRWINDS
+FAITH
+FAMILY
+FAN
+FANS
+FARM
+FASHION
+FAST
+FEEDBACK
+FERRERO
+FI
+FILM
+FINAL
+FINANCE
+FINANCIAL
+FIRESTONE
+FIRMDALE
+FISH
+FISHING
+FIT
+FITNESS
+FJ
+FK
+FLICKR
+FLIGHTS
+#! FLIR
+FLORIST
+FLOWERS
+FLSMIDTH
+FLY
+FM
+FO
+FOO
+FOOTBALL
+FORD
+FOREX
+FORSALE
+FORUM
+FOUNDATION
+FOX
+FR
+FRESENIUS
+FRL
+FROGANS
+FRONTIER
+FTR
+FUND
+FURNITURE
+FUTBOL
+FYI
+GA
+GAL
+GALLERY
+GALLO
+GALLUP
+GAME
+#! GAMES
+GARDEN
+GB
+GBIZ
+GD
+GDN
+GE
+GEA
+GENT
+GENTING
+GF
+GG
+GGEE
+GH
+GI
+GIFT
+GIFTS
+GIVES
+GIVING
+GL
+GLASS
+GLE
+GLOBAL
+GLOBO
+GM
+GMAIL
+GMBH
+GMO
+GMX
+GN
+GOLD
+GOLDPOINT
+GOLF
+GOO
+GOOG
+GOOGLE
+GOP
+GOT
+GOV
+GP
+GQ
+GR
+GRAINGER
+GRAPHICS
+GRATIS
+GREEN
+GRIPE
+GROUP
+GS
+GT
+GU
+#! GUARDIAN
+GUCCI
+GUGE
+GUIDE
+GUITARS
+GURU
+GW
+GY
+HAMBURG
+HANGOUT
+HAUS
+HDFCBANK
+HEALTH
+HEALTHCARE
+HELP
+HELSINKI
+HERE
+HERMES
+HIPHOP
+#! HISAMITSU
+HITACHI
+HIV
+HK
+#! HKT
+HM
+HN
+HOCKEY
+HOLDINGS
+HOLIDAY
+HOMEDEPOT
+HOMES
+HONDA
+HORSE
+HOST
+HOSTING
+HOTELES
+HOTMAIL
+HOUSE
+HOW
+HR
+HSBC
+HT
+HTC
+HU
+HYUNDAI
+IBM
+ICBC
+ICE
+ICU
+ID
+IE
+IFM
+IINET
+IL
+IM
+IMAMAT
+IMMO
+IMMOBILIEN
+IN
+INDUSTRIES
+INFINITI
+INFO
+ING
+INK
+INSTITUTE
+INSURANCE
+INSURE
+INT
+INTERNATIONAL
+INVESTMENTS
+IO
+IPIRANGA
+IQ
+IR
+IRISH
+IS
+ISELECT
+ISMAILI
+IST
+ISTANBUL
+IT
+ITAU
+IWC
+JAGUAR
+JAVA
+JCB
+JCP
+JE
+JETZT
+JEWELRY
+JLC
+JLL
+JM
+JMP
+JNJ
+JO
+JOBS
+JOBURG
+JOT
+JOY
+JP
+JPMORGAN
+JPRS
+JUEGOS
+KAUFEN
+KDDI
+KE
+KERRYHOTELS
+KERRYLOGISTICS
+KERRYPROPERTIES
+KFH
+KG
+KH
+KI
+KIA
+KIM
+KINDER
+KITCHEN
+KIWI
+KM
+KN
+KOELN
+KOMATSU
+KP
+KPMG
+KPN
+KR
+KRD
+KRED
+KUOKGROUP
+KW
+KY
+KYOTO
+KZ
+LA
+LACAIXA
+LAMBORGHINI
+LAMER
+LANCASTER
+LAND
+LANDROVER
+LANXESS
+LASALLE
+LAT
+LATROBE
+LAW
+LAWYER
+LB
+LC
+LDS
+LEASE
+LECLERC
+LEGAL
+LEXUS
+LGBT
+LI
+LIAISON
+LIDL
+LIFE
+LIFEINSURANCE
+LIFESTYLE
+LIGHTING
+LIKE
+LIMITED
+LIMO
+LINCOLN
+LINDE
+LINK
+#! LIPSY
+LIVE
+LIVING
+LIXIL
+LK
+LOAN
+LOANS
+#! LOCKER
+LOCUS
+LOL
+LONDON
+LOTTE
+LOTTO
+LOVE
+LR
+LS
+LT
+LTD
+LTDA
+LU
+LUPIN
+LUXE
+LUXURY
+LV
+LY
+MA
+MADRID
+MAIF
+MAISON
+MAKEUP
+MAN
+MANAGEMENT
+MANGO
+MARKET
+MARKETING
+MARKETS
+MARRIOTT
+#! MATTEL
+MBA
+MC
+MD
+ME
+MED
+MEDIA
+MEET
+MELBOURNE
+MEME
+MEMORIAL
+MEN
+MENU
+MEO
+#! METLIFE
+MG
+MH
+MIAMI
+MICROSOFT
+MIL
+MINI
+MK
+ML
+#! MLB
+MLS
+MM
+MMA
+MN
+MO
+MOBI
+MOBILY
+MODA
+MOE
+MOI
+MOM
+MONASH
+MONEY
+MONTBLANC
+MORMON
+MORTGAGE
+MOSCOW
+MOTORCYCLES
+MOV
+MOVIE
+MOVISTAR
+MP
+MQ
+MR
+MS
+MT
+MTN
+MTPC
+MTR
+MU
+MUSEUM
+MUTUAL
+MUTUELLE
+MV
+MW
+MX
+MY
+MZ
+NA
+NADEX
+NAGOYA
+NAME
+NATURA
+NAVY
+NC
+NE
+NEC
+NET
+NETBANK
+#! NETFLIX
+NETWORK
+NEUSTAR
+NEW
+NEWS
+#! NEXT
+#! NEXTDIRECT
+NEXUS
+NF
+NG
+NGO
+NHK
+NI
+NICO
+NIKON
+NINJA
+NISSAN
+NISSAY
+NL
+NO
+NOKIA
+NORTHWESTERNMUTUAL
+NORTON
+NOWRUZ
+#! NOWTV
+NP
+NR
+NRA
+NRW
+NTT
+NU
+NYC
+NZ
+OBI
+OFFICE
+OKINAWA
+#! OLAYAN
+#! OLAYANGROUP
+#! OLLO
+OM
+OMEGA
+ONE
+ONG
+ONL
+ONLINE
+OOO
+ORACLE
+ORANGE
+ORG
+ORGANIC
+ORIGINS
+OSAKA
+OTSUKA
+#! OTT
+OVH
+PA
+PAGE
+PAMPEREDCHEF
+PANERAI
+PARIS
+PARS
+PARTNERS
+PARTS
+PARTY
+PASSAGENS
+#! PCCW
+PE
+PET
+PF
+PG
+PH
+PHARMACY
+PHILIPS
+PHOTO
+PHOTOGRAPHY
+PHOTOS
+PHYSIO
+PIAGET
+PICS
+PICTET
+PICTURES
+PID
+PIN
+PING
+PINK
+#! PIONEER
+PIZZA
+PK
+PL
+PLACE
+PLAY
+PLAYSTATION
+PLUMBING
+PLUS
+PM
+PN
+POHL
+POKER
+PORN
+POST
+PR
+PRAXI
+PRESS
+PRO
+PROD
+PRODUCTIONS
+PROF
+PROGRESSIVE
+PROMO
+PROPERTIES
+PROPERTY
+PROTECTION
+PS
+PT
+PUB
+PW
+PWC
+PY
+QA
+QPON
+QUEBEC
+QUEST
+RACING
+RE
+READ
+#! REALESTATE
+REALTOR
+REALTY
+RECIPES
+RED
+REDSTONE
+REDUMBRELLA
+REHAB
+REISE
+REISEN
+REIT
+REN
+RENT
+RENTALS
+REPAIR
+REPORT
+REPUBLICAN
+REST
+RESTAURANT
+REVIEW
+REVIEWS
+REXROTH
+RICH
+#! RICHARDLI
+RICOH
+RIO
+RIP
+RO
+ROCHER
+ROCKS
+RODEO
+ROOM
+RS
+RSVP
+RU
+RUHR
+RUN
+RW
+RWE
+RYUKYU
+SA
+SAARLAND
+SAFE
+SAFETY
+SAKURA
+SALE
+SALON
+SAMSUNG
+SANDVIK
+SANDVIKCOROMANT
+SANOFI
+SAP
+SAPO
+SARL
+SAS
+SAXO
+SB
+SBI
+SBS
+SC
+SCA
+SCB
+SCHAEFFLER
+SCHMIDT
+SCHOLARSHIPS
+SCHOOL
+SCHULE
+SCHWARZ
+SCIENCE
+SCOR
+SCOT
+SD
+SE
+SEAT
+SECURITY
+SEEK
+SELECT
+SENER
+SERVICES
+SEVEN
+SEW
+SEX
+SEXY
+SFR
+SG
+SH
+SHARP
+SHAW
+SHELL
+SHIA
+SHIKSHA
+SHOES
+#! SHOP
+SHOUJI
+SHOW
+SHRIRAM
+SI
+SINA
+SINGLES
+SITE
+SJ
+SK
+SKI
+SKIN
+SKY
+SKYPE
+SL
+SM
+SMILE
+SN
+SNCF
+SO
+SOCCER
+SOCIAL
+SOFTBANK
+SOFTWARE
+SOHU
+SOLAR
+SOLUTIONS
+SONG
+SONY
+SOY
+SPACE
+SPIEGEL
+SPOT
+SPREADBETTING
+SR
+SRL
+ST
+STADA
+STAR
+STARHUB
+STATEBANK
+STATEFARM
+STATOIL
+STC
+STCGROUP
+STOCKHOLM
+STORAGE
+STORE
+STREAM
+STUDIO
+STUDY
+STYLE
+SU
+SUCKS
+SUPPLIES
+SUPPLY
+SUPPORT
+SURF
+SURGERY
+SUZUKI
+SV
+SWATCH
+SWISS
+SX
+SY
+SYDNEY
+SYMANTEC
+SYSTEMS
+SZ
+TAB
+TAIPEI
+TALK
+TAOBAO
+TATAMOTORS
+TATAR
+TATTOO
+TAX
+TAXI
+TC
+TCI
+TD
+TEAM
+TECH
+TECHNOLOGY
+TEL
+TELECITY
+TELEFONICA
+TEMASEK
+TENNIS
+TEVA
+TF
+TG
+TH
+THD
+THEATER
+THEATRE
+TICKETS
+TIENDA
+TIFFANY
+TIPS
+TIRES
+TIROL
+TJ
+TK
+TL
+TM
+TMALL
+TN
+TO
+TODAY
+TOKYO
+TOOLS
+TOP
+TORAY
+TOSHIBA
+TOTAL
+TOURS
+TOWN
+TOYOTA
+TOYS
+TR
+TRADE
+TRADING
+TRAINING
+TRAVEL
+TRAVELERS
+TRAVELERSINSURANCE
+TRUST
+TRV
+TT
+TUBE
+TUI
+TUNES
+TUSHU
+TV
+TVS
+TW
+TZ
+UA
+UBS
+UG
+UK
+UNICOM
+UNIVERSITY
+UNO
+UOL
+#! UPS
+US
+UY
+UZ
+VA
+VACATIONS
+VANA
+VC
+VE
+VEGAS
+VENTURES
+VERISIGN
+VERSICHERUNG
+VET
+VG
+VI
+VIAJES
+VIDEO
+VIG
+VIKING
+VILLAS
+VIN
+VIP
+VIRGIN
+VISION
+VISTA
+VISTAPRINT
+VIVA
+VLAANDEREN
+VN
+VODKA
+VOLKSWAGEN
+VOTE
+VOTING
+VOTO
+VOYAGE
+VU
+VUELOS
+WALES
+WALTER
+WANG
+WANGGOU
+#! WARMAN
+WATCH
+WATCHES
+WEATHER
+WEATHERCHANNEL
+WEBCAM
+WEBER
+WEBSITE
+WED
+WEDDING
+WEIBO
+WEIR
+WF
+WHOSWHO
+WIEN
+WIKI
+WILLIAMHILL
+WIN
+WINDOWS
+WINE
+WME
+WOLTERSKLUWER
+WORK
+WORKS
+WORLD
+WS
+WTC
+WTF
+XBOX
+XEROX
+XIHUAN
+XIN
+XN--11B4C3D
+XN--1CK2E1B
+XN--1QQW23A
+XN--30RR7Y
+XN--3BST00M
+XN--3DS443G
+XN--3E0B707E
+XN--3PXU8K
+XN--42C2D9A
+XN--45BRJ9C
+XN--45Q11C
+XN--4GBRIM
+XN--55QW42G
+XN--55QX5D
+XN--5TZM5G
+XN--6FRZ82G
+XN--6QQ986B3XL
+XN--80ADXHKS
+XN--80AO21A
+XN--80ASEHDB
+XN--80ASWG
+XN--8Y0A063A
+XN--90A3AC
+XN--90AIS
+XN--9DBQ2A
+XN--9ET52U
+XN--9KRT00A
+XN--B4W605FERD
+XN--BCK1B9A5DRE4C
+XN--C1AVG
+XN--C2BR7G
+XN--CCK2B3B
+XN--CG4BKI
+XN--CLCHC0EA0B2G2A9GCD
+XN--CZR694B
+XN--CZRS0T
+XN--CZRU2D
+XN--D1ACJ3B
+XN--D1ALF
+XN--E1A4C
+XN--ECKVDTC9D
+XN--EFVY88H
+XN--ESTV75G
+XN--FCT429K
+XN--FHBEI
+XN--FIQ228C5HS
+XN--FIQ64B
+XN--FIQS8S
+XN--FIQZ9S
+XN--FJQ720A
+XN--FLW351E
+XN--FPCRJ9C3D
+XN--FZC2C9E2C
+XN--FZYS8D69UVGM
+XN--G2XX48C
+XN--GCKR3F0F
+XN--GECRJ9C
+XN--H2BRJ9C
+XN--HXT814E
+XN--I1B6B1A6A2E
+XN--IMR513N
+XN--IO0A7I
+XN--J1AEF
+XN--J1AMH
+XN--J6W193G
+XN--JLQ61U9W7B
+XN--JVR189M
+XN--KCRX77D1X4A
+XN--KPRW13D
+XN--KPRY57D
+XN--KPU716F
+XN--KPUT3I
+XN--L1ACC
+XN--LGBBAT1AD8J
+XN--MGB9AWBF
+XN--MGBA3A3EJT
+XN--MGBA3A4F16A
+XN--MGBA7C0BBN0A
+XN--MGBAAM7A8H
+XN--MGBAB2BD
+XN--MGBAYH7GPA
+XN--MGBB9FBPOB
+XN--MGBBH1A71E
+XN--MGBC0A9AZCG
+XN--MGBCA7DZDO
+XN--MGBERP4A5D4AR
+XN--MGBPL2FH
+XN--MGBT3DHD
+XN--MGBTX2B
+XN--MGBX4CD0AB
+XN--MIX891F
+XN--MK1BU44C
+XN--MXTQ1M
+XN--NGBC5AZD
+XN--NGBE9E0A
+XN--NODE
+XN--NQV7F
+XN--NQV7FS00EMA
+XN--NYQY26A
+XN--O3CW4H
+XN--OGBPF8FL
+XN--P1ACF
+XN--P1AI
+XN--PBT977C
+XN--PGBS0DH
+XN--PSSY2U
+XN--Q9JYB4C
+XN--QCKA1PMC
+XN--QXAM
+XN--RHQV96G
+XN--ROVU88B
+XN--S9BRJ9C
+XN--SES554G
+XN--T60B56A
+XN--TCKWE
+XN--UNUP4Y
+XN--VERMGENSBERATER-CTB
+XN--VERMGENSBERATUNG-PWB
+XN--VHQUV
+XN--VUQ861B
+XN--W4R85EL8FHU5DNRA
+XN--W4RS40L
+XN--WGBH1C
+XN--WGBL6A
+XN--XHQ521B
+XN--XKC2AL3HYE2A
+XN--XKC2DL3A5EE0H
+XN--Y9A3AQ
+XN--YFRO4I67O
+XN--YGBI2AMMX
+XN--ZFR164B
+XPERIA
+XXX
+XYZ
+YACHTS
+YAHOO
+YAMAXUN
+YANDEX
+YE
+YODOBASHI
+YOGA
+YOKOHAMA
+YOU
+YOUTUBE
+YT
+YUN
+ZA
+#! ZAPPOS
+ZARA
+ZERO
+ZIP
+ZM
+ZONE
+ZUERICH
+ZW
diff --git a/lib/commons/BUCK b/lib/commons/BUCK
index f9bf064..7c27477 100644
--- a/lib/commons/BUCK
+++ b/lib/commons/BUCK
@@ -72,10 +72,15 @@
exclude = ['META-INF/LICENSE'],
)
+# When updating the version of commons-validator, also update the
+# list of supported TLDs in:
+# gerrit-server/src/test/resources/com/google/gerrit/server/mail/tlds-alpha-by-domain.txt
+# from:
+# http://data.iana.org/TLD/tlds-alpha-by-domain.txt
maven_jar(
name = 'validator',
- id = 'commons-validator:commons-validator:1.4.1',
- sha1 = '2231238e391057a53f92bde5bbc588622c1956c3',
+ id = 'commons-validator:commons-validator:1.5.1',
+ sha1 = '86d05a46e8f064b300657f751b5a98c62807e2a0',
license = 'Apache2.0',
)
diff --git a/plugins/cookbook-plugin b/plugins/cookbook-plugin
index 6eecd42..69b8f9f 160000
--- a/plugins/cookbook-plugin
+++ b/plugins/cookbook-plugin
@@ -1 +1 @@
-Subproject commit 6eecd42fd629c700409826273d9ed02499a1d12c
+Subproject commit 69b8f9f413ce83a71593a4068a3b8e81f684cbad
diff --git a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.html b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.html
index d2026f6..b741784 100644
--- a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.html
+++ b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.html
@@ -59,7 +59,7 @@
}
</style>
<div>
- <section hidden$="[[!_keyCount(actions)]]" hidden>
+ <section hidden$="[[!_actionCount(actions.*, _additionalActions.*)]]">
<div class="groupLabel">Change</div>
<template is="dom-repeat" items="[[_changeActionValues]]" as="action">
<gr-button title$="[[action.title]]"
@@ -72,7 +72,7 @@
on-tap="_handleActionTap"></gr-button>
</template>
</section>
- <section hidden$="[[!_keyCount(_revisionActions)]]" hidden>
+ <section hidden$="[[!_actionCount(_revisionActions.*, _additionalActions.*)]]">
<div class="groupLabel">Revision</div>
<template is="dom-repeat" items="[[_revisionActionValues]]" as="action">
<gr-button title$="[[action.title]]"
diff --git a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.js b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.js
index d195241..9783831 100644
--- a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.js
+++ b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.js
@@ -59,7 +59,10 @@
*/
properties: {
- actions: Object,
+ actions: {
+ type: Object,
+ value: function() { return {}; },
+ },
primaryActionKeys: {
type: Array,
value: function() {
@@ -77,7 +80,10 @@
type: Boolean,
value: true,
},
- _revisionActions: Object,
+ _revisionActions: {
+ type: Object,
+ value: function() { return {}; },
+ },
_revisionActionValues: {
type: Array,
computed: '_computeRevisionActionValues(_revisionActions.*, ' +
@@ -103,7 +109,7 @@
],
observers: [
- '_actionsChanged(actions, _revisionActions, _additionalActions)',
+ '_actionsChanged(actions.*, _revisionActions.*, _additionalActions.*)',
],
ready: function() {
@@ -129,7 +135,7 @@
}.bind(this));
},
- addActionButton: function(key, type, label) {
+ addActionButton: function(type, label) {
if (type !== ActionType.CHANGE && type !== ActionType.REVISION) {
throw Error('Invalid action type: ' + type);
}
@@ -137,39 +143,59 @@
enabled: true,
label: label,
__type: type,
- __key: ADDITIONAL_ACTION_KEY_PREFIX + key + Math.random().toString(36),
+ __key: ADDITIONAL_ACTION_KEY_PREFIX + Math.random().toString(36),
};
this.push('_additionalActions', action);
return action.__key;
},
removeActionButton: function(key) {
- var idx = -1;
- for (var i = 0; i < this._additionalActions.length; i++) {
- if (this._additionalActions[i].__key === key) {
- idx = i;
- break;
- }
- }
+ var idx = this._indexOfActionButtonWithKey(key);
if (idx === -1) {
- console.error('Could not find action button with key:', key);
+ return;
}
this.splice('_additionalActions', idx, 1);
},
+ setActionButtonProp: function(key, prop, value) {
+ this.set([
+ '_additionalActions',
+ this._indexOfActionButtonWithKey(key),
+ prop,
+ ], value);
+ },
+
+ _indexOfActionButtonWithKey: function(key) {
+ for (var i = 0; i < this._additionalActions.length; i++) {
+ if (this._additionalActions[i].__key === key) {
+ return i;
+ }
+ }
+ return -1;
+ },
+
_getRevisionActions: function() {
return this.$.restAPI.getChangeRevisionActions(this.changeNum,
this.patchNum);
},
- _keyCount: function(obj) {
- return Object.keys(obj).length;
+ _actionCount: function(actionsChangeRecord, additionalActionsChangeRecord) {
+ var additionalActions = (additionalActionsChangeRecord &&
+ additionalActionsChangeRecord.base) || [];
+ return this._keyCount(actionsChangeRecord) + additionalActions.length;
},
- _actionsChanged: function(actions, revisionActions, additionalActions) {
- this.hidden = this._keyCount(actions) === 0 &&
- this._keyCount(revisionActions) === 0 &&
- this._keyCount(additionalActions) === 0;
+ _keyCount: function(changeRecord) {
+ return Object.keys((changeRecord && changeRecord.base) || {}).length;
+ },
+
+ _actionsChanged: function(actionsChangeRecord, revisionActionsChangeRecord,
+ additionalActionsChangeRecord) {
+ var additionalActions = (additionalActionsChangeRecord &&
+ additionalActionsChangeRecord.base) || [];
+ this.hidden = this._keyCount(actionsChangeRecord) === 0 &&
+ this._keyCount(revisionActionsChangeRecord) === 0 &&
+ additionalActions.length === 0;
},
_getValuesFor: function(obj) {
diff --git a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.html b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.html
index fe04843..462b01b 100644
--- a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.html
+++ b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.html
@@ -203,8 +203,7 @@
test('custom actions', function(done) {
// Add a button with the same key as a server-based one to ensure
// collisions are taken care of.
- var key = element.addActionButton('submit', element.ActionType.REVISION,
- 'Bork!');
+ var key = element.addActionButton(element.ActionType.REVISION, 'Bork!');
element.addEventListener(key + '-tap', function(e) {
assert.equal(e.detail.node.getAttribute('data-action-key'), key);
element.removeActionButton(key);
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
index 6af4493..f19d528 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
@@ -93,6 +93,10 @@
Gerrit.RESTClientBehavior,
],
+ observers: [
+ '_labelsChanged(_change.labels.*)',
+ ],
+
ready: function() {
this._headerEl = this.$$('.header');
},
@@ -485,6 +489,13 @@
}
},
+ _labelsChanged: function(changeRecord) {
+ if (!changeRecord) { return; }
+ this.$.jsAPI.handleEvent(this.$.jsAPI.EventType.LABEL_CHANGE, {
+ change: this._change,
+ });
+ },
+
_openReplyDialog: function() {
this.$.replyOverlay.open().then(function() {
this.$.replyOverlay.setFocusStops(this.$.replyDialog.getFocusStops());
diff --git a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.html b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.html
index a364c79..556691a 100644
--- a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.html
+++ b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.html
@@ -19,6 +19,7 @@
<link rel="import" href="../../../bower_components/iron-selector/iron-selector.html">
<link rel="import" href="../../../behaviors/rest-client-behavior.html">
<link rel="import" href="../../shared/gr-button/gr-button.html">
+<link rel="import" href="../../shared/gr-js-api-interface/gr-js-api-interface.html">
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
<dom-module id="gr-reply-dialog">
@@ -154,6 +155,7 @@
on-tap="_cancelTapHandler">Cancel</gr-button>
</section>
</div>
+ <gr-js-api-interface id="jsAPI"></gr-js-api-interface>
<gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
</template>
<script src="gr-reply-dialog.js"></script>
diff --git a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.js b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.js
index a702899..0ee779e 100644
--- a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.js
+++ b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.js
@@ -59,6 +59,10 @@
}.bind(this));
},
+ ready: function() {
+ this.$.jsAPI.addElement(this.$.jsAPI.Element.REPLY_DIALOG, this);
+ },
+
focus: function() {
this.async(function() {
this.$.textarea.textarea.focus();
@@ -72,6 +76,48 @@
};
},
+ setLabelValue: function(label, value) {
+ var selectorEl = this.$$('iron-selector[data-label="' + label + '"]');
+ // The selector may not be present if it’s not at the latest patch set.
+ if (!selectorEl) { return; }
+ var item = selectorEl.$$('gr-button[data-value="' + value + '"]');
+ if (!item) { return; }
+ selectorEl.selectIndex(selectorEl.indexOf(item));
+ },
+
+ send: function() {
+ var obj = {
+ drafts: 'PUBLISH_ALL_REVISIONS',
+ labels: {},
+ };
+ for (var label in this.permittedLabels) {
+ if (!this.permittedLabels.hasOwnProperty(label)) { continue; }
+
+ var selectorEl = this.$$('iron-selector[data-label="' + label + '"]');
+
+ // The selector may not be present if it’s not at the latest patch set.
+ if (!selectorEl) { continue; }
+
+ var selectedVal = selectorEl.selectedItem.getAttribute('data-value');
+ selectedVal = parseInt(selectedVal, 10);
+ obj.labels[label] = selectedVal;
+ }
+ if (this.draft != null) {
+ obj.message = this.draft;
+ }
+ this.disabled = true;
+ return this._saveReview(obj).then(function(response) {
+ this.disabled = false;
+ if (!response.ok) { return response; }
+
+ this.draft = '';
+ this.fire('send', null, {bubbles: false});
+ }.bind(this)).catch(function(err) {
+ this.disabled = false;
+ throw err;
+ }.bind(this));
+ },
+
_computeShowLabels: function(patchNum, revisions) {
var num = parseInt(patchNum, 10);
for (var rev in revisions) {
@@ -147,34 +193,7 @@
_sendTapHandler: function(e) {
e.preventDefault();
- var obj = {
- drafts: 'PUBLISH_ALL_REVISIONS',
- labels: {},
- };
- for (var label in this.permittedLabels) {
- var selectorEl = this.$$('iron-selector[data-label="' + label + '"]');
-
- // The selector may not be present if it’s not at the latest patch set.
- if (!selectorEl) { continue; }
-
- var selectedVal = selectorEl.selectedItem.getAttribute('data-value');
- selectedVal = parseInt(selectedVal, 10);
- obj.labels[label] = selectedVal;
- }
- if (this.draft != null) {
- obj.message = this.draft;
- }
- this.disabled = true;
- this._saveReview(obj).then(function(response) {
- this.disabled = false;
- if (!response.ok) { return response; }
-
- this.draft = '';
- this.fire('send', null, {bubbles: false});
- }.bind(this)).catch(function(err) {
- this.disabled = false;
- throw err;
- }.bind(this));
+ this.send();
},
_saveReview: function(review) {
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.html b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.html
index 67aa0ba..832665b 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.html
@@ -100,6 +100,12 @@
return null;
},
+ getLineNumberByChild: function(node) {
+ var lineEl = this.getLineElByChild(node);
+ return lineEl ?
+ parseInt(lineEl.getAttribute('data-value'), 10) : null;
+ },
+
renderLineRange: function(startLine, endLine, opt_side) {
var groups =
this._builder.getGroupsByLineRange(startLine, endLine, opt_side);
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight.html b/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight.html
index c8ed041..bc3b23f 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight.html
@@ -20,7 +20,7 @@
<dom-module id="gr-diff-highlight">
<template>
<style>
- :host {
+ .contentWrapper ::content {
position: relative;
}
.contentWrapper ::content .range {
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight.js b/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight.js
index db092e6..1c3572d 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight.js
@@ -59,6 +59,11 @@
},
_enabledChanged: function() {
+ if (this.enabled) {
+ this.listen(document, 'selectionchange', '_handleSelectionChange');
+ } else {
+ this.unlisten(document, 'selectionchange', '_handleSelectionChange');
+ }
for (var eventName in this._enabledListeners) {
var methodName = this._enabledListeners[eventName];
if (this.enabled) {
@@ -88,6 +93,14 @@
}
},
+ _handleSelectionChange: function() {
+ // Can't use up or down events to handle selection started and/or ended in
+ // in comment threads or outside of diff.
+ // Debounce removeActionBox to give it a chance to react to click/tap.
+ this._removeActionBoxDebounced();
+ this.debounce('selectionChange', this._handleSelection, 200);
+ },
+
_handleRender: function() {
this._applyAllHighlights();
},
@@ -129,6 +142,111 @@
}, this);
},
+ /**
+ * Convert DOM Range selection to concrete numbers (line, column, side).
+ * Moves range end if it's not inside td.content.
+ * Returns null if selection end is not valid (outside of diff).
+ *
+ * @param {Node} node td.content child
+ * @param {number} offset offset within node
+ * @return {{
+ * node: Node,
+ * side: string,
+ * line: Number,
+ * column: Number
+ * }}
+ */
+ _normalizeSelectionSide: function(node, offset) {
+ var column;
+ if (!this.contains(node)) {
+ return;
+ }
+ var lineEl = this.diffBuilder.getLineElByChild(node);
+ if (!lineEl) {
+ return;
+ }
+ var side = this.diffBuilder.getSideByLineEl(lineEl);
+ if (!side) {
+ return;
+ }
+ var line = this.diffBuilder.getLineNumberByChild(lineEl);
+ if (!line) {
+ return;
+ }
+ var content = this.diffBuilder.getContentByLineEl(lineEl);
+ if (!content) {
+ return;
+ }
+ if (!content.contains(node)) {
+ node = content;
+ column = 0;
+ } else {
+ var thread = content.querySelector('gr-diff-comment-thread');
+ if (thread && thread.contains(node)) {
+ column = this._getLength(content);
+ node = content;
+ } else {
+ column = this._convertOffsetToColumn(node, offset);
+ }
+ }
+
+ return {
+ node: node,
+ side: side,
+ line: line,
+ column: column,
+ };
+ },
+
+ _handleSelection: function() {
+ var selection = window.getSelection();
+ if (selection.rangeCount != 1) {
+ return;
+ }
+ var range = selection.getRangeAt(0);
+ if (range.collapsed) {
+ return;
+ }
+ var start =
+ this._normalizeSelectionSide(range.startContainer, range.startOffset);
+ if (!start) {
+ return;
+ }
+ var end =
+ this._normalizeSelectionSide(range.endContainer, range.endOffset);
+ if (!end) {
+ return;
+ }
+ if (start.side !== end.side ||
+ end.line < start.line ||
+ (start.line === end.line && start.column === end.column)) {
+ return;
+ }
+
+ // TODO (viktard): Drop empty first and last lines from selection.
+
+ var actionBox = document.createElement('gr-selection-action-box');
+ Polymer.dom(this.root).appendChild(actionBox);
+ actionBox.range = {
+ startLine: start.line,
+ startChar: start.column,
+ endLine: end.line,
+ endChar: end.column,
+ };
+ actionBox.side = start.side;
+ if (start.line === end.line) {
+ actionBox.placeAbove(range);
+ } else if (start.node instanceof Text) {
+ actionBox.placeAbove(start.node.splitText(start.column));
+ start.node.parentElement.normalize(); // Undo splitText from above.
+ } else if (start.node.classList.contains('content') &&
+ start.node.firstChild) {
+ actionBox.placeAbove(start.node.firstChild);
+ } else {
+ actionBox.placeAbove(start.node);
+ }
+ },
+
_renderCommentRange: function(comment, el) {
var lineEl = this.diffBuilder.getLineElByChild(el);
if (!lineEl) {
@@ -181,6 +299,10 @@
range.endLine, range.endChar, side);
},
+ _removeActionBoxDebounced: function() {
+ this.debounce('removeActionBox', this._removeActionBox, 10);
+ },
+
_removeActionBox: function() {
var actionBox = this.$$('gr-selection-action-box');
if (actionBox) {
@@ -188,8 +310,24 @@
}
},
+ _convertOffsetToColumn: function(el, offset) {
+ if (el instanceof Element && el.classList.contains('content')) {
+ return offset;
+ }
+ while (el.previousSibling ||
+ !el.parentElement.classList.contains('content')) {
+ if (el.previousSibling) {
+ el = el.previousSibling;
+ offset += this._getLength(el);
+ } else {
+ el = el.parentElement;
+ }
+ }
+ return offset;
+ },
+
/**
- * Traverse diff content from right to left, call callback for each node.
+ * Traverse Element from right to left, call callback for each node.
* Stops if callback returns true.
*
* @param {!Node} startNode
@@ -200,7 +338,9 @@
var travelLeft = opt_flags && opt_flags.left;
var node = startNode;
while (node) {
- if (node instanceof Element && node.tagName !== 'HL') {
+ if (node instanceof Element &&
+ node.tagName !== 'HL' &&
+ node.tagName !== 'SPAN') {
break;
}
var nextNode = travelLeft ? node.previousSibling : node.nextSibling;
@@ -222,7 +362,6 @@
node = node.firstChild;
var length = 0;
while (node) {
- // Only measure Text nodes and <hl>
if (node instanceof Text || node.tagName == 'HL') {
length += this._getLength(node);
}
@@ -242,10 +381,16 @@
* @return {!Element} Wrapped node.
*/
_wrapInHighlight: function(node, cssClass) {
- var hl = document.createElement('hl');
- hl.className = cssClass;
- Polymer.dom(node.parentElement).replaceChild(hl, node);
- hl.appendChild(node);
+ var hl;
+ if (node.tagName === 'HL') {
+ hl = node;
+ hl.classList.add(cssClass);
+ } else {
+ hl = document.createElement('hl');
+ hl.className = cssClass;
+ Polymer.dom(node.parentElement).replaceChild(hl, node);
+ hl.appendChild(node);
+ }
return hl;
},
@@ -256,7 +401,7 @@
* @param {number} offset
* @return {!Text} Trailing Text Node.
*/
- _splitText: function(node, offset) {
+ _splitTextNode: function(node, offset) {
if (node.textContent.match(REGEX_ASTRAL_SYMBOL)) {
// DOM Api for splitText() is broken for Unicode:
// https://mathiasbynens.be/notes/javascript-unicode
@@ -275,10 +420,41 @@
},
/**
+ * Split Node at offset.
+ * If Node is Element, it's cloned and the node at offset is split too.
+ *
+ * @param {!Node} node
+ * @param {number} offset
+ * @return {!Node} Trailing Node.
+ */
+ _splitNode: function(element, offset) {
+ if (element instanceof Text) {
+ return this._splitTextNode(element, offset);
+ }
+ var tail = element.cloneNode(false);
+ element.parentElement.insertBefore(tail, element.nextSibling);
+ // Skip nodes before offset.
+ var node = element.firstChild;
+ while (node &&
+ this._getLength(node) <= offset ||
+ this._getLength(node) === 0) {
+ offset -= this._getLength(node);
+ node = node.nextSibling;
+ }
+ if (this._getLength(node) > offset) {
+ tail.appendChild(this._splitNode(node, offset));
+ }
+ while (node.nextSibling) {
+ tail.appendChild(node.nextSibling);
+ }
+ return tail;
+ },
+
+ /**
* Split Text Node and wrap it in hl with cssClass.
* Wraps trailing part after split, tailing one if opt_firstPart is true.
*
- * @param {!Text} node
+ * @param {!Node} node
* @param {number} offset
* @param {string} cssClass
* @param {boolean=} opt_firstPart
@@ -288,10 +464,10 @@
return this._wrapInHighlight(node, cssClass);
} else {
if (opt_firstPart) {
- this._splitText(node, offset);
+ this._splitNode(node, offset);
// Node points to first part of the Text, second one is sibling.
} else {
- node = this._splitText(node, offset);
+ node = this._splitNode(node, offset);
}
return this._wrapInHighlight(node, cssClass);
}
@@ -329,29 +505,21 @@
if (startNode instanceof Text) {
startNode =
this._splitAndWrapInHighlight(startNode, startOffset, cssClass);
- startContent.insertBefore(startNode, startNode.nextSibling);
// Edge case: single line, text node wraps the highlight.
if (isOneLine && this._getLength(startNode) > length) {
- var extra = this._splitText(startNode.firstChild, length);
+ var extra = this._splitTextNode(startNode.firstChild, length);
startContent.insertBefore(extra, startNode.nextSibling);
startContent.normalize();
}
} else if (startNode.tagName == 'HL') {
if (!startNode.classList.contains(cssClass)) {
- var hl = startNode;
- startNode = this._splitAndWrapInHighlight(
- startNode.firstChild, startOffset, cssClass);
- startContent.insertBefore(startNode, hl.nextSibling);
// Edge case: single line, <hl> wraps the highlight.
- if (isOneLine && this._getLength(startNode) > length) {
- var trailingHl = hl.cloneNode(false);
- trailingHl.appendChild(
- this._splitText(startNode.firstChild, length));
- startContent.insertBefore(trailingHl, startNode.nextSibling);
+ // Should leave wrapping HL's content after the highlight.
+ if (isOneLine && startOffset + length < this._getLength(startNode)) {
+ this._splitNode(startNode, startOffset + length);
}
- if (hl.textContent.length === 0) {
- hl.remove();
- }
+ startNode =
+ this._splitAndWrapInHighlight(startNode, startOffset, cssClass);
}
} else {
startNode = null;
@@ -393,8 +561,7 @@
// Split text inside HL.
var hl = endNode;
endNode = this._splitAndWrapInHighlight(
- endNode.firstChild, endOffset, cssClass, true);
- endContent.insertBefore(endNode, hl);
+ endNode, endOffset, cssClass, true);
if (hl.textContent.length === 0) {
hl.remove();
}
@@ -424,19 +591,32 @@
// Grow starting highlight until endNode or end of line.
if (startNode && startNode != endNode) {
- this._traverseContentSiblings(startNode.nextSibling, function(node) {
- startNode.textContent += node.textContent;
- node.remove();
+ var growStartHl = function(node) {
+ if (node instanceof Text || node.tagName === 'SPAN') {
+ startNode.appendChild(node);
+ } else if (node.tagName === 'HL') {
+ this._traverseContentSiblings(node.firstChild, growStartHl);
+ node.remove();
+ }
return node == endNode;
- });
+ }.bind(this);
+ this._traverseContentSiblings(startNode.nextSibling, growStartHl);
+ startNode.normalize();
}
if (!isOneLine && endNode) {
+ var growEndHl = function(node) {
+ if (node instanceof Text || node.tagName === 'SPAN') {
+ endNode.insertBefore(node, endNode.firstChild);
+ } else if (node.tagName === 'HL') {
+ this._traverseContentSiblings(node.firstChild, growEndHl);
+ node.remove();
+ }
+ }.bind(this);
// Prepend text up to line start to the ending highlight.
- this._traverseContentSiblings(endNode.previousSibling, function(node) {
- endNode.textContent = node.textContent + endNode.textContent;
- node.remove();
- }, {left: true});
+ this._traverseContentSiblings(
+ endNode.previousSibling, growEndHl, {left: true});
+ endNode.normalize();
}
},
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight_test.html b/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight_test.html
index 60bfb96..0df6a20 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight_test.html
@@ -31,38 +31,74 @@
<tbody class="section both">
<tr class="diff-row side-by-side" left-type="both" right-type="both">
- <td class="left lineNum" data-value="138"></td>
+ <td class="left lineNum" data-value="138">138</td>
<td class="content both darkHighlight">[14] Nam cum ad me in Cumanum salutandi causa uterque venisset,</td>
- <td class="right lineNum" data-value="119"></td>
+ <td class="right lineNum" data-value="119">119</td>
<td class="content both darkHighlight">[14] Nam cum ad me in Cumanum salutandi causa uterque venisset,</td>
</tr>
</tbody>
<tbody class="section delta">
<tr class="diff-row side-by-side" left-type="remove" right-type="add">
- <td class="left lineNum" data-value="140"></td>
+ <td class="left lineNum" data-value="140">140</td>
<!-- Next tag is formatted to eliminate zero-length text nodes. -->
- <td class="content remove lightHighlight">na💢ti <hl class="foo">te, inquit</hl>, sumus <hl class="bar">aliquando</hl> otiosum, <hl>certe</hl> a udiam, <hl>quid</hl> sit, quod <hl>Epicurum</hl><gr-diff-comment-thread>
+ <td class="content remove lightHighlight">na💢ti <hl class="foo">te, inquit</hl>, sumus <hl class="bar">aliquando</hl> otiosum, <hl>certe</hl> a <hl><span class="tab withIndicator" style="tab-size:8;"></span></hl>udiam, <hl>quid</hl> sit, <span class="tab withIndicator" style="tab-size:8;"></span>quod <hl>Epicurum</hl><gr-diff-comment-thread>
[Yet another random diff thread content here]
</gr-diff-comment-thread></td>
- <td class="right lineNum" data-value="121"></td>
- <td class="content add lightHighlight">
- nacti ,
- <hl>,</hl>
- sumus otiosum, audiam, sit, quod
- </td>
+ <td class="right lineNum" data-value="120">120</td>
+ <!-- Next tag is formatted to eliminate zero-length text nodes. -->
+ <td class="content add lightHighlight">nacti , <hl>,</hl> sumus <hl><span class="tab withIndicator" style="tab-size:8;"></span></hl> otiosum, <span class="tab withIndicator" style="tab-size:8;"></span> audiam, sit, quod</td>
</tr>
</tbody>
<tbody class="section both">
<tr class="diff-row side-by-side" left-type="both" right-type="both">
- <td class="left lineNum" data-value="149"></td>
- <td class="content both darkHighlight">nam et complectitur verbis, quod vult, et dicit plane, quod intellegam;</td>
+ <td class="left lineNum" data-value="141"></td>
+ <td class="content both darkHighlight">nam et<hl><span class="tab withIndicator" style="tab-size:8;"> </span></hl>complectitur<span class="tab withIndicator" style="tab-size:8;"> </span>verbis, quod vult, et dicit plane, quod intellegam;</td>
<td class="right lineNum" data-value="130"></td>
<td class="content both darkHighlight">nam et complectitur verbis, quod vult, et dicit plane, quod intellegam;</td>
</tr>
</tbody>
+ <tbody class="section contextControl">
+ <tr class="diff-row side-by-side" left-type="contextControl" right-type="contextControl">
+ <td class="left contextLineNum" data-value="@@"></td>
+ <td>
+ <gr-button>+10↑</gr-button>
+ -
+ <gr-button>Show 21 common lines</gr-button>
+ -
+ <gr-button>+10↓</gr-button>
+ </td>
+ <td class="right contextLineNum" data-value="@@"></td>
+ <td>
+ <gr-button>+10↑</gr-button>
+ -
+ <gr-button>Show 21 common lines</gr-button>
+ -
+ <gr-button>+10↓</gr-button>
+ </td>
+ </tr>
+ </tbody>
+
+ <tbody class="section delta">
+ <tr class="diff-row side-by-side" left-type="blank" right-type="add">
+ <td class="left"></td>
+ <td class="blank darkHighlight"></td>
+ <td class="right lineNum" data-value="146"></td>
+ <td class="content add darkHighlight">[17] Quid igitur est? inquit; audire enim cupio, quid non probes. Principio, inquam,</td>
+ </tr>
+ </tbody>
+
+ <tbody class="section both">
+ <tr class="diff-row side-by-side" left-type="both" right-type="both">
+ <td class="left lineNum" data-value="165"></td>
+ <td class="content both darkHighlight">in physicis, quibus maxime gloriatur, primum totus est alienus. Democritea dicit</td>
+ <td class="right lineNum" data-value="147"></td>
+ <td class="content both darkHighlight">in physicis, <hl><span class="tab withIndicator" style="tab-size:8;"> </span></hl> quibus maxime gloriatur, primum totus est alienus. Democritea dicit</td>
+ </tr>
+ </tbody>
+
</table>
</gr-diff-highlight>
</template>
@@ -116,6 +152,28 @@
}
});
+ test('does not listen to selectionchange when disabled', function() {
+ sandbox.stub(element, '_handleSelection');
+ sandbox.stub(element, '_removeActionBox');
+ element.enabled = false;
+ document.dispatchEvent(new CustomEvent('selectionchange'));
+ element.flushDebouncer('selectionChange');
+ assert.isFalse(element._handleSelection.called);
+ element.flushDebouncer('removeActionBox');
+ assert.isFalse(element._removeActionBox.called);
+ });
+
+ test('listens to selectionchange when enabled', function() {
+ sandbox.stub(element, '_handleSelection');
+ sandbox.stub(element, '_removeActionBox');
+ element.enabled = true;
+ document.dispatchEvent(new CustomEvent('selectionchange'));
+ element.flushDebouncer('selectionChange');
+ assert.isTrue(element._handleSelection.called);
+ element.flushDebouncer('removeActionBox');
+ assert.isTrue(element._removeActionBox.called);
+ });
+
suite('comment events', function() {
var builder;
@@ -162,7 +220,6 @@
});
});
-
test('renders lines in comment range on comment discard', function(done) {
element.fire('comment-discard', {
comment: {
@@ -254,10 +311,10 @@
var diff = element.querySelector('#diffTable');
var startContent =
diff.querySelector('.left.lineNum[data-value="138"] ~ .content');
- var endContent =
- diff.querySelector('.left.lineNum[data-value="149"] ~ .content');
var betweenContent =
diff.querySelector('.left.lineNum[data-value="140"] ~ .content');
+ var endContent =
+ diff.querySelector('.left.lineNum[data-value="141"] ~ .content');
var commentThread =
diff.querySelector('gr-diff-comment-thread');
var builder = {
@@ -271,9 +328,9 @@
element.enabled = true;
builder.getContentByLine.withArgs(138, 'left').returns(
startContent);
- builder.getContentByLine.withArgs(149, 'left').returns(
+ builder.getContentByLine.withArgs(141, 'left').returns(
endContent);
- element._applyRangedHighlight('some', 138, 4, 149, 8, 'left');
+ element._applyRangedHighlight('some', 138, 4, 141, 28, 'left');
assert.instanceOf(startContent.childNodes[0], Text);
assert.equal(startContent.childNodes[0].textContent, '[14]');
assert.instanceOf(startContent.childNodes[1], Element);
@@ -282,14 +339,6 @@
assert.equal(startContent.childNodes[1].tagName, 'HL');
assert.equal(startContent.childNodes[1].className, 'some');
- assert.instanceOf(endContent.childNodes[0], Element);
- assert.equal(endContent.childNodes[0].textContent, 'nam et c');
- assert.equal(endContent.childNodes[0].tagName, 'HL');
- assert.equal(endContent.childNodes[0].className, 'some');
- assert.instanceOf(endContent.childNodes[1], Text);
- assert.equal(endContent.childNodes[1].textContent,
- 'omplectitur verbis, quod vult, et dicit plane, quod intellegam;');
-
assert.instanceOf(betweenContent.firstChild, Element);
assert.equal(betweenContent.firstChild.tagName, 'HL');
assert.equal(betweenContent.firstChild.className, 'some');
@@ -304,6 +353,22 @@
assert.strictEqual(betweenContent.querySelector('gr-diff-comment-thread'),
commentThread, 'Comment threads should be preserved.');
+
+ assert.instanceOf(endContent.childNodes[0], Element);
+ assert.equal(endContent.childNodes[0].textContent,
+ 'nam et\tcomplectitur\tverbis, ');
+ assert.equal(endContent.childNodes[0].tagName, 'HL');
+ assert.equal(endContent.childNodes[0].className, 'some');
+ assert.instanceOf(endContent.childNodes[1], Text);
+ assert.equal(endContent.childNodes[1].textContent,
+ 'quod vult, et dicit plane, quod intellegam;');
+ var endHl = endContent.querySelector('hl.some');
+ assert.equal(endHl.childNodes.length, 5);
+ var tabs = endHl.querySelectorAll('span.tab');
+ assert.equal(tabs.length, 2);
+ assert.equal(tabs[0].previousSibling.textContent, 'nam et');
+ assert.equal(tabs[1].previousSibling.textContent, 'complectitur');
+ assert.equal(tabs[1].nextSibling.textContent, 'verbis, ');
});
suite('single line ranges', function() {
@@ -334,10 +399,18 @@
assert.equal(content.firstChild.tagName, 'HL');
assert.equal(content.firstChild.className, 'some');
assert.equal(content.childNodes.length, 2);
- assert.equal(content.firstChild.childNodes.length, 1);
+ assert.equal(content.firstChild.childNodes.length, 5);
assert.equal(content.firstChild.textContent,
'na💢ti te, inquit, sumus aliquando otiosum, certe a udiam, ' +
'quid sit, quod Epicurum');
+ var tabs = content.querySelectorAll('span.tab');
+ assert.equal(tabs.length, 2);
+ assert.strictEqual(tabs[1].previousSibling, tabs[0].nextSibling);
+ assert.equal(tabs[0].previousSibling.textContent,
+ 'na💢ti te, inquit, sumus aliquando otiosum, certe a ');
+ assert.equal(tabs[1].previousSibling.textContent,
+ 'udiam, quid sit, ');
+ assert.equal(tabs[1].nextSibling.textContent, 'quod Epicurum');
});
test('merging multiple other hls', function() {
@@ -346,7 +419,8 @@
assert.equal(content.childNodes.length, 4);
var hl = content.querySelector('hl.some');
assert.strictEqual(content.firstChild, hl.previousSibling);
- assert.equal(hl.childNodes.length, 1);
+ assert.equal(hl.childNodes.length, 5);
+ assert.equal(content.querySelectorAll('span.tab').length, 2);
assert.equal(hl.textContent,
'a💢ti te, inquit, sumus aliquando otiosum, certe a udiam, ' +
'quid sit, quod Epicuru');
@@ -375,7 +449,7 @@
// After: na💢ti <hl class="foo">te, in</hl><hl class="some">quit, ...
element._applyRangedHighlight('some', 140, 12, 140, 21, 'left');
var hl = content.querySelector('hl.some');
- assert.equal(hl.outerHTML, '<hl class="some">quit, sum</hl>');
+ assert.equal(hl.textContent, 'quit, sum');
assert.equal(
hl.previousSibling.outerHTML, '<hl class="foo">te, in</hl>');
});
@@ -385,7 +459,7 @@
// After: <hl class="foo">t</hl><hl="some">e, i</hl><hl class="foo">n..
element._applyRangedHighlight('some', 140, 7, 140, 12, 'left');
var hl = content.querySelector('hl.some');
- assert.equal(hl.outerHTML, '<hl class="some">e, in</hl>');
+ assert.equal(hl.textContent, 'e, in');
assert.equal(hl.previousSibling.outerHTML, '<hl class="foo">t</hl>');
assert.equal(hl.nextSibling.outerHTML, '<hl class="foo">quit</hl>');
});
@@ -393,7 +467,7 @@
test('hl starts and ends in different hls', function() {
element._applyRangedHighlight('some', 140, 8, 140, 27, 'left');
var hl = content.querySelector('hl.some');
- assert.equal(hl.outerHTML, '<hl class="some">, inquit, sumus ali</hl>');
+ assert.equal(hl.textContent, ', inquit, sumus ali');
assert.equal(hl.previousSibling.outerHTML, '<hl class="foo">te</hl>');
assert.equal(hl.nextSibling.outerHTML, '<hl class="bar">quando</hl>');
});
@@ -408,9 +482,7 @@
test('hl starting and ending in boundaries', function() {
element._applyRangedHighlight('some', 140, 6, 140, 33, 'left');
var hl = content.querySelector('hl.some');
- assert.equal(
- hl.outerHTML, '<hl class="some">te, inquit, sumus aliquando</hl>');
- assert.notOk(content.querySelector('.foo'));
+ assert.equal(hl.textContent, 'te, inquit, sumus aliquando');
assert.notOk(content.querySelector('.bar'));
});
@@ -422,7 +494,7 @@
assert.equal(hl.outerHTML, '<hl class="some">a💢t</hl>');
});
- test('growing hl left including another hl', function() {
+ test('growing hl right including another hl', function() {
element._applyRangedHighlight('some', 140, 1, 140, 4, 'left');
element._applyRangedHighlight('some', 140, 3, 140, 10, 'left');
assert.equal(content.querySelectorAll('hl.some').length, 1);
@@ -431,7 +503,7 @@
assert.equal(hl.nextSibling.outerHTML, '<hl class="foo">inquit</hl>');
});
- test('growing hl right to start of line', function() {
+ test('growing hl left to start of line', function() {
element._applyRangedHighlight('some', 140, 2, 140, 5, 'left');
element._applyRangedHighlight('some', 140, 0, 140, 3, 'left');
assert.equal(content.querySelectorAll('hl.some').length, 1);
@@ -439,6 +511,14 @@
assert.equal(hl.outerHTML, '<hl class="some">na💢ti</hl>');
assert.strictEqual(content.firstChild, hl);
});
+
+ test('splitting hl containing a tab', function() {
+ element._applyRangedHighlight('some', 140, 63, 140, 72, 'left');
+ assert.equal(content.querySelector('hl.some').textContent, 'sit, quod');
+ element._applyRangedHighlight('another', 140, 66, 140, 81, 'left');
+ assert.equal(content.querySelector('hl.another').textContent,
+ ', quod Epicurum');
+ });
});
test('_applyAllHighlights', function() {
@@ -499,5 +579,281 @@
element.fire('show-context');
assert.isFalse(element._applyAllHighlights.called);
});
+
+ suite('selection', function() {
+ var diff;
+ var builder;
+ var contentStubs;
+
+ var stubContent = function(line, side, opt_child) {
+ var content = diff.querySelector(
+ '.' + side + '.lineNum[data-value="' + line + '"] ~ .content');
+ var lineEl = diff.querySelector(
+ '.' + side + '.lineNum[data-value="' + line + '"]');
+ contentStubs.push({
+ lineEl: lineEl,
+ content: content,
+ });
+ builder.getContentByLineEl.withArgs(lineEl).returns(content);
+ builder.getLineNumberByChild.withArgs(lineEl).returns(line);
+ builder.getContentByLine.withArgs(line, side).returns(content);
+ builder.getSideByLineEl.withArgs(lineEl).returns(side);
+ return content;
+ };
+
+ var emulateSelection = function(
+ startNode, startOffset, endNode, endOffset) {
+ var selection = window.getSelection();
+ var range = document.createRange();
+ range.setStart(startNode, startOffset);
+ range.setEnd(endNode, endOffset);
+ selection.addRange(range);
+ element._handleSelection();
+ };
+
+ var getActionRange = function() {
+ return Polymer.dom(element.root).querySelector(
+ 'gr-selection-action-box').range;
+ };
+
+ var getActionSide = function() {
+ return Polymer.dom(element.root).querySelector(
+ 'gr-selection-action-box').side;
+ };
+
+ var getLineElByChild = function(node) {
+ var stubs = contentStubs.find(function(stub) {
+ return stub.content.contains(node);
+ });
+ return stubs && stubs.lineEl;
+ };
+
+ setup(function() {
+ contentStubs = [];
+ stub('gr-selection-action-box', {
+ placeAbove: sandbox.stub(),
+ });
+ diff = element.querySelector('#diffTable');
+ builder = {
+ getContentByLine: sandbox.stub(),
+ getContentByLineEl: sandbox.stub(),
+ getLineElByChild: getLineElByChild,
+ getLineNumberByChild: sandbox.stub(),
+ getSideByLineEl: sandbox.stub(),
+ };
+ element._cachedDiffBuilder = builder;
+ element.enabled = true;
+ });
+
+ teardown(function() {
+ contentStubs = null;
+ window.getSelection().removeAllRanges();
+ });
+
+ test('single line', function() {
+ var content = stubContent(138, 'left');
+ emulateSelection(content.firstChild, 5, content.firstChild, 12);
+ assert.isTrue(element.isRangeSelected());
+ assert.deepEqual(getActionRange(), {
+ startLine: 138,
+ startChar: 5,
+ endLine: 138,
+ endChar: 12,
+ });
+ assert.equal(getActionSide(), 'left');
+ });
+
+ test('multiline', function() {
+ var startContent = stubContent(119, 'right');
+ var endContent = stubContent(120, 'right');
+ emulateSelection(
+ startContent.firstChild, 10, endContent.lastChild, 7);
+ assert.isTrue(element.isRangeSelected());
+ assert.deepEqual(getActionRange(), {
+ startLine: 119,
+ startChar: 10,
+ endLine: 120,
+ endChar: 34,
+ });
+ assert.equal(getActionSide(), 'right');
+ });
+
+ test('multiline grow end highlight over tabs', function() {
+ var startContent = stubContent(119, 'right');
+ var endContent = stubContent(120, 'right');
+ emulateSelection(startContent.firstChild, 10, endContent.firstChild, 2);
+ assert.isTrue(element.isRangeSelected());
+ assert.deepEqual(getActionRange(), {
+ startLine: 119,
+ startChar: 10,
+ endLine: 120,
+ endChar: 2,
+ });
+ assert.equal(getActionSide(), 'right');
+ });
+
+ test('collapsed', function() {
+ var content = stubContent(138, 'left');
+ emulateSelection(content.firstChild, 5, content.firstChild, 5);
+ assert.isOk(window.getSelection().getRangeAt(0).startContainer);
+ assert.isFalse(element.isRangeSelected());
+ });
+
+ test('starts inside hl', function() {
+ var content = stubContent(140, 'left');
+ var hl = content.querySelector('.foo');
+ emulateSelection(hl.firstChild, 2, hl.nextSibling, 7);
+ assert.isTrue(element.isRangeSelected());
+ assert.deepEqual(getActionRange(), {
+ startLine: 140,
+ startChar: 8,
+ endLine: 140,
+ endChar: 23,
+ });
+ assert.equal(getActionSide(), 'left');
+ });
+
+ test('ends inside hl', function() {
+ var content = stubContent(140, 'left');
+ var hl = content.querySelector('.bar');
+ emulateSelection(hl.previousSibling, 2, hl.firstChild, 3);
+ assert.isTrue(element.isRangeSelected());
+ assert.deepEqual(getActionRange(), {
+ startLine: 140,
+ startChar: 18,
+ endLine: 140,
+ endChar: 27,
+ });
+ });
+
+ test('multiple hl', function() {
+ var content = stubContent(140, 'left');
+ var hl = content.querySelectorAll('hl')[4];
+ emulateSelection(content.firstChild, 2, hl.firstChild, 2);
+ assert.isTrue(element.isRangeSelected());
+ assert.deepEqual(getActionRange(), {
+ startLine: 140,
+ startChar: 2,
+ endLine: 140,
+ endChar: 60,
+ });
+ assert.equal(getActionSide(), 'left');
+ });
+
+ test('starts outside of diff', function() {
+ var content = stubContent(140, 'left');
+ emulateSelection(content.previousElementSibling.firstChild, 2,
+ content.firstChild, 2);
+ assert.isFalse(element.isRangeSelected());
+ });
+
+ test('ends outside of diff', function() {
+ var content = stubContent(140, 'left');
+ emulateSelection(content.nextElementSibling.firstChild, 2,
+ content.firstChild, 2);
+ assert.isFalse(element.isRangeSelected());
+ });
+
+ test('starts and ends on different sides', function() {
+ var startContent = stubContent(140, 'left');
+ var endContent = stubContent(130, 'right');
+ emulateSelection(startContent.firstChild, 2, endContent.firstChild, 2);
+ assert.isFalse(element.isRangeSelected());
+ });
+
+ test('starts in comment thread element', function() {
+ var startContent = stubContent(140, 'left');
+ var comment = startContent.querySelector('gr-diff-comment-thread');
+ var endContent = stubContent(141, 'left');
+ emulateSelection(comment.firstChild, 2, endContent.firstChild, 4);
+ assert.isTrue(element.isRangeSelected());
+ assert.deepEqual(getActionRange(), {
+ startLine: 140,
+ startChar: 81,
+ endLine: 141,
+ endChar: 4,
+ });
+ assert.equal(getActionSide(), 'left');
+ });
+
+ test('ends in comment thread element', function() {
+ var content = stubContent(140, 'left');
+ var comment = content.querySelector('gr-diff-comment-thread');
+ emulateSelection(content.firstChild, 4, comment.firstChild, 1);
+ assert.isTrue(element.isRangeSelected());
+ assert.deepEqual(getActionRange(), {
+ startLine: 140,
+ startChar: 4,
+ endLine: 140,
+ endChar: 81,
+ });
+ assert.equal(getActionSide(), 'left');
+ });
+
+ test('starts in context element', function() {
+ var contextControl = diff.querySelector('.contextControl');
+ var content = stubContent(146, 'right');
+ emulateSelection(contextControl, 0, content.firstChild, 7);
+ // TODO (viktard): Select nearest line.
+ assert.isFalse(element.isRangeSelected());
+ });
+
+ test('ends in context element', function() {
+ var contextControl = diff.querySelector('.contextControl');
+ var content = stubContent(141, 'left');
+ emulateSelection(content.firstChild, 2, contextControl, 0);
+ // TODO (viktard): Select nearest line.
+ assert.isFalse(element.isRangeSelected());
+ });
+
+ test('selection containing context element', function() {
+ var startContent = stubContent(130, 'right');
+ var endContent = stubContent(146, 'right');
+ emulateSelection(startContent.firstChild, 3, endContent.firstChild, 14);
+ assert.isTrue(element.isRangeSelected());
+ assert.deepEqual(getActionRange(), {
+ startLine: 130,
+ startChar: 3,
+ endLine: 146,
+ endChar: 14,
+ });
+ assert.equal(getActionSide(), 'right');
+ });
+
+ test('ends at a tab', function() {
+ var content = stubContent(140, 'left');
+ emulateSelection(
+ content.firstChild, 1, content.querySelector('span'), 0);
+ assert.isTrue(element.isRangeSelected());
+ assert.deepEqual(getActionRange(), {
+ startLine: 140,
+ startChar: 1,
+ endLine: 140,
+ endChar: 51,
+ });
+ assert.equal(getActionSide(), 'left');
+ });
+
+ test('starts at a tab', function() {
+ var content = stubContent(140, 'left');
+ emulateSelection(
+ content.querySelectorAll('hl')[3], 0,
+ content.querySelectorAll('span')[1], 0);
+ assert.isTrue(element.isRangeSelected());
+ assert.deepEqual(getActionRange(), {
+ startLine: 140,
+ startChar: 51,
+ endLine: 140,
+ endChar: 68,
+ });
+ assert.equal(getActionSide(), 'left');
+ });
+
+ // TODO (viktard): Selection starts in line number.
+ // TODO (viktard): Empty lines in selection start.
+ // TODO (viktard): Empty lines in selection end.
+ // TODO (viktard): Only empty lines selected.
+ // TODO (viktard): Unified mode.
+ });
});
</script>
diff --git a/polygerrit-ui/app/elements/diff/gr-selection-action-box/gr-selection-action-box.html b/polygerrit-ui/app/elements/diff/gr-selection-action-box/gr-selection-action-box.html
index 6f95789..9a8ea37 100644
--- a/polygerrit-ui/app/elements/diff/gr-selection-action-box/gr-selection-action-box.html
+++ b/polygerrit-ui/app/elements/diff/gr-selection-action-box/gr-selection-action-box.html
@@ -29,6 +29,7 @@
cursor: pointer;
padding: .3em;
position: absolute;
+ white-space: nowrap;
}
.arrow {
background: #fff;
@@ -36,6 +37,7 @@
border-width: 0 1px 1px 0;
height: var(--gr-arrow-size);
left: calc(50% - 1em);
+ margin-top: .05em;
position: absolute;
transform: rotate(45deg);
width: var(--gr-arrow-size);
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-actions-js-api.js b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-actions-js-api.js
new file mode 100644
index 0000000..f7c337b
--- /dev/null
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-actions-js-api.js
@@ -0,0 +1,62 @@
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+(function(window) {
+ 'use strict';
+
+ function GrChangeActionsInterface(el) {
+ this._el = el;
+ this.RevisionActions = el.RevisionActions;
+ this.ChangeActions = el.ChangeActions;
+ this.ActionType = el.ActionType;
+ }
+
+ GrChangeActionsInterface.prototype.addPrimaryActionKey = function(key) {
+ if (this._el.primaryActionKeys.indexOf(key) !== -1) { return; }
+
+ this._el.push('primaryActionKeys', key);
+ };
+
+ GrChangeActionsInterface.prototype.removePrimaryActionKey = function(key) {
+ this._el.primaryActionKeys = this._el.primaryActionKeys.filter(function(k) {
+ return k !== key;
+ });
+ };
+
+ GrChangeActionsInterface.prototype.add = function(type, label) {
+ return this._el.addActionButton(type, label);
+ };
+
+ GrChangeActionsInterface.prototype.remove = function(key) {
+ return this._el.removeActionButton(key);
+ };
+
+ GrChangeActionsInterface.prototype.addTapListener = function(key, handler) {
+ this._el.addEventListener(key + '-tap', handler);
+ };
+
+ GrChangeActionsInterface.prototype.removeTapListener = function(key,
+ handler) {
+ this._el.removeEventListener(key + '-tap', handler);
+ };
+
+ GrChangeActionsInterface.prototype.setLabel = function(key, text) {
+ this._el.setActionButtonProp(key, 'label', text);
+ };
+
+ GrChangeActionsInterface.prototype.setEnabled = function(key, enabled) {
+ this._el.setActionButtonProp(key, 'enabled', enabled);
+ };
+
+ window.GrChangeActionsInterface = GrChangeActionsInterface;
+})(window);
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-actions-js-api_test.html b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-actions-js-api_test.html
new file mode 100644
index 0000000..3030870
--- /dev/null
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-actions-js-api_test.html
@@ -0,0 +1,123 @@
+<!DOCTYPE html>
+<!--
+Copyright (C) 2016 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<title>gr-change-actions-js-api</title>
+
+<script src="../../../bower_components/webcomponentsjs/webcomponents.min.js"></script>
+<script src="../../../bower_components/web-component-tester/browser.js"></script>
+
+<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
+<!--
+This must refer to the element this interface is wrapping around. Otherwise
+breaking changes to gr-change-actions won’t be noticed.
+-->
+<link rel="import" href="../../change/gr-change-actions/gr-change-actions.html">
+
+<test-fixture id="basic">
+ <template>
+ <gr-change-actions></gr-change-actions>
+ </template>
+</test-fixture>
+
+<script>
+ suite('gr-js-api-interface tests', function() {
+ var element;
+ var changeActions;
+
+ setup(function() {
+ element = fixture('basic');
+ var plugin;
+ Gerrit.install(function(p) { plugin = p; }, '0.1',
+ 'http://test.com/plugins/testplugin/static/test.js');
+ changeActions = plugin.changeActions();
+ });
+
+ teardown(function() {
+ changeActions = null;
+ });
+
+ test('property existence', function() {
+ [
+ 'ActionType',
+ 'ChangeActions',
+ 'RevisionActions',
+ ].forEach(function(p) {
+ assertArraysEqual(changeActions[p], element[p]);
+ });
+ });
+
+ // Because deepEqual doesn’t behave in Safari.
+ function assertArraysEqual(actual, expected) {
+ assert.equal(actual.length, expected.length);
+ for (var i = 0; i < actual.length; i++) {
+ assert.equal(actual[i], expected[i]);
+ }
+ }
+
+ test('add/remove primary action keys', function() {
+ element.primaryActionKeys = [];
+ changeActions.addPrimaryActionKey('foo');
+ assertArraysEqual(element.primaryActionKeys, ['foo']);
+ changeActions.addPrimaryActionKey('foo');
+ assertArraysEqual(element.primaryActionKeys, ['foo']);
+ changeActions.addPrimaryActionKey('bar');
+ assertArraysEqual(element.primaryActionKeys, ['foo', 'bar']);
+ changeActions.removePrimaryActionKey('foo');
+ assertArraysEqual(element.primaryActionKeys, ['bar']);
+ changeActions.removePrimaryActionKey('baz');
+ assertArraysEqual(element.primaryActionKeys, ['bar']);
+ changeActions.removePrimaryActionKey('bar');
+ assertArraysEqual(element.primaryActionKeys, []);
+ });
+
+ test('action buttons', function(done) {
+ var key = changeActions.add(changeActions.ActionType.REVISION, 'Bork!');
+ var handler = sinon.spy();
+ changeActions.addTapListener(key, handler);
+ flush(function() {
+ MockInteractions.tap(element.$$('[data-action-key="' + key + '"]'));
+ assert(handler.calledOnce);
+ changeActions.removeTapListener(key, handler);
+ MockInteractions.tap(element.$$('[data-action-key="' + key + '"]'));
+ assert(handler.calledOnce);
+ changeActions.remove(key);
+ flush(function() {
+ assert.isNull(element.$$('[data-action-key="' + key + '"]'));
+ done();
+ });
+ });
+ });
+
+ test('action button properties', function(done) {
+ var key = changeActions.add(changeActions.ActionType.REVISION, 'Bork!');
+ flush(function() {
+ var button = element.$$('[data-action-key="' + key + '"]');
+ assert.isOk(button);
+ assert.equal(button.getAttribute('data-label'), 'Bork!');
+ assert.isFalse(button.disabled);
+ changeActions.setLabel(key, 'Yo');
+ changeActions.setEnabled(key, false);
+ flush(function() {
+ assert.equal(button.getAttribute('data-label'), 'Yo');
+ assert.isTrue(button.disabled);
+ done();
+ });
+ });
+ });
+ });
+</script>
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-reply-js-api.js b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-reply-js-api.js
new file mode 100644
index 0000000..9d6b83b
--- /dev/null
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-reply-js-api.js
@@ -0,0 +1,30 @@
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+(function(window) {
+ 'use strict';
+
+ function GrChangeReplyInterface(el) {
+ this._el = el;
+ }
+
+ GrChangeReplyInterface.prototype.setLabelValue = function(label, value) {
+ this._el.setLabelValue(label, value);
+ };
+
+ GrChangeReplyInterface.prototype.send = function() {
+ return this._el.send();
+ };
+
+ window.GrChangeReplyInterface = GrChangeReplyInterface;
+})(window);
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-reply-js-api_test.html b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-reply-js-api_test.html
new file mode 100644
index 0000000..2e5aa56
--- /dev/null
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-reply-js-api_test.html
@@ -0,0 +1,70 @@
+<!DOCTYPE html>
+<!--
+Copyright (C) 2016 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<title>gr-change-reply-js-api</title>
+
+<script src="../../../bower_components/webcomponentsjs/webcomponents.min.js"></script>
+<script src="../../../bower_components/web-component-tester/browser.js"></script>
+
+<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
+<!--
+This must refer to the element this interface is wrapping around. Otherwise
+breaking changes to gr-reply-dialog won’t be noticed.
+-->
+<link rel="import" href="../../change/gr-reply-dialog/gr-reply-dialog.html">
+
+<test-fixture id="basic">
+ <template>
+ <gr-reply-dialog></gr-reply-dialog>
+ </template>
+</test-fixture>
+
+<script>
+ suite('gr-change-reply-js-api tests', function() {
+ var element;
+ var sandbox;
+ var changeReply;
+
+ setup(function() {
+ stub('gr-rest-api-interface', {
+ getAccount: function() { return Promise.resolve(null); },
+ });
+ element = fixture('basic');
+ sandbox = sinon.sandbox.create();
+ var plugin;
+ Gerrit.install(function(p) { plugin = p; }, '0.1',
+ 'http://test.com/plugins/testplugin/static/test.js');
+ changeReply = plugin.changeReply();
+ });
+
+ teardown(function() {
+ changeReply = null;
+ sandbox.restore();
+ });
+
+ test('calls', function() {
+ var setLabelValueStub = sinon.stub(element, 'setLabelValue');
+ changeReply.setLabelValue('My-Label', '+1337');
+ assert(setLabelValueStub.calledWithExactly('My-Label', '+1337'));
+
+ var sendStub = sinon.stub(element, 'send');
+ changeReply.send();
+ assert(sendStub.calledWithExactly());
+ });
+ });
+</script>
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface.html b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface.html
index a3b489d..1967b80 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface.html
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface.html
@@ -14,9 +14,12 @@
limitations under the License.
-->
<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="../gr-rest-api-interface/gr-rest-api-interface.html">
<dom-module id="gr-js-api-interface">
<template></template>
+ <script src="gr-change-actions-js-api.js"></script>
+ <script src="gr-change-reply-js-api.js"></script>
<script src="gr-js-api-interface.js"></script>
<script src="gr-public-js-api.js"></script>
</dom-module>
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface.js b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface.js
index cf4edf3..bb37085 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface.js
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface.js
@@ -16,6 +16,7 @@
var EventType = {
HISTORY: 'history',
+ LABEL_CHANGE: 'labelchange',
SHOW_CHANGE: 'showchange',
SUBMIT_CHANGE: 'submitchange',
COMMENT: 'comment',
@@ -23,6 +24,7 @@
var Element = {
CHANGE_ACTIONS: 'changeactions',
+ REPLY_DIALOG: 'replydialog',
};
Polymer({
@@ -53,6 +55,9 @@
case EventType.COMMENT:
this._handleComment(detail);
break;
+ case EventType.LABEL_CHANGE:
+ this._handleLabelChange(detail);
+ break;
default:
console.warn('handleEvent called with unsupported event type:', type);
break;
@@ -133,6 +138,16 @@
});
},
+ _handleLabelChange: function(detail) {
+ this._getEventCallbacks(EventType.LABEL_CHANGE).forEach(function(cb) {
+ try {
+ cb(detail.change);
+ } catch (err) {
+ console.error(err);
+ }
+ });
+ },
+
_getEventCallbacks: function(type) {
return this._eventCallbacks[type] || [];
},
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface_test.html b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface_test.html
index 3be232e..5936bee 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface_test.html
@@ -38,9 +38,14 @@
};
setup(function() {
+ stub('gr-rest-api-interface', {
+ getAccount: function() {
+ return Promise.resolve({name: 'Judy Hopps'});
+ },
+ })
element = fixture('basic');
errorStub = sinon.stub(console, 'error');
- Gerrit.install(function(p) { plugin = p; },
+ Gerrit.install(function(p) { plugin = p; }, '0.1',
'http://test.com/plugins/testplugin/static/test.js');
});
@@ -97,6 +102,17 @@
element.handleEvent(element.EventType.COMMENT, {node: testCommentNode});
});
+ test('labelchange event', function(done) {
+ var testChange = {_number: 42};
+ plugin.on(element.EventType.LABEL_CHANGE, throwErrFn);
+ plugin.on(element.EventType.LABEL_CHANGE, function(change) {
+ assert.deepEqual(change, testChange);
+ assert.isTrue(errorStub.calledOnce);
+ done();
+ });
+ element.handleEvent(element.EventType.LABEL_CHANGE, {change: testChange});
+ });
+
test('submitchange', function() {
plugin.on(element.EventType.SUBMIT_CHANGE, throwErrFn);
plugin.on(element.EventType.SUBMIT_CHANGE, function() { return true; });
@@ -108,5 +124,18 @@
assert.isTrue(errorStub.calledTwice);
});
+ test('versioning', function() {
+ var callback = sinon.spy();
+ Gerrit.install(callback, '0.0pre-alpha');
+ assert(callback.notCalled);
+ });
+
+ test('getAccount', function(done) {
+ Gerrit.getLoggedIn().then(function(loggedIn) {
+ assert.isTrue(loggedIn);
+ done();
+ });
+ });
+
});
</script>
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-public-js-api.js b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-public-js-api.js
index 578f44d..21d76f1 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-public-js-api.js
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-public-js-api.js
@@ -14,11 +14,19 @@
(function(window) {
'use strict';
+ var API_VERSION = '0.1';
+
// GWT JSNI uses $wnd to refer to window.
// http://www.gwtproject.org/doc/latest/DevGuideCodingBasicsJSNI.html
window.$wnd = window;
function Plugin(opt_url) {
+ if (!opt_url) {
+ console.warn('Plugin not being loaded from /plugins base path.',
+ 'Unable to determine name.');
+ return;
+ }
+
this._url = new URL(opt_url);
if (this._url.pathname.indexOf('/plugins') !== 0) {
console.warn('Plugin not being loaded from /plugins base path:',
@@ -44,9 +52,14 @@
return this._url.origin + '/plugins/' + this._name + (opt_path || '/');
};
- Plugin.prototype.getChangeActionsElement = function() {
- return Plugin._sharedAPIElement.getElement(
- Plugin._sharedAPIElement.Element.CHANGE_ACTIONS);
+ Plugin.prototype.changeActions = function() {
+ return new GrChangeActionsInterface(Plugin._sharedAPIElement.getElement(
+ Plugin._sharedAPIElement.Element.CHANGE_ACTIONS));
+ };
+
+ Plugin.prototype.changeReply = function() {
+ return new GrChangeReplyInterface(Plugin._sharedAPIElement.getElement(
+ Plugin._sharedAPIElement.Element.REPLY_DIALOG));
};
var Gerrit = window.Gerrit || {};
@@ -68,12 +81,22 @@
return name;
};
- Gerrit.install = function(callback, opt_src) {
+ Gerrit.install = function(callback, opt_version, opt_src) {
+ if (opt_version && opt_version !== API_VERSION) {
+ console.warn('Only version ' + API_VERSION +
+ ' is supported in PolyGerrit. ' + opt_version + ' was given.');
+ return;
+ }
+
// TODO(andybons): Polyfill currentScript for IE10/11 (edge supports it).
var src = opt_src || (document.currentScript && document.currentScript.src);
callback(new Plugin(src));
};
+ Gerrit.getLoggedIn = function() {
+ return document.createElement('gr-rest-api-interface').getLoggedIn();
+ };
+
Gerrit.installGwt = function() {
// NOOP since PolyGerrit doesn’t support GWT plugins.
};
diff --git a/polygerrit-ui/app/test/index.html b/polygerrit-ui/app/test/index.html
index 98f7eef..153db20 100644
--- a/polygerrit-ui/app/test/index.html
+++ b/polygerrit-ui/app/test/index.html
@@ -75,6 +75,8 @@
'shared/gr-date-formatter/gr-date-formatter_test.html',
'shared/gr-editable-content/gr-editable-content_test.html',
'shared/gr-editable-label/gr-editable-label_test.html',
+ 'shared/gr-js-api-interface/gr-change-actions-js-api_test.html',
+ 'shared/gr-js-api-interface/gr-change-reply-js-api_test.html',
'shared/gr-js-api-interface/gr-js-api-interface_test.html',
'shared/gr-linked-text/gr-linked-text_test.html',
'shared/gr-rest-api-interface/gr-rest-api-interface_test.html',