Re: [xsl] "Tunneling" a parameter in document order

Subject: Re: [xsl] "Tunneling" a parameter in document order
From: Christian Roth <roth@xxxxxxxxxxxxxx>
Date: Thu, 12 Apr 2012 22:42:07 +0200
>> How do you go about such problems in general? Is there a best practice?
>
> Post a small complete sample input covering the various possibilities,
> along with the required output for that input.
>

Before going further: I do *not* want anyone to create an XSLT that transforms
the sample source to the desired result below! That would be time well wasted.
I'm merely interested in ideas on how to tackle such a kind (or class?) of
problem.

Trying to describe the problem at a high level, that would essentially be:
Keeping a (virtual) global variable's value current while doing a depth-first
traversal of a document, where the variable's value is not scoped by either
the document's element hierarchy, nor the nesting of XSLT template calls.

Hoping to have gotten them right :-), here are source and desired result:

-- source.xml --
<?xml version="1.0" encoding="UTF-8"?>
<document>
  <listdefs>
    <list id="list1" restart="none">
      <leveldef level="1" type="decimal"/>
      <leveldef level="2" type="lower-alpha"/>
      <leveldef level="3" type="lower-roman"/>
    </list>
    <list id="list2" restart="level">
      <leveldef level="1" type="decimal"/>
      <leveldef level="2" type="decimal"/>
      <leveldef level="3" type="decimal"/>
    </list>
  </listdefs>
  <listrefs>
    <ref id="ref1" idref="list1"/>
    <ref id="ref2" idref="list1">
      <override level="1" startat="5"/>
    </ref>
    <ref id="ref3" idref="list2"/>
  </listrefs>
  <body>
    <a>
      <b><item idref="ref1" level="1"/></b>
      <item idref="ref1" level="1"/>
      <c>
        <d><item idref="ref1" level="2"/></d>
      </c>
    </a>
    <item idref="ref1" level="3"/>
    <item idref="ref1" level="2"/>
    <e><item idref="ref1" level="3"/></e>
    <item idref="ref2" level="1"/>
    <item idref="ref2" level="2"/>
    <f>
      <item idref="ref3" level="1"/>
    </f>
    <item idref="ref3" level="2"/>
    <item idref="ref2" level="1"/>
    <g>
      <h><item idref="ref3" level="1"/></h>
    </g>
    <i><item idref="ref3" level="2"/></i>
    <item idref="ref2" level="3"/>
  </body>
</document>
--snip--

-- desired-result.xml --
<document>
  <body>
    <a>
      <b><item num="1" idref="ref1" level="1"/></b>
      <item num="2" idref="ref1" level="1"/>
      <c>
        <d><item num="2.a" idref="ref1" level="2"/></d>
      </c>
    </a>
    <item num="2.a.i" idref="ref1" level="3"/>
    <item num="2.b" idref="ref1" level="2"/>
    <e><item num="2.b.ii" idref="ref1" level="3"/></e>
    <item num="5" idref="ref2" level="1"/>
    <item num="5.c" idref="ref2" level="2"/>
    <f>
      <item num="1" idref="ref3" level="1"/>
    </f>
    <item num="1.1" idref="ref3" level="2"/>
    <item num="6" idref="ref2" level="1"/>
    <g>
      <h><item num="2" idref="ref3" level="1"/></h>
    </g>
    <i><item num="2.1" idref="ref3" level="2"/></i>
    <item num="6.c.iii" idref="ref2" level="3"/>
  </body>
</document>
--snip--


Think of each /document/listdefs/list representing the declaration of a global
3-element array of counters holding the current item number at that level
while traversing the document in document order, and /document/listrefs/ref
each being an indirection to the referenced list, resetting some level of the
counter array when first referenced. Any occurrence of an <item> increments
the counter by 1 for the specified list+level combination unless there's some
reset override defined in the referenced list <ref>, which must only be
applied the first time a <ref> is referred from the document's body. (I know
this is all not too easy to understand, but that is basically how list
numbering in OOXML is spec'd.)

list1 is set to number each level continuously (restart="none"), list2 is set
to restart numbering for a level when a lower level (or higher level - depends
on how you name them...) is incremented (restart="level").

Here's what the 3-element array would look like at each element start during
document traversal, resulting in the depicted numbering string (@num
attribute) to be added to each <item> (see desired-result.xml):

element    list1         list2
-------------------------------
document   0 0 0         0 0 0
body       | | |         | | |
a          | | |         | | |
b          | | |         | | |
item       1 | |         | | |
item       2 | |         | | |
c          | | |         | | |
d          | | |         | | |
item       | 1 |         | | |
item       | | 1         | | |
item       | 2 |         | | |
e          | | |         | | |
item       | | 2         | | |
item       5 | |         | | |   <- new startat value for level 1
item       | 3 |         | | |
f          | | |         | | |
item       | | |         1 | |
item       | | |         | 1 |
item       6 | |         | | |   <- @startat only applies once; subsequent
refs just increment as usual
g          | | |         | | |
h          | | |         | | |
item       | | |         2 0 |   <- level 2 is reset to "0" because a lower
level was incremented
i          | | |         | | |
item       | | |         | 1 |
item       | | 3         | | |
-------------------------------


Christian

Current Thread