Reputation: 347
I'm totally having 10 Detail
element like below, In that I want to split and arrange the Code
element value as per the Indicator Plan
value.
<Details>
<Detail>
<Code>001</Code>
<Type>Alt</Type>
<Plan>N</Plan>
</Detail>
<Detail>
<Code>002</Code>
<Type>Alt</Type>
<Plan>N</Plan>
</Detail>
<Detail>
<Code>003</Code>
<Type>Alt</Type>
<Plan>N</Plan>
</Detail>
<Detail>
<Code>004</Code>
<Type>Alt</Type>
<Plan>N</Plan>
</Detail>
<Detail>
<Code>005</Code>
<Type>Alt</Type>
<Plan>N</Plan>
</Detail>
<Detail>
<Code>006</Code>
<Type>Alt</Type>
<Plan>Y</Plan>
</Detail>
<Detail>
<Code>007</Code>
<Type>Alt</Type>
<Plan>Y</Plan>
</Detail>
<Detail>
<Code>008</Code>
<Type>Alt</Type>
<Plan>N</Plan>
</Detail>
<Detail>
<Code>009</Code>
<Type>Alt</Type>
<Plan>N</Plan>
</Detail>
<Detail>
<Code>010</Code>
<Type>Alt</Type>
<Plan>N</Plan>
</Detail>
</Details>
Per page I need only 5 Code or rows to allow, in that I want the Plan='N'
code should come first and Plan='Y'
Code should come next and I have tried the below code
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" version="3.0" exclude-result-prefixes="xs">
<xsl:template match="Details">
<xsl:call-template name="Table_Left">
<xsl:with-param name="start_row" as="xs:integer">1</xsl:with-param>
<xsl:with-param name="end_row" as="xs:integer">10</xsl:with-param>
</xsl:call-template>
</xsl:template>
<xsl:template name="Table_Left">
<xsl:param name="start_row" as="xs:integer">0</xsl:param>
<xsl:param name="end_row" as="xs:integer">0</xsl:param>
<xsl:variable name="Details" select="Details"/>
<xsl:variable name="total_rows">
<xsl:value-of select="count($Details//Detail[Type = 'Alt'])"/>
</xsl:variable>
<xsl:variable name="sorted_plans_alt" as="element()">
<sorted_plans>
<xsl:for-each select="$Details//Detail[Type = 'Alt']">
<xsl:sort select=".//Detail[Type = 'Alt']"
order="ascending" data-type="number"/>
<xsl:sequence select="."/>
</xsl:for-each>
</sorted_plans>
</xsl:variable>
<xsl:variable name="plans_subset">
<xsl:sequence
select="
subsequence($sorted_plans_alt//Detail[Type = 'Alt'], $start_row, $end_row)"
/>
</xsl:variable>
<xsl:variable name="preceding_plan" as="element()">
<preceding_plan>
<xsl:choose>
<xsl:when test="$start_row = 1">
<empty/>
</xsl:when>
<xsl:otherwise>
<xsl:sequence
select="subsequence($sorted_plans_alt//Detail[Type = 'Alt'], $start_row - 1, 1)"
/>
</xsl:otherwise>
</xsl:choose>
</preceding_plan>
</xsl:variable>
<xsl:variable name="rows_to_output" as="xs:integer">
<xsl:variable name="number_of_r_plans" as="xs:integer">
<xsl:value-of
select="count($Details//Detail[Type = 'Alt'])"/>
</xsl:variable>
<xsl:choose>
<xsl:when test="$number_of_r_plans > 0">
<xsl:value-of select="$end_row - 5"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$end_row"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="left_table" as="element()">
<xsl:for-each select="($plans_subset//Detail[Type = 'Alt'][Plan='N'])[position() <= $rows_to_output]">
<xsl:value-of select="current()/Code"/>
</xsl:for-each>
<xsl:for-each select="($plans_subset//Detail[Type = 'Alt'][Plan='Y'])[position() <= $rows_to_output]">
<xsl:value-of select="current()/Code"/>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="right_table">
<topic id="topic_1" xml:lang="en-US">
<xsl:apply-templates select="$left_table/*"/>
</topic>
</xsl:variable>
<xsl:sequence select="$right_table"/>
<xsl:choose>
<xsl:when test="$start_row + $rows_to_output <= $total_rows">
<xsl:call-template name="Table_Left">
<xsl:with-param name="start_row" as="xs:integer"
select="$start_row + $rows_to_output"/>
<xsl:with-param name="end_row" as="xs:integer" select="$end_row"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="$start_row = 999"/>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
In the output below I'm getting 7 codes in the first Page (expecting only 5 codes per page even mentioned in the xsl). But I'm getting Plan='Y'
(006 and 007) codes also.
1st page 2nd page
001 008
002 009
003 010
004
005
006
007
Expected output would be like below, 5 codes in the first page and remaning codes in the 2nd page. I need Plan=N
Code should come first and Plan=Y
Code should follow
1st page 2nd page
001 008
002 009
003 010
004 006
005 007
Upvotes: 0
Views: 55
Reputation: 3250
The format of the expected output is not clear.
Try something along the lines of:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/Details">
<!-- sort plan N first -->
<xsl:variable name="sorted">
<xsl:perform-sort>
<xsl:sort select="Plan='Y'"/>
<xsl:sequence select="Detail"/>
</xsl:perform-sort>
</xsl:variable>
<!-- output in groups of 5 per page -->
<output>
<xsl:for-each-group select="$sorted/Detail" group-adjacent="(position() - 1) idiv 5">
<page num="{position()}">
<xsl:copy-of select="current-group()/Code"/>
</page>
</xsl:for-each-group>
</output>
</xsl:template>
</xsl:stylesheet>
With the provided input, the output will be:
<?xml version="1.0" encoding="UTF-8"?>
<output>
<page num="1">
<Code>001</Code>
<Code>002</Code>
<Code>003</Code>
<Code>004</Code>
<Code>005</Code>
</page>
<page num="2">
<Code>008</Code>
<Code>009</Code>
<Code>010</Code>
<Code>006</Code>
<Code>007</Code>
</page>
</output>
Upvotes: 1
Reputation: 163595
It's not clear to me exactly what output you want, but here are some observations about your code, some of them purely stylistic.
<xsl:with-param name="start_row" as="xs:integer">1</xsl:with-param>
Use
<xsl:with-param name="start_row" as="xs:integer" select="1"/>
It avoids a type conversion (from a text node to an integer). Some people advocate always using an as
clause but I would leave it out here because it's obvious that select="1"
gives you an xs:integer
.
<xsl:variable name="total_rows">
<xsl:value-of select="count($Details//Detail[Type = 'Alt'])"/>
</xsl:variable>
Use
<xsl:variable name="total_rows" select="count($Details/Detail[Type = 'Alt'])"/>
You really don't want to construct an XML document here, you want to compute an integer.
And there's no point searching all descendants (//
) when you know that the Detail
elements will be immediate children of Details
.
<xsl:for-each select="$Details//Detail[Type = 'Alt']">
<xsl:sort select=".//Detail[Type = 'Alt']"
This is wrong. The sort key is computed relative to the element you are sorting, your select expression will select nothing. I don't know how to correct it because I don't know what you actually want to sort on.
<xsl:variable name="left_table" as="element()">
<xsl:for-each select="($plans_subset//Detail[Type = 'Alt'][Plan='N'])[position() <= $rows_to_output]">
<xsl:value-of select="current()/Code"/>
</xsl:for-each>
<xsl:for-each select="($plans_subset//Detail[Type = 'Alt'][Plan='Y'])[position() <= $rows_to_output]">
<xsl:value-of select="current()/Code"/>
</xsl:for-each>
</xsl:variable>
This is wrong because your variable will consist of text nodes (produced by xsl:value-of
), not a single element as declared in the as
attribute. Perhaps you intended xsl:copy-of
, but then it would be element()*
.
And current()/Code
is a longwinded way of saying Code
.
I'm afraid these are points of detail, and I suspect I'm not seeing the wood for the trees. I suspect there's something much more fundamental that's wrong; I haven't understood what you want to achieve, but I don't see anything in the requirement that suggests that a recursive named template is an appropriate way of achieving it.
Upvotes: 0