4

I have a code generating XSL that includes several XSL files. The files are included in order to call templates. There can be the case when one of the files that I need to include does not exist in my file. I could create a dummy file with the same name and an empty template when the original file is missing but I would get a duplicate error when the original file exists.

Bellow is an example:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:date="java.util.Date"                
    xmlns:vector="java.util.Vector"                
    xmlns:math="java.lang.Math"
    xmlns:int="java.lang.Integer"
xmlns:saxon="http://saxon.sf.net/"
    extension-element-prefixes="date vector math int saxon">
  <xsl:include href="file_for_template1.xsl"/> <!-- MIGHT NOT EXIST -->
  <xsl:include href="file_for_template2.xsl"/> <!-- MIGHT NOT EXIST -->
  <xsl:include href="file_for_template3.xsl"/> <!-- MIGHT NOT EXIST -->
  <xsl:output method="xml" 
              version="1.0" 
              encoding="UTF-8" 
              indent="yes"/>
  <xsl:variable name="path" 
                select="concat(base-uri(//Test),'.temp')"/>

  <xsl:template match="/"> 

    <xsl:result-document href="file:/{$path}" method="xml">

      <!-- create the root node -->
      <TEST_GENERATION xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
                       xsi:noNamespaceSchemaLocation="test.xsd">

        <xsl:element name="TEST_ELEMENT">
          <!-- SHOULD NOT BE CALLED IF THE FILE DOES NOT EXIST -->
          <xsl:call-template name="template1Call"/>

          <!-- SHOULD NOT BE CALLED IF THE FILE DOES NOT EXIST -->
          <xsl:call-template name="template2Call"/>

          <!-- SHOULD NOT BE CALLED IF THE FILE DOES NOT EXIST -->
          <xsl:call-template name="template3Call"/>

        </xsl:element>

      <!-- end of root node -->
      </TEST_GENERATION>
      <!-- end of document -->
    </xsl:result-document>
  </xsl:template>
C. M. Sperberg-McQueen
  • 24,596
  • 5
  • 38
  • 65
Alexandru Cimpanu
  • 1,029
  • 2
  • 14
  • 38
  • This [link](http://stackoverflow.com/questions/4471992/how-to-import-stylesheets-in-xslt-conditionally) might help you. – greenPadawan Apr 04 '16 at 10:40
  • I suggest you to use xml catalogs. You will define your dummy templates in the "defaut packages", and when you will extract your stylesheets, along with a dedicated catalog, this will "redirect" the initial imports toward the specialized ones. – potame Apr 04 '16 at 10:58
  • @greenPadawan I saw it but I don't know how to write `` – Alexandru Cimpanu Apr 04 '16 at 11:31
  • @potame I have never used catalogs so I don't understand what you are trying to say. – Alexandru Cimpanu Apr 04 '16 at 11:31

2 Answers2

3

I wish you had told us more about the problem you are trying to solve, rather than focusing on the difficulties you are having with your chosen approach. If we knew the real requirement, we might be able to suggest better ways of tackling it.

The use-when attribute allows you compile-time control of this kind, enabling you to selectively exclude parts of a stylesheet (including xsl:import/include declarations) based on external conditions. You could try:

<xsl:import href="module1.xsl" 
            use-when="doc-available('module1.xsl')"/>

XSLT 2.0 says that in the context for evaluating use-when expressions there are no available documents, so this will always return false, but this rule changes in XSLT 3.0. In 3.0 you could also pass a static parameter to say whether an xsl:import should be active or not, and reference the static parameter in the use-when attribute.

Your next problem will be that the call-template instructions will fail if the import has been excluded, unless you do something to de-activate these instructions as well. One solution would be to replace them with function calls, which you can conditionally exclude with a test on function-available().

But I'm uneasy about helping you down this road, which is why I didn't answer the question before. I think I might be leading you into a swamp, and that if we knew your real destination, we might be able to suggest a better route to it than the one you are taking.

C. M. Sperberg-McQueen
  • 24,596
  • 5
  • 38
  • 65
Michael Kay
  • 156,231
  • 11
  • 92
  • 164
  • Right now I have all the xsl in one sub-project so I know that they all exist, even if just as an empty template. What I am trying to do is to move the imported xsl into other sub-projects. The problems is that I might not have all the sub-projects in the project. – Alexandru Cimpanu Apr 08 '16 at 05:20
  • I think my approach would be to have a different top-level module for each project, and to handle all the includes/imports at the top level. Alternatively, if the application justifies it, I might use a meta-stylesheet to generate customised stylesheets for the different tasks. – Michael Kay Apr 08 '16 at 08:29
1

Here's the approach I would use with catalogs (as background information, catalogs are files to can help providing an entity resolver relying on configuration files, so that entities can be effectively resolved against a symbolic name - among other possibilities):

  1. In your input stylesheet, you can modify the imports the following way :

    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet version="2.0" 
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:xs="http://www.w3.org/2001/XMLSchema"
        xmlns:date="java.util.Date"                
        xmlns:vector="java.util.Vector"                
        xmlns:math="java.lang.Math"
        xmlns:int="java.lang.Integer"
    xmlns:saxon="http://saxon.sf.net/"
        extension-element-prefixes="date vector math int saxon">
      <xsl:include href="cfg:module:file_for_template1.xsl"/> <!-- MIGHT NOT EXIST -->
      <xsl:include href="cfg:module:file_for_template2.xsl"/> <!-- MIGHT NOT EXIST -->
      <xsl:include href="cfg:module:file_for_template3.xsl"/> <!-- MIGHT NOT EXIST -->
    
        ...
    </xsl:stylesheet>
    

The references to files have been changed to a symbolic name - the referenced file itself does not exist.

  1. Here comes in the catalog. You need to first set up a catalog like this so that you will be able to load a "real" file, for the moment, only pointing to the dummy (default) template files.

    <?xml version="1.0" encoding="UTF-8"?>
    <catalog prefer="system" xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog">
        <uri name="cfg:module:file_for_template1.xsl" uri="path/to/default/file_for_template1.xsl"/>
        <uri name="cfg:module:file_for_template2.xsl" uri="path/to/default/file_for_template2.xsl"/>
        <uri name="cfg:module:file_for_template3.xsl" uri="path/to/default/file_for_template3.xsl"/>
    </catalog>
    

The Saxon documentation - Using XML catalogs will provide you information on how to specify the catalog to be used by the XSL-T engine.

  1. Now when you need to override the template files with the the ones that have been defined in the modules, you will set up a catalog for your module along with the redefining template files, with almost the same entries except that the uri attribute (in the catalog) will point to the template file in the module. I assume that from your code that generates the XSLs, you will also be able to create the appropriate catalog.

  2. you will supply a list of catalogs (semicolon-separated) when invoking your transformation. Here you will typically enter first the "standard" catalog, then the "module" catalog (the order is important). When a same entry is found several times in the catalogs, the latest wins. So if the module catalog you redefine the name "cfg:module:file_for_template1.xsl" so that it points to the module file, it is this file that will be loaded.

Note: the file paths, when you use catalogs, are resolved relatively to the XML catalog.

potame
  • 7,597
  • 4
  • 26
  • 33