blob: 36a52e23cae83fd6fb4957df391c534008c78b7e [file] [log] [blame]
// 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.RefRight;
import com.google.gerrit.reviewdb.ApprovalCategory.Id;
import com.google.gerrit.server.CurrentUser;
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.ProjectControl;
import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.project.RefControl;
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.List;
import java.util.Map;
/** 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;
@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();
}
Change getChange() {
return change;
}
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();
}
/**
* 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>
*/
private void applyTypeFloor(final ApprovalType at, final PatchSetApproval a) {
final ApprovalCategoryValue atMin = at.getMin();
if (atMin != null && a.getValue() < atMin.getValue()) {
a.setValue(atMin.getValue());
}
final ApprovalCategoryValue atMax = at.getMax();
if (atMax != null && a.getValue() > atMax.getValue()) {
a.setValue(atMax.getValue());
}
}
/**
* Normalize the approval record to be inside the maximum range permitted by
* the RefRights granted to groups the account is a member of.
* <p>
* If multiple RefRights 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>
*/
private void applyRightFloor(final PatchSetApproval a) {
final IdentifiedUser user = userFactory.create(a.getAccountId());
RefControl rc = controlFor(user);
// Find the maximal range actually granted to the user.
//
short minAllowed = 0, maxAllowed = 0;
for (final RefRight r : rc.getApplicableRights(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.
//
if (a.getValue() < minAllowed) {
a.setValue(minAllowed);
} else if (a.getValue() > maxAllowed) {
a.setValue(maxAllowed);
}
}
RefControl controlFor(final CurrentUser user) {
ProjectControl pc = project.controlFor(user);
RefControl rc = pc.controlForRef(change.getDest().get());
return rc;
}
/** 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();
}
}