use of org.javarosa.core.model.condition.Triggerable in project javarosa by opendatakit.
the class IDag method reportDependencyCycles.
public final void reportDependencyCycles(XFormParserReporter reporter) {
HashSet<TreeReference> vertices = new HashSet<TreeReference>();
ArrayList<TreeReference[]> edges = new ArrayList<TreeReference[]>();
// build graph
ArrayList<TreeReference> targets = new ArrayList<TreeReference>();
for (TreeReference trigger : triggerIndex.keySet()) {
if (!vertices.contains(trigger))
vertices.add(trigger);
ArrayList<QuickTriggerable> triggered = triggerIndex.get(trigger);
targets.clear();
for (QuickTriggerable qt : triggered) {
Triggerable t = qt.t;
for (int j = 0; j < t.getTargets().size(); j++) {
TreeReference target = t.getTargets().get(j);
if (!targets.contains(target))
targets.add(target);
}
}
for (int i = 0; i < targets.size(); i++) {
TreeReference target = targets.get(i);
if (!vertices.contains(target))
vertices.add(target);
TreeReference[] edge = { trigger, target };
edges.add(edge);
}
}
// find cycles
boolean acyclic = true;
HashSet<TreeReference> leaves = new HashSet<TreeReference>(vertices.size());
while (vertices.size() > 0) {
// determine leaf nodes
leaves.clear();
leaves.addAll(vertices);
for (int i = 0; i < edges.size(); i++) {
TreeReference[] edge = (TreeReference[]) edges.get(i);
leaves.remove(edge[0]);
}
// if no leaf nodes while graph still has nodes, graph has cycles
if (leaves.size() == 0) {
acyclic = false;
break;
}
// remove leaf nodes and edges pointing to them
for (TreeReference leaf : leaves) {
vertices.remove(leaf);
}
for (int i = edges.size() - 1; i >= 0; i--) {
TreeReference[] edge = (TreeReference[]) edges.get(i);
if (leaves.contains(edge[1]))
edges.remove(i);
}
}
if (!acyclic) {
StringBuilder b = new StringBuilder();
b.append("XPath Dependency Cycle:\n");
for (int i = 0; i < edges.size(); i++) {
TreeReference[] edge = edges.get(i);
b.append(edge[0].toString()).append(" => ").append(edge[1].toString()).append("\n");
}
reporter.error(b.toString());
throw new RuntimeException("Dependency cycles amongst the xpath expressions in relevant/calculate");
}
}
use of org.javarosa.core.model.condition.Triggerable in project javarosa by opendatakit.
the class IDag method addTriggerable.
/**
* Add the triggerables to the dataset prior to finalizing.
*
* @param t
*/
public final Triggerable addTriggerable(Triggerable t) {
QuickTriggerable qt = findTriggerable(t);
if (qt != null) {
// one node may control access to many nodes; this means many nodes
// effectively have the same condition
// let's identify when conditions are the same, and store and calculate
// it only once
// nov-2-2011: ctsims - We need to merge the context nodes together
// whenever we do this (finding the highest
// common ground between the two), otherwise we can end up failing to
// trigger when the ignored context
// exists and the used one doesn't
Triggerable existingTriggerable = qt.t;
existingTriggerable.changeContextRefToIntersectWithTriggerable(t);
return existingTriggerable;
// note, if the contextRef is unnecessarily deep, the condition will be
// evaluated more times than needed
// perhaps detect when 'identical' condition has a shorter contextRef,
// and use that one instead?
} else {
qt = new QuickTriggerable(t);
unorderedTriggerables.add(qt);
Set<TreeReference> triggers = t.getTriggers();
for (TreeReference trigger : triggers) {
ArrayList<QuickTriggerable> triggered = triggerIndex.get(trigger);
if (triggered == null) {
triggered = new ArrayList<QuickTriggerable>();
triggerIndex.put(trigger.clone(), triggered);
}
if (!triggered.contains(qt)) {
triggered.add(qt);
}
}
return t;
}
}
use of org.javarosa.core.model.condition.Triggerable in project javarosa by opendatakit.
the class LegacyDagImpl method finalizeTriggerables.
/**
* Finalize the DAG associated with the form's triggered conditions. This
* will create the appropriate ordering and dependencies to ensure the
* conditions will be evaluated in the appropriate orders.
*
* @throws IllegalStateException
* - If the trigger ordering contains an illegal cycle and the
* triggers can't be laid out appropriately
*/
@Override
public void finalizeTriggerables(FormInstance mainInstance, EvaluationContext evalContext) throws IllegalStateException {
//
// DAGify the triggerables based on dependencies and sort them so that
// triggerables come only after the triggerables they depend on
//
triggerablesDAG.clear();
//
// DAGify the triggerables based on dependencies and sort them so that
// trigbles come only after the trigbles they depend on
//
ArrayList<QuickTriggerable[]> partialOrdering = new ArrayList<QuickTriggerable[]>();
for (int i = 0; i < unorderedTriggerables.size(); i++) {
QuickTriggerable qt = unorderedTriggerables.get(i);
Triggerable t = qt.t;
ArrayList<QuickTriggerable> deps = new ArrayList<QuickTriggerable>();
if (t.canCascade()) {
for (int j = 0; j < t.getTargets().size(); j++) {
TreeReference target = t.getTargets().get(j);
ArrayList<QuickTriggerable> triggered = triggerIndex.get(target);
if (triggered != null) {
for (int k = 0; k < triggered.size(); k++) {
QuickTriggerable qu = triggered.get(k);
if (!deps.contains(qu))
deps.add(qu);
}
}
}
}
for (int j = 0; j < deps.size(); j++) {
QuickTriggerable qu = deps.get(j);
QuickTriggerable[] edge = { qt, qu };
partialOrdering.add(edge);
}
}
ArrayList<QuickTriggerable> vertices = new ArrayList<QuickTriggerable>(unorderedTriggerables);
ArrayList<QuickTriggerable> roots = new ArrayList<QuickTriggerable>(unorderedTriggerables.size());
while (vertices.size() > 0) {
// determine root nodes
roots.clear();
roots.addAll(vertices);
for (int i = 0; i < partialOrdering.size(); i++) {
QuickTriggerable[] edge = partialOrdering.get(i);
roots.remove(edge[1]);
}
// if no root nodes while graph still has nodes, graph has cycles
if (roots.size() == 0) {
throw new RuntimeException("Cannot create partial ordering of triggerables due to dependency cycle. Why wasn't this caught during parsing?");
}
// remove root nodes and edges originating from them
for (int i = 0; i < roots.size(); i++) {
QuickTriggerable root = roots.get(i);
triggerablesDAG.add(root);
vertices.remove(root);
}
for (int i = partialOrdering.size() - 1; i >= 0; i--) {
QuickTriggerable[] edge = partialOrdering.get(i);
if (roots.contains(edge[0]))
partialOrdering.remove(i);
}
}
//
// build the condition index for repeatable nodes
//
conditionRepeatTargetIndex.clear();
for (QuickTriggerable qt : triggerablesDAG) {
if (qt.t instanceof Condition) {
List<TreeReference> targets = qt.t.getTargets();
for (TreeReference target : targets) {
if (mainInstance.getTemplate(target) != null) {
conditionRepeatTargetIndex.put(target, qt);
}
}
}
}
// printTriggerables();
}
Aggregations