Search in sources :

Example 1 with Triggerable

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");
    }
}
Also used : ArrayList(java.util.ArrayList) Triggerable(org.javarosa.core.model.condition.Triggerable) TreeReference(org.javarosa.core.model.instance.TreeReference) HashSet(java.util.HashSet)

Example 2 with Triggerable

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;
    }
}
Also used : TreeReference(org.javarosa.core.model.instance.TreeReference) Triggerable(org.javarosa.core.model.condition.Triggerable)

Example 3 with Triggerable

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();
}
Also used : Condition(org.javarosa.core.model.condition.Condition) TreeReference(org.javarosa.core.model.instance.TreeReference) ArrayList(java.util.ArrayList) Triggerable(org.javarosa.core.model.condition.Triggerable)

Aggregations

Triggerable (org.javarosa.core.model.condition.Triggerable)3 TreeReference (org.javarosa.core.model.instance.TreeReference)3 ArrayList (java.util.ArrayList)2 HashSet (java.util.HashSet)1 Condition (org.javarosa.core.model.condition.Condition)1