Open ebruchez opened 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>
Implementation plan:
xf:function
from modelsHow 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.
Since #2812, XFormsStaticState
exposes a function library. So now we have a convenient location to store the functions coming from <xf:function>
as well.
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.
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'
So right now, we have XFormsStaticState.functionLibrary
, which returns a function library global to the form definition. This is used by:
CaseControl
via evaluateAsLiteralIfPossible
PathMapXPathAnalysis
XPathMIP
XFormsStaticStateImpl.uploadMaxSizeAggregateExpression
XFormsStaticStateImpl.nonDefaultPropertiesOnly
XFormsContainingDocument
Changes:
Model
has an Option[FunctionLibrary]
(with the default being the part's library, or maybe the XFormsStaticState
's).Model
compiles it's xf:function
s if any and adds them to its FunctionLibrary
.
xf:var
/xf:result
(we support let
anyway)SystemFunction
and add it to the library.Model
's library when available.xf:function
anywhere?xf:var
/xf:result
.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
:
xf:function
, create a string which looks like an XQuery function declaration and bodyQueryParser
XQueryFunctionLibrary
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.
Not easy to figure out which code keeps a pointer to Executable
. It seems that the following, non-XSLT code does:
AbstractStaticContext
(super of IndependentContext
)ExpressionVisitor
(should be transient)GlobalVariable
(not sure if used by XQuery stuff)QueryModule
QueryParser
(should be transient)StaticQueryContext
XPathExpressionImpl
(JAXP, not sure if we use)XQueryExpression
XQueryFunction
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.
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:
Model
's functionsFunctionLibrary
FunctionLibrary
as the Model
's Option[FunctionLibrary]
XPathCache
FunctionLibrary
, directly or indirectly, to a different Executable
, but that should be fine, right?Also would be good if functions could be global without duplicating them. For example:
See:
http://www.w3.org/TR/xforms20/#The_function_Element