diff options
Diffstat (limited to 'gerrit-server/src/main/java/com/google/gerrit/server/workflow/FunctionState.java')
-rw-r--r-- | gerrit-server/src/main/java/com/google/gerrit/server/workflow/FunctionState.java | 261 |
1 files changed, 261 insertions, 0 deletions
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/workflow/FunctionState.java b/gerrit-server/src/main/java/com/google/gerrit/server/workflow/FunctionState.java new file mode 100644 index 0000000000..69f571d47c --- /dev/null +++ b/gerrit-server/src/main/java/com/google/gerrit/server/workflow/FunctionState.java @@ -0,0 +1,261 @@ +// Copyright (C) 2008 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.workflow; + +import com.google.gerrit.common.data.ApprovalType; +import com.google.gerrit.common.data.ApprovalTypes; +import com.google.gerrit.reviewdb.AccountGroup; +import com.google.gerrit.reviewdb.ApprovalCategory; +import com.google.gerrit.reviewdb.ApprovalCategoryValue; +import com.google.gerrit.reviewdb.Change; +import com.google.gerrit.reviewdb.PatchSet; +import com.google.gerrit.reviewdb.PatchSetApproval; +import com.google.gerrit.reviewdb.Project; +import com.google.gerrit.reviewdb.ProjectRight; +import com.google.gerrit.reviewdb.ApprovalCategory.Id; +import com.google.gerrit.server.IdentifiedUser; +import com.google.gerrit.server.account.GroupCache; +import com.google.gerrit.server.project.ProjectCache; +import com.google.gerrit.server.project.ProjectState; +import com.google.inject.Inject; +import com.google.inject.assistedinject.Assisted; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** State passed through to a {@link CategoryFunction}. */ +public class FunctionState { + public interface Factory { + FunctionState create(Change c, PatchSet.Id psId, + Collection<PatchSetApproval> all); + } + + private final ApprovalTypes approvalTypes; + private final IdentifiedUser.GenericFactory userFactory; + + private final Map<ApprovalCategory.Id, Collection<PatchSetApproval>> approvals = + new HashMap<ApprovalCategory.Id, Collection<PatchSetApproval>>(); + private final Map<ApprovalCategory.Id, Boolean> valid = + new HashMap<ApprovalCategory.Id, Boolean>(); + private final Change change; + private final ProjectState project; + private final Map<ApprovalCategory.Id, Collection<ProjectRight>> allRights = + new HashMap<ApprovalCategory.Id, Collection<ProjectRight>>(); + private Map<ApprovalCategory.Id, Collection<ProjectRight>> projectRights; + private Map<ApprovalCategory.Id, Collection<ProjectRight>> inheritedRights; + private Set<PatchSetApproval> modified; + + @Inject + FunctionState(final ApprovalTypes approvalTypes, + final ProjectCache projectCache, + final IdentifiedUser.GenericFactory userFactory, final GroupCache egc, + @Assisted final Change c, @Assisted final PatchSet.Id psId, + @Assisted final Collection<PatchSetApproval> all) { + this.approvalTypes = approvalTypes; + this.userFactory = userFactory; + + change = c; + project = projectCache.get(change.getProject()); + + for (final PatchSetApproval ca : all) { + if (psId.equals(ca.getPatchSetId())) { + Collection<PatchSetApproval> l = approvals.get(ca.getCategoryId()); + if (l == null) { + l = new ArrayList<PatchSetApproval>(); + approvals.put(ca.getCategoryId(), l); + } + l.add(ca); + } + } + } + + List<ApprovalType> getApprovalTypes() { + return approvalTypes.getApprovalTypes(); + } + + public Change getChange() { + return change; + } + + public Project getProject() { + return project.getProject(); + } + + public void valid(final ApprovalType at, final boolean v) { + valid.put(id(at), v); + } + + public boolean isValid(final ApprovalType at) { + return isValid(id(at)); + } + + public boolean isValid(final ApprovalCategory.Id id) { + final Boolean b = valid.get(id); + return b != null && b; + } + + public Collection<PatchSetApproval> getApprovals(final ApprovalType at) { + return getApprovals(id(at)); + } + + public Collection<PatchSetApproval> getApprovals(final ApprovalCategory.Id id) { + final Collection<PatchSetApproval> l = approvals.get(id); + return l != null ? l : Collections.<PatchSetApproval> emptySet(); + } + + public void dirty(final PatchSetApproval ap) { + if (modified == null) { + modified = new HashSet<PatchSetApproval>(); + } + modified.add(ap); + } + + public Collection<PatchSetApproval> getDirtyChangeApprovals() { + if (modified != null) { + return modified; + } + return Collections.emptySet(); + } + + public Collection<ProjectRight> getProjectRights(final ApprovalType at) { + return getProjectRights(id(at)); + } + + public Collection<ProjectRight> getProjectRights(final ApprovalCategory.Id id) { + if (projectRights == null) { + projectRights = index(project.getLocalRights()); + } + final Collection<ProjectRight> l = projectRights.get(id); + return l != null ? l : Collections.<ProjectRight> emptySet(); + } + + public Collection<ProjectRight> getWildcardRights(final ApprovalType at) { + return getWildcardRights(id(at)); + } + + public Collection<ProjectRight> getWildcardRights(final ApprovalCategory.Id id) { + if (inheritedRights == null) { + inheritedRights = index(project.getInheritedRights()); + } + final Collection<ProjectRight> l = inheritedRights.get(id); + return l != null ? l : Collections.<ProjectRight> emptySet(); + } + + public Collection<ProjectRight> getAllRights(final ApprovalType at) { + return getAllRights(id(at)); + } + + public Collection<ProjectRight> getAllRights(final ApprovalCategory.Id id) { + Collection<ProjectRight> l = allRights.get(id); + if (l == null) { + l = new ArrayList<ProjectRight>(); + l.addAll(getProjectRights(id)); + l.addAll(getWildcardRights(id)); + l = Collections.unmodifiableCollection(l); + allRights.put(id, l); + } + return l; + } + + private static Map<Id, Collection<ProjectRight>> index( + final Collection<ProjectRight> rights) { + final HashMap<ApprovalCategory.Id, Collection<ProjectRight>> r; + + r = new HashMap<ApprovalCategory.Id, Collection<ProjectRight>>(); + for (final ProjectRight pr : rights) { + Collection<ProjectRight> l = r.get(pr.getApprovalCategoryId()); + if (l == null) { + l = new ArrayList<ProjectRight>(); + r.put(pr.getApprovalCategoryId(), l); + } + l.add(pr); + } + return r; + } + + /** + * Normalize the approval record down to the range permitted by the type, in + * case the type was modified since the approval was originally granted. + * <p> + * If the record's value was modified, its automatically marked as dirty. + */ + public void applyTypeFloor(final ApprovalType at, final PatchSetApproval a) { + final ApprovalCategoryValue atMin = at.getMin(); + + if (atMin != null && a.getValue() < atMin.getValue()) { + a.setValue(atMin.getValue()); + dirty(a); + } + + final ApprovalCategoryValue atMax = at.getMax(); + if (atMax != null && a.getValue() > atMax.getValue()) { + a.setValue(atMax.getValue()); + dirty(a); + } + } + + /** + * Normalize the approval record to be inside the maximum range permitted by + * the ProjectRights granted to groups the account is a member of. + * <p> + * If multiple ProjectRights are matched (assigned to different groups the + * account is a member of) the lowest minValue and the highest maxValue of the + * union of them is used. + * <p> + * If the record's value was modified, its automatically marked as dirty. + */ + public void applyRightFloor(final PatchSetApproval a) { + final IdentifiedUser user = userFactory.create(a.getAccountId()); + + // Find the maximal range actually granted to the user. + // + short minAllowed = 0, maxAllowed = 0; + for (final ProjectRight r : getAllRights(a.getCategoryId())) { + final AccountGroup.Id grp = r.getAccountGroupId(); + if (user.getEffectiveGroups().contains(grp)) { + minAllowed = (short) Math.min(minAllowed, r.getMinValue()); + maxAllowed = (short) Math.max(maxAllowed, r.getMaxValue()); + } + } + + // Normalize the value into that range, returning true if we changed + // the value. + // + if (a.getValue() < minAllowed) { + a.setValue(minAllowed); + dirty(a); + + } else if (a.getValue() > maxAllowed) { + a.setValue(maxAllowed); + dirty(a); + } + } + + /** Run <code>applyTypeFloor</code>, <code>applyRightFloor</code>. */ + public void normalize(final ApprovalType at, final PatchSetApproval ca) { + applyTypeFloor(at, ca); + applyRightFloor(ca); + } + + private static Id id(final ApprovalType at) { + return at.getCategory().getId(); + } +} |