http://mathling.com/art/components library module
http://mathling.com/art/components
Component merging part of art execution framework.
Copyright© Mary Holstege 2020-2022
CC-BY (https://creativecommons.org/licenses/by/4.0/)
Status: Stable
Imports
http://mathling.com/core/utilitiesimport module namespace util="http://mathling.com/core/utilities" at "../core/utilities.xqy"http://mathling.com/core/errors
import module namespace errors="http://mathling.com/core/errors" at "../core/errors.xqy"
Functions
Function: expand
declare function expand($components as map(xs:string,item()*),
$lookup as function(xs:QName, xs:integer) as function(*)?) as map(xs:string,item()*)
declare function expand($components as map(xs:string,item()*), $lookup as function(xs:QName, xs:integer) as function(*)?) as map(xs:string,item()*)
expand()
Workaround for function visibility issue with XSLT packages. Included
in the XQuery, which doesn't otherwise need it, to allow for
single-sourcing. The idea is that the component user calls
this inside their components() method to do the function lookup, in a
context where their methods are in scope so that it can succeed. The
lookup function needs to be defined in the scope of the component itself.
That means components need to be responsible for their own subcomponents
also.
Params
- components as map(xs:string,item()*)
- lookup as function(xs:QName,xs:integer)asfunction(*)?
Returns
- map(xs:string,item()*)
declare function this:expand( $components as map(xs:string,item()*), $lookup as function(xs:QName, xs:integer) as function(*)? ) as map(xs:string,item()*) { fold-left($components=>map:keys(), $components, function($components as map(xs:string,item()*), $key as xs:string) { let $component := $components($key) return ( $components=>map:put($key, $component=>map:put("namespace-fn", $lookup(QName($component("namespace"), "metadata"), 3) )=>map:put("colophon-fn", $lookup(QName($component("namespace"), "colophon"), 1) )=>map:put("algorithm-parameters-fn", $lookup(QName($component("namespace"), "algorithm-parameters"), 2) )=>map:put("algorithm-mode-parameters", $lookup(QName($component("namespace"), "algorithm-mode-parameters"), 3) )=>map:put("rendering-parameters-fn", $lookup(QName($component("namespace"), "rendering-parameters"), 2) )=>map:put("randomizers-fn", $lookup(QName($component("namespace"), "randomizers"), 2) )=>map:put("components-fn", $lookup(QName($component("namespace"), "components"), 0) ) ) ) } ) }
Function: rendering-parameters
declare function rendering-parameters($components as map(xs:string, map(xs:string,item()*)),
$canvas as map(xs:string,item()*),
$algorithm-parameters as map(xs:string, item()*)) as map(xs:string, item()*)
declare function rendering-parameters($components as map(xs:string, map(xs:string,item()*)), $canvas as map(xs:string,item()*), $algorithm-parameters as map(xs:string, item()*)) as map(xs:string, item()*)
rendering-parameters()
Accumulated rendering parameters for the set of components.
Params
- components as map(xs:string,map(xs:string,item()*)): map of components, a map from the component name to the component properties, e.g. 'namespace', 'mode', and 'render'.
- canvas as map(xs:string,item()*): drawing canvas
- algorithm-parameters as map(xs:string,item()*): accumulated algorithm parameter bundle
Returns
- map(xs:string,item()*): Map of rendering parameters
declare function this:rendering-parameters( $components as map(xs:string, map(xs:string,item()*)), $canvas as map(xs:string,item()*), $algorithm-parameters as map(xs:string, item()*) ) as map(xs:string, item()*) { fold-left( $components=>map:keys(), map {}, function($map as map(xs:string,item()*), $key as xs:string) as map(xs:string,item()*) { let $component := $components($key) let $add := head(($component("render"), true())) let $fn := if ($add) then ( head(( $component("rendering-parameters-fn"), function-lookup(QName($component("namespace"), "rendering-parameters"), 2) )) ) else () (: Causes failure with -TP let $subcomponents := if ($add) then ( head(( $component("components-fn"), function-lookup(QName($component("namespace"), "components"), 0) )) ) else () let $subparameters := if (empty($subcomponents)) then () else this:rendering-parameters($subcomponents(), $canvas, $algorithm-parameters) :) let $add-subcomponents := $add and exists( head(( $component("components-fn"), function-lookup(QName($component("namespace"), "components"), 0) )) ) return ( util:merge-into(( $map, if ($add-subcomponents) then ( this:rendering-parameters( head(( $component("components-fn"), function-lookup(QName($component("namespace"), "components"), 0) ))(), $canvas, $algorithm-parameters ) ) else (), (: $subparameters, :) if (empty($fn)) then () else $fn($canvas, $algorithm-parameters) )) ) } ) }
Function: algorithm-parameters
declare function algorithm-parameters($components as map(xs:string, map(xs:string,item()*)),
$resolution as xs:string,
$canvas as map(xs:string, item()*)) as map(xs:string, item()*)
declare function algorithm-parameters($components as map(xs:string, map(xs:string,item()*)), $resolution as xs:string, $canvas as map(xs:string, item()*)) as map(xs:string, item()*)
algorithm-parameters()
Accumulated algorithm parameters for the set of components.
Params
- components as map(xs:string,map(xs:string,item()*)): map of components, a map from the component name to the component properties, e.g. 'namespace', 'mode', and 'render'.
- resolution as xs:string: drawing resolution (@see core.html)
- canvas as map(xs:string,item()*): drawing canvas
Returns
- map(xs:string,item()*): Map of algorithm parameters
declare function this:algorithm-parameters( $components as map(xs:string, map(xs:string,item()*)), $resolution as xs:string, $canvas as map(xs:string, item()*) ) as map(xs:string, item()*) { fold-left( $components=>map:keys(), map {}, function($map as map(xs:string,item()*), $key as xs:string) as map(xs:string,item()*) { let $component := $components($key) let $mod := head(($component("mode"), "default")) let $fn := head(( $component("algorithm-parameters-fn"), function-lookup(QName($component("namespace"), "algorithm-parameters"), 2) )) let $fnmode := head(( $component("algorithm-mode-parameters-fn"), function-lookup(QName($component("namespace"), "algorithm-mode-parameters"), 3) )) (: Causes failure with -TP let $subcomponents := head(( $component("components-fn"), function-lookup(QName($component("namespace"), "components"), 0) )) let $subparameters := if (empty($subcomponents)) then () else this:algorithm-parameters($subcomponents(), $resolution, $canvas) :) let $add-subcomponents := exists( head(( $component("components-fn"), function-lookup(QName($component("namespace"), "components"), 0) )) ) return ( util:merge-into(( $map, if ($add-subcomponents) then ( this:algorithm-parameters( head(( $component("components-fn"), function-lookup(QName($component("namespace"), "components"), 0) ))(), $resolution, $canvas) ) else (), (: $subparameters, :) if (empty($fn)) then () else ( $fn($resolution, $canvas), if (empty($fnmode)) then () else ( $fnmode(($component("mode"),"default")[1], $resolution, $canvas) ) ) )) ) } ) }
Function: randomizers
declare function randomizers($components as map(xs:string, map(xs:string,item()*)),
$canvas as map(xs:string,item()*),
$parameters as map(xs:string,item()*)) as map(xs:string,item()*)
declare function randomizers($components as map(xs:string, map(xs:string,item()*)), $canvas as map(xs:string,item()*), $parameters as map(xs:string,item()*)) as map(xs:string,item()*)
randomizers()
Accumulated set of randomizer descriptors for the set of components.
Params
- components as map(xs:string,map(xs:string,item()*)): map of components, a map from the component name to the component properties, e.g. 'namespace', 'mode', and 'render'.
- canvas as map(xs:string,item()*): drawing canvas
- parameters as map(xs:string,item()*): accumulated algorithm parameter bundle
Returns
- map(xs:string,item()*): Map of randomizers
declare function this:randomizers( $components as map(xs:string, map(xs:string,item()*)), $canvas as map(xs:string,item()*), $parameters as map(xs:string,item()*) ) as map(xs:string,item()*) { fold-left( $components=>map:keys(), map {}, function($map as map(xs:string,item()*), $key as xs:string) as map(xs:string,item()*) { let $component := $components($key) let $fn := head(( $component("randomizers-fn"), function-lookup(QName($component("namespace"), "randomizers"), 2) )) (: Causes error with -TP let $subcomponents := head(( $component("components-fn"), function-lookup(QName($component("namespace"), "components"), 0) )) let $subrandomizers := if (empty($subcomponents)) then () else this:randomizers($subcomponents(), $canvas, $parameters) :) let $add-subcomponents := exists( head(( $component("components-fn"), function-lookup(QName($component("namespace"), "components"), 0) )) ) return ( util:merge-into(( $map, if ($add-subcomponents) then ( this:randomizers( head(( $component("components-fn"), function-lookup(QName($component("namespace"), "components"), 0) ))(), $canvas, $parameters) ) else (), (: $subrandomizers, :) if (empty($fn)) then () else $fn($canvas, $parameters) )) ) } ) }
Function: metadata
declare function metadata($components as map(xs:string, map(xs:string,item()*)),
$canvas as map(xs:string,item()*),
$randomizers as map(xs:string,item()*),
$parameters as map(xs:string,item()*))
declare function metadata($components as map(xs:string, map(xs:string,item()*)), $canvas as map(xs:string,item()*), $randomizers as map(xs:string,item()*), $parameters as map(xs:string,item()*))
metadata()
Accumulated set of metadata for the set of components. Will collect
descriptions for all parameters and randomizers as well as whatever
additional metadata is in the component's metadata callback.
Params
- components as map(xs:string,map(xs:string,item()*)): map of components, a map from the component name to the component properties, e.g. 'namespace', 'mode', and 'render'.
- canvas as map(xs:string,item()*): drawing canvas
- randomizers as map(xs:string,item()*): accumulated randomizer bundle
- parameters as map(xs:string,item()*): accumulated algorithm parameter bundle
declare function this:metadata( $components as map(xs:string, map(xs:string,item()*)), $canvas as map(xs:string,item()*), $randomizers as map(xs:string,item()*), $parameters as map(xs:string,item()*) ) { for $key in $components=>map:keys() let $component := $components($key) let $fn := head(( $component("metadata-fn"), function-lookup(QName($component("namespace"), "metadata"), 3) )) (: let $subcomponents := head(( $component("components-fn"), function-lookup(QName($component("namespace"), "components"), 0) )) let $submetadata := if (empty($subcomponents)) then () else this:metadata($subcomponents(), $canvas, $randomizers, $parameters) :) let $add-subcomponents := exists( head(( $component("components-fn"), function-lookup(QName($component("namespace"), "components"), 0) )) ) return ( <art:component name="{$key}">{ if (empty($fn)) then () else $fn($canvas, $randomizers, $parameters), if ($add-subcomponents) then ( this:metadata( head(( $component("components-fn"), function-lookup(QName($component("namespace"), "components"), 0) ))(), $canvas, $randomizers, $parameters ) ) else () (: $submetadata :) }</art:component> ) }
Function: colophons
declare function colophons($components as map(xs:string, map(xs:string,item()*)),
$parameters as map(xs:string,item()*)) as xs:string*
declare function colophons($components as map(xs:string, map(xs:string,item()*)), $parameters as map(xs:string,item()*)) as xs:string*
colophons()
Accumulated colophon strings for the set of components.
Params
- components as map(xs:string,map(xs:string,item()*)): map of components, a map from the component name to the component properties, e.g. 'namespace', 'mode', and 'render'.
- parameters as map(xs:string,item()*): accumulated algorithm parameter bundle
Returns
- xs:string*: Sequence of descriptive strings
declare function this:colophons( $components as map(xs:string, map(xs:string,item()*)), $parameters as map(xs:string,item()*) ) as xs:string* { for $key in $components=>map:keys() let $component := $components($key) let $fn := head(( $component("colophon-fn"), function-lookup(QName($component("namespace"), "colophon"), 1) )) (: let $subcomponents := head(( $component("components-fn"), function-lookup(QName($component("namespace"), "components"), 0) )) let $subcolophons := if (empty($subcomponents)) then () else this:colophons($subcomponents(), $parameters) :) let $add-subcomponents := exists( head(( $component("components-fn"), function-lookup(QName($component("namespace"), "components"), 0) )) ) return ( if (empty($fn)) then () else $fn($parameters), if ($add-subcomponents) then ( this:colophons( head(( $component("components-fn"), function-lookup(QName($component("namespace"), "components"), 0) ))(), $parameters ) ) else () (: $subcolophons :) ) }
Function: check-components
declare function check-components($components as map(xs:string,item()*),
$what as xs:string*) as empty-sequence()
declare function check-components($components as map(xs:string,item()*), $what as xs:string*) as empty-sequence()
check-components()
Verify that component map properly refers to well-defined components.
That means that function-lookup can locate the callback functions for
the component's namespace.
Params
- components as map(xs:string,item()*): map of components, a map from the component name to the component properties, e.g. 'namespace', 'mode', and 'render'.
- what as xs:string*: which aspects to check, either "all", "basics", or some sequence of "algorithm-parameters", "algorithm-mode-parameters", "rendering-parameters", and "randomizers". "all" checks everything. "basics" checks "algorithm-parameters", "rendering-parameters", and "randomizers"
Returns
- : empty sequence
declare function this:check-components( $components as map(xs:string,item()*), $what as xs:string* ) as empty-sequence() { let $what := if ($what="all") then ( "metadata", "colophon", "algorithm-parameters", "algorithm-mode-parameters", "rendering-parameters", "randomizers" ) else if ($what="basics") then ( "algorithm-parameters", "rendering-parameters", "randomizers" ) else ( $what ) for $key in $components=>map:keys() let $component := $components($key) let $subcomponents := head(( $component("components-fn"), function-lookup(QName($component("namespace"), "components"), 0) )) return ( for $kind in $what return switch($kind) case "metadata" return ( let $fn := head(( $component("metadata-fn"), function-lookup(QName($component("namespace"), "metadata"), 3) )) return ( if (exists($fn)) then () else errors:error("ART-MISSING-COMPONENT", ($fn,$what)) ) ) case "colophon" return ( let $fn := head(( $component("colophon-fn"), function-lookup(QName($component("namespace"), "colophon"), 1) )) return ( if (exists($fn)) then () else errors:error("ART-MISSING-COMPONENT", ($fn,$what)) ) ) case "algorithm-parameters" return ( let $fn := head(( $component("algorithm-parameters-fn"), function-lookup(QName($component("namespace"), "algorithm-parameters"), 2) )) return ( if (exists($fn)) then () else errors:error("ART-MISSING-COMPONENT", ($fn,$what)) ) ) case "algorithm-mode-parameters" return ( let $fn := head(( $component("algorithm-mode-parameters-fn"), function-lookup(QName($component("namespace"), "algorithm-mode-parameters"), 3) )) return ( if (exists($fn)) then () else errors:error("ART-MISSING-COMPONENT", ($fn,$what)) ) ) case "rendering-parameters" return ( let $add := head(($component("render"), true())) let $fn := if ($add) then ( head(( $component("rendering-parameters-fn"), function-lookup(QName($component("namespace"), "rendering-parameters"), 2) )) ) else () return ( if (exists($fn) and $add) then () else errors:error("ART-MISSING-COMPONENT", ($fn,$what)) ) ) case "randomizers" return ( let $fn := head(( $component("randomizers-fn"), function-lookup(QName($component("namespace"), "randomizers"), 2) )) return ( if (exists($fn)) then () else errors:error("ART-MISSING-COMPONENT", ($fn,$what)) ) ) default return () , if (empty($subcomponents)) then () else this:check-components($subcomponents(), $what) ) }
Errors
ART-MISSING-COMPONENT if component is malformed
Original Source Code
xquery version "3.1"; (:~ : Component merging part of art execution framework. : Copyright© Mary Holstege 2020-2022 : CC-BY (https://creativecommons.org/licenses/by/4.0/) : @since March 2022 : @custom:Status Stable :) module namespace this="http://mathling.com/art/components"; import module namespace errors="http://mathling.com/core/errors" at "../core/errors.xqy"; import module namespace util="http://mathling.com/core/utilities" at "../core/utilities.xqy"; declare namespace svg="http://www.w3.org/2000/svg"; declare namespace art="http://mathling.com/art"; declare namespace map="http://www.w3.org/2005/xpath-functions/map"; declare namespace array="http://www.w3.org/2005/xpath-functions/array"; declare namespace math="http://www.w3.org/2005/xpath-functions/math"; (:~ : expand() : Workaround for function visibility issue with XSLT packages. Included : in the XQuery, which doesn't otherwise need it, to allow for : single-sourcing. The idea is that the component user calls : this inside their components() method to do the function lookup, in a : context where their methods are in scope so that it can succeed. The : lookup function needs to be defined in the scope of the component itself. : That means components need to be responsible for their own subcomponents : also. :) declare function this:expand( $components as map(xs:string,item()*), $lookup as function(xs:QName, xs:integer) as function(*)? ) as map(xs:string,item()*) { fold-left($components=>map:keys(), $components, function($components as map(xs:string,item()*), $key as xs:string) { let $component := $components($key) return ( $components=>map:put($key, $component=>map:put("namespace-fn", $lookup(QName($component("namespace"), "metadata"), 3) )=>map:put("colophon-fn", $lookup(QName($component("namespace"), "colophon"), 1) )=>map:put("algorithm-parameters-fn", $lookup(QName($component("namespace"), "algorithm-parameters"), 2) )=>map:put("algorithm-mode-parameters", $lookup(QName($component("namespace"), "algorithm-mode-parameters"), 3) )=>map:put("rendering-parameters-fn", $lookup(QName($component("namespace"), "rendering-parameters"), 2) )=>map:put("randomizers-fn", $lookup(QName($component("namespace"), "randomizers"), 2) )=>map:put("components-fn", $lookup(QName($component("namespace"), "components"), 0) ) ) ) } ) }; (:~ : rendering-parameters() : Accumulated rendering parameters for the set of components. : @param $components: map of components, a map from the component name to : the component properties, e.g. 'namespace', 'mode', and 'render'. : @param $canvas: drawing canvas : @param $algorithm-parameters: accumulated algorithm parameter bundle : @return Map of rendering parameters :) declare function this:rendering-parameters( $components as map(xs:string, map(xs:string,item()*)), $canvas as map(xs:string,item()*), $algorithm-parameters as map(xs:string, item()*) ) as map(xs:string, item()*) { fold-left( $components=>map:keys(), map {}, function($map as map(xs:string,item()*), $key as xs:string) as map(xs:string,item()*) { let $component := $components($key) let $add := head(($component("render"), true())) let $fn := if ($add) then ( head(( $component("rendering-parameters-fn"), function-lookup(QName($component("namespace"), "rendering-parameters"), 2) )) ) else () (: Causes failure with -TP let $subcomponents := if ($add) then ( head(( $component("components-fn"), function-lookup(QName($component("namespace"), "components"), 0) )) ) else () let $subparameters := if (empty($subcomponents)) then () else this:rendering-parameters($subcomponents(), $canvas, $algorithm-parameters) :) let $add-subcomponents := $add and exists( head(( $component("components-fn"), function-lookup(QName($component("namespace"), "components"), 0) )) ) return ( util:merge-into(( $map, if ($add-subcomponents) then ( this:rendering-parameters( head(( $component("components-fn"), function-lookup(QName($component("namespace"), "components"), 0) ))(), $canvas, $algorithm-parameters ) ) else (), (: $subparameters, :) if (empty($fn)) then () else $fn($canvas, $algorithm-parameters) )) ) } ) }; (:~ : algorithm-parameters() : Accumulated algorithm parameters for the set of components. : @param $components: map of components, a map from the component name to : the component properties, e.g. 'namespace', 'mode', and 'render'. : @param $resolution: drawing resolution (@see core.html) : @param $canvas: drawing canvas : @return Map of algorithm parameters :) declare function this:algorithm-parameters( $components as map(xs:string, map(xs:string,item()*)), $resolution as xs:string, $canvas as map(xs:string, item()*) ) as map(xs:string, item()*) { fold-left( $components=>map:keys(), map {}, function($map as map(xs:string,item()*), $key as xs:string) as map(xs:string,item()*) { let $component := $components($key) let $mod := head(($component("mode"), "default")) let $fn := head(( $component("algorithm-parameters-fn"), function-lookup(QName($component("namespace"), "algorithm-parameters"), 2) )) let $fnmode := head(( $component("algorithm-mode-parameters-fn"), function-lookup(QName($component("namespace"), "algorithm-mode-parameters"), 3) )) (: Causes failure with -TP let $subcomponents := head(( $component("components-fn"), function-lookup(QName($component("namespace"), "components"), 0) )) let $subparameters := if (empty($subcomponents)) then () else this:algorithm-parameters($subcomponents(), $resolution, $canvas) :) let $add-subcomponents := exists( head(( $component("components-fn"), function-lookup(QName($component("namespace"), "components"), 0) )) ) return ( util:merge-into(( $map, if ($add-subcomponents) then ( this:algorithm-parameters( head(( $component("components-fn"), function-lookup(QName($component("namespace"), "components"), 0) ))(), $resolution, $canvas) ) else (), (: $subparameters, :) if (empty($fn)) then () else ( $fn($resolution, $canvas), if (empty($fnmode)) then () else ( $fnmode(($component("mode"),"default")[1], $resolution, $canvas) ) ) )) ) } ) }; (:~ : randomizers() : Accumulated set of randomizer descriptors for the set of components. : @param $components: map of components, a map from the component name to : the component properties, e.g. 'namespace', 'mode', and 'render'. : @param $canvas: drawing canvas : @param $parameters: accumulated algorithm parameter bundle : @return Map of randomizers :) declare function this:randomizers( $components as map(xs:string, map(xs:string,item()*)), $canvas as map(xs:string,item()*), $parameters as map(xs:string,item()*) ) as map(xs:string,item()*) { fold-left( $components=>map:keys(), map {}, function($map as map(xs:string,item()*), $key as xs:string) as map(xs:string,item()*) { let $component := $components($key) let $fn := head(( $component("randomizers-fn"), function-lookup(QName($component("namespace"), "randomizers"), 2) )) (: Causes error with -TP let $subcomponents := head(( $component("components-fn"), function-lookup(QName($component("namespace"), "components"), 0) )) let $subrandomizers := if (empty($subcomponents)) then () else this:randomizers($subcomponents(), $canvas, $parameters) :) let $add-subcomponents := exists( head(( $component("components-fn"), function-lookup(QName($component("namespace"), "components"), 0) )) ) return ( util:merge-into(( $map, if ($add-subcomponents) then ( this:randomizers( head(( $component("components-fn"), function-lookup(QName($component("namespace"), "components"), 0) ))(), $canvas, $parameters) ) else (), (: $subrandomizers, :) if (empty($fn)) then () else $fn($canvas, $parameters) )) ) } ) }; (:~ : metadata() : Accumulated set of metadata for the set of components. Will collect : descriptions for all parameters and randomizers as well as whatever : additional metadata is in the component's metadata callback. : : @param $components: map of components, a map from the component name to : the component properties, e.g. 'namespace', 'mode', and 'render'. : @param $canvas: drawing canvas : @param $randomizers: accumulated randomizer bundle : @param $parameters: accumulated algorithm parameter bundle : @return Metadata elements :) declare function this:metadata( $components as map(xs:string, map(xs:string,item()*)), $canvas as map(xs:string,item()*), $randomizers as map(xs:string,item()*), $parameters as map(xs:string,item()*) ) { for $key in $components=>map:keys() let $component := $components($key) let $fn := head(( $component("metadata-fn"), function-lookup(QName($component("namespace"), "metadata"), 3) )) (: let $subcomponents := head(( $component("components-fn"), function-lookup(QName($component("namespace"), "components"), 0) )) let $submetadata := if (empty($subcomponents)) then () else this:metadata($subcomponents(), $canvas, $randomizers, $parameters) :) let $add-subcomponents := exists( head(( $component("components-fn"), function-lookup(QName($component("namespace"), "components"), 0) )) ) return ( <art:component name="{$key}">{ if (empty($fn)) then () else $fn($canvas, $randomizers, $parameters), if ($add-subcomponents) then ( this:metadata( head(( $component("components-fn"), function-lookup(QName($component("namespace"), "components"), 0) ))(), $canvas, $randomizers, $parameters ) ) else () (: $submetadata :) }</art:component> ) }; (:~ : colophons() : Accumulated colophon strings for the set of components. : : @param $components: map of components, a map from the component name to : the component properties, e.g. 'namespace', 'mode', and 'render'. : @param $parameters: accumulated algorithm parameter bundle : @return Sequence of descriptive strings :) declare function this:colophons( $components as map(xs:string, map(xs:string,item()*)), $parameters as map(xs:string,item()*) ) as xs:string* { for $key in $components=>map:keys() let $component := $components($key) let $fn := head(( $component("colophon-fn"), function-lookup(QName($component("namespace"), "colophon"), 1) )) (: let $subcomponents := head(( $component("components-fn"), function-lookup(QName($component("namespace"), "components"), 0) )) let $subcolophons := if (empty($subcomponents)) then () else this:colophons($subcomponents(), $parameters) :) let $add-subcomponents := exists( head(( $component("components-fn"), function-lookup(QName($component("namespace"), "components"), 0) )) ) return ( if (empty($fn)) then () else $fn($parameters), if ($add-subcomponents) then ( this:colophons( head(( $component("components-fn"), function-lookup(QName($component("namespace"), "components"), 0) ))(), $parameters ) ) else () (: $subcolophons :) ) }; (:~ : check-components() : Verify that component map properly refers to well-defined components. : That means that function-lookup can locate the callback functions for : the component's namespace. : : @param $components: map of components, a map from the component name to : the component properties, e.g. 'namespace', 'mode', and 'render'. : @param $what: which aspects to check, either "all", "basics", or some : sequence of "algorithm-parameters", "algorithm-mode-parameters", : "rendering-parameters", and "randomizers". "all" checks everything. : "basics" checks "algorithm-parameters", "rendering-parameters", and : "randomizers" : @return empty sequence : @error ART-MISSING-COMPONENT if component is malformed :) declare function this:check-components( $components as map(xs:string,item()*), $what as xs:string* ) as empty-sequence() { let $what := if ($what="all") then ( "metadata", "colophon", "algorithm-parameters", "algorithm-mode-parameters", "rendering-parameters", "randomizers" ) else if ($what="basics") then ( "algorithm-parameters", "rendering-parameters", "randomizers" ) else ( $what ) for $key in $components=>map:keys() let $component := $components($key) let $subcomponents := head(( $component("components-fn"), function-lookup(QName($component("namespace"), "components"), 0) )) return ( for $kind in $what return switch($kind) case "metadata" return ( let $fn := head(( $component("metadata-fn"), function-lookup(QName($component("namespace"), "metadata"), 3) )) return ( if (exists($fn)) then () else errors:error("ART-MISSING-COMPONENT", ($fn,$what)) ) ) case "colophon" return ( let $fn := head(( $component("colophon-fn"), function-lookup(QName($component("namespace"), "colophon"), 1) )) return ( if (exists($fn)) then () else errors:error("ART-MISSING-COMPONENT", ($fn,$what)) ) ) case "algorithm-parameters" return ( let $fn := head(( $component("algorithm-parameters-fn"), function-lookup(QName($component("namespace"), "algorithm-parameters"), 2) )) return ( if (exists($fn)) then () else errors:error("ART-MISSING-COMPONENT", ($fn,$what)) ) ) case "algorithm-mode-parameters" return ( let $fn := head(( $component("algorithm-mode-parameters-fn"), function-lookup(QName($component("namespace"), "algorithm-mode-parameters"), 3) )) return ( if (exists($fn)) then () else errors:error("ART-MISSING-COMPONENT", ($fn,$what)) ) ) case "rendering-parameters" return ( let $add := head(($component("render"), true())) let $fn := if ($add) then ( head(( $component("rendering-parameters-fn"), function-lookup(QName($component("namespace"), "rendering-parameters"), 2) )) ) else () return ( if (exists($fn) and $add) then () else errors:error("ART-MISSING-COMPONENT", ($fn,$what)) ) ) case "randomizers" return ( let $fn := head(( $component("randomizers-fn"), function-lookup(QName($component("namespace"), "randomizers"), 2) )) return ( if (exists($fn)) then () else errors:error("ART-MISSING-COMPONENT", ($fn,$what)) ) ) default return () , if (empty($subcomponents)) then () else this:check-components($subcomponents(), $what) ) };