04 April 2016

1. Creating Stylesheets

This chapter describes how to develop the stylesheets that are used to convert the XML data produced by the report generator into for example XSL-FO, which is used to create PDF reports, or into HTML format or another XML format.

The stylesheets should be stored as page objects within ENOVIA.

1.1. Extract Raw XML

In order to make the process of creating stylesheets easier, you could extract the raw XML that the report generator is producing by setting the "Output Format" to an empty value. E.g.

<AdvancedReport>
    <OutputFormat></OutputFormat>
</AdvancedReport>

By doing so, you could use the produced XML in an external editor to develop the stylesheet.

1.2. Table Settings

The same tables used in the application may also be used for the report creation. However, there are some things to remember.

  • Only columns containing business object select data, relationship select data and program output is included in the report. Columns that has the setting "Column Type" set to either "programHTMLOutput, "checkbox" or "icon" are not included.

  • You can have data-handlers within the table that generates XML formatted data. However, to preserve the output from the data-handler the setting "Preserve Output" must be set to true; otherwise the XML tags are escaped.

  • It is possible to exclude a column; simply set the column setting Exclude From Report to TRUE.

1.3. XML Format

The default XML format is structured like the example below:

<report>
    <group-headers>...
    <headers>...
    <objects>...
    <table-calculations>...
    <meta-data>...
</report>

If you are using the "Advanced Report" definition type, the XML data will be formatted like:

<report>
    <section-1>
        <group-headers>...
        <headers>...
        <objects>...
        <table-calculations>...
    </section-1>

    <section-2>
        <group-headers>...
        <headers>...
        <objects>...
        <table-calculations>...
    </section-2>
    ...
    <meta-data>...
</report>

E.g. each section will be grouped inside its own element, but the content within each section is similar as in the other cases.

The group headers are formatted like:

<group-headers>
    <group-header columns="2">first group header</group-header>
    <group-header columns="3">second group header</group-header>
    <group-header columns="8"/>
</group-headers>

The headers are formatted like:

<headers>
    <header id="c1" type="the-data-type">
        <name>the name of the column</name>
        <expression>the expression</expression>
        <label>the label for the column</label>
        <setting name="name of setting">value of setting</setting>
        ...
    </header>
    ...
</headers>

The objects are formatted like:

<objects>
    <row level="0" isFrom="true" oid="45044.10990.53681.16271" type="Part"
            name="EV-000001" rev="A" current="Create" policy="Development Part"
            symbolicType="type_Part">
        <cell index="0" colRef="c0">
            <value>Part</value>
        </cell>
        <cell index="1" colRef="c1">
            <value>EV-000001</value>
        </cell>
        <cell index="2" colRef="c2">
            <value>A</value>
        </cell>
        <cell index="3" colRef="c3">
            <value>Create</value>
        </cell>
        ...
        <row ...>

        </row>
    </row>
    ...
</objects>

The table calculations are formatted like:

<table-calculations row-count="1540">
    <calculation column="name of column">
        <title>The title text</title>
        <value>The calculated value</value>
    </calculation>
    ...
</table-calculations>

The meta-data section is formatted like:

<meta-data>
    <rt-property key="user">Test Everything</rt-property>
    <rt-property key="fullname">Everything, Test</rt-property>
    ...
    <basic key="type" localizedKey="Type">Part</basic>
    <basic key="policy" localizedKey="Policy">Development Part</basic>
    ...
    <files>
        <file format="..." name="..." size="..."/>
        ...
    </files>
    <attr ...
    ...
</meta-data>

The meta-data section contains information regarding the object, which the report was created for. All basic information and attributes can be found here. Additional select statements could be entered into the TVC Conversion Properties attribute, which will force the selected value to appear in this meta-data section.

1.3.1. Date Values

A value containing date values, will be generated like following:

<value displayDate="..." isoDate="..." isoDateTime="...">mx-value</value>

The mx-value is the date as formatted by ENOVIA. This will vary depending on the "ENOVIA ini-parameters".

The displayDate value is the value as formatted according to the locale, and the iso-date and iso-datetime contains ISO formatted dates with and without the timestamp.

1.4. Stylesheet Template

The following example shows how an XSL-XML document is generated from a structural report (the example lacks details about formatting and should only be used as a template).

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format" version="1.0">

    <!-- Define layout, paper-size, margins etc -->
    <xsl:template match="/">
        <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
            <fo:layout-master-set>
                <fo:simple-page-master master-name="only"
                                        margin-right="1.5cm"
                                        margin-left="1.5cm"
                                        margin-bottom="0.5cm"
                                        margin-top="1cm"
                                        page-width="29.7cm"
                                        page-height="21cm">
                    <fo:region-body margin-top="3cm" margin-bottom="1.8cm"/>
                    <fo:region-before extent="3cm"/>
                    <fo:region-after extent="1.5cm"/>
                </fo:simple-page-master>
            </fo:layout-master-set>
            <fo:page-sequence master-reference="only">
                <xsl:call-template name="header"/>
                <xsl:call-template name="footer"/>
                <xsl:call-template name="body"/>
            </fo:page-sequence>
        </fo:root>
    </xsl:template>

    <!—- Generates the header that will appear on each page -->
    <!-- The header contains some basic information regarding -->
    <!-- creator and creation date. -->
    <xsl:template name="header">
        <fo:static-content flow-name="xsl-region-before">
            <fo:table width="100%">
                <fo:table-column/>
                <fo:table-column/>
                <fo:table-body>
                    <fo:table-row>
                        <fo:table-cell>
                            <fo:block text-align="right">
                                Created by: <xsl:value-of select="/report/meta-data/rt-property[@key =
                                'fullname']"/>
                            </fo:block>
                            <fo:block text-align="right">
                                Created date: <xsl:value-of select="/report/meta-data/rt-property[@key =
                                'date']"/>
                            </fo:block>
                        </fo:table-cell>
                    </fo:table-row>
                    <fo:table-row>
                        <fo:table-cell>
                            <fo:block>
                                Report
                            </fo:block>
                        </fo:table-cell>
                    </fo:table-row>
                </fo:table-body>
            </fo:table>
        </fo:static-content>
    </xsl:template>

    <!—- Generates the footer that will appear on each page -->
    <!-- The footer contains some basic information regarding -->
    <!-- type,name and revision and page numbering -->
    <xsl:template name="footer">
        <fo:static-content flow-name="xsl-region-after">
            <fo:table width="100%">
                <fo:table-column/>
                <fo:table-column/>
                <fo:table-body>
                    <fo:table-row>
                        <fo:table-cell>
                            <fo:block>
                                Report for <xsl:value-of select="/report/meta-data/basic[@key = 'type']"/>,
                                <xsl:value-of select="/report/meta-data/basic[@key = 'name']"/>,
                                <xsl:value-of select="/report/meta-data/basic[@key = 'revision']"/>
                            </fo:block>
                        </fo:table-cell>
                        <fo:table-cell>
                            <fo:block line-height="14pt" font-size="10pt" text-align="end">
                                Page <fo:page-number/> of <fo:page-number-citation ref-id="endofdoc"/>
                            </fo:block>
                        </fo:table-cell>
                    </fo:table-row>
                </fo:table-body>
            </fo:table>
        </fo:static-content>
    </xsl:template>

    <!-- Generates the body, i.e. the table with the data -->
    <!-- The table contains header information for each column -->
    <xsl:template name="body">
        <fo:flow flow-name="xsl-region-body">
            <fo:table width="100%">
                <fo:table-column/>
                <xsl:for-each select="/report/headers/header">
                    <fo:table-column>
                        <xsl:if test="setting[@name='Column Width']">
                            <xsl:attribute name="column-width">
                                <xsl:value-of select="setting[@name='Column Width']"/>
                            </xsl:attribute>
                        </xsl:if>
                    </fo:table-column>
                </xsl:for-each>
                <fo:table-header>
                    <fo:table-row background-color="#999999">
                        <fo:table-cell>
                            <fo:block>Level</fo:block>
                        </fo:table-cell>
                        <xsl:for-each select="/report/headers/header">
                            <fo:table-cell xsl:use-attribute-sets="tableHeader">
                                <fo:block><xsl:value-of select="label"/></fo:block>
                            </fo:table-cell>
                        </xsl:for-each>
                    </fo:table-row>
                </fo:table-header>
                <fo:table-body>
                    <xsl:for-each select="//row[@level != 0]">
                        <fo:table-row>
                            <xsl:if test="position() mod 2 = 0">
                                <xsl:attribute name="background-color">#CCCCCC</xsl:attribute>
                            </xsl:if>
                            <fo:table-cell xsl:use-attribute-sets="tableCell">
                                <fo:block><xsl:value-of select="@level"/></fo:block>
                            </fo:table-cell>
                            <xsl:for-each select="cell/value">
                                <fo:table-cell xsl:use-attribute-sets="tableCell">
                                    <fo:block><xsl:value-of select="."/></fo:block>
                                </fo:table-cell>
                            </xsl:for-each>
                        </fo:table-row>
                    </xsl:for-each>
                </fo:table-body>
            </fo:table>
            <!-- Empty final block to use when counting pages -->
            <fo:block id="endofdoc"
                      font-size="11pt"
                      font-family="sans-serif"
                      line-height="15pt"
                      space-after.optimum="7pt"
                      text-align="start">
                <xsl:text/>
            </fo:block>
        </fo:flow>
    </xsl:template>

</xsl:stylesheet>

1.5. Stylesheet Import/Include

It is possible to import or include another stylesheet that is stored either on disk or within a page object.

By using the prefix of page: and the name of the page object in the <xsl:import…​> or <xsl:include…​> tags, the URI resolver knows that it should include the referenced page object.

<xsl:include href="tvc:stylesheet/MyIncludedStylesheet.xslt"/>

<xsl:import href="page:My Imported Stylesheet"/>

1.6. Passing Custom Properties to the Stylesheet

In order to make the stylesheets more flexible, it is possible to pass custom parameters to the stylesheet that is evaluated during the XSL transformation step.

There are two ways how to send these parameters:

  1. Defining the transformer properties within the Report Definition configuration.

    If you are using business object based report definitions, these transformer properties are entered within the conversion properties section; the difference is that the transformer properties must start with a prefix as shown below:
    transformProperty.paperSize=A3

    Note that the prefix "transformProperty." is stripped when the property is passed to the XSLT process.

  2. Via the pre-process page set transform properties. All request parameters submitted from the pre-process page that starts with "transformer." is treated as a transformer parameter.

    The prefix "transformer." is stripped when the property is passed to the XSLT process.

    The transformer parameters are retrieved in the XSL as shown below:

    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet xmlns:xsl=http://www.w3.org/1999/XSL/Transform
                    xmlns:fo="http://www.w3.org/1999/XSL/Format" version="1.0">
        <xsl:param name="paperSize" select="'A4'"/>
        ...
    </xsl:stylesheet>

1.6.1. Default Parameters Passed To the Stylesheet

Following table contains the parameters that automatically are passed to the transformer process.

appServer.scheme
appServer.host
appServer.port
appServer.contextPath
appServer.URL

1.7. Creating Excel Reports

To create a report that’s possible to open with Microsoft Excel™, this can be done in two different ways:

  • Using SpreadSheetML.

  • Using a Custom report and produce native Excel through the POI API.

1.7.1. SpreadsheetML

SpreadsheetML is an XML format accepted by Microsoft Excel 2003 and later.

When producing reports following this standard, you need to specify the following on the report definition:

Configuration Option Value / Description Additional Notes

Stylesheet

Name of a stylesheet that converts the XML into SpreadsheetML XML.

<Stylesheet>…​</Stylesheet>

Output Format

XML

<OutputFormat>XML</OutputFormat>

This will instruct the RPT framework that your output will be XML and the conversion is done through an XSLT transformation.

Displayed Output Format

Excel

This value is displayed for the end user.

Report Settings

file.suffix=xml file.contentType=application/vnd.ms-excel

<Settings>
    <Setting name="file.suffix">xml</Setting>
    <Setting name="contentType">application/vnd.ms-excel</Setting>
</Settings>

These properties are used when downloading the report.

Below are some useful references that describe the SpreadsheetML format deeper:

1.7.2. Custom Report using POI

POI is an open source library provided by the Apache organization for working with Microsoft Office files. Through POI, you can for example create native Excel files (XLS).

POI is part of the TVC-Core library, which RPT depends upon. The POI classes are available within the "com.technia.tvc.poi" package.

To create custom reports, see the developer documentation.

To summarize, you need do the following:

  1. Create a report definition of type "Custom Report". Example:

    import com.technia.tvc.reportgenerator.DataExtractor;
    import com.technia.tvc.reportgenerator.DataExtractorCtx;
    import com.technia.tvc.reportgenerator.impl.CustomReport;
    import com.technia.tvc.poi.hssf.usermodel.HSSFCell;
    import com.technia.tvc.poi.hssf.usermodel.HSSFRow;
    import com.technia.tvc.poi.hssf.usermodel.HSSFSheet;
    import com.technia.tvc.poi.hssf.usermodel.HSSFWorkbook;
    import java.io.IOException;
    
    public class ExampleWithPOI extends CustomReport implements DataExtractor {
    
        private static final long serialVersionUID = 1L;
    
        public void extract(DataExtractorCtx ctx) throws IOException {
            HSSFWorkbook workbook = new HSSFWorkbook();
            HSSFSheet sheet = workbook.createSheet();
            for (int rowNo = 0; rowNo < 10; rowNo++) {
                HSSFRow row = sheet.createRow(rowNo);
                for (int colNo = 0; colNo < 10; colNo++) {
                    HSSFCell cell = row.createCell(colNo);
                    cell.setCellType(HSSFCell.CELL_TYPE_STRING);
                    cell.setCellValue(String.format("%d : %d", rowNo, colNo));
                }
            }
            workbook.write(ctx.getOutputStream());
        }
        @Override
        public DataExtractor getDataExtractor() {
            return this;
        }
    }
  2. Define some properties within the Conversion Properties attribute:

    file.suffix=xls
    file.contentType=application/vnd.ms-excel

    Example report in XML:

    <CustomReport>
        <Name>Excel Report Example</Name>
        <Description></Description>
        <Global>true</Global>
        <Class>com.acme.rpt.ExampleWithPOI</Class>
        <DisplayedOutputFormat>Excel</DisplayedOutputFormat>
        <Settings>
            <Setting name="file.on.demand" value="true"/>
            <Setting name="file.suffix" value="xls"/>
            <Setting name="file.contentType" value="application/vnd.ms-excel"/>
        </Settings>
    </CustomReport>