Search in sources :

Example 1 with IFunctionHandler

use of org.javarosa.core.model.condition.IFunctionHandler in project javarosa by opendatakit.

the class FormDef method initEvalContext.

/**
 * @param ec The new Evaluation Context
 */
private void initEvalContext(EvaluationContext ec) {
    if (!ec.getFunctionHandlers().containsKey("jr:itext")) {
        final FormDef f = this;
        ec.addFunctionHandler(new IFunctionHandler() {

            @Override
            public String getName() {
                return "jr:itext";
            }

            @Override
            public Object eval(Object[] args, EvaluationContext ec) {
                String textID = (String) args[0];
                try {
                    // SUUUUPER HACKY
                    String form = ec.getOutputTextForm();
                    if (form != null) {
                        textID = textID + ";" + form;
                        String result = f.getLocalizer().getRawText(f.getLocalizer().getLocale(), textID);
                        return result == null ? "" : result;
                    } else {
                        String text = f.getLocalizer().getText(textID);
                        return text == null ? "[itext:" + textID + "]" : text;
                    }
                } catch (NoSuchElementException nsee) {
                    return "[nolocale]";
                }
            }

            @Override
            public List<Class[]> getPrototypes() {
                Class[] proto = { String.class };
                List<Class[]> v = new ArrayList<Class[]>(1);
                v.add(proto);
                return v;
            }

            @Override
            public boolean rawArgs() {
                return false;
            }

            @Override
            public boolean realTime() {
                return false;
            }
        });
    }
    /*
       * function to reverse a select value into the display label for that
       * choice in the question it came from
       * 
       * arg 1: select value arg 2: string xpath referring to origin question;
       * must be absolute path
       * 
       * this won't work at all if the original label needed to be
       * processed/calculated in some way (<output>s, etc.) (is this even
       * allowed?) likely won't work with multi-media labels _might_ work for
       * itemsets, but probably not very well or at all; could potentially work
       * better if we had some context info DOES work with localization
       * 
       * it's mainly intended for the simple case of reversing a question with
       * compile-time-static fields, for use inside an <output>
       */
    if (!ec.getFunctionHandlers().containsKey("jr:choice-name")) {
        final FormDef f = this;
        ec.addFunctionHandler(new IFunctionHandler() {

            @Override
            public String getName() {
                return "jr:choice-name";
            }

            @Override
            public Object eval(Object[] args, EvaluationContext ec) {
                try {
                    String value = (String) args[0];
                    String questionXpath = (String) args[1];
                    TreeReference ref = RestoreUtils.xfFact.ref(questionXpath);
                    QuestionDef q = findQuestionByRef(ref, f);
                    if (q == null || (q.getControlType() != Constants.CONTROL_SELECT_ONE && q.getControlType() != Constants.CONTROL_SELECT_MULTI)) {
                        return "";
                    }
                    // NOTE: this is highly suspect. We have no context against
                    // which to evaluate
                    // a dynamic selection list. This will generally cause that
                    // evaluation to break
                    // if any filtering is done, or, worst case, give unexpected
                    // results.
                    // 
                    // We should hook into the existing code (FormEntryPrompt) for
                    // pulling
                    // display text for select choices. however, it's hard,
                    // because we don't really have
                    // any context to work with, and all the situations where that
                    // context would be used
                    // don't make sense for trying to reverse a select value back
                    // to a label in an unrelated
                    // expression
                    List<SelectChoice> choices;
                    ItemsetBinding itemset = q.getDynamicChoices();
                    if (itemset != null) {
                        if (itemset.getChoices() == null) {
                            if (ref.isAmbiguous()) {
                                // SurveyCTO: We need a absolute "ref" to populate the dynamic choices,
                                // like we do when we populate those at FormEntryPrompt (line 251).
                                // The "ref" here is ambiguous, so we need to make it concrete first.
                                ref = ref.contextualize(ec.getContextRef());
                            }
                            f.populateDynamicChoices(itemset, ref);
                        }
                        choices = itemset.getChoices();
                    } else {
                        // static choices
                        choices = q.getChoices();
                    }
                    if (choices != null) {
                        for (SelectChoice ch : choices) {
                            if (ch.getValue().equals(value)) {
                                // this is really not ideal. we should hook into the
                                // existing code (FormEntryPrompt) for pulling
                                // display text for select choices. however, it's
                                // hard, because we don't really have
                                // any context to work with, and all the situations
                                // where that context would be used
                                // don't make sense for trying to reverse a select
                                // value back to a label in an unrelated
                                // expression
                                String textID = ch.getTextID();
                                String templateStr;
                                if (textID != null) {
                                    templateStr = f.getLocalizer().getText(textID);
                                } else {
                                    templateStr = ch.getLabelInnerText();
                                }
                                return fillTemplateString(templateStr, ref);
                            }
                        }
                    }
                    return "";
                } catch (Exception e) {
                    throw new WrappedException("error in evaluation of xpath function [choice-name]", e);
                }
            }

            @Override
            public List<Class[]> getPrototypes() {
                Class[] proto = { String.class, String.class };
                List<Class[]> v = new ArrayList<Class[]>(1);
                v.add(proto);
                return v;
            }

            @Override
            public boolean rawArgs() {
                return false;
            }

            @Override
            public boolean realTime() {
                return false;
            }
        });
    }
}
Also used : WrappedException(org.javarosa.core.log.WrappedException) IFunctionHandler(org.javarosa.core.model.condition.IFunctionHandler) XPathException(org.javarosa.xpath.XPathException) InvalidReferenceException(org.javarosa.core.model.instance.InvalidReferenceException) DeserializationException(org.javarosa.core.util.externalizable.DeserializationException) NoSuchElementException(java.util.NoSuchElementException) WrappedException(org.javarosa.core.log.WrappedException) IOException(java.io.IOException) XFormParseException(org.javarosa.xform.parse.XFormParseException) TreeReference(org.javarosa.core.model.instance.TreeReference) List(java.util.List) ArrayList(java.util.ArrayList) ExtWrapList(org.javarosa.core.util.externalizable.ExtWrapList) EvaluationContext(org.javarosa.core.model.condition.EvaluationContext) NoSuchElementException(java.util.NoSuchElementException)

Example 2 with IFunctionHandler

use of org.javarosa.core.model.condition.IFunctionHandler in project javarosa by opendatakit.

the class XPathFuncExpr method eval.

/**
 * Evaluate the function call.
 *
 * First check if the function is a member of the built-in function suite. If not, then check
 * for any custom handlers registered to handler the function. If not, throw and exception.
 *
 * Both function name and appropriate arguments are taken into account when finding a suitable
 * handler. For built-in functions, the number of arguments must match; for custom functions,
 * the supplied arguments must match one of the function prototypes defined by the handler.
 */
public Object eval(DataInstance model, EvaluationContext evalContext) {
    String name = id.toString();
    Object[] argVals = new Object[args.length];
    HashMap<String, IFunctionHandler> funcHandlers = evalContext.getFunctionHandlers();
    // TODO: Func handlers should be able to declare the desire for short circuiting as well
    if (name.equals("if")) {
        assertArgsCount(name, args, 3);
        return ifThenElse(model, evalContext, args, argVals);
    } else if (name.equals("coalesce")) {
        assertArgsCount(name, args, 2);
        argVals[0] = args[0].eval(model, evalContext);
        if (!isNull(argVals[0])) {
            return argVals[0];
        } else {
            // that was null, so try the other one...
            argVals[1] = args[1].eval(model, evalContext);
            return argVals[1];
        }
    } else if (name.equals("indexed-repeat")) {
        if ((args.length == 3 || args.length == 5 || args.length == 7 || args.length == 9 || args.length == 11)) {
            return indexedRepeat(model, evalContext, args, argVals);
        } else {
            throw new XPathUnhandledException("function \'" + name + "\' requires " + "3, 5, 7, 9 or 11 arguments. Only " + args.length + " provided.");
        }
    }
    for (int i = 0; i < args.length; i++) {
        argVals[i] = args[i].eval(model, evalContext);
    }
    // check built-in functions
    if (name.equals("true")) {
        assertArgsCount(name, args, 0);
        return Boolean.TRUE;
    } else if (name.equals("false")) {
        assertArgsCount(name, args, 0);
        return Boolean.FALSE;
    } else if (name.equals("boolean")) {
        assertArgsCount(name, args, 1);
        return toBoolean(argVals[0]);
    } else if (name.equals("number")) {
        assertArgsCount(name, args, 1);
        return toNumeric(argVals[0]);
    } else if (name.equals("int")) {
        // non-standard
        assertArgsCount(name, args, 1);
        return toInt(argVals[0]);
    } else if (name.equals("round")) {
        // Proximate XPath 3.0 and Excel-style round(value,decimal place)
        final int places;
        if (args.length == 1) {
            places = 0;
        } else {
            assertArgsCount(name, args, 2);
            places = toNumeric(argVals[1]).intValue();
        }
        return round(toNumeric(argVals[0]), places);
    } else if (name.equals("string")) {
        assertArgsCount(name, args, 1);
        return toString(argVals[0]);
    } else if (name.equals("date")) {
        // non-standard
        assertArgsCount(name, args, 1);
        return toDate(argVals[0], false);
    } else if (name.equals("date-time")) {
        // non-standard -- convert double/int/string to Date object
        assertArgsCount(name, args, 1);
        return toDate(argVals[0], true);
    } else if (name.equals("decimal-date-time")) {
        // non-standard -- convert string/date to decimal days off 1970-01-01T00:00:00.000-000
        assertArgsCount(name, args, 1);
        return toDecimalDateTime(argVals[0], true);
    } else if (name.equals("decimal-time")) {
        // non-standard -- convert string/date to decimal days off 1970-01-01T00:00:00.000-000
        assertArgsCount(name, args, 1);
        return toDecimalDateTime(argVals[0], false);
    } else if (name.equals("not")) {
        assertArgsCount(name, args, 1);
        return boolNot(argVals[0]);
    } else if (name.equals("boolean-from-string")) {
        assertArgsCount(name, args, 1);
        return boolStr(argVals[0]);
    } else if (name.equals("format-date")) {
        assertArgsCount(name, args, 2);
        return formatDateTime(argVals[0], argVals[1]);
    } else if (name.equals("abs")) {
        // XPath 3.0
        checkArity(name, 1, args.length);
        return Math.abs(toDouble(argVals[0]));
    } else if (name.equals("acos")) {
        // XPath 3.0
        checkArity(name, 1, args.length);
        return Math.acos(toDouble(argVals[0]));
    } else if (name.equals("asin")) {
        // XPath 3.0
        checkArity(name, 1, args.length);
        return Math.asin(toDouble(argVals[0]));
    } else if (name.equals("atan")) {
        // XPath 3.0
        checkArity(name, 1, args.length);
        return Math.atan(toDouble(argVals[0]));
    } else if (name.equals("atan2")) {
        // XPath 3.0
        checkArity(name, 2, args.length);
        return Math.atan2(toDouble(argVals[0]), toDouble(argVals[1]));
    } else if (name.equals("cos")) {
        // XPath 3.0
        checkArity(name, 1, args.length);
        return Math.cos(toDouble(argVals[0]));
    } else if (name.equals("exp")) {
        // XPath 3.0
        checkArity(name, 1, args.length);
        return Math.exp(toDouble(argVals[0]));
    } else if (name.equals("exp10")) {
        // XPath 3.0
        checkArity(name, 1, args.length);
        return Math.pow(10.0, toDouble(argVals[0]));
    } else if (name.equals("log")) {
        // XPath 3.0
        checkArity(name, 1, args.length);
        return Math.log(toDouble(argVals[0]));
    } else if (name.equals("log10")) {
        // XPath 3.0
        checkArity(name, 1, args.length);
        return Math.log10(toDouble(argVals[0]));
    } else if (name.equals("pi")) {
        // XPath 3.0
        checkArity(name, 0, args.length);
        return Math.PI;
    } else if (name.equals("sin")) {
        // XPath 3.0
        checkArity(name, 1, args.length);
        return Math.sin(toDouble(argVals[0]));
    } else if (name.equals("sqrt")) {
        // XPath 3.0
        checkArity(name, 1, args.length);
        return Math.sqrt(toDouble(argVals[0]));
    } else if (name.equals("tan")) {
        // XPath 3.0
        checkArity(name, 1, args.length);
        return Math.tan(toDouble(argVals[0]));
    } else if (name.equals("format-date-time")) {
        // non-standard
        assertArgsCount(name, args, 2);
        return formatDateTime(argVals[0], argVals[1]);
    } else if ((name.equals("selected") || name.equals("is-selected"))) {
        // non-standard
        assertArgsCount(name, args, 2);
        return multiSelected(argVals[0], argVals[1], name);
    } else if (name.equals("count-selected")) {
        // non-standard
        assertArgsCount(name, args, 1);
        return countSelected(argVals[0]);
    } else if (name.equals("selected-at")) {
        // non-standard
        assertArgsCount(name, args, 2);
        return selectedAt(argVals[0], argVals[1]);
    } else if (name.equals("position")) {
        // TODO: Technically, only the 0 length argument is valid here.
        if (args.length == 1) {
            XPathNodeset nodes = (XPathNodeset) argVals[0];
            if (nodes.size() == 0) {
                // Will likely cause an error downstream when used in an XPath.
                return (double) (1 + TreeReference.INDEX_UNBOUND);
            } else {
                // if or how this might manifest into a bug... .
                return position(nodes.getRefAt(0));
            }
        } else if (args.length == 0) {
            if (evalContext.getContextPosition() != -1) {
                return (double) (1 + evalContext.getContextPosition());
            }
            return position(evalContext.getContextRef());
        } else {
            throw new XPathUnhandledException("function \'" + name + "\' requires either exactly one argument or no arguments. Only " + args.length + " provided.");
        }
    } else if (name.equals("count")) {
        assertArgsCount(name, args, 1);
        return count(argVals[0]);
    } else if (name.equals("count-non-empty")) {
        assertArgsCount(name, args, 1);
        return countNonEmpty(argVals[0]);
    } else if (name.equals("sum")) {
        assertArgsCount(name, args, 1);
        if (argVals[0] instanceof XPathNodeset) {
            return sum(((XPathNodeset) argVals[0]).toArgList());
        } else {
            throw new XPathTypeMismatchException("not a nodeset");
        }
    } else if (name.equals("max")) {
        if (args.length == 1 && argVals[0] instanceof XPathNodeset) {
            return max(((XPathNodeset) argVals[0]).toArgList());
        } else {
            return max(argVals);
        }
    } else if (name.equals("min")) {
        if (args.length == 1 && argVals[0] instanceof XPathNodeset) {
            return min(((XPathNodeset) argVals[0]).toArgList());
        } else {
            return min(argVals);
        }
    } else if (name.equals("today")) {
        assertArgsCount(name, args, 0);
        return DateUtils.roundDate(new Date());
    } else if (name.equals("now")) {
        assertArgsCount(name, args, 0);
        return new Date();
    } else if (name.equals("concat")) {
        if (args.length == 1 && argVals[0] instanceof XPathNodeset) {
            return join("", ((XPathNodeset) argVals[0]).toArgList());
        } else {
            return join("", argVals);
        }
    } else if (name.equals("join") && args.length >= 1) {
        if (args.length == 2 && argVals[1] instanceof XPathNodeset) {
            return join(argVals[0], ((XPathNodeset) argVals[1]).toArgList());
        } else {
            return join(argVals[0], subsetArgList(argVals, 1));
        }
    } else if (name.equals("substr") && (args.length == 2 || args.length == 3)) {
        return substring(argVals[0], argVals[1], args.length == 3 ? argVals[2] : null);
    } else if (name.equals("contains") && args.length == 2) {
        return toString(argVals[0]).contains(toString(argVals[1]));
    } else if (name.equals("starts-with") && args.length == 2) {
        return toString(argVals[0]).startsWith(toString(argVals[1]));
    } else if (name.equals("ends-with") && args.length == 2) {
        return toString(argVals[0]).endsWith(toString(argVals[1]));
    } else if (name.equals("string-length")) {
        assertArgsCount(name, args, 1);
        return stringLength(argVals[0]);
    } else if (name.equals("checklist") && args.length >= 2) {
        // non-standard
        if (args.length == 3 && argVals[2] instanceof XPathNodeset) {
            return checklist(argVals[0], argVals[1], ((XPathNodeset) argVals[2]).toArgList());
        } else {
            return checklist(argVals[0], argVals[1], subsetArgList(argVals, 2));
        }
    } else if (name.equals("weighted-checklist") && args.length >= 2 && args.length % 2 == 0) {
        // non-standard
        if (args.length == 4 && argVals[2] instanceof XPathNodeset && argVals[3] instanceof XPathNodeset) {
            Object[] factors = ((XPathNodeset) argVals[2]).toArgList();
            Object[] weights = ((XPathNodeset) argVals[3]).toArgList();
            if (factors.length != weights.length) {
                throw new XPathTypeMismatchException("weighted-checklist: nodesets not same length");
            }
            return checklistWeighted(argVals[0], argVals[1], factors, weights);
        } else {
            return checklistWeighted(argVals[0], argVals[1], subsetArgList(argVals, 2, 2), subsetArgList(argVals, 3, 2));
        }
    } else if (name.equals("regex")) {
        // non-standard
        assertArgsCount(name, args, 2);
        return regex(argVals[0], argVals[1]);
    } else if (name.equals("depend") && args.length >= 1) {
        // non-standard
        return argVals[0];
    } else if (name.equals("random")) {
        // non-standard
        assertArgsCount(name, args, 0);
        // calculated expressions may be recomputed w/o warning! use with caution!!
        return MathUtils.getRand().nextDouble();
    } else if (name.equals("once")) {
        assertArgsCount(name, args, 1);
        XPathPathExpr currentFieldPathExpr = XPathPathExpr.fromRef(evalContext.getContextRef());
        Object currValue = currentFieldPathExpr.eval(model, evalContext).unpack();
        if (currValue == null || toString(currValue).length() == 0) {
            // this is the "once" case
            return argVals[0];
        } else {
            return currValue;
        }
    } else if (name.equals("uuid") && (args.length == 0 || args.length == 1)) {
        // calculated expressions may be recomputed w/o warning! use with caution!!
        if (args.length == 0) {
            return PropertyUtils.genUUID();
        }
        int len = toInt(argVals[0]).intValue();
        return PropertyUtils.genGUID(len);
    } else if (name.equals("version")) {
        // non-standard
        assertArgsCount(name, args, 0);
        final String formVersion = (model instanceof FormInstance) ? ((FormInstance) model).formVersion : "";
        return formVersion == null ? "" : formVersion;
    } else if (name.equals("property")) {
        // non-standard
        // return a property defined by the property manager.
        // NOTE: Property should be immutable.
        // i.e., does not work with 'start' or 'end' property.
        assertArgsCount(name, args, 1);
        String s = toString(argVals[0]);
        return PropertyManager._().getSingularProperty(s);
    } else if (name.equals("pow") && (args.length == 2)) {
        // XPath 3.0
        double a = toDouble(argVals[0]);
        double b = toDouble(argVals[1]);
        return Math.pow(a, b);
    } else if (name.equals("enclosed-area") || name.equals("area")) {
        assertArgsCount(name, args, 1);
        Object argVal = argVals[0];
        if (!(argVal instanceof XPathNodeset)) {
            throw new XPathUnhandledException("function \'" + name + "\' requires a field as the parameter.");
        }
        Object[] argList = ((XPathNodeset) argVal).toArgList();
        int repeatSize = argList.length;
        List<GeoUtils.GPSCoordinates> gpsCoordinatesList;
        if (repeatSize == 1) {
            // Try to determine if the argument is of type GeoShapeData
            try {
                GeoShapeData geoShapeData = new GeoShapeData().cast(new UncastData(toString(argList[0])));
                if (geoShapeData.points.size() <= 2) {
                    return 0d;
                } else {
                    gpsCoordinatesList = new ArrayList<GeoUtils.GPSCoordinates>();
                    for (GeoPointData point : geoShapeData.points) {
                        gpsCoordinatesList.add(new GeoUtils.GPSCoordinates(point.getPart(0), point.getPart(1)));
                    }
                }
            } catch (Exception e) {
                throw new XPathTypeMismatchException("The function \'" + name + "\' received a value that does not represent GPS coordinates: " + argList[0]);
            }
        } else {
            if (repeatSize <= 2) {
                return 0d;
            } else {
                // treat the input as a series of GeoPointData
                gpsCoordinatesList = new ArrayList<GeoUtils.GPSCoordinates>();
                for (Object arg : argList) {
                    try {
                        GeoPointData geoPointData = new GeoPointData().cast(new UncastData(toString(arg)));
                        gpsCoordinatesList.add(new GeoUtils.GPSCoordinates(geoPointData.getPart(0), geoPointData.getPart(1)));
                    } catch (Exception e) {
                        throw new XPathTypeMismatchException("The function \'" + name + "\' received a value that does not represent GPS coordinates: " + arg);
                    }
                }
            }
        }
        return GeoUtils.calculateAreaOfGPSPolygonOnEarthInSquareMeters(gpsCoordinatesList);
    } else if (name.equals("digest") && (args.length == 2 || args.length == 3)) {
        return DigestAlgorithm.from((String) argVals[1]).digest((String) argVals[0], args.length == 3 ? Encoding.from((String) argVals[2]) : Encoding.BASE64);
    } else {
        // check for custom handler
        IFunctionHandler handler = funcHandlers.get(name);
        if (handler != null) {
            return evalCustomFunction(handler, argVals, evalContext);
        } else {
            throw new XPathUnhandledException("function \'" + name + "\'");
        }
    }
}
Also used : ArrayList(java.util.ArrayList) GeoPointData(org.javarosa.core.model.data.GeoPointData) IFunctionHandler(org.javarosa.core.model.condition.IFunctionHandler) XPathUnhandledException(org.javarosa.xpath.XPathUnhandledException) Date(java.util.Date) XPathUnhandledException(org.javarosa.xpath.XPathUnhandledException) UnpivotableExpressionException(org.javarosa.core.model.condition.pivot.UnpivotableExpressionException) IOException(java.io.IOException) XPathTypeMismatchException(org.javarosa.xpath.XPathTypeMismatchException) XPathArityException(org.javarosa.xpath.XPathArityException) DeserializationException(org.javarosa.core.util.externalizable.DeserializationException) GeoUtils(org.javarosa.core.util.GeoUtils) GeoShapeData(org.javarosa.core.model.data.GeoShapeData) UncastData(org.javarosa.core.model.data.UncastData) XPathNodeset(org.javarosa.xpath.XPathNodeset) ArrayList(java.util.ArrayList) List(java.util.List) XPathTypeMismatchException(org.javarosa.xpath.XPathTypeMismatchException) FormInstance(org.javarosa.core.model.instance.FormInstance)

Example 3 with IFunctionHandler

use of org.javarosa.core.model.condition.IFunctionHandler in project javarosa by opendatakit.

the class XPathEvalTest method getFunctionHandlers.

private EvaluationContext getFunctionHandlers() {
    EvaluationContext ec = new EvaluationContext(null);
    final Class[][] allPrototypes = { { Double.class, Double.class }, { Double.class }, { String.class, String.class }, { Double.class, String.class, Boolean.class }, { Boolean.class }, { Boolean.class, Double.class, String.class, Date.class, CustomType.class } };
    ec.addFunctionHandler(new IFunctionHandler() {

        @Override
        public String getName() {
            return "testfunc";
        }

        @Override
        public List<Class[]> getPrototypes() {
            List<Class[]> p = new ArrayList<Class[]>();
            p.add(new Class[0]);
            return p;
        }

        @Override
        public boolean rawArgs() {
            return false;
        }

        @Override
        public boolean realTime() {
            return false;
        }

        @Override
        public Object eval(Object[] args, EvaluationContext ec) {
            return TRUE;
        }
    });
    ec.addFunctionHandler(new IFunctionHandler() {

        @Override
        public String getName() {
            return "regex";
        }

        @Override
        public Object eval(Object[] args, EvaluationContext ec) {
            System.out.println("EVAL REGEX TESTS:");
            for (Object arg : args) {
                System.out.println("REGEX ARGS: " + arg.toString());
            }
            // String.re  args[0].
            return TRUE;
        }

        @Override
        public List<Class[]> getPrototypes() {
            List<Class[]> p = new ArrayList<Class[]>();
            p.add(allPrototypes[2]);
            return p;
        }

        @Override
        public boolean rawArgs() {
            return false;
        }

        @Override
        public boolean realTime() {
            return false;
        }
    });
    ec.addFunctionHandler(new IFunctionHandler() {

        @Override
        public String getName() {
            return "add";
        }

        @Override
        public List<Class[]> getPrototypes() {
            List<Class[]> p = new ArrayList<Class[]>();
            p.add(allPrototypes[0]);
            return p;
        }

        @Override
        public boolean rawArgs() {
            return false;
        }

        @Override
        public boolean realTime() {
            return false;
        }

        @Override
        public Object eval(Object[] args, EvaluationContext ec) {
            return (Double) args[0] + (Double) args[1];
        }
    });
    ec.addFunctionHandler(new IFunctionHandler() {

        @Override
        public String getName() {
            return "proto";
        }

        @Override
        public List<Class[]> getPrototypes() {
            List<Class[]> p = new ArrayList<Class[]>();
            p.add(allPrototypes[0]);
            p.add(allPrototypes[1]);
            p.add(allPrototypes[2]);
            p.add(allPrototypes[3]);
            return p;
        }

        @Override
        public boolean rawArgs() {
            return false;
        }

        @Override
        public boolean realTime() {
            return false;
        }

        @Override
        public Object eval(Object[] args, EvaluationContext ec) {
            return printArgs(args);
        }
    });
    ec.addFunctionHandler(new IFunctionHandler() {

        @Override
        public String getName() {
            return "raw";
        }

        @Override
        public List<Class[]> getPrototypes() {
            List<Class[]> p = new ArrayList<Class[]>();
            p.add(allPrototypes[3]);
            return p;
        }

        @Override
        public boolean rawArgs() {
            return true;
        }

        @Override
        public boolean realTime() {
            return false;
        }

        @Override
        public Object eval(Object[] args, EvaluationContext ec) {
            return printArgs(args);
        }
    });
    ec.addFunctionHandler(new IFunctionHandler() {

        @Override
        public String getName() {
            return "null-proto";
        }

        @Override
        public List<Class[]> getPrototypes() {
            return null;
        }

        @Override
        public boolean rawArgs() {
            return false;
        }

        @Override
        public boolean realTime() {
            return false;
        }

        @Override
        public Object eval(Object[] args, EvaluationContext ec) {
            return FALSE;
        }
    });
    ec.addFunctionHandler(new IFunctionHandler() {

        @Override
        public String getName() {
            return "concat";
        }

        @Override
        public List<Class[]> getPrototypes() {
            return new ArrayList<Class[]>();
        }

        @Override
        public boolean rawArgs() {
            return true;
        }

        @Override
        public boolean realTime() {
            return false;
        }

        @Override
        public Object eval(Object[] args, EvaluationContext ec) {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < args.length; i++) sb.append(XPathFuncExpr.toString(args[i]));
            return sb.toString();
        }
    });
    ec.addFunctionHandler(new IFunctionHandler() {

        @Override
        public String getName() {
            return "convertible";
        }

        @Override
        public List<Class[]> getPrototypes() {
            List<Class[]> p = new ArrayList<Class[]>();
            p.add(new Class[0]);
            return p;
        }

        @Override
        public boolean rawArgs() {
            return false;
        }

        @Override
        public boolean realTime() {
            return false;
        }

        @Override
        public Object eval(Object[] args, EvaluationContext ec) {
            return new IExprDataType() {

                @Override
                public Boolean toBoolean() {
                    return TRUE;
                }

                @Override
                public Double toNumeric() {
                    return 5.0;
                }

                public String toString() {
                    return "hi";
                }
            };
        }
    });
    ec.addFunctionHandler(new IFunctionHandler() {

        @Override
        public String getName() {
            return "inconvertible";
        }

        @Override
        public List<Class[]> getPrototypes() {
            List<Class[]> p = new ArrayList<Class[]>();
            p.add(new Class[0]);
            return p;
        }

        @Override
        public boolean rawArgs() {
            return false;
        }

        @Override
        public boolean realTime() {
            return false;
        }

        @Override
        public Object eval(Object[] args, EvaluationContext ec) {
            return new Object();
        }
    });
    ec.addFunctionHandler(new IFunctionHandler() {

        @Override
        public String getName() {
            return "get-custom";
        }

        @Override
        public List<Class[]> getPrototypes() {
            List<Class[]> p = new ArrayList<Class[]>();
            p.add(allPrototypes[4]);
            return p;
        }

        @Override
        public boolean rawArgs() {
            return false;
        }

        @Override
        public boolean realTime() {
            return false;
        }

        @Override
        public Object eval(Object[] args, EvaluationContext ec) {
            return (Boolean) args[0] ? new CustomSubType() : new CustomType();
        }
    });
    ec.addFunctionHandler(new IFunctionHandler() {

        @Override
        public String getName() {
            return "check-types";
        }

        @Override
        public List<Class[]> getPrototypes() {
            List<Class[]> p = new ArrayList<Class[]>();
            p.add(allPrototypes[5]);
            return p;
        }

        @Override
        public boolean rawArgs() {
            return false;
        }

        @Override
        public boolean realTime() {
            return false;
        }

        @Override
        public Object eval(Object[] args, EvaluationContext ec) {
            if (args.length != 5 || !(args[0] instanceof Boolean) || !(args[1] instanceof Double) || !(args[2] instanceof String) || !(args[3] instanceof Date) || !(args[4] instanceof CustomType))
                fail("Types in custom function handler not converted properly/prototype not matched properly");
            return TRUE;
        }
    });
    return ec;
}
Also used : IExprDataType(org.javarosa.xpath.IExprDataType) IFunctionHandler(org.javarosa.core.model.condition.IFunctionHandler) Date(java.util.Date) ArrayList(java.util.ArrayList) List(java.util.List) EvaluationContext(org.javarosa.core.model.condition.EvaluationContext)

Aggregations

ArrayList (java.util.ArrayList)3 List (java.util.List)3 IFunctionHandler (org.javarosa.core.model.condition.IFunctionHandler)3 IOException (java.io.IOException)2 Date (java.util.Date)2 EvaluationContext (org.javarosa.core.model.condition.EvaluationContext)2 DeserializationException (org.javarosa.core.util.externalizable.DeserializationException)2 NoSuchElementException (java.util.NoSuchElementException)1 WrappedException (org.javarosa.core.log.WrappedException)1 UnpivotableExpressionException (org.javarosa.core.model.condition.pivot.UnpivotableExpressionException)1 GeoPointData (org.javarosa.core.model.data.GeoPointData)1 GeoShapeData (org.javarosa.core.model.data.GeoShapeData)1 UncastData (org.javarosa.core.model.data.UncastData)1 FormInstance (org.javarosa.core.model.instance.FormInstance)1 InvalidReferenceException (org.javarosa.core.model.instance.InvalidReferenceException)1 TreeReference (org.javarosa.core.model.instance.TreeReference)1 GeoUtils (org.javarosa.core.util.GeoUtils)1 ExtWrapList (org.javarosa.core.util.externalizable.ExtWrapList)1 XFormParseException (org.javarosa.xform.parse.XFormParseException)1 IExprDataType (org.javarosa.xpath.IExprDataType)1