You are viewing a plain text version of this content. The canonical link for it is here.
Posted to fop-users@xmlgraphics.apache.org by robert frapples <oy...@gmail.com> on 2004/11/30 22:09:06 UTC

setting up tables differently based on existance of xml tags. (static variables, recursion, templates, and intrigue)

Here's a (hopefully) interesting problem: (I've been editing this
email every day. Every time I can't figure out how to do something, I
come up with an alternate plan, assisted by a few people in #xml on
irc.freenode.net. (Thank you Bebabo).  I figured at this point I'd
share my experiences. If you can think of better, faster, cleaner ways
to do this, I'd love to hear from you.)

source xml:
<document>
    <field>
        <title>a</title>
    </field>
    <field>
        <title>b</title>
        <value/>
    <field>
        <title>c</title>
        <value/>
    </field>
    <field>
        <title>d</title>
    </field>
    <field>
        <title>e</title>
        <value/>
    </field>
</document>

What I want to happen is basically, for-each select="field", if
<field> has a <value/> and the next <field> also has a value, have 4
cells in a row: field[n]/title, text , field[n+1]/title, 'text'.  If
<field> does not have a <value/> or if the next <field> does not have
a <value/>, then have 2 cells: field[n]/title, and 'other text' with
number-columns-spanned=3.

My first thought was this:
/-----------------------------------------------------------------------------------------\
. . .
<xsl:variable name="col" select="''"/>
<xsl:table-body>
<xsl:for-each select="field">
<xsl:choose>
    <xsl:when test="value and not($col)">
        <fo:table-row>
            <fo:table-cell><xsl:value-of select="title"/></fo:table-cell>
            <fo:table-cell>text</fo:table-cell>
            <xsl:variable name="col" select="'true'"/>
    </xsl:when>
    <xsl:when test="value and $col">
            <fo:table-cell><xsl:value-of select="title"/></fo:table-cell>
            <fo:table-cell>text</fo:table-cell>
         </fo:table-row>
         <xsl:variable name="col" select="''"/>
    </xsl:when>
    <xsl:when test="not(value) and not($col)">
         <fo:table-row>
             <fo:table-cell><xsl:value-of select="title"/></fo:table-cell>
             <fo:table-cell>other text</fo:table-cell>
             <fo:table-cell number-columns-spanned="2"/>
         </fo:table-row> 
    </xsl:when>
    <xsl:when test="not(value) and $col">
             <fo:table-cell number-columns-spanned="2"/>
          </fo:table-row>
          <fo:table-row>
             <fo:table-cell><xsl:value-of select="title"/></fo:table-cell>
             <fo:table-cell>other text</fo:table-cell>
             <fo:table-cell number-columns-spanned="2"/>
          </fo:table-row>
           <xsl:variable name="col" select="''"/>
    </xsl:when>
</xsl:choose>
</xsl:for-each>
</fo:table-body>
\---------------------------------------------------------------------------------------/

This obviously will not work, because XSLT does not allow variables to
be reassigned.  After reading extensively from O'Reilly's XSLT book
and the w3c specs for XSLT and XPath, I decided I could achive my goal
with recursive template matches.  Here is what I arrived at:

/----------------------------------------------------------------------------------------\
<xsl:stylesheet . . .>
    <xsl:template match="document">
. . .
         <fo:table-body>
             <xsl:apply-templates select="field[1]">
         </fo:table-body>
. . .
    </xsl:template> <!-- end template match="document" -->
    <xsl:template match="field">
        <xsl:choose>
            <!-- When current <field> has <value/> and next <field>
has <value/> -->
            <xsl:when test="value and following-sibling::field[1]/value">
                <fo:table-row>
                    <fo:table-cell>
                        <!-- current title -->
                        <fo:block><xsl:value-of select="title"/></fo:block>
                    </fo:table-cell>
                    <fo:table-cell>
                        <fo:block>text</fo:block>
                    </fo:table-cell>
                    <fo:table-cell>
                        <!-- next title -->
                        <fo:block><xsl:value-of
select="following-sibling::field[1]/title"/></fo:block>
                    </fo:table-cell>
                    <fo:table-cell>
                        <fo:block>text</fo:block>
                    </fo:table-cell>
                </fo:table-row>
                <!-- use this template for the next element (after the
one previously referred
                      to as 'next'. omitting the final '[1]' will
cause this template to be processed
                      remaining <field>, which results in very long
files with lots of stuff i don't
                      want. -->
                <xsl:apply-templates
select="following-sibling::field[position() &gt; 1][1]"/>
            </xsl:when>
            <!-- if not. . . -->
            <xsl:otherwise>
                <fo:table-row>
                    <fo:table-cell>
                        <fo:block><xsl:value-of select="title"/></fo:block>
                    </fo:table-cell>
                    <fo:table-cell number-columns-spanned="3">
                        <fo:block>other text</fo:block>
                    </fo:table-cell>
                </fo:table-row>
                <!-- use this template for the next element. again,
don't omit '[1]'. -->
                <xsl:apply-templates select="following-sibling::field[1]"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
\------------------------------------------------------------------------------------/

This works great.  Based on my understanding of the XSLT parsers, this
is pretty memory intensive, as applying "following-sibling::field[1]"
actually passes the remaining <field> nodes in <document> every time,
leaving a lot of stuff in the stack. Is that correct?

At any rate, any ideas on a better way to do this?  (Other than a SAX
parsing of the XML with a few variables and leaving an extra node in
select <field>s.)

---------------------------------------------------------------------
To unsubscribe, e-mail: fop-user-unsubscribe@xml.apache.org
For additional commands, e-mail: fop-user-help@xml.apache.org


Re: setting up tables differently based on existance of xml tags. (static variables, recursion, templates, and intrigue)

Posted by JB...@s-s-t.com.
Couple things: First, I'd dump this topic on the XSL list. Those folks 
love this kind of question, and they are good at helping with this kind of 
issue. Second, I'd look at making a key to hold the 
field-that-has-a-value-and-whose-next-sibling-has-a-value combos. Then, 
rather than read the whole document, the processor would read the key. If 
it works, that should reduce memory usage and increase performance.

Jay Bryant
Bryant Communication Services




robert frapples <oy...@gmail.com> 
11/30/2004 03:09 PM
Please respond to
fop-user@xml.apache.org


To
fop-user@xml.apache.org
cc

Subject
setting up tables differently based on existance of xml tags. (static 
variables, recursion, templates, and intrigue)






Here's a (hopefully) interesting problem: (I've been editing this
email every day. Every time I can't figure out how to do something, I
come up with an alternate plan, assisted by a few people in #xml on
irc.freenode.net. (Thank you Bebabo).  I figured at this point I'd
share my experiences. If you can think of better, faster, cleaner ways
to do this, I'd love to hear from you.)

source xml:
<document>
    <field>
        <title>a</title>
    </field>
    <field>
        <title>b</title>
        <value/>
    <field>
        <title>c</title>
        <value/>
    </field>
    <field>
        <title>d</title>
    </field>
    <field>
        <title>e</title>
        <value/>
    </field>
</document>

What I want to happen is basically, for-each select="field", if
<field> has a <value/> and the next <field> also has a value, have 4
cells in a row: field[n]/title, text , field[n+1]/title, 'text'.  If
<field> does not have a <value/> or if the next <field> does not have
a <value/>, then have 2 cells: field[n]/title, and 'other text' with
number-columns-spanned=3.

My first thought was this:
/-----------------------------------------------------------------------------------------\
. . .
<xsl:variable name="col" select="''"/>
<xsl:table-body>
<xsl:for-each select="field">
<xsl:choose>
    <xsl:when test="value and not($col)">
        <fo:table-row>
            <fo:table-cell><xsl:value-of select="title"/></fo:table-cell>
            <fo:table-cell>text</fo:table-cell>
            <xsl:variable name="col" select="'true'"/>
    </xsl:when>
    <xsl:when test="value and $col">
            <fo:table-cell><xsl:value-of select="title"/></fo:table-cell>
            <fo:table-cell>text</fo:table-cell>
         </fo:table-row>
         <xsl:variable name="col" select="''"/>
    </xsl:when>
    <xsl:when test="not(value) and not($col)">
         <fo:table-row>
             <fo:table-cell><xsl:value-of select="title"/></fo:table-cell>
             <fo:table-cell>other text</fo:table-cell>
             <fo:table-cell number-columns-spanned="2"/>
         </fo:table-row> 
    </xsl:when>
    <xsl:when test="not(value) and $col">
             <fo:table-cell number-columns-spanned="2"/>
          </fo:table-row>
          <fo:table-row>
             <fo:table-cell><xsl:value-of select="title"/></fo:table-cell>
             <fo:table-cell>other text</fo:table-cell>
             <fo:table-cell number-columns-spanned="2"/>
          </fo:table-row>
           <xsl:variable name="col" select="''"/>
    </xsl:when>
</xsl:choose>
</xsl:for-each>
</fo:table-body>
\---------------------------------------------------------------------------------------/

This obviously will not work, because XSLT does not allow variables to
be reassigned.  After reading extensively from O'Reilly's XSLT book
and the w3c specs for XSLT and XPath, I decided I could achive my goal
with recursive template matches.  Here is what I arrived at:

/----------------------------------------------------------------------------------------\
<xsl:stylesheet . . .>
    <xsl:template match="document">
. . .
         <fo:table-body>
             <xsl:apply-templates select="field[1]">
         </fo:table-body>
. . .
    </xsl:template> <!-- end template match="document" -->
    <xsl:template match="field">
        <xsl:choose>
            <!-- When current <field> has <value/> and next <field>
has <value/> -->
            <xsl:when test="value and following-sibling::field[1]/value">
                <fo:table-row>
                    <fo:table-cell>
                        <!-- current title -->
                        <fo:block><xsl:value-of 
select="title"/></fo:block>
                    </fo:table-cell>
                    <fo:table-cell>
                        <fo:block>text</fo:block>
                    </fo:table-cell>
                    <fo:table-cell>
                        <!-- next title -->
                        <fo:block><xsl:value-of
select="following-sibling::field[1]/title"/></fo:block>
                    </fo:table-cell>
                    <fo:table-cell>
                        <fo:block>text</fo:block>
                    </fo:table-cell>
                </fo:table-row>
                <!-- use this template for the next element (after the
one previously referred
                      to as 'next'. omitting the final '[1]' will
cause this template to be processed
                      remaining <field>, which results in very long
files with lots of stuff i don't
                      want. -->
                <xsl:apply-templates
select="following-sibling::field[position() &gt; 1][1]"/>
            </xsl:when>
            <!-- if not. . . -->
            <xsl:otherwise>
                <fo:table-row>
                    <fo:table-cell>
                        <fo:block><xsl:value-of 
select="title"/></fo:block>
                    </fo:table-cell>
                    <fo:table-cell number-columns-spanned="3">
                        <fo:block>other text</fo:block>
                    </fo:table-cell>
                </fo:table-row>
                <!-- use this template for the next element. again,
don't omit '[1]'. -->
                <xsl:apply-templates 
select="following-sibling::field[1]"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
\------------------------------------------------------------------------------------/

This works great.  Based on my understanding of the XSLT parsers, this
is pretty memory intensive, as applying "following-sibling::field[1]"
actually passes the remaining <field> nodes in <document> every time,
leaving a lot of stuff in the stack. Is that correct?

At any rate, any ideas on a better way to do this?  (Other than a SAX
parsing of the XML with a few variables and leaving an extra node in
select <field>s.)

---------------------------------------------------------------------
To unsubscribe, e-mail: fop-user-unsubscribe@xml.apache.org
For additional commands, e-mail: fop-user-help@xml.apache.org




---------------------------------------------------------------------
To unsubscribe, e-mail: fop-user-unsubscribe@xml.apache.org
For additional commands, e-mail: fop-user-help@xml.apache.org


Re: setting up tables differently based on existance of xml tags. (static variables, recursion, templates, and intrigue)

Posted by JB...@s-s-t.com.
Forgot to add the URL for the XSL list: 
http://www.mulberrytech.com/xsl/xsl-list/

Jay Bryant
Bryant Communication Services




robert frapples <oy...@gmail.com> 
11/30/2004 03:09 PM
Please respond to
fop-user@xml.apache.org


To
fop-user@xml.apache.org
cc

Subject
setting up tables differently based on existance of xml tags. (static 
variables, recursion, templates, and intrigue)






Here's a (hopefully) interesting problem: (I've been editing this
email every day. Every time I can't figure out how to do something, I
come up with an alternate plan, assisted by a few people in #xml on
irc.freenode.net. (Thank you Bebabo).  I figured at this point I'd
share my experiences. If you can think of better, faster, cleaner ways
to do this, I'd love to hear from you.)

source xml:
<document>
    <field>
        <title>a</title>
    </field>
    <field>
        <title>b</title>
        <value/>
    <field>
        <title>c</title>
        <value/>
    </field>
    <field>
        <title>d</title>
    </field>
    <field>
        <title>e</title>
        <value/>
    </field>
</document>

What I want to happen is basically, for-each select="field", if
<field> has a <value/> and the next <field> also has a value, have 4
cells in a row: field[n]/title, text , field[n+1]/title, 'text'.  If
<field> does not have a <value/> or if the next <field> does not have
a <value/>, then have 2 cells: field[n]/title, and 'other text' with
number-columns-spanned=3.

My first thought was this:
/-----------------------------------------------------------------------------------------\
. . .
<xsl:variable name="col" select="''"/>
<xsl:table-body>
<xsl:for-each select="field">
<xsl:choose>
    <xsl:when test="value and not($col)">
        <fo:table-row>
            <fo:table-cell><xsl:value-of select="title"/></fo:table-cell>
            <fo:table-cell>text</fo:table-cell>
            <xsl:variable name="col" select="'true'"/>
    </xsl:when>
    <xsl:when test="value and $col">
            <fo:table-cell><xsl:value-of select="title"/></fo:table-cell>
            <fo:table-cell>text</fo:table-cell>
         </fo:table-row>
         <xsl:variable name="col" select="''"/>
    </xsl:when>
    <xsl:when test="not(value) and not($col)">
         <fo:table-row>
             <fo:table-cell><xsl:value-of select="title"/></fo:table-cell>
             <fo:table-cell>other text</fo:table-cell>
             <fo:table-cell number-columns-spanned="2"/>
         </fo:table-row> 
    </xsl:when>
    <xsl:when test="not(value) and $col">
             <fo:table-cell number-columns-spanned="2"/>
          </fo:table-row>
          <fo:table-row>
             <fo:table-cell><xsl:value-of select="title"/></fo:table-cell>
             <fo:table-cell>other text</fo:table-cell>
             <fo:table-cell number-columns-spanned="2"/>
          </fo:table-row>
           <xsl:variable name="col" select="''"/>
    </xsl:when>
</xsl:choose>
</xsl:for-each>
</fo:table-body>
\---------------------------------------------------------------------------------------/

This obviously will not work, because XSLT does not allow variables to
be reassigned.  After reading extensively from O'Reilly's XSLT book
and the w3c specs for XSLT and XPath, I decided I could achive my goal
with recursive template matches.  Here is what I arrived at:

/----------------------------------------------------------------------------------------\
<xsl:stylesheet . . .>
    <xsl:template match="document">
. . .
         <fo:table-body>
             <xsl:apply-templates select="field[1]">
         </fo:table-body>
. . .
    </xsl:template> <!-- end template match="document" -->
    <xsl:template match="field">
        <xsl:choose>
            <!-- When current <field> has <value/> and next <field>
has <value/> -->
            <xsl:when test="value and following-sibling::field[1]/value">
                <fo:table-row>
                    <fo:table-cell>
                        <!-- current title -->
                        <fo:block><xsl:value-of 
select="title"/></fo:block>
                    </fo:table-cell>
                    <fo:table-cell>
                        <fo:block>text</fo:block>
                    </fo:table-cell>
                    <fo:table-cell>
                        <!-- next title -->
                        <fo:block><xsl:value-of
select="following-sibling::field[1]/title"/></fo:block>
                    </fo:table-cell>
                    <fo:table-cell>
                        <fo:block>text</fo:block>
                    </fo:table-cell>
                </fo:table-row>
                <!-- use this template for the next element (after the
one previously referred
                      to as 'next'. omitting the final '[1]' will
cause this template to be processed
                      remaining <field>, which results in very long
files with lots of stuff i don't
                      want. -->
                <xsl:apply-templates
select="following-sibling::field[position() &gt; 1][1]"/>
            </xsl:when>
            <!-- if not. . . -->
            <xsl:otherwise>
                <fo:table-row>
                    <fo:table-cell>
                        <fo:block><xsl:value-of 
select="title"/></fo:block>
                    </fo:table-cell>
                    <fo:table-cell number-columns-spanned="3">
                        <fo:block>other text</fo:block>
                    </fo:table-cell>
                </fo:table-row>
                <!-- use this template for the next element. again,
don't omit '[1]'. -->
                <xsl:apply-templates 
select="following-sibling::field[1]"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
\------------------------------------------------------------------------------------/

This works great.  Based on my understanding of the XSLT parsers, this
is pretty memory intensive, as applying "following-sibling::field[1]"
actually passes the remaining <field> nodes in <document> every time,
leaving a lot of stuff in the stack. Is that correct?

At any rate, any ideas on a better way to do this?  (Other than a SAX
parsing of the XML with a few variables and leaving an extra node in
select <field>s.)

---------------------------------------------------------------------
To unsubscribe, e-mail: fop-user-unsubscribe@xml.apache.org
For additional commands, e-mail: fop-user-help@xml.apache.org




---------------------------------------------------------------------
To unsubscribe, e-mail: fop-user-unsubscribe@xml.apache.org
For additional commands, e-mail: fop-user-help@xml.apache.org