For the implementation of Schematron QuickFixes, an extension of the Schematron Validation Report Language (SVRL) became necessary. Moreover, there was a further reason which led to the adaptations of the SVRL schema: Apart from the QuickFixes, one aim of this SQF project is to achieve a complete as possibile implementation of the Schematron features in the Escali Schematron. On this page, the changes (extensions and adaptations) of the SVRL schema shall be described.
The implementation of the QuickFixes is carried out with the same namespace as the one for the extension of Schematron (http://www.schematron-quickfix.com/validator/process).
The implementation can be divided into two tasks:
Definition of the individual QuickFixes, which contain a description and XSLT templates, for the appropriate <svrl:successful-report> or <svrl:failed-assert> elements.
Indication of the stylesheet framework with global variables, keys, fixed templates, etc. for the manipulator.
Here the implementation of a QuickFix for a <svrl:failed-assert> element shall be demonstrated. A QuickFix for a <svrl:successful-report> element is implemented in an equivalent way. The example from the User Guide serves as the basis.
Therefore, let us hava a look again on a Schematron <assert> from the connected QuickFixes:
15 | <assert test="$birth gt xs:date($maxBirth) and $birth le xs:date($minBirth)" sqf:fix="setAge birthEntry"><value-of select="name"/> is not <value-of select="age"/> years old or is not born on <value-of select="dateOfBirth"/>.</assert> |
[...] | |
17 | <sqf:fix id="setAge"> |
[...] | |
26 | </sqf:fix> |
[...] | |
34 | <sqf:fix id="birthEntry"> |
[...] | |
53 | </sqf:fix> |
For each QuickFix which was assigned to the appropriate <sch:assert>, the <svrl:failed-assert> element gets apart from the present <svrl:text> element a <sqf:fix> element:
17 | <svrl:failed-assert test="$birth gt xs:date($maxBirth) and $birth le xs:date($minBirth)" location="/*[namespace-uri()=''][local-name()='people'][1]/*[namespace-uri()=''][local-name()='person'][1]" sqf:id="d1e3_d1e25" sqf:patternId="d1e6"> |
18 | <svrl:text>Nico Kutscherauer is not 23 years old or is not born on 1986-04-05.</svrl:text> |
19 | <sqf:fix fixId="setAge" messageId="d1e25" contextId="d1e3" id="setAge-d1e25-d1e3" role="replace"> |
[...] | |
46 | </sqf:fix> |
47 | <sqf:fix fixId="birthEntry" messageId="d1e25" contextId="d1e3" id="birthEntry-d1e25-d1e3" role="replace"> |
[...] | |
110 | </sqf:fix> |
111 | </svrl:failed-assert> |
There is one exception if a QuickFix is not available because of its use-when attribute for the <svrl:failed-assert> element.
The <sqf:fix> element always has five attributes. The most important one is the id attribute. It contains an unique ID for each <sqf:fix> element in a SVRL report. The extractor expects in its $id parameter an ID list. If the QuickFix shall be used, this list must contain the ID of the QuickFix. The ID consists of the three other IDs fixId, messageId and contextId, separated by dashes, and becomes unique in this way. For the application of the other attributes, please read their documentation in the SVRL extension reference.
As child elements, the <sqf:fix> element contains at least the description of the QuickFix (<sqf:description>) and a <sqf:sheet> element:
19 | <sqf:fix fixId="setAge" messageId="d1e25" contextId="d1e3" id="setAge-d1e25-d1e3" role="replace"> |
20 | <sqf:description> |
21 | <svrl:text>Calculate the age from the date of birth.</svrl:text> |
22 | </sqf:description> |
23 | <sqf:sheet> |
[...] | |
45 | </sqf:sheet> |
46 | </sqf:fix> |
The <sqf:sheet> element now contains all necessary XSLT templates which are required for the implementation of the QuickFix. The templates are finished – which means they are already included in the XSLT namespace (http://www.w3.org/1999/XSL/Transform) and do not have to be further processed. In the follwoing, the QuickFix from a Schematron schema shall be compared with its implementation in the SVRL report.
The QuickFix in the Schematron schema:
5 | <rule context="person"> |
6 | <let name="currYear" value="year-from-date(current-date())"/> |
7 | <let name="maxBirth" value="replace(string(current-date()),string($currYear), string($currYear - age - 1))"/> |
8 | <let name="minBirth" value="replace(string(current-date()),string($currYear), string($currYear - age))"/> |
9 | <let name="birth" value="xs:date(dateOfBirth)"/> |
[...] | |
11 | <let name="yearOfBirth" value="year-from-date($birth)"/> |
12 | <let name="yearDif" value="$currYear - $yearOfBirth"/> |
13 | <let name="birthdayInThisYear" value="replace(string($birth), string($yearOfBirth), string($currYear))"/> |
[...] | |
17 | <sqf:fix id="setAge"> |
18 | <sqf:description> |
19 | <sqf:title>Calculate the age from the date of birth.</sqf:title> |
20 | <sqf:p>This fix assumes that the date of birth is correct but the age not. The new age will be calculated from the date of birth.</sqf:p> |
21 | </sqf:description> |
22 | <let name="newAge" value="if(xs:date($birthdayInThisYear) > current-date()) |
23 | then ($yearDif - 1) |
24 | else ($yearDif)"/> |
25 | <sqf:replace match="age" target="age" node-type="element" select="$newAge"/> |
26 | </sqf:fix> |
[...] | |
54 | </rule> |
Its implementation in the SVRL report:
19 | <sqf:fix fixId="setAge" messageId="d1e25" contextId="d1e3" id="setAge-d1e25-d1e3" role="replace"> |
20 | <sqf:description> |
21 | <svrl:text>Calculate the age from the date of birth.</svrl:text> |
22 | </sqf:description> |
23 | <sqf:sheet> |
24 | <xsl:template match="node()[generate-id(.)=('d1e11')]" priority="11"> |
25 | <xsl:variable name="ruleContext" select="key('sqf:nodesById',('d1e3'))"/> |
26 | <xsl:variable name="activityContext" select="self::node()"/> |
27 | <xsl:for-each select="$ruleContext"> |
28 | <xsl:variable name="currYear" select="year-from-date(current-date())"/> |
29 | <xsl:variable name="maxBirth" select="replace(string(current-date()),string($currYear), string($currYear - age - 1))"/> |
30 | <xsl:variable name="minBirth" select="replace(string(current-date()),string($currYear), string($currYear - age))"/> |
31 | <xsl:variable name="birth" select="xs:date(dateOfBirth)"/> |
32 | <xsl:variable name="yearOfBirth" select="year-from-date($birth)"/> |
33 | <xsl:variable name="yearDif" select="$currYear - $yearOfBirth"/> |
34 | <xsl:variable name="birthdayInThisYear" select="replace(string($birth), string($yearOfBirth), string($currYear))"/> |
35 | <xsl:variable name="newAge" select="if(xs:date($birthdayInThisYear) > current-date()) then ($yearDif - 1) else ($yearDif)"/> |
36 | <xsl:for-each select="$activityContext"> |
37 | <xsl:processing-instruction name="sqfc-start" sqf:changeMarker="true"><xsl:text>replace-</xsl:text><xsl:value-of select="generate-id()"/></xsl:processing-instruction> |
38 | <xsl:element name="age"> |
39 | <xsl:value-of select="$newAge"/> |
40 | </xsl:element> |
41 | <xsl:processing-instruction name="sqfc-end" sqf:changeMarker="true"><xsl:text>replace-</xsl:text><xsl:value-of select="generate-id()"/></xsl:processing-instruction> |
42 | </xsl:for-each> |
43 | </xsl:for-each> |
44 | </xsl:template> |
45 | </sqf:sheet> |
46 | </sqf:fix> |
Here, XSLT experts may already comprehend to some extent how the activity elements are converted to XSLT templates.
For further processing of the SVRL report, at this point only the user entries are to be considered. In SVRL these are also defined as <sqf:user-entry> elements and as child elements of the <sqf:fix> element. The user entry in the Schematron schema
39 | <sqf:user-entry name="birth" type="xs:date"> |
40 | <sqf:description> |
41 | <sqf:title>Enter the correct date.</sqf:title> |
42 | <sqf:p>Set the correct date in the format YYYY-MM-DD.</sqf:p> |
43 | </sqf:description> |
44 | </sqf:user-entry> |
becomes in the SVRL report the following:
51 | <sqf:user-entry xmlns="http://purl.oclc.org/dsdl/schematron"> |
52 | <sqf:description> |
53 | <svrl:text>Enter the correct date.</svrl:text> |
54 | </sqf:description> |
55 | <sqf:param name="birth" as="xs:date" param-id="d8e99_d1e3"/> |
56 | </sqf:user-entry> |
In case a QuickFix has one or more user entries, the appropriate <sqf:sheet> element contains apart from the XSLT templates also a XSLT parameter for each user entry:
51 | <sqf:user-entry xmlns="http://purl.oclc.org/dsdl/schematron"> |
52 | <sqf:description> |
53 | <svrl:text>Enter the correct date.</svrl:text> |
54 | </sqf:description> |
55 | <sqf:param name="birth" as="xs:date" param-id="d8e99_d1e3"/> |
56 | </sqf:user-entry> |
57 | <sqf:sheet> |
58 | <xsl:param name="sqf:d8e99_d1e3" as="xs:date"/> |
59 | <xsl:template match="node()[generate-id(.)=('d1e8')]" priority="13"> |
[...] | |
62 | <xsl:variable name="birth" select="$sqf:d8e99_d1e3"/> |
[...] | |
83 | </xsl:template> |
84 | <xsl:template match="node()[generate-id(.)=('d1e11')]" priority="11"> |
[...] | |
87 | <xsl:variable name="birth" select="$sqf:d8e99_d1e3"/> |
[...] | |
108 | </xsl:template> |
109 | </sqf:sheet> |
The QName of the parameter name consists of the SQF namespace and the parameter ID from the param-id attribute consists of the <sqf:param> element. When calling up the manipulator, the value of the user entry must be passed on to the parameter with this name. Therefore, the assignment of the parameter value when called up is not as expected birth=value, but {http://www.schematron-quickfix.com/validator/process}d8e99_d1e3=value. Since this parameter name is unique for each user entry, parameter dublicates are avoided when calling up the manipulator.
As presented above, each QuickFix in the SVRL report contains the QuickFix-specific components of the manipulator as content of the <sqf:sheet> element. Now, the manipulator only needs the stylesheet framework. Since the approach was that the SVRL report contains the whole manipulator and the extractor unites all components, also the framework must be generated by the Escali Schematron. The stylesheet is fix for the most part, but may also contain elements dependent on the Schematron schema. If, for example, the Schematron schema contains global variables, XSLT keys or self-defined XSLT functions, they are defined here. The stylesheet framework in our example only contains the fix elements:
2 | <svrl:schematron-output xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:saxon="http://saxon.sf.net/" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:sqf="http://www.schematron-quickfix.com/validator/process" xmlns:svrl="http://purl.oclc.org/dsdl/svrl" schemaVersion="" title="People - test" phase="#all"> |
3 | <sqf:topLevel schema="http://www.schematron-quickfix.com/files/userGuide/people.sch" instance="http://www.schematron-quickfix.com/files/userGuide/people.xml"> |
4 | <xsl:stylesheet version="2.0"> |
5 | <xsl:output method="xml"/> |
6 | <xsl:key name="sqf:nodesById" match="node()" use="generate-id()"/> |
7 | <xsl:template match="node()|@*" priority="-2" mode="addAtt addChild"/> |
8 | <xsl:template match="node() | @*" priority="-3" mode="#all"> |
9 | <xsl:copy> |
10 | <xsl:apply-templates select="node() | @*"/> |
11 | </xsl:copy> |
12 | </xsl:template> |
13 | </xsl:stylesheet> |
14 | </sqf:topLevel> |
[...] | |
204 | </svrl:schematron-output> |
The <sqf:topLevel> element contains the stylesheet framework of the manipulator. The extractor inserts the manipulator contents of the selected QuickFixes (<sqf:sheet>) at the end of the stylesheet framework.
For all extensions which do not deal with the QuickFixes but for which a complete implementation of all Schematron features exists in SVRL, the namespace http://www.escali.schematron-quickfix.com/ (with the prefix es) was introduced. The following features of the ISO standard Schematron have no implementation in the SVRL specification:
Backtracing of elements
The assignment of paragraphs (<sch:p>) to the appropriate <svrl:fired-rule> or <svrl:active-pattern> elements
The attributes see and icon
Backtracing of abstract patterns (is-a attribute)
Inline elements
In order that, for example, for two different <svrl:fired-rule> elements the statement can be made that they originate from the same <sch:rule> element, the es:id attribute was introduced for these elements. Moreover, reference can be made to these elements (see Paragraphs).
In ISO Schematron, rules and patterns can be preceded by paragraphs (<sch:p>). In SVRL this assignment of the paragraphs does not exist. They are converted to <svrl:text> elements at the beginning of the report. The reason why Schematron is more advanced in this aspect as SVRL is incomprehensible to me.
In order to avoid inventing a completely new SVRL structure, the assignment of the paragraphs was realised by es:ref attributes. They reference to the es:id attribute of the corresponding SVRL element (<svrl:fired-rule> or <svrl:active-pattern>).
For the attributes see, icon and is-a, the appropriate SVRL elements simply get corresponding es:see, es:icon or es:is-a attributes.
The inline elements allowed in Schematron but not in SVRL are simply transferred to the extension namespace http://www.escali.schematron-quickfix.com/.
© Copyright 2014-2018 Nico Kutscherauer (last update 2018-07-17)
Imprint – Privacy Policy – Contact – Sitemap