Always pass non-null ChangeData into Prolog environment

Injecting arbitrary objects via StoredValues is possible but
cumbersome. The ChangeData passed in is useful because it can lazily
compute its own fields without passing in additional objects. The
only problem was it might have been null; fix it so that is not the
case.

This requires in turn allowing ChangeControl to produce non-null
ChangeDatas in its method, which is enough to tip the scales in favor
of having a ChangeControl.AssistedFactory.

Change-Id: I6bac3cfac52b31ddaf36e1504ed229a0b188b6f4
This commit is contained in:
Dave Borowitz
2013-12-23 15:15:51 -08:00
parent d7ba1805cc
commit b5a4ef9d52
12 changed files with 62 additions and 49 deletions

View File

@@ -48,7 +48,6 @@ import java.util.Map;
public final class StoredValues { public final class StoredValues {
public static final StoredValue<ReviewDb> REVIEW_DB = create(ReviewDb.class); public static final StoredValue<ReviewDb> REVIEW_DB = create(ReviewDb.class);
public static final StoredValue<Change> CHANGE = create(Change.class);
public static final StoredValue<ChangeData> CHANGE_DATA = create(ChangeData.class); public static final StoredValue<ChangeData> CHANGE_DATA = create(ChangeData.class);
public static final StoredValue<PatchSet> PATCH_SET = create(PatchSet.class); public static final StoredValue<PatchSet> PATCH_SET = create(PatchSet.class);
public static final StoredValue<ChangeControl> CHANGE_CONTROL = create(ChangeControl.class); public static final StoredValue<ChangeControl> CHANGE_CONTROL = create(ChangeControl.class);
@@ -56,7 +55,7 @@ public final class StoredValues {
public static final StoredValue<PatchSetInfo> PATCH_SET_INFO = new StoredValue<PatchSetInfo>() { public static final StoredValue<PatchSetInfo> PATCH_SET_INFO = new StoredValue<PatchSetInfo>() {
@Override @Override
public PatchSetInfo createValue(Prolog engine) { public PatchSetInfo createValue(Prolog engine) {
Change change = StoredValues.CHANGE.get(engine); Change change = StoredValues.CHANGE_DATA.get(engine).getChange();
PatchSet ps = StoredValues.PATCH_SET.get(engine); PatchSet ps = StoredValues.PATCH_SET.get(engine);
PrologEnvironment env = (PrologEnvironment) engine.control; PrologEnvironment env = (PrologEnvironment) engine.control;
PatchSetInfoFactory patchInfoFactory = PatchSetInfoFactory patchInfoFactory =
@@ -75,7 +74,7 @@ public final class StoredValues {
PrologEnvironment env = (PrologEnvironment) engine.control; PrologEnvironment env = (PrologEnvironment) engine.control;
PatchSetInfo psInfo = StoredValues.PATCH_SET_INFO.get(engine); PatchSetInfo psInfo = StoredValues.PATCH_SET_INFO.get(engine);
PatchListCache plCache = env.getArgs().getPatchListCache(); PatchListCache plCache = env.getArgs().getPatchListCache();
Change change = StoredValues.CHANGE.get(engine); Change change = StoredValues.CHANGE_DATA.get(engine).getChange();
Project.NameKey projectKey = change.getProject(); Project.NameKey projectKey = change.getProject();
ObjectId a = null; ObjectId a = null;
ObjectId b = ObjectId.fromString(psInfo.getRevId()); ObjectId b = ObjectId.fromString(psInfo.getRevId());
@@ -96,7 +95,7 @@ public final class StoredValues {
public Repository createValue(Prolog engine) { public Repository createValue(Prolog engine) {
PrologEnvironment env = (PrologEnvironment) engine.control; PrologEnvironment env = (PrologEnvironment) engine.control;
GitRepositoryManager gitMgr = env.getArgs().getGitRepositoryManager(); GitRepositoryManager gitMgr = env.getArgs().getGitRepositoryManager();
Change change = StoredValues.CHANGE.get(engine); Change change = StoredValues.CHANGE_DATA.get(engine).getChange();
Project.NameKey projectKey = change.getProject(); Project.NameKey projectKey = change.getProject();
final Repository repo; final Repository repo;
try { try {

View File

@@ -43,6 +43,7 @@ public class AccessControlModule extends FactoryModule {
.annotatedWith(GitReceivePackGroups.class) // .annotatedWith(GitReceivePackGroups.class) //
.toProvider(GitReceivePackGroupsProvider.class).in(SINGLETON); .toProvider(GitReceivePackGroupsProvider.class).in(SINGLETON);
factory(ChangeControl.AssistedFactory.class);
factory(ProjectControl.AssistedFactory.class); factory(ProjectControl.AssistedFactory.class);
} }
} }

View File

@@ -35,6 +35,8 @@ import com.google.gerrit.server.query.change.ChangeData;
import com.google.gwtorm.server.OrmException; import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Provider; import com.google.inject.Provider;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject;
import com.googlecode.prolog_cafe.lang.IntegerTerm; import com.googlecode.prolog_cafe.lang.IntegerTerm;
import com.googlecode.prolog_cafe.lang.ListTerm; import com.googlecode.prolog_cafe.lang.ListTerm;
@@ -167,6 +169,10 @@ public class ChangeControl {
} }
} }
interface AssistedFactory {
ChangeControl create(RefControl refControl, Change change);
}
/** /**
* Exception thrown when the label term of a submit record * Exception thrown when the label term of a submit record
* unexpectedly didn't contain a user term. * unexpectedly didn't contain a user term.
@@ -181,18 +187,25 @@ public class ChangeControl {
} }
private final ApprovalsUtil approvalsUtil; private final ApprovalsUtil approvalsUtil;
private final ChangeData.Factory changeDataFactory;
private final RefControl refControl; private final RefControl refControl;
private final Change change; private final Change change;
ChangeControl(ApprovalsUtil au, RefControl r, Change c) { @AssistedInject
this.approvalsUtil = au; ChangeControl(
this.refControl = r; ApprovalsUtil approvalsUtil,
this.change = c; ChangeData.Factory changeDataFactory,
@Assisted RefControl refControl,
@Assisted Change change) {
this.approvalsUtil = approvalsUtil;
this.changeDataFactory = changeDataFactory;
this.refControl = refControl;
this.change = change;
} }
public ChangeControl forUser(final CurrentUser who) { public ChangeControl forUser(final CurrentUser who) {
return new ChangeControl(approvalsUtil, getRefControl().forUser(who), return new ChangeControl(approvalsUtil, changeDataFactory,
getChange()); getRefControl().forUser(who), change);
} }
public RefControl getRefControl() { public RefControl getRefControl() {
@@ -327,9 +340,7 @@ public class ChangeControl {
public boolean isReviewer(ReviewDb db, @Nullable ChangeData cd) public boolean isReviewer(ReviewDb db, @Nullable ChangeData cd)
throws OrmException { throws OrmException {
if (getCurrentUser().isIdentifiedUser()) { if (getCurrentUser().isIdentifiedUser()) {
Collection<Account.Id> results = cd != null Collection<Account.Id> results = changeData(db, cd).reviewers().values();
? cd.reviewers().values()
: approvalsUtil.getReviewers(db, change.getId()).values();
IdentifiedUser user = (IdentifiedUser) getCurrentUser(); IdentifiedUser user = (IdentifiedUser) getCurrentUser();
return results.contains(user.getAccountId()); return results.contains(user.getAccountId());
} }
@@ -410,6 +421,7 @@ public class ChangeControl {
return ruleError("Patch set " + patchSet.getPatchSetId() + " is not current"); return ruleError("Patch set " + patchSet.getPatchSetId() + " is not current");
} }
cd = changeData(db, cd);
if ((change.getStatus() == Change.Status.DRAFT || patchSet.isDraft()) if ((change.getStatus() == Change.Status.DRAFT || patchSet.isDraft())
&& !allowDraft) { && !allowDraft) {
return cannotSubmitDraft(db, patchSet, cd); return cannotSubmitDraft(db, patchSet, cd);
@@ -449,7 +461,7 @@ public class ChangeControl {
} }
private List<SubmitRecord> cannotSubmitDraft(ReviewDb db, PatchSet patchSet, private List<SubmitRecord> cannotSubmitDraft(ReviewDb db, PatchSet patchSet,
ChangeData cd) { @Nullable ChangeData cd) {
try { try {
if (!isDraftVisible(db, cd)) { if (!isDraftVisible(db, cd)) {
return ruleError("Patch set " + patchSet.getPatchSetId() + " not found"); return ruleError("Patch set " + patchSet.getPatchSetId() + " not found");
@@ -555,6 +567,7 @@ public class ChangeControl {
public SubmitTypeRecord getSubmitTypeRecord(ReviewDb db, PatchSet patchSet, public SubmitTypeRecord getSubmitTypeRecord(ReviewDb db, PatchSet patchSet,
@Nullable ChangeData cd) { @Nullable ChangeData cd) {
cd = changeData(db, cd);
try { try {
if (change.getStatus() == Change.Status.DRAFT && !isDraftVisible(db, cd)) { if (change.getStatus() == Change.Status.DRAFT && !isDraftVisible(db, cd)) {
return typeRuleError("Patch set " + patchSet.getPatchSetId() return typeRuleError("Patch set " + patchSet.getPatchSetId()
@@ -657,6 +670,10 @@ public class ChangeControl {
return rec; return rec;
} }
private ChangeData changeData(ReviewDb db, @Nullable ChangeData cd) {
return cd != null ? cd : changeDataFactory.create(db, change);
}
private void appliedBy(SubmitRecord.Label label, Term status) private void appliedBy(SubmitRecord.Label label, Term status)
throws UserTermExpected { throws UserTermExpected {
if (status.isStructure() && status.arity() == 1) { if (status.isStructure() && status.arity() == 1) {

View File

@@ -30,7 +30,6 @@ import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.Branch; import com.google.gerrit.reviewdb.client.Branch;
import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.CurrentUser; import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser; import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.InternalUser; import com.google.gerrit.server.InternalUser;
@@ -146,7 +145,7 @@ public class ProjectControl {
private final CurrentUser user; private final CurrentUser user;
private final ProjectState state; private final ProjectState state;
private final GitRepositoryManager repoManager; private final GitRepositoryManager repoManager;
private final ApprovalsUtil approvalsUtil; private final ChangeControl.AssistedFactory changeControlFactory;
private final PermissionCollection.Factory permissionFilter; private final PermissionCollection.Factory permissionFilter;
private final Collection<ContributorAgreement> contributorAgreements; private final Collection<ContributorAgreement> contributorAgreements;
@@ -162,12 +161,12 @@ public class ProjectControl {
ProjectCache pc, ProjectCache pc,
PermissionCollection.Factory permissionFilter, PermissionCollection.Factory permissionFilter,
GitRepositoryManager repoManager, GitRepositoryManager repoManager,
ApprovalsUtil approvalsUtil, ChangeControl.AssistedFactory changeControlFactory,
@CanonicalWebUrl @Nullable String canonicalWebUrl, @CanonicalWebUrl @Nullable String canonicalWebUrl,
@Assisted CurrentUser who, @Assisted CurrentUser who,
@Assisted ProjectState ps) { @Assisted ProjectState ps) {
this.repoManager = repoManager; this.repoManager = repoManager;
this.approvalsUtil = approvalsUtil; this.changeControlFactory = changeControlFactory;
this.uploadGroups = uploadGroups; this.uploadGroups = uploadGroups;
this.receiveGroups = receiveGroups; this.receiveGroups = receiveGroups;
this.permissionFilter = permissionFilter; this.permissionFilter = permissionFilter;
@@ -185,8 +184,7 @@ public class ProjectControl {
} }
public ChangeControl controlFor(final Change change) { public ChangeControl controlFor(final Change change) {
return new ChangeControl(approvalsUtil, controlForRef(change.getDest()), return changeControlFactory.create(controlForRef(change.getDest()), change);
change);
} }
public RefControl controlForRef(Branch.NameKey ref) { public RefControl controlForRef(Branch.NameKey ref) {

View File

@@ -14,8 +14,9 @@
package com.google.gerrit.server.project; package com.google.gerrit.server.project;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.PatchSet; import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.reviewdb.server.ReviewDb; import com.google.gerrit.reviewdb.server.ReviewDb;
@@ -70,7 +71,7 @@ public class SubmitRuleEvaluator {
*/ */
public SubmitRuleEvaluator(ReviewDb db, PatchSet patchSet, public SubmitRuleEvaluator(ReviewDb db, PatchSet patchSet,
ProjectControl projectControl, ProjectControl projectControl,
ChangeControl changeControl, Change change, @Nullable ChangeData cd, ChangeControl changeControl, Change change, ChangeData cd,
boolean fastEvalLabels, boolean fastEvalLabels,
String userRuleLocatorName, String userRuleWrapperName, String userRuleLocatorName, String userRuleWrapperName,
String filterRuleLocatorName, String filterRuleWrapperName) { String filterRuleLocatorName, String filterRuleWrapperName) {
@@ -95,7 +96,7 @@ public class SubmitRuleEvaluator {
*/ */
public SubmitRuleEvaluator(ReviewDb db, PatchSet patchSet, public SubmitRuleEvaluator(ReviewDb db, PatchSet patchSet,
ProjectControl projectControl, ProjectControl projectControl,
ChangeControl changeControl, Change change, @Nullable ChangeData cd, ChangeControl changeControl, Change change, ChangeData cd,
boolean fastEvalLabels, boolean fastEvalLabels,
String userRuleLocatorName, String userRuleWrapperName, String userRuleLocatorName, String userRuleWrapperName,
String filterRuleLocatorName, String filterRuleWrapperName, String filterRuleLocatorName, String filterRuleWrapperName,
@@ -105,7 +106,7 @@ public class SubmitRuleEvaluator {
this.projectControl = projectControl; this.projectControl = projectControl;
this.changeControl = changeControl; this.changeControl = changeControl;
this.change = change; this.change = change;
this.cd = cd; this.cd = checkNotNull(cd, "ChangeData");
this.fastEvalLabels = fastEvalLabels; this.fastEvalLabels = fastEvalLabels;
this.userRuleLocatorName = userRuleLocatorName; this.userRuleLocatorName = userRuleLocatorName;
this.userRuleWrapperName = userRuleWrapperName; this.userRuleWrapperName = userRuleWrapperName;
@@ -182,7 +183,6 @@ public class SubmitRuleEvaluator {
+ getProjectName(), err); + getProjectName(), err);
} }
env.set(StoredValues.REVIEW_DB, db); env.set(StoredValues.REVIEW_DB, db);
env.set(StoredValues.CHANGE, change);
env.set(StoredValues.CHANGE_DATA, cd); env.set(StoredValues.CHANGE_DATA, cd);
env.set(StoredValues.PATCH_SET, patchSet); env.set(StoredValues.PATCH_SET, patchSet);
env.set(StoredValues.CHANGE_CONTROL, changeControl); env.set(StoredValues.CHANGE_CONTROL, changeControl);

View File

@@ -4,9 +4,7 @@ package gerrit;
import com.google.gerrit.common.data.LabelType; import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.LabelTypes; import com.google.gerrit.common.data.LabelTypes;
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.reviewdb.client.PatchSetApproval; import com.google.gerrit.reviewdb.client.PatchSetApproval;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.rules.StoredValues; import com.google.gerrit.rules.StoredValues;
import com.google.gerrit.server.query.change.ChangeData; import com.google.gerrit.server.query.change.ChangeData;
import com.google.gwtorm.server.OrmException; import com.google.gwtorm.server.OrmException;
@@ -40,20 +38,11 @@ class PRED__load_commit_labels_1 extends Predicate.P1 {
Term listHead = Prolog.Nil; Term listHead = Prolog.Nil;
try { try {
ReviewDb db = StoredValues.REVIEW_DB.get(engine); ChangeData cd = StoredValues.CHANGE_DATA.get(engine);
PatchSet patchSet = StoredValues.PATCH_SET.get(engine);
ChangeData cd = StoredValues.CHANGE_DATA.getOrNull(engine);
LabelTypes types = LabelTypes types =
StoredValues.CHANGE_CONTROL.get(engine).getLabelTypes(); StoredValues.CHANGE_CONTROL.get(engine).getLabelTypes();
Iterable<PatchSetApproval> approvals; for (PatchSetApproval a : cd.currentApprovals()) {
if (cd != null) {
approvals = cd.currentApprovals();
} else {
approvals = db.patchSetApprovals().byPatchSet(patchSet.getId());
}
for (PatchSetApproval a : approvals) {
if (a.getValue() == 0) { if (a.getValue() == 0) {
continue; continue;
} }

View File

@@ -36,7 +36,7 @@ public class PRED_change_branch_1 extends Predicate.P1 {
engine.setB0(); engine.setB0();
Term a1 = arg1.dereference(); Term a1 = arg1.dereference();
Change change = StoredValues.CHANGE.get(engine); Change change = StoredValues.CHANGE_DATA.get(engine).getChange();
Branch.NameKey name = change.getDest(); Branch.NameKey name = change.getDest();
if (!a1.unify(SymbolTerm.create(name.get()), engine.trail)) { if (!a1.unify(SymbolTerm.create(name.get()), engine.trail)) {

View File

@@ -40,7 +40,7 @@ public class PRED_change_owner_1 extends Predicate.P1 {
engine.setB0(); engine.setB0();
Term a1 = arg1.dereference(); Term a1 = arg1.dereference();
Change change = StoredValues.CHANGE.get(engine); Change change = StoredValues.CHANGE_DATA.get(engine).getChange();
Account.Id ownerId = change.getOwner(); Account.Id ownerId = change.getOwner();
if (!a1.unify(new StructureTerm(user, new IntegerTerm(ownerId.get())), engine.trail)) { if (!a1.unify(new StructureTerm(user, new IntegerTerm(ownerId.get())), engine.trail)) {

View File

@@ -36,7 +36,7 @@ public class PRED_change_project_1 extends Predicate.P1 {
engine.setB0(); engine.setB0();
Term a1 = arg1.dereference(); Term a1 = arg1.dereference();
Change change = StoredValues.CHANGE.get(engine); Change change = StoredValues.CHANGE_DATA.get(engine).getChange();
Project.NameKey name = change.getProject(); Project.NameKey name = change.getProject();
if (!a1.unify(SymbolTerm.create(name.get()), engine.trail)) { if (!a1.unify(SymbolTerm.create(name.get()), engine.trail)) {

View File

@@ -36,7 +36,7 @@ public class PRED_change_topic_1 extends Predicate.P1 {
Term a1 = arg1.dereference(); Term a1 = arg1.dereference();
Term topicTerm = Prolog.Nil; Term topicTerm = Prolog.Nil;
Change change = StoredValues.CHANGE.get(engine); Change change = StoredValues.CHANGE_DATA.get(engine).getChange();
String topic = change.getTopic(); String topic = change.getTopic();
if (topic != null) { if (topic != null) {
topicTerm = SymbolTerm.create(topic); topicTerm = SymbolTerm.create(topic);

View File

@@ -85,7 +85,6 @@ public class GerritCommonTest extends PrologTestCase {
new Account.Id(2), new Account.Id(2),
new Branch.NameKey(localKey, "refs/heads/master"), new Branch.NameKey(localKey, "refs/heads/master"),
TimeUtil.nowTs()); TimeUtil.nowTs());
env.set(StoredValues.CHANGE, change);
env.set(StoredValues.CHANGE_CONTROL, util.user(local).controlFor(change)); env.set(StoredValues.CHANGE_CONTROL, util.user(local).controlFor(change));
} }

View File

@@ -31,7 +31,6 @@ import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.client.Project.NameKey; import com.google.gerrit.reviewdb.client.Project.NameKey;
import com.google.gerrit.rules.PrologEnvironment; import com.google.gerrit.rules.PrologEnvironment;
import com.google.gerrit.rules.RulesCache; import com.google.gerrit.rules.RulesCache;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.CurrentUser; import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.account.CapabilityControl; import com.google.gerrit.server.account.CapabilityControl;
import com.google.gerrit.server.account.GroupMembership; import com.google.gerrit.server.account.GroupMembership;
@@ -42,9 +41,12 @@ import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.SitePaths; import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.git.GitRepositoryManager; import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.ProjectConfig; import com.google.gerrit.server.git.ProjectConfig;
import com.google.gerrit.server.patch.PatchListCache;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.testutil.InMemoryRepositoryManager; import com.google.gerrit.testutil.InMemoryRepositoryManager;
import com.google.inject.Guice; import com.google.inject.Guice;
import com.google.inject.Injector; import com.google.inject.Injector;
import com.google.inject.util.Providers;
import org.eclipse.jgit.errors.ConfigInvalidException; import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.lib.Config; import org.eclipse.jgit.lib.Config;
@@ -119,6 +121,7 @@ public class Util {
private final Map<Project.NameKey, ProjectState> all; private final Map<Project.NameKey, ProjectState> all;
private final ProjectCache projectCache; private final ProjectCache projectCache;
private final CapabilityControl.Factory capabilityControlFactory; private final CapabilityControl.Factory capabilityControlFactory;
private final ChangeControl.AssistedFactory changeControlFactory;
private final PermissionCollection.Factory sectionSorter; private final PermissionCollection.Factory sectionSorter;
private final GitRepositoryManager repoManager; private final GitRepositoryManager repoManager;
@@ -190,8 +193,13 @@ public class Util {
protected void configure() { protected void configure() {
bind(Config.class).annotatedWith(GerritServerConfig.class).toInstance( bind(Config.class).annotatedWith(GerritServerConfig.class).toInstance(
new Config()); new Config());
bind(GitRepositoryManager.class).toInstance(repoManager);
bind(PatchListCache.class)
.toProvider(Providers.<PatchListCache> of(null));
factory(CapabilityControl.Factory.class); factory(CapabilityControl.Factory.class);
factory(ChangeControl.AssistedFactory.class);
factory(ChangeData.Factory.class);
bind(ProjectCache.class).toInstance(projectCache); bind(ProjectCache.class).toInstance(projectCache);
} }
}); });
@@ -201,6 +209,8 @@ public class Util {
sectionSorter = new PermissionCollection.Factory(new SectionSortCache(c)); sectionSorter = new PermissionCollection.Factory(new SectionSortCache(c));
capabilityControlFactory = capabilityControlFactory =
injector.getInstance(CapabilityControl.Factory.class); injector.getInstance(CapabilityControl.Factory.class);
changeControlFactory =
injector.getInstance(ChangeControl.AssistedFactory.class);
} }
public ProjectConfig getParentConfig() { public ProjectConfig getParentConfig() {
@@ -234,7 +244,7 @@ public class Util {
return new ProjectControl(Collections.<AccountGroup.UUID> emptySet(), return new ProjectControl(Collections.<AccountGroup.UUID> emptySet(),
Collections.<AccountGroup.UUID> emptySet(), projectCache, Collections.<AccountGroup.UUID> emptySet(), projectCache,
sectionSorter, null, new ApprovalsUtil(), canonicalWebUrl, sectionSorter, null, changeControlFactory, canonicalWebUrl,
new MockUser(name, memberOf), newProjectState(local)); new MockUser(name, memberOf), newProjectState(local));
} }