tags:

views:

27

answers:

1

I want to change the values of the @page attributes of <line/> that are descendants of <section type="cover"/> to (max(@page) + 1) of <section type="contents"/>.

Basically I need the page number for the cover section to be 1 higher than the last numeric page number of the contents section.

Note: <line/> nodes are not always siblings, they can be nested at any level.

For example transform this:

<root>
    <section type="contents">
        <line page="i">text</line> 
        <line page="ii">text</line> 
        <line page="1">text</line> 
        <line page="1">text</line> 
        <line page="2">text</line>
        <block>
            <line page="3">text</line> 
            <line page="4">text</line> 
        </block>
    </section>
    <section type="cover">
        <line page="i">text</line> 
        <line page="i">text</line> 
    </section>
</root>

to this:

<root>
    <section type="contents">
        <line page="i">text</line> 
        <line page="ii">text</line> 
        <line page="1">text</line> 
        <line page="1">text</line> 
        <line page="2">text</line>
        <block>
            <line page="3">text</line> 
            <line page="4">text</line> 
        </block>
    </section>
    <section type="cover">
        <line page="5">text</line> <!-- @page changes to max()+1 -->
        <line page="5">text</line> <!-- @page changes to max()+1 -->
    </section>
</root>

Is there an easy way to achieve this with PHP5, XSLT1.0, XPATH?

+2  A: 

This transformation:

<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
    <xsl:output omit-xml-declaration="yes" indent="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:variable name="vMax">
      <xsl:for-each select="/*/*//line/@page">
       <xsl:sort data-type="number"/>
       <xsl:if test="position() = last()">
         <xsl:value-of select="."/>
       </xsl:if>
      </xsl:for-each>
    </xsl:variable>

 <xsl:template match="node()|@*">
     <xsl:copy>
       <xsl:apply-templates select="node()|@*"/>
     </xsl:copy>
 </xsl:template>

 <xsl:template match="section[@type='cover']/line/@page">
  <xsl:attribute name="page">
   <xsl:value-of select="$vMax+1"/>
  </xsl:attribute>
 </xsl:template>
</xsl:stylesheet>

when applied on the provided XML document:

<root>
    <section type="contents">
        <line page="i">text</line>
        <line page="ii">text</line>
        <line page="1">text</line>
        <line page="1">text</line>
        <line page="2">text</line>
        <block>
            <line page="3">text</line>
            <line page="4">text</line>
        </block>
    </section>
    <section type="cover">
        <line page="i">text</line>
        <line page="i">text</line>
    </section>
</root>

produces the wanted, correct result:

<root>
   <section type="contents">
      <line page="i">text</line>
      <line page="ii">text</line>
      <line page="1">text</line>
      <line page="1">text</line>
      <line page="2">text</line>
      <block>
         <line page="3">text</line>
         <line page="4">text</line>
      </block>
   </section>
   <section type="cover">
      <line page="5">text</line>
      <line page="5">text</line>
   </section>
</root>

Do note: The required maximum is produced using sorting on the page attribute of all line elements and taking the page attribute of the last element in the sorted node-set.

In XSLT 1.0 for all practical purposes this way of finding a maximum or a minimum is the fastest, although it has a complexity of O(N*log(N)), while there are O(N) algorithms. Everything is a matter of a constant ...

Dimitre Novatchev
@Dimitre: +1 This is better: find maximum only once.
Alejandro
+1 for useful answer. "while there are O(N) logarithms" - did you mean algorithms? Otherwise I don't understand... Also, I wonder if there would be a performance advantage, possibly depending on the XSLT processor, to using `<xslt:sort order="descending">` and `position() = 1`.
LarsH
@LarsH: Thanks for noticing this -- fixed. As for whether to use 1 or last() -- this would depend on the optimizing abilities of the XSLT processor -- for a non-optimizing one they are equivalent.
Dimitre Novatchev