} catch (OrmException err) {
return logRuleError("Cannot read patch set " + patchSet.getId(), err);
}
List<Term> results = new ArrayList<Term>();
Term submitRule;
ProjectState projectState = getProjectControl().getProjectState();
PrologEnvironment env;
try {
env = projectState.newPrologEnvironment();
} catch (CompileException err) {
return logRuleError("Cannot consult rules.pl for "
+ getProject().getName(), err);
}
try {
env.set(StoredValues.REVIEW_DB, db);
env.set(StoredValues.CHANGE, change);
env.set(StoredValues.CHANGE_DATA, cd);
env.set(StoredValues.PATCH_SET, patchSet);
env.set(StoredValues.CHANGE_CONTROL, this);
submitRule = env.once(
"gerrit", "locate_submit_rule",
new VariableTerm());
if (submitRule == null) {
return logRuleError("No user:submit_rule found for "
+ getProject().getName());
}
if (fastEvalLabels) {
env.once("gerrit", "assume_range_from_label");
}
try {
for (Term[] template : env.all(
"gerrit", "can_submit",
submitRule,
new VariableTerm())) {
results.add(template[1]);
}
} catch (PrologException err) {
return logRuleError("Exception calling " + submitRule + " on change "
+ change.getId() + " of " + getProject().getName(), err);
} catch (RuntimeException err) {
return logRuleError("Exception calling " + submitRule + " on change "
+ change.getId() + " of " + getProject().getName(), err);
}
ProjectState parentState = projectState.getParentState();
PrologEnvironment childEnv = env;
Set<Project.NameKey> projectsSeen = new HashSet<Project.NameKey>();
projectsSeen.add(getProject().getNameKey());
while (parentState != null) {
if (!projectsSeen.add(parentState.getProject().getNameKey())) {
//parent has been seen before, stop walk up inheritance tree
break;
}
PrologEnvironment parentEnv;
try {
parentEnv = parentState.newPrologEnvironment();
} catch (CompileException err) {
return logRuleError("Cannot consult rules.pl for "
+ parentState.getProject().getName(), err);
}
parentEnv.copyStoredValues(childEnv);
Term filterRule =
parentEnv.once("gerrit", "locate_submit_filter", new VariableTerm());
if (filterRule != null) {
try {
if (fastEvalLabels) {
env.once("gerrit", "assume_range_from_label");
}
Term resultsTerm = toListTerm(results);
results.clear();
Term[] template = parentEnv.once(
"gerrit", "filter_submit_results",
filterRule,
resultsTerm,
new VariableTerm());
@SuppressWarnings("unchecked")
final List<? extends Term> termList = ((ListTerm) template[2]).toJava();
results.addAll(termList);
} catch (PrologException err) {
return logRuleError("Exception calling " + filterRule + " on change "
+ change.getId() + " of " + parentState.getProject().getName(), err);
} catch (RuntimeException err) {
return logRuleError("Exception calling " + filterRule + " on change "
+ change.getId() + " of " + parentState.getProject().getName(), err);
}
}
parentState = parentState.getParentState();
childEnv = parentEnv;
}
} finally {
env.close();
}
if (results.isEmpty()) {
// This should never occur. A well written submit rule will always produce
// at least one result informing the caller of the labels that are
// required for this change to be submittable. Each label will indicate
// whether or not that is actually possible given the permissions.
log.error("Submit rule " + submitRule + " for change " + change.getId()
+ " of " + getProject().getName() + " has no solution.");
return ruleError("Project submit rule has no solution");
}
// Convert the results from Prolog Cafe's format to Gerrit's common format.
// can_submit/1 terminates when an ok(P) record is found. Therefore walk
// the results backwards, using only that ok(P) record if it exists. This
// skips partial results that occur early in the output. Later after the loop
// the out collection is reversed to restore it to the original ordering.
//
List<SubmitRecord> out = new ArrayList<SubmitRecord>(results.size());
for (int resultIdx = results.size() - 1; 0 <= resultIdx; resultIdx--) {
Term submitRecord = results.get(resultIdx);
SubmitRecord rec = new SubmitRecord();
out.add(rec);
if (!submitRecord.isStructure() || 1 != submitRecord.arity()) {
return logInvalidResult(submitRule, submitRecord);
}
if ("ok".equals(submitRecord.name())) {
rec.status = SubmitRecord.Status.OK;
} else if ("not_ready".equals(submitRecord.name())) {
rec.status = SubmitRecord.Status.NOT_READY;
} else {
return logInvalidResult(submitRule, submitRecord);
}
// Unpack the one argument. This should also be a structure with one
// argument per label that needs to be reported on to the caller.
//
submitRecord = submitRecord.arg(0);
if (!submitRecord.isStructure()) {
return logInvalidResult(submitRule, submitRecord);
}
rec.labels = new ArrayList<SubmitRecord.Label> (submitRecord.arity());
for (Term state : ((StructureTerm) submitRecord).args()) {
if (!state.isStructure() || 2 != state.arity() || !"label".equals(state.name())) {
return logInvalidResult(submitRule, submitRecord);
}
SubmitRecord.Label lbl = new SubmitRecord.Label();
rec.labels.add(lbl);
lbl.label = state.arg(0).name();
Term status = state.arg(1);
if ("ok".equals(status.name())) {
lbl.status = SubmitRecord.Label.Status.OK;
appliedBy(lbl, status);
} else if ("reject".equals(status.name())) {
lbl.status = SubmitRecord.Label.Status.REJECT;
appliedBy(lbl, status);
} else if ("need".equals(status.name())) {
lbl.status = SubmitRecord.Label.Status.NEED;
} else if ("may".equals(status.name())) {
lbl.status = SubmitRecord.Label.Status.MAY;
} else if ("impossible".equals(status.name())) {
lbl.status = SubmitRecord.Label.Status.IMPOSSIBLE;
} else {
return logInvalidResult(submitRule, submitRecord);
}