1 <?xml version="1.0"?> 2 <!-- 3 A stylesheet which produces initialisation/input stylesheets for a particular 4 kind of document. 5 6 Copyright (C) 2005 Paul Boddie <paul@boddie.org.uk> 7 8 This program is free software; you can redistribute it and/or modify it under 9 the terms of the GNU Lesser General Public License as published by the Free 10 Software Foundation; either version 3 of the License, or (at your option) any 11 later version. 12 13 This program is distributed in the hope that it will be useful, but WITHOUT 14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 16 details. 17 18 You should have received a copy of the GNU Lesser General Public License along 19 with this program. If not, see <http://www.gnu.org/licenses/>. 20 --> 21 <xsl:stylesheet version="1.0" 22 xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 23 xmlns:axsl="http://www.w3.org/1999/XSL/TransformAlias" 24 xmlns:template="http://www.boddie.org.uk/ns/xmltools/template"> 25 26 <xsl:output indent="yes"/> 27 <xsl:namespace-alias stylesheet-prefix="axsl" result-prefix="xsl"/> 28 29 30 31 <xsl:param name="init-enumerations">yes</xsl:param> 32 33 34 35 <!-- Match the document itself. --> 36 37 <xsl:template match="/"> 38 <axsl:stylesheet version="1.0" xmlns:dyn="http://exslt.org/dynamic" 39 extension-element-prefixes="dyn"> 40 41 <axsl:output indent="yes"/> 42 43 <!-- Make document parameters for all elements appearing to use enumerations. --> 44 <xsl:if test="$init-enumerations = 'yes'"> 45 <xsl:for-each select="//element[@type='multiple-choice' or @type='multiple-choice-list']"> 46 <axsl:param name="{@name}"/> 47 </xsl:for-each> 48 </xsl:if> 49 50 <!-- Make a document-level rule. --> 51 <axsl:template match="/"> 52 <axsl:apply-templates select="*" mode="{generate-id(*[1])}"/> 53 </axsl:template> 54 55 <!-- Process the top-level element to make other rules. --> 56 <xsl:apply-templates select="*"/> 57 58 <!-- Replicate unknown elements. --> 59 <axsl:template match="@*|placeholder|node()"> 60 <axsl:copy> 61 <axsl:apply-templates select="@*|node()"/> 62 </axsl:copy> 63 </axsl:template> 64 65 <!-- Replicate placeholders for specific elements. --> 66 <xsl:for-each select="//element"> 67 <axsl:template match="placeholder" mode="{generate-id(.)}"> 68 <axsl:copy> 69 <axsl:apply-templates select="@*|node()"/> 70 </axsl:copy> 71 </axsl:template> 72 </xsl:for-each> 73 74 </axsl:stylesheet> 75 </xsl:template> 76 77 78 79 <!-- Match element references. --> 80 81 <xsl:template match="element"> 82 83 <!-- Make a rule for the element. --> 84 <axsl:template match="{@name}" mode="{generate-id(.)}"> 85 86 <!-- Copy the element. --> 87 <xsl:element name="{@name}"> 88 89 <!-- Process attributes. --> 90 <axsl:apply-templates select="@*"/> 91 92 <!-- Find elements and determine how to process them. --> 93 <xsl:call-template name="process-elements"/> 94 </xsl:element> 95 </axsl:template> 96 97 <!-- Make rules for nested elements. --> 98 <xsl:call-template name="process-rules"/> 99 100 </xsl:template> 101 102 103 104 <!-- Process elements. --> 105 106 <xsl:template name="process-elements"> 107 <xsl:param name="path">.</xsl:param> 108 109 <!-- To ensure "stable ordering" of elements, the initialised/static elements are 110 added first; the collection/dynamic elements are added afterwards. This may not 111 necessarily match the schema, however. --> 112 113 <xsl:for-each select="element|element-ref"> 114 <!-- Define elements which do not have selectors. --> 115 <xsl:variable name="adding-selectors" select="count(//selector[@element=current()/@name])"/> 116 117 <xsl:choose> 118 <!-- Recursive element references. --> 119 <xsl:when test="local-name(.) = 'element-ref'"> 120 <axsl:apply-templates select="{@name}" mode="{generate-id(ancestor::element[@name=current()/@name])}"/> 121 </xsl:when> 122 <!-- Enumerations. --> 123 <xsl:when test="@type='multiple-choice-value' or @type='multiple-choice-list-value'"> 124 <!-- Only generate enumerations if requested. --> 125 <xsl:if test="$init-enumerations = 'yes'"> 126 <xsl:call-template name="inside-enumeration"> 127 <xsl:with-param name="path" select="concat($path, '/', @name)"/> 128 </xsl:call-template> 129 </xsl:if> 130 </xsl:when> 131 <!-- Added elements. --> 132 <xsl:when test="(not(@init) or @init = 'auto') and $adding-selectors = 0 or @init = 'yes'"> 133 <xsl:element name="{@name}"> 134 <axsl:apply-templates select="{$path}/{@name}/@*"/> 135 <xsl:call-template name="process-elements"> 136 <xsl:with-param name="path" select="concat($path, '/', @name)"/> 137 </xsl:call-template> 138 </xsl:element> 139 </xsl:when> 140 <!-- Other elements are only added if found and must appear last - see below. --> 141 <xsl:otherwise/> 142 </xsl:choose> 143 </xsl:for-each> 144 145 <!-- Add the collection/dynamic elements at the end. This includes placeholder 146 elements which may have represented the static elements. 147 NOTE: We may wish to exclude placeholder elements in any situation where static 148 NOTE: elements are employed, since the only place where keeping them around is 149 NOTE: necessary/meaningful is in dynamic element collections. --> 150 151 <xsl:call-template name="produce-selection"> 152 <xsl:with-param name="path" select="$path"/> 153 <xsl:with-param name="element" select="element[1]"/> 154 </xsl:call-template> 155 156 </xsl:template> 157 158 159 160 <!-- Produce a selection of collection/dynamic elements. 161 This should produce apply-templates instructions selecting dynamic elements. --> 162 163 <xsl:template name="produce-selection"> 164 <xsl:param name="path"/> 165 <xsl:param name="element"/> 166 167 <xsl:choose> 168 <!-- While elements remain... --> 169 <xsl:when test="$element"> 170 171 <!-- Define elements which do not have selectors. --> 172 <xsl:variable name="adding-selectors" select="count(//selector[@element=$element/@name])"/> 173 174 <xsl:choose> 175 <!-- Do not select added elements or enumerations - see process-elements. --> 176 <xsl:when test="((not($element/@init) or $element/@init = 'auto') and $adding-selectors = 0 or $element/@init = 'yes') 177 or (@type='multiple-choice-value' or @type='multiple-choice-list-value')"> 178 <xsl:call-template name="produce-selection"> 179 <xsl:with-param name="path" select="$path"/> 180 <xsl:with-param name="element" select="$element/following-sibling::element[1]"/> 181 </xsl:call-template> 182 </xsl:when> 183 <!-- Other elements are only added if found. --> 184 <xsl:otherwise> 185 <axsl:apply-templates select="{$path}/placeholder|{$path}/{$element/@name}" mode="{generate-id($element)}"/> 186 <xsl:call-template name="produce-selection"> 187 <xsl:with-param name="path" select="$path"/> 188 <xsl:with-param name="element" select="$element/following-sibling::element[1]"/> 189 </xsl:call-template> 190 </xsl:otherwise> 191 </xsl:choose> 192 </xsl:when> 193 <!-- Do nothing when no elements remain. --> 194 <xsl:otherwise/> 195 </xsl:choose> 196 </xsl:template> 197 198 199 200 <!-- Process rules. --> 201 202 <xsl:template name="process-rules"> 203 <xsl:param name="path">.</xsl:param> 204 205 <xsl:for-each select="element"> 206 <!-- Define elements which do not have selectors. --> 207 <!-- NOTE: Duplicating adding-selectors - see above. --> 208 <xsl:variable name="adding-selectors" select="count(//selector[@element=current()/@name])"/> 209 210 <xsl:choose> 211 <xsl:when test="@type='multiple-choice-value' or @type='multiple-choice-list-value'"> 212 <!-- Do not match multiple-choice values. --> 213 </xsl:when> 214 <xsl:when test="(not(@init) or @init = 'auto') and $adding-selectors = 0 or @init = 'yes'"> 215 <xsl:call-template name="process-rules"> 216 <xsl:with-param name="path" select="concat($path, '/', @name)"/> 217 </xsl:call-template> 218 </xsl:when> 219 <xsl:otherwise> 220 <xsl:apply-templates select="."/> 221 </xsl:otherwise> 222 </xsl:choose> 223 </xsl:for-each> 224 </xsl:template> 225 226 227 228 <!-- Fill in enumerations. --> 229 230 <xsl:template name="inside-enumeration"> 231 <xsl:param name="path"/> 232 233 <!-- Store multiple-choice selections, if appropriate. --> 234 <xsl:if test="../@type='multiple-choice-list'"> 235 <!-- NOTE: It is assumed here that ../attribute/@name (if it exists) == attribute/@name. --> 236 <axsl:variable name="values-{@name}" select="{$path}/@{attribute/@name}"/> 237 </xsl:if> 238 239 <!-- Select inside the enumeration source, inside an element with the field's name, the enumeration elements. --> 240 <xsl:choose> 241 <xsl:when test="../@source='dynamic'"> 242 <axsl:for-each select="dyn:evaluate(${../@name})/{../@name}/{@name}"> 243 <xsl:call-template name="inside-enumeration-element"/> 244 </axsl:for-each> 245 </xsl:when> 246 <xsl:otherwise> 247 <axsl:for-each select="${../@name}/{../@name}/{@name}"> 248 <xsl:call-template name="inside-enumeration-element"/> 249 </axsl:for-each> 250 </xsl:otherwise> 251 </xsl:choose> 252 </xsl:template> 253 254 <xsl:template name="inside-enumeration-element"> 255 <axsl:copy> 256 <axsl:apply-templates select="@*"/> 257 <xsl:if test="@type='multiple-choice-list-value'"> 258 <axsl:if test="$values-{@name}[string() = current()/@{attribute/@name}]"> 259 <axsl:attribute name="{@expr-name}">true</axsl:attribute> 260 </axsl:if> 261 </xsl:if> 262 <!-- Include child nodes, if provided. --> 263 <axsl:copy-of select="node()"/> 264 </axsl:copy> 265 </xsl:template> 266 267 </xsl:stylesheet>