Merge "Refactor: Extract group list manipulation"
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/GroupList.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/GroupList.java
new file mode 100644
index 0000000..29948c2
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/GroupList.java
@@ -0,0 +1,142 @@
+// 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.git;
+
+import com.google.gerrit.common.data.GroupReference;
+import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.reviewdb.client.AccountGroup.UUID;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class GroupList {
+ public static final String FILE_NAME = "groups";
+ private final Map<AccountGroup.UUID, GroupReference> byUUID;
+
+ private GroupList(Map<AccountGroup.UUID, GroupReference> byUUID) {
+ this.byUUID = byUUID;
+ }
+
+ public static GroupList parse(String text, ValidationError.Sink errors) throws IOException {
+ Map<AccountGroup.UUID, GroupReference> groupsByUUID = new HashMap<>();
+
+ BufferedReader br = new BufferedReader(new StringReader(text));
+ String s;
+ for (int lineNumber = 1; (s = br.readLine()) != null; lineNumber++) {
+ if (s.isEmpty() || s.startsWith("#")) {
+ continue;
+ }
+
+ int tab = s.indexOf('\t');
+ if (tab < 0) {
+ errors.error(new ValidationError(FILE_NAME, lineNumber, "missing tab delimiter"));
+ continue;
+ }
+
+ AccountGroup.UUID uuid = new AccountGroup.UUID(s.substring(0, tab).trim());
+ String name = s.substring(tab + 1).trim();
+ GroupReference ref = new GroupReference(uuid, name);
+
+ groupsByUUID.put(uuid, ref);
+ }
+
+ return new GroupList(groupsByUUID);
+ }
+
+ public GroupReference byUUID(AccountGroup.UUID uuid) {
+ return byUUID.get(uuid);
+ }
+
+ public GroupReference resolve(GroupReference group) {
+ if (group != null) {
+ GroupReference ref = byUUID.get(group.getUUID());
+ if (ref != null) {
+ return ref;
+ }
+ byUUID.put(group.getUUID(), group);
+ }
+ return group;
+ }
+
+ public Collection<GroupReference> references() {
+ return byUUID.values();
+ }
+
+ public Set<AccountGroup.UUID> uuids() {
+ return byUUID.keySet();
+ }
+
+ public void put(UUID uuid, GroupReference reference) {
+ byUUID.put(uuid, reference);
+ }
+
+ private static String pad(int len, String src) {
+ if (len <= src.length()) {
+ return src;
+ }
+
+ StringBuilder r = new StringBuilder(len);
+ r.append(src);
+ while (r.length() < len) {
+ r.append(' ');
+ }
+ return r.toString();
+ }
+
+ private static <T extends Comparable<? super T>> List<T> sort(Collection<T> m) {
+ ArrayList<T> r = new ArrayList<>(m);
+ Collections.sort(r);
+ return r;
+ }
+
+ public String asText() {
+ if (byUUID.isEmpty()) {
+ return null;
+ }
+
+ final int uuidLen = 40;
+ StringBuilder buf = new StringBuilder();
+ buf.append(pad(uuidLen, "# UUID"));
+ buf.append('\t');
+ buf.append("Group Name");
+ buf.append('\n');
+
+ buf.append('#');
+ buf.append('\n');
+
+ for (GroupReference g : sort(byUUID.values())) {
+ if (g.getUUID() != null && g.getName() != null) {
+ buf.append(pad(uuidLen, g.getUUID().get()));
+ buf.append('\t');
+ buf.append(g.getName());
+ buf.append('\n');
+ }
+ }
+ return buf.toString();
+ }
+
+ public void retainUUIDs(Collection<AccountGroup.UUID> toBeRetained) {
+ byUUID.keySet().retainAll(toBeRetained);
+ }
+
+}
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 8b902bd..3142798 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
@@ -58,9 +58,7 @@
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.util.StringUtils;
-import java.io.BufferedReader;
import java.io.IOException;
-import java.io.StringReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -75,7 +73,7 @@
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
-public class ProjectConfig extends VersionedMetaData {
+public class ProjectConfig extends VersionedMetaData implements ValidationError.Sink {
public static final String COMMENTLINK = "commentlink";
private static final String KEY_MATCH = "match";
private static final String KEY_HTML = "html";
@@ -83,7 +81,6 @@
private static final String KEY_ENABLED = "enabled";
public static final String PROJECT_CONFIG = "project.config";
- private static final String GROUP_LIST = "groups";
private static final String PROJECT = "project";
private static final String KEY_DESCRIPTION = "description";
@@ -154,7 +151,7 @@
private Project.NameKey projectName;
private Project project;
private AccountsSection accountsSection;
- private Map<AccountGroup.UUID, GroupReference> groupsByUUID;
+ private GroupList groupList;
private Map<String, AccessSection> accessSections;
private BranchOrderSection branchOrderSection;
private Map<String, ContributorAgreement> contributorAgreements;
@@ -324,24 +321,17 @@
}
public GroupReference resolve(GroupReference group) {
- if (group != null) {
- GroupReference ref = groupsByUUID.get(group.getUUID());
- if (ref != null) {
- return ref;
- }
- groupsByUUID.put(group.getUUID(), group);
- }
- return group;
+ return groupList.resolve(group);
}
/** @return the group reference, if the group is used by at least one rule. */
public GroupReference getGroup(AccountGroup.UUID uuid) {
- return groupsByUUID.get(uuid);
+ return groupList.byUUID(uuid);
}
/** @return set of all groups used by this configuration. */
public Set<AccountGroup.UUID> getAllGroupUUIDs() {
- return Collections.unmodifiableSet(groupsByUUID.keySet());
+ return groupList.uuids();
}
/**
@@ -375,7 +365,7 @@
*/
public boolean updateGroupNames(GroupBackend groupBackend) {
boolean dirty = false;
- for (GroupReference ref : groupsByUUID.values()) {
+ for (GroupReference ref : groupList.references()) {
GroupDescription.Basic g = groupBackend.get(ref.getUUID());
if (g != null && !g.getName().equals(ref.getName())) {
dirty = true;
@@ -405,7 +395,8 @@
@Override
protected void onLoad() throws IOException, ConfigInvalidException {
- Map<String, GroupReference> groupsByName = readGroupList();
+ readGroupList();
+ Map<String, GroupReference> groupsByName = mapGroupReferences();
rulesId = getObjectId("rules.pl");
Config rc = readConfig(PROJECT_CONFIG);
@@ -531,7 +522,7 @@
n.addEmail(ref);
} else {
error(new ValidationError(PROJECT_CONFIG,
- "group \"" + ref.getName() + "\" not in " + GROUP_LIST));
+ "group \"" + ref.getName() + "\" not in " + GroupList.FILE_NAME));
}
} else if (dst.startsWith("user ")) {
error(new ValidationError(PROJECT_CONFIG, dst + " not supported"));
@@ -627,7 +618,7 @@
ref = rule.getGroup();
groupsByName.put(ref.getName(), ref);
error(new ValidationError(PROJECT_CONFIG,
- "group \"" + ref.getName() + "\" not in " + GROUP_LIST));
+ "group \"" + ref.getName() + "\" not in " + GroupList.FILE_NAME));
}
rule.setGroup(ref);
@@ -774,31 +765,18 @@
return new PluginConfig(pluginName, pluginConfig, this);
}
- private Map<String, GroupReference> readGroupList() throws IOException {
- groupsByUUID = new HashMap<>();
- Map<String, GroupReference> groupsByName = new HashMap<>();
+ private void readGroupList() throws IOException {
+ groupList = GroupList.parse(readUTF8(GroupList.FILE_NAME), this);
+ }
- BufferedReader br = new BufferedReader(new StringReader(readUTF8(GROUP_LIST)));
- String s;
- for (int lineNumber = 1; (s = br.readLine()) != null; lineNumber++) {
- if (s.isEmpty() || s.startsWith("#")) {
- continue;
- }
-
- int tab = s.indexOf('\t');
- if (tab < 0) {
- error(new ValidationError(GROUP_LIST, lineNumber, "missing tab delimiter"));
- continue;
- }
-
- AccountGroup.UUID uuid = new AccountGroup.UUID(s.substring(0, tab).trim());
- String name = s.substring(tab + 1).trim();
- GroupReference ref = new GroupReference(uuid, name);
-
- groupsByUUID.put(uuid, ref);
- groupsByName.put(name, ref);
+ private Map<String, GroupReference> mapGroupReferences() {
+ Collection<GroupReference> references = groupList.references();
+ Map<String, GroupReference> result = new HashMap<>(references.size());
+ for (GroupReference ref : references) {
+ result.put(ref.getName(), ref);
}
- return groupsByName;
+
+ return result;
}
@Override
@@ -837,7 +815,7 @@
saveContributorAgreements(rc, keepGroups);
saveAccessSections(rc, keepGroups);
saveNotifySections(rc, keepGroups);
- groupsByUUID.keySet().retainAll(keepGroups);
+ groupList.retainUUIDs(keepGroups);
saveLabelSections(rc);
savePluginSections(rc);
@@ -1110,30 +1088,7 @@
}
private void saveGroupList() throws IOException {
- if (groupsByUUID.isEmpty()) {
- saveFile(GROUP_LIST, null);
- return;
- }
-
- final int uuidLen = 40;
- StringBuilder buf = new StringBuilder();
- buf.append(pad(uuidLen, "# UUID"));
- buf.append('\t');
- buf.append("Group Name");
- buf.append('\n');
-
- buf.append('#');
- buf.append('\n');
-
- for (GroupReference g : sort(groupsByUUID.values())) {
- if (g.getUUID() != null && g.getName() != null) {
- buf.append(pad(uuidLen, g.getUUID().get()));
- buf.append('\t');
- buf.append(g.getName());
- buf.append('\n');
- }
- }
- saveUTF8(GROUP_LIST, buf.toString());
+ saveUTF8(GroupList.FILE_NAME, groupList.asText());
}
private <E extends Enum<?>> E getEnum(Config rc, String section,
@@ -1146,26 +1101,13 @@
}
}
- private void error(ValidationError error) {
+ public void error(ValidationError error) {
if (validationErrors == null) {
validationErrors = new ArrayList<>(4);
}
validationErrors.add(error);
}
- private static String pad(int len, String src) {
- if (len <= src.length()) {
- return src;
- }
-
- StringBuilder r = new StringBuilder(len);
- r.append(src);
- while (r.length() < len) {
- r.append(' ');
- }
- return r.toString();
- }
-
private static <T extends Comparable<? super T>> List<T> sort(Collection<T> m) {
ArrayList<T> r = new ArrayList<>(m);
Collections.sort(r);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ValidationError.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ValidationError.java
index e1ab41d..ad84046 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ValidationError.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ValidationError.java
@@ -38,4 +38,8 @@
public String toString() {
return "ValidationError[" + message + "]";
}
+
+ public interface Sink {
+ void error(ValidationError error);
+ }
}
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/git/GroupListTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/git/GroupListTest.java
new file mode 100644
index 0000000..e741a91c
--- /dev/null
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/git/GroupListTest.java
@@ -0,0 +1,121 @@
+// 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.git;
+
+import static org.easymock.EasyMock.anyObject;
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.createNiceMock;
+import static org.easymock.EasyMock.expectLastCall;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import com.google.gerrit.common.data.GroupReference;
+import com.google.gerrit.reviewdb.client.AccountGroup;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Set;
+
+public class GroupListTest {
+
+ private static final String TEXT =
+ "# UUID \tGroup Name\n" + "#\n"
+ + "d96b998f8a66ff433af50befb975d0e2bb6e0999\tNon-Interactive Users\n"
+ + "ebe31c01aec2c9ac3b3c03e87a47450829ff4310\tAdministrators\n";
+
+ private GroupList groupList;
+
+ @Before
+ public void setup() throws IOException {
+ ValidationError.Sink sink = createNiceMock(ValidationError.Sink.class);
+ replay(sink);
+ groupList = GroupList.parse(TEXT, sink);
+ }
+
+ @Test
+ public void testByUUID() throws Exception {
+ AccountGroup.UUID uuid =
+ new AccountGroup.UUID("d96b998f8a66ff433af50befb975d0e2bb6e0999");
+
+ GroupReference groupReference = groupList.byUUID(uuid);
+
+ assertEquals(uuid, groupReference.getUUID());
+ assertEquals("Non-Interactive Users", groupReference.getName());
+ }
+
+ @Test
+ public void testPut() {
+ AccountGroup.UUID uuid = new AccountGroup.UUID("abc");
+ GroupReference groupReference = new GroupReference(uuid, "Hutzliputz");
+
+ groupList.put(uuid, groupReference);
+
+ assertEquals(3, groupList.references().size());
+ GroupReference found = groupList.byUUID(uuid);
+ assertEquals(groupReference, found);
+ }
+
+ @Test
+ public void testReferences() throws Exception {
+ Collection<GroupReference> result = groupList.references();
+
+ assertEquals(2, result.size());
+ AccountGroup.UUID uuid =
+ new AccountGroup.UUID("ebe31c01aec2c9ac3b3c03e87a47450829ff4310");
+ GroupReference expected = new GroupReference(uuid, "Administrators");
+
+ assertTrue(result.contains(expected));
+ }
+
+ @Test
+ public void testUUIDs() throws Exception {
+ Set<AccountGroup.UUID> result = groupList.uuids();
+
+ assertEquals(2, result.size());
+ AccountGroup.UUID expected =
+ new AccountGroup.UUID("ebe31c01aec2c9ac3b3c03e87a47450829ff4310");
+ assertTrue(result.contains(expected));
+ }
+
+ @Test
+ public void testValidationError() throws Exception {
+ ValidationError.Sink sink = createMock(ValidationError.Sink.class);
+ sink.error(anyObject(ValidationError.class));
+ expectLastCall().times(2);
+ replay(sink);
+ groupList = GroupList.parse(TEXT.replace("\t", " "), sink);
+ verify(sink);
+ }
+
+ @Test
+ public void testRetainAll() throws Exception {
+ AccountGroup.UUID uuid =
+ new AccountGroup.UUID("d96b998f8a66ff433af50befb975d0e2bb6e0999");
+ groupList.retainUUIDs(Collections.singleton(uuid));
+
+ assertNotNull(groupList.byUUID(uuid));
+ assertNull(groupList.byUUID(new AccountGroup.UUID(
+ "ebe31c01aec2c9ac3b3c03e87a47450829ff4310")));
+ }
+
+}