[ignore] switching to memory processing

[ignore] switching to memory processing

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

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())

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:false())


[repo]  tweaking public repository ui

[repo]  tweaking public repository ui

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

xquery version "1.0";

declare namespace xdb="http://exist-db.org/xquery/xmldb";
declare namespace package="http://expath.org/ns/pkg";

declare variable $repo-coll := "/db/repo";

declare function local:entry-data($path as xs:anyURI, $type as xs:string, $data as item()?, $param as item()*) as item()?
{

	<entry>
		<path>{$path}</path>
		<type>{$type}</type>
		<data>{$data}</data>
	</entry>
};

declare function local:entry-filter($path as xs:anyURI, $type as xs:string, $param as item()*) as xs:boolean
{

	true()
};

declare function local:process-action() as element()*
{
    let $repocol :=  if (collection($repo-coll)) then () else xmldb:create-collection('/db','repo')
    let $action := request:get-parameter("action", ())
    return
        util:catch("java.lang.Exception",
            if($action eq "Upload Package") then
              local:upload()
            else
              (),

                An error occurred while processing the action:

                {$util:exception-message}

        )
};

declare function local:upload() as element()
{
    let $name := request:get-parameter("name", ()),
    $docName := if($name) then $name else request:get-uploaded-file-name("upload"),
    $file := request:get-uploaded-file-data("upload") return

Actions:

                {
                if (contains($docName,'.xar')) then
                    (
uploaded package: {$docName}
,
                   xdb:decode-uri(xs:anyURI(xdb:store($repo-coll, xdb:encode-uri($docName), $file)))
                   ,
                    let $meta := compression:unzip($file, util:function(xs:QName("local:entry-filter"), 3), (),  util:function(xs:QName("local:entry-data"), 4), ())
                    let $package := $meta//package:package
                    let $repo := $meta//repo:meta
                    return
                     xdb:store($repo-coll, concat(substring-before($docName,'.xar'),'.xml'), <meta>{$package}{$repo}</meta>)
                   )
                else

Error uploading - Must be a valid Package archive (.xar file extension)

                }

};

    <title>eXist XML Database Public Package Repository</title>
    <link rel="stylesheet" type="text/css" href="../../styles/default-style2.css" />

eXist XML Database Public Repository

 To install packages from this repository you will require:

In your eXist repo manager use this uri: http://{request:get-hostname()}:{request:get-server-port()}/exist/repo/public/all/

access permission to this repository (set by the administrator of this repository)

    {local:process-action()}

Package Listing

            Package Name
            Description
            Date Created
            Author
            License
            Type

     {
     let $files := if (collection($repo-coll)) then collection($repo-coll)/util:document-name(.) else ()
     return
       for $file in $files[contains(.,'.xar')]
       let $name := substring-before($file,'.xar')
       let $package := document(concat($repo-coll,'/',$name,'.xml'))
       return

            {$name}
            {$package//package:title/text()}
            {xmldb:created($repo-coll, $file)}
            {$package//repo:author}
            {$package//repo:license}
            {if ($package//repo:deploy) then 'application' else 'library'}

     }

Add Package

    <form method="POST" enctype="multipart/form-data">

        <input type="submit" name="action" value="Upload Package"/>
        <input type="file" size="30" name="upload"/>

    </form>

xquery version "1.0";

declare namespace xdb="http://exist-db.org/xquery/xmldb";
declare namespace package="http://expath.org/ns/pkg";

declare variable $repo-coll := "/db/repo";

declare function local:entry-data($path as xs:anyURI, $type as xs:string, $data as item()?, $param as item()*) as item()?
{

	<entry>
		<path>{$path}</path>
		<type>{$type}</type>
		<data>{$data}</data>
	</entry>
};

declare function local:entry-filter($path as xs:anyURI, $type as xs:string, $param as item()*) as xs:boolean
{

	true()
};

declare function local:process-action() as element()*
{
    let $repocol :=  if (collection($repo-coll)) then () else xmldb:create-collection('/db','repo')
    let $action := request:get-parameter("action", ())
    return
        util:catch("java.lang.Exception",
            if($action eq "Upload Package") then
              local:upload()
            else
              (),

                An error occurred while processing the action:

                {$util:exception-message}

        )
};

declare function local:upload() as element()
{
    let $name := request:get-parameter("name", ()),
    $docName := if($name) then $name else request:get-uploaded-file-name("upload"),
    $file := request:get-uploaded-file-data("upload") return

Actions:

                {
                if (contains($docName,'.xar')) then
                    (
uploaded package: {$docName}
,
                   xdb:decode-uri(xs:anyURI(xdb:store($repo-coll, xdb:encode-uri($docName), $file)))
                   ,
                    let $meta := compression:unzip($file, util:function(xs:QName("local:entry-filter"), 3), (),  util:function(xs:QName("local:entry-data"), 4), ())
                    let $package := $meta//package:package
                    let $repo := $meta//repo:meta
                    return
                     xdb:store($repo-coll, concat(substring-before($docName,'.xar'),'.xml'), <meta>{$package}{$repo}</meta>)
                   )
                else

Error uploading - Must be a valid Package archive (.xar file extension)

                }

};

    <title>eXist XML Database Public Package Repository</title>
    <link rel="stylesheet" type="text/css" href="../../styles/default-style2.css" />

eXist XML Database Public Repository

    {local:process-action()}

Package Listing

            Package Name
            Description
            Date Created
            Author
            License
            Type

     {
     let $files := if (collection($repo-coll)) then collection($repo-coll)/util:document-name(.) else ()
     return
       for $file in $files[contains(.,'.xar')]
       let $name := substring-before($file,'.xar')
       let $package := document(concat($repo-coll,'/',$name,'.xml'))
       return

            {$name}
            {$package//package:title/text()}
            {xmldb:created($repo-coll, $file)}
            {$package//repo:author}
            {$package//repo:license}
            {if ($package//repo:deploy) then 'application' else 'library'}

     }

Add Package

    <form method="POST" enctype="multipart/form-data">

        <input type="submit" name="action" value="Upload Package"/>
        <input type="file" size="30" name="upload"/>

    </form>


[ignore] store web applications descriptors under /db/packages, because /db/system is not readable by the world

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

xquery version "1.0";
(: $Id$ :)
(:
    Packages management.
:)

module namespace pckg="http://exist-db.org/packages";

declare function pckg:firstRunImport() as xs:string* {
	if (xmldb:is-admin-user(xmldb:get-current-user())) then
		let $collection := "/db/packages/" (: usage @xmldb:store-files-from-pattern give error, is it bug? :)
		return
		if (xmldb:collection-available("/db/packages/")) then ()
		else
			let $temp := xmldb:create-collection("/db","packages")
			let $temp := xmldb:set-collection-permissions("/db/packages/","admin","dba",509)
			return
			xmldb:store-files-from-pattern(
				"/db/packages",
				concat(system:get-exist-home(),"/webapp/administration/"),
				"**/descriptor.xml",
				'application/xml',
				true())
	else ""
};

xquery version "1.0";
(: $Id$ :)
(:
    Packages management.
:)

module namespace pckg="http://exist-db.org/packages";

declare function pckg:firstRunImport() as xs:string* {
	if (xmldb:is-admin-user(xmldb:get-current-user())) then
		let $collection := "/db/system/packages/" (: usage @xmldb:store-files-from-pattern give error, is it bug? :)
		return
		(: if (xmldb:collection-available("/db/system/packages/")) then () :)
		(: else :)
			let $temp := xmldb:create-collection("/db/system","packages")
			return
			xmldb:store-files-from-pattern(
				"/db/system/packages",
				concat(system:get-exist-home(),"/webapp/administration/"),
				"**/descriptor.xml",
				'application/xml',
				true())
	else ""
};


refactor - don't need a variable for function-name

xquery version "1.0-ml";

declare variable $xml external;
let $xml := xdmp:unquote("<wrap>" || $xml || "</wrap>")

let $definition := xdmp:xslt-eval(
  <xsl:stylesheet version="2.0" exclude-result-prefixes="#all" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:apidoc="http://marklogic.com/xdmp/apidoc" xmlns:local="local">

    <xsl:output method="text" indent="yes" omit-xml-declaration="yes"/>

    <xsl:template match="@*|node()" mode="#all">
      <xsl:copy>
        <xsl:apply-templates select="@*|node()" mode="#current"/>
      </xsl:copy>
    </xsl:template>

    <xsl:template match="apidoc:function" mode="#all">
      <xsl:param name="use-function-keyword" select="false()"/>
      <xsl:param name="use-declare-keyword" select="false()"/>

      <xsl:text>    /** </xsl:text>
      <xsl:value-of select="normalize-space(apidoc:summary)"/>
      <xsl:text> **/
</xsl:text>

      <xsl:text>  </xsl:text>
      <xsl:if test="$use-declare-keyword">
        <xsl:text>declare </xsl:text>
      </xsl:if>
      <xsl:if test="$use-function-keyword">
        <xsl:text>function </xsl:text>
      </xsl:if>

      <!--
      The function name can usually be derived by convetion. But in some cases (well, one case - toJSON), an override
      is provided in the documentation. In those cases, the override should be used. This is also how this is handled
      in RunDMC in api:javascript-name
      (https://github.com/marklogic-community/RunDMC/blob/master/src/apidoc/model/data-access.xqy#L407).
      -->
      <xsl:variable name="name-override" select="apidoc:name[@class eq 'javascript']/text()"/>
      <xsl:choose>
        <xsl:when test="exists($name-override)">
          <!-- an override exists, use it -->
          <xsl:value-of select="$name-override"/>
        </xsl:when>
        <xsl:otherwise>
          <!-- no override exists, follow convention in local:fixName -->
          <xsl:value-of select="local:fixName(@name)"/>
        </xsl:otherwise>
      </xsl:choose>

      <xsl:text>(</xsl:text>
      <xsl:for-each select="apidoc:params/apidoc:param">
        <xsl:variable name="is-multi" select="matches(@type, ',\.\.\.$')"/>
        <xsl:if test="$is-multi">...</xsl:if>
        <xsl:value-of select="local:fixName(@name)"/>
        <xsl:if test="preceding-sibling::apidoc:param[@name = current()/@name]">2</xsl:if>
        <xsl:if test="not($is-multi) and string(@optional) = 'true'">?</xsl:if>
        <xsl:text>: </xsl:text>
        <xsl:value-of select="local:fixType(@type, true())"/>
        <xsl:if test="$is-multi">[]</xsl:if>
        <xsl:if test="not($is-multi) and string(@optional) = 'true'">|null|undefined</xsl:if>
        <xsl:if test="not(position() = last())">, </xsl:if>
      </xsl:for-each>
      <xsl:text>): </xsl:text>
      <xsl:value-of select="local:fixType(string(apidoc:return), false())"/>
      <xsl:text>;

</xsl:text>
    </xsl:template>

    <xsl:template match="/wrap">
      <xsl:variable name="root" select="."/>

      <xsl:text>// Type definitions for </xsl:text><xsl:value-of select="apidoc:module[exists(apidoc:function)]/@name"/><xsl:text>
</xsl:text>
      <xsl:text>// Definitions: </xsl:text><xsl:value-of select="base-uri(.)"/><xsl:text>

</xsl:text>

        <xsl:for-each select="distinct-values(((//apidoc:module[exists(apidoc:function)]/@lib, 'xdmp')[1], //apidoc:function/@lib))">
          <xsl:sort/>
          <xsl:variable name="lib" select="."/>
          <xsl:variable name="name" select="string-join(local:fixName($lib), '')"/>
          <xsl:variable name="functions" select="$root//apidoc:function[@lib = $lib]"/>
          <xsl:variable name="module" select="$functions/.."/>

          <xsl:text>
/**
</xsl:text>
          <xsl:value-of select="$module/apidoc:summary"/>
          <xsl:text>
**/

</xsl:text>

          <xsl:choose>
          <xsl:when test="$name = 'GlobalObject'">
            <xsl:apply-templates select="$functions">
              <xsl:sort select="@name" />
              <xsl:with-param name="use-function-keyword" select="true()"/>
              <xsl:with-param name="use-declare-keyword" select="true()"/>
            </xsl:apply-templates>
          </xsl:when>
          <xsl:when test="$name = 'alert'">
            <xsl:text>declare module </xsl:text>
            <xsl:value-of select="$name"/>
            <xsl:text> {{

</xsl:text>

            <xsl:apply-templates select="$functions">
              <xsl:sort select="@name" />
              <xsl:with-param name="use-function-keyword" select="true()"/>
            </xsl:apply-templates>

            <xsl:text>}}
</xsl:text>
          </xsl:when>
          <xsl:otherwise>
            <xsl:text>interface </xsl:text>
            <xsl:value-of select="$name"/>
            <xsl:text>Functions {{

</xsl:text>

            <xsl:apply-templates select="$functions">
              <xsl:sort select="@name" />
            </xsl:apply-templates>

            <xsl:text>}}
</xsl:text>

            <xsl:text>declare var </xsl:text>
            <xsl:value-of select="$name"/>
            <xsl:text>:</xsl:text>
            <xsl:value-of select="$name"/>
            <xsl:text>Functions
</xsl:text>
          </xsl:otherwise>
          </xsl:choose>
        </xsl:for-each>
    </xsl:template>

    <xsl:function name="local:fixType">
      <xsl:param name="fullType"/>
      <xsl:param name="isForParameter"/>

      <xsl:variable name="fullType" select="translate($fullType, ' ', '')"/>
      <xsl:variable name="type" select="replace(replace($fullType, ',\.\.\.$', ''), '([?*+]|\[\])$', '')"/>
      <xsl:variable name="cardinality" select="substring($fullType, string-length($type) + 1)"/>
      <xsl:variable name="isArray" select="matches($cardinality, '^([*+]|\[\])')"/>
      <xsl:variable name="type" select="if (substring($type, 1, 1) = '(') then substring($type, 2, string-length($type) - 2) else $type"/>

      <xsl:value-of select="local:fixEachType($type, $isArray, $isForParameter)"/>
    </xsl:function>

    <xsl:function name="local:fixEachType">
      <xsl:param name="typeUnion"/>
      <xsl:param name="isArray"/>
      <xsl:param name="isForParameter"/>

      <xsl:variable name="type" select="substring-before(concat($typeUnion,'|'),'|')"/>
      <xsl:variable name="type" select="replace($type, '([?*+]|\[\])$', '')"/>

      <xsl:choose>
        <xsl:when test="matches($type, '^function\(.*\)as(.+)$')">
          <xsl:value-of select="local:fixFunctionType($type, $isArray)"/>
        </xsl:when>
        <xsl:when test="$isArray">
          <xsl:choose>
            <xsl:when test="$type = ('document-node()', 'binary()', 'element()', 'node()')">
              <xsl:value-of select="local:fixArrayType('any', $isForParameter)"/>
            </xsl:when>
            <xsl:otherwise>
              <xsl:value-of select="local:fixArrayType(string-join(local:fixSingleType($type, $isForParameter), ''), $isForParameter)"/>
            </xsl:otherwise>
          </xsl:choose>
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="local:fixSingleType($type, $isForParameter)"/>
        </xsl:otherwise>
      </xsl:choose>

      <xsl:if test="contains($typeUnion, '|')">
        <xsl:value-of select="concat('|', string-join(local:fixEachType(substring-after($typeUnion, '|'), $isArray, $isForParameter), ''))"/>
      </xsl:if>
    </xsl:function>

    <xsl:function name="local:fixArrayType">
      <xsl:param name="type"/>
      <xsl:param name="isForParameter"/>
      <xsl:value-of select="concat(concat(if($isForParameter) then 'MLArray<' else 'Sequence<', $type), '>')"/>
    </xsl:function>

    <xsl:function name="local:fixSingleType">
      <xsl:param name="type"/>
      <xsl:param name="isForParameter"/>
      <xsl:choose>
        <!-- workarounds for docs bugs -->
        <xsl:when test="$type = 'query-results-serialize($results)'">MLNode<any></xsl:when>
        <xsl:when test="$type = 'option'">Sequence<any></xsl:when>
        <xsl:when test="$type = 'genericDateTimeArg'">xs.GenericDateTimeArg</xsl:when>
        <xsl:when test="$type = 'rdf:collatedString'">sql.CollatedString</xsl:when>

        <xsl:when test="$type = 'any[]'">Array<any></xsl:when>
        <xsl:when test="$type = ('ValueIterator', 'Sequence')">Sequence<any></xsl:when>
        <xsl:when test="$type = 'document-node()'">DocumentNode<any></xsl:when>
        <xsl:when test="$type = ('binary()')">MLNode<any></xsl:when>
        <xsl:when test="$type = ('element()', 'node()')">
          <xsl:choose>
            <xsl:when test="$isForParameter">MLNodeOrObject<any></xsl:when>
            <xsl:otherwise>MLNode<any></xsl:otherwise>
          </xsl:choose>
        </xsl:when>
        <xsl:when test="$type = ('empty-seq', 'empty-sequence', 'empty-sequence()', 'null', 'Null')">null</xsl:when>
        <xsl:when test="$type = ('String', 'xs:anyURI', 'xs:NCName', 'xs:string', 'xs:time', 'xs:duration', 'xs:dayTimeDuration')">string</xsl:when>
        <xsl:when test="$type = ('json:array', 'Array')">Array<any></xsl:when>
        <xsl:when test="$type = ('json:object', 'map:map', 'Options')">{[key:string]:any}</xsl:when>
        <xsl:when test="$type = ('function()', 'xdmp:function', 'function(*', 'function(*)', 'function')">() => any</xsl:when>
        <xsl:when test="$type = 'xs:boolean'">boolean</xsl:when>
        <xsl:when test="$type = ('any', 'xs:anyAtomicType', 'item()')">any</xsl:when>
        <xsl:when test="$type = ('xs:dateTime', 'xs:date')">Date</xsl:when>
        <xsl:when test="$type = ('Int', 'double', 'xs:numeric', 'numeric', 'xs:decimal', 'xs:double', 'xs:unsigned', 'xs:unsingedLong', 'xs:unsignedLong', 'xs:int', 'xs:float', 'xs:integer', 'xs:long', 'xs:nonNegativeInteger', 'xs:positiveInteger', 'xs:unsignedInt')">number</xsl:when>
        <xsl:when test="matches($type, '^(schema-)?[Ee]lement\([^)]+\)')">
          <xsl:choose>
            <!-- TODO: Can we perform generics on this? -->
            <xsl:when test="$isForParameter">MLNodeOrObject<any></xsl:when>
            <xsl:otherwise>MLNode<any></xsl:otherwise>
          </xsl:choose>
        </xsl:when>
        <xsl:otherwise>
          <xsl:variable name="local-name" select="replace($type, '^([^:.]+[:.])?([^\(\)]+)\(?\)?$', '$2')"/>
          <xsl:variable name="fixed-local-name" select="string-join(local:fixName($local-name), '')"/>
          <xsl:variable name="type-name" select="concat(upper-case(substring($fixed-local-name, 1, 1)), substring($fixed-local-name, 2))"/>
          <xsl:choose>
            <xsl:when test="contains($type, ':') or contains($type, '.')">
              <xsl:variable name="prefix-name" select="replace($type, '^([^:.]+[:.])?(.+)$', '$1')"/>
              <xsl:value-of select="concat(substring($prefix-name, 1, string-length($prefix-name) - 1), concat('.', $type-name))"/>
            </xsl:when>
            <xsl:otherwise>
              <xsl:value-of select="$type-name"/>
            </xsl:otherwise>
          </xsl:choose>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:function>

    <xsl:function name="local:fixFunctionType">
      <xsl:param name="type"/>
      <xsl:param name="isArray"/>
      <xsl:text>(</xsl:text>
      <xsl:if test="matches($type, '^function\((.+)\)as(.+)$')">
        <xsl:value-of select="local:fixParameters(replace($type, '^function\((.+)\)as(.+)$', '$1'), 1)"/>
      </xsl:if>
      <xsl:text>)=></xsl:text>
      <xsl:variable name="returnType" select="local:fixType(replace($type, '^function\(.*\)as(.+)$', '$1'), false())"/>
      <xsl:choose>
        <xsl:when test="$isArray">
          <xsl:value-of select="local:fixArrayType($returnType, false())"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="$returnType"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:function>

    <xsl:function name="local:fixParameters">
      <xsl:param name="parameters"/>
      <xsl:param name="number"/>
      <xsl:value-of select="concat(concat('p', $number), ':')"/>
      <xsl:variable name="type" select="substring-before(concat($parameters,','),',')"/>
      <xsl:value-of select="local:fixType($type, true())"/>
      <xsl:if test="contains($parameters, ',')">
        <xsl:value-of select="concat(',', string-join(local:fixParameters(substring-after($parameters, ','), $number + 1), ''))"/>
      </xsl:if>
    </xsl:function>

    <xsl:function name="local:fixName">
      <xsl:param name="name"/>

      <xsl:choose>
        <xsl:when test="$name = 'default'">defaultVal</xsl:when>
        <xsl:when test="$name = 'function'">functionArg</xsl:when>
        <xsl:when test="$name = 'in'">input</xsl:when>
        <xsl:when test="$name = 'else'">else_</xsl:when>
        <xsl:when test="$name = 'new'">new_</xsl:when>
        <xsl:when test="$name = 'class'">class_</xsl:when>
        <xsl:when test="$name = 'delete'">delete_</xsl:when>
        <xsl:otherwise>
          <xsl:analyze-string select="replace($name, ' ', '_')" regex="-([a-zA-Z])">

            <xsl:matching-substring>
              <xsl:value-of select="upper-case(regex-group(1))"/>
            </xsl:matching-substring>

            <xsl:non-matching-substring>
              <xsl:value-of select="translate(., '-', '')"/>
            </xsl:non-matching-substring>

          </xsl:analyze-string>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:function>

  </xsl:stylesheet>, $xml)

return $definition

xquery version "1.0-ml";

declare variable $xml external;
let $xml := xdmp:unquote("<wrap>" || $xml || "</wrap>")

let $definition := xdmp:xslt-eval(
  <xsl:stylesheet version="2.0" exclude-result-prefixes="#all" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:apidoc="http://marklogic.com/xdmp/apidoc" xmlns:local="local">

    <xsl:output method="text" indent="yes" omit-xml-declaration="yes"/>

    <xsl:template match="@*|node()" mode="#all">
      <xsl:copy>
        <xsl:apply-templates select="@*|node()" mode="#current"/>
      </xsl:copy>
    </xsl:template>

    <xsl:template match="apidoc:function" mode="#all">
      <xsl:param name="use-function-keyword" select="false()"/>
      <xsl:param name="use-declare-keyword" select="false()"/>

      <xsl:text>    /** </xsl:text>
      <xsl:value-of select="normalize-space(apidoc:summary)"/>
      <xsl:text> **/
</xsl:text>

      <xsl:text>  </xsl:text>
      <xsl:if test="$use-declare-keyword">
        <xsl:text>declare </xsl:text>
      </xsl:if>
      <xsl:if test="$use-function-keyword">
        <xsl:text>function </xsl:text>
      </xsl:if>

      <!--
      The function name can usually be derived by convetion. But in some cases (well, one case - toJSON), an override
      is provided in the documentation. In those cases, the override should be used. This is also how this is handled
      in RunDMC in api:javascript-name
      (https://github.com/marklogic-community/RunDMC/blob/master/src/apidoc/model/data-access.xqy#L407).
      -->
      <xsl:variable name="function-name">
        <xsl:value-of>
          <xsl:variable name="name-override" select="apidoc:name[@class eq 'javascript']/text()"/>
          <xsl:choose>
            <xsl:when test="exists($name-override)">
              <!-- an override exists, use it -->
              <xsl:value-of select="$name-override"/>
            </xsl:when>
            <xsl:otherwise>
              <!-- no override exists, follow convention in local:fixName -->
              <xsl:value-of select="local:fixName(@name)"/>
            </xsl:otherwise>
          </xsl:choose>
        </xsl:value-of>
      </xsl:variable>

      <xsl:value-of select="$function-name"/>
      <xsl:text>(</xsl:text>
      <xsl:for-each select="apidoc:params/apidoc:param">
        <xsl:variable name="is-multi" select="matches(@type, ',\.\.\.$')"/>
        <xsl:if test="$is-multi">...</xsl:if>
        <xsl:value-of select="local:fixName(@name)"/>
        <xsl:if test="preceding-sibling::apidoc:param[@name = current()/@name]">2</xsl:if>
        <xsl:if test="not($is-multi) and string(@optional) = 'true'">?</xsl:if>
        <xsl:text>: </xsl:text>
        <xsl:value-of select="local:fixType(@type, true())"/>
        <xsl:if test="$is-multi">[]</xsl:if>
        <xsl:if test="not($is-multi) and string(@optional) = 'true'">|null|undefined</xsl:if>
        <xsl:if test="not(position() = last())">, </xsl:if>
      </xsl:for-each>
      <xsl:text>): </xsl:text>
      <xsl:value-of select="local:fixType(string(apidoc:return), false())"/>
      <xsl:text>;

</xsl:text>
    </xsl:template>

    <xsl:template match="/wrap">
      <xsl:variable name="root" select="."/>

      <xsl:text>// Type definitions for </xsl:text><xsl:value-of select="apidoc:module[exists(apidoc:function)]/@name"/><xsl:text>
</xsl:text>
      <xsl:text>// Definitions: </xsl:text><xsl:value-of select="base-uri(.)"/><xsl:text>

</xsl:text>

        <xsl:for-each select="distinct-values(((//apidoc:module[exists(apidoc:function)]/@lib, 'xdmp')[1], //apidoc:function/@lib))">
          <xsl:sort/>
          <xsl:variable name="lib" select="."/>
          <xsl:variable name="name" select="string-join(local:fixName($lib), '')"/>
          <xsl:variable name="functions" select="$root//apidoc:function[@lib = $lib]"/>
          <xsl:variable name="module" select="$functions/.."/>

          <xsl:text>
/**
</xsl:text>
          <xsl:value-of select="$module/apidoc:summary"/>
          <xsl:text>
**/

</xsl:text>

          <xsl:choose>
          <xsl:when test="$name = 'GlobalObject'">
            <xsl:apply-templates select="$functions">
              <xsl:sort select="@name" />
              <xsl:with-param name="use-function-keyword" select="true()"/>
              <xsl:with-param name="use-declare-keyword" select="true()"/>
            </xsl:apply-templates>
          </xsl:when>
          <xsl:when test="$name = 'alert'">
            <xsl:text>declare module </xsl:text>
            <xsl:value-of select="$name"/>
            <xsl:text> {{

</xsl:text>

            <xsl:apply-templates select="$functions">
              <xsl:sort select="@name" />
              <xsl:with-param name="use-function-keyword" select="true()"/>
            </xsl:apply-templates>

            <xsl:text>}}
</xsl:text>
          </xsl:when>
          <xsl:otherwise>
            <xsl:text>interface </xsl:text>
            <xsl:value-of select="$name"/>
            <xsl:text>Functions {{

</xsl:text>

            <xsl:apply-templates select="$functions">
              <xsl:sort select="@name" />
            </xsl:apply-templates>

            <xsl:text>}}
</xsl:text>

            <xsl:text>declare var </xsl:text>
            <xsl:value-of select="$name"/>
            <xsl:text>:</xsl:text>
            <xsl:value-of select="$name"/>
            <xsl:text>Functions
</xsl:text>
          </xsl:otherwise>
          </xsl:choose>
        </xsl:for-each>
    </xsl:template>

    <xsl:function name="local:fixType">
      <xsl:param name="fullType"/>
      <xsl:param name="isForParameter"/>

      <xsl:variable name="fullType" select="translate($fullType, ' ', '')"/>
      <xsl:variable name="type" select="replace(replace($fullType, ',\.\.\.$', ''), '([?*+]|\[\])$', '')"/>
      <xsl:variable name="cardinality" select="substring($fullType, string-length($type) + 1)"/>
      <xsl:variable name="isArray" select="matches($cardinality, '^([*+]|\[\])')"/>
      <xsl:variable name="type" select="if (substring($type, 1, 1) = '(') then substring($type, 2, string-length($type) - 2) else $type"/>

      <xsl:value-of select="local:fixEachType($type, $isArray, $isForParameter)"/>
    </xsl:function>

    <xsl:function name="local:fixEachType">
      <xsl:param name="typeUnion"/>
      <xsl:param name="isArray"/>
      <xsl:param name="isForParameter"/>

      <xsl:variable name="type" select="substring-before(concat($typeUnion,'|'),'|')"/>
      <xsl:variable name="type" select="replace($type, '([?*+]|\[\])$', '')"/>

      <xsl:choose>
        <xsl:when test="matches($type, '^function\(.*\)as(.+)$')">
          <xsl:value-of select="local:fixFunctionType($type, $isArray)"/>
        </xsl:when>
        <xsl:when test="$isArray">
          <xsl:choose>
            <xsl:when test="$type = ('document-node()', 'binary()', 'element()', 'node()')">
              <xsl:value-of select="local:fixArrayType('any', $isForParameter)"/>
            </xsl:when>
            <xsl:otherwise>
              <xsl:value-of select="local:fixArrayType(string-join(local:fixSingleType($type, $isForParameter), ''), $isForParameter)"/>
            </xsl:otherwise>
          </xsl:choose>
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="local:fixSingleType($type, $isForParameter)"/>
        </xsl:otherwise>
      </xsl:choose>

      <xsl:if test="contains($typeUnion, '|')">
        <xsl:value-of select="concat('|', string-join(local:fixEachType(substring-after($typeUnion, '|'), $isArray, $isForParameter), ''))"/>
      </xsl:if>
    </xsl:function>

    <xsl:function name="local:fixArrayType">
      <xsl:param name="type"/>
      <xsl:param name="isForParameter"/>
      <xsl:value-of select="concat(concat(if($isForParameter) then 'MLArray<' else 'Sequence<', $type), '>')"/>
    </xsl:function>

    <xsl:function name="local:fixSingleType">
      <xsl:param name="type"/>
      <xsl:param name="isForParameter"/>
      <xsl:choose>
        <!-- workarounds for docs bugs -->
        <xsl:when test="$type = 'query-results-serialize($results)'">MLNode<any></xsl:when>
        <xsl:when test="$type = 'option'">Sequence<any></xsl:when>
        <xsl:when test="$type = 'genericDateTimeArg'">xs.GenericDateTimeArg</xsl:when>
        <xsl:when test="$type = 'rdf:collatedString'">sql.CollatedString</xsl:when>

        <xsl:when test="$type = 'any[]'">Array<any></xsl:when>
        <xsl:when test="$type = ('ValueIterator', 'Sequence')">Sequence<any></xsl:when>
        <xsl:when test="$type = 'document-node()'">DocumentNode<any></xsl:when>
        <xsl:when test="$type = ('binary()')">MLNode<any></xsl:when>
        <xsl:when test="$type = ('element()', 'node()')">
          <xsl:choose>
            <xsl:when test="$isForParameter">MLNodeOrObject<any></xsl:when>
            <xsl:otherwise>MLNode<any></xsl:otherwise>
          </xsl:choose>
        </xsl:when>
        <xsl:when test="$type = ('empty-seq', 'empty-sequence', 'empty-sequence()', 'null', 'Null')">null</xsl:when>
        <xsl:when test="$type = ('String', 'xs:anyURI', 'xs:NCName', 'xs:string', 'xs:time', 'xs:duration', 'xs:dayTimeDuration')">string</xsl:when>
        <xsl:when test="$type = ('json:array', 'Array')">Array<any></xsl:when>
        <xsl:when test="$type = ('json:object', 'map:map', 'Options')">{[key:string]:any}</xsl:when>
        <xsl:when test="$type = ('function()', 'xdmp:function', 'function(*', 'function(*)', 'function')">() => any</xsl:when>
        <xsl:when test="$type = 'xs:boolean'">boolean</xsl:when>
        <xsl:when test="$type = ('any', 'xs:anyAtomicType', 'item()')">any</xsl:when>
        <xsl:when test="$type = ('xs:dateTime', 'xs:date')">Date</xsl:when>
        <xsl:when test="$type = ('Int', 'double', 'xs:numeric', 'numeric', 'xs:decimal', 'xs:double', 'xs:unsigned', 'xs:unsingedLong', 'xs:unsignedLong', 'xs:int', 'xs:float', 'xs:integer', 'xs:long', 'xs:nonNegativeInteger', 'xs:positiveInteger', 'xs:unsignedInt')">number</xsl:when>
        <xsl:when test="matches($type, '^(schema-)?[Ee]lement\([^)]+\)')">
          <xsl:choose>
            <!-- TODO: Can we perform generics on this? -->
            <xsl:when test="$isForParameter">MLNodeOrObject<any></xsl:when>
            <xsl:otherwise>MLNode<any></xsl:otherwise>
          </xsl:choose>
        </xsl:when>
        <xsl:otherwise>
          <xsl:variable name="local-name" select="replace($type, '^([^:.]+[:.])?([^\(\)]+)\(?\)?$', '$2')"/>
          <xsl:variable name="fixed-local-name" select="string-join(local:fixName($local-name), '')"/>
          <xsl:variable name="type-name" select="concat(upper-case(substring($fixed-local-name, 1, 1)), substring($fixed-local-name, 2))"/>
          <xsl:choose>
            <xsl:when test="contains($type, ':') or contains($type, '.')">
              <xsl:variable name="prefix-name" select="replace($type, '^([^:.]+[:.])?(.+)$', '$1')"/>
              <xsl:value-of select="concat(substring($prefix-name, 1, string-length($prefix-name) - 1), concat('.', $type-name))"/>
            </xsl:when>
            <xsl:otherwise>
              <xsl:value-of select="$type-name"/>
            </xsl:otherwise>
          </xsl:choose>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:function>

    <xsl:function name="local:fixFunctionType">
      <xsl:param name="type"/>
      <xsl:param name="isArray"/>
      <xsl:text>(</xsl:text>
      <xsl:if test="matches($type, '^function\((.+)\)as(.+)$')">
        <xsl:value-of select="local:fixParameters(replace($type, '^function\((.+)\)as(.+)$', '$1'), 1)"/>
      </xsl:if>
      <xsl:text>)=></xsl:text>
      <xsl:variable name="returnType" select="local:fixType(replace($type, '^function\(.*\)as(.+)$', '$1'), false())"/>
      <xsl:choose>
        <xsl:when test="$isArray">
          <xsl:value-of select="local:fixArrayType($returnType, false())"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="$returnType"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:function>

    <xsl:function name="local:fixParameters">
      <xsl:param name="parameters"/>
      <xsl:param name="number"/>
      <xsl:value-of select="concat(concat('p', $number), ':')"/>
      <xsl:variable name="type" select="substring-before(concat($parameters,','),',')"/>
      <xsl:value-of select="local:fixType($type, true())"/>
      <xsl:if test="contains($parameters, ',')">
        <xsl:value-of select="concat(',', string-join(local:fixParameters(substring-after($parameters, ','), $number + 1), ''))"/>
      </xsl:if>
    </xsl:function>

    <xsl:function name="local:fixName">
      <xsl:param name="name"/>

      <xsl:choose>
        <xsl:when test="$name = 'default'">defaultVal</xsl:when>
        <xsl:when test="$name = 'function'">functionArg</xsl:when>
        <xsl:when test="$name = 'in'">input</xsl:when>
        <xsl:when test="$name = 'else'">else_</xsl:when>
        <xsl:when test="$name = 'new'">new_</xsl:when>
        <xsl:when test="$name = 'class'">class_</xsl:when>
        <xsl:when test="$name = 'delete'">delete_</xsl:when>
        <xsl:otherwise>
          <xsl:analyze-string select="replace($name, ' ', '_')" regex="-([a-zA-Z])">

            <xsl:matching-substring>
              <xsl:value-of select="upper-case(regex-group(1))"/>
            </xsl:matching-substring>

            <xsl:non-matching-substring>
              <xsl:value-of select="translate(., '-', '')"/>
            </xsl:non-matching-substring>

          </xsl:analyze-string>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:function>

  </xsl:stylesheet>, $xml)

return $definition


s/write/write-text/

s/write/write-text/

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

(:~
 : Zorba FOTS driver util
 : @author Sorin Nasoi
 :)

module namespace util =
  "http://www.zorba-xquery.com/fots-driver/util";

import schema namespace output =
  "http://www.w3.org/2010/xslt-xquery-serialization";

import module namespace file  =
  "http://expath.org/ns/file";

import module namespace fots-err =
  "http://www.zorba-xquery.com/fots-driver/errors" at "errors.xq";

declare namespace err =
  "http://www.w3.org/2005/xqt-errors";

declare namespace fots =
  "http://www.w3.org/2010/09/qt-fots-catalog";

declare namespace ann =
  "http://www.zorba-xquery.com/annotations";

declare namespace op = "http://www.zorba-xquery.com/options/features";
declare namespace f = "http://www.zorba-xquery.com/features";
declare option op:disable "f:trace";

(:~
 : The serialization parameters for XML serialization.
 :)
 declare variable $util:serParamXml :=
  <output:serialization-parameters>
    <output:method                value="xml" />
    <output:indent                value="no"  />
    <output:omit-xml-declaration  value="yes" />
  </output:serialization-parameters>;

(:~
 : The serialization parameters for text serialization.
 :)
 declare variable $util:writeText :=
  <output:serialization-parameters>
    <output:method                value="text" />
    <output:indent                value="yes"  />
  </output:serialization-parameters>;

(:~
 : The serialization parameters for text serialization.
 :)
 declare variable $util:writeXML :=
  <output:serialization-parameters>
    <output:method                value="xml" />
    <output:indent                value="yes"  />
  </output:serialization-parameters>;

(:~
 : Search within a given test-case for all element nodes with a given node name.
 : For each such element node return:
 : (a) if the node has a @file attr, the content of that file as unparsed text,
 : or
 : (b) the typed value of the node (assuming it is promotable to string).
 :
 : @param $parentNode
 : @param $baseURI
 : @param $node-name
 : @return the content of the node with name 'node-name'.
 :)
declare %ann:nondeterministic function util:get-value(
  $parentNode as element(),
  $baseURI    as xs:anyURI,
  $node-name  as xs:string
) as xs:string
{
  try
  {
    for $node in $parentNode/descendant-or-self::*
    where (fn:local-name-from-QName(fn:node-name($node)) eq $node-name)
    return
      if ($node/@file)
      then
      {
        if(ends-with($node/@file, ".xml"))
        then fn:serialize(doc(resolve-uri($node/@file, $baseURI)), $util:serParamXml)
        else fn:unparsed-text(resolve-uri($node/@file, $baseURI))
      }
      else fn:string($node)
  }
  catch *
  {
    fn:error($fots-err:errNA, $err:description)
  }
};

(:~
 :  Returns the parent folder of the given file path.
 :  example: util:parent-folder('/home/user/file.ext') returns '/home/user'.
 :
 : @param $path Path.
 : @return the parent folder of the given file.
 :)
declare function util:parent-folder(
  $path as xs:string
) as xs:anyURI
{
  xs:anyURI(fn:substring-before($path, file:base-name($path)))
};

declare %ann:sequential function util:write-query-to-file(
  $query        as xs:string,
  $queryName    as xs:string
) {
  file:write-text(concat("query_", $queryName, ".xq"),
                  $query,
                  $util:writeText);
};

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

(:~
 : Zorba FOTS driver util
 : @author Sorin Nasoi
 :)

module namespace util =
  "http://www.zorba-xquery.com/fots-driver/util";

import schema namespace output =
  "http://www.w3.org/2010/xslt-xquery-serialization";

import module namespace file  =
  "http://expath.org/ns/file";

import module namespace fots-err =
  "http://www.zorba-xquery.com/fots-driver/errors" at "errors.xq";

declare namespace err =
  "http://www.w3.org/2005/xqt-errors";

declare namespace fots =
  "http://www.w3.org/2010/09/qt-fots-catalog";

declare namespace ann =
  "http://www.zorba-xquery.com/annotations";

declare namespace op = "http://www.zorba-xquery.com/options/features";
declare namespace f = "http://www.zorba-xquery.com/features";
declare option op:disable "f:trace";

(:~
 : The serialization parameters for XML serialization.
 :)
 declare variable $util:serParamXml :=
  <output:serialization-parameters>
    <output:method                value="xml" />
    <output:indent                value="no"  />
    <output:omit-xml-declaration  value="yes" />
  </output:serialization-parameters>;

(:~
 : The serialization parameters for text serialization.
 :)
 declare variable $util:writeText :=
  <output:serialization-parameters>
    <output:method                value="text" />
    <output:indent                value="yes"  />
  </output:serialization-parameters>;

(:~
 : The serialization parameters for text serialization.
 :)
 declare variable $util:writeXML :=
  <output:serialization-parameters>
    <output:method                value="xml" />
    <output:indent                value="yes"  />
  </output:serialization-parameters>;

(:~
 : Search within a given test-case for all element nodes with a given node name.
 : For each such element node return:
 : (a) if the node has a @file attr, the content of that file as unparsed text,
 : or
 : (b) the typed value of the node (assuming it is promotable to string).
 :
 : @param $parentNode
 : @param $baseURI
 : @param $node-name
 : @return the content of the node with name 'node-name'.
 :)
declare %ann:nondeterministic function util:get-value(
  $parentNode as element(),
  $baseURI    as xs:anyURI,
  $node-name  as xs:string
) as xs:string
{
  try
  {
    for $node in $parentNode/descendant-or-self::*
    where (fn:local-name-from-QName(fn:node-name($node)) eq $node-name)
    return
      if ($node/@file)
      then
      {
        if(ends-with($node/@file, ".xml"))
        then fn:serialize(doc(resolve-uri($node/@file, $baseURI)), $util:serParamXml)
        else fn:unparsed-text(resolve-uri($node/@file, $baseURI))
      }
      else fn:string($node)
  }
  catch *
  {
    fn:error($fots-err:errNA, $err:description)
  }
};

(:~
 :  Returns the parent folder of the given file path.
 :  example: util:parent-folder('/home/user/file.ext') returns '/home/user'.
 :
 : @param $path Path.
 : @return the parent folder of the given file.
 :)
declare function util:parent-folder(
  $path as xs:string
) as xs:anyURI
{
  xs:anyURI(fn:substring-before($path, file:base-name($path)))
};

declare %ann:sequential function util:write-query-to-file(
  $query        as xs:string,
  $queryName    as xs:string
) {
  file:write(concat("query_", $queryName, ".xq"),
             $query,
             $util:writeText);
};