A classic problem with functional programming is that a function can only return one result. If you want to compute two values (say a maximum and minimum) from the same input then you have two choices. You can either process the input more than once (which may involve some redundant computation), or you can return a composite result.
In this application there are many cases where we need to return a composite result.
To take an example, suppose we are validating an attribute, and we find that the declared type for that attribute
is a user-defined list type, where the item type of the list is part-number
, and part-number
is derived from xs:ID
. The calling code wants to know (a) whether the attribute is valid against its declared type;
(b) what error messages to report if not; and (c) whether the value contains any xs:ID
or xs:IDREF
values that need to be added to global tables of xs:ID
and xs:IDREF
values for document-level
validity checking at the end.
Our solution to this is that we recurse through the instance document in a tree-walk driven by xsl:apply-templates
in the normal way, but the return value from xsl:apply-templates
is a map containing all the information gleaned
from the processing of this subtree.
Very often the information returned from several calls on xsl:apply-templates
(for example, one call for child elements
and another for attributes) will need to be combined into a single map. At the top level, when we return from the initial call on
xsl:apply-templates
on the root node, all the information that is needed to produce the validation report is present in
one large map, and the final stage of processing takes this map and generates the XML report.
The maps that are produced by the different processing stages thus typically include some subset of a common set of fields. These include:
Table 1. The structure of maps used to return partial results of processing
Name | Value |
---|---|
valid | An xs:boolean indicating whether the subtree is valid |
errors | A set of error objects indicating error information to be included in the validation report |
value | The typed value of an element or attribute |
type | The type against which a subtree was validated |
lexical | The lexical form of an attribute or text node after whitespace normalization |
id | A set of xs:ID values found in the subtree |
id-map | A mapping from xs:ID values found in the subtree, to the nodes on which they appeared |
idrefs | A set of xs:IDREF values found in the subtree |
When two of these maps representing properties of different subtrees are combined, different rules apply to each field.
For example, for the id
and idrefs
and errors
fields we can take the union of the two values.
For the valid
property, we can apply a logical AND; a tree is valid only if all its subtrees are valid.
For value
and type
we can drop the value; these fields are used only at the next level up, and
do not propagate all the way to the root.