0

Good afternoon! I have a big trouble with xsl..

On enter I have this XML of menu with multiple <chapter />s, that consits multiple <paragraph />s, that consists multiple <text />s or <quote />s:

<menu>
    <chapter>
        <name>Chapter 1</name>
        <paragraph>
            <name>Paragraph 1</name>
            <text>
                <name>Text 1</name>
            </text>
            <text>
                <name>Text 2</name>
            </text>
            <text>
                <name>Text 3</name>
            </text>
        </paragraph>
        <paragraph>
            <name>Paragraph 2</name>
            <quote>
                <name>Quote 1</name>
            </quote>
            <quote>
                <name>Quote 2</name>
            </quote>
        </paragraph>
        <paragraph>
            <name>Paragraph 3</name>
            <quote>
                <name>Quote 3</name>
            </quote>
        </paragraph>
        <paragraph>
            <name>Paragraph 4</name>
            <text>
                <name>Text 4</name>
            </text>
        </paragraph>
    </chapter>
</menu>

On exit I want have something like this (paragraphs that proportionally separated by rows - the same quantity of paragraphs + texts + quotes in each row):

<h1>Chapter 1</h1>

<div class="clearfix">
    <div class="row">
        <h2>Paragraph 1</h2>

        <p class="text">Text 1</p>
        <p class="text">Text 2</p>
        <p class="text">Text 3</p>
    </div>
    <div class="row">
        <h2>Paragraph 2</h2>

        <p class="quote">Quote 1</p>
        <p class="quote">Quote 2</p>
    </div>
    <div class="row">
        <h2>Paragraph 3</h2>

        <p class="quote">Quote 3</p>

        <h2>Paragraph 4</h2>

        <p class="text">Text 4</p>
    </div>
</div>

I'm trying to give this result by xsl:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xsl:stylesheet>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output xmlns="http://www.w3.org/TR/xhtml1/strict" doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" encoding="utf-8" indent="yes" method="html" omit-xml-declaration="no" version="1.0" media-type="text/xml"/>

    <xsl:variable name="n" select="number(3)" />

    <xsl:template match="/">
        <xsl:apply-templates select="menu" />
    </xsl:template>

    <xsl:template match="/menu">
        <xsl:apply-templates select="chapter" />
    </xsl:template>

    <xsl:template match="chapter">
        <h1><xsl:value-of disable-output-escaping="yes" select="name" /></h1>

        <xsl:if test="paragraph/node()">
            <div class="clearfix">
                <div class="row">
                    <xsl:apply-templates select="paragraph" />
                </div>
            </div>
        </xsl:if>
    </xsl:template>

    <xsl:template match="chapter/paragraph">
        <xsl:variable name="limit" select="ceiling((count(../paragraph) + count(../paragraph/*[name() = 'text' or name() = 'quote'])) div $n)" />
        <xsl:variable name="position" select="position() - 1" />
        <xsl:variable name="prev_count" select="count(preceding-sibling::paragraph/*[name() = 'text' or name() = 'quote'])" />

        <h2><xsl:value-of disable-output-escaping="yes" select="name" /></h2>

        <xsl:if test="text/node() or quote/node()">
            <xsl:for-each select="text">
                <p class="text"><xsl:value-of disable-output-escaping="yes" select="name" /></p>
            </xsl:for-each>

            <xsl:for-each select="quote">
                <p class="quote"><xsl:value-of disable-output-escaping="yes" select="name" /></p>
            </xsl:for-each>
        </xsl:if>

        <xsl:variable name="prev_index" select="round(($position + $prev_count) div $limit)" />
        <xsl:variable name="next_index" select="round(($position + 1 + $prev_count + count(*[name() = 'text' or name() = 'quote'])) div $limit)" />

        <xsl:if test="$prev_index != $next_index">
            <xsl:text disable-output-escaping="yes">&lt;/div&gt;&lt;div class=&quot;row&quot;&gt;</xsl:text>
        </xsl:if>
    </xsl:template>
</xsl:stylesheet>

But this code return good results don't always. Maybe somebody have a better solution? I'm sorry for this worst code ;(

UPD: I can't split paragraphs - just group by rows

2 Answers2

0

Check this,

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

    <xsl:template match="chapter/name">
        <h1>
            <xsl:apply-templates/>
        </h1>
    </xsl:template>

    <xsl:template match="paragraph">
        <div class="row">
            <xsl:apply-templates/>
        </div>
    </xsl:template>

    <xsl:template match="paragraph/name">
        <h2>
            <xsl:apply-templates/>
        </h2>
    </xsl:template>

    <xsl:template match="text">
        <p class="text">
            <xsl:apply-templates/>
        </p>
    </xsl:template>

    <xsl:template match="quote">
        <p class="quote">
            <xsl:apply-templates/>
        </p>
    </xsl:template>

    <xsl:template match="text/name">
        <xsl:apply-templates/>
    </xsl:template>

    <xsl:template match="quote/name">
        <xsl:apply-templates/>
    </xsl:template>

    <xsl:template match="chapter">
        <div class="clearfix">
            <xsl:apply-templates/>
        </div>
    </xsl:template>

    <xsl:template match="menu">
        <xsl:apply-templates/>
    </xsl:template>
sanjay
  • 1,020
  • 1
  • 16
  • 38
  • No, in one row can be more then one paragraph (see my example). I want to receive $n rows (see variable $n) with evenly distributed (count(paragraphs) + count(quotes) + count(texts) must be ~= in each row) paragraphs, quotes and texts. – Nikita Sviridenko Oct 09 '15 at 07:54
0

Thanks michael.hor257k code, i was found a solution:

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
xmlns:exsl="http://exslt.org/common"
xmlns:set="http://exslt.org/sets"
extension-element-prefixes="exsl set">
    <xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>

    <xsl:template match="/">
        <xsl:apply-templates select="menu/chapter"/>
    </xsl:template>

    <xsl:template match="chapter">
        <h1><xsl:value-of disable-output-escaping="yes" select="name" /></h1>

        <div class="clearfix">
            <xsl:call-template name="split-chapter">
                <xsl:with-param name="nodes" select="paragraph" />
                <xsl:with-param name="rows_count" select="3" />
            </xsl:call-template>
        </div>
    </xsl:template>

    <xsl:template match="paragraph">
        <h2><xsl:value-of disable-output-escaping="yes" select="name" /></h2>

        <xsl:if test="text/node() or quote/node()">
            <xsl:for-each select="text">
                <p class="text"><xsl:value-of disable-output-escaping="yes" select="name" /></p>
            </xsl:for-each>

            <xsl:for-each select="quote">
                <p class="quote"><xsl:value-of disable-output-escaping="yes" select="name" /></p>
            </xsl:for-each>
        </xsl:if>
    </xsl:template>

    <xsl:template name="split-chapter">
        <xsl:param name="nodes" />
        <xsl:param name="rows_count" />
        <xsl:param name="remaining_nodes" select="dummy-node" />
        <xsl:param name="limit" select="0" />
        <xsl:param name="i" select="0" />

        <xsl:variable name="total_length" select="count($nodes/text) + count($nodes/quote) + count($nodes)" />
        <xsl:variable name="real_limit">
            <xsl:choose>
                <xsl:when test="$limit = 0">
                    <xsl:value-of select="floor($total_length div $rows_count)" />
                </xsl:when>
                <xsl:otherwise>
                    <xsl:value-of select="$limit" />
                </xsl:otherwise>
            </xsl:choose>
        </xsl:variable>

        <xsl:choose>
            <xsl:when test="$total_length &gt; $real_limit and count($nodes) &gt; 1">
                <xsl:call-template name="split-chapter">
                    <xsl:with-param name="nodes" select="$nodes[not(position()=last())]"/>
                    <xsl:with-param name="rows_count" select="$rows_count"/>
                    <xsl:with-param name="remaining_nodes" select="$remaining_nodes | $nodes[last()]"/>
                    <xsl:with-param name="limit" select="$real_limit" />
                    <xsl:with-param name="i" select="$i" />
                </xsl:call-template>
            </xsl:when>
            <xsl:when test="$i + 1 &gt;= $rows_count">
                <div class="row">
                    <xsl:apply-templates select="$nodes | $remaining_nodes" />
                </div>
            </xsl:when>
            <xsl:otherwise>
                <div class="row">
                    <xsl:apply-templates select="$nodes" />
                </div>

                <xsl:if test="$remaining_nodes">
                    <xsl:call-template name="split-chapter">
                        <xsl:with-param name="nodes" select="$remaining_nodes"/>
                        <xsl:with-param name="rows_count" select="$rows_count"/>
                        <xsl:with-param name="limit" select="$real_limit" />
                        <xsl:with-param name="i" select="$i + 1" />
                    </xsl:call-template>
                </xsl:if>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
</xsl:stylesheet>
Community
  • 1
  • 1