Search in sources :

Example 6 with IDataReference

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

the class XFormParser method parseSubmission.

private void parseSubmission(Element submission) {
    String id = submission.getAttributeValue(null, ID_ATTR);
    // These two are always required
    String method = submission.getAttributeValue(null, "method");
    String action = submission.getAttributeValue(null, "action");
    SubmissionParser parser = new SubmissionParser();
    for (SubmissionParser p : submissionParsers) {
        if (p.matchesCustomMethod(method)) {
            parser = p;
        }
    }
    // These two might exist, but if neither do, we just assume you want the entire instance.
    String ref = submission.getAttributeValue(null, REF_ATTR);
    String bind = submission.getAttributeValue(null, BIND_ATTR);
    IDataReference dataRef = null;
    boolean refFromBind = false;
    if (bind != null) {
        DataBinding binding = bindingsByID.get(bind);
        if (binding == null) {
            throw new XFormParseException("XForm Parse: invalid binding ID in submit'" + bind + "'", submission);
        }
        dataRef = binding.getReference();
        refFromBind = true;
    } else if (ref != null) {
        dataRef = new XPathReference(ref);
    } else {
        // no reference! No big deal, assume we want the root reference
        dataRef = new XPathReference("/");
    }
    if (dataRef != null) {
        if (!refFromBind) {
            dataRef = FormDef.getAbsRef(dataRef, TreeReference.rootRef());
        }
    }
    SubmissionProfile profile = parser.parseSubmission(method, action, dataRef, submission);
    if (id == null) {
        // default submission profile
        _f.setDefaultSubmission(profile);
    } else {
        // typed submission profile
        _f.addSubmissionProfile(id, profile);
    }
}
Also used : IDataReference(org.javarosa.core.model.IDataReference) DataBinding(org.javarosa.core.model.DataBinding) SubmissionProfile(org.javarosa.core.model.SubmissionProfile) XPathReference(org.javarosa.model.xform.XPathReference)

Example 7 with IDataReference

use of org.javarosa.core.model.IDataReference 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 8 with IDataReference

use of org.javarosa.core.model.IDataReference in project briefcase by opendatakit.

the class BaseFormParserForJavaRosa method compareXml.

/**
 * Compare two XML files to assess their level of structural difference (if
 * any).
 *
 * @param incomingParser -- parsed version of incoming form
 * @param existingXml    -- the existing Xml for this form
 * @return XFORMS_SHARE_INSTANCE when bodies differ but instances and bindings
 *     are identical; XFORMS_SHARE_SCHEMA when bodies and/or bindings
 *     differ, but database structure remains unchanged; XFORMS_DIFFERENT
 *     when forms are different enough to affect database structure and/or
 *     encryption.
 * @throws ODKIncompleteSubmissionData
 */
public static DifferenceResult compareXml(BaseFormParserForJavaRosa incomingParser, String existingXml, String existingTitle, boolean isWithinUpdateWindow) throws ODKIncompleteSubmissionData {
    if (incomingParser == null || existingXml == null) {
        throw new ODKIncompleteSubmissionData(Reason.MISSING_XML);
    }
    // generally only the case within Briefcase
    if (incomingParser.xml.equals(existingXml)) {
        return DifferenceResult.XFORMS_IDENTICAL;
    }
    // parse XML
    FormDef formDef1;
    FormDef formDef2;
    BaseFormParserForJavaRosa existingParser = new BaseFormParserForJavaRosa(existingXml, existingTitle, true);
    formDef1 = incomingParser.rootJavaRosaFormDef;
    formDef2 = existingParser.rootJavaRosaFormDef;
    if (formDef1 == null || formDef2 == null) {
        throw new ODKIncompleteSubmissionData("Javarosa failed to construct a FormDef.  Is this an XForm definition?", Reason.BAD_JR_PARSE);
    }
    // check that the version is advancing from the earlier
    // form upload. The comparison is string-based, not
    // numeric-based (OpenRosa compliance). The recommended
    // version format is: yyyymmddnn e.g., 2012060100
    String ivs = incomingParser.rootElementDefn.versionString;
    if (ivs == null) {
        // if we are changing the file, the new file must have a version string
        return DifferenceResult.XFORMS_MISSING_VERSION;
    }
    String evs = existingParser.rootElementDefn.versionString;
    boolean modelVersionSame = (incomingParser.rootElementDefn.modelVersion == null) ? (existingParser.rootElementDefn.modelVersion == null) : incomingParser.rootElementDefn.modelVersion.equals(existingParser.rootElementDefn.modelVersion);
    boolean isEarlierVersion = false;
    if (!(evs == null || (modelVersionSame && ivs.length() > evs.length()) || (!modelVersionSame && ivs.compareTo(evs) > 0))) {
        // disallow updates if none of the following applies:
        // (1) if the existing form does not have a version (the new one does).
        // (2) if the existing form and new form have the same model version
        // and the new form has more leading zeros.
        // (3) if the existing form and new form have different model versions
        // and the new version string is lexically greater than the old one.
        isEarlierVersion = true;
        return DifferenceResult.XFORMS_EARLIER_VERSION;
    }
    /*
     * Changes in encryption (either on or off, or change in key) are a major
     * change. We could allow the public key to be revised, but most users won't
     * understand that this is possible or know how to do it.
     *
     * Ignore whether a submission profile is present or absent provided it does
     * not affect encryption or change the portion of the form being returned.
     */
    SubmissionProfile subProfile1 = formDef1.getSubmissionProfile();
    SubmissionProfile subProfile2 = formDef2.getSubmissionProfile();
    if (subProfile1 != null && subProfile2 != null) {
        // we have two profiles -- check that any encryption key matches...
        String publicKey1 = subProfile1.getAttribute(BASE64_RSA_PUBLIC_KEY);
        String publicKey2 = subProfile2.getAttribute(BASE64_RSA_PUBLIC_KEY);
        if (publicKey1 != null && publicKey2 != null) {
            // both have encryption
            if (!publicKey1.equals(publicKey2)) {
                // keys differ
                return (DifferenceResult.XFORMS_DIFFERENT);
            }
        } else if (publicKey1 != null || publicKey2 != null) {
            // one or the other has encryption (and the other doesn't)...
            return (DifferenceResult.XFORMS_DIFFERENT);
        }
        // get the TreeElement (e1, e2) that identifies the portion of the form
        // that will be submitted to Aggregate.
        IDataReference r;
        r = subProfile1.getRef();
        AbstractTreeElement<?> e1 = (r != null) ? formDef1.getInstance().resolveReference(r) : null;
        r = subProfile2.getRef();
        AbstractTreeElement<?> e2 = (r != null) ? formDef2.getInstance().resolveReference(r) : null;
        if (e1 != null && e2 != null) {
            // Ignore all namespace differences (Aggregate ignores them)...
            while (e1 != null && e2 != null) {
                if (!e1.getName().equals(e2.getName())) {
                    return (DifferenceResult.XFORMS_DIFFERENT);
                }
                e1 = e1.getParent();
                e2 = e2.getParent();
            }
            if (e1 != null || e2 != null) {
                // they should both terminate at the same time...
                return (DifferenceResult.XFORMS_DIFFERENT);
            }
        // we may still have differences, but if the overall form
        // is identical, we are golden...
        } else if (e1 != null || e2 != null) {
            // one returns a portion of the form and the other doesn't
            return (DifferenceResult.XFORMS_DIFFERENT);
        }
    } else if (subProfile1 != null) {
        if (subProfile1.getAttribute(BASE64_RSA_PUBLIC_KEY) != null) {
            // xml1 does encryption, the other doesn't
            return (DifferenceResult.XFORMS_DIFFERENT);
        }
        IDataReference r = subProfile1.getRef();
        if (r != null && formDef1.getInstance().resolveReference(r) != null) {
            // xml1 returns a portion of the form, the other doesn't
            return (DifferenceResult.XFORMS_DIFFERENT);
        }
    } else if (subProfile2 != null) {
        if (subProfile2.getAttribute(BASE64_RSA_PUBLIC_KEY) != null) {
            // xml2 does encryption, the other doesn't
            return (DifferenceResult.XFORMS_DIFFERENT);
        }
        IDataReference r = subProfile2.getRef();
        if (r != null && formDef2.getInstance().resolveReference(r) != null) {
            // xml2 returns a portion of the form, the other doesn't
            return (DifferenceResult.XFORMS_DIFFERENT);
        }
    }
    // get data model to compare instances
    FormInstance dataModel1 = formDef1.getInstance();
    FormInstance dataModel2 = formDef2.getInstance();
    if (dataModel1 == null || dataModel2 == null) {
        throw new ODKIncompleteSubmissionData("Javarosa failed to construct a FormInstance.  Is this an XForm definition?", Reason.BAD_JR_PARSE);
    }
    // return result of element-by-element instance/binding comparison
    DifferenceResult rc = compareTreeElements(dataModel1.getRoot(), incomingParser, dataModel2.getRoot(), existingParser);
    if (DifferenceResult.XFORMS_DIFFERENT == rc) {
        return rc;
    } else if (isEarlierVersion) {
        return DifferenceResult.XFORMS_EARLIER_VERSION;
    } else {
        return rc;
    }
}
Also used : FormDef(org.javarosa.core.model.FormDef) IDataReference(org.javarosa.core.model.IDataReference) ODKIncompleteSubmissionData(org.opendatakit.aggregate.exception.ODKIncompleteSubmissionData) SubmissionProfile(org.javarosa.core.model.SubmissionProfile) FormInstance(org.javarosa.core.model.instance.FormInstance)

Example 9 with IDataReference

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

the class StandardBindAttributesProcessor method createBinding.

DataBinding createBinding(IXFormParserFunctions parserFunctions, FormDef formDef, Collection<String> usedAttributes, Collection<String> passedThroughAttributes, Element element) {
    final DataBinding binding = new DataBinding();
    binding.setId(element.getAttributeValue("", ID_ATTR));
    final String nodeset = element.getAttributeValue(null, NODESET_ATTR);
    if (nodeset == null) {
        throw new XFormParseException("XForm Parse: <bind> without nodeset", element);
    }
    IDataReference ref;
    try {
        ref = new XPathReference(nodeset);
    } catch (XPathException xpe) {
        throw new XFormParseException(xpe.getMessage());
    }
    ref = parserFunctions.getAbsRef(ref, formDef);
    binding.setReference(ref);
    binding.setDataType(getDataType(element.getAttributeValue(null, "type")));
    String xpathRel = element.getAttributeValue(null, "relevant");
    if (xpathRel != null) {
        if ("true()".equals(xpathRel)) {
            binding.relevantAbsolute = true;
        } else if ("false()".equals(xpathRel)) {
            binding.relevantAbsolute = false;
        } else {
            Condition c = buildCondition(xpathRel, "relevant", ref);
            c = (Condition) formDef.addTriggerable(c);
            binding.relevancyCondition = c;
        }
    }
    String xpathReq = element.getAttributeValue(null, "required");
    if (xpathReq != null) {
        if ("true()".equals(xpathReq)) {
            binding.requiredAbsolute = true;
        } else if ("false()".equals(xpathReq)) {
            binding.requiredAbsolute = false;
        } else {
            Condition c = buildCondition(xpathReq, "required", ref);
            c = (Condition) formDef.addTriggerable(c);
            binding.requiredCondition = c;
        }
    }
    String xpathRO = element.getAttributeValue(null, "readonly");
    if (xpathRO != null) {
        if ("true()".equals(xpathRO)) {
            binding.readonlyAbsolute = true;
        } else if ("false()".equals(xpathRO)) {
            binding.readonlyAbsolute = false;
        } else {
            Condition c = buildCondition(xpathRO, "readonly", ref);
            c = (Condition) formDef.addTriggerable(c);
            binding.readonlyCondition = c;
        }
    }
    final String xpathConstr = element.getAttributeValue(null, "constraint");
    if (xpathConstr != null) {
        try {
            binding.constraint = new XPathConditional(xpathConstr);
        } catch (XPathSyntaxException xse) {
            throw new XFormParseException("bind for " + nodeset + " contains invalid constraint expression [" + xpathConstr + "] " + xse.getMessage());
        }
        binding.constraintMessage = element.getAttributeValue(NAMESPACE_JAVAROSA, "constraintMsg");
    }
    final String xpathCalc = element.getAttributeValue(null, "calculate");
    if (xpathCalc != null) {
        Recalculate r;
        try {
            r = buildCalculate(xpathCalc, ref);
        } catch (XPathSyntaxException xpse) {
            throw new XFormParseException("Invalid calculate for the bind attached to \"" + nodeset + "\" : " + xpse.getMessage() + " in expression " + xpathCalc);
        }
        r = (Recalculate) formDef.addTriggerable(r);
        binding.calculate = r;
    }
    binding.setPreload(element.getAttributeValue(NAMESPACE_JAVAROSA, "preload"));
    binding.setPreloadParams(element.getAttributeValue(NAMESPACE_JAVAROSA, "preloadParams"));
    saveUnusedAttributes(binding, element, usedAttributes, passedThroughAttributes);
    return binding;
}
Also used : Condition(org.javarosa.core.model.condition.Condition) XPathSyntaxException(org.javarosa.xpath.parser.XPathSyntaxException) Recalculate(org.javarosa.core.model.condition.Recalculate) XPathException(org.javarosa.xpath.XPathException) IDataReference(org.javarosa.core.model.IDataReference) DataBinding(org.javarosa.core.model.DataBinding) XPathConditional(org.javarosa.xpath.XPathConditional) XPathReference(org.javarosa.model.xform.XPathReference)

Example 10 with IDataReference

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

the class XFormParser method parseSetValueAction.

private void parseSetValueAction(FormDef form, Element e) {
    String ref = e.getAttributeValue(null, REF_ATTR);
    String bind = e.getAttributeValue(null, BIND_ATTR);
    String event = e.getAttributeValue(null, "event");
    IDataReference dataRef = null;
    boolean refFromBind = false;
    // TODO: There is a _lot_ of duplication of this code, fix that!
    if (bind != null) {
        DataBinding binding = bindingsByID.get(bind);
        if (binding == null) {
            throw new XFormParseException("XForm Parse: invalid binding ID in submit'" + bind + "'", e);
        }
        dataRef = binding.getReference();
        refFromBind = true;
    } else if (ref != null) {
        dataRef = new XPathReference(ref);
    } else {
        throw new XFormParseException("setvalue action with no target!", e);
    }
    if (dataRef != null) {
        if (!refFromBind) {
            dataRef = FormDef.getAbsRef(dataRef, TreeReference.rootRef());
        }
    }
    String valueRef = e.getAttributeValue(null, "value");
    Action action;
    TreeReference treeref = FormInstance.unpackReference(dataRef);
    actionTargets.add(treeref);
    if (valueRef == null) {
        if (e.getChildCount() == 0 || !e.isText(0)) {
            throw new XFormParseException("No 'value' attribute and no inner value set in <setvalue> associated with: " + treeref, e);
        }
        // Set expression
        action = new SetValueAction(treeref, e.getText(0));
    } else {
        try {
            action = new SetValueAction(treeref, XPathParseTool.parseXPath(valueRef));
        } catch (XPathSyntaxException e1) {
            Std.printStack(e1);
            throw new XFormParseException("Invalid XPath in value set action declaration: '" + valueRef + "'", e);
        }
    }
    form.registerEventListener(event, action);
}
Also used : Action(org.javarosa.core.model.Action) SetValueAction(org.javarosa.core.model.actions.SetValueAction) XPathSyntaxException(org.javarosa.xpath.parser.XPathSyntaxException) IDataReference(org.javarosa.core.model.IDataReference) TreeReference(org.javarosa.core.model.instance.TreeReference) DataBinding(org.javarosa.core.model.DataBinding) SetValueAction(org.javarosa.core.model.actions.SetValueAction) XPathReference(org.javarosa.model.xform.XPathReference)

Aggregations

IDataReference (org.javarosa.core.model.IDataReference)15 XPathReference (org.javarosa.model.xform.XPathReference)6 DataBinding (org.javarosa.core.model.DataBinding)5 TreeElement (org.javarosa.core.model.instance.TreeElement)5 IFormElement (org.javarosa.core.model.IFormElement)4 SubmissionProfile (org.javarosa.core.model.SubmissionProfile)4 AbstractTreeElement (org.javarosa.core.model.instance.AbstractTreeElement)4 TreeReference (org.javarosa.core.model.instance.TreeReference)4 GroupDef (org.javarosa.core.model.GroupDef)3 QuestionDef (org.javarosa.core.model.QuestionDef)3 StringData (org.javarosa.core.model.data.StringData)3 Test (org.junit.Test)3 Element (org.kxml2.kdom.Element)3 DataInputStream (java.io.DataInputStream)2 DataOutputStream (java.io.DataOutputStream)2 IOException (java.io.IOException)2 ArrayList (java.util.ArrayList)2 FormDef (org.javarosa.core.model.FormDef)2 IntegerData (org.javarosa.core.model.data.IntegerData)2 DeserializationException (org.javarosa.core.util.externalizable.DeserializationException)2