Subject: [xsl] Map of functions compared to xsl:attribute-set From: "Tony Graham" <tgraham@xxxxxxxxxx> Date: Mon, 25 Nov 2013 18:22:59 -0000 (GMT) |
As part of trying out XSLT 3.0 techniques [1], I've been looking at using a map of functions that return attributes as an alternative to using xsl:attribute-set. However, in the work so far, neither is significantly better than the other IMO. The current input is a JATS [6] document that contains two <table-wrap> [3][4] that each contain a <table>. For the sake of the exercise, the <table> in the second <table-wrap> has 'style="orange"' [5]. There's currently two branches in the repository: 'master' uses xsl:attribute-set, and 'table-map' uses maps of functions. Also for the sake of the exercise there's multiple stylesheets in use: - xhtml-tables-fo3xsl - Base table module 'master': [7] 'table-map': [8] - red.xsl - Styles text is the table head as red 'master': [9] 'table-map': [10] - blue.xsl - Styles text is the table body as blue 'master': [11] 'table-map': [12] - red-blue.xsl - Imports both 'red.xsl' and 'blue.xsl' to achieve a combined effect (or try to) 'master': [13] 'table-map': [14] - orange.xsl - Styles table background as orange 'master': [15] 'table-map': [16] There's also an Oxygen project file to make it easier to run the different-coloured stylesheets. The xsl:attribute-set approach uses an attribute set named after each table-related element, and the different stylesheets add attribute instructions to the appropriate attribute set. 'red-blue.xsl' takes the convenient approach of just importing 'red.xsl' and 'blue.xsl' (and, coincidentally, manages to import the whole JATS stylesheets twice, but that's the price you pay for convenience) and the xsl:attribute-set from the different stylesheets just combine (since at present there's no overlap/conflict to worry about) [19]. For the table that wants to be orange, 'orange.xsl' passes specific attributes in a 'table-attributes' parameter: ----- <xsl:template match="table"> <xsl:param name="table-attributes" as="attribute()*" tunnel="yes" /> <fo:table xsl:use-attribute-sets="table fo:table"> <xsl:sequence select="$table-attributes" /> <xsl:apply-templates /> </fo:table> </xsl:template> ----- that override attributes defined in the 'table' attribute set: ----- <xsl:template match="table"> <xsl:param name="table-attributes" as="attribute()*" tunnel="yes" /> <fo:table xsl:use-attribute-sets="table fo:table"> <xsl:sequence select="$table-attributes" /> <xsl:apply-templates /> </fo:table> </xsl:template> ----- For the 'map of functions' approach, the templates for the table-related elements each have a 'table-functions' tunnel parameter that is a map of the functions to use for the appropriate table-related element(s). These override the default functions, which don't do anything. Making a default map is analogous to needing to define empty attribute sets since calling a non-existent attribute set is an error [17], but an alternative would be to only call the function for the current element if it exists in the current $table-functions map. 'red.xsl' and 'blue.xsl' work by passing appropriate maps of functions. 'red-blue.xsl' is the same as for the xsl:attribute-set approach, and it doesn't produce red table head text because, with the way that import precedence works, the template for 'thead' in 'red.xsl' is never used. The template for the table that wants to be orange uses the same mechanism as the general table templates and passes a map containing a function to use for the <table> element: ----- <xsl:function name="x3tb:orange-table" as="attribute()*"> <xsl:param name="context" as="element()" /> <xsl:attribute name="background-color" select="'orange'" /> </xsl:function> <xsl:template match="table[@style eq 'orange']"> <xsl:next-match> <xsl:with-param name="table-functions" as="map(xs:string, function(element()) as attribute()*)" select="map { 'table' := x3tb:orange-table#1 }" tunnel="yes" /> </xsl:next-match> </xsl:template> ----- The absence of an XPath-level computed attribute constructor made making the function verbose compared to how you'd make an attribute in XQuery and meant that it could not be an anonymous function. That function for 'table' overrode the default: ----- <xsl:template match="table"> <xsl:param name="table-functions" as="map(xs:string, function(*))?" tunnel="yes" /> <xsl:variable name="use-table-functions" select="map:new(($default-table-functions, $table-functions))" as="map(xs:string, function(*))" /> <fo:table> <xsl:sequence select="$use-table-functions('table')(.)" /> <xsl:apply-templates /> </fo:table> </xsl:template> ----- Using a function that takes the context node as a parameter to define attributes is not dissimilar to using xsl:attribute-set, since both can evaluate expressions based on the context node and global variables only. The way that attribute instructions defined with xsl:attribute-set can combine across modules can work to your advantage, but there's no way to 'undefine' attribute instructions in attribute sets other than defining attributes with the same name in a situation that has higher precedence. Combining the functions that define attributes or the maps of functions that define attributes across modules in a way that 'just works' when you add a new module or override an existing template still requires thought. However, the 'map of functions' approach could be extended to use functions that take other, additional parameters to further control the processing and/or other maps of other functions could be used in other places to generate elements. Indeed, the entire table processing could be rewritten to use a map of functions that each 'do' the processing for the context element, but by then you'll have just reimplemented 'typeswitch' [18] in XSLT. Regards, Tony Graham tgraham@xxxxxxxxxx Consultant http://www.mentea.net Mentea 13 Kelly's Bay Beach, Skerries, Co. Dublin, Ireland -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- XML, XSL-FO and XSLT consulting, training and programming Chair, Print and Page Layout Community Group @ W3C [1] http://www.biglist.com/lists/lists.mulberrytech.com/xsl-list/archives/201310/msg00107.html [2] https://github.com/MenteaXML/xslt3testbed/blob/table-map/xml/table-test/table-test.xml [3] https://github.com/MenteaXML/xslt3testbed/blob/table-map/xml/table-test/table-test.xml#L30 [4] https://github.com/MenteaXML/xslt3testbed/blob/table-map/xml/table-test/table-test.xml#L50 [5] http://jats.nlm.nih.gov/articleauthoring/tag-library/1.0/index.html?attr=style [6] http://jats.nlm.nih.gov/index.html [7] https://github.com/MenteaXML/xslt3testbed/blob/master/xsl/xhtml-tables-fo3.xsl [8] https://github.com/MenteaXML/xslt3testbed/blob/table-map/xsl/xhtml-tables-fo3.xsl [9] https://github.com/MenteaXML/xslt3testbed/blob/master/xml/table-test/red.xsl [10] https://github.com/MenteaXML/xslt3testbed/blob/table-map/xml/table-test/red.xsl [11] https://github.com/MenteaXML/xslt3testbed/blob/master/xml/table-test/blue.xsl [12] https://github.com/MenteaXML/xslt3testbed/blob/table-map/xml/table-test/blue.xsl [13] https://github.com/MenteaXML/xslt3testbed/blob/master/xml/table-test/red-blue.xsl [14] https://github.com/MenteaXML/xslt3testbed/blob/table-map/xml/table-test/red-blue.xsl [15] https://github.com/MenteaXML/xslt3testbed/blob/master/xml/table-test/orange.xsl [16] https://github.com/MenteaXML/xslt3testbed/blob/table-map/xml/table-test/orange.xsl [17] http://www.w3.org/TR/xslt-30/#err-XTSE0710 [18] http://www.w3.org/TR/xquery/#id-typeswitch [19] There's also attribute sets corresponding to the FOs that the table elements become, but they have no effect at present.
Current Thread |
---|
|
<- Previous | Index | Next -> |
---|---|---|
[no subject], Unknown | Thread | [xsl] [ANN] Saxon/C - Saxon for the, Michael Kay |
[no subject], Unknown | Date | Re: [xsl] XSLT streaming: the proce, Michael Kay |
Month |