use of org.javarosa.core.model.instance.TreeReference in project javarosa by opendatakit.
the class Safe2014DagImplTest method deleteThirdRepeatGroup_evaluatesTriggerables_dependentOnTheRepeatGroupsNumber.
@Test
public void deleteThirdRepeatGroup_evaluatesTriggerables_dependentOnTheRepeatGroupsNumber() throws Exception {
// Given
final FormDef formDef = parse(r("calculation-dependent-on-the-repeat-groups-number.xml")).formDef;
assertIDagImplUnderTest(formDef);
// trigger all calculations
formDef.initialize(false, new InstanceInitializationFactory());
// it's important to set the test event notifier now to avoid storing events from the above initialization
formDef.setEventNotifier(eventNotifier);
final FormInstance mainInstance = formDef.getMainInstance();
final TreeElement elementToBeDeleted = mainInstance.getRoot().getChildAt(2);
final TreeReference elementToBeDeletedRef = elementToBeDeleted.getRef();
// Index pointing to the second repeat group
final FormIndex indexToBeDeleted = new FormIndex(0, 2, elementToBeDeletedRef);
// When
TreeElement summaryNode = mainInstance.getRoot().getChildrenWithName("summary").get(0);
// check the calculation result for 10 repeat groups
assertThat(summaryNode.getValue().getDisplayText(), equalTo("55"));
// Safe2014DagImplTest.deleteRepeatGroup is called by the below method
formDef.deleteRepeat(indexToBeDeleted);
// Then
final List<TreeElement> repeats = mainInstance.getRoot().getChildrenWithName("houseM");
// check the values based on the position of the parents
assertThat(repeats.get(0).getChildAt(0).getValue().getDisplayText(), equalTo("1"));
assertThat(repeats.get(1).getChildAt(0).getValue().getDisplayText(), equalTo("2"));
assertThat(repeats.get(2).getChildAt(0).getValue().getDisplayText(), equalTo("3"));
assertThat(repeats.get(3).getChildAt(0).getValue().getDisplayText(), equalTo("4"));
assertThat(repeats.get(4).getChildAt(0).getValue().getDisplayText(), equalTo("5"));
assertThat(repeats.get(5).getChildAt(0).getValue().getDisplayText(), equalTo("6"));
assertThat(repeats.get(6).getChildAt(0).getValue().getDisplayText(), equalTo("7"));
assertThat(repeats.get(7).getChildAt(0).getValue().getDisplayText(), equalTo("8"));
assertThat(repeats.get(8).getChildAt(0).getValue().getDisplayText(), equalTo("9"));
assertThat(summaryNode.getValue().getDisplayText(), equalTo("45"));
// check that correct calculations were triggered
final String[] expectedMessages = { "Processing 'Recalculate' for no [3_1] (3.0)", "Processing 'Recalculate' for summary [1] (51.0)", "Processing 'Deleted: houseM [3]: 2 triggerables were fired.' for ", "Processing 'Deleted: no [3_1]: 0 triggerables were fired.' for ", "Processing 'Recalculate' for no [4_1] (4.0)", "Processing 'Recalculate' for summary [1] (50.0)", "Processing 'Deleted: houseM [4]: 2 triggerables were fired.' for ", "Processing 'Recalculate' for no [5_1] (5.0)", "Processing 'Recalculate' for summary [1] (49.0)", "Processing 'Deleted: houseM [5]: 2 triggerables were fired.' for ", "Processing 'Recalculate' for no [6_1] (6.0)", "Processing 'Recalculate' for summary [1] (48.0)", "Processing 'Deleted: houseM [6]: 2 triggerables were fired.' for ", "Processing 'Recalculate' for no [7_1] (7.0)", "Processing 'Recalculate' for summary [1] (47.0)", "Processing 'Deleted: houseM [7]: 2 triggerables were fired.' for ", "Processing 'Recalculate' for no [8_1] (8.0)", "Processing 'Recalculate' for summary [1] (46.0)", "Processing 'Deleted: houseM [8]: 2 triggerables were fired.' for ", "Processing 'Recalculate' for no [9_1] (9.0)", "Processing 'Recalculate' for summary [1] (45.0)", "Processing 'Deleted: houseM [9]: 2 triggerables were fired.' for " };
assertThat(dagEvents.size(), equalTo(expectedMessages.length));
int messageIndex = 0;
for (String expectedMessage : expectedMessages) {
assertThat(dagEvents.get(messageIndex++).getDisplayMessage(), equalTo(expectedMessage));
}
}
use of org.javarosa.core.model.instance.TreeReference in project javarosa by opendatakit.
the class XPathConditional method getTriggers.
private static void getTriggers(XPathExpression x, Set<TreeReference> v, TreeReference contextRef) {
if (x instanceof XPathPathExpr) {
TreeReference ref = ((XPathPathExpr) x).getReference();
TreeReference contextualized = ref;
if (contextRef != null) {
contextualized = ref.contextualize(contextRef);
}
// TODO: It's possible we should just handle this the same way as "genericize". Not entirely clear.
if (contextualized.hasPredicates()) {
contextualized = contextualized.removePredicates();
}
v.add(contextualized);
for (int i = 0; i < ref.size(); i++) {
List<XPathExpression> predicates = ref.getPredicate(i);
if (predicates == null) {
continue;
}
// we can't generate this properly without an absolute reference
if (!ref.isAbsolute()) {
throw new IllegalArgumentException("can't get triggers for relative references");
}
TreeReference predicateContext = ref.getSubReference(i);
for (XPathExpression predicate : predicates) {
getTriggers(predicate, v, predicateContext);
}
}
} else if (x instanceof XPathBinaryOpExpr) {
getTriggers(((XPathBinaryOpExpr) x).a, v, contextRef);
getTriggers(((XPathBinaryOpExpr) x).b, v, contextRef);
} else if (x instanceof XPathUnaryOpExpr) {
getTriggers(((XPathUnaryOpExpr) x).a, v, contextRef);
} else if (x instanceof XPathFuncExpr) {
XPathFuncExpr fx = (XPathFuncExpr) x;
for (int i = 0; i < fx.args.length; i++) getTriggers(fx.args[i], v, contextRef);
}
}
use of org.javarosa.core.model.instance.TreeReference in project javarosa by opendatakit.
the class XFormParser method parseControl.
/**
* Parses a form control element into a {@link org.javarosa.core.model.QuestionDef} and attaches it to its parent.
*
* @param parent the form control element's parent
* @param e the form control element to parse
* @param controlType one of the control types defined in {@link org.javarosa.core.model.Constants}
* @param additionalUsedAtts attributes specific to the control type
* @param passedThroughAtts attributes specific to the control type that should be passed through to
* additionalAttributes for historical reasons
* @return a {@link org.javarosa.core.model.QuestionDef} representing the form control element
*/
private QuestionDef parseControl(IFormElement parent, Element e, int controlType, List<String> additionalUsedAtts, List<String> passedThroughAtts) {
final QuestionDef question = questionForControlType(controlType);
// until we come up with a better scheme
question.setID(serialQuestionID++);
final List<String> usedAtts = new ArrayList<>(Arrays.asList(REF_ATTR, BIND_ATTR, APPEARANCE_ATTR));
if (additionalUsedAtts != null) {
usedAtts.addAll(additionalUsedAtts);
}
IDataReference dataRef = null;
boolean refFromBind = false;
String ref = e.getAttributeValue(null, REF_ATTR);
String bind = e.getAttributeValue(null, BIND_ATTR);
if (bind != null) {
DataBinding binding = bindingsByID.get(bind);
if (binding == null) {
throw new XFormParseException("XForm Parse: invalid binding ID '" + bind + "'", e);
}
dataRef = binding.getReference();
refFromBind = true;
} else if (ref != null) {
try {
dataRef = new XPathReference(ref);
} catch (RuntimeException el) {
Std.out.println(XFormParser.getVagueLocation(e));
throw el;
}
} else {
// noinspection StatementWithEmptyBody
if (controlType == Constants.CONTROL_TRIGGER) {
// TODO: special handling for triggers? also, not all triggers created equal
} else {
throw new XFormParseException("XForm Parse: input control with neither 'ref' nor 'bind'", e);
}
}
if (dataRef != null) {
if (!refFromBind) {
dataRef = getAbsRef(dataRef, parent);
}
question.setBind(dataRef);
if (controlType == Constants.CONTROL_SELECT_ONE) {
selectOnes.add((TreeReference) dataRef.getReference());
} else if (controlType == Constants.CONTROL_SELECT_MULTI) {
selectMultis.add((TreeReference) dataRef.getReference());
}
}
boolean isSelect = (controlType == Constants.CONTROL_SELECT_MULTI || controlType == Constants.CONTROL_SELECT_ONE);
question.setControlType(controlType);
question.setAppearanceAttr(e.getAttributeValue(null, APPEARANCE_ATTR));
for (int i = 0; i < e.getChildCount(); i++) {
int type = e.getType(i);
Element child = (type == Node.ELEMENT ? e.getElement(i) : null);
String childName = (child != null ? child.getName() : null);
if (LABEL_ELEMENT.equals(childName)) {
parseQuestionLabel(question, child);
} else if ("hint".equals(childName)) {
parseHint(question, child);
} else if (isSelect && "item".equals(childName)) {
parseItem(question, child);
} else if (isSelect && "itemset".equals(childName)) {
parseItemset(question, child, parent);
}
}
if (isSelect) {
if (question.getNumChoices() > 0 && question.getDynamicChoices() != null) {
throw new XFormParseException("Select question contains both literal choices and <itemset>");
} else if (question.getNumChoices() == 0 && question.getDynamicChoices() == null) {
throw new XFormParseException("Select question has no choices");
}
}
if (question instanceof RangeQuestion) {
populateQuestionWithRangeAttributes((RangeQuestion) question, e);
}
parent.addChild(question);
processAdditionalAttributes(question, e, usedAtts, passedThroughAtts);
return question;
}
use of org.javarosa.core.model.instance.TreeReference in project javarosa by opendatakit.
the class FormInstanceParser method createMissingTemplates.
// if repeatables have no template node, duplicate first as template
private void createMissingTemplates(FormInstance instance, List<TreeReference> missingTemplates) {
// every ref is listed after a ref that could be its parent. checkRepeatsForTemplate currently behaves this way
for (TreeReference templRef : missingTemplates) {
final TreeReference firstMatch;
// make template ref generic and choose first matching node
final TreeReference ref = templRef.clone();
for (int j = 0; j < ref.size(); j++) {
ref.setMultiplicity(j, TreeReference.INDEX_UNBOUND);
}
final List<TreeReference> nodes = new EvaluationContext(instance).expandReference(ref);
if (nodes.size() == 0) {
// binding error; not a single node matches the repeat binding; will be reported later
continue;
} else {
firstMatch = nodes.get(0);
}
try {
instance.copyNode(firstMatch, templRef);
} catch (InvalidReferenceException e) {
reporter.warning(XFormParserReporter.TYPE_INVALID_STRUCTURE, "Could not create a default repeat template; this is almost certainly a homogeneity error! Your form will not work! (Failed on " + templRef.toString() + ")", null);
}
trimRepeatChildren(instance.resolveReference(templRef));
}
}
use of org.javarosa.core.model.instance.TreeReference 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));
}
}
Aggregations