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