71897

Unanticipated behavior

I've been trying to figure out how to best modularize my XSLT stylesheets to facilitate re-use. I hit upon the idea of using <xsl:apply-imports/> as a way of introducing document-specific attributes to standard tag transformations. This is <strong>not</strong> working the way I expected it would, and I can't even begin to fathom what is going on here. Here is a simplified version of the stylesheet:

<!-- main.xsl --> <?xml version="1.0" encoding="utf-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format"> <xsl:import href="html-customizations.xsl"/> <xsl:output method="xml" indent="yes" omit-xml-declaration="no"/> <xsl:template match="para"> <fo:block> <xsl:attribute name="space-after">1em</xsl:attribute> <xsl:apply-templates/> </fo:block> </xsl:template> <!-- =============== --> <!-- Inline Elements --> <!-- =============== --> <xsl:template match="i"> <fo:inline font-style="italic"> <xsl:apply-imports/> <xsl:apply-templates/> </fo:inline> </xsl:template> <!-- ================ --> <!-- Tables --> <!-- ================ --> <xsl:template match="table"> <fo:table> <xsl:apply-imports/> <xsl:apply-templates/> </fo:table> </xsl:template> <xsl:template match="tr"> <fo:table-row> <xsl:apply-imports/> <xsl:apply-templates/> </fo:table-row> </xsl:template> <xsl:template match="td | th"> <fo:table-cell> <xsl:apply-imports/> <xsl:apply-templates/> </fo:table-cell> </xsl:template> </xsl:stylesheet>

The imported stylesheet:

<!-- html-customizations.xsl --> <?xml version="1.0" encoding="utf-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format"> <xsl:template match="td | th"> <xsl:attribute name="hyphenate">true</xsl:attribute> </xsl:template> </xsl:stylesheet>

Here is the XML input file:

<!-- test.xml --> <para> <table> <tr><td>Spongebob Squarepants, <i>Chair</i></td></tr> <tr><td>Patrick Starfish, <i>Vice Cchair</i></td></tr> <tr><td>Squidword, <i>Secretary</i></td></tr> </table> </para>

$ xalan -o out.xml test.xml main.xsl

out.xml: <?xml version="1.0" encoding="UTF-8"?> <fo:block xmlns:fo="http://www.w3.org/1999/XSL/Format" space-after="1em"> <fo:table> <fo:table-row> <fo:table-cell hyphenate="true">Spongebob Squarepants, <fo:inline font-style="italic">ChairChair</fo:inline> </fo:table-cell> <fo:table-cell hyphenate="true">Spongebob Squarepants, <fo:inline font-style="italic">ChairChair</fo:inline> </fo:table-cell> </fo:table-row> <fo:table-row> <fo:table-cell hyphenate="true">Patrick Starfish, <fo:inline font-style="italic">Vice CchairVice Cchair</fo:inline> </fo:table-cell> <fo:table-cell hyphenate="true">Patrick Starfish, <fo:inline font-style="italic">Vice CchairVice Cchair</fo:inline> </fo:table-cell> </fo:table-row> <fo:table-row> <fo:table-cell hyphenate="true">Squidword, <fo:inline font-style="italic">SecretarySecretary</fo:inline> </fo:table-cell> <fo:table-cell hyphenate="true">Squidword, <fo:inline font-style="italic">SecretarySecretary</fo:inline> </fo:table-cell> </fo:table-row> <fo:table-row> <fo:table-cell hyphenate="true">Spongebob Squarepants, <fo:inline font-style="italic">ChairChair</fo:inline> ... ...

As you can see, every child of an element matched by a template that includes <xsl:apply-imports/> is repeated! I included the imported stylesheet in order to illustrate what I'm trying to do. If I comment out this import:

<!-- <xsl:import href="html-customizations.xsl"/> -->

The repeating behavior is the same:

<?xml version="1.0" encoding="UTF-8"?> <fo:block xmlns:fo="http://www.w3.org/1999/XSL/Format" space-after="1em"> <fo:table> <fo:table-row> <fo:table-cell>Spongebob Squarepants, <fo:inline font-style="italic">ChairChair</fo:inline>Spongebob Squarepants, <fo:inline font-style="italic">ChairChair</fo:inline> </fo:table-cell> <fo:table-cell>Spongebob Squarepants, <fo:inline font-style="italic">ChairChair</fo:inline>Spongebob Squarepants, <fo:inline font-style="italic">ChairChair</fo:inline> </fo:table-cell> </fo:table-row> <fo:table-row> ... ...

sans the attribute I'm trying to add from the imported stylesheet; i.e. just the <strong>presence</strong> of the <xsl:apply-imports/> processing instruction causes the output elements to be doubled. Also note that this is not just a <strong>xalan</strong> problem -- the same thing happens on MSXML on Windows 7.

Any thoughts? I was counting on this working, so am now pulling my hair out trying to figure out how to fix this so it works.

BTW, my assumptions of how <xsl:apply-imports/> can be used is based on the examples given under the <strong>xsl:import</strong> section of Michael Kay's book. If anyone knows of a reference that explains the behavior I'm seeing above, please share.

Answer1:

I agree that the behaviour of apply-imports is difficult to understand. The problem is that apply-imports always finds a template that matches the current node, even if the user did not define it. In that case, the default template applies.

The following stylesheet works:

<strong>XSLT Stylesheet</strong>

<?xml version="1.0" encoding="utf-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format"> <xsl:import href="html-customizations.xsl"/> <xsl:output method="xml" indent="yes" omit-xml-declaration="no"/> <xsl:template match="para"> <fo:block> <xsl:attribute name="space-after">1em</xsl:attribute> <xsl:apply-templates/> </fo:block> </xsl:template> <!-- =============== --> <!-- Inline Elements --> <!-- =============== --> <xsl:template match="i"> <fo:inline font-style="italic"> <xsl:apply-templates/> </fo:inline> </xsl:template> <!-- ================ --> <!-- Tables --> <!-- ================ --> <xsl:template match="table"> <fo:table> <xsl:apply-templates/> </fo:table> </xsl:template> <xsl:template match="tr"> <fo:table-row> <xsl:apply-templates/> </fo:table-row> </xsl:template> <xsl:template match="td | th"> <fo:table-cell> <xsl:apply-imports/> <xsl:apply-templates/> </fo:table-cell> </xsl:template> </xsl:stylesheet>

As you can see, I have removed two apply-imports elements, only leaving the one inside template/@match='td | th'. Then, the output will be

<strong>XML Output</strong>

<?xml version="1.0" encoding="UTF-8"?> <fo:block xmlns:fo="http://www.w3.org/1999/XSL/Format" space-after="1em"> <fo:table> <fo:table-row> <fo:table-cell hyphenate="true">Spongebob Squarepants, <fo:inline font-style="italic">ChairChair</fo:inline> </fo:table-cell> </fo:table-row> <fo:table-row> <fo:table-cell hyphenate="true">Patrick Starfish, <fo:inline font-style="italic">Vice CchairVice Cchair</fo:inline> </fo:table-cell> </fo:table-row> <fo:table-row> <fo:table-cell hyphenate="true">Squidword, <fo:inline font-style="italic">SecretarySecretary</fo:inline> </fo:table-cell> </fo:table-row> </fo:table> </fo:block>

<strong>What exactly is happening?</strong>

apply-imports looks for a template that

    <li>matches the current node</li> <li>matches the current mode</li> <li>is inside an imported stylesheet</li> </ul>

    Now, the crucial bit is: this instruction will invoke the <strong>built-in templates</strong> if no such template can be found in an imported stylesheet. In the case of tr:

    <xsl:template match="tr"> <fo:table-row> <xsl:apply-imports/> <xsl:apply-templates/> </fo:table-row> </xsl:template>

    The default action for element nodes is traversing it and applying templates to its content, so the snippet above actually translates to

    <xsl:template match="tr"> <fo:table-row> <xsl:apply-templates/> <xsl:apply-templates/> </fo:table-row> </xsl:template>

    and this is why the output contains duplicates. I assume now you also understand why commenting out xsl:import did not help, otherwise I'm glad to elaborate.

    <hr>

    Since you were also asking for a reference, this is explained in the XSLT 2.0 and XPath 2.0 Programmer's Reference by Michael Kay, page 238.

Recommend

  • upload image after crop in tinymce 4
  • How to capture a string between two tags
  • Connect ipython-notebook via SSH tunnel from a remote location
  • Content Security Policy Internet explorer error
  • Use pnorm from Rmath.h with Rcpp
  • What is the Performance, Safety, and Alignment of a Data member hidden in an embedded char array in
  • Bundled scripts not working MVC
  • Why is my event listener written in Google Closure not working?
  • javascript XSLT nodes, selecting the first of a group (merge-like)
  • css Star-rating html
  • How can i use same custom fonts for both android and ios using react native
  • How to record a JNLP/ Java Web Start application with JMeter
  • How to align an image side by side with a heading element?
  • How to make nicEditor snaplet? (Several questions)
  • Formatting a text in a table cell with PHPWord e.g. bold, font, size e.t.c
  • Why does the font in these TD elements render at different sizes?
  • garbled css name when styling within UiBinder
  • How to open html table in xls on click of a button
  • Plotting line graph with factors in R
  • Custom validator control occupying space even though display set to dynamic
  • How do I alternate colors in Flat List (React Native)
  • Listbox within Listbox and scrolling trouble in Windows Phone 7 Silverlight
  • How to use remove-erase idiom for removing empty vectors in a vector?
  • Read a local file using javascript
  • Highlight and Bold text in JTextPane
  • Xamarin Forms - UWP Fonts
  • Why HTML5 Canvas with a larger size stretch a drawn line?
  • Why doesn't :active or :focus work on text links in webkit? (safari & chrome)
  • Submit form in a displaytag pagination
  • How to apply VCL Styles to DLL-based forms in Inno Setup?
  • Change an a tag attribute in JavaScript based on screen width
  • When should I choose bucket sort over other sorting algorithms?
  • jquery mobile loadPage not working
  • Delete MySQLi record without showing the id in the URL
  • align graphs with different xlab
  • Comma separated Values
  • using conditional logic : check if record exists; if it does, update it, if not, create it
  • Trying to get generic when generic is not available
  • Can't mass-assign protected attributes when import data from csv file
  • Unable to use reactive element in my shiny app