[XSL-LIST Mailing List Archive Home] [By Thread] [By Date] [Recent Entries] [Reply To This Message] Re: Problems grouping nested items within a completely
Hi Heiko. I think now we're getting somewhere:-) Please let me revisit two areas to see if I understood the grouping *exactly* right. First, it's the for-each-group statement in the 'Chapter' template. If I understand it correctly, it defines four groups, the first selecting the first para[@pgftag='Body text'], the second selecting the first para[@pgftag='Bullet text'] since it matches the second pattern of not being immediately preceded by a para with one of the pgftag attribute values 'Bulleted text', 'Bullet sub', or 'Note'. In effect, all the following para elements with one of these three attribute values are associated with that group (six in total). Then, there is a third group with a single 'Body text', and a final group with two 'Bulleted text', because the subsequent 'Chapter' is not part of the population. Second, the for-each-group statement in the 'Bulleted text' template in 'trigger' mode. Within the current group of six nodes, groups are started with one of 'Bulleted next' and 'Node', so the first group comprises the initial 'Bulleted text', joined by the following two 'Bullet sub' paras, with three further groups each consisting of a single node, 'Note' once, and 'Bulleted text' twice. Only then it dawned on me that the condition "count(current-group()) > 1 work" really makes sense since the first group is a sequence of three nodes, the latter two being 'Bullet sub' so that you can boldly go ahead and open a nested ul. Okay... So... this means that in order to make this pattern of yours universally work for the complete document(s), I have to identify all nodes that may appear as first node after para[@pgftag='Chapter'] (which are 'Body text', 'Bulleted text', 'Step head', 'Checklist', 'Note', 'Tip', 'Warning box', 'Example' and the proper element <table>)? Because this is where I think your code still pretty much relies on the sequence of nodes as given in my original sample file. Then, there are different hierarchical levels, ordered in the sequence 'Preface title', 'Chapter', and 'Appendix title', each of them may have properly nested subdivisions ('1st Section', '2nd Section', '3rd Section', '4th Section'). And technically, they are all para elements and siblings of one another (and any of them can start with the above list of elements). And notes may appear everywhere, yes. Can I somehow design templates in such a way that I don't need single templates for all of these hierarchy elements? (Yes, I'm still quite an XSLT 2.0 illiterate...) Now, for the sample data. Let's add this after the first para[@pgftag='Note']: <para pgftag="1st Section"> <paraline>Subheading</paraline> </para> A legitimate scenario that occurs here and there in the data, unfortunately. We cannot decide whether the 'Note' is part of the second 'Bullet sub', or part of the first 'Bulleted text' or not part of these lists, instead standing alone! (Please forget about the fact that we effectively have a very short first bullet list with only a single entry...). Then I would argue that this note is standalone if it is not followed by further 'Bullet sub's or 'Bulleted text's. I'm sorry if you might get an impression of letting you write most of the stylesheets. I just try to understand how to best tackle this rather complicated case with different levels of grouping. If that is rather obvious to the members of this group, I apologize, but it looks pretty complicated to me and I have not enough insight into XSLT 2.0 (yet), to know if there are any further features helping me out here. Thanks so far! Frank -----Original Message----- From: Heiko Niemann kontakt@xxxxxxxxxxxxxxxx [mailto:xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx] Sent: Freitag, 8. August 2014 01:11 To: xsl-list@xxxxxxxxxxxxxxxxxxxxxx Subject: Re: Problems grouping nested items within a completely flat structure Hi Frank, well yes I had to make some assumptions to write the code and one was that your sources would not change. :) I also assumed that 'Body text' could be the trigger to start a group. But as far as I understand now also 'Bulleted text' can. I guess the tricky part here is that there is no explicit para element that tells you where a list starts and that the first 'Bulleted text' of a group is the initial item of the group and part of the list at the same time which means you have to use modes. So now I determine the beginning of a list as follows: the preceding sibling of the first 'Bulleted text' may not be 'Bulleted text', 'Bullet sub' or 'Note' which should be the same as: has to be 'Chapter' or 'Body text'. You mentioned more types of para elements - maybe you could provide some samples. Could you also tell whether 'Note' can be placed anywhere or is it always part of a list. Anyhow here is the modified version which still just takes the sources of your first post into account. Regards, Heiko <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"> <xsl:output method="xml" encoding="UTF-8"/> <xsl:template match="/textflow"> <book> <xsl:for-each-group select="para" group-starting-with="para[@pgftag eq 'Chapter']"> <xsl:apply-templates select="."/> </xsl:for-each-group> </book> </xsl:template> <xsl:template match="para[@pgftag eq 'Chapter']"> <chapter> <title> <xsl:apply-templates select="*"/> </title> <xsl:for-each-group group-starting-with="para[@pgftag eq 'Body text']| para[@pgftag eq 'Bulleted text'][preceding-sibling::para[1][not(@pgftag = ('Bulleted text', 'Bullet sub', 'Note'))]]" select="current-group() except ."> <xsl:apply-templates select="." mode="trigger"/> </xsl:for-each-group> </chapter> </xsl:template> <xsl:template match="para[@pgftag eq 'Body text']" mode="trigger"> <p> <xsl:apply-templates select="*"/> </p> </xsl:template> <xsl:template match="para[@pgftag eq 'Bulleted text']" mode="trigger"> <ul> <xsl:for-each-group select="current-group()" group-starting-with="para[@pgftag = ('Bulleted text', 'Note')]"> <xsl:apply-templates select="." mode="listitem"/> </xsl:for-each-group> </ul> </xsl:template> <xsl:template match="para[@pgftag eq 'Bulleted text']" mode="listitem"> <li> <xsl:apply-templates select="*"/> </li> <xsl:if test="count(current-group()) > 1"> <ul> <xsl:for-each-group select="current-group() except ." group-starting-with="para[@pgftag eq 'Bullet sub']"> <xsl:apply-templates select="."/> </xsl:for-each-group> </ul> </xsl:if> </xsl:template> <xsl:template match="para[@pgftag eq 'Bullet sub']"> <li> <xsl:apply-templates select="*"/> </li> </xsl:template> <xsl:template match="para[@pgftag eq 'Note']" mode="listitem"> <note> <xsl:apply-templates select="*"/> </note> </xsl:template> <xsl:template match="paraline|xref"> <xsl:apply-templates/> </xsl:template> <xsl:template match="render"> <xsl:choose> <xsl:when test="@charformat eq 'Emphasis'"> <em> <xsl:value-of select="."/> </em> </xsl:when> <xsl:when test="@charformat eq 'Bold'"> <b> <xsl:value-of select="."/> </b> </xsl:when> <xsl:otherwise> <xsl:value-of select="."/> </xsl:otherwise> </xsl:choose> </xsl:template> <!--next template for testing purposes only--> <xsl:template match="*"> <bah/> </xsl:template> </xsl:stylesheet> > Heiko, > > thanks for your approach. While this helped me to grasp the grouping > concept in XSLT 2.0 a little bit better (but only a little bit), I > noticed that your solution is specifically tailored to the exact > sequence of nodes that I used in the example file. But, unfortunately, > life doesn't work that way:-) If, e.g. I put the first > para[@pgftag='Body text'] down to right after the note, the stylesheet yields: > > <book> > <chapter> > <title>Introduction</title> > <p>Display the online help as follows:</p> > <ul> > <li>Display the online help as follows:</li> > <ul> > <li>To view the help for a panel, press the help (PF1) > key.</li> > </ul> > </ul> > <p>This chapter expains...</p> > <ul> > <li>To view the help for an input field or select a parameter > from a pop-up window, press PF1.</li> > <li>Check relevant sections of <em>XXXX</em>.</li> > <li>Visit our web site to get...</li> > </ul> > [...] > > Clearly not what is intended. So what is the right strategy to solve > this in a generic way? Sure, you'd want to group all sibling elements > to chapter as long as they are not indicating a new chapter or a subdivision. > But then? You cannot go for a group like > > <xsl:for-each-group group-starting-with="para[@pgftag eq 'Body > text']" select="current-group() except ."> > <p> > <xsl:apply-templates select="*"/> > </p> > <ul> > <xsl:apply-templates select="."/> > </ul> > </xsl:for-each-group> > > when you don't know what first child element you have. Instead, I'd > imagine templates that can match single paras and don't need no know > about their context (like para[@pgftag='Body text'] or > para[@pgftag='Note']. But as soon as you have list structures I > currently don't see a solution that goes beyond grouping consecutive > list items and if something like a note drops in, you have to regard > the following list item as the first item of a new list. > And here I'm back to square one. I cannot e.g. use > > <xsl:for-each-group group-starting-with="para[@pgftag eq 'Bullet > text']" select="current-group() except ."> > > within the chapter template, since I don't know if and when such a > list can be expected, so to me it looks more like: > > <xsl:template match="para[@pgftag='Chapter']"> > <chapter><title><xsl:apply-templates/></title> > <xsl:apply-templates select="all siblings that are in the > same chapter until a subdivision comes up"/> > </chapter> > </xsl:template> > > <xsl:template match="para[@pgftag='Bulleted text'][1]"> > <ul> > <xsl:for-each-group select="following-sibling::*" > group-adjacent="name()"> > <xsl:apply-templates select="." mode="listitem"/> > </xsl:for-each-group> > </ul> > </xsl:template> > > But Saxon doesn't like that for-each-group and complains: "the only > axes allowed in a pattern are the child and attribute axes". > To complicate matters, subdivisions like '1st Section', ..., '4th Section' > may be nested, so a chapter may consist either of simple content, as > shown here, or some overview, followed by one or more subsections... >>From what I've seen so far, I cannot deduce a generic solution to my >>problem. Probably I'm still missing something elementary here... > > Thanks so far, > Frank > > > -----Original Message----- > From: Heiko Niemann kontakt@xxxxxxxxxxxxxxxx > [mailto:xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx] > Sent: Mittwoch, 6. August 2014 18:09 > To: xsl-list@xxxxxxxxxxxxxxxxxxxxxx > Subject: Re: Problems grouping nested items within a completely > flat structure > > Hi, > > this should get you close to the desired result. Just add more > templates necessary. > > Regards, > Heiko > > > > <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" > version="2.0"> > <xsl:output method="xml" encoding="UTF-8"/> > > <xsl:template match="/textflow"> > <book> > <xsl:for-each-group select="para" > group-starting-with="para[@pgftag eq 'Chapter']"> > <xsl:apply-templates select="."/> > </xsl:for-each-group> > </book> > </xsl:template> > > <xsl:template match="para[@pgftag eq 'Chapter']"> > <chapter> > <title> > <xsl:apply-templates select="*"/> > </title> > <xsl:for-each-group group-starting-with="para[@pgftag eq 'Body > text']" select="current-group() except ."> > <p> > <xsl:apply-templates select="*"/> > </p> > <ul> > <xsl:apply-templates select="."/> > </ul> > </xsl:for-each-group> > </chapter> > </xsl:template> > > <xsl:template match="para[@pgftag eq 'Body text']"> > <xsl:for-each-group group-starting-with="para[@pgftag = ('Bulleted > text', 'Note')]" select="current-group() except ."> > <xsl:apply-templates select="."/> > </xsl:for-each-group> > </xsl:template> > > <xsl:template match="para[@pgftag eq 'Bulleted text']"> > <li> > <xsl:apply-templates select="*"/> > </li> > <xsl:if test="count(current-group()) > 1"> > <ul> > <xsl:for-each-group select="current-group() except ." > group-starting-with="para[@pgftag eq 'Bullet sub']"> > <xsl:apply-templates select="."/> > </xsl:for-each-group> > </ul> > </xsl:if> > </xsl:template> > > <xsl:template match="para[@pgftag eq 'Bullet sub']"> > <li> > <xsl:apply-templates select="*"/> > </li> > </xsl:template> > > <xsl:template match="para[@pgftag eq 'Note']"> > <note> > <xsl:apply-templates select="*"/> > </note> > </xsl:template> > > <xsl:template match="paraline|xref"> > <xsl:apply-templates/> > </xsl:template> > > <xsl:template match="render"> > <xsl:choose> > <xsl:when test="@charformat eq 'Emphasis'"> > <em> > <xsl:value-of select="."/> > </em> > </xsl:when> > <xsl:when test="@charformat eq 'Bold'"> > <b> > <xsl:value-of select="."/> > </b> > </xsl:when> > <xsl:otherwise> > <xsl:value-of select="."/> > </xsl:otherwise> > </xsl:choose> > </xsl:template> > > <!--next template for testing purposes only--> > <xsl:template match="*"> > <bah/> > </xsl:template> > > </xsl:stylesheet> > > > > >> Hi. >> >> While there is a lot of information about grouping available, I still >> have problems applying it to my particular case of a document-centric >> XML file. >> Obviously I havenCB"C"bB,C"bB"t yet understood it fully. >> >> This is my (redacted) source. Please excuse its length, but this >> better illustrates my problem. I use Saxon 9HE, but I am open to >> both, XSL 1.0 or >> 2.0 solutions. >> >> <textflow tftag=CB"C"bB,CBACB"C"bB,CB> >> <para pgftag=CB"C"bB,CBChapterCB"C"bB,CB> >> <paraline>Introduction</paraline> >> </para> >> <para pgftag="Body text"> >> <paraline>This chapter expains...</paraline> >> </para> >> <para pgftag="Bulleted text"> >> <paraline>Display the online help as follows:</paraline> >> </para> >> <para pgftag="Bullet sub"> >> <paraline>To view the help for a panel, press the help >> (PF1) key.</paraline> >> </para> >> <para pgftag="Bullet sub"> >> <paraline>To view the help for an input field or select a >> parameter from a pop-up window, </paraline> >> <paraline>press PF1.</paraline> >> </para> >> <para pgftag="Note"> >> <paraline>If you do not specify a required parameter, or >> enter an incorrect one, XXXX </paraline> >> <paraline>will prompt you for the correct >> information.</paraline> >> </para> >> <para pgftag="Bulleted text"> >> <paraline>Check relevant sections of <render >> charformat="Emphasis">XXXX</render>.</paraline> >> </para> >> <para pgftag="Bulleted text"> >> <paraline>Visit our web site to get...</paraline> >> </para> >> <para pgftag="Body text"> >> <paraline>The topics covered are:</paraline> >> </para> >> <para pgftag="Bulleted text"> >> <paraline> >> <xref srctext="55167: 1st Section: What is >> XXX?"><render charformat="Bold">What is XXX?</render></xref> >> </paraline> >> </para> >> <para pgftag="Bulleted text"> >> <paraline> >> <xref srctext="55167: 1st Section: How Does XXX >> Work?"><render charformat="Bold"> How Does XXX Work?</render></xref> >> </paraline> >> </para> >> <para pgftag=CB"C"bB,CBChapterCB"C"bB,CB> >> <paraline>Next chapter</paraline> >> </para> >> </textflow> >> >> The idea is, quite obviously, grouping the relevant list items, so >> youCB"C"bB,C"bB"d end up (ideally!) with something like e.g.: >> >> <book> >> <chapter> >> <title>Introduction</title> >> <p>This chapter explains...</p> >> <ul> >> <li>Display the online help as follows:</li> >> <ul> >> <li>To view the help for a panel, press the help (PF1) >> key.</li> >> <li>To view the help for an input field or select a >> parameter from a pop-up window, press PF1.</li> >> </ul> >> <note> If you do not specify a required parameter, or enter >> an incorrect one, XXXX will prompt you for the correct >> information.</note> >> <li>Check relevant sections of <em>XXXX</em>.</li> >> <li>Visit our web site to get ...</li> >> </ul> >> <p> The topics covered are:</p> >> <ul> >> <li><b>What is XXX?</b></li> >> <li><b>How Does XXX Work?</b></li> >> </ul> >> </chapter> >> <chapter> >> <title>Next chapter</title> >> </chapter> >> </book> >> >> >> As you can see, the source is a completely flat, linear sequence from >> which I have to establish every kind of structure. Therefore, I use >> something like >> >> <xsl:template match=CB"C"bB,CBtextflowCB"C"bB,CB> >> <book><xsl:apply-templates/></book> >> </xsl:template> >> >> <xsl:template >> match=CB"C"bB,CBpara[@pgftag=CB"C"bB,C"bB"ChapterCB"C"bB,C"bB" ]CB"C"bB,CB> >> <xsl:variable name="chapter-id" select="generate-id()"/> >> <chapter> >> <title><xsl:apply-templates/></title> >> <xsl:apply-templates >> select="following-sibling::*[not(self::*[@pgftag='Chapter'])] >> >> [generate-id(preceding-sibling::para[@pgftag='Chapter'][1]) >> = $chapter-id]"/> >> </chapter> >> </xsl:template> >> >> <xsl:template match=CB"C"bB,CBpara[@pgftag=CB"C"bB,C"bB"Bulleted >> textCB"C"bB,C"bB"]CB"C"bB,CB>... >> >> That is, I canCB"C"bB,C"bB"t imagine having a single template matching >> textflow in which I apply <xsl:for-each-group> for all kinds of >> different paras. >> Instead, I use Muenchian grouping (yep, starting with XSL 1.0, but >> now I use 2.0), but ran into serious recursion trouble when fiddling >> with nested chapter and list structures. >> The other principal problem is how to decide when a structure has >> ended, because all elements are on the same sibling axis. Now, a >> chapter ends, when another <para pgftag=CB"C"bB,C"bB"ChapterCB"C"bB,C"bB"> or >> some <para pgftag=CB"C"bB,C"bB"AppendixCB"C"bB,C"bB"> appears. But there is no >> way to decide when the first bulleted list in the example really >> ends, since the list items may include other elements such as notes >> or nested lists. You could only use criteria such as CB"C"bB,CbThis list >> has ended, when the next paragraph is e.g. >> <para @pgftag=CB"C"bB,C"bB"Body textCB"C"bB,C"bB"> or <para >> @pgftag=CB"C"bB,C"bB"ChapterCB"C"bB,C"bB"> appearsCB"C"bB,CB. >> >> Now, if anyone could point me in the right direction, ICB"C"bB,C"bB"d be >> very grateful, since itCB"C"bB,C"bB"s bugging me for some time now. And, >> please apologize the length... >> >> Thank you, >> Frank Software AG b Sitz/Registered office: UhlandstraCe 12, 64297 Darmstadt, Germany b Registergericht/Commercial register: Darmstadt HRB 1562 - Vorstand/Management Board: Karl-Heinz Streibich (Vorsitzender/Chairman), Dr. Wolfram Jost, Arnd Zinnhardt; - Aufsichtsratsvorsitzender/Chairman of the Supervisory Board: Dr. Andreas Bereczky - http://www.softwareag.com
|
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
|