blob: 14b61a42d5665c572c2ea42ff03b90984119dc3f [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.client.workflow;
import com.google.gerrit.client.data.ApprovalType;
import com.google.gerrit.client.data.ProjectCache;
import com.google.gerrit.client.reviewdb.Account;
import com.google.gerrit.client.reviewdb.AccountGroup;
import com.google.gerrit.client.reviewdb.ApprovalCategory;
import com.google.gerrit.client.reviewdb.ApprovalCategoryValue;
import com.google.gerrit.client.reviewdb.Change;
import com.google.gerrit.client.reviewdb.ChangeApproval;
import com.google.gerrit.client.reviewdb.Project;
import com.google.gerrit.client.reviewdb.ProjectRight;
import com.google.gerrit.client.reviewdb.ApprovalCategory.Id;
import com.google.gerrit.client.rpc.Common;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/** State passed through to a {@link CategoryFunction}. */
public class FunctionState {
private final Map<Account.Id, Set<AccountGroup.Id>> groupCache =
new HashMap<Account.Id, Set<AccountGroup.Id>>();
private final Map<ApprovalCategory.Id, Collection<ChangeApproval>> approvals =
new HashMap<ApprovalCategory.Id, Collection<ChangeApproval>>();
private final Map<ApprovalCategory.Id, Boolean> valid =
new HashMap<ApprovalCategory.Id, Boolean>();
private final Change change;
private final ProjectCache.Entry 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>> wildcardRights;
private Set<ChangeApproval> modified;
public FunctionState(final Change c, final Collection<ChangeApproval> all) {
change = c;
project = Common.getProjectCache().get(change.getDest().getParentKey());
for (final ChangeApproval ca : all) {
Collection<ChangeApproval> l = approvals.get(ca.getCategoryId());
if (l == null) {
l = new ArrayList<ChangeApproval>();
approvals.put(ca.getCategoryId(), l);
}
l.add(ca);
}
}
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<ChangeApproval> getApprovals(final ApprovalType at) {
return getApprovals(id(at));
}
public Collection<ChangeApproval> getApprovals(final ApprovalCategory.Id id) {
final Collection<ChangeApproval> l = approvals.get(id);
return l != null ? l : Collections.<ChangeApproval> emptySet();
}
public void dirty(final ChangeApproval ap) {
if (modified == null) {
modified = new HashSet<ChangeApproval>();
}
modified.add(ap);
}
public Collection<ChangeApproval> 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.getRights());
}
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 (wildcardRights == null) {
wildcardRights = index(Common.getProjectCache().getWildcardRights());
}
final Collection<ProjectRight> l = wildcardRights.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;
}
public boolean isMember(final ChangeApproval ca, final ProjectRight r) {
return isMember(ca.getAccountId(), r.getAccountGroupId());
}
public boolean isMember(final Account.Id accountId,
final AccountGroup.Id groupId) {
return getGroups(accountId).contains(groupId);
}
public Set<AccountGroup.Id> getGroups(final Account.Id id) {
Set<AccountGroup.Id> g = groupCache.get(id);
if (g == null) {
g = Common.getGroupCache().getEffectiveGroups(id);
groupCache.put(id, g);
}
return g;
}
/**
* 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 ChangeApproval 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 ChangeApproval a) {
// Find the maximal range actually granted to the user.
//
short minAllowed = 0, maxAllowed = 0;
for (final ProjectRight r : getAllRights(a.getCategoryId())) {
if (isMember(a, r)) {
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 ChangeApproval ca) {
applyTypeFloor(at, ca);
applyRightFloor(ca);
}
private static Id id(final ApprovalType at) {
return at.getCategory().getId();
}
}