What is new in the Second Draft?

On March 6, 2018 we released the Second Draft of the Schematron QuickFix specification. The first Draft was published in April 2015. The specification was very incomplete, but the distributed Schema was almost ready. The Second Draft contains now content for sections which was just defined by "Tbd." placeholders in the First Draft so that the specification is almost completed. But it also contains schema changes, comparing to the First Draft. In the following, we will focus on these schema changes.

Basically there are five schema changes:

The following sections should describe this changes in detail.

Generic QuickFixes

In the first draft, it was not possible to have a dynamic amount of QuickFixes for each Schematron error. The QuickFix developer needs to define, how many QuickFixes a Schematron error should have at the end. The only chance to make the amount of available QuickFixes depending on the source document, was the use-when attribute. But this is only to hide specific QuickFixes, not to create a dynamic amount of QuickFixes for one error.

The following use case should demonstrate, what is possible with generic QuickFixes:

Let assume you have multiple persons in your person.xml (example from the User Guide):

1 <?xml-model href="people.sch" type="application/xml" schematypens="http://purl.oclc.org/dsdl/schematron"?>
2 <people>
3 <person>
4 <name>Nico Kutscherauer</name>
5 <dateOfBirth>1986-04-05</dateOfBirth>
6 <age>23</age>
7 </person>
8 <person>
9 <name>Lisa Simpson</name>
10 <dateOfBirth>2006-01-01</dateOfBirth>
11 <age>9</age>
12 </person>
13 <person>
14 <name>Homer Simpson</name>
15 <dateOfBirth>1960-01-01</dateOfBirth>
16 <age>10</age>
17 </person>
18 </people>

Just to remember: The business rule was, that all persons should be of age in this list.

One possible fix would be to take over the <dateOfBirth> of another <person> which is of age. So it should be a number of QuickFixes available, as much as other persons are in this list. But this is depending on the source document, so the Schematron developer does not know it when he design his QuickFixes.

With the generic QuickFixes, this is now possible:

13 <sqf:fix id="takeFrom" use-for-each="../person except .">
14 <let name="person" value="$sqf:current"/>
15 <sqf:description>
16 <sqf:title>Take the birthday and age of <value-of select="$person/name"/></sqf:title>
17 </sqf:description>
18 <sqf:replace match="age" target="age" node-type="element" select="$person/age/text()"/>
19 <sqf:replace match="dateOfBirth" target="dateOfBirth" node-type="element" select="$person/dateOfBirth/text()"/>
20 </sqf:fix>

To create a generic QuickFix, the <sqf:fix> element gets an use-for-each attribute. The XPath will be evaluated by the context of the Schematron error. The result value will be treated as a sequence. For each item of this sequence one QuickFix will be generated.

The context inside of the QuickFix will be still the current Schematron error, but in the $sqf:current variable the current item of the sequence will be stored.

QuickFix content model

In the first draft, the content model of the <sqf:fix> element was very strict. The simplified description (excluding all non-SQF elements) would be:

(sqf:param*, sqf:description, sqf:user-entry*, (sqf:call-fix | (sqf:add | sqf:delete | sqf:replace | sqf:stringReplace)+))

There are two weak points in this structure. On the one hand the <sqf:call-fix> is not combinable with other <sqf:call-fix> or activity elements. On the other hand the <sqf:description> is required for each QuickFix. By using a <sqf:call-fix> element, the called and the calling QuickFix are forced to have a <sqf:description> element, so the description of the called QuickFix is always useless.

Let this demonstrate on an example:

3 <sqf:fixes>
4 <sqf:fix id="movePerson">
5 <sqf:description>
6 <sqf:title>Move the person at the end</sqf:title>
7 </sqf:description>
8 <sqf:call-fix ref="moveQuickFix">
9 <sqf:with-param name="moveContext" select="."/>
10 <sqf:with-param name="moveTo" select="/*/person[last()]"/>
11 </sqf:call-fix>
12 </sqf:fix>
13 <sqf:fix id="moveQuickFix">
14 <sqf:param name="moveContext" required="yes"/>
15 <sqf:param name="moveTo" required="yes"/>
16 <sqf:description>
17 <sqf:title>Move the element <name path="$moveContext"/> after the element <name path="$moveTo"/>.</sqf:title>
18 </sqf:description>
19 <sqf:add match="$moveTo" position="after" select="$moveContext"/>
20 <sqf:delete match="$moveContext"/>
21 </sqf:fix>
22 </sqf:fixes>

As the QuickFix moveQuickFix has required parameters, it is not possible to refer to the QuickFix directly from a <sch:assert> or <sch:report> element. So the only way to use the QuickFix is, to call it by a <sqf:call-fix> element. But regarding to the first draft structure it is required, that the calling QuickFix has an own <sqf:description> element (like the QuickFix movePerson in the example). This description will be used as effective description. So the description of the called QuickFix moveQuickFix will never be used.

The second draft has in contrast a quite lax content model for the <sqf:fix> element. The simplified version looks as following:

sqf:param*, (sqf:description, ( sqf:call-fix | sqf:add | sqf:delete | sqf:replace | sqf:stringReplace )+ ) | (sqf:call-fix)

If a <sqf:description> element is used, the <sqf:call-fix> element can be combined with any other Activity Element. In fact, it can be used synonymical. Additionally, it is permitted to omit the description, if the <sqf:fix> element contains exactly one <sqf:call-fix> element and no Activity Element. Only in this case the description of the called QuickFix will be used as description of the calling QuickFix.

This lax content model enables significant use cases:

Localisation concept

There is no localisation concept in the first draft or just a rudimentary one: there is a xml:lang attribute available for the following elements:

But the logic, how to implement QuickFixes with descriptions for different languages is missing. There is only one <sqf:description> element permitted inside of <sqf:fix> or <sqf:user-entry> and it can contain exactly one <sqf:title> element only.

The second draft introduce now a first version of a real localisation concept. It is based on the Schematron localisation concept, but differs to be more flexible. A short description of the Schematorn localisation concept you will find here.

To support the Schematron localisation concept, a ref attribute was introduced for the elements <sqf:title> and <sqf:p>. The value of the ref attribute should be a reference to any text phrase. The specification does not restrict the implementations of the ref attribute to a specific reference structure.

A minimal implementation should be a reference to a <sch:diagnostic> element. The following example shows, how to localize a QuickFix in this way:

1 <schema xmlns="http://purl.oclc.org/dsdl/schematron" queryBinding="xslt2" xmlns:sqf="http://www.schematron-quickfix.com/validator/process" xml:lang="en">
   [...]
3 <pattern>
4 <rule context="person">
   [...]
12 <assert test="not($birth gt xs:date($maxBirth) and $birth le xs:date($minBirth)) and number(age) ge 18" sqf:fix="delete"><value-of select="name"/> is too young.</assert>
13 <sqf:fix id="delete">
14 <sqf:description>
15 <sqf:title ref="delete.title.de delete.title.fr">Delete the person from the list.</sqf:title>
16 <sqf:p ref="delete.p1.de delete.p1.fr">The fix deletes this person from the list, because it is out of age.</sqf:p>
17 </sqf:description>
18 <sqf:delete/>
19 </sqf:fix>
20 </rule>
21 </pattern>
22 <diagnostics>
23 <diagnostic id="delete.title.de" xml:lang="de">Lösche die Person von der Liste.</diagnostic>
24 <diagnostic id="delete.p1.de" xml:lang="de">Dieser Fix löscht die Person von der Liste, da sie nicht volljährig ist.</diagnostic>
   [...]
26 <diagnostic id="delete.title.fr" xml:lang="fr">Supprimer la personne de la liste.</diagnostic>
27 <diagnostic id="delete.p1.fr" xml:lang="fr">Ce correctif supprime la personne de la liste parce qu'elle n'a pas atteint l'âge de la majorité.</diagnostic>
28 </diagnostics>
29 </schema>

If the implementation does not support the localisation concept, it should display the content of <sqf:title> or <sqf:p> as fallback description.

Reference to other structures

The localisation based on <sch:diagnostic> elements is not very flexible. As the data type of the sch:diagnostic/@id attibuttes is xs:ID, multiple <sch:diagnostic> elements with the same id attributes violates against the ISO Schematron validation rules. This has the effect, that the introduction of a new language does not only mean to add corresponding <sch:diagnostic> elements, but also to add references to each Schematron error or QuickFix.

As consequence, the ref attribute is not restricted to reference to <sch:diagnostic> elements only. The implementation is free to support other localisaation structures. One example would be Java property files in XML syntax. In this case, the localised text phrases should be stored in external files, grouped by language. These files should be placed parallel to the Schematron schema with the name pattern ${sfe}_${lang}.xml. ${sfe} should be the name of the Schematron schema but without the extension .sch.

The content of the extenal files should be valid to the Java properties DTD. See the file localized2_de.xml:

1 <?xml version="1.0" encoding="UTF-8"?>
2 <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
3 <properties>
4 <entry key="delete.title">Lösche die Person von der Liste.</entry>
5 <entry key="delete.p1">Dieser Fix löscht die Person von der Liste, da sie nicht volljährig ist.</entry>
6 </properties>

Inside of the Schematron schema localized2.sch a ref attribute is now able to refer to this text prases:

17 <sqf:fix id="delete">
18 <sqf:description>
19 <sqf:title ref="delete.title">Delete the person from the list.</sqf:title>
20 <sqf:p ref="delete.p1">The fix deletes this person from the list, because it is out of age.</sqf:p>
21 </sqf:description>
22 <sqf:delete/>
23 </sqf:fix>

In contrast to the references to sch:diagnostic elements, in this case it is not necessary to make any changes on the Schematron schema to introduce a new language. You just have to add a file with the name localized2_fr.xml in the same folder of the localized2.sch:

1 <?xml version="1.0" encoding="UTF-8"?>
2 <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
3 <properties>
4 <entry key="delete.title">Supprimer la personne de la liste.</entry>
5 <entry key="delete.p1">Ce correctif supprime la personne de la liste parce qu'elle n'a pas atteint l'âge de la majorité.</entry>
6 </properties>

By using the same key attributes, the implementation should associate both languages - the German and the French - to the corresponding sqf:title or sqf:p elements

Further discussions

Please note, that the issue about the language concept is not closed yet. The reason is, that this concept does not include the possibility to store all descriptions into an external file and just use them by references. Depending on the decisions of this discussion there could be changes on the existing structure. This is one reason, because the second draft is not published yet.

© Copyright 2014-2018 Nico Kutscherauer (last update 2018-07-17)

ImprintPrivacy PolicyContactSitemap