logo

10 июн. 2011 г.

BIP: XSLT шаблон таблицы среза

Пример реализации в XSL шаблона разметки, позволяющего отобразить таблицу среза (pivot table)

Не стал подробно расписывать все конструкции, использованные в шаблоне. Думаю, приведенных в них комментариев будет достаточно.

Пример состоит их 2 файлов: XML и XSL.
XML файл данных

<?xml version="1.0" encoding="UTF-8"?>
<ROWSET>
<ROW>
<CALC_PERIOD>2011-01-01</CALC_PERIOD>
<CALC_PERIOD_MONTH>Январь</CALC_PERIOD_MONTH>
<PERIOD>2011-01-01</PERIOD>
<PERIOD_MONTH>Январь</PERIOD_MONTH>
<LIAB_VOLUME>1000</LIAB_VOLUME>
</ROW>
<ROW>
<CALC_PERIOD>2011-02-01</CALC_PERIOD>
<CALC_PERIOD_MONTH>Февраль</CALC_PERIOD_MONTH>
<PERIOD>2011-01-01</PERIOD>
<PERIOD_MONTH>Январь</PERIOD_MONTH>
<LIAB_VOLUME>2000</LIAB_VOLUME>
</ROW>
<ROW>
<CALC_PERIOD>2011-02-01</CALC_PERIOD>
<CALC_PERIOD_MONTH>Февраль</CALC_PERIOD_MONTH>
<PERIOD>2011-02-01</PERIOD>
<PERIOD_MONTH>Февраль</PERIOD_MONTH>
<LIAB_VOLUME>2000</LIAB_VOLUME>
</ROW>
<ROW>
<CALC_PERIOD>2011-03-01</CALC_PERIOD>
<CALC_PERIOD_MONTH>Март</CALC_PERIOD_MONTH>
<PERIOD>2011-01-01</PERIOD>
<PERIOD_MONTH>Январь</PERIOD_MONTH>
<LIAB_VOLUME>2000</LIAB_VOLUME>
</ROW>
<ROW>
<CALC_PERIOD>2011-03-01</CALC_PERIOD>
<CALC_PERIOD_MONTH>Март</CALC_PERIOD_MONTH>
<PERIOD>2011-02-01</PERIOD>
<PERIOD_MONTH>Февраль</PERIOD_MONTH>
<LIAB_VOLUME>2000</LIAB_VOLUME>
</ROW>
<ROW>
<CALC_PERIOD>2011-03-01</CALC_PERIOD>
<CALC_PERIOD_MONTH>Март</CALC_PERIOD_MONTH>
<PERIOD>2011-03-01</PERIOD>
<PERIOD_MONTH>Март</PERIOD_MONTH>
<LIAB_VOLUME>2000</LIAB_VOLUME>
</ROW>
<ROW>
<CALC_PERIOD>2011-04-01</CALC_PERIOD>
<CALC_PERIOD_MONTH>Апрель</CALC_PERIOD_MONTH>
<PERIOD>2011-01-01</PERIOD>
<PERIOD_MONTH>Январь</PERIOD_MONTH>
<LIAB_VOLUME>4000</LIAB_VOLUME>
</ROW>
<ROW>
<CALC_PERIOD>2011-04-01</CALC_PERIOD>
<CALC_PERIOD_MONTH>Апрель</CALC_PERIOD_MONTH>
<PERIOD>2011-02-01</PERIOD>
<PERIOD_MONTH>Февраль</PERIOD_MONTH>
<LIAB_VOLUME>1000</LIAB_VOLUME>
</ROW>
<ROW>
<CALC_PERIOD>2011-04-01</CALC_PERIOD>
<CALC_PERIOD_MONTH>Апрель</CALC_PERIOD_MONTH>
<PERIOD>2011-03-01</PERIOD>
<PERIOD_MONTH>Март</PERIOD_MONTH>
<LIAB_VOLUME>3000</LIAB_VOLUME>
</ROW>
<ROW>
<CALC_PERIOD>2011-04-01</CALC_PERIOD>
<CALC_PERIOD_MONTH>Апрель</CALC_PERIOD_MONTH>
<PERIOD>2011-04-01</PERIOD>
<PERIOD_MONTH>Апрель</PERIOD_MONTH>
<LIAB_VOLUME>5000</LIAB_VOLUME>
</ROW>
</ROWSET>



XSL шаблон разметки

<?xml version="1.0"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns="urn:schemas-microsoft-com:office:spreadsheet" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet" xmlns:ora="http://www.oracle.com/XSL/Transform/java/" xmlns:xdofo="http://xmlns.oracle.com/oxp/fo/extensions" xmlns:xdoxslt="http://www.oracle.com/XSL/Transform/java/oracle.apps.xdo.template.rtf.XSLTFunctions" xmlns:xdoxliff="urn:oasis:names:tc:xliff:document:1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:msxsl="urn:schemas-microsoft-com:xslt">
<xsl:decimal-format name="myFormat" decimal-separator="." grouping-separator=" "/>
<!--ключ-индекс по расчетным периодам -->
<xsl:key name="key_col_1" match="CALC_PERIOD" use="."/>
<!--ключ-индекс по учетным периодам -->
<xsl:key name="key_row_1" match="PERIOD" use="."/>
<!--главная функция-шаблон -->
<xsl:template match="/">
<xsl:processing-instruction name="mso-application">
<xsl:text>progid="Excel.Sheet"</xsl:text>
</xsl:processing-instruction>
<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet" xmlns:html="http://www.w3.org/TR/REC-html40">
<DocumentProperties xmlns="urn:schemas-microsoft-com:office:office">
<Author/>
<LastAuthor/>
<Created/>
<Company/>
<Version>11.9999</Version>
</DocumentProperties>
<ExcelWorkbook xmlns="urn:schemas-microsoft-com:office:excel">
<WindowHeight>11895</WindowHeight>
<WindowWidth>15180</WindowWidth>
<WindowTopX>480</WindowTopX>
<WindowTopY>135</WindowTopY>
<ProtectStructure>False</ProtectStructure>
<ProtectWindows>False</ProtectWindows>
</ExcelWorkbook>
<Styles>
<Style ss:ID="Default" ss:Name="Normal">
<Alignment ss:Vertical="Bottom"/>
<Borders/>
<Font ss:FontName="Arial Cyr" x:CharSet="204"/>
<Interior/>
<NumberFormat/>
<Protection/>
</Style>
<Style ss:ID="s1">
<Alignment ss:Vertical="Bottom" ss:Horizontal="Center"/>
<Borders>
<Border ss:Position="Bottom" ss:LineStyle="Continuous" ss:Weight="1"/>
<Border ss:Position="Left" ss:LineStyle="Continuous" ss:Weight="1"/>
<Border ss:Position="Right" ss:LineStyle="Continuous" ss:Weight="1"/>
<Border ss:Position="Top" ss:LineStyle="Continuous" ss:Weight="1"/>
</Borders>
<Font ss:FontName="Arial Cyr" x:CharSet="204"/>
<Interior/>
<NumberFormat/>
<Protection/>
</Style>
<Style ss:ID="s2">
<Alignment ss:Vertical="Bottom" ss:WrapText="1"/>
<Borders>
<Border ss:Position="Bottom" ss:LineStyle="Continuous" ss:Weight="1"/>
<Border ss:Position="Left" ss:LineStyle="Continuous" ss:Weight="2"/>
<Border ss:Position="Right" ss:LineStyle="Continuous" ss:Weight="1"/>
</Borders>
<Font ss:FontName="Times New Roman" x:CharSet="204" x:Family="Roman" ss:Color="#000000"/>
<Interior ss:Color="#FFFFFF" ss:Pattern="Solid"/>
</Style>
<Style ss:ID="s3">
<Alignment ss:Horizontal="Center" ss:Vertical="Center" ss:WrapText="1"/>
<Borders>
<Border ss:Position="Bottom" ss:LineStyle="Continuous" ss:Weight="2"/>
<Border ss:Position="Left" ss:LineStyle="Continuous" ss:Weight="2"/>
<Border ss:Position="Right" ss:LineStyle="Continuous" ss:Weight="2"/>
<Border ss:Position="Top" ss:LineStyle="Continuous" ss:Weight="2"/>
</Borders>
<Font ss:FontName="Times New Roman" x:CharSet="204" x:Family="Roman" ss:Bold="1"/>
<Interior ss:Color="#CCCCCC" ss:Pattern="Solid"/>
</Style>
<Style ss:ID="s4">
<Alignment ss:Horizontal="Right" ss:Vertical="Center" ss:WrapText="1"/>
<Borders>
<Border ss:Position="Bottom" ss:LineStyle="Continuous" ss:Weight="1"/>
<Border ss:Position="Left" ss:LineStyle="Continuous" ss:Weight="1"/>
<Border ss:Position="Right" ss:LineStyle="Continuous" ss:Weight="1"/>
<Border ss:Position="Top" ss:LineStyle="Continuous" ss:Weight="1"/>
</Borders>
<Font ss:FontName="Times New Roman" x:CharSet="204" x:Family="Roman" ss:Bold="0"/>
<NumberFormat ss:Format="#,##0.000"/>
</Style>
<Style ss:ID="s41">
<Alignment ss:Horizontal="Right" ss:Vertical="Center" ss:WrapText="1"/>
<Borders>
<Border ss:Position="Bottom" ss:LineStyle="Continuous" ss:Weight="1"/>
<Border ss:Position="Left" ss:LineStyle="Continuous" ss:Weight="1"/>
<Border ss:Position="Right" ss:LineStyle="Continuous" ss:Weight="1"/>
<Border ss:Position="Top" ss:LineStyle="Continuous" ss:Weight="1"/>
</Borders>
<Font ss:FontName="Times New Roman" x:CharSet="204" x:Family="Roman" ss:Bold="0"/>
<Interior ss:Color="#CCCCCC" ss:Pattern="Solid"/>
<NumberFormat ss:Format="#,##0.000"/>
</Style>
<Style ss:ID="s5">
<Alignment ss:Horizontal="Right" ss:Vertical="Center" ss:WrapText="1"/>
<Borders>
<Border ss:Position="Bottom" ss:LineStyle="Continuous" ss:Weight="1"/>
<Border ss:Position="Left" ss:LineStyle="Continuous" ss:Weight="1"/>
<Border ss:Position="Right" ss:LineStyle="Continuous" ss:Weight="2"/>
<Border ss:Position="Top" ss:LineStyle="Continuous" ss:Weight="1"/>
</Borders>
<Font ss:FontName="Times New Roman" x:CharSet="204" x:Family="Roman" ss:Bold="1"/>
<Interior ss:Color="#CCCCCC" ss:Pattern="Solid"/>
</Style>
<Style ss:ID="s6">
<Alignment ss:Horizontal="Right" ss:Vertical="Center" ss:WrapText="1"/>
<Borders>
<Border ss:Position="Left" ss:LineStyle="Continuous" ss:Weight="2"/>
</Borders>
<Font ss:FontName="Times New Roman" x:CharSet="204" x:Family="Roman" ss:Bold="0"/>
</Style>
<Style ss:ID="s7">
<Alignment ss:Horizontal="Right" ss:Vertical="Center" ss:WrapText="1"/>
<Borders>
<Border ss:Position="Top" ss:LineStyle="Continuous" ss:Weight="2"/>
</Borders>
<Font ss:FontName="Times New Roman" x:CharSet="204" x:Family="Roman" ss:Bold="0"/>
</Style>
</Styles>
<Worksheet ss:Name="Sheet 1">
<!--матрица с данными -->
<xsl:variable name="rowset" select="/ROWSET/ROW"/>
<!--список расчетных периодов -->
<xsl:variable name="col_1" select="$rowset/CALC_PERIOD"/>
<!--список учетных периодов -->
<xsl:variable name="row_1" select="$rowset/PERIOD"/>
<!--кол-во distinct-элементов в списках расчетных и учетных периодов -->
<xsl:variable name="col_1_cnt" select="count($col_1[generate-id(.)=generate-id(key('key_col_1',.)[1])])"/>
<xsl:variable name="row_1_cnt" select="count($row_1[generate-id(.)=generate-id(key('key_row_1',.)[1])])"/>
<Table ss:DefaultColumnWidth="75">
<!--заголовочная строка таблицы: Период | Расчетный период , где вторая ячейка объединяет все ячейки доступных расчетных периодов -->
<Row>
<Cell ss:StyleID="s3">
<Data ss:Type="String">Период</Data>
</Cell>
<Cell ss:StyleID="s3" ss:MergeAcross="{($col_1_cnt)-1}">
<Data ss:Type="String">Расчетный период</Data>
</Cell>
</Row>
<!--выводим заголовочную строку с ячейками по каждому расчетному периоду
==================================
| Период | Расчетный период |
==================================
| | Январь | Февраль | Март |
==================================
-->
<!--если есть хоть один расчетный период - столбец кросс-таблицы, то ... -->
<xsl:if test="count($col_1[generate-id(.)=generate-id(key('key_col_1',.)[1])])!=0">
<!--в цикле по отсортированному списку (по дате в маске YYYY-MM-DD) расчетных периодов находим для каждого периода его описание - название месяца -->
<Row>
<xsl:for-each select="$col_1[generate-id(.)=generate-id(key('key_col_1',.)[1])]">
<xsl:sort select="substring-before(.,'-')" data-type="number" order="ascending"/>
<xsl:sort select="substring-before(substring-after(.,'-'),'-')" data-type="number" order="ascending"/>
<xsl:variable name="cur" select="."/>
<Cell ss:StyleID="s3" ss:Index="{1+position()}">
<Data ss:Type="String">
<xsl:value-of select="$rowset[CALC_PERIOD = $cur][1]/CALC_PERIOD_MONTH"/>
</Data>
</Cell>
</xsl:for-each>
</Row>
</xsl:if>

<!--в distinct-цикле по учетным периодам ... -->
<xsl:for-each select="$row_1[generate-id(.)=generate-id(key('key_row_1',.)[1])]">
<!--сортируем набор периодов (по дате в маске YYYY-MM-DD) -->
<xsl:sort select="substring-before(.,'-')" data-type="number" order="ascending"/>
<xsl:sort select="substring-before(substring-after(.,'-'),'-')" data-type="number" order="ascending"/>
<xsl:variable name="cur" select="."/>
<Row>
<!--выводим описание учетного периода - название месяца -->
<Cell ss:StyleID="s5">
<Data ss:Type="String">
<xsl:value-of select="$rowset[PERIOD = $cur][1]/PERIOD_MONTH"/>
</Data>
</Cell>
<xsl:variable name="cr" select="."/>
<xsl:variable name="CrPs" select="position()"/>
<!--в отсортированном distinct-цикле по расчетным периодам -->
<xsl:for-each select="$col_1[generate-id(.)=generate-id(key('key_col_1',.)[1])]">
<xsl:sort select="substring-before(.,'-')" data-type="number" order="ascending"/>
<xsl:sort select="substring-before(substring-after(.,'-'),'-')" data-type="number" order="ascending"/>
<!--проверяем на индексы i/j ячейки матрицы - если равны, то закрашиваем ячейку цветом -->
<xsl:variable name="mn" select="."/>
<xsl:variable name="CrStl">
<xsl:choose>
<xsl:when test="$CrPs=position()">
<xsl:value-of select="'s41'"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="'s4'"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<!--выводим значение суммы в текущем разрезе учетного и расчетного периода -->
<Cell ss:StyleID="{$CrStl}">
<Data ss:Type="Number">
<xsl:value-of select="sum($rowset[PERIOD = $cr and CALC_PERIOD = $mn ]/LIAB_VOLUME)"/>
</Data>
</Cell>
</xsl:for-each>
<Cell ss:StyleID="s6"/>
</Row>
</xsl:for-each>
<!--выводим строку, которая верхней bold-границей визуально закрывает кросс-таблицу -->
<Row>
<Cell ss:StyleID="s7"/>
<xsl:for-each select="$col_1[generate-id(.)=generate-id(key('key_col_1',.)[1])]">
<Cell ss:StyleID="s7"/>
</xsl:for-each>
</Row>
</Table>
<WorksheetOptions xmlns="urn:schemas-microsoft-com:office:excel">
<PageSetup>
<PageMargins x:Bottom="0.984251969" x:Left="0.78740157499999996" x:Right="0.78740157499999996" x:Top="0.984251969"/>
</PageSetup>
<Selected/>
<ProtectObjects>False</ProtectObjects>
<ProtectScenarios>False</ProtectScenarios>
<FreezePanes/>
<FrozenNoSplit/>
<SplitHorizontal>2</SplitHorizontal>
<SplitVertical>1</SplitVertical>
<!--закрепляем верхние строки -->
<TopRowBottomPane>2</TopRowBottomPane>
<LeftColumnRightPane>1</LeftColumnRightPane>
<ActivePane>2</ActivePane>
</WorksheetOptions>
</Worksheet>
</Workbook>
</xsl:template>
</xsl:stylesheet>


Использование подобной пары - XML + XSL - позволяет получить вот такую симпатичную таблицу среза.

Комментариев нет:

Отправить комментарий