Re: [xsl] Unable to select wanted nodes

Subject: Re: [xsl] Unable to select wanted nodes
From: Brandon Ibach <brandon.ibach@xxxxxxxxxxxxxxxxxxx>
Date: Thu, 6 Oct 2011 18:41:39 -0400
Hi, Mark...

The following produces the output you indicated, though it may need to
be tweaked a bit if your actual requirements are more complicated than
your sample would indicate.  Note that the for-each-group uses a
composite key of the attribute and its value to essentially
de-duplicate the list of formats.

<xsl:for-each-group select="../../Stamp/Formats"
group-by="concat(name(@*), '=', @*)">
  <xsl:variable name="active" select="name(@*) = $format and @* = $number"/>
  <xsl:element name="Formats">
    <xsl:if test="position() gt 1">
      <xsl:attribute name="label" select="position() - 1"/>
    </xsl:if>
    <xsl:attribute name="state" select="if ($active) then 'depressed'
else 'active'"/>
    <xsl:copy-of select="@*"/>
  </xsl:element>
</xsl:for-each-group>

Also, the code for the content of the following "StampButtons" element
could be simplified, first avoiding duplication by moving the "more
than one" check deeper in and second by removing the for-each-group.
Since you're not doing anything with the groups other than a for-each
on their members, you don't really need the grouping and you can do
the filtering right up front, which will also fix the behavior of the
"more than one" check, which would have produced incorrect results,
had your test data included an "only-one" case.  The following should
replace the xsl:choose.

<xsl:variable name="stamps"
select="../../Stamp[Formats/@*[name(.)=$format][.=$number]]"/>
<xsl:for-each select="$stamps">
  <xsl:element name="Stamp">
    <xsl:if test="count($stamps) gt 1">
      <xsl:attribute name="label" select="position()"/>
    </xsl:if>
    <xsl:attribute name="state" select="'active'"/>
    <xsl:copy-of select="CatNumbers/@pofis-number"/>
  </xsl:element>
</xsl:for-each>

For some final tweaks, you can actually incorporate the xsl:if at the
top of the (match="Formats" mode="formats") template into the match
expression as a predicate so the template won't even be used if that
condition isn't true (the built-in template for an element with only
attributes and no content will produce nothing).

Also, it is somewhat a matter of personal style, but your
comprehensive use of xsl:element and xsl:attribute makes for code that
is both more verbose and less obvious in intent, from a casual glance.
 I think the more "template-like" style of using literal result
elements and attribute value templates (name="{...}"), where possible,
makes for more readable code.  Just the opinion of an old C++ (here
and there) hack, tho... ;)

-Brandon :)


On Thu, Oct 6, 2011 at 4:02 PM, Mark <mark@xxxxxxxxxxxx> wrote:
> For the below <Input> file, I want the <DesiredOutput> file but so far I
> have only been able to write code that produces <MyOutput>.
>
> In <MyOutPut>:
> (1) The mode="stamp" templates produce satisfactory <StampPage> elements.
> These templates and their output were included only for completeness and
may
> be ignored.
>
> (2) The mode="formats" templates produce the <FormatPage> and its child
> <StampButtons> properly, but I cannot puzzle out how to produce the
children
> of <FormatButtons>.  I am not even sure that what I have done so far is the
> best way to do this.
>
> Side note: the redundant attribute in the <FormatPage> element appears
> solely as debugging aid.
>
> Help graciously accepted,
> Mark
>
> <Input>
>   <Set>
>     <Stamp>
>       <CatNumbers pofis-number="105"/>
>       <Formats souvenir-sheet="105"/>
>       <Formats se-tenant="105"/>
>     </Stamp>
>     <Stamp>
>       <CatNumbers pofis-number="106"/>
>       <Formats souvenir-sheet="105"/>
>       <Formats se-tenant="105"/>
>     </Stamp>
>     <Stamp>
>       <CatNumbers pofis-number="107"/>
>       <Formats souvenir-sheet="105"/>
>       <Formats se-tenant="107"/>
>     </Stamp>
>     <Stamp>
>       <CatNumbers pofis-number="108"/>
>       <Formats souvenir-sheet="105"/>
>       <Formats se-tenant="107"/>
>     </Stamp>
>   </Set>
>  </Input>
> ----------------
> <DesiredOutput>
>   <StampPage>
>     <FormatButtons>
>       <Formats state="active" souvenir-sheet="105"/>
>       <Formats state="active" se-tenant="105"/>
>     </FormatButtons>
>     <StampButtons>
>       <Stamp state="depressed" pofis-number="105"/>
>     </StampButtons>
>   </StampPage>
>   <StampPage>
>     <FormatButtons>
>       <Formats state="active" souvenir-sheet="105"/>
>       <Formats state="active" se-tenant="105"/>
>     </FormatButtons>
>     <StampButtons>
>       <Stamp state="depressed" pofis-number="106"/>
>     </StampButtons>
>   </StampPage>
>   <StampPage >
>     <FormatButtons>
>       <Formats state="active" souvenir-sheet="105"/>
>       <Formats state="active" se-tenant="107"/>
>     </FormatButtons>
>     <StampButtons>
>       <Stamp state="depressed" pofis-number="107"/>
>     </StampButtons>
>   </StampPage>
>   <StampPage>
>     <FormatButtons>
>       <Formats state="active" souvenir-sheet="105"/>
>       <Formats state="active" se-tenant="107"/>
>     </FormatButtons>
>     <StampButtons>
>       <Stamp state="depressed" pofis-number="108"/>
>     </StampButtons>
>   </StampPage>
>   <FormatPage souvenir-sheet="105">
>     <FormatButtons>
>       <Formats state="depressed" souvenir-sheet="105"/>
>       <Formats label="1" state="active" se-tenant="105"/>
>       <Formats label="2" state="active" se-tenant="107"/>
>     </FormatButtons>
>     <StampButtons>
>       <Stamp label="1" state="active" pofis-number="105"/>
>       <Stamp label="2" state="active" pofis-number="106"/>
>       <Stamp label="3" state="active" pofis-number="107"/>
>       <Stamp label="4" state="active" pofis-number="108"/>
>     </StampButtons>
>   </FormatPage>
>   <FormatPage se-tenant="105">
>     <FormatButtons>
>       <Formats state="active" souvenir-sheet="105"/>
>       <Formats label="1" state="depressed" se-tenant="105"/>
>       <Formats label="2" state="active" se-tenant="107"/>
>     </FormatButtons>
>     <StampButtons>
>       <Stamp label="1" state="active" pofis-number="105"/>
>       <Stamp label="2" state="active" pofis-number="106"/>
>     </StampButtons>
>   </FormatPage>
>   <FormatPage se-tenant="107">
>     <FormatButtons>
>       <Formats state="active" souvenir-sheet="105"/>
>       <Formats label="1" state="active" se-tenant="105"/>
>       <Formats label="2" state="depressed" se-tenant="107"/>
>     </FormatButtons>
>     <StampButtons>
>       <Stamp label="1" state="active" pofis-number="107"/>
>       <Stamp label="2" state="active" pofis-number="108"/>
>     </StampButtons>
>   </FormatPage>
>  </DesiredOutput>
>
> -------------------
> <MyOutput>
>  <StampPage>
>   <FormatButtons>
>     <Formats state="active" souvenir-sheet="105"/>
>     <Formats state="active" se-tenant="105"/>
>   </FormatButtons>
>   <StampButtons>
>     <Stamp state="depressed" pofis-number="105"/>
>   </StampButtons>
>  </StampPage>
>  <StampPage>
>   <FormatButtons>
>     <Formats state="active" souvenir-sheet="105"/>
>     <Formats state="active" se-tenant="105"/>
>   </FormatButtons>
>   <StampButtons>
>     <Stamp state="depressed" pofis-number="106"/>
>   </StampButtons>
>  </StampPage>
>  <StampPage>
>   <FormatButtons>
>     <Formats state="active" souvenir-sheet="105"/>
>     <Formats state="active" se-tenant="107"/>
>   </FormatButtons>
>   <StampButtons>
>     <Stamp state="depressed" pofis-number="107"/>
>   </StampButtons>
>  </StampPage>
>  <StampPage>
>   <FormatButtons>
>     <Formats state="active" souvenir-sheet="105"/>
>     <Formats state="active" se-tenant="107"/>
>   </FormatButtons>
>   <StampButtons>
>     <Stamp state="depressed" pofis-number="108"/>
>   </StampButtons>
>  </StampPage>
>  <FormatPage souvenir-sheet="105">
>   <FormatButtons>
>     <!-- My problem -->
>   </FormatButtons>
>   <StampButtons>
>     <Stamp label="1" state="active" pofis-number="105"/>
>     <Stamp label="2" state="active" pofis-number="106"/>
>     <Stamp label="3" state="active" pofis-number="107"/>
>     <Stamp label="4" state="active" pofis-number="108"/>
>   </StampButtons>
>  </FormatPage>
>  <FormatPage se-tenant="105">
>   <FormatButtons>
>     <!-- My problem -->
>   </FormatButtons>
>   <StampButtons>
>     <Stamp label="1" state="active" pofis-number="105"/>
>     <Stamp label="2" state="active" pofis-number="106"/>
>   </StampButtons>
>  </FormatPage>
>  <FormatPage se-tenant="107">
>   <FormatButtons>
>     <!-- My problem -->
>   </FormatButtons>
>   <StampButtons>
>     <Stamp label="1" state="active" pofis-number="107"/>
>     <Stamp label="2" state="active" pofis-number="108"/>
>   </StampButtons>
>  </FormatPage>
> </MyOutput>
>
> ------------------------
> Stylesheet:
> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
> xmlns:xs="http://www.w3.org/2001/XMLSchema";
> xmlns:xd="http://www.oxygenxml.com/ns/doc/xsl"; exclude-result-prefixes="xs
> xd"
>  version="2.0">
>
>  <xsl:strip-space elements="*"/>
>
>  <xsl:template match="Set">
>   <xsl:element name="Output">
>     <xsl:apply-templates mode="stamp"/>
>     <xsl:apply-templates mode="formats"/>
>   </xsl:element>
>  </xsl:template>
>
>  <!-- Create a StampPage for every Stamp -->
>  <xsl:template match="Stamp" mode="stamp">
>   <xsl:element name="StampPage">
>     <xsl:element name="FormatButtons">
>       <xsl:apply-templates mode="stamp"/>
>     </xsl:element>
>     <xsl:element name="StampButtons">
>       <xsl:element name="Stamp">
>         <xsl:attribute name="state" select="'depressed'"/>
>         <xsl:attribute name="pofis-number"
> select="CatNumbers/@pofis-number"/>
>       </xsl:element>
>     </xsl:element>
>   </xsl:element>
>  </xsl:template>
>
>  <xsl:template match="Stamp" mode="formats">
>   <xsl:apply-templates mode="formats"/>
>  </xsl:template>
>
>  <xsl:template match="Formats" mode="stamp">
>   <xsl:element name="Formats">
>     <xsl:attribute name="state" select="'active'"/>
>     <xsl:copy-of select="@*"/>
>   </xsl:element>
>  </xsl:template>
>
>  <xsl:template match="Formats" mode="formats">
>   <xsl:if test="@* eq ../CatNumbers/@pofis-number">
>     <xsl:element name="FormatPage">
>       <xsl:copy-of select="@*"/>
>       <xsl:variable name="format" select="name(@*)"/>
>       <xsl:variable name="number" select="@*"/>
>       <xsl:element name="FormatButtons">
>        <!-- I do not know how to select the correct components -->
>       </xsl:element>
>       <xsl:element name="StampButtons">
>         <xsl:choose>
>           <xsl:when test="count(../../Stamp/Formats/@*[name(.)=$format]) eq
> 1">
>             <xsl:element name="Stamp">
>               <xsl:attribute name="state" select="'active'"/>
>               <xsl:copy-of select="../CatNumbers/@pofis-number"/>
>             </xsl:element>
>           </xsl:when>
>           <xsl:otherwise>
>             <xsl:for-each-group select="../../Stamp/Formats"
> group-by="@*[name(.)=$format]">
>               <xsl:for-each select="current-group()">
>                 <xsl:if test="@* eq $number">
>                   <xsl:element name="Stamp">
>                     <xsl:attribute name="label" select="position()"/>
>                     <xsl:attribute name="state" select="'active'"/>
>                     <xsl:copy-of select="../CatNumbers/@pofis-number"/>
>                   </xsl:element>
>                 </xsl:if>
>               </xsl:for-each>
>             </xsl:for-each-group>
>           </xsl:otherwise>
>         </xsl:choose>
>       </xsl:element>
>     </xsl:element>
>   </xsl:if>
>  </xsl:template>
>
> </xsl:stylesheet>

Current Thread