Re: RE: Designs for XSLT functions
Mike Kay wrote: >> > Saxon allows xsl:for-each with saxon:function, but doesn't allow >> > saxon:return within xsl:for-each. >> >> Did you see the use cases that I put forward where it could be >> useful?: >> >> What do you think? > > There's some logic to it: it suggests that <xsl:break> within an ordinary > <xsl:for-each> could also be defined cleanly, as "discard any result nodes > generated beyond this point". Could be useful: > > <xsl:template match="h1"> > <section> > <xsl:for-each select="following-sibling::*"> > <xsl:if test="h1"><xsl:break/></xsl:if> > <xsl:apply-templates/> > </xsl:for-each> > </section> > </xsl:template> > > An alternative to saxon:leading(). I quite like it. I wouldn't like > it if you could achieve this effect by writing a function and not > achieve the same effect inline. It would certainly help with that kind of grouping. The approximate equivalent, of course, is: <xsl:template match="h1"> <section> <xsl:apply-templates select="following-sibling::* [generate-id(following-sibling::h1) = generate-id(current()/following-sibling::h1)]" /> </section> </xsl:template> But I don't see how this effect could be achieved in a function using exsl:result? To build a new node set, you need to either put it in the content of an xsl:variable and then return the value of that variable, or put it in the content of exsl:result. I don't think that exsl:result is (should be) allowed in either of those places. Essentially, allowing xsl:for-each within exsl:function does two things: (a) Gives a way of changing the current node within a function when you need to return nodes from another document. There are three functions in XSLT 1.0 that use information about the document the current node is in: id(), key() and unparsed-entity-uri(). (b) Gives a way of returning a node based on its position in a node list sorted in something other than document order. I think that all other ways of using xsl:for-each within exsl:function can be achieved through other means. [Perhaps it would be clearer to have different ways of achieving the two goals above and forget about xsl:for-each. I can't really think of an intuitive way of doing so. For example: <exsl:in document="document($file-name, $base-node)"> <exsl:result select="key($key-name, $key-value)" /> </exsl:in> <exsl:sort nodes="$nodes"> <xsl:if test="position() = 1"> <exsl:result select="." /> </xsl:if> </exsl:sort> ] I didn't exactly mean it as a way of exiting early from a function, but this is something we're going to have to cope with in some way if we allow anything other than xsl:variable. For example: <exsl:function name="my:foo"> <xsl:if test="$test"><exsl:result select="'foo'" /></xsl:if> <exsl:result select="'bar'" /> </exsl:function> equivalent to: <exsl:function name="my:foo"> <xsl:choose> <xsl:when test="$test"><exsl:result select="'foo'" /></xsl:when> <xsl:otherwise><exsl:result select="'bar'" /></xsl:otherwise> </xsl:choose> </exsl:function> We could just say that exsl:result cannot be instantiated more than once during the processing of the function. That would prevent people from doing the first example above, and it would prevent them from having an xsl:for-each that ever returned more than one exsl:result - either you only choose one node with it (and use it as a way of changing the current node) or you have an xsl:if that picks out the node you want by position (to have access to xsl:sort). Of course there is a demand for building larger node sets that would be nicely catered for with something like exsl:append or exsl:reference-of. However, I think that these can both be achieved with a combination of the above and some recursion. For example, to return the first five nodes in alphabetically sorted order, I could use something like: <xsl:variable select="my:first-alphabetically($nodes, 5)" /> <exsl:function name="my:first-alphabetically"> <exsl:param name="nodes" /> <exsl:param name="number" /> <xsl:choose> <xsl:when test="$number = 1"> <xsl:for-each select="$nodes"> <xsl:sort /> <xsl:if test="position() = 1"> <exsl:result select="." /> </xsl:if> </xsl:for-each> </xsl:when> <xsl:otherwise> <xsl:variable name="first" select="my:first-alphabetically($nodes, 1)" /> <xsl:variable name="rest" select="$nodes[count(.|$first) != 1]" /> <exsl:result select="$first | my:first-alphabetically($rest, $number - 1)" /> </xsl:otherwise> </xsl:choose> </exsl:function> It's not particularly pretty, but at least it's doable. Cheers, Jeni --- Jeni Tennison http://www.jenitennison.com/ XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list
PURCHASE STYLUS STUDIO ONLINE TODAY!
Purchasing Stylus Studio from our online shop is Easy, Secure and Value Priced!
Download The World's Best XML IDE!
Accelerate XML development with our award-winning XML IDE - Download a free trial today!
Subscribe in XML format