Sensible use of patterns
By way of illustration, we want to replace the second pattern for our example:
<pattern>
<rule context="ark:animal[@carnivore='yes']">
<report test="parent::*/ark:animal[@carnivore='no']">
There are carnivores and herbivores in one accommodation.
The animals are not a food source!
</report>
<report test="parent::*/ark:animal/ark:weight < (ark:weight div 2)">
Noah, this carnivore is too strong (heavy) for its roommate.
The carnivore could use it as a food source.
</report>
</rule>
</pattern>
<pattern>
<rule context="ark:animal">
<report test="parent::*/ark:animal/ark:weight < (ark:weight div 10)">
Noah, the animal is too heavy for its roommates!
It could trample down one of them.
</report>
</rule>
</pattern>
The second pattern contains a rule which checks for all <animal> elements whether they are significantly heavier than the other roommates. Here, the condition shall apply that an animal shall be no more than ten times heavier as its lightest roommate.
As you can see, this test is similar to the second test of the already known rule. The query is the same, only the criterion for the carnivore rule is stricter: A carnivore can be maximally twice as heavy and all animals maximally ten times as heavy as their rommates.
This means: An animal which passes the test in the carnivore rule, will in any case also pass the test in the new rule. As a consequence, a renewed check of the carnivores by means of the second rule becomes unnecessary and the new rule could also only check the non-carnivores:
<pattern>
<rule context="ark:animal[@carnivore='yes']">
<report test="parent::*/ark:animal[@carnivore='no']">
There are carnivores and herbivores in one accomodation.
The animals are not a food source!
</report>
<report test="parent::*/ark:animal/ark:weight < (ark:weight div 2)">
Noah, this carnivore is too strong (heavy) for its roommate.
The carnivore could use it as a food source.
</report>
</rule>
</pattern>
<pattern>
<rule context=" ark:animal[@carnivore='no']">
<report test="parent::*/ark:animal/ark:weight < (ark:weight div 10)">
Noah, the animal is too heavy for its roommates!
It could trample down one of them.
</report>
</rule>
</pattern>
This version entails the risk that for example the instance can be supplemented by a further value for the carnivore attribute. If vegans shall be included into the instance with the value vegan, they will be tested by neither of the two rules. The crucial error could be fixed by the condition [not(@carnivore='yes')] instead of [@carnivore='no']. But there is also a more elegant and more efficient method in order to arrange both rules in a pattern:
<pattern>
<rule context="ark:animal[@carnivore='yes']">
<report test="parent::*/ark:animal[@carnivore='no']">
There are carnivores and herbivores in one accommodation.
The animals are not a food source!
</report>
<report test="parent::*/ark:animal/ark:weight < (ark:weight div 2)">
Noah, this carnivore is too strong (heavy) for its roommate.
It could use it as a food source.
</report>
</rule>
<rule context="ark:animal">
<report test="parent::*/ark:animal/ark:weight < (ark:weight div 10)">
Noah, this animal is too heavy for its rommmates!
It could trample down one of them.
</report>
</rule>
</pattern>
If both rules are put in one pattern, it can be taken advantage of the order of the rules: Each <animal> element which has been selected by the first rule as a context node will not be checked anymore by the second rule. All the other animals – which means animals being not a carnivore – will be checked by the second rule. Therefore further conditions for the <animal> element to be selected are not required.
Since for each pattern the parser runs one time through the whole node tree of the instance and has to check for each node whether it applies to one of the included rules, two rules in one pattern are much more performant. The pattern arrangement will also be of interest in connection with the <phase> element.
Note for XSLT experts:
For XSLT experts, we are going to take a look behind the scenes. A XSLT implementation realises the Schematron schema with two patterns as follows:
<xsl:template match="/">
<xsl:apply-templates mode="P1"/> (1)
<xsl:apply-templates mode="P2"/> (1)
</xsl:template>
<xsl:template match="ark:animal[@carnivore='yes']" mode="P1"> (2)
<xsl:if test="parent::*/ark:animal[@carnivore='no']">
<!-- Implementation of the error message -->
</xsl:if>
<xsl:if test="parent::*/ark:animal/ark:weight < (ark:weight div 2)">
<!-- Implementation of the error message -->
</xsl:if>
</xsl:template>
<xsl:template match="ark:animal[@carnivore='no']" mode="P2"> (2)
<xsl:if test="parent::*/ark:animal/ark:weight < (ark:weight div 10)">
<!-- Implementation of the error message -->
</xsl:if>
</xsl:template>
(1) | For each pattern a <xsl:apply-templates> command is executed in a separate mode. |
(2) | For each rule a separate <xsl:template> is generated for the respective mode. |
For purposes of comparison you can see below how the implementation realises the one pattern solution:
<xsl:template match="/">
<xsl:apply-templates mode="P1"/> (1)
</xsl:template>
<xsl:template match="ark:animal[@carnivore='yes']" priority="100" mode="P1"> (2)
<xsl:if test="parent::*/ark:animal[@carnivore='no']">
<!-- Implementation of the error message -->
</xsl:if>
<xsl:if test="parent::*/ark:animal/ark:weight < (ark:weight div 2)">
<!-- Implementation of the error message -->
</xsl:if>
</xsl:template>
<xsl:template match="ark:animal" priority="99" mode="P1"> (2)
<xsl:if test="parent::*/ark:animal/ark:weight < (ark:weight div 10)">
<!-- Implementation of the error message -->
</xsl:if>
</xsl:template>
(1) | Because of the omitted pattern, the second <xsl:apply-templates> command is also no longer required. |
(2) | Now that both rules are put into the same pattern, the templates are also in the same mode. The conflict which arises if a node applies to several rules or templates, is resolved by the priority attribute which is generated depending on the order of the rules. This demonstrates why nodes which apply to a rule are not checked by a further rule in the same pattern. |
Since we have offered deep insights into the Skeleton implementation, the following table shall provide an overview of the implementation of the Schematron constructs in XSLT elements:
Schematron | XSLT |
<assert test="XPath">error message </assert> | <xsl:choose> <xsl:when test="XPath"/> <xsl:otherwise>error message </xsl:otherwise> </xsl:choose> |
<report test="XPath">error message </report> | <xsl:if test="XPath">error message </xsl:if> |
<rule context="XPath"> | <xsl:template match="XPath" priority="xxx" mode="pattern ID"> |
<pattern> | <xsl:apply-templates select ="/" mode="pattern ID"/> |
<< back | next >> |
Copyright © dpunkt.verlag GmbH 2011
Printing of the online version is permitted exclusively for private use. Otherwise this chapter from the book "Schematron - Effiziente Business Rules für XML-Dokumente" is subject to the same provisions as those applicable for the hardcover edition: The work including all its components is protected by copyright. All rights reserved, including reproduction, translation, microfilming as well as storage and processing in electronic systems.
dpunkt.verlag GmbH, Ringstraße 19B, 69115 Heidelberg, fon 06221-14830, fax 06221-148399, hallo(at)dpunkt.de