orbeon / orbeon-forms

Orbeon Forms is an open source web forms solution. It includes an XForms engine, the Form Builder web-based form editor, and the Form Runner runtime.
http://www.orbeon.com/
GNU Lesser General Public License v2.1
514 stars 220 forks source link

XForms 2.0 xf:function support #764

Open ebruchez opened 11 years ago

ebruchez commented 11 years ago

See:

http://www.w3.org/TR/xforms20/#The_function_Element

ebruchez commented 11 years ago

Note that the spec is not final on this yet. I had proposed a lighter syntax for the body, e.g.:

<function signature="my:foo($p as xs:decimal*) as xs:decimal">
    ...XPath expression here...
</function>
ebruchez commented 11 years ago

See also:

http://lists.w3.org/Archives/Public/public-forms/2013Jan/0039.html

ebruchez commented 11 years ago

Implementation plan:

Phase 1: only define top-level functions in the model

Phase 2: functions everywhere

ebruchez commented 11 years ago

How can a function library be reused?

NOTE: Imported functions wouldn't run in the scope of the model. E.g. say you have:

<xf:var name="foo"/>
<xf:function>
  $foo
</xf:function>

This would work if the function is defined directly in the model, but not if it is imported as where the function would be defined, $foo wouldn't be visible. Scoping would be lexical. However, imported functions become visible to the model as well as the view when the model is in scope.

ebruchez commented 7 years ago

Since #2812, XFormsStaticState exposes a function library. So now we have a convenient location to store the functions coming from <xf:function> as well.

ebruchez commented 7 years ago

However, the spec says:

A custom function is included in the in-scope functions of the static context for all XPath expressions associated with the model that contains the function definition

So we need a function library for each model and scope them appropriately.

ebruchez commented 7 years ago

Finding that I could use this right now, where I have this logic which I must duplicate:

let $key   := name/value,
    $label :=
        if (exists($key)) then
            if (not(xxf:is-blank($key))) then
                let $r := xxf:r(concat('buttons.', $key), '|fr-fr-resources|')
                return
                    if (exists($r)) then $r else $key
            else
                $key
        else
            name/name
return
    if (xxf:non-blank($label)) then
        $label
    else
        'Untitled'
ebruchez commented 7 years ago

+1 from customer

ebruchez commented 7 years ago

So right now, we have XFormsStaticState.functionLibrary, which returns a function library global to the form definition. This is used by:

Changes:

ebruchez commented 7 years ago

Since we need to parse the function signature, I looked at how Saxon does it in QueryParser. I managed to run simple code to just parse a declare function with the function body.

It might be possible to use that not only to parse the signature but also to parse the entire function body. The result is an XQueryFunction which is then registered with declareFunction into the QueryModule's local XQueryFunctionLibrary as well as the module's top-level (main) module's globalFunctionLibrary.

XQueryFunctionLibrary then creates instances of UserFunctionCall (or some other Expression if optimized.

We should be able to just leverage this machinery to register a model's set of xf:function:

We should be able to use the resulting XQueryFunctionLibrary as context for XPath expressions. There is one question of which Executable should be passed to the functions.

When we use our own XPathCache, we create an IndependentContext for each pooled expression, which creates a new Executable(config) with XPATH as language. However, there are no tests in the code to later distinguish whether the language is set to XPATH or XQUERY. The Executable also holds the function library.

So we'll need to see whether we need to play with the QueryModule's StaticQueryContext for the function libraries at all.

With this, in the future, we might be able to leverage this to create global/importable function library modules for XForms too. For now, just one module per model should be fine.

ebruchez commented 7 years ago

Not easy to figure out which code keeps a pointer to Executable. It seems that the following, non-XSLT code does:

ebruchez commented 7 years ago

It would definitely be nice if we could load function libraries shared between forms, without duplication. These libraries would be compiled once only and made available globally/per form.

ebruchez commented 7 years ago

At first glance, I think that for the Executable, we pretty much only need the FunctionLibrary which results from the expression compilation. So we could:

avernet commented 6 years ago

+1 from customer

ebruchez commented 4 years ago

+1 from customer

ebruchez commented 2 years ago

Would be nice for a validation function like in case of customer.

ebruchez commented 2 years ago

+1 from Stack Overflow

ebruchez commented 1 year ago

Also would be good if functions could be global without duplicating them. For example:

avernet commented 1 year ago

+1 from customer