xsl -- using math in element subscripts?

W

William Krick

I am writing an XSL transform that converts XML data about vehicles
into XML data that will fill printed forms.

The default form can handle up to 5 vehicles which I handle using
subscripts...

<xsl:for-each select="VEHICLE[1]">
<!-- spit out some stuff about this vehicle -->
</xsl:for-each>
<xsl:for-each select="VEHICLE[2]">
<!-- spit out some stuff about this vehicle -->
</xsl:for-each>
<xsl:for-each select="VEHICLE[3]">
<!-- spit out some stuff about this vehicle -->
</xsl:for-each>
<xsl:for-each select="VEHICLE[4]">
<!-- spit out some stuff about this vehicle -->
</xsl:for-each>
<xsl:for-each select="VEHICLE[5]">
<!-- spit out some stuff about this vehicle -->
</xsl:for-each>

Then, if there are more than 5 vehicles, I need to output them, 5 at a
time, onto one or more "extension" forms as needed. Note that the
extra vehicles will not always be in multiples of 5.

I'm at a loss as to how to handle this in a generic way without
hardcoding conditional forms for every multiple of 5 up to some
arbitrary maximum.

I'm thinking that to handle it generically, I'd need to do something
tricky with math in the subscripts possibly using the mod operator but
I'm not sure if that's even possible.

Has anyone dealt with a similar problem?

I can post more detailed examples if my description isn't clear. Just
ask.
 
S

Soren Kuula

William said:
I am writing an XSL transform that converts XML data about vehicles
into XML data that will fill printed forms.

The default form can handle up to 5 vehicles which I handle using
subscripts...

<xsl:for-each select="VEHICLE[1]">
<!-- spit out some stuff about this vehicle -->
</xsl:for-each>
....

Yes, copy and paste is not the best solution...
Then, if there are more than 5 vehicles, I need to output them, 5 at a
time, onto one or more "extension" forms as needed. Note that the
extra vehicles will not always be in multiples of 5.

I'm at a loss as to how to handle this in a generic way without
hardcoding conditional forms for every multiple of 5 up to some
arbitrary maximum.

I'm thinking that to handle it generically, I'd need to do something
tricky with math in the subscripts possibly using the mod operator but
I'm not sure if that's even possible.

You are right.

I fooled around with it a little. You can use this (I hope) as a template:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">

<xsl:template match="cars">
<report>
<xsl:apply-templates select="car[(position()-1) mod 5 = 0]"/>
</report>
</xsl:template>

<xsl:template match="car[1]">
<first-page>
<xsl:apply-templates select=". | following-sibling::car[position()
&lt; 5]" mode="info"/>
</first-page>
</xsl:template>

<xsl:template match="car[(position()-1)!=0]">
<extra-page>
<xsl:apply-templates select=". | following-sibling::car[position()
&lt; 5]" mode="info"/>
</extra-page>
</xsl:template>

<xsl:template match="car" mode="info">
<carinfo car="{@id}"/>
</xsl:template>

</xsl:stylesheet>


With the input

<cars>
<car id="1"/>
<car id="2"/>
<car id="3"/>
<car id="4"/>
<car id="5"/>
<car id="6"/>
<car id="7"/>
<car id="8"/>
<car id="9"/>
<car id="10"/>
<car id="11"/>
</cars>

you get (after formatting)

[dongfang@skodspand tmp]$ xsltproc car.xsl car.xml | xmllint --format -
<?xml version="1.0"?>
<report>
<first-page>
<carinfo car="1"/>
<carinfo car="2"/>
<carinfo car="3"/>
<carinfo car="4"/>
<carinfo car="5"/>
</first-page>
<extra-page>
<carinfo car="6"/>
<carinfo car="7"/>
<carinfo car="8"/>
<carinfo car="9"/>
<carinfo car="10"/>
</extra-page>
<extra-page>
<carinfo car="11"/>
</extra-page>
</report>


Hope that helped ;)

(you can experiment it yourself to see how it works. This was just to
get you started)

Søren
 
W

William Krick

Soren said:
William said:
I am writing an XSL transform that converts XML data about vehicles
into XML data that will fill printed forms.

I fooled around with it a little. You can use this (I hope) as a template:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">

<xsl:template match="cars">
<report>
<xsl:apply-templates select="car[(position()-1) mod 5 = 0]"/>
</report>
</xsl:template>

<xsl:template match="car[1]">
<first-page>
<xsl:apply-templates select=". | following-sibling::car[position()
&lt; 5]" mode="info"/>
</first-page>
</xsl:template>

<xsl:template match="car[(position()-1)!=0]">
<extra-page>
<xsl:apply-templates select=". | following-sibling::car[position()
&lt; 5]" mode="info"/>
</extra-page>
</xsl:template>

<xsl:template match="car" mode="info">
<carinfo car="{@id}"/>
</xsl:template>

</xsl:stylesheet>

Well, it does seem to work but I'm not sure I understand how.

I don't understand the syntax.

<xsl:apply-templates select="car[(position()-1) mod 5 = 0]"/>

How does "(position()-1) mod 5 = 0" evaluate to a subscript?
It seems like it would evaluate to true/false.

<xsl:apply-templates select=". | following-sibling::car[position() &lt;
5]" mode="info"/>

Then I don't understand what ". |" means. I assume it means: "the
current node boolean OR the thing that follows it".

Regardless, it *does* seem to work. The only issue now is that the
first vehicle in each "page" has to be treated slightly different
because of the morons who created the original XML spec I'm trying to
meet. On each page, there are "year" and "age group" fields for the
vehicles. Notice that the name of the age group on the first vehicle
isn't "AGE GROUP #001" as you'd expect. It's simply "AGE GROUP".
Here's some sample XML output that is very close to the output I'm
generating (with a lot of stuff omitted for brevity). This example has
12 cars...

<FORM>
<FIRST-PAGE>
<FIELD NAME="YEAR #001">1991</FIELD>
<FIELD NAME="AGE GROUP">5</FIELD>
<FIELD NAME="YEAR #002">2007</FIELD>
<FIELD NAME="AGE GROUP #002">3</FIELD>
<FIELD NAME="YEAR #003">1997</FIELD>
<FIELD NAME="AGE GROUP #003">2</FIELD>
<FIELD NAME="YEAR #004">2001</FIELD>
<FIELD NAME="AGE GROUP #004">8</FIELD>
<FIELD NAME="YEAR #005">2003</FIELD>
<FIELD NAME="AGE GROUP #005">6</FIELD>
</FIRST-PAGE>
<EXTRA-PAGE>
<FIELD NAME="COVERED AUTO #01A">6</FIELD>
<FIELD NAME="YEAR #001">1992</FIELD>
<FIELD NAME="AGE GROUP">5</FIELD>
<FIELD NAME="COVERED AUTO #02A">7</FIELD>
<FIELD NAME="YEAR #002">2008</FIELD>
<FIELD NAME="AGE GROUP #002">3</FIELD>
<FIELD NAME="COVERED AUTO #03A">8</FIELD>
<FIELD NAME="YEAR #003">1996</FIELD>
<FIELD NAME="AGE GROUP #003">2</FIELD>
<FIELD NAME="COVERED AUTO #04A">9</FIELD>
<FIELD NAME="YEAR #004">2002</FIELD>
<FIELD NAME="AGE GROUP #004">8</FIELD>
<FIELD NAME="COVERED AUTO #05A">10</FIELD>
<FIELD NAME="YEAR #005">2004</FIELD>
<FIELD NAME="AGE GROUP #005">6</FIELD>
</EXTRA-PAGE>
<EXTRA-PAGE>
<FIELD NAME="COVERED AUTO #01A">11</FIELD>
<FIELD NAME="YEAR #001">1993</FIELD>
<FIELD NAME="AGE GROUP">5</FIELD>
<FIELD NAME="COVERED AUTO #02A">12</FIELD>
<FIELD NAME="YEAR #002">1999</FIELD>
<FIELD NAME="AGE GROUP #002">3</FIELD>
</EXTRA-PAGE>
</FORM>

....the AGE GROUP field is the only one that is different. All the rest
have the correct numbers in their name. Note that the number is the
position of the vehicle on the page, not it's position in the original
XML document.
 
S

Soren Kuula

William said:
Well, it does seem to work but I'm not sure I understand how.

I don't understand the syntax.

<xsl:apply-templates select="car[(position()-1) mod 5 = 0]"/>

How does "(position()-1) mod 5 = 0" evaluate to a subscript?
It seems like it would evaluate to true/false.

What you call a subscript is actually not. It is a predicate (which is
exactly something that, as you say, 'would evaluate to true/false').

The way XPath path evaluation works is a litte bit unlike other things,
but still worth understanding.

In each complete XPath path step:
-- first there is an axis step, like parent::, or following-sibling::,
etc. This will map oen node to a list of nodes (default is child::)

-- Then, there is a node test. This will kick out of the list any nodes
that fail the test

-- Then, there are zero or more predicates. Each will kick out of the
list any nodes that make the predicate eval to false.

So, the step:

child::foo[position()>2][position()<2]

it seems to never find any nodes, right? Greater than 2 and smaller than
2 ... sometimes, it does. Say:

Evaluation starts from bar, in
<bar>
<foo/>
<foo/>
<foo/>
<foo/>
</bar>

Ok, first the axis step; it will result in a list of all 3 foo's
Then, the node test. If will (in this example) leave that list as is.
Then, the first predicate. It will result in a new list, of the last 2
orignal foo's.
Then, the last predicate. It will result in a new list, of the first 1
of the last 2 orignal foo's.

The net result is a list of the 3rd foo. The exp. could have been
simplified to

child::foo[position()=3]

which has an abbreviated form:

foo[3]

So, predicates are not subscripts. They are node removers.


> How does "(position()-1) mod 5 = 0" evaluate to a subscript?
> It seems like it would evaluate to true/false.

Now maube you can see that it kicks out all nodes of a list, except
those whose (index minus one) are divisible by 5. That will be no. 1,
no. 6, ....
<xsl:apply-templates select=". | following-sibling::car[position() &lt;
5]" mode="info"/>

Then I don't understand what ". |" means. I assume it means: "the
current node boolean OR the thing that follows it".

Not quite; it means: The list resulting from evaluating the left hand
side exp, merged with the list resulting from evaluating the right hand
side exp; merging preserves the doc order and does not duplicate any
nodes in both lists.
Regardless, it *does* seem to work. The only issue now is that the
first vehicle in each "page" has to be treated slightly different
because of the morons who created the original XML spec I'm trying to
meet. On each page, there are "year" and "age group" fields for the
vehicles. Notice that the name of the age group on the first vehicle
isn't "AGE GROUP #001" as you'd expect. It's simply "AGE GROUP".

Ouch. Tell me this isn't a government operation ;)
Here's some sample XML output that is very close to the output I'm
generating (with a lot of stuff omitted for brevity). This example has
12 cars... ....

...the AGE GROUP field is the only one that is different. All the rest
have the correct numbers in their name. Note that the number is the
position of the vehicle on the page, not it's position in the original
XML document.

OK .. modolo will do it again....

In my example, it can be done by replacing the last template by:

<xsl:template match="car" mode="info">

<xsl:variable name="silly-field-thing">
<xsl:value-of select="(position() - 1) mod 5"/>
</xsl:variable>

<xsl:variable name="silly-field-value">
<xsl:choose>
<xsl:when test="$silly-field-thing=0">AGE GROUP</xsl:when>
<xsl:eek:therwise>AGE GROUP #00<xsl:value-of
select="$silly-field-thing"/></xsl:eek:therwise>
</xsl:choose>
</xsl:variable>

<carinfo car="{@id}">
<FIELD NAME="{$silly-field-value}"/>
</carinfo>

</xsl:template>

The central idea is to, once again, use modolo .. for the x'th car, x
mod 5 IS the position of the car's date RELATIVE to the top of each page.

It will spew out:

<?xml version="1.0"?>
<report>
<first-page>
<carinfo car="1">
<FIELD NAME="AGE GROUP"/>
</carinfo>
<carinfo car="2">
<FIELD NAME="AGE GROUP #001"/>
</carinfo>
<carinfo car="3">
<FIELD NAME="AGE GROUP #002"/>
</carinfo>
<carinfo car="4">
<FIELD NAME="AGE GROUP #003"/>
</carinfo>
<carinfo car="5">
<FIELD NAME="AGE GROUP #004"/>
</carinfo>
</first-page>
<extra-page>
<carinfo car="6">
<FIELD NAME="AGE GROUP"/>
</carinfo>
<carinfo car="7">
<FIELD NAME="AGE GROUP #001"/>
</carinfo>
<carinfo car="8">
<FIELD NAME="AGE GROUP #002"/>
</carinfo>
<carinfo car="9">
<FIELD NAME="AGE GROUP #003"/>
</carinfo>
<carinfo car="10">
<FIELD NAME="AGE GROUP #004"/>
</carinfo>
</extra-page>
<extra-page>
<carinfo car="11">
<FIELD NAME="AGE GROUP"/>
</carinfo>
</extra-page>
</report>


-- Søren ;)
 
W

William Krick

Soren said:
OK .. modolo will do it again....

In my example, it can be done by replacing the last template by:

<xsl:template match="car" mode="info">

<xsl:variable name="silly-field-thing">
<xsl:value-of select="(position() - 1) mod 5"/>
</xsl:variable>

<xsl:variable name="silly-field-value">
<xsl:choose>
<xsl:when test="$silly-field-thing=0">AGE GROUP</xsl:when>
<xsl:eek:therwise>AGE GROUP #00<xsl:value-of
select="$silly-field-thing"/></xsl:eek:therwise>
</xsl:choose>
</xsl:variable>

<carinfo car="{@id}">
<FIELD NAME="{$silly-field-value}"/>
</carinfo>

</xsl:template>

The central idea is to, once again, use modolo .. for the x'th car, x
mod 5 IS the position of the car's date RELATIVE to the top of each page.

It will spew out:

<?xml version="1.0"?>
<report>
<first-page>
<carinfo car="1">
<FIELD NAME="AGE GROUP"/>
</carinfo>
<carinfo car="2">
<FIELD NAME="AGE GROUP #001"/>
</carinfo>
<carinfo car="3">
<FIELD NAME="AGE GROUP #002"/>
</carinfo>
<carinfo car="4">
<FIELD NAME="AGE GROUP #003"/>
</carinfo>
<carinfo car="5">
<FIELD NAME="AGE GROUP #004"/>
</carinfo>
</first-page>
<extra-page>
<carinfo car="6">
<FIELD NAME="AGE GROUP"/>
</carinfo>
<carinfo car="7">
<FIELD NAME="AGE GROUP #001"/>
</carinfo>
<carinfo car="8">
<FIELD NAME="AGE GROUP #002"/>
</carinfo>
<carinfo car="9">
<FIELD NAME="AGE GROUP #003"/>
</carinfo>
<carinfo car="10">
<FIELD NAME="AGE GROUP #004"/>
</carinfo>
</extra-page>
<extra-page>
<carinfo car="11">
<FIELD NAME="AGE GROUP"/>
</carinfo>
</extra-page>
</report>

Thanks for your help. It's like a crash course in XPath.

Unfortunately, I need to go back to my original plan to handle the cars
5 at a time. In addition to the stupid "AGE GROUP", "AGE GROUP #002",
etc.. problem, we've just discovered that the first vehicle has "GVW
#001" but the second vehicle has "GVW #006" (!!!), followed by "GVW
#003" for the third vehicle. As far as we can tell, only the second
vehicle overall is handled this way and I'm sure it's a bug in their
software. The 7th, 12th, etc... are "GVW #002" as as expected. These
people are morons. Unfortunately, we're just a software vendor that
needs to connect two systems and we don't have control over the target
XML format.

Another reason I need to do 5 cars at a time is that I need to generate
sub-totals for certain fields on each "page".

I'm hoping I can figure out how to modify your original code to process
vehicles 5 at a time.
 
S

Soren Kuula

William said:
Thanks for your help. It's like a crash course in XPath.

Unfortunately, I need to go back to my original plan to handle the cars
5 at a time. In addition to the stupid "AGE GROUP", "AGE GROUP #002",
etc.. problem, we've just discovered that the first vehicle has "GVW
#001" but the second vehicle has "GVW #006" (!!!), followed by "GVW
#003" for the third vehicle. As far as we can tell, only the second
vehicle overall is handled this way and I'm sure it's a bug in their
software. The 7th, 12th, etc... are "GVW #002" as as expected. These
people are morons. Unfortunately, we're just a software vendor that
needs to connect two systems and we don't have control over the target
XML format.

I'd advise you not to copy code... never (almost).

What you need is a MAP from one set of value to another: Like:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">

<xsl:template match="cars">
<report>
<xsl:apply-templates select="car[(position()-1) mod 5 = 0]"/>
</report>
</xsl:template>

<xsl:template match="car[1]">
<first-page>
<xsl:apply-templates select=". | following-sibling::car[position()
&lt; 5]" mode="info"/>
<xsl:apply-templates select="." mode="page-summary"/>
</first-page>
</xsl:template>

<xsl:template match="car[position()!=1]">
<extra-page>
<xsl:apply-templates select=". | following-sibling::car[position()
&lt; 5]" mode="info"/>
<xsl:apply-templates select="." mode="page-summary"/>
</extra-page>
</xsl:template>

<xsl:template match="car" mode="info">
<xsl:variable name="silly-field-thing">
<xsl:value-of select="(position() - 1) mod 5"/>
</xsl:variable>

<xsl:variable name="silly-field-value">
<xsl:choose>
<xsl:when test="$silly-field-thing=0">AGE GROUP</xsl:when>
<xsl:eek:therwise>AGE GROUP #00<xsl:value-of
select="$silly-field-thing"/></xsl:eek:therwise>
</xsl:choose>
</xsl:variable>


<!-- compute the position of the car in the whole list -->
<!-- (position() in here would just get the position in the
list of nodes, of which one is the context node .... -->

<xsl:variable name="total-position">
<xsl:value-of select="count(preceding-sibling::car) + 1"/>
</xsl:variable>

<xsl:variable name="buggy-and-silly-field-value">
<xsl:value-of select="'GVW #'"/>
<xsl:choose>
<xsl:when test="$total-position=2"><xsl:value-of
select="006"/></xsl:when>
<xsl:eek:therwise><xsl:value-of
select="format-number($total-position, '###')"/></xsl:eek:therwise>
</xsl:choose>
</xsl:variable>

<carinfo car="{@id}">
<FIELD NAME="{$silly-field-value}"/>
<FIELD NAME="{$buggy-and-silly-field-value}"/>
</carinfo>
</xsl:template>

<xsl:template match="car" mode="page-summary">
<sum-of-ids-of-cars-on-this-page>
<xsl:value-of select="sum(./@id | following-sibling::car[position()
&lt; 5]/@id)"/>
</sum-of-ids-of-cars-on-this-page>
</xsl:template>

</xsl:stylesheet>


will (with the original input) produce

<?xml version="1.0"?>
<report>
<first-page>
<carinfo car="1">
<FIELD NAME="AGE GROUP"/>
<FIELD NAME="GVW #1"/>
</carinfo>
<carinfo car="2">
<FIELD NAME="AGE GROUP #001"/>
<FIELD NAME="GVW #6"/>
</carinfo>
<carinfo car="3">
<FIELD NAME="AGE GROUP #002"/>
<FIELD NAME="GVW #3"/>
</carinfo>
<carinfo car="4">
<FIELD NAME="AGE GROUP #003"/>
<FIELD NAME="GVW #4"/>
</carinfo>
<carinfo car="5">
<FIELD NAME="AGE GROUP #004"/>
<FIELD NAME="GVW #5"/>
</carinfo>
<sum-of-ids-of-cars-on-this-page>15</sum-of-ids-of-cars-on-this-page>
</first-page>
<extra-page>
<carinfo car="6">
<FIELD NAME="AGE GROUP"/>
<FIELD NAME="GVW #6"/>
</carinfo>
<carinfo car="7">
<FIELD NAME="AGE GROUP #001"/>
<FIELD NAME="GVW #7"/>
</carinfo>
<carinfo car="8">
<FIELD NAME="AGE GROUP #002"/>
<FIELD NAME="GVW #8"/>
</carinfo>
<carinfo car="9">
<FIELD NAME="AGE GROUP #003"/>
<FIELD NAME="GVW #9"/>
</carinfo>
<carinfo car="10">
<FIELD NAME="AGE GROUP #004"/>
<FIELD NAME="GVW #10"/>
</carinfo>
<sum-of-ids-of-cars-on-this-page>40</sum-of-ids-of-cars-on-this-page>
</extra-page>
<extra-page>
<carinfo car="11">
<FIELD NAME="AGE GROUP"/>
<FIELD NAME="GVW #11"/>
</carinfo>
<sum-of-ids-of-cars-on-this-page>11</sum-of-ids-of-cars-on-this-page>
</extra-page>
Another reason I need to do 5 cars at a time is that I need to generate
sub-totals for certain fields on each "page".

No problem.

Søren
 
W

William Krick

Soren said:
No problem.

Amazing. I hope you do this for a living and are well paid for it
because you're really good.

Ok, I've managed to modify your example to do what I need it to do but
I've run into one more situation I'm not sure how to handle. The data
for each vehicle object in the source XML needs to be split across
multple pages in the output XML.

For example, given this XML with 6 cars (3-5 omitted for clarity)...

<cars>
<car id="1" agegroup="5" gvw="5,000" collprem="300" compprem="100" />
<car id="2" agegroup="6" gvw="10,000" collprem="250" compprem="75" />
...
<car id="6" agegroup="4" gvw="20,000" collprem="200" compprem="125"
/>
</cars>

....the output needs to look something like this...

<?xml version="1.0"?>
<report>
<main-section>
<first-page>
<FIELD NAME="AGE GROUP">5</FIELD>
<FIELD NAME="AGE GROUP #002">6</FIELD>
<FIELD NAME="GVW #001">5,000</FIELD>
<FIELD NAME="GVW #006">10,000</FIELD>
...
</first-page>
<second-page>
<FIELD NAME="COLL PREM #001">300</FIELD>
<FIELD NAME="COLL PREM #002">250</FIELD>
<FIELD NAME="COMP PREM #001">100</FIELD>
<FIELD NAME="COMP PREM #002">75</FIELD>
...
</second-page>
</main-section>
<extra-section>
<first-page>
<FIELD NAME="AGE GROUP">4</FIELD>
<FIELD NAME="GVW #001">20,000</FIELD>
</first-page>
<second-page>
<FIELD NAME="COLL PREM #001">200</FIELD>
<FIELD NAME="COMP PREM #001">125</FIELD>
</second-page>
</extra-section>
</report>

I'm pretty sure I need to use "apply-templates" twice, once for each
page but I'm not sure how to distinguish between the two calls. Is
that "mode" attribute in the templates the secret?
 
S

Soren Kuula

William said:
Amazing. I hope you do this for a living and are well paid for it
because you're really good.

Why, didn't you receive my invoice yet ;)?

Yes I hsould suggest my manager that we (www.aragost.com) also should do
XSL meta-consulting. If you like to, you can use / recommend me for your
next project.
Ok, I've managed to modify your example to do what I need it to do but
I've run into one more situation I'm not sure how to handle. The data
for each vehicle object in the source XML needs to be split across
multple pages in the output XML.
<extra-section>
<first-page>
<FIELD NAME="AGE GROUP">4</FIELD>
<FIELD NAME="GVW #001">20,000</FIELD>
</first-page>
<second-page>
<FIELD NAME="COLL PREM #001">200</FIELD>
<FIELD NAME="COMP PREM #001">125</FIELD>
</second-page>
</extra-section>

I guess that the extra-section does some summarization (or whatever that
is called ;) ), and its content's structure is not driven by the order
and number of cars, ain the same extent as in the first-page /
extra-page case.

What you may want to do is to take a look at xsl:key. This can be used
for rounding up all car nodes with the property that ... whatever -- for
example, that they belong to a specific age group. Then, you can add
some templates (maybe named templates, see the XSLT 1 spec) that use the
key() function to extract the nodes that you rounded up before, and do
some math (summartion) on them.

The mode feature is for enabling the same nodes to be processed in
different ways, while still using patterns (match="..."). An
apply-templates with mode="x" will only invoke templates with mode="x".
An apply-templates with no mode will only invoke templates with no mode.

Happy hackin'

Søren
 
W

William Krick

Soren said:
I guess that the extra-section does some summarization (or whatever that
is called ;) ), and its content's structure is not driven by the order
and number of cars, ain the same extent as in the first-page /
extra-page case.

I'm sorry, I think I confused things by changing the tags from your
original example. Basically, on each "page" there are two "sections".
The first section has half of the attributes about the first 5 cars,
the second section has the rest of the attributes about the first 5
cars. The extra page is the same except it's the next group of 5 cars.
There are sub-totals within each section for certain attributes.
Maybe this will make things clearer...

<?xml version="1.0"?>
<report>
<first-page>
<section-one>
<!-- stuff about cars 1-5 goes here -->
</section-one>
<section-two>
<!-- other stuff about cars 1-5 goes here -->
</section-two>
</first-page>
<extra-page>
<section-one>
<!-- stuff about cars 6-10 goes here -->
</section-one>
<section-two>
<!-- other stuff about cars 6-10 goes here -->
</section-two>
</extra-page>
</report>

Again, I think that matching in groups of 5 cars might be the easiest
way to handle this.
 
W

William Krick

William said:
Again, I think that matching in groups of 5 cars might be the easiest
way to handle this.


Nevermind, I figured it out. I was right with my thinking in my
earlier post. I needed to use "apply-templates" twice to the same
batch of vehicles, once in each section using the mode parameter to
distinguish between the "calls".

I've got the XML skeleton and the logic all working. Now it's a matter
of brute forcing my way through all the data and formatting to fill the
fields correctly.

Thank you so much again for your help. I've really learned a lot more
than I ever thought I would about XSL and XPath.
 
S

Soren Kuula

William said:
Nevermind, I figured it out. I was right with my thinking in my
earlier post. I needed to use "apply-templates" twice to the same
batch of vehicles, once in each section using the mode parameter to
distinguish between the "calls".

Yes, got it now.
Thank you so much again for your help. I've really learned a lot more
than I ever thought I would about XSL and XPath.

Always nice to help ;)

BTW, you can use node-set typed variables to make the code appear
cleaner, like:

<xsl:template match="car" mode="page-summary">
<xsl:variable name="five-cars" select=". |
following-sibling::car[position() &lt; 5]"/>
<sum-of-ids-of-cars-on-this-page>
<xsl:value-of select="sum($five-cars/@id)"/>
</sum-of-ids-of-cars-on-this-page>
<average>
<xsl:value-of select="sum($five-cars/@id) div count($five-cars)"/>
</average>
</xsl:template>

Søren
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,482
Members
44,901
Latest member
Noble71S45

Latest Threads

Top