in src/main/java/org/apache/commons/scxml2/semantics/SCXMLSemanticsImpl.java [763:813]
public boolean isLegalConfiguration(final Set<EnterableState> states, final ErrorReporter errRep) {
/*
* For every active state we add 1 to the count of its parent. Each
* Parallel should reach count equal to the number of its children and
* contribute by 1 to its parent. Each State should reach count exactly
* 1. SCXML elemnt (top) should reach count exactly 1. We essentially
* summarize up the hierarchy tree starting with a given set of
* states = active configuration.
*/
boolean legalConfig = true; // let's be optimists
final Map<EnterableState, Set<EnterableState>> counts = new HashMap<>();
final Set<EnterableState> scxmlCount = new HashSet<>();
for (EnterableState es : states) {
EnterableState parent;
while ((parent = es.getParent()) != null) {
final Set<EnterableState> cnt = counts.computeIfAbsent(parent, k -> new HashSet<>());
cnt.add(es);
es = parent;
}
//top-level contribution
scxmlCount.add(es);
}
if (scxmlCount.size() > 1) {
errRep.onError(ErrorConstants.ILLEGAL_CONFIG, "Multiple top-level OR states active!", scxmlCount);
legalConfig = false;
}
else {
//Validate child counts:
for (final Map.Entry<EnterableState, Set<EnterableState>> entry : counts.entrySet()) {
final EnterableState es = entry.getKey();
final Set<EnterableState> count = entry.getValue();
if (es instanceof Parallel) {
final Parallel p = (Parallel) es;
if (count.size() < p.getChildren().size()) {
errRep.onError(ErrorConstants.ILLEGAL_CONFIG, "Not all AND states active for parallel " + p.getId(), entry);
legalConfig = false;
}
} else {
if (count.size() > 1) {
errRep.onError(ErrorConstants.ILLEGAL_CONFIG, "Multiple OR states active for state " + es.getId(), entry);
legalConfig = false;
}
}
count.clear(); //cleanup
}
}
//cleanup
scxmlCount.clear();
counts.clear();
return legalConfig;
}