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 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 <xsl:call-template name="insert-translated-value"/> 271 </xsl:when> 272 <!-- Look for a translation based on the expression. --> 273 <xsl:when test="starts-with(@template:i18n, '{') and substring(@template:i18n, string-length(@template:i18n), 1) = '}'"> 274 <!-- Select the expression. --> 275 <axsl:variable name="i18n-expr" select="{substring(@template:i18n, 2, string-length(@template:i18n) - 2)}"/> 276 <!-- Translate according to the expression. --> 277 <axsl:variable name="translation" 278 select="$translations/translations/locale[code/@value=$locale]/translation[@value=$i18n-expr]/text()"/> 279 <xsl:call-template name="insert-translated-value"> 280 <xsl:with-param name="default">$i18n-expr</xsl:with-param> 281 </xsl:call-template> 282 </xsl:when> 283 <!-- Look for a named translation. --> 284 <xsl:otherwise> 285 <!-- NOTE: Quoting not done completely. --> 286 <axsl:variable name="translation" 287 select="$translations/translations/locale[code/@value=$locale]/translation[@value='{@template:i18n}']/text()"/> 288 <xsl:call-template name="insert-translated-value"/> 289 </xsl:otherwise> 290 </xsl:choose> 291 </xsl:template> 292 293 <xsl:template name="insert-translated-value"> 294 <xsl:param name="default"/> 295 <axsl:choose> 296 <!-- Insert the translated value. --> 297 <axsl:when test="$translation"> 298 <axsl:value-of select="$translation"/> 299 </axsl:when> 300 <!-- Otherwise, just process the descendants. --> 301 <axsl:otherwise> 302 <xsl:choose> 303 <xsl:when test="$default"> 304 <axsl:value-of select="{$default}"/> 305 </xsl:when> 306 <xsl:otherwise> 307 <xsl:apply-templates select="node()"/> 308 </xsl:otherwise> 309 </xsl:choose> 310 </axsl:otherwise> 311 </axsl:choose> 312 </xsl:template> 313 314 315 316 <!-- Remove template attributes. --> 317 318 <xsl:template match="@template:element|@template:init|@template:attribute|@template:value|@template:expr|@template:expr-attr|@template:effect|@template:if|@template:i18n|@template:copy"> 319 </xsl:template> 320 321 322 323 <!-- Remove special attributes for preserving namespace prefixes used in expressions. --> 324 325 <xsl:template match="@*[local-name() = 'expr-prefix']"> 326 </xsl:template> 327 328 329 330 <!-- Replicate unknown elements. --> 331 332 <xsl:template match="@*|node()"> 333 <xsl:copy> 334 <xsl:apply-templates select="@*|node()"/> 335 </xsl:copy> 336 </xsl:template> 337 338 </xsl:stylesheet>