moved to all reference to db being in csd_webconf instead of a variable so we can let basex optimize

import module namespace csd_webconf =  "https://github.com/openhie/openinfoman/csd_webconf";
import module namespace csd_lsc = "https://github.com/openhie/openinfoman/csd_lsc";
import module namespace csd_dm = "https://github.com/openhie/openinfoman/csd_dm";
import module namespace anonymize = "https://github.com/openhie/openinfoman-anon";
import module namespace functx = "http://www.functx.com";

declare namespace csd  =  "urn:ihe:iti:csd:2013";

declare variable $careServicesRequest as item() external;

let $dest_doc := /.
let $dest := $careServicesRequest/@resource

let $values_doc := "anonymous/bible.xml"
let $fac_values_doc := "anonymous/star.xml"

let $values_src_0 := db:open($csd_webconf:db,$values_doc)
let $values_src_1 :=
 if (count($values_src_0//value[1]) > 0)
 then $values_src_0
 else
   let $number-for-a := string-to-codepoints('A')
   let $number-for-z := string-to-codepoints('Z')

   let $values_src :=
     <values>
       {
	 for $letter in ($number-for-a to $number-for-z)
	 let $url := concat('http://en.wikipedia.org/wiki/List_of_biblical_names_starting_with_', codepoints-to-string($letter))
	 let $resp := http:send-request(<http:request method='get' href="{$url}"/>)
 	   for $li in $resp/html/body//div[@id="mw-content-text"]/ul[1]/li
	   let $text := string-join(($li/a[1]/text(),$li/text()))
	   let $name := tokenize($text,'\W+')[1]
	   return <value>{functx:trim($name)}</value>
       }
     </values>
    return $values_src
let $values := $values_src_1//value/text()

let $fac_values_src_0 := db:open($csd_webconf:db,  $fac_values_doc)
let $fac_values_src_1 :=
 if (count($fac_values_src_0//value[1]) > 0)
 then $fac_values_src_0
 else
   let $fac_url := 'http://en.wikipedia.org/wiki/List_of_proper_names_of_stars_in_alphabetical_order'
   let $resp := http:send-request(<http:request method='get' href="{$fac_url}"/>)

   return
     <values>
       {
	 for $td in $resp//table[@class="wikitable"]//td[1]
	 let $text :=
	   functx:trim(string-join(($td/a[1]/text(),$td/text())))
	 return if ($text) then <value>{$text}</value> else ()
       }
     </values>

let $fac_values := $fac_values_src_1//value/text()

return
(
  if (count($values_src_0//value[1]) = 0)
  then
     if (db:exists($csd_webconf:db, $values_doc))
     then db:replace($csd_webconf:db, $values_src_1, $values_doc)
     else db:add($csd_webconf:db, $values_src_1, $values_doc)
  else ()
  ,
  if (count($fac_values_src_0//value[1]) = 0)
  then
     if (db:exists($csd_webconf:db, $fac_values_doc))
     then db:replace($csd_webconf:db, $fac_values_src_1, $fac_values_doc)
     else db:add($csd_webconf:db, $fac_values_src_1, $fac_values_doc)
  else ()
  ,
  for $doc  in $careServicesRequest/documents/document
    let $name := $doc/@resource
    let $src_doc :=
      if (not ($name = $dest)) then csd_dm:open_document( $name)
      else ()
    let $anon_doc := anonymize:document($src_doc,$values,$fac_values,$fac_values)
    return   (csd_lsc:refresh_doc($dest_doc, $anon_doc))

)

import module namespace csd_webconf =  "https://github.com/openhie/openinfoman/csd_webconf";
import module namespace csd_lsc = "https://github.com/openhie/openinfoman/csd_lsc";
import module namespace csd_dm = "https://github.com/openhie/openinfoman/csd_dm";
import module namespace anonymize = "https://github.com/openhie/openinfoman-anon";
import module namespace functx = "http://www.functx.com";

declare namespace csd  =  "urn:ihe:iti:csd:2013";

declare variable $careServicesRequest as item() external;

let $dest_doc := /.
let $dest := $careServicesRequest/@resource

let $values_doc := "anonymous/bible.xml"
let $fac_values_doc := "anonymous/star.xml"

let $values_src_0 := db:open($csd_webconf:db,  $values_doc)
let $values_src_1 :=
 if (count($values_src_0//value[1]) > 0)
 then $values_src_0
 else
   let $number-for-a := string-to-codepoints('A')
   let $number-for-z := string-to-codepoints('Z')

   let $values_src :=
     <values>
       {
	 for $letter in ($number-for-a to $number-for-z)
	 let $url := concat('http://en.wikipedia.org/wiki/List_of_biblical_names_starting_with_', codepoints-to-string($letter))
	 let $resp := http:send-request(<http:request method='get' href="{$url}"/>)
 	   for $li in $resp/html/body//div[@id="mw-content-text"]/ul[1]/li
	   let $text := string-join(($li/a[1]/text(),$li/text()))
	   let $name := tokenize($text,'\W+')[1]
	   return <value>{functx:trim($name)}</value>
       }
     </values>
    return $values_src
let $values := $values_src_1//value/text()

let $fac_values_src_0 := db:open($csd_webconf:db,  $fac_values_doc)
let $fac_values_src_1 :=
 if (count($fac_values_src_0//value[1]) > 0)
 then $fac_values_src_0
 else
   let $fac_url := 'http://en.wikipedia.org/wiki/List_of_proper_names_of_stars_in_alphabetical_order'
   let $resp := http:send-request(<http:request method='get' href="{$fac_url}"/>)

   return
     <values>
       {
	 for $td in $resp//table[@class="wikitable"]//td[1]
	 let $text :=
	   functx:trim(string-join(($td/a[1]/text(),$td/text())))
	 return if ($text) then <value>{$text}</value> else ()
       }
     </values>

let $fac_values := $fac_values_src_1//value/text()

return
(
  if (count($values_src_0//value[1]) = 0)
  then
     if (db:exists($csd_webconf:db, $values_doc))
     then db:replace($csd_webconf:db, $values_src_1, $values_doc)
     else db:add($csd_webconf:db, $values_src_1, $values_doc)
  else ()
  ,
  if (count($fac_values_src_0//value[1]) = 0)
  then
     if (db:exists($csd_webconf:db, $fac_values_doc))
     then db:replace($csd_webconf:db, $fac_values_src_1, $fac_values_doc)
     else db:add($csd_webconf:db, $fac_values_src_1, $fac_values_doc)
  else ()
  ,
  for $doc  in $careServicesRequest/documents/document
    let $name := $doc/@resource
    let $src_doc :=
      if (not ($name = $dest)) then csd_dm:open_document($csd_webconf:db, $name)
      else ()
    let $anon_doc := anonymize:document($src_doc,$values,$fac_values,$fac_values)
    return   (csd_lsc:refresh_doc($dest_doc, $anon_doc))

)


Added missing "return".

Added missing "return".

(: try parsing a valid JSON string with invalid JSON option parameter :)

import module namespace json = "http://www.zorba-xquery.com/modules/converters/json";

let $options :=
  <options xmlns="http://www.zorba-xquery.com/modules/converters/json-options">
    <json-format value="array"/>
  </options>

return json:parse('{}',$options)

(: try parsing a valid JSON string with invalid JSON option parameter :)

import module namespace json = "http://www.zorba-xquery.com/modules/converters/json";

let $options :=
  <options xmlns="http://www.zorba-xquery.com/modules/converters/json-options">
    <json-format value="array"/>
  </options>

json:parse('{}',$options)


Add additional annotations on BaseX admin function signatures.

xquery version "3.0";
(:~
:  BaseX admin module functions
:
: @see http://docs.basex.org/wiki/Admin_Module
:)
module namespace admin = "http://basex.org/modules/admin";

import module namespace a = "http://reecedunn.co.uk/xquery/annotations" at "res://reecedunn.co.uk/xquery/annotations.xqy";

declare %a:since("basex", "7.5") function admin:sessions() as element(session)* external;
declare %a:since("basex", "7.5") function admin:logs() as element(file)* external;
declare %a:since("basex", "7.5") function admin:logs($date as xs:string) as element(entry)* external;
declare %a:since("basex", "7.8.2") function admin:logs($date as xs:string, $merge as xs:boolean) as element(entry)* external;
declare %a:since("basex", "8.0") function admin:write-log($text as xs:string) as empty-sequence() external;
declare %a:since("basex", "8.3") function admin:write-log($text as xs:string, $type as xs:string) as empty-sequence() external;
declare %a:since("basex", "8.2") function admin:delete-logs($date as xs:string) as empty-sequence() external;
declare %a:since("basex", "7.5") %a:deprecated("basex", "8.0", "user:list-details#1") function admin:users() as element(user)* external;
declare %a:since("basex", "7.5") %a:deprecated("basex", "8.0", "user:list-details#2") function admin:users($db as xs:string) as element(user)* external;

xquery version "3.0";
(:~
:  BaseX admin module functions
:
: @see http://docs.basex.org/wiki/Admin_Module
:)
module namespace admin = "http://basex.org/modules/admin";

import module namespace a = "http://reecedunn.co.uk/xquery/annotations" at "res://reecedunn.co.uk/xquery/annotations.xqy";

declare %a:since("basex", "7.5") function admin:sessions() as element(session)* external;
declare %a:since("basex", "7.5") function admin:logs() as element(file)* external;
declare %a:since("basex", "7.5") function admin:logs($date as xs:string) as element(entry)* external;
declare %a:since("basex", "7.8.2") function admin:logs($date as xs:string, $merge as xs:boolean) as element(entry)* external;
declare %a:since("basex", "8.0") function admin:write-log($text as xs:string) as empty-sequence() external;
declare %a:since("basex", "8.3") function admin:write-log($text as xs:string, $type as xs:string) as empty-sequence() external;
declare %a:since("basex", "8.2") function admin:delete-logs($date as xs:string) as empty-sequence() external;
declare %a:until("basex", "8.0", "user:list-details#1") function admin:users() as element(user)* external;
declare %a:until("basex", "8.0", "user:list-details#2") function admin:users($db as xs:string) as element(user)* external;


parametrized output

parametrized output

xquery version "1.0";
import module namespace request="http://exist-db.org/xquery/request";
(:import module namespace cradmin  = "http://aac.ac.at/content_repository/admin" at "/db/cr/admin.xqm";:)
import module namespace cmdcheck = "http://clarin.eu/cmd/check" at "/db/cr/modules/cmd/cmd-check.xqm";
import module namespace diag =  "http://www.loc.gov/zing/srw/diagnostic/" at  "modules/diagnostics/diagnostics.xqm";
import module namespace smc = "http://clarin.eu/smc" at "/db/cr/modules/smc/smc.xqm";

let $config-path := request:get-parameter("config", "/db/cr/etc/config_mdrepo.xml"),
    $op := request:get-parameter("operation", ""),
    $config := doc($config-path),
    $format := request:get-parameter("x-format",'htmlpage'),
    $x-context := request:get-parameter("x-context", "") (: "univie.at:cpas"  "clarin.at:icltt:cr:stb" :),

    $result := if ($op eq '') then
                    cmdcheck:display-overview($config-path)
                else if (contains ($op, 'mappings-overview')) then
                    smc:mappings-overview($config, $format)
                else if (contains ($op, 'mappings')) then
                    smc:get-mappings($config, $x-context, (contains($op, 'run')), $format)
                else
                    diag:diagnostics("unsupported-operation", $op)
(:                    else cmdcheck:run-stats($config-path):)

return $result

xquery version "1.0";
import module namespace request="http://exist-db.org/xquery/request";
(:import module namespace cradmin  = "http://aac.ac.at/content_repository/admin" at "/db/cr/admin.xqm";:)
import module namespace cmdcheck = "http://clarin.eu/cmd/check" at "/db/cr/modules/cmd/cmd-check.xqm";

let $config-path := request:get-parameter("config", "/db/cr/etc/config_mdrepo.xml"),
    $op := request:get-parameter("operation", ""),
    $config := doc($config-path),
    $x-context := request:get-parameter("x-context", "") (: "univie.at:cpas"  "clarin.at:icltt:cr:stb" :),

    $result := if ($op eq '') then cmdcheck:display-overview($config-path)
                    else cmdcheck:run-stats($config-path)

return $result


ordering in test

ordering in test

import module namespace ddl = "http://www.zorba-xquery.com/modules/store/static/collections/ddl";
import module namespace dml = "http://www.zorba-xquery.com/modules/store/static/collections/dml";
import module namespace iddl = "http://www.zorba-xquery.com/modules/store/static/indexes/ddl";
import module namespace idml = "http://www.zorba-xquery.com/modules/store/static/indexes/dml";

import module namespace data = "http://www.test.com/" at "paging.xqlib";

declare function local:order($items)
{
  for $item in $items
  order by $item/name/text()
  return $item
};

data:init();
(
  <one>{
    local:order(idml:probe-index-point-value-skip($data:idx-age-point-val, 1, 3))
  }</one>,
  <two>{
    count(idml:probe-index-point-value-skip($data:idx-age-point-val, 1, 3))
  }</two>,
  <three>{
    local:order(idml:probe-index-range-value-skip($data:idx-age-range-val,
                                      2, 3, 4, true(), true(), true(), true()))
  }</three>,
  <four>{
    count(idml:probe-index-range-value-skip($data:idx-age-range-val,
                                            2, 3, 4,
                                            true(), true(), true(), true()))
  }</four>
)

import module namespace ddl = "http://www.zorba-xquery.com/modules/store/static/collections/ddl";
import module namespace dml = "http://www.zorba-xquery.com/modules/store/static/collections/dml";
import module namespace iddl = "http://www.zorba-xquery.com/modules/store/static/indexes/ddl";
import module namespace idml = "http://www.zorba-xquery.com/modules/store/static/indexes/dml";

import module namespace data = "http://www.test.com/" at "paging.xqlib";

data:init();
(
  <one>{
    idml:probe-index-point-value-skip($data:idx-age-point-val, 1, 3)
  }</one>,
  <two>{
    count(idml:probe-index-point-value-skip($data:idx-age-point-val, 1, 3))
  }</two>,
  <three>{
    idml:probe-index-range-value-skip($data:idx-age-range-val,
                                      2, 3, 4, true(), true(), true(), true())
  }</three>,
  <four>{
    count(idml:probe-index-range-value-skip($data:idx-age-range-val,
                                            2, 3, 4,
                                            true(), true(), true(), true()))
  }</four>
)


[bugfix] fixed XQTS runner to use file:read instead of deprecated util:file-read.

Port of rev 10525

svn path=/stable/eXist-1.4.x/; revision=10622

xquery version "1.0";

(: reuse webapp\xqts\xqts.xql :)

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

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"
at "java:org.exist.xquery.modules.xmldiff.XmlDiffModule";
import module namespace request="http://exist-db.org/xquery/request";
declare namespace system="http://exist-db.org/xquery/system";

declare option exist:output-size-limit "-1";

(: $Id: xqts.xql 7721 2008-05-07 15:36:39Z brihaye $ :)

(:~  ----------------------------------------------------------------------------
     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:

     * Run EXIST_HOME/webapp/xqts/build.xml and follow the instructions.
     -------------------------------------------------------------------------- :)

declare variable $xqts:CONFIG := xqts:initialize();
declare variable $xqts:XQTS_HOME := xqts:path-to-uri(concat(system:get-exist-home(),"/",$xqts:CONFIG/basedir/text()));
declare variable $xqts:IN_MEMORY := ($xqts:CONFIG/mode/text() = "memory");

declare function xqts:initialize() as element() {
    let $home := system:get-exist-home()
    let $path := concat($home, "/webapp/xqts")
    let $collection := xdb:create-collection("/db", "XQTS")
    let $config0 := if (doc-available("/db/XQTS/config.xml")) then doc("/db/XQTS/config.xml")/config else ()
    let $config :=
        if ($config0) then
            $config0
        else
            let $stored := xdb:store-files-from-pattern("/db/XQTS", $path, "*.xml", "text/xml")
            return
                if (doc-available("/db/XQTS/config.xml")) then doc("/db/XQTS/config.xml")/config else ()
    let $xqtsHome := $config/basedir/text()
    let $catalog := doc("/db/XQTS/XQTSCatalog.xml")
    let $sources0 := collection("/db/XQTS/TestSources")
    let $sourceCol := xdb:create-collection("/db/XQTS", "TestSources")
    let $collection1 := xdb:create-collection("/db/XQTS/TestSources", "collection1")
    let $collection2 := xdb:create-collection("/db/XQTS/TestSources", "collection2")
    let $sources :=
        if ($sources0) then
            $sources0
        else
            xdb:store-files-from-pattern($sourceCol, concat($xqtsHome, "TestSources"), "*.xml", "text/xml")
    let $docs :=
        (
            (: since XQTS URIs are resolved against /db/XQTS/TestSources :)
            xdb:copy($sourceCol, "/db/XQTS/TestSources/collection1", "bib.xml"),
            xdb:copy($sourceCol, "/db/XQTS/TestSources/collection1", "reviews.xml"),
            xdb:copy($sourceCol, "/db/XQTS/TestSources/collection2", "bib.xml"),
            xdb:copy($sourceCol, "/db/XQTS/TestSources/collection2", "reviews.xml"),
            xdb:copy($sourceCol, "/db/XQTS/TestSources/collection2", "books.xml"),
            xdb:store($collection, "hacked-tests.xml",
                xs:anyURI(xqts:path-to-uri(
                        concat($home, "/test/src/org/exist/xquery/xqts/hacked-tests.xml")
                    )))
        )
    return
        $config
};

declare function xqts:create-progress-file($testCount as xs:int) as empty() {
    let $results := xdb:store("/db/XQTS", "progress.xml",
        <progress total="{$testCount}" done="0" failed="0" passed="0" error="0"/>)
    return ()
};

declare function xqts:create-collections($group as element(catalog:test-group)) as node() {
    let $rootColl := xdb:create-collection("/db/XQTS", "test-results")
    let $temp := xdb:create-collection("/db/XQTS", "temp")
    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" error="0"/>)
    return
        doc($results)
};

declare function xqts:create-collections($parent as xs:string,
    $pathElements as element()+, $currentPath as xs:string) as xs:string {
    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 $filePath := concat( $xqts:XQTS_HOME, "Queries/XQuery/", $case/@FilePath, $query-name, ".xq" )
    let $xq-string := file:read($filePath, "UTF-8")
    return
        $xq-string
};

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:get-input-value-uri($input as element(catalog:input-file)) as item()* {
    if ($input eq "emptydoc") then
       ()
    else
        let $source := root($input)//catalog:source[@ID = $input/text()]
        return concat( $xqts:XQTS_HOME, "/", $source/@FileName )
};

declare function xqts:get-variable($case as element(catalog:test-case), $varName as xs:string) as item()* {
    let $filePath := concat($xqts:XQTS_HOME, "Queries/XQuery/", $case/@FilePath, $varName, ".xq")
    let $xq-string := file:read($filePath, "UTF-8")
    return
        if (empty($xq-string)) then
            ()
        else
            util:eval($xq-string)
};

declare function xqts:compute-specific-static-context($testCaseName as xs:string) as element()* {
    (
        <unbind-namespace uri="http://exist.sourceforge.net/NS/exist"/>
    ,
        if ($testCaseName eq "ForExpr013") then
            <output-size-limit value="-1"/>
        else if ($testCaseName eq "fn-current-time-4") then
            (: arbitrary date :)
            <current-dateTime value="2005-12-05T13:38:03.455-05:00"/>
        else if ($testCaseName eq "fn-current-time-6") then
            (: arbitrary date :)
            <current-dateTime value="2005-12-05T13:38:18.059-05:00"/>
        else if ($testCaseName eq "fn-current-time-7") then
            (: arbitrary date :)
            <current-dateTime value="2005-12-05T13:38:18.059-05:00"/>
        else if ($testCaseName eq "fn-current-time-10") then
            (: arbitrary date :)
            <current-dateTime value="2005-12-05T13:38:18.09-05:00"/>
        else if (starts-with($testCaseName, "fn-current-time-")) then
            (: arbitrary date :)
            <current-dateTime value="2005-12-05T10:15:03.408-05:00"/>
        else if ($testCaseName eq "fn-current-dateTime-6") then
            <current-dateTime value="2005-12-05T17:10:00.312-05:00"/>
        (: bloody lower-case ! :)
        else if ($testCaseName eq "fn-current-datetime-7") then
            <current-dateTime value="2005-12-05T17:10:00.312-05:00"/>
        else if ($testCaseName eq "fn-current-dateTime-10") then
            <current-dateTime value="2005-12-05T17:10:00.344-05:00"/>
        else if ($testCaseName eq "fn-current-dateTime-21") then
            <current-dateTime value="2005-12-05T17:10:00.453-05:00"/>
        else if ($testCaseName eq "fn-current-dateTime-24") then
            <current-dateTime value="2005-12-05T17:10:00.469-05:00"/>
        else
            <current-dateTime value="2005-12-05T17:10:00.203-05:00"/>
    ,
        if (starts-with($testCaseName, "modules-") or starts-with($testCaseName, "K-ModuleImport-")) then
            (
            <mapModule namespace="http://www.w3.org/TestModules/test1"
                uri="{xs:anyURI('xmldb:exist:///db/XQTS/TestSources/test1-lib.xq')}"/>,
            <mapModule namespace="http://www.w3.org/TestModules/test2"
                uri="{xs:anyURI('xmldb:exist:///db/XQTS/TestSources/test2-lib.xq')}"/>,
            <mapModule namespace="http://www.w3.org/TestModules/module1"
                uri="{xs:anyURI('xmldb:exist:///db/XQTS/TestSources/module1-lib.xq')}"/>,
            <mapModule namespace="http://www.w3.org/TestModules/module2"
                uri="{xs:anyURI('xmldb:exist:///db/XQTS/TestSources/module2-lib.xq')}"/>,
            <mapModule namespace="http://www.w3.org/TestModules/defs"
                uri="{xs:anyURI('xmldb:exist:///db/XQTS/TestSources/moduleDefs-lib.xq')}"/>,
            <mapModule namespace="http://www.w3.org/TestModules/diffns"
                uri="{xs:anyURI('xmldb:exist:///db/XQTS/TestSources/modulesdiffns-lib.xq')}"/>
            )
        else
            ()
    ,
        if (matches($testCaseName,"^fn-implicit-timezone-.*$") or
            matches($testCaseName,"^K-ContextImplicitTimezoneFunc-.*$")) then
            <implicit-timezone value="-PT5H"/>
        else
            ()
    )
};

declare function xqts:get-context-item($input as element(catalog:contextItem)?) as item()* {
    if (empty($input)) 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:path-to-uri($path as xs:string) as xs:string {
    if (starts-with($path, "file:///")) then
        replace($path, "\\", "/")
    else if (starts-with($path, "/")) then
        concat("file://", replace($path, "\\", "/"))
    else
        concat("file:///", replace($path, "\\", "/"))
};

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

declare function xqts:normalize-and-expand($text as item()*) as xs:string {
	replace(replace(replace(xqts:normalize-text($text), "&amp;", "&", "m"), "&lt;", "<"),
		"&gt;", ">")
};

declare function xqts:get-expected-results($testCase as element(catalog:test-case), $inMemory as xs:boolean) as element()* {
    let $testName := $testCase/@name
    let $hackedTest := /hack:test-cases/hack:test-case[@name = $testName]
    return
    (
        for $output in $testCase/catalog:output-file
        return
            let $compare :=
                if (exists($hackedTest/@compare)) then
                    $hackedTest/@compare
                else if ($output/@compare eq "Inspect") then
                    (: "Text" by default. OK in most if not all the cases :)
                    "Text"
                else
                    $output/@compare
            let $outputFilePath := concat($xqts:XQTS_HOME, "ExpectedTestResults/", $testCase/@FilePath,
                $output/text())
            return
                <expected-result compare="{$compare}">
                {
                    if ($compare eq "Text") then
                        xqts:normalize-and-expand(file:read($outputFilePath, "UTF-8"))
                    else if ($compare eq "UnnormalizedText") then
                        xqts:normalize-and-expand(file:read($outputFilePath, "UTF-8"))
                    else if ($compare eq "TextAsXML") then
                        xqts:normalize-and-expand(file:read($outputFilePath, "UTF-8"))
                    else if ($compare eq "XML") then
                        if ($inMemory) then
                            util:parse(file:read($outputFilePath, "UTF-8"))
                        else
                            util:catch(
                                "java.lang.Exception",
                                doc(xdb:store("/db/XQTS/temp", substring-before($output/text(), "."), xs:anyURI($outputFilePath), "text/xml")),
                                (: Handle unexpected exceptions :)
                                util:log("ERROR", concat("Exception while loading expected result: ", $util:exception-message))
                            )
                    else if ($compare eq "Fragment") then
                        let $xmlFrag := concat("<f>", file:read($outputFilePath, "UTF-8"), "</f>")
                        return
                            if ($inMemory) then
                                util:parse($xmlFrag)
                            else
                                util:catch(
                                    "java.lang.Exception",
                                    doc(xdb:store("/db/XQTS/temp", substring-before($output/text(), "."), $xmlFrag, "text/xml")),
                                    (: Handle unexpected exceptions :)
                                    util:log("ERROR", concat("Exception while loading expected result fragment: ", $util:exception-message))
                                )
                    else
                        util:log("ERROR", concat("Unknown comparison method: ", $compare))
                }
                </expected-result>
    ,
        (: due to a possible bug in the MemTreeBuilder, the parent element will be copied ! :)
        if (exists($hackedTest/hack:expected-result)) then
            $hackedTest/hack:expected-result
        else
            ()
    )
};

declare function xqts:execute-test-case($testCase as element(catalog:test-case), $inMemory as xs:boolean) as element()? {
    let $context :=
        <static-context>
            {
                xqts:compute-specific-static-context(string($testCase/@name))
            }
            {
                for $input in $testCase/catalog:input-file
                return
                    if ($inMemory) then
                        <variable name="{$input/@variable}" source="{xqts:get-input-value-uri($input)}"/>
                    else
                        <variable name="{$input/@variable}">{xqts:get-input-value($input)}</variable>,
                for $var in $testCase/catalog:input-query
                return
                    let $variable := xqts:get-variable($testCase, $var/@name)
                    return
                        <variable name="{$var/@variable}" type="{util:get-sequence-type($variable)}">{$variable}</variable>,
                for $input in $testCase/catalog:input-URI
                return
                    if ($input eq "works") then
                        <variable name="{$input/@variable}" type="xs:anyURI">{xs:anyURI("/db/XQTS/TestSources/works.xml")}</variable>
                    else if ($input eq "works-mod") then
                        <variable name="{$input/@variable}" type="xs:anyURI">{xs:anyURI("/db/XQTS/TestSources/works-mod.xml")}</variable>
                    else
                        <variable name="{$input/@variable}" type="xs:anyURI">{xs:anyURI(concat("/db/XQTS/TestSources/", $input))}</variable>
            }
            {
                for $input in $testCase/catalog:defaultCollection
                return
                    <staticallyKnownDocuments>{concat("/db/XQTS/TestSources/", $input)}</staticallyKnownDocuments>
            }
        </static-context>
    let $query := xqts:get-query($testCase)
    let $expectedResults := xqts:get-expected-results($testCase, $inMemory)
    let $formatedResult :=
        util:catch(
            "java.lang.Exception",
            let $raw_result := util:eval-with-context($query, $context, false(), xqts:get-context-item($testCase/catalog:contextItem))
            return
                xqts:compute-result($testCase, $query, $raw_result, $expectedResults),

            (: Exception processing :)
            (: Only expected exception and got exception. No check on if it is the right exception, though. :)
            if (fn:exists($testCase//catalog:expected-error) and empty($expectedResults)) then
               <test-case name="{$testCase/@name}" result="pass" dateRun="{util:system-time()}" print="expected-exception-got-exception">
                    (: TODO : check exception code :)
                    <exception>{$util:exception-message}</exception>
                    {
                        for $expected-error in $testCase//catalog:expected-error
                        return
                            <expected-error>{$expected-error/text()}</expected-error>
                    }
                    <query>{$query}</query>
               </test-case>
            (: Expected either output or exception. :)
            else if (fn:exists($testCase//catalog:expected-error) and fn:exists($expectedResults)) then
                <test-case name="{$testCase/@name}" result="pass" dateRun="{util:system-time()}" print="expected-exception-or-output">
                    (: TODO : check exception code :)
                    <exception>{$util:exception-message}</exception>
                    {
                        for $expectedResult in $expectedResults
                        return
                            $expectedResult
                    }
                    {
                        for $expected-error in $testCase//catalog:expected-error
                        return
                            <expected-error>{$expected-error/text()}</expected-error>
                    }
                    <query>{$query}</query>
                </test-case>
            (: Expected output, but got exception. :)
            else if (fn:empty($testCase//catalog:expected-error) and fn:exists($testCase//catalog:output-file)) then
                <test-case name="{$testCase/@name}" result="fail" dateRun="{util:system-time()}" print="expected-output-got-exception">
                    <exception>{$util:exception-message}</exception>
                    {
                        for $expectedResult in $expectedResults
                        return
                            $expectedResult
                    }
                    <query>{$query}</query>
                </test-case>
            (: Don't know what is expected but got here anyway. :)
            else
                <test-case name="{$testCase/@name}" result="fail" dateRun="{util:system-time()}" print="unhandled-error">
                    <exception>{$util:exception-message}</exception>
                    {
                        for $expectedResult in $expectedResults
                        return
                            $expectedResult
                    }
                    {
                        for $expected-error in $testCase//catalog:expected-error
                        return
                            <expected-error>{$expected-error/text()}</expected-error>
                    }
                    <query>{$query}</query>
                </test-case>
        )
        let $log :=
            if (fn:empty($formatedResult)) then
                util:log("DEBUG", ("Unable to generate a formated result: ", xs:string($testCase/@name)))
            else
                ()
    return $formatedResult
};

declare function xqts:compute-result($testCase as element(catalog:test-case), $query as xs:string, $result as item()*, $expectedResults as element()* ) {
    let $passed := some $expectedResultTest in (
        for $expectedResult in $expectedResults
        return
            let $comparisonMethod := $expectedResult/@compare
            return
                if ($comparisonMethod eq "Text") then
                    (: don't use text() because () neq "" :)
                    $expectedResult/string() eq xqts:normalize-text($result)
                else if ($comparisonMethod eq "UnnormalizedText") then
                    (: don't use text() because () neq "" :)
                    (: join the results in one single string :)
                    $expectedResult/string() eq string-join($result, "")
                else if ($comparisonMethod eq "TextAsXML") then
                    xdiff:compare($expectedResult/*, util:eval($result))
                else if ($comparisonMethod eq "XML") then
                    xdiff:compare($expectedResult/*, $result)
                else if ($comparisonMethod eq "Fragment") then
                    xdiff:compare($expectedResult/*, <f>{$result}</f>)
                (: unlikely :)
                else
                    false()
    ) satisfies $expectedResultTest
    return
        xqts:format-result($testCase, $passed, $query, $result, $expectedResults)
};

declare function xqts:format-result($testCase as element(catalog:test-case), $passed as xs:boolean, $query as xs:string,
    $result as item()*, $expectedResults as item()*) as element() {
    <test-case name="{$testCase/@name}" result="{if ($passed) then 'pass' else 'fail'}" dateRun="{util:system-time()}" print="result-without-exception">
        <result>{$result}</result>
        {
            for $expectedResult in $expectedResults
            return
                $expectedResult
        }
        {
            for $expected-error in $testCase//catalog:expected-error
            return
                <expected-error>{$expected-error/text()}</expected-error>
        }
        <query>{$query}</query>
    </test-case>
};

declare function xqts:run-single-test-case($case as element(catalog:test-case),
    $resultRoot as element()?, $inMemory as xs:boolean) as empty() {
    let $result := xqts:execute-test-case($case, $inMemory)
    return
        util:catch(
            "java.lang.Exception",
            update insert $result into $resultRoot,
            update insert <test-case name="{$case/@name}" result="fail" dateRun="{util:system-time()}" print="unhandled-error">
                    <exception>Caught late exception while inserting result: {$util:exception-message}</exception>
               </test-case> into $resultRoot
        ),
        let $added := $resultRoot/test-case[@name = $case/@name]
        let $log :=
            if (fn:empty($added)) then
                util:log("DEBUG", ("FAILURE in-memory-frag in test: ", xs:string($case/@name)))
            else
                ()
        return
            if ($added) then
                xqts:report-progress($added)
            else
                ()
};

declare function xqts:run-test-group($group as element(catalog:test-group), $inMemory as xs:boolean) 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, $inMemory),
                    if (fn:exists($tests) and fn:exists($resultsDoc/test-result)) 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, $inMemory)
        )
};

declare function xqts:test-single($name as xs:string, $inMemory as xs:boolean) 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, $inMemory)
            return
                $resultsDoc/test-result
        else
            <error>Test case not found: {$name}.</error>
};

declare function xqts:test-group($groupName as xs:string, $inMemory as xs:boolean) as empty() {
    let $group := //catalog:test-group[@name = $groupName]
    return (
        xqts:create-progress-file(count($group//catalog:test-case)),
        xqts:run-test-group($group, $inMemory)
    )
};

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

declare function xqts:report-progress($test-case as element()) as empty() {
    let $result := string($test-case/@result)
    let $progress := if (doc-available("/db/XQTS/progress.xml")) then doc("/db/XQTS/progress.xml")/progress else ()
    let $counter := $progress/@done
    return (
        update value $counter with xs:int($counter + 1),
        if ($result eq 'fail') then
            update value $progress/@failed with xs:int($progress/@failed + 1)
        else if ($result eq 'error') then
            update value $progress/@error with xs:int($progress/@error + 1)
        else
            update value $progress/@passed with xs:int($progress/@passed + 1)
    )
};

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'])
    let $error := count($result//test-case[@result = 'error'])
    return (
        update value $result/@passed with xs:int($passed),
        update value $result/@failed with xs:int($failed),
        update value $result/@error with xs:int($error)
    )
};

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

declare variable $testGroup external;
declare variable $testCase external;

let $XQTSCatalog := xmldb:document('/db/XQTS/XQTSCatalog.xml'),
    $case := $XQTSCatalog//catalog:test-group[@name = $testGroup]/catalog:test-case[@name = $testCase]
return
xqts:execute-test-case($case, fn:true())

xquery version "1.0";

(: reuse webapp\xqts\xqts.xql :)

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

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"
at "java:org.exist.xquery.modules.xmldiff.XmlDiffModule";
import module namespace request="http://exist-db.org/xquery/request";
declare namespace system="http://exist-db.org/xquery/system";

declare option exist:output-size-limit "-1";

(: $Id: xqts.xql 7721 2008-05-07 15:36:39Z brihaye $ :)

(:~  ----------------------------------------------------------------------------
     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:

     * Run EXIST_HOME/webapp/xqts/build.xml and follow the instructions.
     -------------------------------------------------------------------------- :)

declare variable $xqts:CONFIG := xqts:initialize();
declare variable $xqts:XQTS_HOME := xqts:path-to-uri(concat(system:get-exist-home(),"/",$xqts:CONFIG/basedir/text()));
declare variable $xqts:IN_MEMORY := ($xqts:CONFIG/mode/text() = "memory");

declare function xqts:initialize() as element() {
    let $home := system:get-exist-home()
    let $path := concat($home, "/webapp/xqts")
    let $collection := xdb:create-collection("/db", "XQTS")
    let $config0 := if (doc-available("/db/XQTS/config.xml")) then doc("/db/XQTS/config.xml")/config else ()
    let $config :=
        if ($config0) then
            $config0
        else
            let $stored := xdb:store-files-from-pattern("/db/XQTS", $path, "*.xml", "text/xml")
            return
                if (doc-available("/db/XQTS/config.xml")) then doc("/db/XQTS/config.xml")/config else ()
    let $xqtsHome := $config/basedir/text()
    let $catalog := doc("/db/XQTS/XQTSCatalog.xml")
    let $sources0 := collection("/db/XQTS/TestSources")
    let $sourceCol := xdb:create-collection("/db/XQTS", "TestSources")
    let $collection1 := xdb:create-collection("/db/XQTS/TestSources", "collection1")
    let $collection2 := xdb:create-collection("/db/XQTS/TestSources", "collection2")
    let $sources :=
        if ($sources0) then
            $sources0
        else
            xdb:store-files-from-pattern($sourceCol, concat($xqtsHome, "TestSources"), "*.xml", "text/xml")
    let $docs :=
        (
            (: since XQTS URIs are resolved against /db/XQTS/TestSources :)
            xdb:copy($sourceCol, "/db/XQTS/TestSources/collection1", "bib.xml"),
            xdb:copy($sourceCol, "/db/XQTS/TestSources/collection1", "reviews.xml"),
            xdb:copy($sourceCol, "/db/XQTS/TestSources/collection2", "bib.xml"),
            xdb:copy($sourceCol, "/db/XQTS/TestSources/collection2", "reviews.xml"),
            xdb:copy($sourceCol, "/db/XQTS/TestSources/collection2", "books.xml"),
            xdb:store($collection, "hacked-tests.xml",
                xs:anyURI(xqts:path-to-uri(
                        concat($home, "/test/src/org/exist/xquery/xqts/hacked-tests.xml")
                    )))
        )
    return
        $config
};

declare function xqts:create-progress-file($testCount as xs:int) as empty() {
    let $results := xdb:store("/db/XQTS", "progress.xml",
        <progress total="{$testCount}" done="0" failed="0" passed="0" error="0"/>)
    return ()
};

declare function xqts:create-collections($group as element(catalog:test-group)) as node() {
    let $rootColl := xdb:create-collection("/db/XQTS", "test-results")
    let $temp := xdb:create-collection("/db/XQTS", "temp")
    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" error="0"/>)
    return
        doc($results)
};

declare function xqts:create-collections($parent as xs:string,
    $pathElements as element()+, $currentPath as xs:string) as xs:string {
    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 $filePath := concat( $xqts:XQTS_HOME, "Queries/XQuery/", $case/@FilePath, $query-name, ".xq" )
    let $xq-string := util:file-read($filePath, "UTF-8")
    return
        $xq-string
};

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:get-input-value-uri($input as element(catalog:input-file)) as item()* {
    if ($input eq "emptydoc") then
       ()
    else
        let $source := root($input)//catalog:source[@ID = $input/text()]
        return concat( $xqts:XQTS_HOME, "/", $source/@FileName )
};

declare function xqts:get-variable($case as element(catalog:test-case), $varName as xs:string) as item()* {
    let $filePath := concat($xqts:XQTS_HOME, "Queries/XQuery/", $case/@FilePath, $varName, ".xq")
    let $xq-string := util:file-read($filePath, "UTF-8")
    return
        if (empty($xq-string)) then
            ()
        else
            util:eval($xq-string)
};

declare function xqts:compute-specific-static-context($testCaseName as xs:string) as element()* {
    (
        <unbind-namespace uri="http://exist.sourceforge.net/NS/exist"/>
    ,
        if ($testCaseName eq "ForExpr013") then
            <output-size-limit value="-1"/>
        else if ($testCaseName eq "fn-current-time-4") then
            (: arbitrary date :)
            <current-dateTime value="2005-12-05T13:38:03.455-05:00"/>
        else if ($testCaseName eq "fn-current-time-6") then
            (: arbitrary date :)
            <current-dateTime value="2005-12-05T13:38:18.059-05:00"/>
        else if ($testCaseName eq "fn-current-time-7") then
            (: arbitrary date :)
            <current-dateTime value="2005-12-05T13:38:18.059-05:00"/>
        else if ($testCaseName eq "fn-current-time-10") then
            (: arbitrary date :)
            <current-dateTime value="2005-12-05T13:38:18.09-05:00"/>
        else if (starts-with($testCaseName, "fn-current-time-")) then
            (: arbitrary date :)
            <current-dateTime value="2005-12-05T10:15:03.408-05:00"/>
        else if ($testCaseName eq "fn-current-dateTime-6") then
            <current-dateTime value="2005-12-05T17:10:00.312-05:00"/>
        (: bloody lower-case ! :)
        else if ($testCaseName eq "fn-current-datetime-7") then
            <current-dateTime value="2005-12-05T17:10:00.312-05:00"/>
        else if ($testCaseName eq "fn-current-dateTime-10") then
            <current-dateTime value="2005-12-05T17:10:00.344-05:00"/>
        else if ($testCaseName eq "fn-current-dateTime-21") then
            <current-dateTime value="2005-12-05T17:10:00.453-05:00"/>
        else if ($testCaseName eq "fn-current-dateTime-24") then
            <current-dateTime value="2005-12-05T17:10:00.469-05:00"/>
        else
            <current-dateTime value="2005-12-05T17:10:00.203-05:00"/>
    ,
        if (starts-with($testCaseName, "modules-") or starts-with($testCaseName, "K-ModuleImport-")) then
            (
            <mapModule namespace="http://www.w3.org/TestModules/test1"
                uri="{xs:anyURI('xmldb:exist:///db/XQTS/TestSources/test1-lib.xq')}"/>,
            <mapModule namespace="http://www.w3.org/TestModules/test2"
                uri="{xs:anyURI('xmldb:exist:///db/XQTS/TestSources/test2-lib.xq')}"/>,
            <mapModule namespace="http://www.w3.org/TestModules/module1"
                uri="{xs:anyURI('xmldb:exist:///db/XQTS/TestSources/module1-lib.xq')}"/>,
            <mapModule namespace="http://www.w3.org/TestModules/module2"
                uri="{xs:anyURI('xmldb:exist:///db/XQTS/TestSources/module2-lib.xq')}"/>,
            <mapModule namespace="http://www.w3.org/TestModules/defs"
                uri="{xs:anyURI('xmldb:exist:///db/XQTS/TestSources/moduleDefs-lib.xq')}"/>,
            <mapModule namespace="http://www.w3.org/TestModules/diffns"
                uri="{xs:anyURI('xmldb:exist:///db/XQTS/TestSources/modulesdiffns-lib.xq')}"/>
            )
        else
            ()
    ,
        if (matches($testCaseName,"^fn-implicit-timezone-.*$") or
            matches($testCaseName,"^K-ContextImplicitTimezoneFunc-.*$")) then
            <implicit-timezone value="-PT5H"/>
        else
            ()
    )
};

declare function xqts:get-context-item($input as element(catalog:contextItem)?) as item()* {
    if (empty($input)) 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:path-to-uri($path as xs:string) as xs:string {
    if (starts-with($path, "file:///")) then
        replace($path, "\\", "/")
    else if (starts-with($path, "/")) then
        concat("file://", replace($path, "\\", "/"))
    else
        concat("file:///", replace($path, "\\", "/"))
};

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

declare function xqts:normalize-and-expand($text as item()*) as xs:string {
	replace(replace(replace(xqts:normalize-text($text), "&amp;", "&", "m"), "&lt;", "<"),
		"&gt;", ">")
};

declare function xqts:get-expected-results($testCase as element(catalog:test-case), $inMemory as xs:boolean) as element()* {
    let $testName := $testCase/@name
    let $hackedTest := /hack:test-cases/hack:test-case[@name = $testName]
    return
    (
        for $output in $testCase/catalog:output-file
        return
            let $compare :=
                if (exists($hackedTest/@compare)) then
                    $hackedTest/@compare
                else if ($output/@compare eq "Inspect") then
                    (: "Text" by default. OK in most if not all the cases :)
                    "Text"
                else
                    $output/@compare
            let $outputFilePath := concat($xqts:XQTS_HOME, "ExpectedTestResults/", $testCase/@FilePath,
                $output/text())
            return
                <expected-result compare="{$compare}">
                {
                    if ($compare eq "Text") then
                        xqts:normalize-and-expand(util:file-read($outputFilePath, "UTF-8"))
                    else if ($compare eq "UnnormalizedText") then
                        xqts:normalize-and-expand(util:file-read($outputFilePath, "UTF-8"))
                    else if ($compare eq "TextAsXML") then
                        xqts:normalize-and-expand(util:file-read($outputFilePath, "UTF-8"))
                    else if ($compare eq "XML") then
                        if ($inMemory) then
                            util:parse(util:file-read($outputFilePath, "UTF-8"))
                        else
                            util:catch(
                                "java.lang.Exception",
                                doc(xdb:store("/db/XQTS/temp", substring-before($output/text(), "."), xs:anyURI($outputFilePath), "text/xml")),
                                (: Handle unexpected exceptions :)
                                util:log("ERROR", concat("Exception while loading expected result: ", $util:exception-message))
                            )
                    else if ($compare eq "Fragment") then
                        let $xmlFrag := concat("<f>", util:file-read($outputFilePath, "UTF-8"), "</f>")
                        return
                            if ($inMemory) then
                                util:parse($xmlFrag)
                            else
                                util:catch(
                                    "java.lang.Exception",
                                    doc(xdb:store("/db/XQTS/temp", substring-before($output/text(), "."), $xmlFrag, "text/xml")),
                                    (: Handle unexpected exceptions :)
                                    util:log("ERROR", concat("Exception while loading expected result fragment: ", $util:exception-message))
                                )
                    else
                        util:log("ERROR", concat("Unknown comparison method: ", $compare))
                }
                </expected-result>
    ,
        (: due to a possible bug in the MemTreeBuilder, the parent element will be copied ! :)
        if (exists($hackedTest/hack:expected-result)) then
            $hackedTest/hack:expected-result
        else
            ()
    )
};

declare function xqts:execute-test-case($testCase as element(catalog:test-case), $inMemory as xs:boolean) as element()? {
    let $context :=
        <static-context>
            {
                xqts:compute-specific-static-context(string($testCase/@name))
            }
            {
                for $input in $testCase/catalog:input-file
                return
                    if ($inMemory) then
                        <variable name="{$input/@variable}" source="{xqts:get-input-value-uri($input)}"/>
                    else
                        <variable name="{$input/@variable}">{xqts:get-input-value($input)}</variable>,
                for $var in $testCase/catalog:input-query
                return
                    let $variable := xqts:get-variable($testCase, $var/@name)
                    return
                        <variable name="{$var/@variable}" type="{util:get-sequence-type($variable)}">{$variable}</variable>,
                for $input in $testCase/catalog:input-URI
                return
                    if ($input eq "works") then
                        <variable name="{$input/@variable}" type="xs:anyURI">{xs:anyURI("/db/XQTS/TestSources/works.xml")}</variable>
                    else if ($input eq "works-mod") then
                        <variable name="{$input/@variable}" type="xs:anyURI">{xs:anyURI("/db/XQTS/TestSources/works-mod.xml")}</variable>
                    else
                        <variable name="{$input/@variable}" type="xs:anyURI">{xs:anyURI(concat("/db/XQTS/TestSources/", $input))}</variable>
            }
            {
                for $input in $testCase/catalog:defaultCollection
                return
                    <staticallyKnownDocuments>{concat("/db/XQTS/TestSources/", $input)}</staticallyKnownDocuments>
            }
        </static-context>
    let $query := xqts:get-query($testCase)
    let $expectedResults := xqts:get-expected-results($testCase, $inMemory)
    let $formatedResult :=
        util:catch(
            "java.lang.Exception",
            let $raw_result := util:eval-with-context($query, $context, false(), xqts:get-context-item($testCase/catalog:contextItem))
            return
                xqts:compute-result($testCase, $query, $raw_result, $expectedResults),

            (: Exception processing :)
            (: Only expected exception and got exception. No check on if it is the right exception, though. :)
            if (fn:exists($testCase//catalog:expected-error) and empty($expectedResults)) then
               <test-case name="{$testCase/@name}" result="pass" dateRun="{util:system-time()}" print="expected-exception-got-exception">
                    (: TODO : check exception code :)
                    <exception>{$util:exception-message}</exception>
                    {
                        for $expected-error in $testCase//catalog:expected-error
                        return
                            <expected-error>{$expected-error/text()}</expected-error>
                    }
                    <query>{$query}</query>
               </test-case>
            (: Expected either output or exception. :)
            else if (fn:exists($testCase//catalog:expected-error) and fn:exists($expectedResults)) then
                <test-case name="{$testCase/@name}" result="pass" dateRun="{util:system-time()}" print="expected-exception-or-output">
                    (: TODO : check exception code :)
                    <exception>{$util:exception-message}</exception>
                    {
                        for $expectedResult in $expectedResults
                        return
                            $expectedResult
                    }
                    {
                        for $expected-error in $testCase//catalog:expected-error
                        return
                            <expected-error>{$expected-error/text()}</expected-error>
                    }
                    <query>{$query}</query>
                </test-case>
            (: Expected output, but got exception. :)
            else if (fn:empty($testCase//catalog:expected-error) and fn:exists($testCase//catalog:output-file)) then
                <test-case name="{$testCase/@name}" result="fail" dateRun="{util:system-time()}" print="expected-output-got-exception">
                    <exception>{$util:exception-message}</exception>
                    {
                        for $expectedResult in $expectedResults
                        return
                            $expectedResult
                    }
                    <query>{$query}</query>
                </test-case>
            (: Don't know what is expected but got here anyway. :)
            else
                <test-case name="{$testCase/@name}" result="fail" dateRun="{util:system-time()}" print="unhandled-error">
                    <exception>{$util:exception-message}</exception>
                    {
                        for $expectedResult in $expectedResults
                        return
                            $expectedResult
                    }
                    {
                        for $expected-error in $testCase//catalog:expected-error
                        return
                            <expected-error>{$expected-error/text()}</expected-error>
                    }
                    <query>{$query}</query>
                </test-case>
        )
        let $log :=
            if (fn:empty($formatedResult)) then
                util:log("DEBUG", ("Unable to generate a formated result: ", xs:string($testCase/@name)))
            else
                ()
    return $formatedResult
};

declare function xqts:compute-result($testCase as element(catalog:test-case), $query as xs:string, $result as item()*, $expectedResults as element()* ) {
    let $passed := some $expectedResultTest in (
        for $expectedResult in $expectedResults
        return
            let $comparisonMethod := $expectedResult/@compare
            return
                if ($comparisonMethod eq "Text") then
                    (: don't use text() because () neq "" :)
                    $expectedResult/string() eq xqts:normalize-text($result)
                else if ($comparisonMethod eq "UnnormalizedText") then
                    (: don't use text() because () neq "" :)
                    (: join the results in one single string :)
                    $expectedResult/string() eq string-join($result, "")
                else if ($comparisonMethod eq "TextAsXML") then
                    xdiff:compare($expectedResult/*, util:eval($result))
                else if ($comparisonMethod eq "XML") then
                    xdiff:compare($expectedResult/*, $result)
                else if ($comparisonMethod eq "Fragment") then
                    xdiff:compare($expectedResult/*, <f>{$result}</f>)
                (: unlikely :)
                else
                    false()
    ) satisfies $expectedResultTest
    return
        xqts:format-result($testCase, $passed, $query, $result, $expectedResults)
};

declare function xqts:format-result($testCase as element(catalog:test-case), $passed as xs:boolean, $query as xs:string,
    $result as item()*, $expectedResults as item()*) as element() {
    <test-case name="{$testCase/@name}" result="{if ($passed) then 'pass' else 'fail'}" dateRun="{util:system-time()}" print="result-without-exception">
        <result>{$result}</result>
        {
            for $expectedResult in $expectedResults
            return
                $expectedResult
        }
        {
            for $expected-error in $testCase//catalog:expected-error
            return
                <expected-error>{$expected-error/text()}</expected-error>
        }
        <query>{$query}</query>
    </test-case>
};

declare function xqts:run-single-test-case($case as element(catalog:test-case),
    $resultRoot as element()?, $inMemory as xs:boolean) as empty() {
    let $result := xqts:execute-test-case($case, $inMemory)
    return
        util:catch(
            "java.lang.Exception",
            update insert $result into $resultRoot,
            update insert <test-case name="{$case/@name}" result="fail" dateRun="{util:system-time()}" print="unhandled-error">
                    <exception>Caught late exception while inserting result: {$util:exception-message}</exception>
               </test-case> into $resultRoot
        ),
        let $added := $resultRoot/test-case[@name = $case/@name]
        let $log :=
            if (fn:empty($added)) then
                util:log("DEBUG", ("FAILURE in-memory-frag in test: ", xs:string($case/@name)))
            else
                ()
        return
            if ($added) then
                xqts:report-progress($added)
            else
                ()
};

declare function xqts:run-test-group($group as element(catalog:test-group), $inMemory as xs:boolean) 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, $inMemory),
                    if (fn:exists($tests) and fn:exists($resultsDoc/test-result)) 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, $inMemory)
        )
};

declare function xqts:test-single($name as xs:string, $inMemory as xs:boolean) 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, $inMemory)
            return
                $resultsDoc/test-result
        else
            <error>Test case not found: {$name}.</error>
};

declare function xqts:test-group($groupName as xs:string, $inMemory as xs:boolean) as empty() {
    let $group := //catalog:test-group[@name = $groupName]
    return (
        xqts:create-progress-file(count($group//catalog:test-case)),
        xqts:run-test-group($group, $inMemory)
    )
};

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

declare function xqts:report-progress($test-case as element()) as empty() {
    let $result := string($test-case/@result)
    let $progress := if (doc-available("/db/XQTS/progress.xml")) then doc("/db/XQTS/progress.xml")/progress else ()
    let $counter := $progress/@done
    return (
        update value $counter with xs:int($counter + 1),
        if ($result eq 'fail') then
            update value $progress/@failed with xs:int($progress/@failed + 1)
        else if ($result eq 'error') then
            update value $progress/@error with xs:int($progress/@error + 1)
        else
            update value $progress/@passed with xs:int($progress/@passed + 1)
    )
};

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'])
    let $error := count($result//test-case[@result = 'error'])
    return (
        update value $result/@passed with xs:int($passed),
        update value $result/@failed with xs:int($failed),
        update value $result/@error with xs:int($error)
    )
};

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

declare variable $testGroup external;
declare variable $testCase external;

let $XQTSCatalog := xmldb:document('/db/XQTS/XQTSCatalog.xml'),
    $case := $XQTSCatalog//catalog:test-group[@name = $testGroup]/catalog:test-case[@name = $testCase]
return
xqts:execute-test-case($case, fn:true())


[test] Furthers tests for make-fragment and display-root-namespace arguments to util:get-fragment-between

xquery version "3.1";

module namespace gfb = "http://exist-db.org/test/util/get-fragment-between";

declare namespace test = "http://exist-db.org/xquery/xqsuite";
declare namespace tei = "http://www.tei-c.org/ns/1.0";

declare variable $gfb:DOC1 :=
    <TEI xmlns="http://www.tei-c.org/ns/1.0">
        <text>
            <front>
                <pb facs="1.jpg"/>

Aus dem Leben einer Kartoffel.

                <pb facs="2.jpg"/>
            </front>

                <pb facs="3.jpg"/>

Hubertus Knoll spazierte über das <pb facs="4.jpg"/> Feld.

                <pb facs="5.jpg"/>

        </text>
    </TEI>;

declare variable $gfb:DOC2 :=
    <root xmlns="http://exist-db.org/xquery/xqsuite">
        <x/>
        <y/>
        <z/>
    </root>;

declare
    %test:setUp
function gfb:setup() {
    xmldb:create-collection("/db", "test-gfb"),
    xmldb:store("/db/test-gfb", "doc1.xml", $gfb:DOC1),
    xmldb:store("/db/test-gfb", "doc2.xml", $gfb:DOC2)
};

declare
    %test:tearDown
function gfb:teardown() {
    xmldb:remove("/db/test-gfb")
};

declare
    %test:assertEquals('<pb facs="1.jpg"></pb>
Aus dem Leben einer Kartoffel.
')
function gfb:tei-fragment() {
    let $doc := doc("/db/test-gfb/doc1.xml")
    let $pbs := $doc//tei:pb
    let $pb1 := $pbs[1]
    let $pb2 := $pbs[2]
    return
        util:get-fragment-between($pb1, $pb2, false(), false())
};

declare
    %test:assertEquals('<TEI><text><front><pb facs="1.jpg"></pb>
Aus dem Leben einer Kartoffel.
</front></text></TEI>')
function gfb:tei-fragment-root() {
    let $doc := doc("/db/test-gfb/doc1.xml")
    let $pbs := $doc//tei:pb
    let $pb1 := $pbs[1]
    let $pb2 := $pbs[2]
    return
        fn:replace(util:get-fragment-between($pb1, $pb2, true(), false()), "\n", "")
};

declare
    %test:assertEquals('<TEI xmlns="http://www.tei-c.org/ns/1.0"><text><front><pb facs="1.jpg"></pb>
Aus dem Leben einer Kartoffel.
</front></text></TEI>')
function gfb:tei-fragment-root-ns() {
    let $doc := doc("/db/test-gfb/doc1.xml")
    let $pbs := $doc//tei:pb
    let $pb1 := $pbs[1]
    let $pb2 := $pbs[2]
    return
        fn:replace(util:get-fragment-between($pb1, $pb2, true(), true()), "\n", "")
};

declare
    %test:assertEquals("<x></x>")
function gfb:fragment-no-namespace() {
    let $doc := doc("/db/test-gfb/doc2.xml")
    let $elems := $doc/test:root/*
    let $fragment := util:get-fragment-between($elems[1], $elems[2], false(), false())
    return
        $fragment
};

declare
    %test:assertEquals("<root><x></x></root>")
function gfb:wrapped-fragment-no-namespace() {
    let $doc := doc("/db/test-gfb/doc2.xml")
    let $elems := $doc/test:root/*
    let $fragment := util:get-fragment-between($elems[1], $elems[2], true(), false())
    return
        $fragment => replace("\s", "")
};

declare
    %test:assertTrue
function gfb:wrapped-fragment-is-parseable() {
    let $doc := doc("/db/test-gfb/doc2.xml")
    let $elems := $doc/test:root/*
    let $fragment := util:get-fragment-between($elems[1], $elems[2], true(), false())
    let $parsed := try { parse-xml($fragment) } catch * { $err:code }
    return
        $parsed instance of document-node(element(root))
};

xquery version "3.1";

module namespace gfb = "http://exist-db.org/test/util/get-fragment-between";

declare namespace test = "http://exist-db.org/xquery/xqsuite";
declare namespace tei = "http://www.tei-c.org/ns/1.0";

declare variable $gfb:DOC1 :=
    <TEI xmlns="http://www.tei-c.org/ns/1.0">
        <text>
            <front>
                <pb facs="1.jpg"/>

Aus dem Leben einer Kartoffel.

                <pb facs="2.jpg"/>
            </front>

                <pb facs="3.jpg"/>

Hubertus Knoll spazierte über das <pb facs="4.jpg"/> Feld.

                <pb facs="5.jpg"/>

        </text>
    </TEI>;

declare variable $gfb:DOC2 :=
    <root xmlns="http://exist-db.org/xquery/xqsuite">
        <x/>
        <y/>
        <z/>
    </root>;

declare
    %test:setUp
function gfb:setup() {
    xmldb:create-collection("/db", "test-gfb"),
    xmldb:store("/db/test-gfb", "doc1.xml", $gfb:DOC1),
    xmldb:store("/db/test-gfb", "doc2.xml", $gfb:DOC2)
};

declare
    %test:tearDown
function gfb:teardown() {
    xmldb:remove("/db/test-gfb")
};

declare
    %test:assertEquals('<pb facs="1.jpg"></pb>
Aus dem Leben einer Kartoffel.
')
function gfb:tei-fragment() {
    let $doc := doc("/db/test-gfb/doc1.xml")
    let $pbs := $doc//tei:pb
    let $pb1 := $pbs[1]
    let $pb2 := $pbs[2]
    return
        util:get-fragment-between($pb1,$pb2,false(),true())
};

declare
    %test:assertEquals("<x></x>")
function gfb:fragment-no-namespace() {
    let $doc := doc("/db/test-gfb/doc2.xml")
    let $elems := $doc/test:root/*
    let $fragment := util:get-fragment-between($elems[1], $elems[2], false(), false())
    return
        $fragment
};

declare
    %test:assertEquals("<root><x></x></root>")
function gfb:wrapped-fragment-no-namespace() {
    let $doc := doc("/db/test-gfb/doc2.xml")
    let $elems := $doc/test:root/*
    let $fragment := util:get-fragment-between($elems[1], $elems[2], true(), false())
    return
        $fragment => replace("\s", "")
};

declare
    %test:assertTrue
function gfb:wrapped-fragment-is-parseable() {
    let $doc := doc("/db/test-gfb/doc2.xml")
    let $elems := $doc/test:root/*
    let $fragment := util:get-fragment-between($elems[1], $elems[2], true(), false())
    let $parsed := try { parse-xml($fragment) } catch * { $err:code }
    return
        $parsed instance of document-node(element(root))
};


Annotate mt:lookupWildcard as pending

Annotate mt:lookupWildcard as pending

xquery version "1.0";

module namespace mt="http://exist-db.org/xquery/test/maps";

import module namespace test="http://exist-db.org/xquery/xqsuite" at "resource:org/exist/xquery/lib/xqsuite/xqsuite.xql";

declare variable $mt:daysOfWeek :=
    map {
        "Su" : "Sunday",
        "Mo" : "Monday",
        "Tu" : "Tuesday",
        "We" : "Wednesday",
        "Th" : "Thursday",
        "Fr" : "Friday",
        "Sa" : "Saturday"
    };

declare %test:assertEquals("Wednesday") function mt:createLiteral1() {
    $mt:daysOfWeek("We")
};

declare %test:assertEquals("Wednesday") function mt:createWithIntKeys() {
    let $map := map {
        1 : "Sunday",
        2 : "Monday",
        3 : "Tuesday",
        4 : "Wednesday",
        5 : "Thursday",
        6 : "Friday",
        7 : "Saturday"
    }
    return
        $map(4)
};

declare %test:assertEquals(20) function mt:createWithFunctionValue() {
    let $fn := function($p) { $p * 2 }
    let $map := map { "callback" : $fn }
    return
        map:get($map, "callback")(10)
};

declare %test:assertEquals("Sunday") function mt:createWithEntry() {
    let $days := ("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday")
    let $map := map:merge((map(function($day) { map:entry(substring($day, 1, 2), $day) }, $days)))
    return
        $map("Su")
};

declare %test:assertXPath("count($result) = 8") %test:assertXPath("7 = $result")
function mt:createFromTwoMaps() {
    let $week := map{0: "Sonntag", 1: "Montag", 2: "Dienstag", 3: "Mittwoch", 4: "Donnerstag", 5: "Freitag", 6: "Samstag"}
    let $map := map:merge(($week, map { 7 : "Sonntag" }))
    return
        map:keys($map)
};

declare
    %test:assertEquals(0)
function mt:size-empty() {
    let $m := map {}
    return
      map:size($m)
};

declare
    %test:assertEquals("Good")
function mt:put-empty() {
    let $m := map {}
    return
      map:put($m, "Cats", "Good")("Cats")
};

declare
    %test:assertEquals("Bad")
function mt:put-single() {
    let $m := map {
        "Cats" : "Good"
    }
    return
      map:put($m, "Dogs", "Bad")("Dogs")
};

declare
    %test:assertEquals("Duck")
function mt:put() {
    let $m := map {
        "Cats" : "Good",
        "Dogs" : "Bad",
        1 : "Chicken"
    }
    return
      map:put($m, 2, "Duck")(2)
};

declare
    %test:assertEquals(4)
function mt:size() {
    let $m := map {
      "Cats" : "Good",
      "Dogs" : "Bad",
      1 : "Chicken",
      2 : "Duck"
    }
    return
      map:size($m)
};

declare
    %test:assertEquals("Sonntag", "Dienstag", "Donnerstag", "Samstag")
function mt:for-each() {
    let $week := map{0: "Sonntag", 1: "Montag", 2: "Dienstag", 3: "Mittwoch", 4: "Donnerstag", 5: "Freitag", 6: "Samstag"}
    return
        map:for-each($week, function($key, $value) {
            if ($key mod 2 = 0) then
                $value
            else
                ()
        })
};

declare
    %test:assertEquals(3)
function mt:for-each2() {
    let $nm := map:merge(map:for-each(map{"a":1, "b":2}, function($k, $v){map:entry($k, $v+1)}))
    return
        $nm?b
};

declare %test:assertEquals("Sunday") function mt:createWithSingleKey() {
    let $map := map:entry("Su", "Sunday")
    return
        $map("Su")
};

declare %test:assertEquals("Samstag", "Sonnabend") function mt:overwriteKeyInNewMap() {
    let $week := map {0: "Sonntag", 1: "Montag", 2: "Dienstag", 3: "Mittwoch", 4: "Donnerstag", 5: "Freitag", 6: "Samstag"}
    let $map := map:merge(($week, map { 6 :  "Sonnabend" }))
    return
        ($week(6), $map(6))
};

declare %test:assertEmpty function mt:mapEmptyValue() {
    let $map := map {0: (), 1 : ("One", "Two") }
    return
        $map(0)
};

declare %test:assertEquals("One", "Two") function mt:mapSequenceValue() {
    let $map := map {0: (), 1 : ("One", "Two") }
    return
        $map(1)
};

declare %test:assertEquals("Heinz", "Roland", "Uschi", "Verona") function mt:keyTypeDate () {
    let $map := map {
        xs:date("1975-03-19") : "Uschi",
        xs:date("1980-01-22") : "Verona",
        xs:date("1960-06-14") : "Heinz",
        xs:date("1963-10-21") : "Roland"
    }
    for $key in map:keys($map)
    let $name := $map($key)
    order by $name ascending
    return
        $name
};

declare %test:assertEquals(0, "One") function mt:mixedKeyTypes() {
    let $map := map{ "Zero" : 0, 1 : "One" }
    return
        ($map("Zero"), $map(1))
};

declare %test:assertTrue function mt:containsOnEmptyValue() {
    let $map := map{0: () }
    return
        map:contains($map, 0)
};

declare %test:assertEquals("Fr", "Mo", "Sa", "Su", "Th", "Tu", "We") function mt:keys() {
    let $days := ("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday")
    let $map := map:merge((map(function($day) { map:entry(substring($day, 1, 2), $day) }, $days)))
    for $day in map:keys($map)
    order by $day ascending
    return
        $day
};

declare %test:assertEquals("Su") function mt:keysOnMapWithSingleKey() {
    let $map := map:entry("Su", "Sunday")
    return
        map:keys($map)
};

declare %test:assertEquals("Fr", "Mo", "Sa", "Su", "Th") function mt:remove() {
    let $days := ("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday")
    let $map := map:merge((map(function($day) { map:entry(substring($day, 1, 2), $day) }, $days)))
    for $day in map:keys(map:remove(map:remove($map, "We"), "Tu"))
    order by $day
    return
        $day
};

declare %test:assertFalse function mt:remove() {
    let $days := ("Monday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday")
    let $map := map:merge((map(function($day) { map:entry(substring($day, 1, 2), $day) }, $days)))
    return
        map:contains(map:remove($map, "Tu"), "Tu")
};

declare %test:assertEmpty function mt:removeSingleKey() {
    let $map := map:entry("Su", "Sunday")
    let $empty := map:remove($map, "Su")
    return
        map:keys($empty)
};

declare %test:assertTrue function mt:contains() {
    map:contains($mt:daysOfWeek, "We")
};

declare %test:assertFalse function mt:containsOnMissingKey() {
    map:contains($mt:daysOfWeek, "Al")
};

declare %test:assertTrue function mt:containsSingleKey() {
    let $map := map:entry("Su", "Sunday")
    return
        map:contains($map, "Su")
};

declare %test:assertEquals("Hello world") function mt:computedKeyValue() {
    let $map := map { 2 + 1 : concat("Hello ", "world") }
    return
        $map(3)
};

declare %test:assertEquals("false", "true") function mt:immutability() {
    let $map := map {
        "Su" : "Sunday",
        "Mo" : "Monday",
        "Tu" : "Tuesday",
        "We" : "Wednesday",
        "Th" : "Thursday",
        "Fr" : "Friday"
    }
    let $map2 := map:merge(($map, map:entry("Sa", "Saturday")))
    return
        (map:contains($map, "Sa"), map:contains($map2, "Sa"))
};

declare %test:assertTrue function mt:immutability2() {
    let $map := map {
        "Su" : "Sunday",
        "Mo" : "Monday",
        "Tu" : "Tuesday",
        "We" : "Wednesday",
        "Th" : "Thursday",
        "Fr" : "Friday"
    }
    let $map2 := map:remove($map, "Fr")
    return
        map:contains($map, "Fr")
};

declare %test:assertFalse function mt:immutability3() {
    let $map := map {
        "Su" : "Sunday",
        "Mo" : "Monday",
        "Tu" : "Tuesday",
        "We" : "Wednesday",
        "Th" : "Thursday",
        "Fr" : "Friday"
    }
    let $map2 := map:remove($map, "Fr")
    return
        map:contains($map2, "Fr")
};

declare %test:assertTrue function mt:immutability4() {

    let $daysOfWeek :=  map {   "Sunday" : 1,
                                 "Monday" : 2,
                                 "Tuesday" : 3,
                                 "Wednesday" : 4,
                                 "Thursday" : 5,
                                 "Friday" : 6,
                                 "Saturday" : 7
                             }
    let $workDays := map:remove($daysOfWeek, "Sunday")

    return map:contains($workDays,"Monday")
};

declare %test:assertEquals("Sunday") function mt:sequenceType1() {
    let $map := map { 1 : "Sunday" }
    return
        map:get(mt:mapTest($map), 1)
};

declare %private function mt:mapTest($map as map(*)) as map(xs:integer, xs:string) {
    $map
};

declare
    %test:assertError("EXMPDY001")
    %test:name("Throw error if key is not a single atomic value")
function mt:wrongCardinality() {
    let $map := map { (1, 2) : "illegal" }
    return
        $map(1)
};

declare
    %test:assertEquals("two")
function mt:nestedMaps() {
    let $map := map { 1: map { 1: "one", 2: "two" } }
    return
        $map(1)(2)
};

declare
    %test:assertEquals("Monday")
function mt:nestedMaps2() {
    let $map := map { "week" := map { "days" : $mt:daysOfWeek } }
    return
        $map("week")("days")("Mo")
};

declare
    %test:assertEquals(2)
function mt:qnameKeys() {
    let $map := map { xs:QName("mt:one") : 1, xs:QName("mt:two") : 2 }
    return
        $map(xs:QName("mt:two"))
};

declare
    %test:assertEquals(2)
function mt:longKeys() {
    let $map := map { xs:long(1) : 1, xs:long(2) : 2 }
    return
        $map(2)
};

declare
    %test:args(2)
    %test:assertEquals(3)
    %test:args("Three")
    %test:assertEmpty
function mt:doubleKeys($key as item()) {
    let $map := map { xs:double(1.1) : 1, xs:double(2) : 2 }
    return
        map:merge(($map, map:entry(xs:double(2), 3)))($key)
};

declare
    %test:assertEquals(2, "value")
function mt:mixedNumeric() {
    let $map := map { 1.1 : 1, 2 : 2, "key" : "value" }
    return
        ($map(2), $map("key"))
};

declare
    %test:assertEquals("<c ns='c'/>", "<c ns='c'/>")
function mt:pathExpr() as element(c)+ {
    let $test :=
        <a>
            <b ns="b">
                <c ns="c"/>
            </b>
        </a>
    let $map :=
        map {
            "a" : $test//c,
            "b" : ($test//c)
        }
    return
        ($map("a"), $map("b"))
};

declare
    %test:assertEquals("What were you thinking?")
function mt:lookupUnaryOperator() {
    let $errors := (
      map { "level" : 1, "text" : "Boys will be boys ..." },
      map { "level" : 2, "text" : "What were you thinking?" },
      map { "level" : 3, "text" : "Call the cops!" }
    )
    return $errors[?level = 2]?text
};

declare
    %test:assertEquals("W0342", "M0535")
function mt:lookupMultipleMaps() {
    let $maps := ( map { "id" : "W0342" }, map { "id" : "M0535" } )
    return $maps?id
};

declare
    %test:assertEquals("W0342", "M0535")
function mt:lookupMultipleMaps2() {
    ( map { "id" : "W0342" }, map { "id" : "M0535" } )?id
};

declare
    %test:assertEquals("large")
function mt:lookupNestedMap() {
    let $map := map {
        "name": "sofa", "keys": map { "price": 200.0, "size": "large" }
    }
    return
        $map?keys?size
};

declare
    %test:args("One")
    %test:assertEquals(1)
    %test:args("Two")
    %test:assertEquals("2")
function mt:lookupParenthesized($key as xs:string) {
    let $map := map { "one": 1, "two": "2" }
    return
        $map?(lower-case($key))
};

declare
    %test:assertError
function mt:lookupWrongType() {
    let $map := map { "one": 1, "two": "2" }
    let $str := "Hello"
    return
        ($map, $str)?one
};

declare
    %test:pending
    %test:assertTrue
function mt:lookupWildcard() {
    let $days := ("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday")
    let $map := $mt:daysOfWeek
    return
        every $day in $map?* satisfies $day = $days
};

declare
    %test:assertEquals(1)
function mt:compat() {
    let $map := map { "one":= 1, "two":= "2" }
    return
        $map("one")
};

declare
    %test:assertError
function mt:no-atomization() {
    data(map { "k": "v" })
};

xquery version "1.0";

module namespace mt="http://exist-db.org/xquery/test/maps";

import module namespace test="http://exist-db.org/xquery/xqsuite" at "resource:org/exist/xquery/lib/xqsuite/xqsuite.xql";

declare variable $mt:daysOfWeek :=
    map {
        "Su" : "Sunday",
        "Mo" : "Monday",
        "Tu" : "Tuesday",
        "We" : "Wednesday",
        "Th" : "Thursday",
        "Fr" : "Friday",
        "Sa" : "Saturday"
    };

declare %test:assertEquals("Wednesday") function mt:createLiteral1() {
    $mt:daysOfWeek("We")
};

declare %test:assertEquals("Wednesday") function mt:createWithIntKeys() {
    let $map := map {
        1 : "Sunday",
        2 : "Monday",
        3 : "Tuesday",
        4 : "Wednesday",
        5 : "Thursday",
        6 : "Friday",
        7 : "Saturday"
    }
    return
        $map(4)
};

declare %test:assertEquals(20) function mt:createWithFunctionValue() {
    let $fn := function($p) { $p * 2 }
    let $map := map { "callback" : $fn }
    return
        map:get($map, "callback")(10)
};

declare %test:assertEquals("Sunday") function mt:createWithEntry() {
    let $days := ("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday")
    let $map := map:merge((map(function($day) { map:entry(substring($day, 1, 2), $day) }, $days)))
    return
        $map("Su")
};

declare %test:assertXPath("count($result) = 8") %test:assertXPath("7 = $result")
function mt:createFromTwoMaps() {
    let $week := map{0: "Sonntag", 1: "Montag", 2: "Dienstag", 3: "Mittwoch", 4: "Donnerstag", 5: "Freitag", 6: "Samstag"}
    let $map := map:merge(($week, map { 7 : "Sonntag" }))
    return
        map:keys($map)
};

declare
    %test:assertEquals(0)
function mt:size-empty() {
    let $m := map {}
    return
      map:size($m)
};

declare
    %test:assertEquals("Good")
function mt:put-empty() {
    let $m := map {}
    return
      map:put($m, "Cats", "Good")("Cats")
};

declare
    %test:assertEquals("Bad")
function mt:put-single() {
    let $m := map {
        "Cats" : "Good"
    }
    return
      map:put($m, "Dogs", "Bad")("Dogs")
};

declare
    %test:assertEquals("Duck")
function mt:put() {
    let $m := map {
        "Cats" : "Good",
        "Dogs" : "Bad",
        1 : "Chicken"
    }
    return
      map:put($m, 2, "Duck")(2)
};

declare
    %test:assertEquals(4)
function mt:size() {
    let $m := map {
      "Cats" : "Good",
      "Dogs" : "Bad",
      1 : "Chicken",
      2 : "Duck"
    }
    return
      map:size($m)
};

declare
    %test:assertEquals("Sonntag", "Dienstag", "Donnerstag", "Samstag")
function mt:for-each() {
    let $week := map{0: "Sonntag", 1: "Montag", 2: "Dienstag", 3: "Mittwoch", 4: "Donnerstag", 5: "Freitag", 6: "Samstag"}
    return
        map:for-each($week, function($key, $value) {
            if ($key mod 2 = 0) then
                $value
            else
                ()
        })
};

declare
    %test:assertEquals(3)
function mt:for-each2() {
    let $nm := map:merge(map:for-each(map{"a":1, "b":2}, function($k, $v){map:entry($k, $v+1)}))
    return
        $nm?b
};

declare %test:assertEquals("Sunday") function mt:createWithSingleKey() {
    let $map := map:entry("Su", "Sunday")
    return
        $map("Su")
};

declare %test:assertEquals("Samstag", "Sonnabend") function mt:overwriteKeyInNewMap() {
    let $week := map {0: "Sonntag", 1: "Montag", 2: "Dienstag", 3: "Mittwoch", 4: "Donnerstag", 5: "Freitag", 6: "Samstag"}
    let $map := map:merge(($week, map { 6 :  "Sonnabend" }))
    return
        ($week(6), $map(6))
};

declare %test:assertEmpty function mt:mapEmptyValue() {
    let $map := map {0: (), 1 : ("One", "Two") }
    return
        $map(0)
};

declare %test:assertEquals("One", "Two") function mt:mapSequenceValue() {
    let $map := map {0: (), 1 : ("One", "Two") }
    return
        $map(1)
};

declare %test:assertEquals("Heinz", "Roland", "Uschi", "Verona") function mt:keyTypeDate () {
    let $map := map {
        xs:date("1975-03-19") : "Uschi",
        xs:date("1980-01-22") : "Verona",
        xs:date("1960-06-14") : "Heinz",
        xs:date("1963-10-21") : "Roland"
    }
    for $key in map:keys($map)
    let $name := $map($key)
    order by $name ascending
    return
        $name
};

declare %test:assertEquals(0, "One") function mt:mixedKeyTypes() {
    let $map := map{ "Zero" : 0, 1 : "One" }
    return
        ($map("Zero"), $map(1))
};

declare %test:assertTrue function mt:containsOnEmptyValue() {
    let $map := map{0: () }
    return
        map:contains($map, 0)
};

declare %test:assertEquals("Fr", "Mo", "Sa", "Su", "Th", "Tu", "We") function mt:keys() {
    let $days := ("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday")
    let $map := map:merge((map(function($day) { map:entry(substring($day, 1, 2), $day) }, $days)))
    for $day in map:keys($map)
    order by $day ascending
    return
        $day
};

declare %test:assertEquals("Su") function mt:keysOnMapWithSingleKey() {
    let $map := map:entry("Su", "Sunday")
    return
        map:keys($map)
};

declare %test:assertEquals("Fr", "Mo", "Sa", "Su", "Th") function mt:remove() {
    let $days := ("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday")
    let $map := map:merge((map(function($day) { map:entry(substring($day, 1, 2), $day) }, $days)))
    for $day in map:keys(map:remove(map:remove($map, "We"), "Tu"))
    order by $day
    return
        $day
};

declare %test:assertFalse function mt:remove() {
    let $days := ("Monday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday")
    let $map := map:merge((map(function($day) { map:entry(substring($day, 1, 2), $day) }, $days)))
    return
        map:contains(map:remove($map, "Tu"), "Tu")
};

declare %test:assertEmpty function mt:removeSingleKey() {
    let $map := map:entry("Su", "Sunday")
    let $empty := map:remove($map, "Su")
    return
        map:keys($empty)
};

declare %test:assertTrue function mt:contains() {
    map:contains($mt:daysOfWeek, "We")
};

declare %test:assertFalse function mt:containsOnMissingKey() {
    map:contains($mt:daysOfWeek, "Al")
};

declare %test:assertTrue function mt:containsSingleKey() {
    let $map := map:entry("Su", "Sunday")
    return
        map:contains($map, "Su")
};

declare %test:assertEquals("Hello world") function mt:computedKeyValue() {
    let $map := map { 2 + 1 : concat("Hello ", "world") }
    return
        $map(3)
};

declare %test:assertEquals("false", "true") function mt:immutability() {
    let $map := map {
        "Su" : "Sunday",
        "Mo" : "Monday",
        "Tu" : "Tuesday",
        "We" : "Wednesday",
        "Th" : "Thursday",
        "Fr" : "Friday"
    }
    let $map2 := map:merge(($map, map:entry("Sa", "Saturday")))
    return
        (map:contains($map, "Sa"), map:contains($map2, "Sa"))
};

declare %test:assertTrue function mt:immutability2() {
    let $map := map {
        "Su" : "Sunday",
        "Mo" : "Monday",
        "Tu" : "Tuesday",
        "We" : "Wednesday",
        "Th" : "Thursday",
        "Fr" : "Friday"
    }
    let $map2 := map:remove($map, "Fr")
    return
        map:contains($map, "Fr")
};

declare %test:assertFalse function mt:immutability3() {
    let $map := map {
        "Su" : "Sunday",
        "Mo" : "Monday",
        "Tu" : "Tuesday",
        "We" : "Wednesday",
        "Th" : "Thursday",
        "Fr" : "Friday"
    }
    let $map2 := map:remove($map, "Fr")
    return
        map:contains($map2, "Fr")
};

declare %test:assertTrue function mt:immutability4() {

    let $daysOfWeek :=  map {   "Sunday" : 1,
                                 "Monday" : 2,
                                 "Tuesday" : 3,
                                 "Wednesday" : 4,
                                 "Thursday" : 5,
                                 "Friday" : 6,
                                 "Saturday" : 7
                             }
    let $workDays := map:remove($daysOfWeek, "Sunday")

    return map:contains($workDays,"Monday")
};

declare %test:assertEquals("Sunday") function mt:sequenceType1() {
    let $map := map { 1 : "Sunday" }
    return
        map:get(mt:mapTest($map), 1)
};

declare %private function mt:mapTest($map as map(*)) as map(xs:integer, xs:string) {
    $map
};

declare
    %test:assertError("EXMPDY001")
    %test:name("Throw error if key is not a single atomic value")
function mt:wrongCardinality() {
    let $map := map { (1, 2) : "illegal" }
    return
        $map(1)
};

declare
    %test:assertEquals("two")
function mt:nestedMaps() {
    let $map := map { 1: map { 1: "one", 2: "two" } }
    return
        $map(1)(2)
};

declare
    %test:assertEquals("Monday")
function mt:nestedMaps2() {
    let $map := map { "week" := map { "days" : $mt:daysOfWeek } }
    return
        $map("week")("days")("Mo")
};

declare
    %test:assertEquals(2)
function mt:qnameKeys() {
    let $map := map { xs:QName("mt:one") : 1, xs:QName("mt:two") : 2 }
    return
        $map(xs:QName("mt:two"))
};

declare
    %test:assertEquals(2)
function mt:longKeys() {
    let $map := map { xs:long(1) : 1, xs:long(2) : 2 }
    return
        $map(2)
};

declare
    %test:args(2)
    %test:assertEquals(3)
    %test:args("Three")
    %test:assertEmpty
function mt:doubleKeys($key as item()) {
    let $map := map { xs:double(1.1) : 1, xs:double(2) : 2 }
    return
        map:merge(($map, map:entry(xs:double(2), 3)))($key)
};

declare
    %test:assertEquals(2, "value")
function mt:mixedNumeric() {
    let $map := map { 1.1 : 1, 2 : 2, "key" : "value" }
    return
        ($map(2), $map("key"))
};

declare
    %test:assertEquals("<c ns='c'/>", "<c ns='c'/>")
function mt:pathExpr() as element(c)+ {
    let $test :=
        <a>
            <b ns="b">
                <c ns="c"/>
            </b>
        </a>
    let $map :=
        map {
            "a" : $test//c,
            "b" : ($test//c)
        }
    return
        ($map("a"), $map("b"))
};

declare
    %test:assertEquals("What were you thinking?")
function mt:lookupUnaryOperator() {
    let $errors := (
      map { "level" : 1, "text" : "Boys will be boys ..." },
      map { "level" : 2, "text" : "What were you thinking?" },
      map { "level" : 3, "text" : "Call the cops!" }
    )
    return $errors[?level = 2]?text
};

declare
    %test:assertEquals("W0342", "M0535")
function mt:lookupMultipleMaps() {
    let $maps := ( map { "id" : "W0342" }, map { "id" : "M0535" } )
    return $maps?id
};

declare
    %test:assertEquals("W0342", "M0535")
function mt:lookupMultipleMaps2() {
    ( map { "id" : "W0342" }, map { "id" : "M0535" } )?id
};

declare
    %test:assertEquals("large")
function mt:lookupNestedMap() {
    let $map := map {
        "name": "sofa", "keys": map { "price": 200.0, "size": "large" }
    }
    return
        $map?keys?size
};

declare
    %test:args("One")
    %test:assertEquals(1)
    %test:args("Two")
    %test:assertEquals("2")
function mt:lookupParenthesized($key as xs:string) {
    let $map := map { "one": 1, "two": "2" }
    return
        $map?(lower-case($key))
};

declare
    %test:assertError
function mt:lookupWrongType() {
    let $map := map { "one": 1, "two": "2" }
    let $str := "Hello"
    return
        ($map, $str)?one
};

declare
    %test:assertTrue
function mt:lookupWildcard() {
    let $days := ("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday")
    let $map := $mt:daysOfWeek
    return
        every $day in $map?* satisfies $day = $days
};

declare
    %test:assertEquals(1)
function mt:compat() {
    let $map := map { "one":= 1, "two":= "2" }
    return
        $map("one")
};

declare
    %test:assertError
function mt:no-atomization() {
    data(map { "k": "v" })
};


[apidoc] Generate function signatures from the apidoc XML.

(:
 : Copyright (C) 2020 Reece H. Dunn
 :
 : 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.
 :)
xquery version "3.1";

declare namespace apidoc = "http://marklogic.com/xdmp/apidoc";

declare variable $apidoc as element(apidoc:apidoc) :=
    typeswitch (.)
    case xs:string return fn:parse-xml(.)/element()
    case document-node() return ./element()
    default return .;

(: functions --------------------------------------------------------------- :)

declare variable $functions as element(apidoc:function)* := $apidoc//apidoc:function;

declare function local:function-name($function as element(apidoc:function)) as xs:string {
    ``[`{$function/@lib}`:`{$function/@name}`]``
};

declare function local:function-parameters($function as element(apidoc:function)) as xs:string* {
    $function/apidoc:params/apidoc:param/@name
};

declare function local:function-parameter-type($function as element(apidoc:function), $name as xs:string) as xs:string {
    $function/apidoc:params/apidoc:param[@name = $name]/@type
};

declare function local:function-return-type($function as element(apidoc:function)) as xs:string {
    $function/apidoc:return
};

declare function local:function-xquery-signatures($function as element(apidoc:function)) as xs:string* {
    let $name := local:function-name($function)
    let $params :=
        for $name in local:function-parameters($function)
        let $type := local:function-parameter-type($function, $name)
        return ``[$`{$name}` as `{$type}`]``
    let $type := local:function-return-type($function)
    return ``[declare function `{$name}`(`{string-join($params, ", ")}`) as `{$type}` external;]``
};

(: query ------------------------------------------------------------------- :)

for $function in $functions
order by $function/@lib, $function/@name
return local:function-xquery-signatures($function)

(:
 : Copyright (C) 2020 Reece H. Dunn
 :
 : 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.
 :)
xquery version "3.1";

declare namespace apidoc = "http://marklogic.com/xdmp/apidoc";

declare variable $apidoc as element(apidoc:apidoc) :=
    typeswitch (.)
    case xs:string return fn:parse-xml(.)/element()
    case document-node() return ./element()
    default return .;

()