The code for checking input data against assertions defined in the schema is very straightforward. Here is the actual logic (no simplifications this time):
<xsl:function name="scm:check-assertions" as="map(*)" saxon:as="tuple(error: element(vr:error)*)"> <xsl:param name="type" as="element(scm:complexType)"/> <xsl:param name="node" as="element()"/> <xsl:variable name="copy-sans-comments" as="element()"> <xsl:apply-templates select="$node" mode="copy-sans-comments"/> </xsl:variable> <xsl:variable name="failures" as="element(vr:error)*"> <xsl:for-each select="$type/scm:assertion"> <xsl:try> <xsl:variable name="assertion-result" as="item()*"> <xsl:evaluate xpath="@test" context-item="$copy-sans-comments" namespace-context="scm:make-namespace-context(.)" base-uri="{base-uri($scm)}"> <xsl:with-param name="value" select="$copy-sans-comments"/> </xsl:evaluate> </xsl:variable> <xsl:if test="not($assertion-result)"> <xsl:sequence select="scm:error($node, ' must satisfy assertion ' || @test)"/> </xsl:if> <xsl:catch errors="*"> <xsl:sequence select="scm:error($node, ' must satisfy assertion ' || @test || '. Evaluation of the assertion failed with a dynamic error: ' || $err:description)"/> </xsl:catch> </xsl:try> </xsl:for-each> </xsl:variable> <xsl:sequence select="map{'errors': $failures}"/> </xsl:function>
Notes relating to this code:
The function returns a map, but the saxon:as
declaration reveals that there is only
one field defined in this map, namely the errors
field. If the constraint is satisfied,
an empty map is returned. The reason for defining it this way is that the calling code can use its standard
mechanism for combining the results of different validation processes.
The function makes a copy of the element being validated, in which comments and processing instructions have been removed. This is prescribed by the specification. A copy of the subtree is needed to ensure that the XPath expressions in the assertion have no access to nodes in the input document that fall outside the subtree being validated.
The assertion is evaluated using xsl:evaluate
, a new XSLT 3.0 instruction that evaluates XPath expressions known only
dynamically (in this case, an expression read from the SCM file). The instruction provides machinery to establish the static and
dynamic context for evaluating the expression, here including the context item, the value of the $value
variable,
the namespace context, and the base URI.
If the effective boolean value of the assertion result is false, the function returns an error value.
If a dynamic error occurs while evaluating the assertion, this is caught using the new xsl:try
instruction in XSLT 3.0, and the function returns an error value.
The whole process is repeated for each defined assertion. If more than one assertion fails, then more than one error will be returned.