Removed the statically-known- docs and types functions from the introspection module.

(:
 : Copyright 2006-2009 The FLWOR Foundation.
 :
 : Licensed under the Apache License, Version 2.0 (the "License");
 : you may not use this file except in compliance with the License.
 : You may obtain a copy of the License at
 :
 : http://www.apache.org/licenses/LICENSE-2.0
 :
 : Unless required by applicable law or agreed to in writing, software
 : distributed under the License is distributed on an "AS IS" BASIS,
 : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 : See the License for the specific language governing permissions and
 : limitations under the License.
:)

(:~
 : Intospection Module for the Static Context components.
 :
 : @see http://www.w3.org/TR/xquery/#id-xq-static-context-components
 :
 : @version 1.0
 :)

module namespace sctx = "http://www.zorba-xquery.com/modules/introspection/sctx";

(:~
 : The function returns true if a collection with the given QName has been created.
 :
 : @param $name The QName of the collection that is being checked.
 : @return true if the collection has been created and false otherwise.
 :)
declare function sctx:is-declared-collection($name as xs:QName) as xs:boolean external;

(:~
 : The function returns a sequence of QNames representing the collections that have
 : been previously created. The sequence will be empty if there are no collections.
 :
 : @return A sequence of QNames, one for each created collection, or an emtpy sequence.
 :)
declare function sctx:declared-collections() as xs:QName* external;

(:~
 : The function returns true if an index with the given QName has been declared.
 :
 : @param $name The QName of the index that is being checked.
 : @return true if the index has been declared and false otherwise.
 :)
declare function sctx:is-declared-index($name as xs:QName) as xs:boolean external;

(:~
 : The function returns a sequence of QNames representing the indexes that have
 : been previously created. The sequence will be empty if there are no indexes.
 :
 : @return A sequence of QNames, one for each created index, or an empty
 : sequence if none exist.
 :)
declare function sctx:declared-indexes() as xs:QName* external;

(:~
 : The function returns a sequence of QNames representing the integrity
 : constraints that have been declared, as described by the Zorba Data
 : Definition Language extension. The sequence will be empty if there are no
 : integrity constraints.
 :
 : @return A sequence of QNames, one for each declared integrity constraint,
 : or an empty sequence if none are declared.
 :)
declare function sctx:declared-integrity-constraints() as xs:QName* external;

(:~
 : The function checks if an integrity constraint with the given QName has
 : been declared.
 :
 : @param $name The QName of the constraint that is being checked.
 : @return true if the integrity constraint has been declared and false
 : otherwise.
 :)
declare function sctx:is-declared-integrity-constraint($name as xs:QName) as xs:boolean external;

(:~
 : The function returns a list of known statically known namespaces as prefixes
 : (fn, xml, xs, xsi, etc).
 : @return A sequence of xs:strings, one for each known namespace prefix.
 :
 : @see http://www.w3.org/TR/xquery/#dt-static-namespaces
 :)
declare function sctx:statically-known-namespaces() as xs:string* external;

(:~
 : For any statically know namespace prefix, the bound URI can be retreived
 : with the statically-known-namespace-bindings() function.
 :
 : @param $prefix The prefix of the known namespace, for which the bound URI
 : is being requested.
 : @return The URI bound to the prefix is returned, or an empty sequence if
 : the prefix is not known.
 :
 : @see http://www.w3.org/TR/xquery/#dt-static-namespaces
 :)
declare function sctx:statically-known-namespace-binding($prefix as xs:string) as xs:string? external;

(:~
 : The function returns the URI of the default function namespace.
 :
 : @return The URI of the of the default function namespace.
 :
 : @see http://www.w3.org/TR/xquery/#dt-def-fn-ns
 :)
declare function sctx:default-function-namespace() as xs:string
{
  sctx:statically-known-namespace-binding("fn")
};

(:~
 : The function returns the base URI.
 :
 : @return The base URI.
 :
 : @see http://www.w3.org/TR/xquery/#dt-base-uri
 :)
declare function sctx:base-uri() as xs:string?
{
  fn:static-base-uri()
};

(:~
 : The function returns the statically known default collection type. This
 : is the type of the sequence of nodes that would result from calling the
 : fn:collection function with no arguments.
 :
 : @return The type of the default collection.
 :
 : @see http://www.w3.org/TR/xquery/#dt-known-default-collection
 :)
declare function sctx:default-collection-type() as xs:string external;

(:~
 : The function return a sequence of QNames identifying declared variables
 : from the static context.
 :
 : @return A sequence of QNames, one for each variable.
 :
 : @see http://www.w3.org/TR/xquery/#dt-in-scope-variables
 :)
declare function sctx:in-scope-variables() as xs:QName* external;

(:~
 : The function returns a sequence containing the QNames of all defined
 : functions, which are available to be called from within an expression.
 :
 : @return A sequence for QNames identifying all functions.
 :
 : @see http://www.w3.org/TR/xquery/#dt-function-signature
 :)
declare function sctx:function-names() as xs:QName* external;

(:~
 : The function returns the number of arguments the given XQuery $function
 : takes.
 :
 : @param $function A QName identifying a function.
 : @return A sequence of zero or more integers, one for each overloaded version
 : of the given function. An empty sequence is returned if the function is not
 : defined.
 :
 : @see http://www.w3.org/TR/xquery/#dt-function-signature
 :)
declare function sctx:function-arguments-count($function as xs:QName) as xs:int* external;

(:~
 : The function returns true or false depending on whether rules for
 : compatibility with XPath 1.0 are in effect. XQuery set the value
 : of this component to false.
 :
 : @return The function should return false
 :
 : @see http://www.w3.org/TR/xquery/#dt-xpath-compat-mode
 :)
declare function sctx:xpath10-compatibility-mode() as xs:boolean external;

(:~
 : The function returns one of statically known collations that is used by
 : functions and operators for comparing and ordering values of type xs:string
 : or xs:anyURI when no explicit collation is specified.
 :
 : @return The collations that is used by default.
 :
 : @see http://www.w3.org/TR/xquery/#dt-def-collation
 :)
declare function sctx:default-collation() as xs:string
{
  fn:default-collation()
};

(:~
 : The function returns a sequence that contains the  statically known collations.
 :
 : @return The sequence of collations.
 :
 : @see http://www.w3.org/TR/xquery/#dt-static-collations
 :)
declare function sctx:statically-known-collations() as xs:anyURI* external;

(:~
 : The function returns the static context construction mode of element and
 : document nodes.
 :
 : @return Either "preserve" or "strip", as strings.
 :
 : @see http://www.w3.org/TR/xquery/#dt-construction-mode
 :)
declare function sctx:construction-mode() as xs:string external;

(:~
 : The function returns the static context ordering mode, which affects the
 : ordering of the result sequences returned by certain expressions.
 :
 : @return Either "ordered" or "unordered", as strings.
 :
 : @see http://www.w3.org/TR/xquery/#dt-ordering-mode
 :)
declare function sctx:ordering-mode() as xs:string external;

(:~
 : The function returns the static context component which controls the
 : processing of empty sequences and NaN values as ordering keys in an order
 : by clause in a FLWOR expression.
 :
 : @return Either "greatest" or "least", as strings.
 :
 : @see http://www.w3.org/TR/xquery/#dt-default-empty-order
 :)
declare function sctx:default-order() as xs:string external;

(:~
 : The function returns the static context component which contrls the
 : processing of boundary whitespace by direct element constructors.
 :
 : @return Either "preserve" or "strip", as strings.
 :
 : @see http://www.w3.org/TR/xquery/#dt-boundary-space-policy
 :)
declare function sctx:boundary-space-policy() as xs:string external;

(:~
 : The function returns the static context components which control the
 : the namespace bindings that are assigned when an existing element node
 : is copied by an element constructor
 :
 : @return A sequence containing exactly two strings, of which the first
 : has a value of either "preserve" or "no-preserve" and the second a value
 : of either "inherit" or "no-inherit".
 :
 : @see http://www.w3.org/TR/xquery/#dt-copy-namespaces-mode
 :)
declare function sctx:copy-namespaces-mode() as xs:string+ external;

(:
 : Copyright 2006-2009 The FLWOR Foundation.
 :
 : Licensed under the Apache License, Version 2.0 (the "License");
 : you may not use this file except in compliance with the License.
 : You may obtain a copy of the License at
 :
 : http://www.apache.org/licenses/LICENSE-2.0
 :
 : Unless required by applicable law or agreed to in writing, software
 : distributed under the License is distributed on an "AS IS" BASIS,
 : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 : See the License for the specific language governing permissions and
 : limitations under the License.
:)

(:~
 : Intospection Module for the Static Context components.
 :
 : @see http://www.w3.org/TR/xquery/#id-xq-static-context-components
 :
 : @version 1.0
 :)

module namespace sctx = "http://www.zorba-xquery.com/modules/introspection/sctx";

(:~
 : The function returns true if a collection with the given QName has been created.
 :
 : @param $name The QName of the collection that is being checked.
 : @return true if the collection has been created and false otherwise.
 :)
declare function sctx:is-declared-collection($name as xs:QName) as xs:boolean external;

(:~
 : The function returns a sequence of QNames representing the collections that have
 : been previously created. The sequence will be empty if there are no collections.
 :
 : @return A sequence of QNames, one for each created collection, or an emtpy sequence.
 :)
declare function sctx:declared-collections() as xs:QName* external;

(:~
 : The function returns true if an index with the given QName has been declared.
 :
 : @param $name The QName of the index that is being checked.
 : @return true if the index has been declared and false otherwise.
 :)
declare function sctx:is-declared-index($name as xs:QName) as xs:boolean external;

(:~
 : The function returns a sequence of QNames representing the indexes that have
 : been previously created. The sequence will be empty if there are no indexes.
 :
 : @return A sequence of QNames, one for each created index, or an empty
 : sequence if none exist.
 :)
declare function sctx:declared-indexes() as xs:QName* external;

(:~
 : The function returns a sequence of QNames representing the integrity
 : constraints that have been declared, as described by the Zorba Data
 : Definition Language extension. The sequence will be empty if there are no
 : integrity constraints.
 :
 : @return A sequence of QNames, one for each declared integrity constraint,
 : or an empty sequence if none are declared.
 :)
declare function sctx:declared-integrity-constraints() as xs:QName* external;

(:~
 : The function checks if an integrity constraint with the given QName has
 : been declared.
 :
 : @param $name The QName of the constraint that is being checked.
 : @return true if the integrity constraint has been declared and false
 : otherwise.
 :)
declare function sctx:is-declared-integrity-constraint($name as xs:QName) as xs:boolean external;

(:~
 : The function returns a list of known statically known namespaces as prefixes
 : (fn, xml, xs, xsi, etc).
 : @return A sequence of xs:strings, one for each known namespace prefix.
 :
 : @see http://www.w3.org/TR/xquery/#dt-static-namespaces
 :)
declare function sctx:statically-known-namespaces() as xs:string* external;

(:~
 : For any statically know namespace prefix, the bound URI can be retreived
 : with the statically-known-namespace-bindings() function.
 :
 : @param $prefix The prefix of the known namespace, for which the bound URI
 : is being requested.
 : @return The URI bound to the prefix is returned, or an empty sequence if
 : the prefix is not known.
 :
 : @see http://www.w3.org/TR/xquery/#dt-static-namespaces
 :)
declare function sctx:statically-known-namespace-binding($prefix as xs:string) as xs:string? external;

(:~
 : The function returns the URI of the default function namespace.
 :
 : @return The URI of the of the default function namespace.
 :
 : @see http://www.w3.org/TR/xquery/#dt-def-fn-ns
 :)
declare function sctx:default-function-namespace() as xs:string
{
  sctx:statically-known-namespace-binding("fn")
};

(:~
 : The function returns the base URI.
 :
 : @return The base URI.
 :
 : @see http://www.w3.org/TR/xquery/#dt-base-uri
 :)
declare function sctx:base-uri() as xs:string?
{
  fn:static-base-uri()
};

(:~
 : The function returns the statically known default collection type. This
 : is the type of the sequence of nodes that would result from calling the
 : fn:collection function with no arguments.
 :
 : @return The type of the default collection.
 :
 : @see http://www.w3.org/TR/xquery/#dt-known-default-collection
 :)
declare function sctx:default-collection-type() as xs:string external;

(:~
 : The function return a sequence of QNames identifying declared variables
 : from the static context.
 :
 : @return A sequence of QNames, one for each variable.
 :
 : @see http://www.w3.org/TR/xquery/#dt-in-scope-variables
 :)
declare function sctx:in-scope-variables() as xs:QName* external;

(:~
 : The function returns a sequence containing the QNames of all defined
 : functions, which are available to be called from within an expression.
 :
 : @return A sequence for QNames identifying all functions.
 :
 : @see http://www.w3.org/TR/xquery/#dt-function-signature
 :)
declare function sctx:function-names() as xs:QName* external;

(:~
 : The function returns the number of arguments the given XQuery $function
 : takes.
 :
 : @param $function A QName identifying a function.
 : @return A sequence of zero or more integers, one for each overloaded version
 : of the given function. An empty sequence is returned if the function is not
 : defined.
 :
 : @see http://www.w3.org/TR/xquery/#dt-function-signature
 :)
declare function sctx:function-arguments-count($function as xs:QName) as xs:int* external;

(:~
 : The function returns the potential static type of the result of fn:doc(),
 : if it would be invoked with the given $uri. If the argument to fn:doc() is
 : string literal that is not present in statically known documents, then the
 : static type of fn:doc is "document-node()?"
 :
 : @param $uri The URI of the document that is being checked.
 : @return The static type of a potential result of fn:doc() invoked with the
 : given URI.
 :
 : @see http://www.w3.org/TR/xquery/#dt-known-docs
 :)
declare function sctx:statically-known-documents($uri as xs:string) as xs:QName external;

(:~
 : The function returns true or false depending on whether rules for
 : compatibility with XPath 1.0 are in effect. XQuery set the value
 : of this component to false.
 :
 : @return The function should return false
 :
 : @see http://www.w3.org/TR/xquery/#dt-xpath-compat-mode
 :)
declare function sctx:xpath10-compatibility-mode() as xs:boolean external;

(:~
 : The function returns one of statically known collations that is used by
 : functions and operators for comparing and ordering values of type xs:string
 : or xs:anyURI when no explicit collation is specified.
 :
 : @return The collations that is used by default.
 :
 : @see http://www.w3.org/TR/xquery/#dt-def-collation
 :)
declare function sctx:default-collation() as xs:string
{
  fn:default-collation()
};

(:~
 : The function returns a sequence that contains the  statically known collations.
 :
 : @return The sequence of collations.
 :
 : @see http://www.w3.org/TR/xquery/#dt-static-collations
 :)
declare function sctx:statically-known-collations() as xs:anyURI* external;

(:~
 : The function returns the static context construction mode of element and
 : document nodes.
 :
 : @return Either "preserve" or "strip", as strings.
 :
 : @see http://www.w3.org/TR/xquery/#dt-construction-mode
 :)
declare function sctx:construction-mode() as xs:string external;

(:~
 : The function returns the static context ordering mode, which affects the
 : ordering of the result sequences returned by certain expressions.
 :
 : @return Either "ordered" or "unordered", as strings.
 :
 : @see http://www.w3.org/TR/xquery/#dt-ordering-mode
 :)
declare function sctx:ordering-mode() as xs:string external;

(:~
 : The function returns the static context component which controls the
 : processing of empty sequences and NaN values as ordering keys in an order
 : by clause in a FLWOR expression.
 :
 : @return Either "greatest" or "least", as strings.
 :
 : @see http://www.w3.org/TR/xquery/#dt-default-empty-order
 :)
declare function sctx:default-order() as xs:string external;

(:~
 : The function returns the static context component which contrls the
 : processing of boundary whitespace by direct element constructors.
 :
 : @return Either "preserve" or "strip", as strings.
 :
 : @see http://www.w3.org/TR/xquery/#dt-boundary-space-policy
 :)
declare function sctx:boundary-space-policy() as xs:string external;

(:~
 : The function returns the static context components which control the
 : the namespace bindings that are assigned when an existing element node
 : is copied by an element constructor
 :
 : @return A sequence containing exactly two strings, of which the first
 : has a value of either "preserve" or "no-preserve" and the second a value
 : of either "inherit" or "no-inherit".
 :
 : @see http://www.w3.org/TR/xquery/#dt-copy-namespaces-mode
 :)
declare function sctx:copy-namespaces-mode() as xs:string+ external;


Some improvements to the test script.

Some improvements to the test script.

svn path=/trunk/eXist-1.0/; revision=2737

declare namespace xqts="http://exist-db.org/xquery/xqts";
declare namespace catalog="http://www.w3.org/2005/02/query-test-XQTSCatalog";

declare namespace props="java:java.io.File";

import module namespace util="http://exist-db.org/xquery/util";
import module namespace xdb="http://exist-db.org/xquery/xmldb";
import module namespace xdiff="http://exist-db.org/xquery/xmldiff";

(:~ ----------------------------------------------------------------------------------------
     W3C XQuery Test Suite

     This is the main module for running the XQTS on eXist. You can either
     run the whole test suite, a specific test group or a single test case.

     Setup:

     * Make sure the XmlDiff module is registered in conf.xml

     * Edit config.xml and change basedir to point to the directory into
     which you unzipped the XQTS sources. Note: it needs to be an URI.

     * Create a collection /db/XQTS in the database.

     * From the XQTS directory, upload XQTSCatalog.xml into the created
     collection.

     * Upload the "TestSources" directory so the source docs can be found
     in /db/XQTS/TestSources.

     * Run this script with the client.
     ------------------------------------------------------------------------------------------- :)
declare variable $xqts:CONFIG { xqts:initialize() };
declare variable $xqts:XQTS_HOME { $xqts:CONFIG/basedir/text() };

declare function xqts:initialize() as element() {
    let $collection := xdb:create-collection("/db", "XQTS")
    let $config := doc("/db/XQTS")/config
    return
        if ($config) then
            $config
        else
            let $home := util:system-property("exist.home")
            let $path := concat($home, "/tools/XQTS")
            let $stored := xdb:store-files-from-pattern("/db/XQTS", $path, "*.xml", "text/xml")
            return
                doc("/db/XQTS/config.xml")/config
};

declare function xqts:create-collections($group as element(catalog:test-group)) as node() {
    let $rootColl := xdb:create-collection("/db/XQTS", "test-results")
    let $ancestors := reverse(($group/ancestor::catalog:test-group, $group))
    let $collection := xqts:create-collections($rootColl, $ancestors, "/db/XQTS/test-results")
    let $results := xdb:store($collection, "results.xml", <test-result failed="0" passed="0"/>)
    return
        doc($results)
};

declare function xqts:create-collections($parent as object,
    $pathElements as element()+, $currentPath as xs:string)
as object {
    let $next := $pathElements[last()]
    let $remainder := subsequence($pathElements, 1, count($pathElements) - 1)
    let $newColl := xdb:create-collection($parent, $next/@name)
    return
        if ($remainder) then
            xqts:create-collections($newColl, $remainder, concat($currentPath, "/", $next/@name))
        else
            $newColl
};

declare function xqts:get-query($case as element(catalog:test-case)) {
   let $query-name := $case//catalog:query/@name
   let $path := concat( $xqts:XQTS_HOME, "Queries/XQuery/", $case/@FilePath, $query-name, ".xq" )
   let $xq-string := util:file-read($path)
   return $xq-string
};

declare function xqts:print-result($test-name as xs:string, $passed as xs:boolean, $query as xs:string,
    $result as item()*, $expected as item()*, $case as element(catalog:test-case)) as element() {
    <test-case name="{$test-name}" result="{if ($passed) then 'pass' else 'fail'}">
    {
        if (not($passed)) then (
            <result>{$result}</result>,
            if ($expected instance of element() and count($expected/*) > 10) then
            	<expected truncated="">{$expected/*[position() < 10]}</expected>
            else
                <expected>{$expected}</expected>,
            (: element {$case/catalog:input-file/@variable} { xqts:getInputValue($case) }, :)
            <query>{$query}</query>
        ) else ()
    }
    </test-case>
};

declare function xqts:normalize-text($result as item()*) as xs:string {
    let $str := string-join($result, " ")
    return
        (: Remove leading and trailing whitespace :)
        replace(replace($str, "^\s+", ""), "\s+$", "")
};

declare function xqts:check-output($query as xs:string, $result as item()*, $case as element(catalog:test-case)) {
    let $output := $case/catalog:output-file[last()]
    return
        (: Expected an error, but got a result :)
        if (exists($case/catalog:expected-error)) then
            <test-case name="{$case/@name}" result="fail">
                   <expected-error>{$case/catalog:expected-error/text()}</expected-error>
                   <query>{$query}</query>
            </test-case>
        (: Comparison method: "Text" :)
        else if ($output/@compare eq "Text") then
            let $text := util:file-read(concat($xqts:XQTS_HOME, "ExpectedTestResults/", $case/@FilePath,
                "/", $output/text()))
            let $test := xqts:normalize-text($text) eq xqts:normalize-text($result)
            return
                xqts:print-result($case/@name, $test, $query, $result, $text, $case)
        (: Comparison method: "XML" :)
        else if ($output/@compare eq "XML") then
            util:catch("java.lang.Exception",
                let $filePath := concat($xqts:XQTS_HOME, "ExpectedTestResults/", $case/@FilePath,
                    $output/text())
                let $expected := doc(xdb:store("/db", "temp.xml", xs:anyURI($filePath), "text/xml"))
                let $test := xdiff:compare($expected, $result)
                return
                    xqts:print-result($case/@name, $test, $query, $result, $expected, $case),
                (: Handle unexpected exceptions :)
                <test-case name="{$case/@name}" result="fail">
                   <exception>Exception while loading expected result: {$util:exception-message}</exception>
                   <query>{$query}</query>
                </test-case>
            )
        (: Comparison method: "Fragment" :)
        else if ($output/@compare eq "Fragment") then
            util:catch("java.lang.Exception",
                let $filePath := concat($xqts:XQTS_HOME, "ExpectedTestResults/", $case/@FilePath, $output/text())
                let $expectedFrag := util:file-read($filePath)
                let $xmlFrag := concat("<f>", $expectedFrag, "</f>")
                let $log := util:log("DEBUG", ("Frag stored: ", $xmlFrag))
                let $expected := doc(xdb:store("/db", "temp.xml", $xmlFrag, "text/xml"))
                let $test := xdiff:compare($expected, <f>{$result}</f>)
                return
                    xqts:print-result($case/@name, $test, $query, $result, $expected, $case),
                (: Handle unexpected exceptions :)
                <test-case name="{$case/@name}" result="fail">
                   <exception>Exception while loading expected result fragment: {$util:exception-message}</exception>
                   <query>{$query}</query>
                </test-case>
            )
        (: Don't know how to compare :)
        else
            <error test="{$case/@name}">Unknown comparison method: {$output/@compare}.</error>
};

declare function xqts:run-test-case( $case as element(catalog:test-case)) as item()* {
   let $query := xqts:get-query($case)
   let $context :=
       <static-context>
       {
           for $input in $case/catalog:input-file
           return
               <variable name="{$input/@variable}">{xqts:get-input-value($input)}</variable>
       }
       </static-context>
   let $result :=
       util:catch("java.lang.Exception",
           let $result :=
               util:eval-with-context($query, $context, false())
           return
               xqts:check-output($query, $result, $case),
           if (exists($case/catalog:expected-error)) then
               <test-case name="{$case/@name}" result="pass"/>
           else
               <test-case name="{$case/@name}" result="fail">
                   <exception>{$util:exception-message}</exception>
                   <query>{$query}</query>
               </test-case>
       )
   return $result
};

declare function xqts:finish($result as element()) as empty() {
    let $passed := count($result//test-case[@result = 'pass'])
    let $failed := count($result//test-case[@result = 'fail'])
    return (
        update value $result/@passed with xs:int($passed),
        update value $result/@failed with xs:int($failed)
    )
};

declare function xqts:run-single-test-case($case as element(catalog:test-case),
    $resultRoot as element()?) as empty() {
    let $result := xqts:run-test-case($case)
    return
        update insert $result into $resultRoot
};

declare function xqts:run-test-group($group as element(catalog:test-group)) as empty() {
    (: Create the collection hierarchy for this group and get the results.xml doc to
        append to. :)
    let $resultsDoc := xqts:create-collections($group)
    let $tests := $group/catalog:test-case
    return (
        (: Execute the test cases :)
        for $test in $tests
        let $log := util:log("DEBUG", ("Running test case: ", string($test/@name)))
        return
            xqts:run-single-test-case($test, $resultsDoc/test-result),
        if ($tests) then
            xqts:finish($resultsDoc/test-result)
        else
            xdb:remove(util:collection-name($resultsDoc), util:document-name($resultsDoc)),
        (: Execute tests in child groups :)
        for $childGroup in $group/catalog:test-group
        let $log := util:log("DEBUG", ("Entering group: ", string($childGroup/@name)))
        return
            xqts:run-test-group($childGroup)
    )
};

declare function xqts:test-single($name as xs:string) as element() {
    let $test := //catalog:test-case[@name = $name]
    return
        if ($test) then
            let $resultsDoc := xqts:create-collections($test/parent::catalog:test-group)
            let $dummy := xqts:run-single-test-case($test, $resultsDoc/test-result)
            return
                $resultsDoc/test-result
        else
            <error>Test case not found: {$name}.</error>
};

declare function xqts:test-group($groupName as xs:string) as empty() {
    let $group := //catalog:test-group[@name = $groupName]
    return
        xqts:run-test-group($group)
};

declare function xqts:test-all() as empty() {
    for $test in /catalog:test-suite/catalog:test-group
    return
        xqts:test-group($test/@name)
};

declare function xqts:get-input-value($input as element(catalog:input-file)) as item()* {
   if ($input eq "emptydoc") then
       ()
   else (
       let $source := root($input)//catalog:source[@ID = $input/text()]
       return
           if (empty($source)) then
               concat("no input found: ", $input/text())
           else
               doc(concat("/db/XQTS/", $source/@FileName))
   )
};

declare function xqts:overall-result() {
    let $passed := count(//test-case[@result = "pass"])
    let $failed := count(//test-case[@result = "fail"])
    return
        <test-result failed="{$failed}" passed="{$passed}"
            percentage="{$passed div ($passed + $failed)}"/>
};

(: xqts:test-single("op-subtract-dateTimes-yielding-DTD-1"), :)
xqts:test-group("Expressions"),
collection("/db/XQTS")/test-result,
xqts:overall-result()

declare namespace xqts="http://exist-db.org/xquery/xqts";
declare namespace catalog="http://www.w3.org/2005/02/query-test-XQTSCatalog";

declare namespace props="java:java.io.File";

import module namespace util="http://exist-db.org/xquery/util";
import module namespace xdb="http://exist-db.org/xquery/xmldb";
import module namespace xdiff="http://exist-db.org/xquery/xmldiff";

(:~ ----------------------------------------------------------------------------------------
     W3C XQuery Test Suite

     This is the main module for running the XQTS on eXist. You can either
     run the whole test suite, a specific test group or a single test case.

     Setup:

     * Make sure the XmlDiff module is registered in conf.xml

     * Edit config.xml and change basedir to point to the directory into
     which you unzipped the XQTS sources. Note: it needs to be an URI.

     * Create a collection /db/XQTS in the database.

     * From the XQTS directory, upload XQTSCatalog.xml into the created
     collection.

     * Upload the "TestSources" directory so the source docs can be found
     in /db/XQTS/TestSources.

     * Run this script with the client.
     ------------------------------------------------------------------------------------------- :)
declare variable $xqts:CONFIG { xqts:initialize() };
declare variable $xqts:XQTS_HOME { $xqts:CONFIG/basedir/text() };

declare function xqts:initialize() as element() {
    let $collection := xdb:create-collection("/db", "XQTS")
    let $config := doc("/db/XQTS")/config
    return
        if ($config) then
            $config
        else
            let $home := util:system-property("exist.home")
            let $path := concat($home, "/tools/XQTS")
            let $stored := xdb:store-files-from-pattern("/db/XQTS", $path, "*.xml", "text/xml")
            return
                doc("/db/XQTS/config.xml")/config
};

declare function xqts:create-collections($group as element(catalog:test-group)) as node() {
    let $rootColl := xdb:create-collection("/db/XQTS", "test-results")
    let $ancestors := reverse(($group/ancestor::catalog:test-group, $group))
    let $collection := xqts:create-collections($rootColl, $ancestors, "/db/XQTS/test-results")
    let $results := xdb:store($collection, "results.xml", <test-result failed="0" passed="0"/>)
    return
        doc($results)
};

declare function xqts:create-collections($parent as object,
    $pathElements as element()+, $currentPath as xs:string)
as object {
    let $next := $pathElements[last()]
    let $remainder := subsequence($pathElements, 1, count($pathElements) - 1)
    let $newColl := xdb:create-collection($parent, $next/@name)
    return
        if ($remainder) then
            xqts:create-collections($newColl, $remainder, concat($currentPath, "/", $next/@name))
        else
            $newColl
};

declare function xqts:get-query($case as element(catalog:test-case)) {
   let $query-name := $case//catalog:query/@name
   let $path := concat( $xqts:XQTS_HOME, "Queries/XQuery/", $case/@FilePath, $query-name, ".xq" )
   let $xq-string := util:file-read($path)
   return $xq-string
};

declare function xqts:print-result($test-name as xs:string, $passed as xs:boolean, $query as xs:string,
    $result as item()*, $expected as item()*, $case as element(catalog:test-case)) as element() {
    <test-case name="{$test-name}" result="{if ($passed) then 'pass' else 'fail'}">
    {
        if (not($passed)) then (
            <result>{$result}</result>,
            if ($expected instance of element() and count($expected/*) > 10) then
            	<expected truncated="">{$expected/*[position() < 10]}</expected>
            else
                <expected>{$expected}</expected>,
            (: element {$case/catalog:input-file/@variable} { xqts:getInputValue($case) }, :)
            <query>{$query}</query>
        ) else ()
    }
    </test-case>
};

declare function xqts:normalize-text($result as item()*) as xs:string {
    let $str := string-join($result, " ")
    return
        (: Remove leading and trailing whitespace :)
        replace(replace($str, "^\s+", ""), "\s+$", "")
};

declare function xqts:check-output($query as xs:string, $result as item()*, $case as element(catalog:test-case)) {
    let $output := $case/catalog:output-file[last()]
    return
        (: Comparison method: "Text" :)
        if ($output/@compare eq "Text") then
            let $text := util:file-read(concat($xqts:XQTS_HOME, "ExpectedTestResults/", $case/@FilePath,
                "/", $output/text()))
            let $test := xqts:normalize-text($text) eq xqts:normalize-text($result)
            return
                xqts:print-result($case/@name, $test, $query, $result, $text, $case)
        (: Comparison method: "XML" :)
        else if ($output/@compare eq "XML") then
            let $filePath := concat($xqts:XQTS_HOME, "ExpectedTestResults/", $case/@FilePath,
                $output/text())
            let $expected := doc(xdb:store("/db", "temp.xml", xs:anyURI($filePath), "text/xml"))
            let $test := xdiff:compare($expected, $result)
            return
                xqts:print-result($case/@name, $test, $query, $result, $expected, $case)
        (: Comparison method: "Fragment" :)
        else if ($output/@compare eq "Fragment") then
            let $filePath := concat($xqts:XQTS_HOME, "ExpectedTestResults/", $case/@FilePath, $output/text())
            let $expectedFrag := util:file-read($filePath)
            let $xmlFrag := concat("<f>", $expectedFrag, "</f>")
            let $log := util:log("DEBUG", ("Frag stored: ", $xmlFrag))
            let $expected := doc(xdb:store("/db", "temp.xml", $xmlFrag, "text/xml"))
            let $test := xdiff:compare($expected, <f>{$result}</f>)
            return
                xqts:print-result($case/@name, $test, $query, $result, $expected, $case)
        (: Don't know how to compare :)
        else
            <error test="{$case/@name}">Unknown comparison method: {$output/@compare}.</error>
};

declare function xqts:run-test-case( $case as element(catalog:test-case)) as item()* {
   let $query := xqts:get-query($case)
   let $context :=
       <static-context>
       {
           for $input in $case/catalog:input-file
           return
               <variable name="{$input/@variable}">{xqts:get-input-value($input)}</variable>
       }
       </static-context>
   let $result :=
       util:catch("java.lang.Exception",
           let $result :=
               util:eval-with-context($query, $context, false())
           return
               xqts:check-output($query, $result, $case),
           if (exists($case/catalog:expected-error)) then
               <test-case name="{$case/@name}" result="pass"/>
           else
               <test-case name="{$case/@name}" result="fail">
                   <exception>{$util:exception-message}</exception>
                   <query>{$query}</query>
               </test-case>
       )
   return $result
};

declare function xqts:finish($result as element()) as empty() {
    let $passed := count($result//test-case[@result = 'pass'])
    let $failed := count($result//test-case[@result = 'fail'])
    return (
        update value $result/@passed with xs:int($passed),
        update value $result/@failed with xs:int($failed)
    )
};

declare function xqts:run-single-test-case($case as element(catalog:test-case),
    $resultRoot as element()?) as empty() {
    let $result := xqts:run-test-case($case)
    return
        update insert $result into $resultRoot
};

declare function xqts:run-test-group($group as element(catalog:test-group)) as empty() {
    (: Create the collection hierarchy for this group and get the results.xml doc to
        append to. :)
    let $resultsDoc := xqts:create-collections($group)
    let $tests := $group/catalog:test-case
    return (
        (: Execute the test cases :)
        for $test in $tests
        let $log := util:log("DEBUG", ("Running test case: ", string($test/@name)))
        return
            xqts:run-single-test-case($test, $resultsDoc/test-result),
        if ($tests) then
            xqts:finish($resultsDoc/test-result)
        else
            xdb:remove(util:collection-name($resultsDoc), util:document-name($resultsDoc)),
        (: Execute tests in child groups :)
        for $childGroup in $group/catalog:test-group
        let $log := util:log("DEBUG", ("Entering group: ", string($childGroup/@name)))
        return
            xqts:run-test-group($childGroup)
    )
};

declare function xqts:test-single($name as xs:string) as element() {
    let $test := //catalog:test-case[@name = $name]
    return
        if ($test) then
            let $resultsDoc := xqts:create-collections($test/parent::catalog:test-group)
            let $dummy := xqts:run-single-test-case($test, $resultsDoc/test-result)
            return
                $resultsDoc/test-result/test-case
        else
            util:log("WARN", ("Test case not found: ", $name))
};

declare function xqts:test-group($groupName as xs:string) as empty() {
    let $group := //catalog:test-group[@name = $groupName]
    return
        xqts:run-test-group($group)
};

declare function xqts:test-all() as empty() {
    for $test in /catalog:test-suite/catalog:test-group
    return
        xqts:test-group($test/@name)
};

declare function xqts:get-input-value($input as element(catalog:input-file)) as item()* {
   if ($input eq "emptydoc") then
       ()
   else (
       let $source := root($input)//catalog:source[@ID = $input/text()]
       return
           if (empty($source)) then
               concat("no input found: ", $input/text())
           else
               doc(concat("/db/XQTS/", $source/@FileName))
   )
};

(: xqts:test-single("Axes066-2") :)
xqts:test-group("PathExpr"),
collection("/db/XQTS")/test-result


Handle xsi:type.

Handle xsi:type.

xquery version "3.1" encoding "UTF-8";

declare namespace output = "http://www.w3.org/2010/xslt-xquery-serialization";
declare namespace map = "http://www.w3.org/2005/xpath-functions/map";
declare namespace array = "http://www.w3.org/2005/xpath-functions/array";
declare namespace mapping = "http://docs.rackspace.com/identity/api/ext/MappingRules";
declare namespace xs = "http://www.w3.org/2001/XMLSchema";
declare namespace xsi = "http://www.w3.org/2001/XMLSchema-instance";

declare default element namespace "http://docs.rackspace.com/identity/api/ext/MappingRules";

declare option output:method "xml";
declare option output:indent "yes";

(: declare variable $__JSON__ external; :)
declare variable $__JSON__ := "{"rules":[{"remote":[{"regex":false,"name":"bar","multiValue":false},{"path":"'AWSPolicy'","regex":false,"multiValue":false}],"local":{"user":{"domain":"{D}","foo":"{0}","name":"{D}","email":"{D}","roles":"{D}","expire":"{D}"},"faws":{"policy":"{1}"}}},{"local":{"user":{},"faws":{"policy":"AWSPolicy2"}}}]}";
declare variable $policyJSON := parse-json($__JSON__);
declare variable $defaultAttributes as xs:string* := ('name','email','expire','domain','roles');

declare function mapping:convertValue($in as xs:string) as xs:string {
  replace($in,' ',' ')
};

declare function mapping:attributeFromValue ($attName as xs:string, $value as item()) as attribute()* {
  typeswitch ($value)
    case $s as xs:string return attribute {$attName} {mapping:convertValue($s)}
    case $b as xs:boolean return attribute {$attName} {mapping:convertValue(string($b))}
    case $a as array(*) return attribute {$attName} {string-join(for $i in $a?* return mapping:convertValue($i),' ')}
    case $o as map(*) return for $att in map:keys($o) return mapping:attributeFromValue($att, $o?($att))
    default return ()
};

declare function mapping:convertLocalAttribute($attribName as xs:string, $attribValue as item(), $userGroup as xs:boolean) as element() {
  element {$attribName} {
    mapping:attributeFromValue("value",$attribValue),
    if ($userGroup and $attribName = $defaultAttributes) then () else attribute {"xsi:type"} {"LocalAttribute"}
  }
};

<rules xmlns="http://docs.rackspace.com/identity/api/ext/MappingRules"
       xmlns:xs="http://www.w3.org/2001/XMLSchema"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
       {
       if (map:contains($policyJSON,"namespaces")) then
         let $namespaces := $policyJSON?namespaces
         for $key in map:keys($namespaces) return namespace {$key} {$namespaces?($key)}
       else ()
       }

       {
         for $rule in $policyJSON?rules?* return
         <rule>
             <local>
                 {
                   let $local := $rule?local
                   for $localGroupName in map:keys($local) return element {$localGroupName} {
                     if ($localGroupName != 'user') then attribute {"xsi:type"} {"LocalAttributeGroup"} else (),
                     let $localGroup := $local?($localGroupName)
                     for $attributeName in map:keys($localGroup) return
                       mapping:convertLocalAttribute($attributeName, $localGroup?($attributeName), $localGroupName = 'user')
                   }
                 }
             </local>
         </rule>
       }
</rules>

xquery version "3.1" encoding "UTF-8";

declare namespace output = "http://www.w3.org/2010/xslt-xquery-serialization";
declare namespace map = "http://www.w3.org/2005/xpath-functions/map";
declare namespace array = "http://www.w3.org/2005/xpath-functions/array";
declare namespace mapping = "http://docs.rackspace.com/identity/api/ext/MappingRules";
declare namespace xs = "http://www.w3.org/2001/XMLSchema";

declare default element namespace "http://docs.rackspace.com/identity/api/ext/MappingRules";

declare option output:method "xml";
declare option output:indent "yes";

(: declare variable $__JSON__ external; :)
declare variable $__JSON__ := "{"rules":[{"local":{"user":{"domain":"{D}","name":"{D}","email":"{D(name)}@rackspace.com","roles":"{D}","expire":"{D}"}}}],"namespaces":{"foo":"bar"}}";
declare variable $policyJSON := parse-json($__JSON__);

declare function mapping:convertValue($in as xs:string) as xs:string {
  replace($in,' ',' ')
};

declare function mapping:attributeFromValue ($attName as xs:string, $value as item()) as attribute()* {
  typeswitch ($value)
    case $s as xs:string return attribute {$attName} {mapping:convertValue($s)}
    case $b as xs:boolean return attribute {$attName} {mapping:convertValue(string($b))}
    case $a as array(*) return attribute {$attName} {string-join(for $i in $a?* return mapping:convertValue($i),' ')}
    case $o as map(*) return for $att in map:keys($o) return mapping:attributeFromValue($att, $o?($att))
    default return ()
};

declare function mapping:convertLocalAttribute($attribName as xs:string, $attribValue as item()) as element() {
  element {$attribName} {
    mapping:attributeFromValue("value",$attribValue)
  }
};

<rules xmlns="http://docs.rackspace.com/identity/api/ext/MappingRules"
       xmlns:xs="http://www.w3.org/2001/XMLSchema"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
       {
       if (map:contains($policyJSON,"namespaces")) then
         let $namespaces := $policyJSON?namespaces
         for $key in map:keys($namespaces) return namespace {$key} {$namespaces?($key)}
       else ()
       }

       {
         for $rule in $policyJSON?rules?* return
         <rule>
             <local>
                 {
                   let $local := $rule?local
                   for $localGroupName in map:keys($local) return element {$localGroupName} {
                     let $localGroup := $local?($localGroupName)
                     for $attributeName in map:keys($localGroup) return mapping:convertLocalAttribute($attributeName, $localGroup?($attributeName))
                   }
                 }
             </local>
         </rule>
       }
</rules>


Added example for XQuery pipeline step

Added example for XQuery pipeline step

xquery version "3.0";

declare namespace tour="http://wrox.com/tour";

declare namespace config="http://www.armatiek.com/xslweb/configuration";
declare namespace req="http://www.armatiek.com/xslweb/request";
declare namespace log="http://www.armatiek.com/xslweb/functions/log";

(:
    <Modified as example XQuery for XSLWeb by Maarten Kroon>

    XQuery program to perform a knight's tour of the chessboard.
    Author: Michael H. Kay
    Date: 26 June 2003
    Modified: 25 Sept 2014

    This version modified to use XQuery 3.0, with sequences and functions.

    This query does not use a source document.
    There is an optional parameter, start, which can be set to any square on the
    chessboard, e.g. a3 or h5. By default the tour starts at a1.

    There is a second optional parameter, end, which indicates that the processing should stop
    after a given number of steps (the default value is 64). This can be used to animate the
    display of the tour. This works especially well when the query is compiled into a Java servlet.

    The output is an HTML display of the completed tour.

    Internally, the following data representations are used:
    * A square on the chessboard: represented as a number in the range 0 to 63
    * A state of the chessboard: a sequence of 64 integers, each containing a move number.
      A square that has not been visited yet is represented by a zero.
    * A set of possible moves: represented as a sequence of integers,
    * each integer representing the number of the destination square
:)

declare variable $context as node() := /;
declare variable $start as xs:string external := xs:string($context/*/req:parameters/req:parameter[@name='start']/req:value);
declare variable $end as xs:string external := xs:string($context/*/req:parameters/req:parameter[@name='end']/req:value);
declare variable $config:webapp-dir as xs:string external;

(: declare variable $end as xs:string external :='64'; :)
declare variable $endd as xs:integer := xs:integer($end);

(: start-column is an integer in the range 0-7 :)

declare variable $start-column as xs:integer :=
    xs:integer(translate(substring($start, 1, 1),
            'abcdefgh', '01234567'));

(: start-row is an integer in the range 0-7, with zero at the top :)

declare variable $start-row as xs:integer :=
    8 - xs:integer(substring($start, 2, 1));

declare function tour:main () as element() {

    (: This function controls the processing. It does not access the source document. :)

    (: Validate the input parameter :)

    if (not(string-length($start)=2) or
        not(translate(substring($start,1,1), 'abcdefgh', 'aaaaaaaa')='a') or
        not(translate(substring($start,2,1), '12345678', '11111111')='1'))
    then
        error((), "Invalid start parameter: try say 'a1' or 'g6'")
    else

    if (not($endd = 1 to 64))
    then
        error((), "Invalid end parameter: must be in range 1 to 64")
    else

    (: Set up the empty board :)

    let $empty-board as xs:integer* :=
        for $i in (1 to 64) return 0

    (: Place the knight on the board at the chosen starting position :)

    let $initial-board as xs:integer* :=
        tour:place-knight(1, $empty-board, $start-row * 8 + $start-column)

    (: Evaluate the knight's tour :)

    let $final-board as xs:integer* :=
       tour:make-moves(2, $initial-board, $start-row * 8 + $start-column)

    (: produce the HTML output :)

    return tour:print-board($final-board)
};

declare function tour:place-knight (
                    $move as xs:integer,
                    $board as xs:integer*,
                    $square as xs:integer (: range 0 to 63 :)
                  ) as xs:integer* {

    (: This function places a knight on the board at a given square. The returned value is
         the supplied board, modified to indicate that the knight reached a given square at a given
         move :)

    for $i in 1 to 64 return
        if ($i = $square + 1) then $move else $board[$i]

};

declare function tour:make-moves (
                    $move as xs:integer,
                    $board as xs:integer*,
                    $square as xs:integer (: range 0 to 63 :)
                ) as xs:integer* {

    (: This function takes the board in a given state, decides on the next move to make,
         and then calls itself recursively to make further moves, until the knight has completed
         his tour of the board. It returns the board in its final state. :)

    (: determine the possible moves that the knight can make :)

    let $possible-move-list as xs:integer* :=
        tour:list-possible-moves($board, $square)

    (: try these moves in turn until one is found that works :)

    return tour:try-possible-moves($move, $board, $square, $possible-move-list)
};

declare function tour:try-possible-moves (
                    $move as xs:integer,
                    $board as xs:integer*,
                    $square as xs:integer, (: range 0 to 63 :)
                    $possible-moves as xs:integer* )
                as xs:integer* {

    (:   This function tries a set of possible moves that the knight can make
         from a given position. It determines the best move as the one to the square with
         fewest exits. If this is unsuccessful then it can backtrack and
         try another move; however this turns out rarely to be necessary.

         The function makes the selected move, and then calls make-moves() to make
         subsequent moves, returning the final state of the board. :)

         if (count($possible-moves)!=0)
                then tour:make-best-move($move, $board, $square, $possible-moves)
                else ()

         (: if there is no possible move, we return the special value () as the final state
             of the board, to indicate to the caller that we got stuck :)
};

declare function tour:make-best-move (
                    $move as xs:integer,
                    $board as xs:integer*,
                    $square as xs:integer, (: range 0 to 63 :)
                    $possible-moves as xs:integer* )
                as xs:integer* {

    (: this function, given the state of the board and a set of possible moves,
       determines which of the moves is the best one. It then makes this move,
       and proceeds recursively to make further moves, eventually returning the
       final state of the board. :)

    (:  if at least one move is possible, find the best one :)

    let $best-move as xs:integer :=
        tour:find-best-move($board, $possible-moves, 9, 999)

    (: find the list of possible moves excluding the best one :)

    let $other-possible-moves as xs:integer* :=
        $possible-moves[. != $best-move]

    (: update the board to make the move chosen as the best one :)

    let $next-board as xs:integer* :=
        tour:place-knight($move, $board, $best-move)

    (: now make further moves using a recursive call, until the board is complete :)

    let $final-board as xs:integer* :=
        if ($move < $endd) (:count($next-board[.=0])!=0:)
                    then tour:make-moves($move+1, $next-board, $best-move)
                    else $next-board

    (:   if the final board has the special value '()', we got stuck, and have to choose
         the next best of the possible moves. This is done by a recursive call. I thought
         that the knight never did get stuck, but it does: if the starting square is f1,
         the wrong choice is made at move 58, and needs to be reversed. :)

    return
        if (empty($final-board))
        then tour:try-possible-moves($move, $board, $square, $other-possible-moves)
        else $final-board

};

declare function tour:find-best-move (
                    $board as xs:integer*,
                    $possible-moves as xs:integer*,
                    $fewest-exits as xs:integer,
                    $best-so-far as xs:integer )
                as xs:integer {

    (:  This function finds from among the possible moves, the one with fewest exits.
         It calls itself recursively. :)

    (:  split the list of possible moves into the first move and the rest of the moves :)

    let $trial-move as xs:integer :=
        $possible-moves[1]

    let $other-possible-moves as xs:integer* :=
        $possible-moves[position() > 1]

    (: try making the first move :)

    let $trial-board as xs:integer* :=
        tour:place-knight(99, $board, $trial-move)

    (: see how many moves would be possible the next time :)

    let $trial-move-exit-list as xs:integer* :=
        tour:list-possible-moves($trial-board, $trial-move)

    let $number-of-exits as xs:integer :=
        count($trial-move-exit-list)

    (:  determine whether this trial move has fewer exits than those considered up till now :)

    let $minimum-exits as xs:integer :=
        min(($number-of-exits, $fewest-exits))

    (:  determine which is the best move (the one with fewest exits) so far :)

    let $new-best-so-far as xs:integer :=
        if ($number-of-exits < $fewest-exits)
            then $trial-move
            else $best-so-far

    (:  if there are other possible moves, consider them too, using a recursive call.
        Otherwise return the best move found. :)

    return
        if (count($other-possible-moves)!=0)
            then tour:find-best-move($board, $other-possible-moves,
                                            $minimum-exits, $new-best-so-far)
            else $new-best-so-far

};

declare function tour:list-possible-moves (
                $board as xs:integer*,
                $square as xs:integer )
            as xs:integer* {

    (:   This function, given the knight's position on the board, returns the set of squares
         he can move to. The squares will be ones that have not been visited before :)

    let $row as xs:integer := $square idiv 8
    let $column as xs:integer := $square mod 8

    return
        (if ($row > 1 and $column > 0 and $board[($square - 17) + 1]=0)
            then $square - 17 else (),
         if ($row > 1 and $column < 7 and $board[($square - 15) + 1]=0)
            then $square - 15 else (),
         if ($row > 0 and $column > 1 and $board[($square - 10) + 1]=0)
            then $square - 10 else (),
         if ($row > 0 and $column < 6 and $board[($square - 6) + 1]=0)
            then $square - 6 else (),
         if ($row < 6 and $column > 0 and $board[($square + 15) + 1]=0)
            then $square + 15 else (),
         if ($row < 6 and $column < 7 and $board[($square + 17) + 1]=0)
            then $square + 17 else (),
         if ($row < 7 and $column > 1 and $board[($square + 6) + 1]=0)
            then $square + 6 else (),
         if ($row < 7 and $column < 6 and $board[($square + 10) + 1]=0)
            then $square + 10 else () )

};

declare function tour:print-board (
                $board as xs:integer* )
            as element()
{
    (: Output the board in HTML format :)

        <title>Knight's tour</title>

Knight's tour starting at {$start}

    <form method="post" name="tour" action="{$context/*/req:context-path}{$context/*/req:webapp-path}/tour.html">
      <fieldset>
        Start field:
        <input type="text" name="start" value="{$start}"/>

        Number of steps:
        <input type="text" name="end" value="{$end}"/>

        <input type="submit" value="Show tour!"/>
      </fieldset>
    </form>

        {for $row in 0 to 7 return

              {for $column in 0 to 7
                let $color :=
                          if ((($row + $column) mod 2)=1)
                          then 'xffff44'
                          else 'white' return
                {
                  let $n := $board[$row * 8 + $column + 1]
                  return
                      if ($endd != 64 and $n = $endd)
                      then {$n}
                      else if ($n = 0)
                      then " "
                      else $n
                }
              }

        }

{
        if ($endd != 64)
        then
          Next steps
        else ()
    }

};

(
  log:log('INFO', 'Start of tour at: ' || $start),
  tour:main()
)

xquery version "3.0";

declare namespace tour="http://wrox.com/tour";

declare namespace config="http://www.armatiek.com/xslweb/configuration";
declare namespace req="http://www.armatiek.com/xslweb/request";
declare namespace log="http://www.armatiek.com/xslweb/functions/log";

(:
    <Modified as example XQuery for XSLWeb by Maarten Kroon>

    XQuery program to perform a knight's tour of the chessboard.
    Author: Michael H. Kay
    Date: 26 June 2003
    Modified: 25 Sept 2014

    This version modified to use XQuery 3.0, with sequences and functions.

    This query does not use a source document.
    There is an optional parameter, start, which can be set to any square on the
    chessboard, e.g. a3 or h5. By default the tour starts at a1.

    There is a second optional parameter, end, which indicates that the processing should stop
    after a given number of steps (the default value is 64). This can be used to animate the
    display of the tour. This works especially well when the query is compiled into a Java servlet.

    The output is an HTML display of the completed tour.

    Internally, the following data representations are used:
    * A square on the chessboard: represented as a number in the range 0 to 63
    * A state of the chessboard: a sequence of 64 integers, each containing a move number.
      A square that has not been visited yet is represented by a zero.
    * A set of possible moves: represented as a sequence of integers,
    * each integer representing the number of the destination square
:)

declare variable $context as node() := /;
declare variable $start as xs:string external := xs:string($context/*/req:parameters/req:parameter[@name='start']/req:value);
declare variable $end as xs:string external := xs:string($context/*/req:parameters/req:parameter[@name='end']/req:value);
declare variable $config:webapp-dir as xs:string external;

(: declare variable $end as xs:string external :='64'; :)
declare variable $endd as xs:integer := xs:integer($end);

(: start-column is an integer in the range 0-7 :)

declare variable $start-column as xs:integer :=
    xs:integer(translate(substring($start, 1, 1),
            'abcdefgh', '01234567'));

(: start-row is an integer in the range 0-7, with zero at the top :)

declare variable $start-row as xs:integer :=
    8 - xs:integer(substring($start, 2, 1));

declare function tour:main () as element() {

    (: This function controls the processing. It does not access the source document. :)

    (: Validate the input parameter :)

    if (not(string-length($start)=2) or
        not(translate(substring($start,1,1), 'abcdefgh', 'aaaaaaaa')='a') or
        not(translate(substring($start,2,1), '12345678', '11111111')='1'))
    then
        error((), "Invalid start parameter: try say 'a1' or 'g6'")
    else

    if (not($endd = 1 to 64))
    then
        error((), "Invalid end parameter: must be in range 1 to 64")
    else

    (: Set up the empty board :)

    let $empty-board as xs:integer* :=
        for $i in (1 to 64) return 0

    (: Place the knight on the board at the chosen starting position :)

    let $initial-board as xs:integer* :=
        tour:place-knight(1, $empty-board, $start-row * 8 + $start-column)

    (: Evaluate the knight's tour :)

    let $final-board as xs:integer* :=
       tour:make-moves(2, $initial-board, $start-row * 8 + $start-column)

    (: produce the HTML output :)

    return tour:print-board($final-board)
};

declare function tour:place-knight (
                    $move as xs:integer,
                    $board as xs:integer*,
                    $square as xs:integer (: range 0 to 63 :)
                  ) as xs:integer* {

    (: This function places a knight on the board at a given square. The returned value is
         the supplied board, modified to indicate that the knight reached a given square at a given
         move :)

    for $i in 1 to 64 return
        if ($i = $square + 1) then $move else $board[$i]

};

declare function tour:make-moves (
                    $move as xs:integer,
                    $board as xs:integer*,
                    $square as xs:integer (: range 0 to 63 :)
                ) as xs:integer* {

    (: This function takes the board in a given state, decides on the next move to make,
         and then calls itself recursively to make further moves, until the knight has completed
         his tour of the board. It returns the board in its final state. :)

    (: determine the possible moves that the knight can make :)

    let $possible-move-list as xs:integer* :=
        tour:list-possible-moves($board, $square)

    (: try these moves in turn until one is found that works :)

    return tour:try-possible-moves($move, $board, $square, $possible-move-list)
};

declare function tour:try-possible-moves (
                    $move as xs:integer,
                    $board as xs:integer*,
                    $square as xs:integer, (: range 0 to 63 :)
                    $possible-moves as xs:integer* )
                as xs:integer* {

    (:   This function tries a set of possible moves that the knight can make
         from a given position. It determines the best move as the one to the square with
         fewest exits. If this is unsuccessful then it can backtrack and
         try another move; however this turns out rarely to be necessary.

         The function makes the selected move, and then calls make-moves() to make
         subsequent moves, returning the final state of the board. :)

         if (count($possible-moves)!=0)
                then tour:make-best-move($move, $board, $square, $possible-moves)
                else ()

         (: if there is no possible move, we return the special value () as the final state
             of the board, to indicate to the caller that we got stuck :)
};

declare function tour:make-best-move (
                    $move as xs:integer,
                    $board as xs:integer*,
                    $square as xs:integer, (: range 0 to 63 :)
                    $possible-moves as xs:integer* )
                as xs:integer* {

    (: this function, given the state of the board and a set of possible moves,
       determines which of the moves is the best one. It then makes this move,
       and proceeds recursively to make further moves, eventually returning the
       final state of the board. :)

    (:  if at least one move is possible, find the best one :)

    let $best-move as xs:integer :=
        tour:find-best-move($board, $possible-moves, 9, 999)

    (: find the list of possible moves excluding the best one :)

    let $other-possible-moves as xs:integer* :=
        $possible-moves[. != $best-move]

    (: update the board to make the move chosen as the best one :)

    let $next-board as xs:integer* :=
        tour:place-knight($move, $board, $best-move)

    (: now make further moves using a recursive call, until the board is complete :)

    let $final-board as xs:integer* :=
        if ($move < $endd) (:count($next-board[.=0])!=0:)
                    then tour:make-moves($move+1, $next-board, $best-move)
                    else $next-board

    (:   if the final board has the special value '()', we got stuck, and have to choose
         the next best of the possible moves. This is done by a recursive call. I thought
         that the knight never did get stuck, but it does: if the starting square is f1,
         the wrong choice is made at move 58, and needs to be reversed. :)

    return
        if (empty($final-board))
        then tour:try-possible-moves($move, $board, $square, $other-possible-moves)
        else $final-board

};

declare function tour:find-best-move (
                    $board as xs:integer*,
                    $possible-moves as xs:integer*,
                    $fewest-exits as xs:integer,
                    $best-so-far as xs:integer )
                as xs:integer {

    (:  This function finds from among the possible moves, the one with fewest exits.
         It calls itself recursively. :)

    (:  split the list of possible moves into the first move and the rest of the moves :)

    let $trial-move as xs:integer :=
        $possible-moves[1]

    let $other-possible-moves as xs:integer* :=
        $possible-moves[position() > 1]

    (: try making the first move :)

    let $trial-board as xs:integer* :=
        tour:place-knight(99, $board, $trial-move)

    (: see how many moves would be possible the next time :)

    let $trial-move-exit-list as xs:integer* :=
        tour:list-possible-moves($trial-board, $trial-move)

    let $number-of-exits as xs:integer :=
        count($trial-move-exit-list)

    (:  determine whether this trial move has fewer exits than those considered up till now :)

    let $minimum-exits as xs:integer :=
        min(($number-of-exits, $fewest-exits))

    (:  determine which is the best move (the one with fewest exits) so far :)

    let $new-best-so-far as xs:integer :=
        if ($number-of-exits < $fewest-exits)
            then $trial-move
            else $best-so-far

    (:  if there are other possible moves, consider them too, using a recursive call.
        Otherwise return the best move found. :)

    return
        if (count($other-possible-moves)!=0)
            then tour:find-best-move($board, $other-possible-moves,
                                            $minimum-exits, $new-best-so-far)
            else $new-best-so-far

};

declare function tour:list-possible-moves (
                $board as xs:integer*,
                $square as xs:integer )
            as xs:integer* {

    (:   This function, given the knight's position on the board, returns the set of squares
         he can move to. The squares will be ones that have not been visited before :)

    let $row as xs:integer := $square idiv 8
    let $column as xs:integer := $square mod 8

    return
        (if ($row > 1 and $column > 0 and $board[($square - 17) + 1]=0)
            then $square - 17 else (),
         if ($row > 1 and $column < 7 and $board[($square - 15) + 1]=0)
            then $square - 15 else (),
         if ($row > 0 and $column > 1 and $board[($square - 10) + 1]=0)
            then $square - 10 else (),
         if ($row > 0 and $column < 6 and $board[($square - 6) + 1]=0)
            then $square - 6 else (),
         if ($row < 6 and $column > 0 and $board[($square + 15) + 1]=0)
            then $square + 15 else (),
         if ($row < 6 and $column < 7 and $board[($square + 17) + 1]=0)
            then $square + 17 else (),
         if ($row < 7 and $column > 1 and $board[($square + 6) + 1]=0)
            then $square + 6 else (),
         if ($row < 7 and $column < 6 and $board[($square + 10) + 1]=0)
            then $square + 10 else () )

};

declare function tour:print-board (
                $board as xs:integer* )
            as element()
{
    (: Output the board in HTML format :)

        <title>Knight's tour</title>

Knight's tour starting at {$start}

    <form method="post" name="tour" action="{$context/*/req:context-path}{$context/*/req:webapp-path}/tour.html">
      <fieldset>
        Start field:
        <input type="text" name="start" value="{$start}"/>

        End:
        <input type="text" name="end" value="{$end}"/>

        <input type="submit" value="Show tour!"/>
      </fieldset>
    </form>

        {for $row in 0 to 7 return

              {for $column in 0 to 7
                let $color :=
                          if ((($row + $column) mod 2)=1)
                          then 'xffff44'
                          else 'white' return
                {
                  let $n := $board[$row * 8 + $column + 1]
                  return
                      if ($endd != 64 and $n = $endd)
                      then {$n}
                      else if ($n = 0)
                      then " "
                      else $n
                }
              }

        }

{
        if ($endd != 64)
        then
          Step
        else ()
    }

};

(
  log:log('INFO', 'Start of tour at: ' || $start),
  tour:main()
)


validation query: only return provider with @oid, not all the other content

import module namespace csd = "urn:ihe:iti:csd:2013" at "../repo/csd_base_library.xqm";
declare default element  namespace   "urn:ihe:iti:csd:2013";
declare variable $careServicesRequest as item() external;

(:
   The query will be executed against the root element of the CSD document.

   The dynamic context of this query has $careServicesRequest set to contain any of the search
   and limit paramaters as sent by the Service Finder
:)

<CSD xmlns:csd="urn:ihe:iti:csd:2013"  >
  <organizationDirectory/>
  <serviceDirectory/>
  <facilityDirectory/>
  <providerDirectory>
    {
      let $facility_oid := $careServicesRequest/facilities/facility[1]/@oid
      let $service_oid := $careServicesRequest/facilities/facility[1]/service/@oid

      (: if no provider id was provided, then this is invalid. :)
      let $provs0 := if (exists($careServicesRequest/id))
	then csd:filter_by_primary_id(/CSD/providerDirectory/*,$careServicesRequest/id)
      else ()

      let $provs1 := if (exists($facility_oid) and count($provs0) == 1)
	then
	   if (count ($provs0[1]/facilities/facility[@oid = $facility_oid]) > 0) then $provs0 else ()
	else $provs0

      let $provs2 := if (exists($service_oid) and count($provs1) == 1)
	then
	   if (count ($provs1[1]/facilities/facility[@oid = $facility_oid]/service[@oid = $service_oid]) > 0) then $provs1 else ()
	else $provs1

      return if (count($provs2) == 1) then
	<provider oid='{$provs2[1]/@oid}'/>
      else
	 ()
    }
  </providerDirectory>
</CSD>

import module namespace csd = "urn:ihe:iti:csd:2013" at "../repo/csd_base_library.xqm";
declare default element  namespace   "urn:ihe:iti:csd:2013";
declare variable $careServicesRequest as item() external;

(:
   The query will be executed against the root element of the CSD document.

   The dynamic context of this query has $careServicesRequest set to contain any of the search
   and limit paramaters as sent by the Service Finder
:)

<CSD xmlns:csd="urn:ihe:iti:csd:2013"  >
  <organizationDirectory/>
  <serviceDirectory/>
  <facilityDirectory/>
  <providerDirectory>
    {
      let $facility_oid := $careServicesRequest/facilities/facility[1]/@oid
      let $service_oid := $careServicesRequest/facilities/facility[1]/service/@oid

      (: if no provider id was provided, then this is invalid. :)
      let $provs0 := if (exists($careServicesRequest/id))
	then csd:filter_by_primary_id(/CSD/providerDirectory/*,$careServicesRequest/id)
      else ()

      let $provs1 := if (exists($facility_oid) and count($provs0) == 1)
	then
	   if (count ($provs0[1]/facilities/facility[@oid = $facility_oid]) > 0) then $provs0 else ()
	else $provs0

      let $provs2 := if (exists($service_oid) and count($provs1) == 1)
	then
	   if (count ($provs1[1]/facilities/facility[@oid = $facility_oid]/service[@oid = $service_oid]) > 0) then $provs1 else ()
	else $provs1

      return $provs2
    }
  </providerDirectory>
</CSD>


Fixed namespace tests

Fixed namespace tests

xquery version "3.1";

(:~
 : Tests for namespace functions.
 :)
module namespace test = 'http://xokomola.com/xquery/origami/tests';

import module namespace o = 'http://xokomola.com/xquery/origami'
    at '../origami.xqm';

declare %unit:test function test:ns-map()
{
    let $ns := o:ns-map()
    return
        unit:assert-equals($ns?html,'http://www.w3.org/1999/xhtml',
            'XHTML namespace is in default namespace map'
        ),

    let $ns := o:ns-map()
    return
        unit:assert-equals($ns?x,(),
            'X namespace is not in default namespace map'
        ),

    let $ns := o:ns-map(map { 'x': 'foobar' })
    return
        unit:assert-equals($ns?x,'foobar',
            'X namespace is in the namespace map'
        ),

    let $ns := o:ns-map(map { 'html': 'foobar' })
    return
        unit:assert-equals($ns?html,'foobar',
            'XHTML is redefined in the namespace map'
        )
};

(:~
 : A document is _normal_ (or _in namespace-normal form_) if all
 : namespace declarations appear on the root element and it is
 : not psychotic.  (A borderline document with all namespace
 : declarations in the same place is automatically psychotic;
 : a neurotic document with this property would be illegal according
 : to the Namespaces REC.)
 :)
declare variable $test:normal :=
    <foo xmlns="foo" xmlns:x="bar"><x:bar foo="bar"/></foo>;

(:~
 : It's not always possible to produce normal documents -- the producer
 : might not know all of the relevant namespaces at the time it emits
 : the root element start-tag -- so a weaker definition is useful:
 : A document is _sane_ if it is neither neurotic nor borderline.
 :)
declare variable $test:sane :=
    <foo xmlns="foo"><x:bar xmlns:x="bar" foo="bar"/></foo>;

(:~
 : Let's say that an XML document is _neurotic_ if it maps the same
 : namespace prefix to two different namespace URIs at different
 : points.  Neurosis makes it necessary for XML processors to
 : work with {URI+localname} pairs instead of GIs, and to keep
 : track of the namespace environment at each point in the tree
 : if there are QNames-in-content.  If it weren't for neurosis,
 : applications could use a single namespace map that applied to
 : the entire document.
 :)
declare variable $test:neurotic :=
    <x:foo xmlns:x="foo">
        <x:bar xmlns:x="bar"/>
    </x:foo>;

(:~
 : Conversely, a document is _borderline_ if it maps two different
 : namespace prefixes to the same namespace URI.  Borderline documents
 : complicate reserialization: the choice of which prefix to
 : use for a particular {URI+localname} pair depends on its
 : position in the tree.
 :)
declare variable $test:borderline :=
    <x:foo xmlns:x="foo">
        <y:bar xmlns:y="foo"/>
    </x:foo>;

(:~
 : A document is _psychotic_ if it maps two different namespace prefixes
 : to the same URI _in the same scope_.  Psychosis presents an even
 : bigger difficulty for reserialization: now applications must keep
 : track of the original prefix as well as the {URI+localname} pair.
 :)
declare variable $test:psychotic :=
    <x:foo xmlns:x="foo" xmlns:y="foo" y:bar="bar"/>;

(:~
 : This document suffers from multiple disorders.
 :)
declare variable $test:psychotic-borderline-neurotic :=
    <x:foo xmlns:x="foo" xmlns:y="foo" y:bar="bar">
        <x:foo xmlns:y="bar" xmlns:z="foo" z:foo="bar"/>
    </x:foo>;

declare %unit:test function test:normal-xml()
{
    unit:assert-equals(
        o:ns-map-from-nodes($test:sane),
        map { '': 'foo', 'x': 'bar' },
        'A sane XML document.'
    )
};

declare %unit:test function test:sane-xml()
{
    unit:assert-equals(
        o:ns-map-from-nodes($test:sane),
        map { '': 'foo', 'x': 'bar' },
        'A sane XML document.'
    )
};

declare %unit:test function test:insane-xml()
{
    unit:assert-equals(
        o:ns-map-from-nodes($test:neurotic),
        map { 'x': 'foo' },
        'A neurotic XML maps the same namespace prefix to two different
         namespace URIs at different points.'
    ),

    unit:assert-equals(
        o:ns-map-from-nodes($test:borderline),
        map { 'x': 'foo', 'y': 'foo' },
        'A borderline XML maps two different namespace prefixes to the same
        namespace URI.'
    ),

    unit:assert-equals(
        o:ns-map-from-nodes($test:psychotic),
        map { 'x': 'foo', 'y': 'foo' },
        'A psychotic XML maps two different URIs to the same prefix.'
    ),

    unit:assert-equals(
        o:ns-map-from-nodes($test:psychotic-borderline-neurotic),
        map { 'x': 'foo', 'y': 'foo', 'z': 'foo' },
        'A mixture of insanity.'
    )
};

declare %unit:test function test:ns-xform()
{
    let $xf := o:ns-xform()
    return
        unit:assert-equals(
            $xf?ns?html,
            'http://www.w3.org/1999/xhtml',
            'Transformer has XHTML namespace URI'
        ),

    let $xf := o:ns-xform($test:sane)
    return
        unit:assert-equals(
            $xf?ns,
            map { '': 'foo', 'x': 'bar' },
            'Transformer has sane XML namespace map'
        ),

    let $xf := o:ns-xform(o:ns-xform(), $test:sane)
    return (
        unit:assert-equals(
            $xf?ns?html,
            'http://www.w3.org/1999/xhtml',
            'Transformer is composed of default namespaces and XML namespaces'
        ),
        unit:assert-equals(
            $xf?ns?x,
            'bar',
            'Transformer has bar namespace'
        ),
        unit:assert-equals(
            $xf?ns(''),
            'foo',
            'Transformer has foo namespace'
        )
    )
};

declare %unit:test function test:default-ns-xform()
{
    unit:assert-equals(
        o:default-ns-xform(map {}, 'foo')?ns(''),
        'foo',
        'Default namespace foo'
    ),

    unit:assert-equals(
        o:default-ns-xform(o:ns-xform(), 'bar')?ns(''),
        'bar',
        'Default namespace foo'
    )
};

declare %unit:test function test:default-namespace()
{
    let $xml := o:xml(['p'])
    return
        unit:assert-equals(namespace-uri($xml), '')
    ,
    let $xml := o:xml(['h:p'], o:ns-xform())
    return
        unit:assert-equals(namespace-uri($xml), 'http://www.w3.org/1999/xhtml')
    ,
    let $xml := o:xml(['p'], o:default-ns-xform('http://foobar'))
    return
        unit:assert-equals(namespace-uri($xml), 'http://foobar')
    ,
    let $xml := o:xml(['h:x',['p']], o:ns-xform() => o:default-ns-xform('http://foobar'))
    return (
        unit:assert-equals(namespace-uri($xml), 'http://www.w3.org/1999/xhtml'),
        (: default is set to a uri so we cannot just use $xml/p to get at the child element :)
        unit:assert-equals(namespace-uri($xml/*[1]), 'http://foobar')
    )
    ,
    let $xml := o:xml(['h:x',['p']], o:ns-xform() => o:default-ns-xform(''))
    return (
        unit:assert-equals(namespace-uri($xml), 'http://www.w3.org/1999/xhtml'),
        unit:assert-equals(namespace-uri($xml/p), '')
    )
};

(: TODO: default ns prefixes are present in the result :)
declare %unit:test function test:no-extra-namespaces-in-result()
{
      unit:assert-equals(
        in-scope-prefixes(o:xml(['x'])),
        ('xml')
      )
};

declare %unit:test function test:prefixes()
{
    let $xml := o:xml(['p'])
    return
        unit:assert-equals(prefix-from-QName(node-name($xml)), ())
    ,
    let $xml := o:xml(['h:p'])
    return
        unit:assert-equals(prefix-from-QName(node-name($xml)), 'h')
    ,
    let $xml := o:xml(['p'], o:ns-xform() => o:default-ns-xform('http://www.w3.org/1999/xhtml'))
    return (
        unit:assert-equals(prefix-from-QName(node-name($xml)), ()),
        unit:assert-equals(namespace-uri($xml), 'http://www.w3.org/1999/xhtml')
    ),
    let $xml := o:xml(['h:p'], o:ns-xform() => o:default-ns-xform('http://www.w3.org/1999/xhtml'))
    return
        unit:assert-equals(prefix-from-QName(node-name($xml)), ())
    ,
    let $xml := o:xml(['x:p'],o:ns-xform(map { 'x': 'http://foobar' }) => o:default-ns-xform('http://foobar'))
    return
        unit:assert-equals(prefix-from-QName(node-name($xml)), ())
    ,
    let $xml := o:xml(['x:p'], o:ns-xform(map { 'x': 'http://foobar' }))
    return
        unit:assert-equals(prefix-from-QName(node-name($xml)), 'x')
};

declare %unit:test function test:mixed-namespaces()
{
    let $xml := o:xml(
        ['app:foo', ['atom:bar'], ['atom:bar'], ['category']],
        o:ns-xform())
    return (
        unit:assert-equals(prefix-from-QName(node-name($xml)), 'app'),
        unit:assert-equals(namespace-uri($xml), 'http://www.w3.org/2007/app'),
        unit:assert-equals(prefix-from-QName(node-name($xml/*[1])), 'atom'),
        unit:assert-equals(namespace-uri($xml/*[1]), 'http://www.w3.org/2005/Atom'),
        unit:assert-equals(prefix-from-QName(node-name($xml/*[3])), ()),
        unit:assert-equals(namespace-uri($xml/*[3]), '')
    ),
    let $xml := o:xml(
        ['app:foo', ['atom:bar'], ['atom:bar'], ['category']],
        o:ns-xform()
        => o:default-ns-xform('http://www.w3.org/2007/app'))
    return (
        unit:assert-equals(prefix-from-QName(node-name($xml)), ()),
        unit:assert-equals(namespace-uri($xml), 'http://www.w3.org/2007/app'),
        unit:assert-equals(prefix-from-QName(node-name($xml/*[1])), 'atom'),
        unit:assert-equals(namespace-uri($xml/*[1]), 'http://www.w3.org/2005/Atom'),
        unit:assert-equals(prefix-from-QName(node-name($xml/*[3])), ()),
        unit:assert-equals(namespace-uri($xml/*[3]), 'http://www.w3.org/2007/app')
    ),
    let $xml := o:xml(
        ['app:foo', ['atom:bar'], ['atom:bar'], ['category']],
        o:ns-xform()
        => o:default-ns-xform('http://www.w3.org/2005/Atom'))
    return (
        unit:assert-equals(prefix-from-QName(node-name($xml)), 'app'),
        unit:assert-equals(namespace-uri($xml), 'http://www.w3.org/2007/app'),
        unit:assert-equals(prefix-from-QName(node-name($xml/*[1])), ()),
        unit:assert-equals(namespace-uri($xml/*[1]), 'http://www.w3.org/2005/Atom'),
        unit:assert-equals(prefix-from-QName(node-name($xml/*[3])), ()),
        unit:assert-equals(namespace-uri($xml/*[3]), 'http://www.w3.org/2005/Atom')
    ),
    let $xml := o:xml(
        ['app:foo', ['x:bar'], ['atom:bar'], ['category']],
        o:ns-xform(map {
            'x': 'http://www.w3.org/2005/Atom',
            'atom': 'http://www.w3.org/2005/Atom',
            'app': 'http://www.w3.org/2007/app' })
        => o:default-ns-xform('http://www.w3.org/2005/Atom'))
    return (
        unit:assert-equals(prefix-from-QName(node-name($xml)), 'app'),
        unit:assert-equals(namespace-uri($xml), 'http://www.w3.org/2007/app'),
        unit:assert-equals(prefix-from-QName(node-name($xml/*[1])), ()),
        unit:assert-equals(namespace-uri($xml/*[1]), 'http://www.w3.org/2005/Atom'),
        unit:assert-equals(prefix-from-QName(node-name($xml/*[2])), ()),
        unit:assert-equals(namespace-uri($xml/*[1]), 'http://www.w3.org/2005/Atom'),
        unit:assert-equals(prefix-from-QName(node-name($xml/*[3])), ()),
        unit:assert-equals(namespace-uri($xml/*[3]), 'http://www.w3.org/2005/Atom')
    )
};

xquery version "3.1";

(:~
 : Tests for namespace functions.
 :)
module namespace test = 'http://xokomola.com/xquery/origami/tests';

import module namespace o = 'http://xokomola.com/xquery/origami'
    at '../origami.xqm';

declare %unit:test function test:ns-map()
{
    let $ns := o:ns-map()
    return
        unit:assert-equals($ns?html,'http://www.w3.org/1999/xhtml',
            'XHTML namespace is in default namespace map'
        ),

    let $ns := o:ns-map()
    return
        unit:assert-equals($ns?x,(),
            'X namespace is not in default namespace map'
        ),

    let $ns := o:ns-map(map { 'x': 'foobar' })
    return
        unit:assert-equals($ns?x,'foobar',
            'X namespace is in the namespace map'
        ),

    let $ns := o:ns-map(map { 'html': 'foobar' })
    return
        unit:assert-equals($ns?html,'foobar',
            'XHTML is redefined in the namespace map'
        )
};

(:~
 : A document is _normal_ (or _in namespace-normal form_) if all
 : namespace declarations appear on the root element and it is
 : not psychotic.  (A borderline document with all namespace
 : declarations in the same place is automatically psychotic;
 : a neurotic document with this property would be illegal according
 : to the Namespaces REC.)
 :)
declare variable $test:normal :=
    <foo xmlns="foo" xmlns:x="bar"><x:bar foo="bar"/></foo>;

(:~
 : It's not always possible to produce normal documents -- the producer
 : might not know all of the relevant namespaces at the time it emits
 : the root element start-tag -- so a weaker definition is useful:
 : A document is _sane_ if it is neither neurotic nor borderline.
 :)
declare variable $test:sane :=
    <foo xmlns="foo"><x:bar xmlns:x="bar" foo="bar"/></foo>;

(:~
 : Let's say that an XML document is _neurotic_ if it maps the same
 : namespace prefix to two different namespace URIs at different
 : points.  Neurosis makes it necessary for XML processors to
 : work with {URI+localname} pairs instead of GIs, and to keep
 : track of the namespace environment at each point in the tree
 : if there are QNames-in-content.  If it weren't for neurosis,
 : applications could use a single namespace map that applied to
 : the entire document.
 :)
declare variable $test:neurotic :=
    <x:foo xmlns:x="foo">
        <x:bar xmlns:x="bar"/>
    </x:foo>;

(:~
 : Conversely, a document is _borderline_ if it maps two different
 : namespace prefixes to the same namespace URI.  Borderline documents
 : complicate reserialization: the choice of which prefix to
 : use for a particular {URI+localname} pair depends on its
 : position in the tree.
 :)
declare variable $test:borderline :=
    <x:foo xmlns:x="foo">
        <y:bar xmlns:y="foo"/>
    </x:foo>;

(:~
 : A document is _psychotic_ if it maps two different namespace prefixes
 : to the same URI _in the same scope_.  Psychosis presents an even
 : bigger difficulty for reserialization: now applications must keep
 : track of the original prefix as well as the {URI+localname} pair.
 :)
declare variable $test:psychotic :=
    <x:foo xmlns:x="foo" xmlns:y="foo" y:bar="bar"/>;

(:~
 : This document suffers from multiple disorders.
 :)
declare variable $test:psychotic-borderline-neurotic :=
    <x:foo xmlns:x="foo" xmlns:y="foo" y:bar="bar">
        <x:foo xmlns:y="bar" xmlns:z="foo" z:foo="bar"/>
    </x:foo>;

declare %unit:test function test:normal-xml()
{
    unit:assert-equals(
        o:ns-map-from-nodes($test:sane),
        map { '': 'foo', 'x': 'bar' },
        'A sane XML document.'
    )
};

declare %unit:test function test:sane-xml()
{
    unit:assert-equals(
        o:ns-map-from-nodes($test:sane),
        map { '': 'foo', 'x': 'bar' },
        'A sane XML document.'
    )
};

declare %unit:test function test:insane-xml()
{
    unit:assert-equals(
        o:ns-map-from-nodes($test:neurotic),
        map { 'x': 'foo' },
        'A neurotic XML maps the same namespace prefix to two different
         namespace URIs at different points.'
    ),

    unit:assert-equals(
        o:ns-map-from-nodes($test:borderline),
        map { 'x': 'foo', 'y': 'foo' },
        'A borderline XML maps two different namespace prefixes to the same
        namespace URI.'
    ),

    unit:assert-equals(
        o:ns-map-from-nodes($test:psychotic),
        map { 'x': 'foo', 'y': 'foo' },
        'A psychotic XML maps two different URIs to the same prefix.'
    ),

    unit:assert-equals(
        o:ns-map-from-nodes($test:psychotic-borderline-neurotic),
        map { 'x': 'foo', 'y': 'foo', 'z': 'foo' },
        'A mixture of insanity.'
    )
};

declare %unit:test function test:ns-xform()
{
    let $xf := o:ns-xform()
    return
        unit:assert-equals(
            $xf?ns?html,
            'http://www.w3.org/1999/xhtml',
            'Transformer has XHTML namespace URI'
        ),

    let $xf := o:ns-xform($test:sane)
    return
        unit:assert-equals(
            $xf?ns,
            map { '': 'foo', 'x': 'bar' },
            'Transformer has sane XML namespace map'
        ),

    let $xf := o:ns-xform(o:ns-xform(), $test:sane)
    return (
        unit:assert-equals(
            $xf?ns?html,
            'http://www.w3.org/1999/xhtml',
            'Transformer is composed of default namespaces and XML namespaces'
        ),
        unit:assert-equals(
            $xf?ns?x,
            'bar',
            'Transformer has bar namespace'
        ),
        unit:assert-equals(
            $xf?ns(''),
            'foo',
            'Transformer has foo namespace'
        )
    )
};

declare %unit:test function test:default-ns-xform()
{
    unit:assert-equals(
        o:default-ns-xform(map {}, 'foo')?ns(''),
        'foo',
        'Default namespace foo'
    ),

    unit:assert-equals(
        o:default-ns-xform(o:ns-xform(), 'bar')?ns(''),
        'bar',
        'Default namespace foo'
    )
};

declare %unit:test %unit:ignore function test:default-namespace()
{
    let $xml := o:xml(['p'])
    return
        unit:assert-equals(namespace-uri($xml), '')
    ,
    let $xml := o:xml(['h:p'])
    return
        unit:assert-equals(namespace-uri($xml), 'http://www.w3.org/1999/xhtml')
    ,
    let $xml := o:xml(['p'], o:qname-resolver(map {}, 'http://foobar'))
    return
        unit:assert-equals(namespace-uri($xml), 'http://foobar')
    (: ,
    let $xml := o:xml(['h:x',['p']], o:qname-resolver(o:ns-map(), 'http://foobar'))
    return (
        unit:assert-equals(namespace-uri($xml), 'http://www.w3.org/1999/xhtml'),
        (: default is set to a uri so we cannot just use $xml/p to get at the child element :)
        unit:assert-equals(namespace-uri($xml/*[1]), 'http://foobar')
    )
    ,
    let $xml := o:xml(['h:x',['p']], o:qname-resolver(o:ns-map(), ''))
    return (
        unit:assert-equals(namespace-uri($xml), 'http://www.w3.org/1999/xhtml'),
        unit:assert-equals(namespace-uri($xml/p), '')
    ) :)
};

(: TODO: default ns prefixes are present in the result :)
declare %unit:test function test:no-extra-namespaces-in-result()
{
      unit:assert-equals(
        in-scope-prefixes(o:xml(['x'])),
        ('xml')
      )
};

declare %unit:test %unit:ignore function test:prefixes()
{
    let $xml := o:xml(['p'])
    return
        unit:assert-equals(prefix-from-QName(node-name($xml)), ())
    (:,
    let $xml := o:xml(['h:p'])
    return
        unit:assert-equals(prefix-from-QName(node-name($xml)), 'h')
    ,
    let $xml := o:xml(['p'], o:qname-resolver(o:ns-map(), 'http://www.w3.org/1999/xhtml'))
    return (
        unit:assert-equals(prefix-from-QName(node-name($xml)), ()),
        unit:assert-equals(namespace-uri($xml), 'http://www.w3.org/1999/xhtml')
    ),
    let $xml := o:xml(['h:p'], o:qname-resolver(o:ns-map(), 'http://www.w3.org/1999/xhtml'))
    return
        unit:assert-equals(prefix-from-QName(node-name($xml)), ())
    ,
    let $xml := o:xml(['x:p'], o:qname-resolver(o:ns-map(map { 'x': 'http://foobar' }), 'http://foobar'))
    return
        unit:assert-equals(prefix-from-QName(node-name($xml)), ())
    ,
    let $xml := o:xml(['x:p'], o:qname-resolver(o:ns-map(map { 'x': 'http://foobar' })))
    return
        unit:assert-equals(prefix-from-QName(node-name($xml)), 'x')
    :)
};

declare %unit:test function test:mixed-namespaces()
{
    true()
     (: let $xml := o:xml(
        ['app:foo', ['atom:bar'], ['atom:bar'], ['category']],
        o:qname-resolver(o:ns-map()))
    return (
        unit:assert-equals(prefix-from-QName(node-name($xml)), 'app'),
        unit:assert-equals(namespace-uri($xml), 'http://www.w3.org/2007/app'),
        unit:assert-equals(prefix-from-QName(node-name($xml/*[1])), 'atom'),
        unit:assert-equals(namespace-uri($xml/*[1]), 'http://www.w3.org/2005/Atom'),
        unit:assert-equals(prefix-from-QName(node-name($xml/*[3])), ()),
        unit:assert-equals(namespace-uri($xml/*[3]), '')
    ),
    let $xml := o:xml(
        ['app:foo', ['atom:bar'], ['atom:bar'], ['category']],
        o:qname-resolver(o:ns-map(), 'http://www.w3.org/2007/app'))
    return (
        unit:assert-equals(prefix-from-QName(node-name($xml)), ()),
        unit:assert-equals(namespace-uri($xml), 'http://www.w3.org/2007/app'),
        unit:assert-equals(prefix-from-QName(node-name($xml/*[1])), 'atom'),
        unit:assert-equals(namespace-uri($xml/*[1]), 'http://www.w3.org/2005/Atom'),
        unit:assert-equals(prefix-from-QName(node-name($xml/*[3])), ()),
        unit:assert-equals(namespace-uri($xml/*[3]), 'http://www.w3.org/2007/app')
    ),
    let $xml := o:xml(
        ['app:foo', ['atom:bar'], ['atom:bar'], ['category']],
        o:qname-resolver(o:ns-map(), 'http://www.w3.org/2005/Atom'))
    return (
        unit:assert-equals(prefix-from-QName(node-name($xml)), 'app'),
        unit:assert-equals(namespace-uri($xml), 'http://www.w3.org/2007/app'),
        unit:assert-equals(prefix-from-QName(node-name($xml/*[1])), ()),
        unit:assert-equals(namespace-uri($xml/*[1]), 'http://www.w3.org/2005/Atom'),
        unit:assert-equals(prefix-from-QName(node-name($xml/*[3])), ()),
        unit:assert-equals(namespace-uri($xml/*[3]), 'http://www.w3.org/2005/Atom')
    ),
    let $xml := o:xml(
        ['app:foo', ['x:bar'], ['atom:bar'], ['category']],
        o:qname-resolver(o:ns-map(map { 'x': 'http://www.w3.org/2005/Atom' }), 'http://www.w3.org/2005/Atom'))
    return (
        unit:assert-equals(prefix-from-QName(node-name($xml)), 'app'),
        unit:assert-equals(namespace-uri($xml), 'http://www.w3.org/2007/app'),
        unit:assert-equals(prefix-from-QName(node-name($xml/*[1])), ()),
        unit:assert-equals(namespace-uri($xml/*[1]), 'http://www.w3.org/2005/Atom'),
        unit:assert-equals(prefix-from-QName(node-name($xml/*[2])), ()),
        unit:assert-equals(namespace-uri($xml/*[1]), 'http://www.w3.org/2005/Atom'),
        unit:assert-equals(prefix-from-QName(node-name($xml/*[3])), ()),
        unit:assert-equals(namespace-uri($xml/*[3]), 'http://www.w3.org/2005/Atom')
    )    :)
};


updated

updated

svn path=/trunk/eXist/; revision=6742

xquery version "1.0";

declare namespace xmldb="http://exist-db.org/xquery/xmldb";

declare variable $data {
    <todo-list>
        <todo>
            <description>Add support for binary objects</description>
            <priority level="6"/>

            <date>2003-11-22</date>
            <status>finished</status>
        </todo>
    </todo-list>
};

declare variable $xupdate {
    <xu:modifications version="1.0" xmlns:xu="http://www.xmldb.org/xupdate">
        <xu:rename select="/todo-list/todo/date">
            start-date
        </xu:rename>
        <xu:append select="/todo-list">
            <todo>
                <description>Document supported XML schema types</description>
                <start-date>{current-date()}</start-date>
                <status>open</status>
            </todo>
        </xu:append>
        <xu:append select="/todo-list/todo[status='open']">
            <priority level="3"/>
        </xu:append>
        <xu:update select="/todo-list/todo/status">
            closed
        </xu:update>
    </xu:modifications>
};

let $isLoggedIn := xmldb:login("xmldb:exist:///db", "admin", ""),
    $collection := xmldb:create-collection("xmldb:exist:///db", "test"),
    $doc := xmldb:store($collection, "test.xml", $data),
    $mods := xmldb:update($collection, $xupdate)
return
    <result>
        <status>{$mods} modifications processed.</status>
        {for $m in doc("/db/test/test.xml")//todo return $m}
    </result>

xquery version "1.0";

declare namespace xmldb="http://exist-db.org/xquery/xmldb";

declare variable $data {
    <todo-list>
        <todo>
            <description>Add support for binary objects</description>
            <priority level="6"/>

            <date>2003-11-22</date>
            <status>finished</status>
        </todo>
    </todo-list>
};

declare variable $xupdate {
    <xu:modifications version="1.0" xmlns:xu="http://www.xmldb.org/xupdate">
        <xu:rename select="/todo-list/todo/date">
            start-date
        </xu:rename>
        <xu:append select="/todo-list">
            <todo>
                <description>Document supported XML schema types</description>
                <start-date>{current-date()}</start-date>
                <status>open</status>
            </todo>
        </xu:append>
        <xu:append select="/todo-list/todo[status='open']">
            <priority level="3"/>
        </xu:append>
        <xu:update select="/todo-list/todo/status">
            closed
        </xu:update>
    </xu:modifications>
};

let $root := xmldb:collection("xmldb:exist:///db", "admin", ""),
    $collection := xmldb:create-collection($root, "test"),
    $doc := xmldb:store($collection, "test.xml", $data),
    $mods := xmldb:update($collection, $xupdate)
return
    <result>
        <status>{$mods} modifications processed.</status>
        {for $m in doc("/db/test/test.xml")//todo return $m}
    </result>