Re: xslt 2.0 suggestion -- explicit extensions, and support for next-match

I your explanation you say:

"Of course, you could do without explicit extensions and instead make the
user assign priorities, but this would be less user friendly."

This is not the case because only the highest priority will match. Within a
template there is no way to direct apply-templates to another template
which would also match but which has a lower priority. However, this is
needed to implement the equivalent of "next-match", which is an enveloping
mechanism.

Another thing is that the "extensions" are aware of being an extension. This is
not necessary. I think the power of polymorphism in XSLT would lie in the
ability to express some transformation logic for an element in terms of a
generic selection along some axis, i.e. a selection which is not more specific
than "axis::*".

Such transformation logic would consist of several templates, of which some
could match nodes along the generic axis. The latter would not be executed
if a more specific match for the same context exists elsewhere. However,
both should be executed.

Let's look at an example. Say we have some element "authorgroup", which can
contain "author", "editor" and "corpauthor" in any order and quantity. The
element declaration would be as follows:

<!ELEMENT authorgroup ((author|editor|corpauthor)+)>

The first two templates in the following example only want to set the children
of authorgroup in a comma-separated sequence, without bothering about how these
children will be transformed. The latter will have their own, more specific, template.
When these only match in the context of an authorgroup, they will precede the
first two templates, which will not be executed. This means the generic sequence
logic is broken.

<xsl:template match="authorgroup/child::*[1]">
   <xsl:apply-templates select="child::*"/>
</xsl:template>

<xsl:template match="authorgroup/child::*[position()>1]">
   <xsl:text>, </xsl:text>
   <xsl:apply-templates select="child::*"/>
</xsl:template>

<xsl:template match="authorgroup/author">
   <!-- Something for author -->
</xsl:template>

<xsl:template match="authorgroup/editor">
   <!-- Something for editor -->
</xsl:template>

<xsl:template match="authorgroup/corpauthor">
   <!-- Something for corpauthor -->
</xsl:template>

This could be solved by using priorities. The first two templates would
have priority -1. The sequencing logic would then have to be replicated in the
three consecutive templates like this:

<xsl:template match="authorgroup/child::*[1]" priority="-1">
   <xsl:apply-templates select="child::*"/>
</xsl:template>

<xsl:template match="authorgroup/child::*[position()>1]" priority="-1">
   <xsl:text>, </xsl:text>
   <xsl:apply-templates select="child::*"/>
</xsl:template>

<xsl:template match="authorgroup/child::*[1][name()='author']">
   <!-- Something for author -->
</xsl:template>

<xsl:template match="authorgroup/child::*[position()>1][name()='author']">
   <xsl:text>, </xsl:text>
   <!-- Something for author -->
</xsl:template>

<xsl:template match="authorgroup/child::*[1][name()='editor']">
   <!-- Something for editor -->
</xsl:template>

<xsl:template match="authorgroup/child::*[position()>1][name()='editor']">
   <xsl:text>, </xsl:text>
   <!-- Something for editor -->
</xsl:template>

<xsl:template match="authorgroup/child::*[1][name()='corpauthor']">
   <!-- Something for corpauthor -->
</xsl:template>

<xsl:template match="authorgroup/child::*[position()>1][name()='corpauthor']">
   <xsl:text>, </xsl:text>
   <!-- Something for corpauthor -->
</xsl:template>

This situation is still under control, but suppose the element declaration of
authorgroup was as follows:

<!ELEMENT authorgroup ((author|editor|collab|corpauthor|othercredit)+)>

The stylesheet could for example not specify anything specific for "collab" and
"othercredit" in the context of authorgroup. However, an independent party could
want to add such a specification. To do that, he would have to examine our
stylesheet to discover any generic logic and then replicate it. This creates a
dependency between stylesheets.

Another solution is using modes. It would look like this:

<xsl:template match="authorgroup/child::*[1]">
   <xsl:apply-templates select="." mode="extension"/>
</xsl:template>

<xsl:template match="authorgroup/child::*[position()>1]">
   <xsl:text>, </xsl:text>
   <xsl:apply-templates select="." mode="extension"/>
</xsl:template>

<xsl:template match="authorgroup/author" mode="extension">
   <!-- Something for author -->
</xsl:template>

<xsl:template match="authorgroup/editor" mode="extension">
   <!-- Something for editor -->
</xsl:template>

<xsl:template match="authorgroup/corpauthor" mode="extension">
   <!-- Something for corpauthor -->
</xsl:template>

Now the sequence logic does not have to be replicated, but the extension
templates are linked to the extended ones, while they could also serve a
purpose on their own or be combined in another stylesheet set.

I propose a small extension for this which has very little impact on the
current process model, which says that only one template may match for some
node. The solution would consist of adding the optional "max-priority" attribute
to the "apply-templates" element. Its meaning is that templates with a priority
above max-priority are not considered for the current node. The example could
then be transformed like this:

<xsl:template match="authorgroup/child::*[1]" priority="1">
   <xsl:apply-templates select="." max-priority="0.5"/>
</xsl:template>

<xsl:template match="authorgroup/child::*[position()>1]" priority="1">
   <xsl:text>, </xsl:text>
   <xsl:apply-templates select="." max-priority="0.5"/>
</xsl:template>

<xsl:template match="authorgroup/author">
   <!-- Something for author -->
</xsl:template>

<xsl:template match="authorgroup/editor">
   <!-- Something for editor -->
</xsl:template>

<xsl:template match="authorgroup/corpauthor">
   <!-- Something for corpauthor -->
</xsl:template>

The two apply-templates statements select the current node again but exclude
their containing template in order to avoid a loop. In fact they provide the chance
to more specific ones, if they are around, to fill in their thing.

The solution is not ideal because it still has a co-operative aspect in it,
being the priority. The advantage, however, is that it has no fundamental
impact on the process model. It is still expressed in terms of concepts that
already exist. A simple additional test against the current node and the priority
is needed.

This particular example can be solved in other ways of course. I have chosen it
for the sake of simplicity.

Regards,

Werner.
-- 
Werner Donné  --  Re BVBA
Engelbeekstraat 8		Papenhof 15
B-3300 Tienen			B-3583 Beringen
tel: (+32) 486 425803	e-mail: werner.donne@re.be

Received on Wednesday, 14 November 2001 09:24:28 UTC