- From: Abel Braaksma <abel.braaksma@xs4all.nl>
- Date: Sun, 11 Oct 2015 02:29:58 +0200
- To: "'Michael Kay'" <mike@saxonica.com>, "'Public XSLWG'" <public-xsl-wg@w3.org>
- Message-ID: <344001d103bb$f6a79300$e3f6b900$@xs4all.nl>
Hmm, I seem to have forgotten that the returned node must have the equivalent focus as the current node (the below function sets the focus at the root of the returned tree). Here's an adjusted alternative, but it loses a bit (large bit) of its charm ;). The function f:copy-ancestors-then-self() was not changed. It is still streamable (the non-streamable version of the same was a bit terser). I am wondering if this can perhaps be more conveniently expressed in an accumulator? <xsl:function name="f:snapshot" as="item()*"> <xsl:param name="items" as="item()*"/> <xsl:copy-of select="$items ! ( let $name := name(), $is-ns-or-at := self::namespace-node() or self::attribute(), $cnt := count(ancestor::node()) return if(. instance of node() and not(($is-ns-or-at) and not(.[..]))) then f:copy-ancestors-then-self(., 0)/descendant-or-self::node()[$cnt]/( if($is-ns-or-at) then (@* | namespace::*)[name() = $name] else ./node()) else .)"/> </xsl:function> <xsl:function name="f:copy-ancestors-then-self" as="node()?"> <xsl:param name="node" as="node()"/> <xsl:param name="ancestor-pos" as="xs:integer"/> <xsl:variable name="current-ancestor" select="count($node/ancestor::node()) - $ancestor-pos" /> <!-- if the snapshot anchor element is a parentless root node, or the document-node --> <xsl:copy-of select="$node[root($node) is $node]"/> <!-- otherwise (the @select serves as an implicit if-statement) --> <xsl:copy select="$node/ancestor::node()[$current-ancestor]"> <xsl:copy-of select="$node/ancestor::node()[$current-ancestor]/attribute::*"/> <xsl:sequence select=" f:copy-ancestors-then-self($node, $ancestor-pos + 1), copy-of($node[count($node/ancestor::node()) = $ancestor-pos + 1])" /> </xsl:copy> </xsl:function> From: Abel Braaksma [mailto:abel.braaksma@xs4all.nl] Sent: Saturday, October 10, 2015 6:32 PM To: 'Michael Kay'; 'Public XSLWG' Subject: RE: snapshot() function - bug 29141 While I agree that your approach looks simpler to what is currently present in the spec, it is not streamable anymore (you apply templates to the parent axis). I gave it a try myself and came up with what I think is a simpler approach (if not simpler, at least it's shorter). It seems to work with a bunch of test cases, including namespace and attribute nodes as starting points, and with non-nodes. Since it uses xsl:copy and xsl:copy-of, their defaults for copying namespaces work with the description of fn:snapshot. We could add copy-accumulators="true" (btw, it is only available on xsl:copy-of, not on xsl:copy), even though the current prose already says that this should be assumed. In addition, this solution is streamable (but only by virtue of implementation optimization, it does not fit the streamability rules we have, which will "think" that there are two downward expressions below and won't statically detect that there aren't; it can be rewritten in such a way without much trouble, if we'd want that). <xsl:function name="f:snapshot" as="item()*"> <xsl:param name="items" as="item()*"/> <xsl:copy-of select="$items ! (if(. instance of node()) then f:copy-ancestors-then-self(., 0) else .)"/> </xsl:function> <xsl:function name="f:copy-ancestors-then-self" as="node()?"> <xsl:param name="node" as="node()"/> <xsl:param name="ancestor-pos" as="xs:integer"/> <xsl:variable name="current-ancestor" select="count($node/ancestor::node()) - $ancestor-pos" /> <!-- if the snapshot anchor element is a parentless root node, or the document-node --> <xsl:copy-of select="$node[root($node) is $node]"/> <!-- otherwise (the @select serves as an implicit if-statement) --> <xsl:copy select="$node/ancestor::node()[$current-ancestor]"> <xsl:copy-of select="$node/ancestor::node()[$current-ancestor]/attribute::*"/> <xsl:sequence select=" f:copy-ancestors-then-self($node, $ancestor-pos + 1), copy-of($node[count($node/ancestor::node()) = $ancestor-pos + 1])" /> </xsl:copy> </xsl:function> Thanks, Abel From: Michael Kay [mailto:mike@saxonica.com] Sent: Friday, October 09, 2015 7:47 PM To: Public XSLWG Subject: snapshot() function - bug 29141 In implementing the changes for the snapshot() function (making it apply to any kind of item and clarifying that it snapshots every item in a sequence), I found myself feeling that there must be a more elegant XSLT implementation of the function than the one we currently publish. This is what I came up with. It avoids the horrible search of the result tree to find the node that corresponds to the node where you started, doing this by always returning the relevant node after adding each new ancestor. It appears to work! Like the existing code, I’m not sure it captures what happens with accumulators. It’s horribly inefficient (in Saxon) because it makes a new copy of the whole tree once for each ancestor that gets added, but who cares? Comments? <xsl:function name="f:snapshot" as="item()*"> <xsl:param name="input" as="item()*"/> <xsl:apply-templates select="$input" mode="snapshot"/> </xsl:function> <!-- for atomic values and function items, return the item unchanged --> <xsl:template match="." mode="snapshot"> <xsl:sequence select="."/> </xsl:template> <!-- for a parentless node, return a deep copy --> <xsl:template match="(/ | node() | @* | namespace-node())[not(..)]" mode="snapshot"> <xsl:copy-of select="."/> </xsl:template> <!-- for an element with a parent, graft it to a copy of its parent; then return the only child element of this copied parent. --> <xsl:template match="*[..]" mode="snapshot" as="element()"> <xsl:sequence select="f:graft-to-parent(.)/*"/> </xsl:template> <!-- for an attribute with a parent, graft it to a copy of its parent; then return the corresponding attribute of this copied parent. --> <xsl:template match="@*[..]" mode="snapshot" as="attribute()"> <xsl:sequence select="f:graft-to-parent(.)/@*[node-name(.) = node-name(current())]"/> </xsl:template> <!-- for a namespace node with a parent, graft it to a copy of its parent; then return the corresponding namespace node of this copied parent. --> <xsl:template match="namespace-node()[..]" mode="snapshot" as="namespace-node()"> <xsl:sequence select="f:graft-to-parent(.)/namespace-node()[local-name(.) = local-name(current())]"/> </xsl:template> <!-- make a copy of the parent of a supplied node, with a copy of the supplied node attached as a child/attribute/namespace of the new parent as appropriate --> <xsl:function name="f:graft-to-parent" as="node()"> <xsl:param name="n" as="node()"/> <xsl:apply-templates select="$n/.." mode="snapshot-ancestor"> <xsl:with-param name="child" select="$n"/> </xsl:apply-templates> </xsl:function> <!-- make a copy of the parent of a supplied node, with a copy of the supplied node attached as a child/attribute/namespace of the new parent as appropriate, grafted on to a copy of its own parent, recursively --> <xsl:template match="node()[..]" mode="snapshot-ancestor"> <xsl:param name="child" required="yes"/> <xsl:apply-templates select=".." mode="snapshot-ancestor"> <xsl:with-param name="child" as="node()"> <xsl:copy> <xsl:copy-of select="@*"/> <xsl:copy-of select="$child"/> </xsl:copy> </xsl:with-param> </xsl:apply-templates> </xsl:template> <!-- make a copy of the parent of a supplied node, with a copy of the supplied node attached as a child/attribute/namespace of the new parent as appropriate, in the case where the parent is the root of the tree --> <xsl:template match="/ | node()[not(..)]" mode="snapshot-ancestor"> <xsl:param name="child" required="yes"/> <xsl:copy> <xsl:copy-of select="@*"/> <xsl:copy-of select="$child"/> </xsl:copy> </xsl:template> </xsl:stylesheet>
Received on Sunday, 11 October 2015 00:30:42 UTC