Search in sources :

Example 1 with IFormElement

use of org.javarosa.core.model.IFormElement in project collect by opendatakit.

the class FormController method getQuestionPrompts.

/**
 * Returns an array of question promps.
 */
public FormEntryPrompt[] getQuestionPrompts() throws RuntimeException {
    // For questions, there is only one.
    // For groups, there could be many, but we set that below
    FormEntryPrompt[] questions = new FormEntryPrompt[1];
    IFormElement element = formEntryController.getModel().getForm().getChild(getFormIndex());
    if (element instanceof GroupDef) {
        GroupDef gd = (GroupDef) element;
        // we only display relevant questions
        List<FormEntryPrompt> questionList = new ArrayList<>();
        for (FormIndex index : getIndicesForGroup(gd)) {
            if (getEvent(index) != FormEntryController.EVENT_QUESTION) {
                String errorMsg = "Only questions and regular groups are allowed in 'field-list'.  Bad node is: " + index.getReference().toString(false);
                RuntimeException e = new RuntimeException(errorMsg);
                Timber.w(errorMsg);
                throw e;
            }
            // we only display relevant questions
            if (formEntryController.getModel().isIndexRelevant(index)) {
                questionList.add(getQuestionPrompt(index));
            }
            questions = new FormEntryPrompt[questionList.size()];
            questionList.toArray(questions);
        }
    } else {
        // We have a question, so just get the one prompt
        questions[0] = getQuestionPrompt();
    }
    return questions;
}
Also used : IFormElement(org.javarosa.core.model.IFormElement) FormEntryPrompt(org.javarosa.form.api.FormEntryPrompt) ArrayList(java.util.ArrayList) FormIndex(org.javarosa.core.model.FormIndex) GroupDef(org.javarosa.core.model.GroupDef)

Example 2 with IFormElement

use of org.javarosa.core.model.IFormElement in project collect by opendatakit.

the class FormController method groupIsFieldList.

/**
 * A convenience method for determining if the current FormIndex is in a group that is/should
 * be
 * displayed as a multi-question view. This is useful for returning from the formhierarchy view
 * to a selected index.
 */
private boolean groupIsFieldList(FormIndex index) {
    // if this isn't a group, return right away
    IFormElement element = formEntryController.getModel().getForm().getChild(index);
    if (!(element instanceof GroupDef)) {
        return false;
    }
    // exceptions?
    GroupDef gd = (GroupDef) element;
    return (ODKView.FIELD_LIST.equalsIgnoreCase(gd.getAppearanceAttr()));
}
Also used : IFormElement(org.javarosa.core.model.IFormElement) GroupDef(org.javarosa.core.model.GroupDef)

Example 3 with IFormElement

use of org.javarosa.core.model.IFormElement in project javarosa by opendatakit.

the class QuestionDefTest method testChild.

@Test
public void testChild() {
    QuestionDef q = new QuestionDef();
    if (q.getChildren() != null) {
        fail("Question has children");
    }
    try {
        q.setChildren(new ArrayList<IFormElement>());
        fail("Set a question's children without exception");
    } catch (IllegalStateException ise) {
    // expected
    }
    try {
        q.addChild(new QuestionDef());
        fail("Added a child to a question without exception");
    } catch (IllegalStateException ise) {
    // expected
    }
}
Also used : IFormElement(org.javarosa.core.model.IFormElement) QuestionDef(org.javarosa.core.model.QuestionDef) Test(org.junit.Test)

Example 4 with IFormElement

use of org.javarosa.core.model.IFormElement 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;
}
Also used : IDataReference(org.javarosa.core.model.IDataReference) TreeElement(org.javarosa.core.model.instance.TreeElement) AbstractTreeElement(org.javarosa.core.model.instance.AbstractTreeElement) Element(org.kxml2.kdom.Element) IFormElement(org.javarosa.core.model.IFormElement) ArrayList(java.util.ArrayList) XPathReference(org.javarosa.model.xform.XPathReference) TreeReference(org.javarosa.core.model.instance.TreeReference) RangeQuestion(org.javarosa.core.model.RangeQuestion) DataBinding(org.javarosa.core.model.DataBinding) QuestionDef(org.javarosa.core.model.QuestionDef)

Example 5 with IFormElement

use of org.javarosa.core.model.IFormElement 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));
    }
}
Also used : IFormElement(org.javarosa.core.model.IFormElement) TreeReference(org.javarosa.core.model.instance.TreeReference) ArrayList(java.util.ArrayList) Constraint(org.javarosa.core.model.condition.Constraint) GroupDef(org.javarosa.core.model.GroupDef) TreeElement(org.javarosa.core.model.instance.TreeElement)

Aggregations

IFormElement (org.javarosa.core.model.IFormElement)26 GroupDef (org.javarosa.core.model.GroupDef)19 QuestionDef (org.javarosa.core.model.QuestionDef)10 ArrayList (java.util.ArrayList)9 TreeReference (org.javarosa.core.model.instance.TreeReference)8 FormIndex (org.javarosa.core.model.FormIndex)6 TreeElement (org.javarosa.core.model.instance.TreeElement)6 FormEntryPrompt (org.javarosa.form.api.FormEntryPrompt)5 IDataReference (org.javarosa.core.model.IDataReference)4 AbstractTreeElement (org.javarosa.core.model.instance.AbstractTreeElement)3 XPathReference (org.javarosa.model.xform.XPathReference)3 Element (org.kxml2.kdom.Element)3 DataBinding (org.javarosa.core.model.DataBinding)2 Constraint (org.javarosa.core.model.condition.Constraint)2 IAnswerData (org.javarosa.core.model.data.IAnswerData)2 FormEntryModel (org.javarosa.form.api.FormEntryModel)2 Test (org.junit.Test)2 QuestionDetails (org.odk.collect.android.formentry.questions.QuestionDetails)2 MockFormEntryPromptBuilder (org.odk.collect.android.support.MockFormEntryPromptBuilder)2 Activity (android.app.Activity)1