Organization & Reusability

One of the main challenges when building XSL style sheets is devising sensible project structure. Decent project design may greatly reduce the complexity and verbosity of your style sheets, thus making them easier to both understand and maintain. For us, ease of maintenance of XSL style sheets has become one of the most important properties of any XSL project.

Using templates and functions is - naturally - the first step in the direction of creating more succinct style sheets. But these only allow you to use chunks of functionality in a single file. Once you would like to leverage the power of certain functions and templates across multiple files, you need to start breaking your style sheets apart and then reconnecting them again using import.

xsl:import

To import an externa style sheet into another one, you can use a simple xsl:import tag:

<xsl:import href="common/utils.xsl"/>

The above imports a file in common/utils.xsl into your style sheet, allowing you to use all variables, functions and templates defined in that file.

There are two important things to keep in mind when importing style sheets:

  • XSL namespaces: User-defined functions in XSL need be declared in custom namespaces, which results in a bit of an overhead when importing them from other files. Apart from the obvious specification of the namespace in the file where the functions are declared, you also need to declare a corresponding namespace in the file into which you are importing them.
  • Imagine the following scenario: style sheet A requires style sheets B and C. Also, style sheet B requires style sheet C for proper functioning. In such case, you have two options: import C into B and then only import B into A. A better way, to make future dependency management straightforward, is to import C and B into A, in this order. That way, it is guaranteed that B has access to C since it has been imported to A.

Let's look at an application of the points mentioned above:

# file: `common/utils.xsl`
<xsl:stylesheet
  version="2.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:utils="utils">

  <xsl:function name="utils:double">
    <xsl:param name="value" />
    <xsl:value-of select="$value * 2" />
  </xsl:function>

  <xsl:function name="utils:triple">
    <xsl:param name="value" />
    <xsl:value-of select="$value * 3" />
  </xsl:function>
</xsl:stylesheet>

# file: `common/layout.xsl`
<xsl:stylesheet
  version="2.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:utils="utils">

  <xsl:variable name="margin" select="utils:double(10)" />
</xsl:stylesheet>

# file: `main.xsl`
<xsl:stylesheet
  version="2.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:utils="utils">

  <xsl:import href="common/utils.xsl" />
  <xsl:import href="common/layout.xsl" />

  <xsl:variable name="padding" select="utils:triple($margin)" />
  ...
  ...
  ...
</xsl:stylesheet>

As you can see, ordering the imports in the correct way allows you to use functions from the utils namespace in both layout.xsl and main.xsl while only importing it once in main.xsl. utils namespace declaration needs to appear in all of them, however.

Relative vs. Absolute

As long as you develop in a single environment, it makes virtually no difference whether you use relative or absolute paths to import style sheets. That is a very unrealistic scenario in real-life, however, and the problems present themselves rather swiftly once you start to develop in and deploy into multiple environments.

Long story short: when importing a style sheet, relative paths are resolved relative to the processor, not the style sheet. (Or at least it would seem that way from our experience.) Therefore, absolute paths are needed. Naturally, another obstacle arises from this requirement: how do you supply XSL with a correct absolute path in a particular environment?

This is where the BIP's xdo configuration file enters the scene. It enables you to define properties later available in the href attribute of xsl:import tags (and, unfortunetaly, nowhere else). Such property can be defined as follows:

<?xml version="1.0" encoding="UTF-8"?>
<config xmlns="http://xmlns.oracle.com/oxp/config/" version="1.0.0">
  <properties>
    ...
    <property name="xslt.PATH_TO_PROJECT">'/path/to/project'</property>
  </properties>
  ...
</config>

It then subsequently be used as follows:

<xsl:import href="${PATH_TO_PROJECT}/tmp/Context.xsl"/>

(Note the dollar sign position: it is placed before the opening curly brace instead of afterwards, as is the case with typical XSL variables.)

As was implied earlier, BIP variables defined in the xdo can only be used in the context of the xsl:import tag. They may not be accessed anywhere else. Since absolute paths should be used for any other resources such as images as well, it is unfortunately necessary to create an XSL variable that contains an identical absolute path. This can then be used in other contexts than the import tag.

<xsl:variable name="PATH_TO_PROJECT select="'/path/to/project'" />
...
<fo:external-graphic src="{$PATH_TO_PROJECT}/stylesheets/media/ikea.bmp" width="106"/>

(Note that in the example above, the dollar sign is inside the curly braces since we are using a regular XSL variable.)

Resolving Absolute Paths in Multiple Environments

Alright, so now we know that we need to have the correct absolute path in two places in our project for every environment. How do we achieve this without having to enter them manually, which is horribly cumbersome and error-prone?

The best way we have managed to conceive goes as follows: every time the Java project generates a template (or every time it starts, if it runs for prolonged periods of time), it searches for a placeholder such as {#absolute-path#} (utterly dependent on your liking, naturally) in both the stylesheets/xdo.xml configuration file and one agreed-upon XSL style sheet such as stylesheets/context.xsl. It then simply replaces the placeholder with the actual absolute path in the given environment and saves the new files in a tmp directory of the project. It is these files that are then used when generating PDFs, XLSs, etc.

results matching ""

    No results matching ""