[xslt] Processing of extension-elements/functions and user-defined data elements



Hi,

I think there is a flaw in the current mechanism of processing
extension element/functions and top-level (user-defined data)
elements.

(Sorry, this has probably gotten a bit too verbose)

Let me try to explain:
1) I don't think the [xsl:]extension-element-prefixes mechanism is
  intended and usefull for registration of extension modules.
  [xsl:]extension-element-prefixes is used just to distinguish
  extension elements from literal result elements in the stylesheet.
  Specifying [xsl:]extension-element-prefixes does not say that
  there will be actually any extension element in the stylesheet.

  Example A (in this stylesheet there's actually no extension element):
  <xsl:stylesheet ...>
    <xsl:template>
      <foo xmlns:ext="urn:test:foo"
xsl:extension-element-prefixes="ext">
        
      </foo>
    ... 

  Example B (we have an extension element here):
  <xsl:stylesheet ...>
    <xsl:template>
      <foo xmlns:ext="urn:test:foo"
xsl:extension-element-prefixes="ext">
        <ext:my-extension-element .../>
      </foo>
    ... 

  So there's no reason why in example B any extension element module
  should be initialized. I think this needs to happen on the first
  occurence of an extension element - not earlier.

2) Top-level elements are not "extension elements":
  http://www.w3.org/TR/xslt#extension-element
  Thus "extension-element-prefixes" does not apply to them.

Let me analyse an example (below) using EXSLT's definitions of
extension functions via "func:function":

1) The top-level data element func:function is correctly
  identified by Libxslt (since it was registered via
  xsltRegisterExtModuleTopLevel()).
  
2) The parsing of the content of the func:function incorrectly
  fails if there was no extension element namespace
  defined via "extension-element-prefixes":
  a) the xsl:param is rejected as invalid, since the
   grammar-checks in preproc.c allow xsl:param to
   occur as a parent of an extension element, but
   the namespace of func:function was not defined
   to be an extension element.
  b) if we remove the xsl:param, then the func:result
   element is incorrectly treated as a literal result
   element, which leads to an addition of
   nodes to the result-tree - which is forbidden by
   the definition of func:function. Thus Libxslt raises
   an error here.

Stylesheet:

<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
  xmlns:func="http://exslt.org/functions";
  xmlns:foo="urn:test:foo"
  exclude-result-prefixes="func foo">

  <func:function name="foo:func-1">
    <xsl:param name="param-1" select="'hello'"/>
    <func:result select="$param-1"/>
  </func:function>

  <xsl:template match="/">
	  <foo>
      <xsl:value-of select="foo:func-1()"/>
    </foo>
  </xsl:template>

</xsl:stylesheet>

This example is valid according to Saxon 6.5.3 and Xalan-J,
which both generate the following result:

<?xml version='1.0' ?>
<foo>hello</foo>

To solve this issue, we should change the following things:

1) The *backtracking* grammar checks are not suitable for
  defining the content of the element func:function.
  The definition of the function:
  <func:function
    name = QName>
    <-- Content: (xsl:param* | template) -->
  </func:function>
  So we need somehow to move the process of content-checking
  to the compilation function of func:function. Backtracking
  grammar checks won't ever cover the specific user-defined
  content model of an extension element or top-level data
  element.

2) Complete the implementation of the EXSLT spec:

  "The EXSLT - Functions namespace (http://exslt.org/functions) 
   is designated as an extension namespace within the subtree
   rooted at a func:function element. The effect of this is
   as if the func:function element had a
   xsl:extension-element-prefixes attribute defined on it,
   with one of the values within it being the prefix used
   for the EXSLT - Functions namespace".

  This means that we need to implicitely define the
  extension element namespace for the content of func:function.
  This way the func:result element will be correctly recognized
  as an extension element (without the need to define its
  namespace via "extension-element-prefixes" on the xsl:stylesheet).


An other variant of this issue reveals that Libxslt does currently
depend on using "extension-element-prefixes" if it comes to
overriding (via xsl:import) equally-named functions.

Example:

"func-3.xsl"
------------
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
  xmlns:func="http://exslt.org/functions";
  xmlns:foo="urn:test:foo">

  <xsl:import href="func-3-imp.xsl"/>

  <func:function name="foo:same-func"/>  

</xsl:stylesheet>

"func-3-imp.xsl"
---------------
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
  xmlns:func="http://exslt.org/functions";
  xmlns:foo="urn:test:foo">

  <func:function name="foo:same-func"/>

</xsl:stylesheet>

Result of xsltproc:

xsltproc func-3.xsl func-3-imp.xsl
compilation error: file func-2.xsl line 9 element function
Failed to register function {urn:test:foo}same-func

This should not happen, since func:function should be
"import predecence" aware, so the imported function should be
simply overriden. Note that we don't get an error if we give
the functions different names.

Here in exsltFuncFunctionComp(), the function xsltStyleGetExtData()
is called to retrieve a stored table with the function names.
If we don't use "extension-element-prefixes" then a distinct
table is created for every imported stylesheet, thus the
names do not clash.
If we *don't* use "extension-element-prefixes", then only one
table is created (on the imported stylesheet) and both functions
are tried to be put into that 1 table.

This effect is based on:
1) the current parsing order
2) a lookup mechanism of extension module information, which
  will query the import-tree in descending order for already
  registered module information.
  The relevant code for this lookup is in xsltStyleGetExtData():
  -----
  tmp = style;
  while (tmp != NULL) {
    if (tmp->extInfos != NULL) {
      data = (xsltExtDataPtr) xmlHashLookup(tmp->extInfos, URI);
      if (data != NULL)
        break;
    }
    tmp = xsltNextImport(tmp);
  }
  -----

Thus we get the following explanation of the effect:

1) "extension-element-prefixes" of the xsl:stylesheet is parsed
  in descending order of the import-tree.
  Thus: 
  a) Subsequent stylesheets are not created yet, so
    the import-tree lookup will not retrieve any already registered
    module information
  b) the table mentioned above (which is the module information)
    will be created for every imported stylesheet.

2) Top-level data elements are parsed in ascending-order of the
  import tree.
  Thus: 
  a) subsequent stylesheets which have already beed processed,
    will be queried for existing tables.
    Since the imported stylesheet has created such a table,
    the importing stylesheet will use it and will try to add
    its function - resulting in a name-clash.


I now realize why my extension functions were initialized
for every stylesheet as described in 
http://mail.gnome.org/archives/xslt/2006-May/msg00003.html :

1) I used "extension-element-prefixes"
2) the parsing process did not see that the module was
  already initialized, since the module-information-lookup
  is performed down the import-tree, but my already registered
  data was registered *up* the import-tree.

Summary:

1) func:function needs an *overriding* mechansim;
  the module information must be stored per stylesheet for this

2) My extension element or extension function does only need
  *one* place to store its module information;
  so initialization in needed only *once*.

The scenarios 1) and 2) have different semantics, but are
currently processed with the same mechanism, i.e., with
xsltStyleGetExtData().
I think we should provide two different mechanisms here.

I currently don't know what semantic is expected
for xsltStyleGetExtData() at transformation time.
  
Some results of poking at the code:

I removed the call to xsltStyleGetExtData() in
xsltRegisterExtPrefix(). This healed the
libxslt:test() function initialization (it was
called only once now). But on the other hand this
produced name-clashes for func:function (read above for
the reasons). xsltStyleGetExtData() is clearly not
able to create per-stylesheet information if the
modules are registered with a deep-first processing
of the import-tree. I don't see a way of fixing
the one without breaking the other.

I noticed that the import-tree lookup in
xsltStyleGetExtData() will only work as expected if
it is started with the main stylesheet, since it won't
see previous-sibling-imports:

main A
  |
  -- import B
  |
  -- import C - a lookup here won't see A and B
     |
     -- import D


I tend to think that we should solve this by introducing
a function which explicitely registers the module data
*per* stylesheet - to be used for user-defined data
elements: xsltStyleGetUserDefinedData(). Plus
eliminate the module-initialization in xsltRegisterExtPrefix().


Regards,

Kasimier



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]