William Krick wrote:
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() <
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:otherwise>AGE GROUP #00<xsl:value-of
select="$silly-field-thing"/></xsl:otherwise>
</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 ;)