use of org.javarosa.core.model.instance.TreeElement in project javarosa by opendatakit.
the class FormInstanceParser method trimRepeatChildren.
/**
* Trims repeatable children of newly created template nodes; we trim because the templates are supposed to be devoid of 'data',
* and # of repeats for a given repeat node is a kind of data.
*/
private static void trimRepeatChildren(TreeElement node) {
for (int i = 0; i < node.getNumChildren(); i++) {
TreeElement child = node.getChildAt(i);
if (child.isRepeatable()) {
node.removeChildAt(i);
i--;
} else {
trimRepeatChildren(child);
}
}
}
use of org.javarosa.core.model.instance.TreeElement in project javarosa by opendatakit.
the class FormInstanceParser method verifyItemsetSrcDstCompatibility.
private void verifyItemsetSrcDstCompatibility(FormInstance instance) {
for (ItemsetBinding itemset : itemsets) {
boolean destRepeatable = (instance.getTemplate(itemset.getDestRef()) != null);
if (itemset.copyMode) {
if (!destRepeatable) {
throw new XFormParseException("itemset copies to node(s) which are not repeatable");
}
// validate homogeneity between src and dst nodes
TreeElement srcNode = instance.getTemplatePath(itemset.copyRef);
TreeElement dstNode = instance.getTemplate(itemset.getDestRef());
if (!FormInstance.isHomogeneous(srcNode, dstNode)) {
reporter.warning(XFormParserReporter.TYPE_INVALID_STRUCTURE, "Your itemset source [" + srcNode.getRef().toString() + "] and dest [" + dstNode.getRef().toString() + "] of appear to be incompatible!", null);
}
// TODO: i feel like, in theory, i should additionally check that the repeatable children of src and dst
// match up (Achild is repeatable <--> Bchild is repeatable). isHomogeneous doesn't check this. but i'm
// hard-pressed to think of scenarios where this would actually cause problems
} else {
if (destRepeatable) {
throw new XFormParseException("itemset sets value on repeatable nodes");
}
}
}
}
use of org.javarosa.core.model.instance.TreeElement in project javarosa by opendatakit.
the class FormInstanceParser method verifyRepeatMemberBindings.
private void verifyRepeatMemberBindings(IFormElement fe, FormInstance instance, GroupDef parentRepeat) {
if (fe.getChildren() == null)
return;
for (int i = 0; i < fe.getChildren().size(); i++) {
IFormElement child = fe.getChildren().get(i);
boolean isRepeat = (child instanceof GroupDef && ((GroupDef) child).getRepeat());
// get bindings of current node and nearest enclosing repeat
TreeReference repeatBind = (parentRepeat == null ? TreeReference.rootRef() : FormInstance.unpackReference(parentRepeat.getBind()));
TreeReference childBind = FormInstance.unpackReference(child.getBind());
// check if current binding is within scope of repeat binding
if (!repeatBind.isParentOf(childBind, false)) {
// catch <repeat nodeset="/a/b"><input ref="/a/c" /></repeat>: repeat question is not a child of the repeated node
throw new XFormParseException("<repeat> member's binding [" + childBind.toString() + "] is not a descendant of <repeat> binding [" + repeatBind.toString() + "]!");
} else if (repeatBind.equals(childBind) && isRepeat) {
// catch <repeat nodeset="/a/b"><repeat nodeset="/a/b">...</repeat></repeat> (<repeat nodeset="/a/b"><input ref="/a/b" /></repeat> is ok)
throw new XFormParseException("child <repeat>s [" + childBind.toString() + "] cannot bind to the same node as their parent <repeat>; only questions/groups can");
}
// check that, in the instance, current node is not within the scope of any closer repeat binding
// build a list of all the node's instance ancestors
List<TreeElement> repeatAncestry = new ArrayList<>();
TreeElement repeatNode = (repeatTree == null ? null : repeatTree.getRoot());
if (repeatNode != null) {
repeatAncestry.add(repeatNode);
for (int j = 1; j < childBind.size(); j++) {
repeatNode = repeatNode.getChild(childBind.getName(j), 0);
if (repeatNode != null) {
repeatAncestry.add(repeatNode);
} else {
break;
}
}
}
// check that no nodes between the parent repeat and the target are repeatable
for (int k = repeatBind.size(); k < childBind.size(); k++) {
TreeElement rChild = (k < repeatAncestry.size() ? repeatAncestry.get(k) : null);
boolean repeatable = rChild != null && rChild.isRepeatable();
if (repeatable && !(k == childBind.size() - 1 && isRepeat)) {
// question's/group's/repeat's most immediate repeat parent in the instance is not its most immediate repeat parent in the form def
throw new XFormParseException("<repeat> member's binding [" + childBind.toString() + "] is within the scope of a <repeat> that is not its closest containing <repeat>!");
}
}
verifyRepeatMemberBindings(child, instance, (isRepeat ? (GroupDef) child : parentRepeat));
}
}
use of org.javarosa.core.model.instance.TreeElement in project javarosa by opendatakit.
the class FormInstanceParser method buildRepeatTree.
/**
* Builds a pseudo-data model tree that describes the repeat structure of the instance. The
* result is a FormInstance collapsed where all indexes are 0, and repeatable nodes are flagged as such.
* Ignores (invalid) repeats that bind outside the top-level instance data node. Returns null if no repeats.
*/
private static FormInstance buildRepeatTree(List<TreeReference> repeatRefs, String topLevelName) {
TreeElement root = new TreeElement(null, 0);
for (TreeReference repeatRef : repeatRefs) {
// check and see if this references a repeat from a non-main instance, if so, skip it
if (repeatRef.getInstanceName() != null) {
continue;
}
if (repeatRef.size() <= 1) {
// invalid repeat: binds too high. ignore for now and error will be raised in verifyBindings
continue;
}
TreeElement cur = root;
for (int j = 0; j < repeatRef.size(); j++) {
String name = repeatRef.getName(j);
TreeElement child = cur.getChild(name, 0);
if (child == null) {
child = new TreeElement(name, 0);
cur.addChild(child);
}
cur = child;
}
cur.setRepeatable(true);
}
return (root.getNumChildren() == 0) ? null : new FormInstance(root.getChild(topLevelName, TreeReference.DEFAULT_MULTIPLICITY));
}
use of org.javarosa.core.model.instance.TreeElement in project javarosa by opendatakit.
the class FormEntryModel method isIndexRelevant.
/**
* Determine if the current FormIndex is relevant. Only relevant indexes
* should be returned when filling out a form.
*
* @param index
* @return true if current element at FormIndex is relevant
*/
public boolean isIndexRelevant(FormIndex index) {
TreeReference ref = form.getChildInstanceRef(index);
boolean isAskNewRepeat = (getEvent(index) == FormEntryController.EVENT_PROMPT_NEW_REPEAT);
boolean isRepeatJuncture = (getEvent(index) == FormEntryController.EVENT_REPEAT_JUNCTURE);
boolean relevant;
if (isAskNewRepeat) {
relevant = form.isRepeatRelevant(ref) && form.canCreateRepeat(ref, index);
// repeat junctures are still relevant if no new repeat can be created; that option
// is simply missing from the menu
} else if (isRepeatJuncture) {
relevant = form.isRepeatRelevant(ref);
} else {
TreeElement node = form.getMainInstance().resolveReference(ref);
// check instance flag first
relevant = node != null && node.isRelevant();
}
if (relevant) {
// if instance flag/condition says relevant, we still
// have to check the <group>/<repeat> hierarchy
FormIndex ancestorIndex = index;
while (!ancestorIndex.isTerminal()) {
// This should be safe now that the TreeReference is contained
// in the ancestor index itself
TreeElement ancestorNode = form.getMainInstance().resolveReference(ancestorIndex.getLocalReference());
if (!ancestorNode.isRelevant()) {
relevant = false;
break;
}
ancestorIndex = ancestorIndex.getNextLevel();
}
}
return relevant;
}
Aggregations