Re: [xsl] Joining list fragments
2020-05-03 08:28:17
Ok, it’s a triply-nested grouping now
(starting-with/adjacent/starting-with):
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="3.0"
xmlns:my="http://localhost/"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="my xs">
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="*[ol[@data-meta = 'listlevel=start']]">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<!-- named template: then you can call it recursively for lower
list levels
(not used here) -->
<xsl:call-template name="collect">
<!-- We are grouping a) the list *items* in both special list
types and
b) any other children of the current div. These a) and b)
nodes are
returned by my:atomic-items() in document order: -->
<xsl:with-param name="nodes" select="my:atomic-items(.)"/>
</xsl:call-template>
</xsl:copy>
</xsl:template>
<xsl:function name="my:atomic-items" as="node()*">
<xsl:param name="context" as="element()"/>
<xsl:variable name="special-lists" as="element(ol)*"
select="$context/( ol[@data-meta = 'listlevel=start']
| ol[@data-meta = 'listlevel=continue']
)" />
<xsl:sequence
select="$context/* except $special-lists | $special-lists/li"/>
</xsl:function>
<xsl:template name="collect">
<xsl:param name="nodes" as="node()*"/>
<!-- First grouping: Start with
ol[@data-meta = 'listlevel=start']/li[1] -->
<xsl:for-each-group select="$nodes"
group-starting-with="li[parent::ol[@data-meta = 'listlevel=start']]
[. is ../*[1]]">
<xsl:choose>
<!-- The first group can, in principle, start with any other node
that precedes the actual first list starting item. Such an
uninteresting initial group will be processed in otherwise. -->
<xsl:when test="parent::ol[@data-meta = 'listlevel=start']">
<!-- This is a grouping that may result in at most two groups:
The current list and anything non-collected and non-continuing
that might come after it (<p>Other arbitrary content</p>): -->
<xsl:for-each-group select="current-group()"
group-adjacent="exists(self::li)
or
@data-meta = ('collect',
'listlevel=continue')">
<xsl:choose>
<xsl:when test="current-grouping-key()">
<!-- We re-create the surrounding ol: -->
<ol>
<!-- The context element is the first li
in an ol[@data-meta = 'listlevel=start'] -->
<xsl:copy-of select="../@data-meta"/>
<xsl:for-each-group select="current-group()"
group-starting-with="li[not(@data-meta =
'listitem=continue')]">
<!-- Create a new li: -->
<xsl:copy>
<xsl:copy-of select="@data-meta"/>
<xsl:apply-templates
select=" current-group()/self::li/node()
| current-group()[empty(self::li)]"/>
</xsl:copy>
</xsl:for-each-group>
</ol>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="current-group()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="current-group()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>
Result:
<div>
<h2 id="E2">Item with content to be joined follows div to collect</h2>
<div>
<ol data-meta="listlevel=start">
<li>
<p>1st item</p>
<div class="box" data-meta="collect">
<p>Hint</p>
</div>
<p>Para ff</p>
</li>
<li>
<p>2nd item</p>
</li>
</ol>
<p>Other arbitrary content</p>
</div>
</div>
This grouping, although it might seem convoluted, is better than
cherry-picking and reassembling nodes, because the input is always
completely covered (selected) and dealt with (apart from the special ol
elements that are not part of the grouping population and need to be
recreated). It's less prone to losing some of the input, and in general
also less prone to duplicating content.
I hope it does the job right. If not, I'll create a Gist on Github and
we can edit it until it works.
Gerrit
On 03.05.2020 12:07, Michael Kay mike(_at_)saxonica(_dot_)com wrote:
On 3 May 2020, at 09:54, gerrit(_dot_)imsieke(_at_)le-tex(_dot_)de
<mailto:gerrit(_dot_)imsieke(_at_)le-tex(_dot_)de>
<xsl-list-service(_at_)lists(_dot_)mulberrytech(_dot_)com
<mailto:xsl-list-service(_at_)lists(_dot_)mulberrytech(_dot_)com>> wrote:
This looks like a nested group-starting-with / group-adjacent to me at
first glance.
My reaction too. Of course the various options on xsl:for-each-group are
"stereotypes" - they handle 90% of grouping requirements but there are
cases they can't cope with, and in that case you have to go to
lower-level solutions. The "window" clause in XQuery is more powerful -
perhaps it takes you from the 90% level to 99%. There's also a small
minority of cases where XQuery's "for $x at $p in..." can be very
valuable. But generally, anything that involves manipulating a sequence
using integer positions smells of desperation, and makes one look hard
to see if there isn't some better way.
Michael Kay
Saxonica
Sent from MailDroid <https://goo.gl/ODgwBb>
-----Original Message-----
From: "Michael Müller-Hillebrand mmh(_at_)docufy(_dot_)de <mailto:mmh(_at_)docufy(_dot_)de>"
<xsl-list-service(_at_)lists(_dot_)mulberrytech(_dot_)com
<mailto:xsl-list-service(_at_)lists(_dot_)mulberrytech(_dot_)com>>
To: xsl-list(_at_)lists(_dot_)mulberrytech(_dot_)com
<mailto:xsl-list(_at_)lists(_dot_)mulberrytech(_dot_)com>
Sent: Sun, 03 May 2020 10:33
Subject: Re: [xsl] Joining list fragments (was: Handling position in
non-atomic sequences)
Am 02.05.2020 um 13:43 schrieb Michael Kay:
You're talking about a solution, but I don't think you've told us
what the problem is. What are you trying to achieve?
Fair enough,
sometimes one is so deep wrapping one’s thoughts around a detail
problem, that the broader picture gets lost.
We want to join list fragments and some content in between them. An
HTML-ish version of the input looks like this:
<div>
<h2id="E2">Item with content to be joined follows div to collect</h2>
<div>
<oldata-meta="listlevel=start">
<li>
<p>1st item</p>
</li>
</ol>
<divclass="box"data-meta="collect">
<p>Hint</p>
</div>
<oldata-meta="listlevel=continue">
<lidata-meta="listitem=continue">
<p>Para ff</p>
</li>
<li>
<p>2nd item</p>
</li>
</ol>
<p>Other arbitrary content</p>
</div>
</div>
Every broken list sequence starts with data-meta="listlevel=start" and
a list or a list item that is supposed to be joined with the start
list is marked using data-meta="listlevel=continue" and
data-meta="listitem=continue". There can be any number of collect
items between lists and multiple continue lists, but it is guaranteed
that whatever needs to be collected will end with a list. In DTD
content model notation: startList, (collectItem*, continueList)+
The lists are not limited to a single level. Gladly, if there is a
"listitem=continue" in a continue list, it is guaranteed to be at the
same level the previous list ends.
The task is to add to the last item of the previous list:
* all content marked "collect" between the lists; other content would
break the process
* content of the next list’s first list item if marked "listitem=continue"
The remaining content of each continue list would be added as
additional items to the start list.
The desired result for the input data above would look like this:
<div>
<h2id="E2">Item with content to be joined follows div to collect</h2>
<div>
<oldata-meta="listlevel=start">
<li>
<p>1st item</p>
<divclass="box"data-meta="collect">
<p>Hint</p>
</div>
<p>Para ff</p>
</li>
<li>
<p>2nd item</p>
</li>
</ol>
<p>Other arbitrary content</p>
</div>
</div>
All the "collect" content and continue lists are at the same
hierarchical level as the start list. So my initial strategy was to
begin with the start list and collect all "valid" siblings (collect,
listlevel="continue") by walking the following-sibling axis one by
one. The result would be a sequence of elements that have to be
processed into the start list at various locations.
My original question was how to find the position of the next <ol> in
that sequence to easily use subsequence(). And I got some helpful
pointers for that (I even looked at xsltfunctions.com
<http://xsltfunctions.com/>, but due to my xsl:copy-of decision node
identity seemed not to be an option), thanks a lot!
But currently I am doubting my general strategy and I have the feeling
I am missing one very obvious thing. The task is basic tree
transformation, and my current ideas all look very complicated. For
the simplified data I cannot yet share XSLT code.
Basically I use a mode to process the continue lists and their items.
Within each template I add special handling for "continue" content
(ignoring the elements, just processing their content). For the very
last item in each list I add rules to process the collect elements.
Getting to the content of a <li data-meta="listitem=continue"> and
making sure it is processed only once is where I am stuck at.
Currently I think of putting more effort in the collect phase, e.g. to
already split continue lists in two parts: the continue item, which is
then easily processed with the collect items, and the rest of the
list, which will always be new items or sub-lists.
Pointers if anyone has had success for a problem like this, would be
very welcome. (And I wonder if I have to deal with position in element
sequences at all.)
- Michael Müller-Hillebrand
--~----------------------------------------------------------------
XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list
EasyUnsubscribe: http://lists.mulberrytech.com/unsub/xsl-list/1167547
or by email: xsl-list-unsub(_at_)lists(_dot_)mulberrytech(_dot_)com
--~--
<Prev in Thread] |
Current Thread |
[Next in Thread>
|
- [xsl] Handling position in non-atomic sequences, Michael Müller-Hillebrand mmh@docufy.de
- Re: [xsl] Handling position in non-atomic sequences, Martin Honnen martin(_dot_)honnen(_at_)gmx(_dot_)de
- Re: [xsl] Handling position in non-atomic sequences, Imsieke, Gerrit, le-tex gerrit(_dot_)imsieke(_at_)le-tex(_dot_)de
- Re: [xsl] Handling position in non-atomic sequences, Michael Kay mike(_at_)saxonica(_dot_)com
- Re: [xsl] Joining list fragments (was: Handling position in non-atomic sequences), Michael Müller-Hillebrand mmh@docufy.de
- Re: [xsl] Joining list fragments (was: Handling position in non-atomic sequences), gerrit(_dot_)imsieke(_at_)le-tex(_dot_)de
- Re: [xsl] Joining list fragments (was: Handling position in non-atomic sequences), Michael Kay mike(_at_)saxonica(_dot_)com
- Re: [xsl] Joining list fragments,
Imsieke, Gerrit, le-tex gerrit(_dot_)imsieke(_at_)le-tex(_dot_)de <=
- Re: [xsl] Joining list fragments, Imsieke, Gerrit, le-tex gerrit(_dot_)imsieke(_at_)le-tex(_dot_)de
- Message not available
- Re: [xsl] Joining list fragments, Imsieke, Gerrit, le-tex gerrit(_dot_)imsieke(_at_)le-tex(_dot_)de
- Re: [xsl] Joining list fragments, Michael Müller-Hillebrand mmh@docufy.de
|
|
|