|
1 | 1 | <?xml version="1.0" encoding="UTF-8"?> |
2 | | -<xsl:stylesheet version="2.0" |
| 2 | +<xsl:stylesheet version="3.0" |
3 | 3 | xmlns="http://csrc.nist.gov/ns/oscal/1.0" |
| 4 | + xmlns:mh="http://csrc.nist.gov/ns/message" |
4 | 5 | xmlns:o="http://csrc.nist.gov/ns/oscal/1.0" |
5 | 6 | xmlns:xsl="http://www.w3.org/1999/XSL/Transform" |
6 | 7 | xmlns:xs="http://www.w3.org/2001/XMLSchema" |
|
9 | 10 | exclude-result-prefixes="xs math o opr" |
10 | 11 | xpath-default-namespace="http://csrc.nist.gov/ns/oscal/1.0" > |
11 | 12 |
|
12 | | - <!-- XSLT 2.0 so as to validate against XSLT 3.0 constructs --> |
| 13 | + <xsl:import href="message-handler.xsl"/> |
13 | 14 |
|
| 15 | + <xsl:variable name="in_xspec" as="xs:boolean" select="false()"/> |
| 16 | + <xsl:variable name="true-content" as="xs:string+" select="('true','1')"/> |
14 | 17 |
|
15 | 18 | <xsl:template match="* | @*" mode="#all"> |
16 | 19 | <xsl:copy copy-namespaces="no"> |
|
25 | 28 | templates not this one. --> |
26 | 29 | </xsl:template> |
27 | 30 |
|
28 | | - <xsl:template match="catalog" priority="2"> |
| 31 | + <!-- If there is no selection and no merge (i.e., we don't reach one of |
| 32 | + the higher-priority templates that match catalog), pass the catalog |
| 33 | + through unchanged. --> |
| 34 | + <xsl:template match="catalog" priority="2" as="element(catalog)"> |
29 | 35 | <xsl:copy-of select="."/> |
30 | 36 | </xsl:template> |
31 | 37 |
|
32 | | - <xsl:template match="catalog[exists(selection)]" priority="10"> |
| 38 | + <!-- If there is a selection but neither merge/as-is nor merge/custom, |
| 39 | + use flat structuring. --> |
| 40 | + <xsl:template match="catalog[exists(selection)]" priority="10" as="element(catalog)"> |
33 | 41 | <catalog> |
34 | 42 | <xsl:apply-templates select="@*"/> |
35 | 43 | <xsl:apply-templates select="metadata"/> |
36 | 44 | <xsl:apply-templates select="selection"/> |
37 | 45 | <xsl:apply-templates select="modify"/> |
38 | | - <xsl:for-each-group select="back-matter/* | selection/back-matter/*" group-by="true()"> |
| 46 | + <xsl:where-populated> |
39 | 47 | <back-matter> |
40 | | - <xsl:copy-of select="current-group()"/> |
| 48 | + <xsl:sequence select="back-matter/* | selection/back-matter/*"/> |
41 | 49 | </back-matter> |
42 | | - </xsl:for-each-group> |
| 50 | + </xsl:where-populated> |
43 | 51 | </catalog> |
44 | 52 | </xsl:template> |
45 | 53 |
|
46 | | - <xsl:template priority="12" match="catalog[merge/as-is=('true','1')]"> |
| 54 | + <!-- If there is a merge/as-is directive, we go down that branch. |
| 55 | + If there is also a merge/custom directive, we apply the |
| 56 | + higher-priority template instead of this one. --> |
| 57 | + <xsl:template match="catalog[merge/as-is=$true-content]" priority="12" as="element(catalog)"> |
| 58 | + <xsl:call-template name="detect-multiple-structuring-directives"/> |
47 | 59 | <catalog> |
48 | 60 | <xsl:apply-templates select="@*"/> |
49 | 61 | <xsl:apply-templates select="metadata"/> |
50 | 62 | <xsl:variable name="merged-selections"> |
51 | | - <xsl:call-template name="o:merge-groups-as-is"> |
52 | | - <xsl:with-param name="merging" select="selection"/> |
53 | | - </xsl:call-template> |
| 63 | + <xsl:call-template name="o:merge-groups-as-is"> |
| 64 | + <xsl:with-param name="merging" select="selection"/> |
| 65 | + </xsl:call-template> |
54 | 66 | </xsl:variable> |
55 | | - <!-- not copying the selection elements only their contents --> |
| 67 | + <!-- not copying the selection elements, only their contents --> |
56 | 68 | <xsl:for-each select="$merged-selections/selection"> |
57 | 69 | <xsl:sequence select="* except back-matter"/> |
58 | 70 | </xsl:for-each> |
|
64 | 76 |
|
65 | 77 |
|
66 | 78 | <!-- If there is a merge/custom directive, we go down that branch. --> |
67 | | - <xsl:template priority="13" match="catalog[exists(merge/custom)]"> |
| 79 | + <xsl:template match="catalog[exists(merge/custom)]" priority="13" as="element(catalog)"> |
| 80 | + <xsl:call-template name="detect-multiple-structuring-directives"/> |
68 | 81 | <catalog> |
69 | 82 | <xsl:apply-templates select="@*"/> |
70 | 83 | <xsl:apply-templates select="metadata"/> |
|
77 | 90 | </catalog> |
78 | 91 | </xsl:template> |
79 | 92 |
|
80 | | - <xsl:template name="combine-back-matter"> |
81 | | - <xsl:variable name="here" select="self::catalog"/> |
82 | | - <xsl:for-each-group select="back-matter/* | selection/back-matter/*" group-by="true()"> |
| 93 | + <xsl:template name="detect-multiple-structuring-directives" as="empty-sequence()"> |
| 94 | + <xsl:context-item as="element(catalog)" use="required"/> |
| 95 | + <xsl:variable name="flat" as="element(flat)*" select="merge/flat[.=$true-content]"/> |
| 96 | + <xsl:variable name="as-is" as="element(as-is)*" select="merge/as-is[.=$true-content]"/> |
| 97 | + <xsl:variable name="custom" as="element(custom)*" select="merge/custom"/> |
| 98 | + <xsl:if test="count($flat) + count($as-is) + count($custom) gt 1"> |
| 99 | + <xsl:call-template name="mh:message-handler"> |
| 100 | + <xsl:with-param name="text">Found multiple structuring directives. Choose at most one from: flat (default), as-is, custom.</xsl:with-param> |
| 101 | + <xsl:with-param name="message-type">Error</xsl:with-param> |
| 102 | + <xsl:with-param name="terminate" select="true()"/> |
| 103 | + </xsl:call-template> |
| 104 | + </xsl:if> |
| 105 | + </xsl:template> |
| 106 | + |
| 107 | + <xsl:template name="combine-back-matter" as="element(back-matter)?"> |
| 108 | + <xsl:context-item as="element(catalog)" use="required"/> |
| 109 | + <xsl:where-populated> |
83 | 110 | <back-matter> |
84 | 111 | <!-- Using combination logic on back matter elements. --> |
85 | | - <xsl:for-each-group select="current-group()" group-by="(@opr:id,@id,generate-id())[1]"> |
| 112 | + <xsl:for-each-group select="back-matter/* | selection/back-matter/*" group-by="(@opr:id,@uuid,generate-id())[1]"> |
86 | 113 | <xsl:call-template name="combine-elements"> |
87 | | - <xsl:with-param name="who" select="current-group()"/> |
| 114 | + <!-- Take last one in group because of spec |
| 115 | + requirement id="req-backmatter-dupe". --> |
| 116 | + <xsl:with-param name="who" select="current-group()[last()]" as="element(resource)"/> |
88 | 117 | </xsl:call-template> |
89 | 118 | </xsl:for-each-group> |
90 | 119 | </back-matter> |
91 | | - </xsl:for-each-group> |
| 120 | + </xsl:where-populated> |
92 | 121 | </xsl:template> |
93 | 122 |
|
94 | 123 | <xsl:template match="selection"> |
95 | 124 | <xsl:apply-templates select="param | .//group/param"/> |
96 | 125 | <xsl:apply-templates select="control | .//group/control"/> |
97 | 126 | </xsl:template> |
98 | 127 |
|
99 | | - <xsl:key name="control-by-id" match="control" use="@id"/> |
100 | | - |
101 | | - <xsl:template match="node() | @*" mode="o:custom-merge"> |
102 | | - <xsl:copy> |
103 | | - <xsl:apply-templates mode="#current" select="node() | @*"/> |
104 | | - </xsl:copy> |
105 | | - </xsl:template> |
106 | | - |
| 128 | + <!-- Process children but do not copy <custom> tags themselves. --> |
107 | 129 | <xsl:template match="custom" mode="o:custom-merge"> |
108 | 130 | <xsl:apply-templates mode="#current"/> |
109 | 131 | </xsl:template> |
110 | 132 |
|
| 133 | + <!-- Apply the requested ordering. |
| 134 | + Creating the properly combined sequence of inserted controls |
| 135 | + is in downstream template. --> |
111 | 136 | <xsl:template match="insert-controls" mode="o:custom-merge"> |
112 | | - <xsl:variable name="inserted-controls"> |
| 137 | + <xsl:variable name="inserted-controls" as="element()*"> |
113 | 138 | <xsl:apply-templates mode="#current"/> |
114 | 139 | </xsl:variable> |
115 | 140 | <xsl:variable name="keep-order" select="not(@order = ('descending','ascending'))" as="xs:boolean"/> |
116 | 141 | <xsl:variable name="sort-order" select="@order[.='descending'],'ascending'"/> |
117 | 142 | <!-- Setting sort-key to '1' sorts into given order --> |
118 | | - <xsl:perform-sort select="$inserted-controls"> |
119 | | - <xsl:sort select="if ($keep-order) then '1' else @control-id"/> |
| 143 | + <xsl:perform-sort select="$inserted-controls"> |
| 144 | + <xsl:sort select="if ($keep-order) then '1' else @id" |
| 145 | + order="{$sort-order[1]}"/> |
120 | 146 | </xsl:perform-sort> |
121 | 147 | </xsl:template> |
122 | 148 |
|
123 | | - <xsl:template match="include-controls" mode="o:custom-merge"> |
124 | | - <xsl:variable name="match-patterns" select="matching/@pattern"/> |
125 | | - <xsl:call-template name="combine-elements"> |
126 | | - <xsl:with-param name="who" select="key('control-by-id', with-id), |
127 | | - /*/selection//control[some $p in ($match-patterns) satisfies (matches(@id,o:glob-as-regex(string($p))))]"/> |
128 | | - </xsl:call-template> |
129 | | - </xsl:template> |
| 149 | + <xsl:include href="select-or-custom-merge.xsl"/> |
130 | 150 |
|
131 | | - <xsl:template match="include-all" mode="o:custom-merge"> |
132 | | - <xsl:variable name="match-patterns" select="matching/@pattern"/> |
| 151 | + <!-- Combine selected controls. |
| 152 | + Creating the sequence of controls ($who) is in code shared with |
| 153 | + selection phase. --> |
| 154 | + <xsl:template match="include-all | include-controls" mode="o:custom-merge"> |
| 155 | + <xsl:variable name="who" as="element(o:control)*"> |
| 156 | + <xsl:apply-templates select="ancestor::*[last()]//selection" mode="o:select"> |
| 157 | + <xsl:with-param tunnel="yes" name="import-instruction" select="ancestor::insert-controls"/> |
| 158 | + </xsl:apply-templates> |
| 159 | + </xsl:variable> |
133 | 160 | <xsl:call-template name="combine-elements"> |
134 | | - <xsl:with-param name="who" select="//control"/> |
| 161 | + <xsl:with-param name="who" select="$who"/> |
135 | 162 | </xsl:call-template> |
136 | 163 | </xsl:template> |
137 | 164 |
|
138 | | - <xsl:include href="oscal-profile-resolve-functions.xsl"/> |
| 165 | + <!-- No-op because exclusion logic is handled in the code shared with |
| 166 | + selection phase. --> |
| 167 | + <xsl:template match="exclude-controls" mode="o:custom-merge"/> |
139 | 168 |
|
140 | | - <!--<xsl:template match="with-id"> |
141 | | - <xsl:call-template name="combine-elements"> |
142 | | - <xsl:with-param name="who" select="key('control-by-id', @control-id)"/> |
143 | | - </xsl:call-template> |
| 169 | + <!-- In o:select mode, process children of selection or group to reach |
| 170 | + controls, which are handled in select-or-custom-merge.xsl. --> |
| 171 | + <xsl:template match="selection | selection//group" mode="o:select"> |
| 172 | + <xsl:apply-templates select="group | control" mode="#current"/> |
144 | 173 | </xsl:template> |
145 | 174 |
|
146 | | - <xsl:template match="matching" mode="o:custom-merge"> |
147 | | - <xsl:variable name="p" select="@pattern"/> |
148 | | - <xsl:call-template name="combine-elements"> |
149 | | - <xsl:with-param name="who" select="/*/selection//control[matches(@id,$p)]"/> |
150 | | - </xsl:call-template> |
151 | | - </xsl:template>--> |
152 | | - |
153 | 175 | <xsl:template name="o:merge-groups-as-is"> |
154 | | - <xsl:param name="merging" select="()"/> |
| 176 | + <xsl:context-item as="element()" use="optional"/> |
| 177 | + <xsl:param name="merging" select="()" as="element()*"/> |
155 | 178 | <xsl:for-each-group select="$merging" group-by="(@uuid,@opr:id,@id,generate-id())[1]"> |
156 | | - <xsl:variable name="merged" select="current-group()"/> |
| 179 | + <xsl:variable name="merged" select="current-group()" as="element()+"/> |
157 | 180 | <xsl:for-each select="$merged[1]"> |
158 | 181 | <xsl:copy copy-namespaces="no"> |
159 | 182 | <xsl:apply-templates select="$merged/@*"/> |
|
177 | 200 | </xsl:template> |
178 | 201 |
|
179 | 202 | <xsl:template name="combine-elements"> |
| 203 | + <xsl:context-item as="element()" use="required"/> |
180 | 204 | <xsl:param name="who" as="element()*"/> |
181 | 205 | <xsl:apply-templates select="ancestor-or-self::catalog" mode="o:combine-elements"> |
182 | 206 | <xsl:with-param name="elements" select="$who"/> |
|
194 | 218 | <!-- All the elements coming in have the same name but different opr:id |
195 | 219 | we operate on controls and parameters (not groups or group contents otherwise) --> |
196 | 220 | <!-- further, we assume all controls or all parameters --> |
197 | | - <xsl:if test="$elements/name() != $elements/name()"> |
198 | | - <opr:error> ... elements of different types combining ...</opr:error></xsl:if> |
| 221 | + <xsl:variable name="uri-qualified-name" as="function(*)" |
| 222 | + select="function($node as element()) as xs:string { |
| 223 | + 'Q{' || namespace-uri($node) || '}' || local-name($node) |
| 224 | + }"/> |
| 225 | + <xsl:variable name="element-names" as="xs:string*" |
| 226 | + select="$elements ! $uri-qualified-name(.)"/> |
| 227 | + <xsl:if test="$element-names != $element-names"> |
| 228 | + <!-- If we get here for a valid profile, it is an XSLT bug rather than a user error. --> |
| 229 | + <xsl:call-template name="mh:message-handler"> |
| 230 | + <xsl:with-param name="text">Combining elements of different types is not supported.</xsl:with-param> |
| 231 | + <xsl:with-param name="message-type">Error</xsl:with-param> |
| 232 | + <xsl:with-param name="terminate" select="$in_xspec"/> |
| 233 | + </xsl:call-template> |
| 234 | + </xsl:if> |
199 | 235 | <xsl:for-each-group select="$elements" group-by="(@opr:id,@id,generate-id())[1]"> |
200 | | - <xsl:variable name="merged" select="current-group()"/> |
| 236 | + <xsl:variable name="merged" select="current-group()" as="element()+"/> |
201 | 237 | <xsl:for-each select="$merged[1]"> |
202 | 238 | <xsl:copy copy-namespaces="no"> |
203 | 239 | <xsl:apply-templates select="$merged/@*"/> |
204 | 240 | <xsl:apply-templates select="$merged/*"/> |
205 | | - |
206 | 241 | </xsl:copy> |
207 | 242 | </xsl:for-each> |
208 | 243 | </xsl:for-each-group> |
|
219 | 254 | <xsl:apply-templates select="$elements"/> |
220 | 255 | </xsl:template> |
221 | 256 |
|
222 | | -<!-- Scrubbing opr:id values on the way out - we don't need them. --> |
| 257 | + <!-- Scrubbing opr:id values on the way out - we don't need them. --> |
223 | 258 | <xsl:template match="@opr:id"/> |
224 | 259 |
|
225 | 260 | </xsl:stylesheet> |
0 commit comments