Ladies and Gentlemen:
Consider the following document:
<a>
<b foo='0'>...</b>
<b foo='9'>...</b>
<b foo='0'>
<c foo='2'>...</c>
<c foo='0'>...</c>
</b>
<a>
The "foo='0'" attribute in any element in the document is a
signal that certain tasks need to be performed. The task does
some work in a database. When it's done it modifies the document
to change the value of foo to a new, unique number.
There are three elements with foo="0" in this particular example
document, two b elements and one c element. When the work is all
done, the document should look something like this:
<a>
<b foo='21'>...</b>
<b foo='9'>...</b>
<b foo='22'>
<c foo='2'>...</c>
<c foo='23'>...</c>
</b>
<a>
I have written an XSLT filter that can locate all of the elements
with foo="0" attributes and can replace a "0" with a new value
passed as a parameter to the filter. The way I'm finding these
elements is with the following pattern:
<xsl:template match = '*[@foo="0"]'>
...
</xsl:template>
So far, so good. But here's the problem:
I need to replace every "0" with a new value. I don't know in
advance how many foo="0" attributes will be encountered. There
might be just one, but there might be more than one.
The filter I've written will find all of the foo's with the value
"0", and replace ALL of them with the same passed value. That
isn't what I need to do. I need to replace each one with a
different passed value. The values are actually significant and
I can't just use a counter to insert them.
So what I want to do is to invoke the filter multiple times, once
for each foo="0" that is encountered. But to do that, I need to
just replace one foo and return a converted document, then filter
again, and so on.
The efficiency isn't an issue here. This happens rarely. I'm
more concerned about getting it right and producing a readable,
maintainable filter script.
Does anyone have a way to do it?
Here is the complete script that I'm using, in case someone needs
to see it or experiment with it. Please feel free to laugh or
insult me if you think my approach warrants that. However, if
you do, I will be grateful if you can provide a better solution.
Thanks.
Alan
------------------------ cut here ------------------------------
<xsl:transform version = '1.0'
xmlns:xsl = '
http://www.w3.org/1999/XSL/Transform'
xmlns:cdr = '
cips.nci.nih.gov/cdr'>
<xsl:output method = 'xml'/>
<xsl:param name = "addTargValue"/>
<!-- Copy almost everything straight through -->
<xsl:template match = '@*|comment()|*|processing-instruction()|
text()'>
<xsl:copy>
<xsl:apply-templates
select = '@*|comment()|*|processing-instruction()|text()'/>
</xsl:copy>
</xsl:template>
<!-- Replace elements with our requested attribute -->
<!-- XXX HOW DO I DO THIS ONLY ONCE? XXX -->
<xsl:template match = '*[@foo="0"]'>
<xsl:element name = '{name()}'>
<xsl:for-each select = '@*'>
<xsl:choose>
<xsl:when test = 'name()="foo"'>
<xsl:attribute name = 'foo'>
<xsl:value-of select = '$addTargValue'/>
</xsl:attribute>
</xsl:when>
<xsl:otherwise>
<xsl:copy/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
</xsl:transform>