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