1 <?xml version="1.0"?> 2 <!-- 3 A stylesheet which takes lower-level template annotations and produces an output 4 stylesheet - something which is capable of transforming XML documents into Web 5 pages or other kinds of XML documents. 6 7 Copyright (C) 2005, 2006, 2007, 2008, 2010 Paul Boddie <paul@boddie.org.uk> 8 9 This program is free software; you can redistribute it and/or modify it under 10 the terms of the GNU Lesser General Public License as published by the Free 11 Software Foundation; either version 3 of the License, or (at your option) any 12 later version. 13 14 This program is distributed in the hope that it will be useful, but WITHOUT 15 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 16 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 17 details. 18 19 You should have received a copy of the GNU Lesser General Public License along 20 with this program. If not, see <http://www.gnu.org/licenses/>. 21 --> 22 <xsl:stylesheet version="1.0" 23 xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 24 xmlns:axsl="http://www.w3.org/1999/XSL/TransformAlias" 25 xmlns:template="http://www.boddie.org.uk/ns/xmltools/template" 26 xmlns:str="http://exslt.org/strings" 27 extension-element-prefixes="str"> 28 29 <xsl:output indent="yes"/> 30 <xsl:namespace-alias stylesheet-prefix="axsl" result-prefix="xsl"/> 31 32 <!-- Fragment extraction support. --> 33 <xsl:param name="element-id"/> 34 35 <!-- Match the document itself. --> 36 37 <xsl:template match="/"> 38 <axsl:stylesheet version="1.0" 39 xmlns:dyn="http://exslt.org/dynamic" 40 extension-element-prefixes="dyn"> 41 42 <!-- Add support for special namespace declarations. --> 43 <xsl:for-each select="//@*[local-name() = 'expr-prefix']"> 44 <xsl:attribute namespace="{namespace-uri()}" name="{name(.)}"></xsl:attribute> 45 </xsl:for-each> 46 47 <axsl:output indent="yes"/> 48 49 <!-- Include internationalisation (i18n) support if appropriate. --> 50 <axsl:param name="translations"/> 51 <axsl:param name="locale"/> 52 53 <!-- Include fragment support if appropriate. --> 54 <axsl:param name="element-path"/> 55 56 <axsl:template match="/"> 57 <xsl:choose> 58 <!-- Fragment production. --> 59 <xsl:when test="$element-id != ''"> 60 <axsl:for-each select="dyn:evaluate($element-path)"> 61 <xsl:apply-templates select="@*|node()"/> 62 </axsl:for-each> 63 </xsl:when> 64 <!-- Whole template production. --> 65 <xsl:otherwise> 66 <xsl:apply-templates select="@*|node()"/> 67 </xsl:otherwise> 68 </xsl:choose> 69 </axsl:template> 70 71 <!-- Produce the rules for each element. --> 72 73 <xsl:apply-templates select="//*[@template:element]"> 74 <xsl:with-param name="top-level">true</xsl:with-param> 75 </xsl:apply-templates> 76 77 </axsl:stylesheet> 78 </xsl:template> 79 80 81 82 <!-- Match elements referencing elements. --> 83 84 <xsl:template match="*[@template:element]" priority="1"> 85 <xsl:param name="top-level">false</xsl:param> 86 <xsl:call-template name="enter-element"> 87 <xsl:with-param name="top-level" select="$top-level"/> 88 </xsl:call-template> 89 </xsl:template> 90 91 <xsl:template name="enter-element"> 92 <xsl:param name="top-level"/> 93 <xsl:choose> 94 <!-- Produce templates where this is a top-level definition. --> 95 <xsl:when test="$top-level = 'true'"> 96 <xsl:call-template name="element-template"> 97 <xsl:with-param name="element-names" select="@template:element"/> 98 </xsl:call-template> 99 </xsl:when> 100 <!-- Produce references to elements where this is within a template. --> 101 <xsl:otherwise> 102 <xsl:variable name="first-name" select="str:split(@template:element, ',')[1]"/> 103 <!-- Check to see if this is a recursive reference. --> 104 <xsl:variable name="recursive-element" select="ancestor::*[$first-name = str:split(@template:element, ',')[1]]"/> 105 <xsl:choose> 106 <!-- Generate a reference to the previous element definition. --> 107 <xsl:when test="$recursive-element"> 108 <axsl:apply-templates select="{$first-name}" mode="{generate-id($recursive-element)}"> 109 <xsl:call-template name="element-sorting"/> 110 </axsl:apply-templates> 111 </xsl:when> 112 <!-- Generate a reference to this element definition. --> 113 <xsl:otherwise> 114 <axsl:apply-templates select="{$first-name}" mode="{generate-id(.)}"> 115 <xsl:call-template name="element-sorting"/> 116 </axsl:apply-templates> 117 </xsl:otherwise> 118 </xsl:choose> 119 </xsl:otherwise> 120 </xsl:choose> 121 </xsl:template> 122 123 <xsl:template name="element-template"> 124 <xsl:param name="element-names"/> 125 <xsl:variable name="this-name" select="substring-before($element-names, ',')"/> 126 <xsl:variable name="next-names" select="substring-after($element-names, ',')"/> 127 <xsl:variable name="next-name" select="str:split($next-names, ',')[1]"/> 128 <xsl:choose> 129 <!-- Non-last part of a list of element names. --> 130 <!-- Produce a template referencing another template. --> 131 <xsl:when test="$this-name != ''"> 132 <!-- Produce a template with a mode. --> 133 <axsl:template match="{$this-name}" mode="{generate-id(.)}"> 134 <axsl:apply-templates select="{$next-name}" mode="{generate-id(.)}"> 135 <xsl:call-template name="element-sorting"/> 136 </axsl:apply-templates> 137 </axsl:template> 138 <!-- Produce the other elements' templates... --> 139 <xsl:call-template name="element-template"> 140 <xsl:with-param name="element-names" select="$next-names"/> 141 </xsl:call-template> 142 </xsl:when> 143 <!-- Last part of a list of element names. --> 144 <!-- Produce a template with content. --> 145 <xsl:otherwise> 146 <!-- Produce a template with a mode. --> 147 <axsl:template match="{$element-names}" mode="{generate-id(.)}"> 148 <xsl:call-template name="enter-attribute"/> 149 </axsl:template> 150 </xsl:otherwise> 151 </xsl:choose> 152 </xsl:template> 153 154 <xsl:template name="element-sorting"> 155 <xsl:if test="@template:sort"> 156 <axsl:sort select="{@template:sort}"/> 157 </xsl:if> 158 </xsl:template> 159 160 161 162 <!-- Match elements referencing selected sections. --> 163 164 <xsl:template match="*[@template:select]" priority="1"> 165 <!-- Produce a loop using the annotation's content. --> 166 <axsl:for-each select="{@template:select}"> 167 <xsl:call-template name="enter-attribute"/> 168 </axsl:for-each> 169 </xsl:template> 170 171 172 173 <!-- Match special conditional expression attributes. --> 174 175 <xsl:template match="*[@template:if]" priority="2"> 176 <xsl:param name="top-level">false</xsl:param> 177 <xsl:choose> 178 <!-- Since this rule may be invoked at the top level, ignore conditions. --> 179 <xsl:when test="$top-level = 'true'"> 180 <xsl:call-template name="enter-element"> 181 <xsl:with-param name="top-level" select="$top-level"/> 182 </xsl:call-template> 183 </xsl:when> 184 <!-- As part of a template, generate the condition. --> 185 <xsl:otherwise> 186 <axsl:if test="{@template:if}"> 187 <xsl:choose> 188 <xsl:when test="@template:element"> 189 <xsl:call-template name="enter-element"/> 190 </xsl:when> 191 <xsl:otherwise> 192 <xsl:call-template name="enter-attribute"/> 193 </xsl:otherwise> 194 </xsl:choose> 195 </axsl:if> 196 </xsl:otherwise> 197 </xsl:choose> 198 </xsl:template> 199 200 201 202 <!-- Match special expression attributes. --> 203 204 <xsl:template match="*[@template:attribute or @template:value or @template:expr or @template:copy]"> 205 <xsl:call-template name="enter-attribute"/> 206 </xsl:template> 207 208 <xsl:template name="enter-attribute"> 209 <xsl:choose> 210 <xsl:when test="@template:attribute"> 211 <axsl:choose> 212 <axsl:when test="@{@template:attribute}"> 213 <axsl:variable name="this-name"><xsl:value-of select="@template:attribute"/></axsl:variable> 214 <axsl:variable name="this-value" select="@{@template:attribute}"/> 215 <xsl:call-template name="special-attributes"/> 216 </axsl:when> 217 <axsl:otherwise> 218 <axsl:variable name="this-name"><xsl:value-of select="@template:attribute"/></axsl:variable> 219 <axsl:variable name="this-value"></axsl:variable> 220 <xsl:call-template name="special-attributes"/> 221 </axsl:otherwise> 222 </axsl:choose> 223 </xsl:when> 224 <xsl:otherwise> 225 <xsl:call-template name="special-attributes"/> 226 </xsl:otherwise> 227 </xsl:choose> 228 </xsl:template> 229 230 <xsl:template name="special-attributes"> 231 <xsl:choose> 232 <xsl:when test="@template:effect = 'replace'"> 233 <xsl:call-template name="special-value"/> 234 </xsl:when> 235 <xsl:otherwise> 236 <xsl:copy> 237 <xsl:apply-templates select="@*"/> 238 <xsl:call-template name="expression-attributes"/> 239 </xsl:copy> 240 </xsl:otherwise> 241 </xsl:choose> 242 </xsl:template> 243 244 <xsl:template name="expression-attributes"> 245 <xsl:if test="@template:expr and @template:expr-attr"> 246 <axsl:if test="{@template:expr}"> 247 <axsl:attribute name="{@template:expr-attr}"><xsl:value-of select="@template:expr-attr"/></axsl:attribute> 248 </axsl:if> 249 </xsl:if> 250 <xsl:call-template name="special-value"/> 251 </xsl:template> 252 253 <xsl:template name="special-value"> 254 <xsl:choose> 255 <!-- Insert the translated value. --> 256 <xsl:when test="@template:i18n"> 257 <xsl:call-template name="translated-value"/> 258 </xsl:when> 259 <!-- Insert the stated value. --> 260 <xsl:when test="@template:value"> 261 <axsl:value-of select="{@template:value}"/> 262 </xsl:when> 263 <!-- Copy the stated expression. --> 264 <xsl:when test="@template:copy"> 265 <axsl:copy-of select="{@template:copy}"/> 266 </xsl:when> 267 <!-- Just process the descendants. --> 268 <xsl:otherwise> 269 <xsl:apply-templates select="node()"/> 270 </xsl:otherwise> 271 </xsl:choose> 272 </xsl:template> 273 274 275 276 <!-- Match internationalisation attributes. --> 277 278 <xsl:template match="*[not(@template:if) and not(@template:element) and not(@template:attribute) and not(@template:value) and not(@template:expr) and @template:i18n]"> 279 <xsl:copy> 280 <xsl:apply-templates select="@*"/> 281 <xsl:call-template name="translated-value"/> 282 </xsl:copy> 283 </xsl:template> 284 285 <xsl:template name="translated-value"> 286 <xsl:choose> 287 <!-- Look for a translation of the contents. --> 288 <xsl:when test="@template:i18n = '-'"> 289 <!-- NOTE: Quoting not done completely. --> 290 <axsl:variable name="translation" 291 select="$translations/translations/locale[code/@value=$locale]/translation[@value='{text()}']/text()"/> 292 <axsl:variable name="translation-default" 293 select="$translations/translations/locale[1]/translation[@value='{text()}']/text()"/> 294 <xsl:call-template name="insert-translated-value"/> 295 </xsl:when> 296 <!-- Look for a translation based on the expression. --> 297 <xsl:when test="starts-with(@template:i18n, '{') and substring(@template:i18n, string-length(@template:i18n), 1) = '}'"> 298 <!-- Select the expression. --> 299 <axsl:variable name="i18n-expr" select="{substring(@template:i18n, 2, string-length(@template:i18n) - 2)}"/> 300 <!-- Translate according to the expression. --> 301 <axsl:variable name="translation" 302 select="$translations/translations/locale[code/@value=$locale]/translation[@value=$i18n-expr]/text()"/> 303 <axsl:variable name="translation-default" 304 select="$translations/translations/locale[1]/translation[@value=$i18n-expr]/text()"/> 305 <xsl:call-template name="insert-translated-value"> 306 <xsl:with-param name="default">$i18n-expr</xsl:with-param> 307 </xsl:call-template> 308 </xsl:when> 309 <!-- Look for a named translation. --> 310 <xsl:otherwise> 311 <!-- NOTE: Quoting not done completely. --> 312 <axsl:variable name="translation" 313 select="$translations/translations/locale[code/@value=$locale]/translation[@value='{@template:i18n}']/text()"/> 314 <axsl:variable name="translation-default" 315 select="$translations/translations/locale[1]/translation[@value='{@template:i18n}']/text()"/> 316 <xsl:call-template name="insert-translated-value"/> 317 </xsl:otherwise> 318 </xsl:choose> 319 </xsl:template> 320 321 <xsl:template name="insert-translated-value"> 322 <xsl:param name="default"/> 323 <axsl:choose> 324 <!-- Insert the translated value. --> 325 <axsl:when test="$translation"> 326 <axsl:value-of select="$translation"/> 327 </axsl:when> 328 <!-- Or insert the default translated value. --> 329 <axsl:when test="$translation-default"> 330 <axsl:value-of select="$translation-default"/> 331 </axsl:when> 332 <!-- Otherwise, just process the descendants. --> 333 <axsl:otherwise> 334 <xsl:choose> 335 <xsl:when test="$default"> 336 <axsl:value-of select="{$default}"/> 337 </xsl:when> 338 <xsl:otherwise> 339 <xsl:apply-templates select="node()"/> 340 </xsl:otherwise> 341 </xsl:choose> 342 </axsl:otherwise> 343 </axsl:choose> 344 </xsl:template> 345 346 347 348 <!-- Remove template attributes. --> 349 350 <xsl:template match="@template:element|@template:init|@template:attribute|@template:value|@template:expr|@template:expr-attr|@template:effect|@template:if|@template:i18n|@template:copy|@template:select|@template:output|@template:sort"> 351 </xsl:template> 352 353 354 355 <!-- Remove special attributes for preserving namespace prefixes used in expressions. --> 356 357 <xsl:template match="@*[local-name() = 'expr-prefix']"> 358 </xsl:template> 359 360 361 362 <!-- Replicate unknown elements. --> 363 364 <xsl:template match="@*|node()"> 365 <xsl:copy> 366 <xsl:apply-templates select="@*|node()"/> 367 </xsl:copy> 368 </xsl:template> 369 370 </xsl:stylesheet>