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 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:when> 110 <!-- Generate a reference to this element definition. --> 111 <xsl:otherwise> 112 <axsl:apply-templates select="{$first-name}" mode="{generate-id(.)}"/> 113 </xsl:otherwise> 114 </xsl:choose> 115 </xsl:otherwise> 116 </xsl:choose> 117 </xsl:template> 118 119 <xsl:template name="element-template"> 120 <xsl:param name="element-names"/> 121 <xsl:variable name="this-name" select="substring-before($element-names, ',')"/> 122 <xsl:variable name="next-names" select="substring-after($element-names, ',')"/> 123 <xsl:variable name="next-name" select="str:split($next-names, ',')[1]"/> 124 <xsl:choose> 125 <!-- Non-last part of a list of element names. --> 126 <!-- Produce a template referencing another template. --> 127 <xsl:when test="$this-name != ''"> 128 <!-- Produce a template with a mode. --> 129 <axsl:template match="{$this-name}" mode="{generate-id(.)}"> 130 <axsl:apply-templates select="{$next-name}" mode="{generate-id(.)}"/> 131 </axsl:template> 132 <!-- Produce the other elements' templates... --> 133 <xsl:call-template name="element-template"> 134 <xsl:with-param name="element-names" select="$next-names"/> 135 </xsl:call-template> 136 </xsl:when> 137 <!-- Last part of a list of element names. --> 138 <!-- Produce a template with content. --> 139 <xsl:otherwise> 140 <!-- Produce a template with a mode. --> 141 <axsl:template match="{$element-names}" mode="{generate-id(.)}"> 142 <xsl:call-template name="enter-attribute"/> 143 </axsl:template> 144 </xsl:otherwise> 145 </xsl:choose> 146 </xsl:template> 147 148 149 150 <!-- Match elements referencing selected sections. --> 151 152 <xsl:template match="*[@template:select]" priority="1"> 153 <!-- Produce a loop using the annotation's content. --> 154 <axsl:for-each select="{@template:select}"> 155 <xsl:call-template name="enter-attribute"/> 156 </axsl:for-each> 157 </xsl:template> 158 159 160 161 <!-- Match special conditional expression attributes. --> 162 163 <xsl:template match="*[@template:if]" priority="2"> 164 <xsl:param name="top-level">false</xsl:param> 165 <xsl:choose> 166 <!-- Since this rule may be invoked at the top level, ignore conditions. --> 167 <xsl:when test="$top-level = 'true'"> 168 <xsl:call-template name="enter-element"> 169 <xsl:with-param name="top-level" select="$top-level"/> 170 </xsl:call-template> 171 </xsl:when> 172 <!-- As part of a template, generate the condition. --> 173 <xsl:otherwise> 174 <axsl:if test="{@template:if}"> 175 <xsl:choose> 176 <xsl:when test="@template:element"> 177 <xsl:call-template name="enter-element"/> 178 </xsl:when> 179 <xsl:otherwise> 180 <xsl:call-template name="enter-attribute"/> 181 </xsl:otherwise> 182 </xsl:choose> 183 </axsl:if> 184 </xsl:otherwise> 185 </xsl:choose> 186 </xsl:template> 187 188 189 190 <!-- Match special expression attributes. --> 191 192 <xsl:template match="*[@template:attribute or @template:value or @template:expr or @template:copy]"> 193 <xsl:call-template name="enter-attribute"/> 194 </xsl:template> 195 196 <xsl:template name="enter-attribute"> 197 <xsl:choose> 198 <xsl:when test="@template:attribute"> 199 <axsl:choose> 200 <axsl:when test="@{@template:attribute}"> 201 <axsl:variable name="this-name"><xsl:value-of select="@template:attribute"/></axsl:variable> 202 <axsl:variable name="this-value" select="@{@template:attribute}"/> 203 <xsl:call-template name="special-attributes"/> 204 </axsl:when> 205 <axsl:otherwise> 206 <axsl:variable name="this-name"><xsl:value-of select="@template:attribute"/></axsl:variable> 207 <axsl:variable name="this-value"></axsl:variable> 208 <xsl:call-template name="special-attributes"/> 209 </axsl:otherwise> 210 </axsl:choose> 211 </xsl:when> 212 <xsl:otherwise> 213 <xsl:call-template name="special-attributes"/> 214 </xsl:otherwise> 215 </xsl:choose> 216 </xsl:template> 217 218 <xsl:template name="special-attributes"> 219 <xsl:choose> 220 <xsl:when test="@template:effect = 'replace'"> 221 <xsl:call-template name="special-value"/> 222 </xsl:when> 223 <xsl:otherwise> 224 <xsl:copy> 225 <xsl:apply-templates select="@*"/> 226 <xsl:call-template name="expression-attributes"/> 227 </xsl:copy> 228 </xsl:otherwise> 229 </xsl:choose> 230 </xsl:template> 231 232 <xsl:template name="expression-attributes"> 233 <xsl:if test="@template:expr and @template:expr-attr"> 234 <axsl:if test="{@template:expr}"> 235 <axsl:attribute name="{@template:expr-attr}"><xsl:value-of select="@template:expr-attr"/></axsl:attribute> 236 </axsl:if> 237 </xsl:if> 238 <xsl:call-template name="special-value"/> 239 </xsl:template> 240 241 <xsl:template name="special-value"> 242 <xsl:choose> 243 <!-- Insert the translated value. --> 244 <xsl:when test="@template:i18n"> 245 <xsl:call-template name="translated-value"/> 246 </xsl:when> 247 <!-- Insert the stated value. --> 248 <xsl:when test="@template:value"> 249 <axsl:value-of select="{@template:value}"/> 250 </xsl:when> 251 <!-- Copy the stated expression. --> 252 <xsl:when test="@template:copy"> 253 <axsl:copy-of select="{@template:copy}"/> 254 </xsl:when> 255 <!-- Just process the descendants. --> 256 <xsl:otherwise> 257 <xsl:apply-templates select="node()"/> 258 </xsl:otherwise> 259 </xsl:choose> 260 </xsl:template> 261 262 263 264 <!-- Match internationalisation attributes. --> 265 266 <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]"> 267 <xsl:copy> 268 <xsl:apply-templates select="@*"/> 269 <xsl:call-template name="translated-value"/> 270 </xsl:copy> 271 </xsl:template> 272 273 <xsl:template name="translated-value"> 274 <xsl:choose> 275 <!-- Look for a translation of the contents. --> 276 <xsl:when test="@template:i18n = '-'"> 277 <!-- NOTE: Quoting not done completely. --> 278 <axsl:variable name="translation" 279 select="$translations/translations/locale[code/@value=$locale]/translation[@value='{text()}']/text()"/> 280 <axsl:variable name="translation-default" 281 select="$translations/translations/locale[1]/translation[@value='{text()}']/text()"/> 282 <xsl:call-template name="insert-translated-value"/> 283 </xsl:when> 284 <!-- Look for a translation based on the expression. --> 285 <xsl:when test="starts-with(@template:i18n, '{') and substring(@template:i18n, string-length(@template:i18n), 1) = '}'"> 286 <!-- Select the expression. --> 287 <axsl:variable name="i18n-expr" select="{substring(@template:i18n, 2, string-length(@template:i18n) - 2)}"/> 288 <!-- Translate according to the expression. --> 289 <axsl:variable name="translation" 290 select="$translations/translations/locale[code/@value=$locale]/translation[@value=$i18n-expr]/text()"/> 291 <axsl:variable name="translation-default" 292 select="$translations/translations/locale[1]/translation[@value=$i18n-expr]/text()"/> 293 <xsl:call-template name="insert-translated-value"> 294 <xsl:with-param name="default">$i18n-expr</xsl:with-param> 295 </xsl:call-template> 296 </xsl:when> 297 <!-- Look for a named translation. --> 298 <xsl:otherwise> 299 <!-- NOTE: Quoting not done completely. --> 300 <axsl:variable name="translation" 301 select="$translations/translations/locale[code/@value=$locale]/translation[@value='{@template:i18n}']/text()"/> 302 <axsl:variable name="translation-default" 303 select="$translations/translations/locale[1]/translation[@value='{@template:i18n}']/text()"/> 304 <xsl:call-template name="insert-translated-value"/> 305 </xsl:otherwise> 306 </xsl:choose> 307 </xsl:template> 308 309 <xsl:template name="insert-translated-value"> 310 <xsl:param name="default"/> 311 <axsl:choose> 312 <!-- Insert the translated value. --> 313 <axsl:when test="$translation"> 314 <axsl:value-of select="$translation"/> 315 </axsl:when> 316 <!-- Or insert the default translated value. --> 317 <axsl:when test="$translation-default"> 318 <axsl:value-of select="$translation-default"/> 319 </axsl:when> 320 <!-- Otherwise, just process the descendants. --> 321 <axsl:otherwise> 322 <xsl:choose> 323 <xsl:when test="$default"> 324 <axsl:value-of select="{$default}"/> 325 </xsl:when> 326 <xsl:otherwise> 327 <xsl:apply-templates select="node()"/> 328 </xsl:otherwise> 329 </xsl:choose> 330 </axsl:otherwise> 331 </axsl:choose> 332 </xsl:template> 333 334 335 336 <!-- Remove template attributes. --> 337 338 <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"> 339 </xsl:template> 340 341 342 343 <!-- Remove special attributes for preserving namespace prefixes used in expressions. --> 344 345 <xsl:template match="@*[local-name() = 'expr-prefix']"> 346 </xsl:template> 347 348 349 350 <!-- Replicate unknown elements. --> 351 352 <xsl:template match="@*|node()"> 353 <xsl:copy> 354 <xsl:apply-templates select="@*|node()"/> 355 </xsl:copy> 356 </xsl:template> 357 358 </xsl:stylesheet>