Re: [xsl] Identifying unique attribute values in nested sibling elements

Subject: Re: [xsl] Identifying unique attribute values in nested sibling elements
From: "Mark" <mark@xxxxxxxxxxxx>
Date: Wed, 5 Oct 2011 14:33:40 -0700
Hi Brandon,

First, I am saying absolutely nothing negative here about Ken's version -- he has been generous and most helpful for a considerable time now. Thanks, Ken.

That said, what strikes me about your implementation is that you have structured it in an idiom I find quite familiar. What I see in your code are polymorphic functions calling upon the data rather than the code to do the messaging. Had I a far better command of XPath and were I more sensitive to power of XSLT, I suspect I might produce something similar your implementation. I find its polymorphic task resolution extremely satisfying.

I think you are aware that my native coding language is C++. So far, I have been unsuccessful in leveraging my hard-earned C++ skill set to advantage with respect to XSLT and XPath. Your essay has given me a leg up.

Thanks,
Mark

-----Original Message----- From: Brandon Ibach
Sent: Wednesday, October 05, 2011 1:15 PM
To: xsl-list@xxxxxxxxxxxxxxxxxxxxxx
Subject: Re: [xsl] Identifying unique attribute values in nested sibling elements


To go a bit beyond Ken's suggestions, here's a very non-C++ style of
approach, relying on the data to drive the processing, which XSLT
supports very well.  This assumes that you only have a single Value
element in each Stamp.  Your code seemed to indicate that this was the
case and your use of <xsl:for-each> seemed to be mostly for purposes
of shifting your context, but if there's a chance you might have more
than one Value, that would make for some strange results, so it's
something to look out for, if you don't have a schema to enforce such
rules on your input.

Hopefully, this isn't too hard to follow.  I have a weakness for
C-cubed code (concise, clever and, all too often, a bit cryptic).
Besides the more content-driven style, the other main feature here is
the simplification allowed by the fact that in all cases, your code
was generating an <a class="button">, so you may as well only generate
it once (the "DRY" rule works in any sort of programming, after all,
regardless of the language used).

<xsl:function name="cps:report">
  <xsl:param name="Stamp"/>
  <xsl:variable name="file">
    <xsl:apply-templates select="$Stamp/Value" mode="cps:href"/>
  </xsl:variable>
  <a class="button" href="../aval/{$file}.htm">
    <xsl:apply-templates select="$Stamp/Value" mode="cps:text"/>
  </a>
</xsl:function>

<xsl:template match="Value[@l-value]" mode="cps:href">
  <xsl:value-of select="lower-case(@l-value)"/>
</xsl:template>
<xsl:template match="Value[not(@l-value)]" mode="cps:href">
  <xsl:value-of select="concat((@kc-value, 0)[1], '-')"/>
  <xsl:value-of select="(@h-value, 0)[1]"/>
</xsl:template>

<xsl:template match="Value[@l-value]" mode="cps:text">
  <xsl:value-of select="@l-value"/>
</xsl:template>
<xsl:template match="Value[not(@l-value)]" mode="cps:text">
  <xsl:variable name="kc" select="@kc-value[. ne '0']"/>
  <xsl:variable name="h" select="@h-value[. ne '0']"/>
  <xsl:value-of select="concat($kc, '.'[$kc][$h], $h)"/>
  <xsl:value-of select="if ($kc) then 'Kc' else 'h'"/>
</xsl:template>

-Brandon :)


On Tue, Oct 4, 2011 at 10:23 AM, Mark <mark@xxxxxxxxxxxx> wrote:
Hi Ken,
Despite my confused presentation of the problem last week, you supplied me
with two functions that obtain the unique attributes from <Value> element
within a set of <Stamp> elements. Works beautifully, but I did make some
modifications to the second function. Please let me know if my modifications
are consistent with the idiom you have been talking about or if I have
slipped back into C++. Any improvements would be greatly appreciated if and
when you have the time and desire. The h-value and l-value were not
presented to you as part of the original use case.


They do exactly what I wanted.
Thanks,
Mark

the XML with the  output:
<Value kc-value="1">                        Text: 1kc     File: 1-0.htm
<Value kc-value="1" h-value="50"> Text 1.50kc File: 1-50.htm
<Value kc-value="0" h-value="50"> Text: 50h  File: 0-50.htm
<Value kc-value="0" l-value="A"     Text: A  File:a.htm

Only the second function, cps:report, has been modified:

<!--compose a string of attribute names and their values;
  avoid the possibility of co-incidental string values;-->
 <xsl:function name="cps:attrs">
  <xsl:param name="this"/>
  <xsl:value-of>
    <xsl:for-each select="$this/@*">
      <xsl:sort select="name(.)"/>
      <xsl:value-of select="name(.),'&#xfdd0;',.,'&#xfdd0;'"/>
    </xsl:for-each>
  </xsl:value-of>
 </xsl:function>

 <!--common reporting-->
 <xsl:function name="cps:report">
  <xsl:param name="Stamp"/>
  <xsl:for-each select="$Stamp">
    <!-- l-value -->
    <xsl:choose>
      <xsl:when test="Value/@l-value">
        <xsl:for-each select="Value/@l-value">
          <a class="button" href="../aval/{lower-case(.)}.htm">
            <xsl:value-of select="."/>
          </a>
        </xsl:for-each>
      </xsl:when>
      <!-- kc-value + h-value -->
      <xsl:when test="Value/@h-value">
        <xsl:variable name="h">
          <xsl:for-each select="Value/@h-value">
            <xsl:value-of select="."/>
          </xsl:for-each>
        </xsl:variable>
        <xsl:for-each select="Value/@kc-value">
          <xsl:choose>
            <xsl:when test="not(. eq '0')">
              <a class="button" href="../aval/{concat(., '-', $h)}.htm">
                <xsl:value-of select="concat(., '.', $h)"/>
                <xsl:text>KD
</xsl:text>
              </a>
            </xsl:when>
            <xsl:otherwise>
              <a class="button" href="../aval/{concat(., '-', $h)}.htm">
                <xsl:value-of select="$h"/>
                <xsl:text>h</xsl:text>
              </a>
            </xsl:otherwise>
          </xsl:choose>
        </xsl:for-each>
      </xsl:when>

      <xsl:otherwise>
        <!-- Only when there is no h-value  -->
        <xsl:for-each select="Value/@kc-value">
          <a class="button" href="../aval/{concat(., '-0')}.htm">
            <xsl:value-of select="."/>KD
<xsl:text/>
          </a>
        </xsl:for-each>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:for-each>
 </xsl:function>

Current Thread