xsl:number restarts when count element is wrapped

2019-09-16 02:05发布

问题:

Given:

<dmodule>
<content>
<procedure>
<mainProcedure>
<proceduralStep>
<proceduralStep id="ps-111-222-test">
<title>Air Valve Assemblies</title>
<proceduralStep>
<title>General</title><proceduralStep><para>Equivalent substitutes can be used for items listed in the Table</para>
</proceduralStep></proceduralStep></proceduralStep>
    <proceduralStep><para>Continue with this</para><para>Hold air valve plate</para></proceduralStep>
    <proceduralStep><para>Turn the screw....</para><para>Install bushing</para></proceduralStep>
    <proceduralStep><para>Hold assembly tool....</para><para>Install spring seat</para></proceduralStep>
    <proceduralStep><para>Install plug</para></proceduralStep>
    </proceduralStep>
    <proceduralStep>
<proceduralStep id="ps-111-223-test">
<title>Water Valve Assemblies</title>
<proceduralStep>
<title>General</title><proceduralStep><para>Equivalent substitutes can be used for items listed in the Table</para>
</proceduralStep></proceduralStep></proceduralStep>
    <proceduralStep><para>Continue with this</para><para>Rotate water valve plate</para></proceduralStep>
    <proceduralStep><para>Install housing....</para><para>Install bushing</para></proceduralStep>
    <proceduralStep><para>Hold water valve plate....</para><para>Install spring seat</para></proceduralStep>
    <proceduralStep><para>Install actuator assembly</para></proceduralStep>
    </proceduralStep>
    </mainProcedure>
    </procedure>
</content>
</dmodule>

I'm using <xsl:number> to count <proceduralStep>, excluding any proceduralStep that has a parent or child with attribute @changeType='delete'. (proceduralSteps can be nested.)

    <xsl:template match="proceduralStep">
        <fo:list-block>
            <fo:list-item>
                <fo:list-item-label>
                    <fo:block>
                        <xsl:number count="proceduralStep[not(*/@changeType = 'delete' or parent::*/@changeType = 'delete')]" from="content" level="multiple" format="1.1.1.1.1"/>
                    </fo:block>
                </fo:list-item-label>
            </fo:list-item>
        </fo:list-block>
</xsl:template>

This works as expected, unless <revst> is a wrapper for <proceduralStep>

<dmodule>
    <content>
    <procedure>
    <mainProcedure>
    <proceduralStep>
    <proceduralStep id="ps-111-222-test">
    <title>Air Valve Assemblies</title>
    <proceduralStep>
    <title>General</title><proceduralStep><para>Equivalent substitutes can be used for items listed in the Table</para>
    </proceduralStep></proceduralStep></proceduralStep>
        <proceduralStep><para>Continue with this</para><para>Hold air valve plate</para></proceduralStep>
        <proceduralStep><para>Turn the screw....</para><para>Install bushing</para></proceduralStep>
        <proceduralStep><para>Hold assembly tool....</para><para>Install spring seat</para></proceduralStep>
        <proceduralStep><para>Install plug</para></proceduralStep>
        </proceduralStep>
        <proceduralStep>
    <proceduralStep id="ps-111-223-test">
    <title>Water Valve Assemblies</title>
    <proceduralStep>
    <title>General</title><proceduralStep><para>Equivalent substitutes can be used for items listed in the Table</para>
    </proceduralStep></proceduralStep></proceduralStep>
        <proceduralStep><para>Continue with this</para><para>Rotate water valve plate</para></proceduralStep>
        <revst changeMark="1">
        <proceduralStep><para>Install housing....</para><para>Install bushing</para></proceduralStep>
        <proceduralStep><para>Hold water valve plate....</para><para>Install spring seat</para></proceduralStep>
        </revst>
        <proceduralStep><para>Install actuator assembly</para></proceduralStep>
        </proceduralStep>
        </mainProcedure>
        </procedure>
    </content>
    </dmodule>

Then the numbering gets restarted:

    1.2.13 Continue with this. Rotate water valve plate
    1.2.1  Install housing...Install bushing
    1.2.2  Hold water valve plate....Install spring seat
    1.2.14 Install actuator assembly

instead of:

    1.2.13 Continue with this. Rotate water valve plate
    1.2.14 Install housing...Install bushing
    1.2.15 Hold water valve plate....Install spring seat
    1.2.16 Install actuator assembly

So I tried

<xsl:number count="revst[not(@changeType = 'delete')] |  proceduralStep[not(*/@changeType = 'delete' or parent::*/@changeType = 'delete')]" from="content" level="multiple" format="1.1.1.1.1"/>

And now I'm getting

    1.2.13 Continue with this. Rotate water valve plate
    1.2.14.1 Install housing...Install bushing
    1.2.14.2 Hold water valve plate....Install spring seat
    1.2.15 Install actuator assembly

I thought xsl:number is supposed to ignore any elements not in the count expression.

回答1:

xsl:number counts siblings (or, for level="any", counts along the preceding and ancestor-or-self axes). See https://www.w3.org/TR/xslt#number. So it's doing what it's meant to do when it's counting siblings within the revst.

My preferred solution would be for an earlier processing stage to have turned the <revst> and </revst> into separate 'milestone' empty elements (in the manner of fo:change-bar-begin and fo:change-bar-end) before processing to do the list numbering.

In the absence of that, you have to do the multi-level counting for yourself (which would be less verbose if you were using XSLT 2.0 instead of XSLT 1.0):

<xsl:template name="preceding-step-count">
  <xsl:value-of
    select="count(preceding-sibling::proceduralStep[not(*/@changeType = 'delete' or parent::*/@changeType = 'delete')]) +
    count(preceding-sibling::revst/proceduralStep[not(*/@changeType = 'delete' or parent::*/@changeType = 'delete')])"/>
</xsl:template>

<xsl:template match="proceduralStep">
  <xsl:param name="current-count" select="0"/>
  <xsl:param name="parent-label" />

  <xsl:variable name="preceding-step-count">
    <xsl:call-template name="preceding-step-count" />
  </xsl:variable>

  <xsl:variable name="label">
    <xsl:value-of
      select="$current-count + $preceding-step-count + 1"/>
  </xsl:variable>

  <xsl:variable name="use-label">
    <xsl:choose>
      <xsl:when test="$parent-label">
        <xsl:value-of select="concat($parent-label, '.', $label)"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="$label"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>
  <fo:list-block provisional-distance-between-starts="36pt">
    <fo:list-item>
      <fo:list-item-label end-indent="label-end()">
        <fo:block>
          <xsl:value-of select="$use-label"/>
        </fo:block>
      </fo:list-item-label>
      <fo:list-item-body start-indent="body-start()">
        <xsl:apply-templates>
          <xsl:with-param name="parent-label" select="$use-label" />
        </xsl:apply-templates>
      </fo:list-item-body>
    </fo:list-item>
  </fo:list-block>
</xsl:template>

<xsl:template match="revst">
  <xsl:param name="parent-label" />

  <!-- If using XSLT 2.0, could use tunnel parameters
    and avoid passing $parent-label. -->
  <xsl:apply-templates>
    <xsl:with-param name="current-count">
      <xsl:call-template name="preceding-step-count" />
    </xsl:with-param>
    <xsl:with-param name="parent-label" select="$parent-label" />
  </xsl:apply-templates>
</xsl:template>

<xsl:template match="para | title">
  <fo:block>
    <xsl:apply-templates />
  </fo:block>
</xsl:template>