fix test for leading-wildcard in ft:search

xquery version "3.0";

module namespace luct="http://exist-db.org/xquery/lucene/test";

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

declare variable $luct:INDEX_DATA :=
    <doc>
        <field name="title">Lorem ipsum dolor</field>
    </doc>;

declare variable $luct:INDEX_DATA_SUBCOLL :=
    <doc>
        <field name="title">Only admin can read this</field>
    </doc>;

declare
    %test:setUp
function luct:setup() {
    xmldb:create-collection("/db", "lucenetest"),
    xmldb:store("/db/lucenetest", "test.txt", "Lorem ipsum", "text/text"),
    sm:chmod(xs:anyURI("/db/lucenetest/test.txt"), "rw-------"),
    ft:index("/db/lucenetest/test.txt", $luct:INDEX_DATA),

    xmldb:create-collection("/db/lucenetest", "sub"),
    sm:chmod(xs:anyURI("/db/lucenetest/sub"), "rwx------"),
    xmldb:store("/db/lucenetest/sub", "test.txt", "Lorem ipsum", "text/text"),
    sm:chmod(xs:anyURI("/db/lucenetest/sub/test.txt"), "rw-rw-rw-"),
    ft:index("/db/lucenetest/sub/test.txt", $luct:INDEX_DATA_SUBCOLL)
};

declare
    %test:tearDown
function luct:cleanup() {
    xmldb:remove("/db/lucenetest")
};

declare
    %test:assertEmpty
function luct:check-visibility-fail() {
    system:as-user("guest", "guest",
        ft:search("/db/lucenetest/", "title:ipsum")/search/@uri/string()
    )
};

declare
    %test:assertEquals("/db/lucenetest/test.txt")
function luct:check-visibility-pass() {
    system:as-user("admin", "",
        ft:search("/db/lucenetest/", "title:ipsum")/search/@uri/string()
    )
};

declare
    %test:assertEmpty
function luct:check-visibility-collection-fail() {
    system:as-user("guest", "guest",
        ft:search("/db/lucenetest/sub/", "title:admin")/search/@uri/string()
    )
};

declare
    %test:assertEquals("/db/lucenetest/sub/test.txt")
function luct:check-visibility-collection-pass() {
    system:as-user("admin", "",
        ft:search("/db/lucenetest/sub/", "title:admin")/search/@uri/string()
    )
};

declare
    %test:assertEquals("/db/lucenetest/test.txt")
function luct:check-leading-wildcard() {
    system:as-user("admin", "",
        ft:search("/db/lucenetest/", "title:*rem", (), <options><leading-wildcard>yes</leading-wildcard></options>)/search/@uri/string()
    )
};

xquery version "3.0";

module namespace luct="http://exist-db.org/xquery/lucene/test";

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

declare variable $luct:INDEX_DATA :=
    <doc>
        <field name="title">Lorem ipsum dolor</field>
    </doc>;

declare variable $luct:INDEX_DATA_SUBCOLL :=
    <doc>
        <field name="title">Only admin can read this</field>
    </doc>;

declare
    %test:setUp
function luct:setup() {
    xmldb:create-collection("/db", "lucenetest"),
    xmldb:store("/db/lucenetest", "test.txt", "Lorem ipsum", "text/text"),
    sm:chmod(xs:anyURI("/db/lucenetest/test.txt"), "rw-------"),
    ft:index("/db/lucenetest/test.txt", $luct:INDEX_DATA),

    xmldb:create-collection("/db/lucenetest", "sub"),
    sm:chmod(xs:anyURI("/db/lucenetest/sub"), "rwx------"),
    xmldb:store("/db/lucenetest/sub", "test.txt", "Lorem ipsum", "text/text"),
    sm:chmod(xs:anyURI("/db/lucenetest/sub/test.txt"), "rw-rw-rw-"),
    ft:index("/db/lucenetest/sub/test.txt", $luct:INDEX_DATA_SUBCOLL)
};

declare
    %test:tearDown
function luct:cleanup() {
    xmldb:remove("/db/lucenetest")
};

declare
    %test:assertEmpty
function luct:check-visibility-fail() {
    system:as-user("guest", "guest",
        ft:search("/db/lucenetest/", "title:ipsum")/search/@uri/string()
    )
};

declare
    %test:assertEquals("/db/lucenetest/test.txt")
function luct:check-visibility-pass() {
    system:as-user("admin", "",
        ft:search("/db/lucenetest/", "title:ipsum")/search/@uri/string()
    )
};

declare
    %test:assertEmpty
function luct:check-visibility-collection-fail() {
    system:as-user("guest", "guest",
        ft:search("/db/lucenetest/sub/", "title:admin")/search/@uri/string()
    )
};

declare
    %test:assertEquals("/db/lucenetest/sub/test.txt")
function luct:check-visibility-collection-pass() {
    system:as-user("admin", "",
        ft:search("/db/lucenetest/sub/", "title:admin")/search/@uri/string()
    )
};

declare
    %test:assertEquals("Lorem")
function luct:check-leading-wildcard() {
    system:as-user("admin", "",
        ft:search("/db/lucenetest/", "title:*rem", (), <options><leading-wildcard>yes</leading-wildcard></options>)/search/@uri/string()
    )
};


[refactor] Mark failing test as 'pending'

xquery version "3.0";

module namespace excepttest="http://exist-db.org/xquery/test/except";
import module namespace test="http://exist-db.org/xquery/xqsuite" at "resource:org/exist/xquery/lib/xqsuite/xqsuite.xql";
declare namespace tei="http://www.tei-c.org/ns/1.0";

declare variable $excepttest:DATA :=
    <TEI xmlns="http://www.tei-c.org/ns/1.0" xml:id="sha-mac">
        <text>

                    Act 1

        </text>
    </TEI>;

declare variable $excepttest:DATA2 :=
    <TEI xmlns="http://www.tei-c.org/ns/1.0" xml:id="sha-mac">
        <text>

                    Act 12

        </text>
    </TEI>;

declare variable $excepttest:COLLECTION_NAME := "/exceptTest";
declare variable $excepttest:SUBCOL1_NAME := $excepttest:COLLECTION_NAME||"/col1";
declare variable $excepttest:SUBCOL2_NAME := $excepttest:COLLECTION_NAME||"/col2";

declare variable $excepttest:COLLECTION := collection("/db"||$excepttest:COLLECTION_NAME);
declare variable $excepttest:SUBCOL1 := collection("/db"||$excepttest:SUBCOL1_NAME);
declare variable $excepttest:SUBCOL2 := collection("/db"||$excepttest:SUBCOL2_NAME);

declare
%test:setUp
function excepttest:setup() {

    xmldb:create-collection("/db", $excepttest:COLLECTION_NAME),
    xmldb:create-collection("/db", $excepttest:SUBCOL1_NAME),
    xmldb:create-collection("/db", $excepttest:SUBCOL2_NAME),
    xmldb:store("/db"||$excepttest:SUBCOL1_NAME, "test.xml", $excepttest:DATA),
    xmldb:store("/db"||$excepttest:SUBCOL2_NAME, "test2.xml", $excepttest:DATA2)
};

declare
 %test:tearDown
 function excepttest:cleanup() {
 xmldb:remove($excepttest:COLLECTION_NAME)
 };

declare
    %test:pending("https://github.com/eXist-db/exist/issues/1348")
    %test:assertEquals("Act 1")
function excepttest:except-wildcard() {
 ($excepttest:COLLECTION/* except $excepttest:SUBCOL2/*)//tei:head/text()
};

declare
%test:assertEquals("Act 1")
function excepttest:except-node-name() {
 ($excepttest:COLLECTION/tei:TEI except $excepttest:SUBCOL2/tei:TEI)//tei:head/text()
};

xquery version "3.0";

module namespace excepttest="http://exist-db.org/xquery/test/except";
import module namespace test="http://exist-db.org/xquery/xqsuite" at "resource:org/exist/xquery/lib/xqsuite/xqsuite.xql";
declare namespace tei="http://www.tei-c.org/ns/1.0";

declare variable $excepttest:DATA :=
    <TEI xmlns="http://www.tei-c.org/ns/1.0" xml:id="sha-mac">
        <text>

                    Act 1

        </text>
    </TEI>;

declare variable $excepttest:DATA2 :=
    <TEI xmlns="http://www.tei-c.org/ns/1.0" xml:id="sha-mac">
        <text>

                    Act 12

        </text>
    </TEI>;

declare variable $excepttest:COLLECTION_NAME := "/exceptTest";
declare variable $excepttest:SUBCOL1_NAME := $excepttest:COLLECTION_NAME||"/col1";
declare variable $excepttest:SUBCOL2_NAME := $excepttest:COLLECTION_NAME||"/col2";

declare variable $excepttest:COLLECTION := collection("/db"||$excepttest:COLLECTION_NAME);
declare variable $excepttest:SUBCOL1 := collection("/db"||$excepttest:SUBCOL1_NAME);
declare variable $excepttest:SUBCOL2 := collection("/db"||$excepttest:SUBCOL2_NAME);

declare
%test:setUp
function excepttest:setup() {

    xmldb:create-collection("/db", $excepttest:COLLECTION_NAME),
    xmldb:create-collection("/db", $excepttest:SUBCOL1_NAME),
    xmldb:create-collection("/db", $excepttest:SUBCOL2_NAME),
    xmldb:store("/db"||$excepttest:SUBCOL1_NAME, "test.xml", $excepttest:DATA),
    xmldb:store("/db"||$excepttest:SUBCOL2_NAME, "test2.xml", $excepttest:DATA2)
};

declare
 %test:tearDown
 function excepttest:cleanup() {
 xmldb:remove($excepttest:COLLECTION_NAME)
 };

declare
%test:assertEquals("Act 1")
function excepttest:except-wildcard() {
 ($excepttest:COLLECTION/* except $excepttest:SUBCOL2/*)//tei:head/text()
};

declare
%test:assertEquals("Act 1")
function excepttest:except-node-name() {
 ($excepttest:COLLECTION/tei:TEI except $excepttest:SUBCOL2/tei:TEI)//tei:head/text()
};


renamed DynamicFunctionInfo to FunctionItemInfo

(:
 : 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 CLI
 : @author Ghislain Fourny, Sorin Nasoi
 :)

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

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

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";

(:~
 : Path to the FOTS catalog.xml file. If the path is relative, it will be
 : resolved relative to the directory containing this cli.xq file.
 : By default it is assumed that the FOTS was imported using CMake (i.e. after
 : 'make fots-import' and 'make fots-activate-sets' were run).
 :)
declare variable $fotsPath as xs:string external :=
  "../../build/test/fots/2011/QT3-test-suite/catalog.xml";

(:~
 : Path to the FOTSZorbaManifest.xml file. If the path is relative, it will be
 : resolved relative to the diractory containing this cli.xq file
 :)
declare variable $fotsZorbaManifestPath as xs:string external :=
  "FOTSZorbaManifest.xml";

(:~
 : Path to the results XML file generated by a previous run. Used when we just
 : want to generate a report based on some previous run, i.e., without running
 : the tests again.
 :)
declare variable $resultsFilePath as xs:string external := "";

(:~
 : Path to the ExpectedFailures file.
 :)
declare variable $expectedFailuresPath as xs:string external :="";

(:~
 : The CLI command you want to run.
 :)
declare variable $mode as xs:string external := "";

(:~
 : A list of comma-seperated prefixes that acts as a filter for the test-sets
 : to be processed: a test-set is going to be proccesed only if its name starts
 : with a prefix in this list. An empty string means no filtering.
 :
 : Used by the list-test-sets, list-test-cases, and run-test-sets commands.
 :)
declare variable $testSetPrefixes as xs:string external := "";

(:~
 : A list of comma-seperated prefixes that acts as a filter for the test-cases
 : to be processed: a test-case is going to be proccesed only if its name starts
 : with a prefix in this list. An empty string means no filtering.
 :
 : Used by the list-test-cases, and run-test-sets commands.
 :)
declare variable $testCasePrefixes as xs:string external := "";

(:~
 : The test cases in this list should not have their queries evaluated at all
 : (because they segfault, or hang, or take too long, etc).
 :
 : Used by the run-test-sets, run-and-report, and report commands.
 :)
declare variable $exceptedTestCases as xs:string* := (
(: "re00987",
   "itunes",
   "raytracer", :)
   "instanceof139",
   "CastAs-UnionType-26",
   "CastAs-UnionType-30",
   "fn-unparsed-text-lines-052",
   "cbcl-subsequence-011",
   "cbcl-subsequence-012",
   "cbcl-subsequence-013",
   "cbcl-subsequence-014"        (:see bug lp:1069794 :)
,  "re00975",
   "re00976",
   "re00976a"                    (:see bug lp:1070533 :)
);

(:~
 : The test sets in this list should not be not be processed at all.
 :
 : Used by the run-test-sets, run-and-report, and report commands.
 :)
declare variable $exceptedTestSets as xs:string* := ();

(:~
 : Name of the test-set containing a test-case to process. Used only for the
 : "run-test-case" command.
 :)
declare variable $testSetName as xs:string external := "";

(:~
 : Name of the test-case to process. Used only for the "run-test-case" command.
 :)
declare variable $testCaseName as xs:string external := "";

(:~
 : A dependency used in the 'list-test-cases' and 'run-test-sets' commands to
 : filter the test-cases that will actually be listed/run. Only test-cases whose
 : applicable dependencies contain at least one dependency the matches this
 : user-specified dependency will be listed/run. The filtering dependency is
 : given as a string of the form "depValue_depSatisfied" (for example,
 : "XQ30+_true"), or just "depValue" (in which case "true" is assumed as the
 : value of the satisfied attribute). If $dependency is set to the empty string,
 : no filtering is done.
 :
 : Used in the list-test-cases and run-test-sets commands.
 :)
declare variable $dependency as xs:string external := "";

(:~
 : List of assertion types used in the 'list-test-cases' and 'run-test-sets'
 : commands to filter the test-cases that will actually be listed/run. A test
 : case qualifies if there is at least one element node under the <result>
 : node of the <test-case> whose local name is equal to one of the strings
 : in the filtering set. If $assrtType is set to the empty sequence, no
 : filtering is done.
 :
 : Used in the list-test-cases and run-test-sets commands.
 :)
declare variable $assertions as xs:string* external := ();

(:~
 : Regex to be used by the 'list-matching-test-cases' command.
 :)
declare variable $pattern as xs:string external := "";

(:~
 : Flags to be used by the 'list-matching-test-cases' command.
 :)
declare variable $flags as xs:string external := "";

(:~
 : Enable or disable verbose output. When this is set to true the exact query
 : that is run by XQXQ is also written down into a query_*.xq file.
 :)
declare variable $verbose as xs:string external := "false";

declare function local:usage() as xs:string
{
  string-join((
    "Zorba FOTS driver usage examples:",
    "If you run Zorba from a checkout of the trunk and the build/bin folder,",
    "- /path/to/cli.xq can be set to ../../test/fots_driver/cli.xq",
    "",
    "Always try to output the result back to an XML file with nice indentation:",
    "./zorba -f -q ../../test/fots_driver/cli.xq -e SET_CLI_OPTIONS_HERE -o output.xml --indent",
    "",
    " This way you will see trace information in the CLI window and detailed",
    " results of the test cases in the 'output.xml'.",
    "",
    " Please note that when Verbose is set to true the query that is executed",
  	" is written down into a query_TESTCASENAME.xq file, where TESTCASENAME is",
  	" the test case name.",
  	"",
    "zorba -f -q /path/to/cli.xq -e fotsPath:=/path/to/QT3-test-suite/catalog.xml -e fotsZorbaManifestPath:=/path/to/Zorba_manifest.xml -e mode:=list-test-sets",
    "zorba -f -q /path/to/cli.xq -e fotsPath:=/path/to/QT3-test-suite/catalog.xml -e mode:=list-test-sets",
    "zorba -f -q /path/to/cli.xq -e fotsPath:=/path/to/QT3-test-suite/catalog.xml -e mode:=list-test-sets -e testSetPrefixes:=prod,app",
    "zorba -f -q /path/to/cli.xq -e fotsPath:=/path/to/QT3-test-suite/catalog.xml -e mode:=list-test-cases -e testSetPrefixes:=prod-Literal",
    "zorba -f -q /path/to/cli.xq -e fotsPath:=/path/to/QT3-test-suite/catalog.xml -e mode:=list-test-cases -e dependency:=higherOrderFunctions",
    "zorba -f -q /path/to/cli.xq -e fotsPath:=/path/to/QT3-test-suite/catalog.xml -e mode:=list-test-cases -e dependency:=higherOrderFunctions -e assertions:=assert-count",
    "zorba -f -q /path/to/cli.xq -e fotsPath:=/path/to/QT3-test-suite/catalog.xml -e mode:=list-matching-test-cases -e pattern:=catch",
    "zorba -f -q /path/to/cli.xq -e fotsPath:=/path/to/QT3-test-suite/catalog.xml -e mode:=run-test-sets -e testSetPrefixes:=prod -o result.xml --indent",
    "zorba -f -q /path/to/cli.xq -e fotsPath:=/path/to/QT3-test-suite/catalog.xml -e mode:=run-test-sets -e testSetPrefixes:=prod -e expectedFailuresPath:=ExpectedFailures.xml -o result.xml --indent",
    "zorba -f -q /path/to/cli.xq -e fotsPath:=/path/to/QT3-test-suite/catalog.xml -e mode:=run-test-sets -e testSetPrefixes:=prod -e dependency:=higherOrderFunctions_false -o result.xml --indent",
    "zorba -f -q /path/to/cli.xq -e fotsPath:=/path/to/QT3-test-suite/catalog.xml -e mode:=run-test-sets -e testSetPrefixes:=prod -e assertions:=assert-count -o result.xml --indent",
    "zorba -f -q /path/to/cli.xq -e fotsPath:=/path/to/QT3-test-suite/catalog.xml -e mode:=run-test-sets -e testSetPrefixes:=prod-Literal -e verbose:=true -o result.xml --indent",
    "zorba -f -q /path/to/cli.xq -e fotsPath:=/path/to/QT3-test-suite/catalog.xml -e mode:=run-test-set  -e testSetName:=fn-matches -o result.xml --indent",
    "zorba -f -q /path/to/cli.xq -e fotsPath:=/path/to/QT3-test-suite/catalog.xml -e mode:=run-test-case -e testSetName:=prod-Literal -e testCaseName:=Literals001 -o result.xml --indent",
    "zorba -f -q /path/to/cli.xq -e fotsPath:=/path/to/QT3-test-suite/catalog.xml -e mode:=run-and-report -o results_Zorba_XQ30.xml --indent --disable-http-resolution",
    "zorba -f -q /path/to/cli.xq -e fotsPath:=/path/to/QT3-test-suite/catalog.xml -e mode:=report -e resultsFilePath:=results.xml -o results_Zorba_XQ30.xml --indent",
    "zorba -f -q /path/to/cli.xq -e mode:=generate-expected-failures -e resultsFilePath:=failures.xml -o ExpectedFailures.xml --indent",
    ""
    ), "
")
};

(:~
 : Tokenize a string that contains a comma-separated list of tokens.
 : Note: if the input string is empty, the function returns the empty sequence.
 :)
declare %private function local:tokenize(
  $input as xs:string
) as xs:string*
{
  tokenize($input, ",")
};

variable $fotsPathMsg := "The path to FOTS catalog.xml was set to: ";

variable $testSetPrefixesMsg := "'testSetPrefixes' was set to: ";

variable $testSetNameMsg := "'testSetName' was set to: ";

variable $testCasePrefixesMsg := "'testCasePrefixes' was set to: ";

variable $testCaseNameMsg := "'testCaseName' was set to: ";

switch ($mode)

case "list-test-sets"
return
{
  trace($fotsPath, $fotsPathMsg);
  trace($testSetPrefixes, $testSetPrefixesMsg);

  string-join
  (
  (
    d:list-test-sets($fotsPath, local:tokenize($testSetPrefixes))
  ),
  "
"
  )
}

case "list-test-cases"
return
{
  trace($fotsPath, $fotsPathMsg);
  trace($testSetPrefixes, $testSetPrefixesMsg);
  trace($dependency, "'dependency' set to:");
  trace($assertions, "'assertions' set to: ");

  string-join
  (
  (
    d:list-test-cases($fotsPath,
                      d:list-test-sets($fotsPath, local:tokenize($testSetPrefixes)),
                      $dependency,
                      $assertions)
  ),
  "
"
  )
}

case "list-matching-test-cases"
return
{
  trace($fotsPath, $fotsPathMsg);
  trace($pattern, "pattern set to: ");
  trace($flags, "flags were set to: ");

  string-join
  (
  (
    d:list-matching-test-cases($fotsPath, $pattern, $flags), ""
  ),
  "
"
  )
}

case "run-test-sets"
return
{
  trace($testSetPrefixes, $testSetPrefixesMsg);
  trace($testCasePrefixes, $testCasePrefixesMsg);
  trace($dependency, "'dependency' set to:");
  trace($assertions, "'assertions' set to: ");
  trace($verbose, "'verbose' set to:");
  trace($mode, "Cli command was set to:");

  d:run-fots($fotsPath,
             $fotsZorbaManifestPath,
             local:tokenize($testSetPrefixes),
             $exceptedTestSets,
             local:tokenize($testCasePrefixes),
             $exceptedTestCases,
             $dependency,
             $assertions,
             xs:boolean($verbose),
             $expectedFailuresPath,
             $mode)
}

case "run-test-set"
return
{
  trace($testSetName, $testSetNameMsg);
  trace($dependency, "'dependency' set to:");
  trace($assertions, "'assertions' set to: ");
  trace($verbose, "'verbose' set to:");
  trace($mode, "Cli command was set to:");

  d:run-test-set($fotsPath,
                 $fotsZorbaManifestPath,
                 $testSetName,
                 $exceptedTestSets,
                 (),
                 $exceptedTestCases,
                 $dependency,
                 $assertions,
                 xs:boolean($verbose),
                 $expectedFailuresPath,
                 $mode)
}

case "run-test-case"
return
{
  trace($testSetName, $testSetNameMsg);
  trace($testCaseName, $testCaseNameMsg);
  trace($verbose, "'verbose' set to:");
  trace($mode, "Cli command was set to:");

  d:run-fots($fotsPath,
             $fotsZorbaManifestPath,
             $testSetName,
             $exceptedTestSets,
             $testCaseName,
             $exceptedTestCases,
             "",
             (),
             xs:boolean($verbose),
             $expectedFailuresPath,
             $mode)
}

case "run-and-report"
return
{
  r:run-and-report($fotsPath,
                   $fotsZorbaManifestPath,
                   $exceptedTestCases,
                   $exceptedTestSets)
}

case "report"
return
{
  r:report($fotsZorbaManifestPath,
           $resultsFilePath)
}

case "wiki-report"
return
{
  r:wiki-report($fotsPath,
                $resultsFilePath)
}

case "generate-expected-failures"
return
{
  r:generate-expected-failures($resultsFilePath)
}

default
return
  local:usage()

(:
 : 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 CLI
 : @author Ghislain Fourny, Sorin Nasoi
 :)

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

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

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";

(:~
 : Path to the FOTS catalog.xml file. If the path is relative, it will be
 : resolved relative to the directory containing this cli.xq file.
 : By default it is assumed that the FOTS was imported using CMake (i.e. after
 : 'make fots-import' and 'make fots-activate-sets' were run).
 :)
declare variable $fotsPath as xs:string external :=
  "../../build/test/fots/2011/QT3-test-suite/catalog.xml";

(:~
 : Path to the FOTSZorbaManifest.xml file. If the path is relative, it will be
 : resolved relative to the diractory containing this cli.xq file
 :)
declare variable $fotsZorbaManifestPath as xs:string external :=
  "FOTSZorbaManifest.xml";

(:~
 : Path to the results XML file generated by a previous run. Used when we just
 : want to generate a report based on some previous run, i.e., without running
 : the tests again.
 :)
declare variable $resultsFilePath as xs:string external := "";

(:~
 : Path to the ExpectedFailures file.
 :)
declare variable $expectedFailuresPath as xs:string external :="";

(:~
 : The CLI command you want to run.
 :)
declare variable $mode as xs:string external := "";

(:~
 : A list of comma-seperated prefixes that acts as a filter for the test-sets
 : to be processed: a test-set is going to be proccesed only if its name starts
 : with a prefix in this list. An empty string means no filtering.
 :
 : Used by the list-test-sets, list-test-cases, and run-test-sets commands.
 :)
declare variable $testSetPrefixes as xs:string external := "";

(:~
 : A list of comma-seperated prefixes that acts as a filter for the test-cases
 : to be processed: a test-case is going to be proccesed only if its name starts
 : with a prefix in this list. An empty string means no filtering.
 :
 : Used by the list-test-cases, and run-test-sets commands.
 :)
declare variable $testCasePrefixes as xs:string external := "";

(:~
 : The test cases in this list should not have their queries evaluated at all
 : (because they segfault, or hang, or take too long, etc).
 :
 : Used by the run-test-sets, run-and-report, and report commands.
 :)
declare variable $exceptedTestCases as xs:string* := (
  "re00987",
  "itunes",
  "raytracer",
  "instanceof139",
  "CastAs-UnionType-26",
  "CastAs-UnionType-30",
  "fn-unparsed-text-lines-052",
  "cbcl-subsequence-011",
  "cbcl-subsequence-012",
  "cbcl-subsequence-013",
  "cbcl-subsequence-014"        (:see bug lp:1069794 :)
, "re00975",
  "re00976",
  "re00976a"                    (:see bug lp:1070533 :)
);

(:~
 : The test sets in this list should not be not be processed at all.
 :
 : Used by the run-test-sets, run-and-report, and report commands.
 :)
declare variable $exceptedTestSets as xs:string* := ();

(:~
 : Name of the test-set containing a test-case to process. Used only for the
 : "run-test-case" command.
 :)
declare variable $testSetName as xs:string external := "";

(:~
 : Name of the test-case to process. Used only for the "run-test-case" command.
 :)
declare variable $testCaseName as xs:string external := "";

(:~
 : A dependency used in the 'list-test-cases' and 'run-test-sets' commands to
 : filter the test-cases that will actually be listed/run. Only test-cases whose
 : applicable dependencies contain at least one dependency the matches this
 : user-specified dependency will be listed/run. The filtering dependency is
 : given as a string of the form "depValue_depSatisfied" (for example,
 : "XQ30+_true"), or just "depValue" (in which case "true" is assumed as the
 : value of the satisfied attribute). If $dependency is set to the empty string,
 : no filtering is done.
 :
 : Used in the list-test-cases and run-test-sets commands.
 :)
declare variable $dependency as xs:string external := "";

(:~
 : List of assertion types used in the 'list-test-cases' and 'run-test-sets'
 : commands to filter the test-cases that will actually be listed/run. A test
 : case qualifies if there is at least one element node under the <result>
 : node of the <test-case> whose local name is equal to one of the strings
 : in the filtering set. If $assrtType is set to the empty sequence, no
 : filtering is done.
 :
 : Used in the list-test-cases and run-test-sets commands.
 :)
declare variable $assertions as xs:string* external := ();

(:~
 : Regex to be used by the 'list-matching-test-cases' command.
 :)
declare variable $pattern as xs:string external := "";

(:~
 : Flags to be used by the 'list-matching-test-cases' command.
 :)
declare variable $flags as xs:string external := "";

(:~
 : Enable or disable verbose output. When this is set to true the exact query
 : that is run by XQXQ is also written down into a query_*.xq file.
 :)
declare variable $verbose as xs:string external := "false";

declare function local:usage() as xs:string
{
  string-join((
    "Zorba FOTS driver usage examples:",
    "If you run Zorba from a checkout of the trunk and the build/bin folder,",
    "- /path/to/cli.xq can be set to ../../test/fots_driver/cli.xq",
    "",
    "Always try to output the result back to an XML file with nice indentation:",
    "./zorba -f -q ../../test/fots_driver/cli.xq -e SET_CLI_OPTIONS_HERE -o output.xml --indent",
    "",
    " This way you will see trace information in the CLI window and detailed",
    " results of the test cases in the 'output.xml'.",
    "",
    " Please note that when Verbose is set to true the query that is executed",
  	" is written down into a query_TESTCASENAME.xq file, where TESTCASENAME is",
  	" the test case name.",
  	"",
    "zorba -f -q /path/to/cli.xq -e fotsPath:=/path/to/QT3-test-suite/catalog.xml -e fotsZorbaManifestPath:=/path/to/Zorba_manifest.xml -e mode:=list-test-sets",
    "zorba -f -q /path/to/cli.xq -e fotsPath:=/path/to/QT3-test-suite/catalog.xml -e mode:=list-test-sets",
    "zorba -f -q /path/to/cli.xq -e fotsPath:=/path/to/QT3-test-suite/catalog.xml -e mode:=list-test-sets -e testSetPrefixes:=prod,app",
    "zorba -f -q /path/to/cli.xq -e fotsPath:=/path/to/QT3-test-suite/catalog.xml -e mode:=list-test-cases -e testSetPrefixes:=prod-Literal",
    "zorba -f -q /path/to/cli.xq -e fotsPath:=/path/to/QT3-test-suite/catalog.xml -e mode:=list-test-cases -e dependency:=higherOrderFunctions",
    "zorba -f -q /path/to/cli.xq -e fotsPath:=/path/to/QT3-test-suite/catalog.xml -e mode:=list-test-cases -e dependency:=higherOrderFunctions -e assertions:=assert-count",
    "zorba -f -q /path/to/cli.xq -e fotsPath:=/path/to/QT3-test-suite/catalog.xml -e mode:=list-matching-test-cases -e pattern:=catch",
    "zorba -f -q /path/to/cli.xq -e fotsPath:=/path/to/QT3-test-suite/catalog.xml -e mode:=run-test-sets -e testSetPrefixes:=prod -o result.xml --indent",
    "zorba -f -q /path/to/cli.xq -e fotsPath:=/path/to/QT3-test-suite/catalog.xml -e mode:=run-test-sets -e testSetPrefixes:=prod -e expectedFailuresPath:=ExpectedFailures.xml -o result.xml --indent",
    "zorba -f -q /path/to/cli.xq -e fotsPath:=/path/to/QT3-test-suite/catalog.xml -e mode:=run-test-sets -e testSetPrefixes:=prod -e dependency:=higherOrderFunctions_false -o result.xml --indent",
    "zorba -f -q /path/to/cli.xq -e fotsPath:=/path/to/QT3-test-suite/catalog.xml -e mode:=run-test-sets -e testSetPrefixes:=prod -e assertions:=assert-count -o result.xml --indent",
    "zorba -f -q /path/to/cli.xq -e fotsPath:=/path/to/QT3-test-suite/catalog.xml -e mode:=run-test-sets -e testSetPrefixes:=prod-Literal -e verbose:=true -o result.xml --indent",
    "zorba -f -q /path/to/cli.xq -e fotsPath:=/path/to/QT3-test-suite/catalog.xml -e mode:=run-test-set  -e testSetName:=fn-matches -o result.xml --indent",
    "zorba -f -q /path/to/cli.xq -e fotsPath:=/path/to/QT3-test-suite/catalog.xml -e mode:=run-test-case -e testSetName:=prod-Literal -e testCaseName:=Literals001 -o result.xml --indent",
    "zorba -f -q /path/to/cli.xq -e fotsPath:=/path/to/QT3-test-suite/catalog.xml -e mode:=run-and-report -o results_Zorba_XQ30.xml --indent --disable-http-resolution",
    "zorba -f -q /path/to/cli.xq -e fotsPath:=/path/to/QT3-test-suite/catalog.xml -e mode:=report -e resultsFilePath:=results.xml -o results_Zorba_XQ30.xml --indent",
    "zorba -f -q /path/to/cli.xq -e mode:=generate-expected-failures -e resultsFilePath:=failures.xml -o ExpectedFailures.xml --indent",
    ""
    ), "
")
};

(:~
 : Tokenize a string that contains a comma-separated list of tokens.
 : Note: if the input string is empty, the function returns the empty sequence.
 :)
declare %private function local:tokenize(
  $input as xs:string
) as xs:string*
{
  tokenize($input, ",")
};

variable $fotsPathMsg := "The path to FOTS catalog.xml was set to: ";

variable $testSetPrefixesMsg := "'testSetPrefixes' was set to: ";

variable $testSetNameMsg := "'testSetName' was set to: ";

variable $testCasePrefixesMsg := "'testCasePrefixes' was set to: ";

variable $testCaseNameMsg := "'testCaseName' was set to: ";

switch ($mode)

case "list-test-sets"
return
{
  trace($fotsPath, $fotsPathMsg);
  trace($testSetPrefixes, $testSetPrefixesMsg);

  string-join
  (
  (
    d:list-test-sets($fotsPath, local:tokenize($testSetPrefixes))
  ),
  "
"
  )
}

case "list-test-cases"
return
{
  trace($fotsPath, $fotsPathMsg);
  trace($testSetPrefixes, $testSetPrefixesMsg);
  trace($dependency, "'dependency' set to:");
  trace($assertions, "'assertions' set to: ");

  string-join
  (
  (
    d:list-test-cases($fotsPath,
                      d:list-test-sets($fotsPath, local:tokenize($testSetPrefixes)),
                      $dependency,
                      $assertions)
  ),
  "
"
  )
}

case "list-matching-test-cases"
return
{
  trace($fotsPath, $fotsPathMsg);
  trace($pattern, "pattern set to: ");
  trace($flags, "flags were set to: ");

  string-join
  (
  (
    d:list-matching-test-cases($fotsPath, $pattern, $flags), ""
  ),
  "
"
  )
}

case "run-test-sets"
return
{
  trace($testSetPrefixes, $testSetPrefixesMsg);
  trace($testCasePrefixes, $testCasePrefixesMsg);
  trace($dependency, "'dependency' set to:");
  trace($assertions, "'assertions' set to: ");
  trace($verbose, "'verbose' set to:");
  trace($mode, "Cli command was set to:");

  d:run-fots($fotsPath,
             $fotsZorbaManifestPath,
             local:tokenize($testSetPrefixes),
             $exceptedTestSets,
             local:tokenize($testCasePrefixes),
             $exceptedTestCases,
             $dependency,
             $assertions,
             xs:boolean($verbose),
             $expectedFailuresPath,
             $mode)
}

case "run-test-set"
return
{
  trace($testSetName, $testSetNameMsg);
  trace($dependency, "'dependency' set to:");
  trace($assertions, "'assertions' set to: ");
  trace($verbose, "'verbose' set to:");
  trace($mode, "Cli command was set to:");

  d:run-test-set($fotsPath,
                 $fotsZorbaManifestPath,
                 $testSetName,
                 $exceptedTestSets,
                 (),
                 $exceptedTestCases,
                 $dependency,
                 $assertions,
                 xs:boolean($verbose),
                 $expectedFailuresPath,
                 $mode)
}

case "run-test-case"
return
{
  trace($testSetName, $testSetNameMsg);
  trace($testCaseName, $testCaseNameMsg);
  trace($verbose, "'verbose' set to:");
  trace($mode, "Cli command was set to:");

  d:run-fots($fotsPath,
             $fotsZorbaManifestPath,
             $testSetName,
             $exceptedTestSets,
             $testCaseName,
             $exceptedTestCases,
             "",
             (),
             xs:boolean($verbose),
             $expectedFailuresPath,
             $mode)
}

case "run-and-report"
return
{
  r:run-and-report($fotsPath,
                   $fotsZorbaManifestPath,
                   $exceptedTestCases,
                   $exceptedTestSets)
}

case "report"
return
{
  r:report($fotsZorbaManifestPath,
           $resultsFilePath)
}

case "wiki-report"
return
{
  r:wiki-report($fotsPath,
                $resultsFilePath)
}

case "generate-expected-failures"
return
{
  r:generate-expected-failures($resultsFilePath)
}

default
return
  local:usage()


[test] - testcase for new range index case="[yes/no]"

xquery version "3.0";

module namespace tt="http://exist-db.org/xquery/range/types/test";

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

declare variable $tt:COLLECTION_CONFIG :=
    <collection xmlns="http://exist-db.org/collection-config/1.0">
        <index xmlns:xs="http://www.w3.org/2001/XMLSchema"
            xmlns:tei="http://www.tei-c.org/ns/1.0">
            <fulltext default="none" attributes="false"/>
            <range>
                <create qname="date" type="xs:date"/>
                <create qname="time" type="xs:time"/>
                <create qname="dateTime" type="xs:dateTime"/>
                <create qname="entry">
                    <field name="date" match="date2" type="xs:date"/>
                </create>
                <create qname="string-lc" type="xs:string" case="no"/>
                <create qname="string" type="xs:string"/>
            </range>
        </index>
    </collection>;

declare variable $tt:XML :=
    <test>
        <entry>
            <id>E1</id>
            <date>1918-02-11</date>
            <date2>1918-02-11</date2>
            <time>09:00:00Z</time>
            <dateTime>1918-02-11T09:00:00Z</dateTime>
            <string-lc>UPPERCASE</string-lc>
            <string>UPPERCASE</string>
        </entry>
        <entry>
            <id>E2</id>
            <date>2012-01-20</date>
            <date2>2012-01-20</date2>
            <time>10:00:00Z</time>
            <dateTime>2012-01-20T10:00:00Z</dateTime>
            <string-lc>lowercase</string-lc>
            <string>lowercase</string>
        </entry>
        <entry>
            <id>E3</id>
            <date>2013-02-04</date>
            <date2>2013-02-04</date2>
            <time>10:00:00+01:00</time>
            <dateTime>2012-01-20T11:00:00+01:00</dateTime>
            <string-lc>MiXeDmOdE</string-lc>
            <string>MiXeDmOdE</string>
        </entry>
    </test>;

declare variable $tt:COLLECTION_NAME := "typestest";
declare variable $tt:COLLECTION := "/db/" || $tt:COLLECTION_NAME;

declare
    %test:setUp
function tt:setup() {
    xmldb:create-collection("/db/system/config/db", $tt:COLLECTION_NAME),
    xmldb:store("/db/system/config/db/" || $tt:COLLECTION_NAME, "collection.xconf", $tt:COLLECTION_CONFIG),
    xmldb:create-collection("/db", $tt:COLLECTION_NAME),
    xmldb:store($tt:COLLECTION, "test.xml", $tt:XML)
};

declare
    %test:tearDown
function tt:cleanup() {
    xmldb:remove($tt:COLLECTION),
    xmldb:remove("/db/system/config/db/" || $tt:COLLECTION_NAME)
};

declare
    %test:args("2012-01-20")
    %test:assertEquals("<id>E2</id>")
    %test:args("1918-02-11")
    %test:assertEquals("<id>E1</id>")
function tt:eq-date($date as xs:date) {
    collection($tt:COLLECTION)//entry[date = $date]/id
};

declare
    %test:stats
    %test:args("2012-01-20")
    %test:assertXPath("$result//stats:index[@type = 'new-range'][@optimization = 2]")
function tt:eq-date-optimize($date as xs:date) {
    collection($tt:COLLECTION)//entry[date = $date]
};

declare
    %test:args("2012-01-20")
    %test:assertEquals("<id>E2</id>")
    %test:args("1918-02-11")
    %test:assertEquals("<id>E1</id>")
function tt:eq-date-field($date as xs:date) {
    collection($tt:COLLECTION)//entry[date2 = $date]/id
};

declare
    %test:stats
    %test:args("2012-01-20")
    %test:assertXPath("$result//stats:index[@type = 'new-range'][@optimization = 2]")
function tt:eq-date-field-optimize($date as xs:date) {
    collection($tt:COLLECTION)//entry[date2 = $date]
};

declare
    %test:args("2012-01-20")
    %test:assertEquals("E3")
    %test:args("1900-01-01")
    %test:assertEquals("E1", "E2", "E3")
    %test:args("2013-12-24")
    %test:assertEmpty
function tt:gt-date($date as xs:date) {
    collection($tt:COLLECTION)//entry[date > $date]/id/string()
};

declare
    %test:stats
    %test:args("2012-01-20")
    %test:assertXPath("$result//stats:index[@type = 'new-range'][@optimization = 2]")
function tt:gt-date-optimize($date as xs:date) {
    collection($tt:COLLECTION)//entry[date > $date]/id/string()
};

declare
    %test:args("2012-01-20")
    %test:assertEquals("E3")
    %test:args("1900-01-01")
    %test:assertEquals("E1", "E2", "E3")
    %test:args("2013-12-24")
    %test:assertEmpty
function tt:gt-date-field($date as xs:date) {
    collection($tt:COLLECTION)//entry[date2 > $date]/id/string()
};

declare
    %test:stats
    %test:args("2012-01-20")
    %test:assertXPath("$result//stats:index[@type = 'new-range'][@optimization = 2]")
function tt:gt-date-field-optimize($date as xs:date) {
    collection($tt:COLLECTION)//entry[date2 > $date]/id/string()
};

declare
    %test:args("2012-01-20")
    %test:assertEquals("E1")
    %test:args("1900-01-01")
    %test:assertEmpty
    %test:args("2013-12-24")
    %test:assertEquals("E1", "E2", "E3")
function tt:lt-date($date as xs:date) {
    collection($tt:COLLECTION)//entry[date < $date]/id/string()
};

declare
    %test:stats
    %test:args("2012-01-20")
    %test:assertXPath("$result//stats:index[@type = 'new-range'][@optimization = 2]")
function tt:lt-date-optimize($date as xs:date) {
    collection($tt:COLLECTION)//entry[date < $date]/id/string()
};

declare
    %test:args("2012-01-20")
    %test:assertEquals("E1")
    %test:args("1900-01-01")
    %test:assertEmpty
    %test:args("2013-12-24")
    %test:assertEquals("E1", "E2", "E3")
function tt:lt-date-field($date as xs:date) {
    collection($tt:COLLECTION)//entry[date2 < $date]/id/string()
};

declare
    %test:stats
    %test:args("2012-01-20")
    %test:assertXPath("$result//stats:index[@type = 'new-range'][@optimization = 2]")
function tt:lt-date-field-optimize($date as xs:date) {
    collection($tt:COLLECTION)//entry[date2 < $date]/id/string()
};

declare
    %test:args("09:00:00Z")
    %test:assertEquals("E1", "E3")
    %test:args("10:00:00Z")
    %test:assertEquals("E2")
function tt:eq-time($time as xs:time) {
    collection($tt:COLLECTION)//entry[time = $time]/id/string()
};

declare
    %test:stats
    %test:args("10:00:00Z")
    %test:assertXPath("$result//stats:index[@type = 'new-range'][@optimization = 2]")
function tt:eq-time-optimize($time as xs:time) {
    collection($tt:COLLECTION)//entry[time = $time]/id/string()
};

declare
    %test:args("09:00:00Z")
    %test:assertEmpty
    %test:args("10:00:00Z")
    %test:assertEquals("E1", "E3")
function tt:lt-time($time as xs:time) {
    collection($tt:COLLECTION)//entry[time < $time]/id/string()
};

declare
    %test:args("09:00:00Z")
    %test:assertEquals("E2")
    %test:args("10:00:00Z")
    %test:assertEmpty
function tt:gt-time($time as xs:time) {
    collection($tt:COLLECTION)//entry[time > $time]/id/string()
};

declare
    %test:args("2012-01-20T10:00:00Z")
    %test:assertEquals("E2", "E3")
function tt:eq-dateTime($dateTime as xs:dateTime) {
    collection($tt:COLLECTION)//entry[dateTime = $dateTime]/id/string()
};

declare
    %test:stats
    %test:args("2012-01-20T10:00:00Z")
    %test:assertXPath("$result//stats:index[@type = 'new-range'][@optimization = 2]")
function tt:eq-dateTime-optimize($dateTime as xs:dateTime) {
    collection($tt:COLLECTION)//entry[dateTime = $dateTime]/id/string()
};

declare
    %test:args("1918-02-11T09:00:00Z")
    %test:assertEquals("E2", "E3")
    %test:args("2012-01-20T10:00:00Z")
    %test:assertEmpty
function tt:gt-dateTime($dateTime as xs:dateTime) {
    collection($tt:COLLECTION)//entry[dateTime > $dateTime]/id/string()
};

declare
    %test:args("1918-02-11T09:00:00Z")
    %test:assertEmpty
    %test:args("2012-01-20T10:00:00Z")
    %test:assertEquals("E1")
function tt:lt-dateTime($dateTime as xs:dateTime) {
    collection($tt:COLLECTION)//entry[dateTime < $dateTime]/id/string()
};

declare
    %test:stats
    %test:args("up")
    %test:assertXPath("$result//stats:index[@type = 'new-range'][@optimization = 2]")
function tt:string-contains-optimized($string as xs:string) {
    collection($tt:COLLECTION)//entry[contains(string, $string)]/id/string()
};

declare
    %test:args("up")
    %test:assertEquals("E1")
    %test:args("UP")
    %test:assertEquals("E1")
    %test:args("Up")
    %test:assertEquals("E1")
    %test:args("case")
    %test:assertEquals("E1", "E2")
    %test:args("mixed")
    %test:assertEquals("E3")
    %test:args("MIXED")
    %test:assertEquals("E3")
    %test:args("MiXeD")
    %test:assertEquals("E3")
function tt:string-contains-ignore-case($string as xs:string) {
    collection($tt:COLLECTION)//entry[contains(string-lc, $string)]/id/string()
};

declare
    %test:args("mixedmode")
    %test:assertEquals("E3")
    %test:args("MIXEDMODE")
    %test:assertEquals("E3")
    %test:args("MiXeDmOdE")
    %test:assertEquals("E3")
function tt:string-equals-ignore-case($string as xs:string) {
    collection($tt:COLLECTION)//entry[string-lc eq $string]/id/string()
};

declare
    %test:args("mixedmode")
    %test:assertEquals("E3")
    %test:args("MIXEDMODE")
    %test:assertEquals("E3")
    %test:args("MiXeDmOdE")
    %test:assertEquals("E3")
function tt:string-equals-2-ignore-case($string as xs:string) {
    collection($tt:COLLECTION)//entry[string-lc = $string]/id/string()
};

declare
    %test:args("up")
    %test:assertEquals("E1")
    %test:args("UP")
    %test:assertEquals("E1")
    %test:args("Up")
    %test:assertEquals("E1")
    %test:args("case")
    %test:assertEmpty
    %test:args("mixed")
    %test:assertEquals("E3")
    %test:args("MIXED")
    %test:assertEquals("E3")
    %test:args("MiXeD")
    %test:assertEquals("E3")
function tt:starts-with-ignore-case($string as xs:string) {
    collection($tt:COLLECTION)//entry[starts-with(string-lc, $string)]/id/string()
};

declare
    %test:args("percase")
    %test:assertEquals("E1")
    %test:args("PeRcAsE")
    %test:assertEquals("E1")
    %test:args("PERCASE")
    %test:assertEquals("E1")
    %test:args("cAsE")
    %test:assertEquals("E1", "E2")
function tt:ends-with-ignore-case($string as xs:string) {
    collection($tt:COLLECTION)//entry[ends-with(string-lc, $string)]/id/string()
};

declare
    %test:args("lo")
    %test:assertEquals("E2")
    %test:args("UP")
    %test:assertEquals("E1")
    %test:args("Up")
    %test:assertEmpty
    %test:args("case")
    %test:assertEquals("E2")
    %test:args("mixed")
    %test:assertEmpty
    %test:args("MIXED")
    %test:assertEmpty
    %test:args("MiXeD")
    %test:assertEquals("E3")
function tt:string-contains($string as xs:string) {
    collection($tt:COLLECTION)//entry[contains(string, $string)]/id/string()
};

declare
    %test:args("mixedmode")
    %test:assertEmpty
    %test:args("MIXEDMODE")
    %test:assertEmpty
    %test:args("MiXeDmOdE")
    %test:assertEquals("E3")
function tt:string-equals($string as xs:string) {
    collection($tt:COLLECTION)//entry[string eq $string]/id/string()
};

declare
    %test:args("mixedmode")
    %test:assertEmpty
    %test:args("MIXEDMODE")
    %test:assertEmpty
    %test:args("MiXeDmOdE")
    %test:assertEquals("E3")
function tt:string-equals-2($string as xs:string) {
    collection($tt:COLLECTION)//entry[string = $string]/id/string()
};

declare
    %test:args("up")
    %test:assertEmpty
    %test:args("UP")
    %test:assertEquals("E1")
    %test:args("Up")
    %test:assertEquals
    %test:args("case")
    %test:assertEmpty
    %test:args("mixed")
    %test:assertEmpty
    %test:args("MIXED")
    %test:assertEmpty
    %test:args("MiXeD")
    %test:assertEquals("E3")
function tt:starts-with($string as xs:string) {
    collection($tt:COLLECTION)//entry[starts-with(string, $string)]/id/string()
};

declare
    %test:args("percase")
    %test:assertEmpty
    %test:args("PeRcAsE")
    %test:assertEmpty
    %test:args("PERCASE")
    %test:assertEquals("E1")
    %test:args("cAsE")
    %test:assertEmpty
    %test:args("case")
    %test:assertEquals("E2")
function tt:ends-with($string as xs:string) {
    collection($tt:COLLECTION)//entry[ends-with(string, $string)]/id/string()
};

xquery version "3.0";

module namespace tt="http://exist-db.org/xquery/range/types/test";

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

declare variable $tt:COLLECTION_CONFIG :=
    <collection xmlns="http://exist-db.org/collection-config/1.0">
        <index xmlns:xs="http://www.w3.org/2001/XMLSchema"
            xmlns:tei="http://www.tei-c.org/ns/1.0">
            <fulltext default="none" attributes="false"/>
            <range>
                <create qname="date" type="xs:date"/>
                <create qname="time" type="xs:time"/>
                <create qname="dateTime" type="xs:dateTime"/>
                <create qname="entry">
                    <field name="date" match="date2" type="xs:date"/>
                </create>
            </range>
        </index>
    </collection>;

declare variable $tt:XML :=
    <test>
        <entry>
            <id>E1</id>
            <date>1918-02-11</date>
            <date2>1918-02-11</date2>
            <time>09:00:00Z</time>
            <dateTime>1918-02-11T09:00:00Z</dateTime>
        </entry>
        <entry>
            <id>E2</id>
            <date>2012-01-20</date>
            <date2>2012-01-20</date2>
            <time>10:00:00Z</time>
            <dateTime>2012-01-20T10:00:00Z</dateTime>
        </entry>
        <entry>
            <id>E3</id>
            <date>2013-02-04</date>
            <date2>2013-02-04</date2>
            <time>10:00:00+01:00</time>
            <dateTime>2012-01-20T11:00:00+01:00</dateTime>
        </entry>
    </test>;

declare variable $tt:COLLECTION_NAME := "typestest";
declare variable $tt:COLLECTION := "/db/" || $tt:COLLECTION_NAME;

declare
    %test:setUp
function tt:setup() {
    xmldb:create-collection("/db/system/config/db", $tt:COLLECTION_NAME),
    xmldb:store("/db/system/config/db/" || $tt:COLLECTION_NAME, "collection.xconf", $tt:COLLECTION_CONFIG),
    xmldb:create-collection("/db", $tt:COLLECTION_NAME),
    xmldb:store($tt:COLLECTION, "test.xml", $tt:XML)
};

declare
    %test:tearDown
function tt:cleanup() {
    xmldb:remove($tt:COLLECTION),
    xmldb:remove("/db/system/config/db/" || $tt:COLLECTION_NAME)
};

declare
    %test:args("2012-01-20")
    %test:assertEquals("<id>E2</id>")
    %test:args("1918-02-11")
    %test:assertEquals("<id>E1</id>")
function tt:eq-date($date as xs:date) {
    collection($tt:COLLECTION)//entry[date = $date]/id
};

declare
    %test:stats
    %test:args("2012-01-20")
    %test:assertXPath("$result//stats:index[@type = 'new-range'][@optimization = 2]")
function tt:eq-date-optimize($date as xs:date) {
    collection($tt:COLLECTION)//entry[date = $date]
};

declare
    %test:args("2012-01-20")
    %test:assertEquals("<id>E2</id>")
    %test:args("1918-02-11")
    %test:assertEquals("<id>E1</id>")
function tt:eq-date-field($date as xs:date) {
    collection($tt:COLLECTION)//entry[date2 = $date]/id
};

declare
    %test:stats
    %test:args("2012-01-20")
    %test:assertXPath("$result//stats:index[@type = 'new-range'][@optimization = 2]")
function tt:eq-date-field-optimize($date as xs:date) {
    collection($tt:COLLECTION)//entry[date2 = $date]
};

declare
    %test:args("2012-01-20")
    %test:assertEquals("E3")
    %test:args("1900-01-01")
    %test:assertEquals("E1", "E2", "E3")
    %test:args("2013-12-24")
    %test:assertEmpty
function tt:gt-date($date as xs:date) {
    collection($tt:COLLECTION)//entry[date > $date]/id/string()
};

declare
    %test:stats
    %test:args("2012-01-20")
    %test:assertXPath("$result//stats:index[@type = 'new-range'][@optimization = 2]")
function tt:gt-date-optimize($date as xs:date) {
    collection($tt:COLLECTION)//entry[date > $date]/id/string()
};

declare
    %test:args("2012-01-20")
    %test:assertEquals("E3")
    %test:args("1900-01-01")
    %test:assertEquals("E1", "E2", "E3")
    %test:args("2013-12-24")
    %test:assertEmpty
function tt:gt-date-field($date as xs:date) {
    collection($tt:COLLECTION)//entry[date2 > $date]/id/string()
};

declare
    %test:stats
    %test:args("2012-01-20")
    %test:assertXPath("$result//stats:index[@type = 'new-range'][@optimization = 2]")
function tt:gt-date-field-optimize($date as xs:date) {
    collection($tt:COLLECTION)//entry[date2 > $date]/id/string()
};

declare
    %test:args("2012-01-20")
    %test:assertEquals("E1")
    %test:args("1900-01-01")
    %test:assertEmpty
    %test:args("2013-12-24")
    %test:assertEquals("E1", "E2", "E3")
function tt:lt-date($date as xs:date) {
    collection($tt:COLLECTION)//entry[date < $date]/id/string()
};

declare
    %test:stats
    %test:args("2012-01-20")
    %test:assertXPath("$result//stats:index[@type = 'new-range'][@optimization = 2]")
function tt:lt-date-optimize($date as xs:date) {
    collection($tt:COLLECTION)//entry[date < $date]/id/string()
};

declare
    %test:args("2012-01-20")
    %test:assertEquals("E1")
    %test:args("1900-01-01")
    %test:assertEmpty
    %test:args("2013-12-24")
    %test:assertEquals("E1", "E2", "E3")
function tt:lt-date-field($date as xs:date) {
    collection($tt:COLLECTION)//entry[date2 < $date]/id/string()
};

declare
    %test:stats
    %test:args("2012-01-20")
    %test:assertXPath("$result//stats:index[@type = 'new-range'][@optimization = 2]")
function tt:lt-date-field-optimize($date as xs:date) {
    collection($tt:COLLECTION)//entry[date2 < $date]/id/string()
};

declare
    %test:args("09:00:00Z")
    %test:assertEquals("E1", "E3")
    %test:args("10:00:00Z")
    %test:assertEquals("E2")
function tt:eq-time($time as xs:time) {
    collection($tt:COLLECTION)//entry[time = $time]/id/string()
};

declare
    %test:stats
    %test:args("10:00:00Z")
    %test:assertXPath("$result//stats:index[@type = 'new-range'][@optimization = 2]")
function tt:eq-time-optimize($time as xs:time) {
    collection($tt:COLLECTION)//entry[time = $time]/id/string()
};

declare
    %test:args("09:00:00Z")
    %test:assertEmpty
    %test:args("10:00:00Z")
    %test:assertEquals("E1", "E3")
function tt:lt-time($time as xs:time) {
    collection($tt:COLLECTION)//entry[time < $time]/id/string()
};

declare
    %test:args("09:00:00Z")
    %test:assertEquals("E2")
    %test:args("10:00:00Z")
    %test:assertEmpty
function tt:gt-time($time as xs:time) {
    collection($tt:COLLECTION)//entry[time > $time]/id/string()
};

declare
    %test:args("2012-01-20T10:00:00Z")
    %test:assertEquals("E2", "E3")
function tt:eq-dateTime($dateTime as xs:dateTime) {
    collection($tt:COLLECTION)//entry[dateTime = $dateTime]/id/string()
};

declare
    %test:stats
    %test:args("2012-01-20T10:00:00Z")
    %test:assertXPath("$result//stats:index[@type = 'new-range'][@optimization = 2]")
function tt:eq-dateTime-optimize($dateTime as xs:dateTime) {
    collection($tt:COLLECTION)//entry[dateTime = $dateTime]/id/string()
};

declare
    %test:args("1918-02-11T09:00:00Z")
    %test:assertEquals("E2", "E3")
    %test:args("2012-01-20T10:00:00Z")
    %test:assertEmpty
function tt:gt-dateTime($dateTime as xs:dateTime) {
    collection($tt:COLLECTION)//entry[dateTime > $dateTime]/id/string()
};

declare
    %test:args("1918-02-11T09:00:00Z")
    %test:assertEmpty
    %test:args("2012-01-20T10:00:00Z")
    %test:assertEquals("E1")
function tt:lt-dateTime($dateTime as xs:dateTime) {
    collection($tt:COLLECTION)//entry[dateTime < $dateTime]/id/string()
};


removed debugging message

removed debugging message

(:~
: This is the Care Services Discovery stored query registry
: @version 1.1
: @see https://github.com/openhie/openinfoman
:
:)
module namespace fadpt = "https://github.com/openhie/openinfoman/adapter/fhir";

(:Import other namespaces.  Set default namespace  to os :)
import module namespace csd_webconf =  "https://github.com/openhie/openinfoman/csd_webconf";
import module namespace csr_proc = "https://github.com/openhie/openinfoman/csr_proc";
import module namespace csd_dm = "https://github.com/openhie/openinfoman/csd_dm";

declare namespace csd = "urn:ihe:iti:csd:2013";
declare namespace atom = "http://www.w3.org/2005/Atom";
declare namespace fhir = "http://hl7.org/fhir";

declare function fadpt:is_fhir_function($search_name) {
  let $function := csr_proc:get_function_definition($csd_webconf:db,$search_name)
  let $ext := $function//csd:extension[  @urn='urn:openhie.org:openinfoman:adapter' and @type='fhir']
  return (count($ext) > 0)
};

declare function fadpt:has_feed($search_name,$doc_name) {
  (fadpt:is_fhir_function($search_name) and csd_dm:is_registered($csd_webconf:db ,$doc_name))
};

declare function fadpt:get_base_url($search_name) {
  fadpt:get_base_url($search_name,$csd_webconf:baseurl)
};
declare function fadpt:get_base_url($search_name,$base_url) {
  concat($base_url,'CSD/adapter/fhir/' ,$search_name)
};

declare function fadpt:create_feed_from_entities($entities,$requestParams) {
  let $search_name := string($requestParams/@function)
  let $doc_name := string($requestParams/@resource)
  let $base_url := string($requestParams/@base_url)
  let $function := csr_proc:get_function_definition($csd_webconf:db,$search_name)
  let $entity := string(($function/csd:extension[ @urn='urn:openhie.org:openinfoman:adapter:fhir:read']/@type)[1])
  let $link := concat(fadpt:get_base_url($search_name,$base_url),'/' , $doc_name ,'/',$entity )
  let $title := concat("CSD entity as FHIR ",$entity)
  return
  <atom:feed>
    <atom:title>{$title}</atom:title>
    <atom:link href="{$link}" rel="self"/>
    <atom:updated>{current-dateTime()}</atom:updated>
     {
       for $entity in $entities
       let $ent_link := concat($link,"/", ($entity//fhir:identifier)[1])
       return
         <atom:entry>
	   <atom:link href="{$ent_link}"/>
	   <atom:content>{$entity}</atom:content>
	 </atom:entry>
     }
  </atom:feed>

};

(:
   Function to turn a CSD Facility entity into a FHIR Location
:)

declare function fadpt:represent_facility_as_location($doc,$facility)
{
  (: See http://www.hl7.org/implement/standards/fhir/location.html :)
  <fhir:Location >
    <fhir:identifier>{string($facility/@oid)}</fhir:identifier>
    <fhir:name>{($facility/csd:primaryName)[1]/text()}</fhir:name>
    {
      (
       (: Note: nothing readily apparent for description :)
       (:Note:  FHIR allows only one facility type:)
       for $type in  ($facility/csd:codedType)[1]
       return  <fhir:type><fhir:coding><fhir:system>{string($type/@codingScheme)}</fhir:system><fhir:code>{string($type/@code)}</fhir:code></fhir:coding></fhir:type>
       ,
       for $contact in  $facility/csd:contactPoint/csd:codedType
       return  <fhir:telecom>{$contact/text()}</fhir:telecom>
       ,
       (: Note: address is a bit weird.. which address? FHIR only allows for one. In CSD a provider
          can have a practice address as well as be assocaited to multiple facilities, each with their own address:)
       let $address :=  ($facility/csd:address[@type='Practice'])[1]
       return
	 if (exists($address))
	 then
	   <fhir:address>
	     {for $al in $address/csd:addressLine
	       return concat(string($al/@component) , ": ", $al/text(), "
")
	     }
           </fhir:address>
	 else ()
	,
        (: Note: nothing immediate for Physical type :)
	if (exists($facility/csd:geocode)) then
	  <fhir:position>
	    <fhir:longitude>{$facility/csd:geocode/csd:longitude}</fhir:longitude>
	    <fhir:latitude>{$facility/csd:geocode/csd:latitude}</fhir:latitude>
	    <fhir:altitude>{$facility/csd:geocode/csd:altitude}</fhir:altitude>
	  </fhir:position>
	else ()
	,
	(:  Note: FHIR only permits one managinh organization but CSD has many :)
	for $org in ($facility/csd:organizations/csd:organization)[1]
	   (: Note: base for URL for reference should maybe be handled by stored function extension metadata   :)
	   return <fhir:managingOrganization><fhir:reference>Organization/{string($org/@oid)}</fhir:reference></fhir:managingOrganization>
	,
	(:May need to map codes :)
        <fhir:status>{string($facility/csd:record/@status)}</fhir:status>
	(:Note nothing immediately obvious for FHIR partOf or for FHIR mode :)
     )
    }

  </fhir:Location>
};

(:
   Function to turn a CSD Provider entity into a FHIR Practitioner
:)

declare function fadpt:represent_provider_as_practitioner($doc,$provider)
{
  (: See http://www.hl7.org/implement/standards/fhir/practitioner.html :)
  <fhir:Practitioner >
    <fhir:identifier>{string($provider/@oid)}</fhir:identifier>
    {
	let $name := ($provider/csd:demographic/csd:name)[1]
	let $cn := ($name/csd:commonName)[1]/text()
	let $sn := ($name/csd:surname)[1]/text()
	let $gn := ($name/csd:forename)[1]/text()
	let $hon := ($name/csd:honorific)[1]/text()
	return
	  <fhir:name>
	    <fhir:text value="{$cn}"/>
	    <fhir:family value="{$sn}"/>
	    <fhir:given value="{$gn}"/>
	    <fhir:prefix value="{$hon}"/>
	  </fhir:name>
    }
    {
      (
       for $contact in  $provider/csd:demographic/csd:contactPoint/csd:codedType
       return  <fhir:telecom>{$contact/text()}</fhir:telecom>
       ,
       (: Note: address is a bit weird.. which address? FHIR only allows for one. In CSD a provider
          can have a practice address as well as be assocaited to multiple facilities, each with their own address:)
       let $address :=  ($provider/csd:demographic/csd:address[@type='Practice'])[1]
       return
	 if (exists($address))
	 then
	   <fhir:address>
	     {for $al in $address/csd:addressLine
	       return concat(string($al/@component) , ": ", $al/text(), "
")
	     }
           </fhir:address>
	 else ()
	,
	(: Note: what code set should we use in our output? :)
	let $gender := ($provider/csd:demographic/csd:gender)[1]
	return
	  if (exists($gender))  then  <fhir:gender>{$gender/text()}</fhir:gender>
	  else ()
	,
	let $dob := ($provider/csd:demographic/csd:dateOfBirth)[1]
	return
	  if (exists($dob))  then  <fhir:birthDate>{$dob/text()}</fhir:birthDate>
	  else ()
	,
	(:  Note: note supported under standard CSD   <photo><!-- 0..* Attachment Image of the person --></photo>  :)
	for $org in ($provider/csd:organizations/csd:organization)
	   (: Note: base for URL for reference should maybe be handled by stored function extension metadata   :)
	return <fhir:organization><fhir:reference>Organization/{string($org/@oid)}</fhir:reference></fhir:organization>
	,
	(: Note: perhaps this should be for services -- see remark below on <fhir:period/> :)
	for $role in ($provider/csd:codedType)
	return <fhir:role><fhir:coding><fhir:system>{string($role/@codingScheme)}</fhir:system><fhir:code>{string($role/@code)}</fhir:code></fhir:coding></fhir:role>
	,
	for $specialty in ($provider/csd:specialty)
	return <fhir:specialty><fhir:coding><fhir:system>{string($specialty/@codingScheme)}</fhir:system><fhir:code>{string($specialty/@code)}</fhir:code></fhir:coding></fhir:specialty>
	,
	(: Note: note supported <period><!-- 0..1 Period      The period during which the practitioner is authorized to perform in these role(s) § --></period>
           unless we interpret the <fhir:role/> element as a service
	:)
	for $fac in ($provider/csd:facilities/csd:facility)
	   (: Note: base for URL for reference should maybe be handled by stored function extension metadata   :)
	return <fhir:location><fhir:reference>Location/{string($fac/@oid)}</fhir:reference></fhir:location>
	,
	for $qual in ($provider/csd:credential)
	return
	  <fhir:qualification>
	    <fhir:code><fhir:coding><fhir:system>{string($qual/@codingScheme)}</fhir:system><fhir:code>{string($qual/@code)}</fhir:code></fhir:coding></fhir:code>
	    <fhir:period>
	       {if (exists($qual/csd:credenitalIssueDate)) then   <fhir:start>{$qual/csd:credenitalIssueDate}</fhir:start> else ()}
	       {if (exists($qual/csd:credentialRenewalDate)) then  <fhir:end>{$qual/csd:credentialRenewalDate}</fhir:end> else ()}
	    </fhir:period>
	    {
	      (:Note: I don't think this is quite correct.  It wants it to be an organization ID :)
	      if (exists($qual/csd:issuingAuthority)) then <fhir:issuer>{$qual/csd:issuingAuthority}</fhir:issuer> else ()
	    }
	  </fhir:qualification>
       ,
       for $lang in $provider/csd:language
       return  <fhir:communication><fhir:coding><fhir:system>{string($lang/@codingScheme)}</fhir:system><fhir:code>{string($lang/@code)}</fhir:code></fhir:coding></fhir:communication>
     )
    }

  </fhir:Practitioner>
};

(:~
: This is the Care Services Discovery stored query registry
: @version 1.1
: @see https://github.com/openhie/openinfoman
:
:)
module namespace fadpt = "https://github.com/openhie/openinfoman/adapter/fhir";

(:Import other namespaces.  Set default namespace  to os :)
import module namespace csd_webconf =  "https://github.com/openhie/openinfoman/csd_webconf";
import module namespace csr_proc = "https://github.com/openhie/openinfoman/csr_proc";
import module namespace csd_dm = "https://github.com/openhie/openinfoman/csd_dm";

declare namespace csd = "urn:ihe:iti:csd:2013";
declare namespace atom = "http://www.w3.org/2005/Atom";
declare namespace fhir = "http://hl7.org/fhir";

declare function fadpt:is_fhir_function($search_name) {
  let $function := csr_proc:get_function_definition($csd_webconf:db,$search_name)
  let $ext := $function//csd:extension[  @urn='urn:openhie.org:openinfoman:adapter' and @type='fhir']
  return (count($ext) > 0)
};

declare function fadpt:has_feed($search_name,$doc_name) {
  (fadpt:is_fhir_function($search_name) and csd_dm:is_registered($csd_webconf:db ,$doc_name))
};

declare function fadpt:get_base_url($search_name) {
  fadpt:get_base_url($search_name,$csd_webconf:baseurl)
};
declare function fadpt:get_base_url($search_name,$base_url) {
  concat($base_url,'CSD/adapter/fhir/' ,$search_name)
};

declare function fadpt:create_feed_from_entities($entities,$requestParams) {
  let $search_name := string($requestParams/@function)
  let $doc_name := string($requestParams/@resource)
  let $base_url := string($requestParams/@base_url)
  let $function := csr_proc:get_function_definition($csd_webconf:db,$search_name)
  let $entity := string(($function/csd:extension[ @urn='urn:openhie.org:openinfoman:adapter:fhir:read']/@type)[1])
  let $link := concat(fadpt:get_base_url($search_name,$base_url),'/' , $doc_name ,'/',$entity )
  let $title := concat("CSD entity as FHIR ",$entity)
  return
  <atom:feed>
   {$requestParams}
    <atom:title>{$title}</atom:title>
    <atom:link href="{$link}" rel="self"/>
    <atom:updated>{current-dateTime()}</atom:updated>
     {
       for $entity in $entities
       let $ent_link := concat($link,"/", ($entity//fhir:identifier)[1])
       return
         <atom:entry>
	   <atom:link href="{$ent_link}"/>
	   <atom:content>{$entity}</atom:content>
	 </atom:entry>
     }
  </atom:feed>

};

(:
   Function to turn a CSD Facility entity into a FHIR Location
:)

declare function fadpt:represent_facility_as_location($doc,$facility)
{
  (: See http://www.hl7.org/implement/standards/fhir/location.html :)
  <fhir:Location >
    <fhir:identifier>{string($facility/@oid)}</fhir:identifier>
    <fhir:name>{($facility/csd:primaryName)[1]/text()}</fhir:name>
    {
      (
       (: Note: nothing readily apparent for description :)
       (:Note:  FHIR allows only one facility type:)
       for $type in  ($facility/csd:codedType)[1]
       return  <fhir:type><fhir:coding><fhir:system>{string($type/@codingScheme)}</fhir:system><fhir:code>{string($type/@code)}</fhir:code></fhir:coding></fhir:type>
       ,
       for $contact in  $facility/csd:contactPoint/csd:codedType
       return  <fhir:telecom>{$contact/text()}</fhir:telecom>
       ,
       (: Note: address is a bit weird.. which address? FHIR only allows for one. In CSD a provider
          can have a practice address as well as be assocaited to multiple facilities, each with their own address:)
       let $address :=  ($facility/csd:address[@type='Practice'])[1]
       return
	 if (exists($address))
	 then
	   <fhir:address>
	     {for $al in $address/csd:addressLine
	       return concat(string($al/@component) , ": ", $al/text(), "
")
	     }
           </fhir:address>
	 else ()
	,
        (: Note: nothing immediate for Physical type :)
	if (exists($facility/csd:geocode)) then
	  <fhir:position>
	    <fhir:longitude>{$facility/csd:geocode/csd:longitude}</fhir:longitude>
	    <fhir:latitude>{$facility/csd:geocode/csd:latitude}</fhir:latitude>
	    <fhir:altitude>{$facility/csd:geocode/csd:altitude}</fhir:altitude>
	  </fhir:position>
	else ()
	,
	(:  Note: FHIR only permits one managinh organization but CSD has many :)
	for $org in ($facility/csd:organizations/csd:organization)[1]
	   (: Note: base for URL for reference should maybe be handled by stored function extension metadata   :)
	   return <fhir:managingOrganization><fhir:reference>Organization/{string($org/@oid)}</fhir:reference></fhir:managingOrganization>
	,
	(:May need to map codes :)
        <fhir:status>{string($facility/csd:record/@status)}</fhir:status>
	(:Note nothing immediately obvious for FHIR partOf or for FHIR mode :)
     )
    }

  </fhir:Location>
};

(:
   Function to turn a CSD Provider entity into a FHIR Practitioner
:)

declare function fadpt:represent_provider_as_practitioner($doc,$provider)
{
  (: See http://www.hl7.org/implement/standards/fhir/practitioner.html :)
  <fhir:Practitioner >
    <fhir:identifier>{string($provider/@oid)}</fhir:identifier>
    {
	let $name := ($provider/csd:demographic/csd:name)[1]
	let $cn := ($name/csd:commonName)[1]/text()
	let $sn := ($name/csd:surname)[1]/text()
	let $gn := ($name/csd:forename)[1]/text()
	let $hon := ($name/csd:honorific)[1]/text()
	return
	  <fhir:name>
	    <fhir:text value="{$cn}"/>
	    <fhir:family value="{$sn}"/>
	    <fhir:given value="{$gn}"/>
	    <fhir:prefix value="{$hon}"/>
	  </fhir:name>
    }
    {
      (
       for $contact in  $provider/csd:demographic/csd:contactPoint/csd:codedType
       return  <fhir:telecom>{$contact/text()}</fhir:telecom>
       ,
       (: Note: address is a bit weird.. which address? FHIR only allows for one. In CSD a provider
          can have a practice address as well as be assocaited to multiple facilities, each with their own address:)
       let $address :=  ($provider/csd:demographic/csd:address[@type='Practice'])[1]
       return
	 if (exists($address))
	 then
	   <fhir:address>
	     {for $al in $address/csd:addressLine
	       return concat(string($al/@component) , ": ", $al/text(), "
")
	     }
           </fhir:address>
	 else ()
	,
	(: Note: what code set should we use in our output? :)
	let $gender := ($provider/csd:demographic/csd:gender)[1]
	return
	  if (exists($gender))  then  <fhir:gender>{$gender/text()}</fhir:gender>
	  else ()
	,
	let $dob := ($provider/csd:demographic/csd:dateOfBirth)[1]
	return
	  if (exists($dob))  then  <fhir:birthDate>{$dob/text()}</fhir:birthDate>
	  else ()
	,
	(:  Note: note supported under standard CSD   <photo><!-- 0..* Attachment Image of the person --></photo>  :)
	for $org in ($provider/csd:organizations/csd:organization)
	   (: Note: base for URL for reference should maybe be handled by stored function extension metadata   :)
	return <fhir:organization><fhir:reference>Organization/{string($org/@oid)}</fhir:reference></fhir:organization>
	,
	(: Note: perhaps this should be for services -- see remark below on <fhir:period/> :)
	for $role in ($provider/csd:codedType)
	return <fhir:role><fhir:coding><fhir:system>{string($role/@codingScheme)}</fhir:system><fhir:code>{string($role/@code)}</fhir:code></fhir:coding></fhir:role>
	,
	for $specialty in ($provider/csd:specialty)
	return <fhir:specialty><fhir:coding><fhir:system>{string($specialty/@codingScheme)}</fhir:system><fhir:code>{string($specialty/@code)}</fhir:code></fhir:coding></fhir:specialty>
	,
	(: Note: note supported <period><!-- 0..1 Period      The period during which the practitioner is authorized to perform in these role(s) § --></period>
           unless we interpret the <fhir:role/> element as a service
	:)
	for $fac in ($provider/csd:facilities/csd:facility)
	   (: Note: base for URL for reference should maybe be handled by stored function extension metadata   :)
	return <fhir:location><fhir:reference>Location/{string($fac/@oid)}</fhir:reference></fhir:location>
	,
	for $qual in ($provider/csd:credential)
	return
	  <fhir:qualification>
	    <fhir:code><fhir:coding><fhir:system>{string($qual/@codingScheme)}</fhir:system><fhir:code>{string($qual/@code)}</fhir:code></fhir:coding></fhir:code>
	    <fhir:period>
	       {if (exists($qual/csd:credenitalIssueDate)) then   <fhir:start>{$qual/csd:credenitalIssueDate}</fhir:start> else ()}
	       {if (exists($qual/csd:credentialRenewalDate)) then  <fhir:end>{$qual/csd:credentialRenewalDate}</fhir:end> else ()}
	    </fhir:period>
	    {
	      (:Note: I don't think this is quite correct.  It wants it to be an organization ID :)
	      if (exists($qual/csd:issuingAuthority)) then <fhir:issuer>{$qual/csd:issuingAuthority}</fhir:issuer> else ()
	    }
	  </fhir:qualification>
       ,
       for $lang in $provider/csd:language
       return  <fhir:communication><fhir:coding><fhir:system>{string($lang/@codingScheme)}</fhir:system><fhir:code>{string($lang/@code)}</fhir:code></fhir:coding></fhir:communication>
     )
    }

  </fhir:Practitioner>
};


[MOD] restxq modified (thx arve)

[MOD] restxq modified (thx arve)

(:~
 : This module contains some basic examples for RESTXQ annotations
 : @author BaseX Team
 :)
module namespace page = 'http://basex.org/modules/web-page';

declare namespace rest = 'http://exquery.org/ns/restxq';

declare variable $page:style :=
  ;

declare %rest:path("")
        %output:method("xhtml")
        %output:omit-xml-declaration("no")
        %output:doctype-public("-//W3C//DTD XHTML 1.0 Transitional//EN")
        %output:doctype-system("http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd")
  function page:start() {

  let $title := 'Welcome to RESTXQ!' return

      <title>{ $title }</title>
      { $page:style }

{ $title }

RESTXQ is a new API
      that facilitates the use of XQuery as a Server Side processing language
      for the Web.

      This page has been generated by a RESTXQ module located in the web
      server's root directory.

The following links return a page with dynamic contents:

http://localhost:8984/restxq/hello/World

http://localhost:8984/restxq/hello/Universe

The following form demonstrates the form parameters:

      <form method="post" action="/restxq/form">

Your message:

        <input name="content" size="50"></input>
        <input type="submit" />

      </form>

The source of this file is shown below:

{ unparsed-text(static-base-uri()) }

...back to main page

};

declare %rest:path("hello/{$world}")
        %rest:GET
        %rest:header-param("User-Agent", "{$agent}")
        function page:hello($world as xs:string, $agent as xs:string*) {
  <response>
    <title>Hello { $world }!</title>
    <info>You requested this page with { $agent }.</info>
  </response>
};

declare %rest:path("form/")
        %rest:POST
        %rest:form-param("content","{$message}", "'no message delivered'")
        function page:hello-postman($message as xs:string) {
  <response>
    <title>Hello!</title>
    <info>It seems you posted a message: { $message }</info>
  </response>
};

(:~
 : This module contains some basic examples for RESTXQ annotations
 : @author BaseX Team
 :)
module namespace page = 'http://basex.org/modules/web-page';

declare namespace rest = 'http://exquery.org/ns/restxq';

declare variable $page:style :=
  ;

declare %rest:path("hello/{$world}")
        %rest:header-param("User-Agent", "{$agent}")
        function page:hello($world as xs:string, $agent as xs:string*) {
  <response>
    <title>Hello { $world }!</title>
    <info>You requested this page with { $agent }.</info>
  </response>
};

declare %rest:path("")
        %output:method("xhtml")
        %output:doctype-public("-//W3C//DTD HTML 4.01//EN")
        %output:doctype-system("http://www.w3.org/TR/html4/strict.dtd")
  function page:start() {

  let $title := 'Welcome to RESTXQ!' return

      <title>{ $title }</title>
      { $page:style }

{ $title }

RESTXQ is a new API
      that facilitates the use of XQuery as a Server Side processing language
      for the Web.

      This page has been generated by a RESTXQ module located in the web
      server's root directory.

The following links return a page with dynamic contents:

http://localhost:8984/restxq/hello/World

http://localhost:8984/restxq/hello/Universe

The source of this file is shown below:

{ unparsed-text(static-base-uri()) }

...back to main page

};


Added error documentation in jn module.

Added error documentation in jn module.

xquery version "1.0";

(:
 : Copyright 2006-2012 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.
:)

(:~
 : This module provides the functions defined by the JSONiq specification,
 : sections 1.7 (Functions) and 1.10 (Update Primitives). JSONiq extends
 : the XQuery specification to also deal with JSON data natively. See
 :
 :     http://www.jsoniq.org/
 :
 : for details.
 :
 : This module depends on having the JSONiq feature enabled in Zorba,
 : i.e., Zorba must be compiled with ZORBA_WITH_JSON.
 :
 : @author Markos Zaharioudakis, Matthias Brantner, Ghislain Fourny
 :)
module namespace jn = "http://www.jsoniq.org/functions";

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

declare namespace jdm = "http://www.jsoniq.org/";

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

declare namespace ver = "http://www.zorba-xquery.com/options/versioning";
declare option ver:module-version "1.0";

(:~
 : This function has the same semantics as fn:parse-xml(), except that
 : it parses the string as JSON (not XML), and returns an Object or
 : Array rather than an XML document.
 :
 : @param $j A string containing a valid JSON text.
 : @return a JSON Object or Array item.
 :)
declare function jn:parse-json($j as xs:string) as json-item()? external;

(:~
 : Returns the names used in the object.
 : The names will be returned in an implementation-defined order
 :
 : @param $o A JSON Object.
 : @return The names of pairs in the object.
 :)
declare function jn:keys($o as object()) as xs:string* external;

(:~
 : Returns the value of a JSON Pair with a given name within a given JSON object.
 : If no such pair exists in the object, returns the empty sequence.
 :
 : @param $o A JSON Object.
 : @param $name The name of the pair whose value is to be retrieved
 : @return the value of specified pair within the given object, or the empty sequence.
 :)
(: obsolete - use $o($name) instead :)
declare function jn:value($o as object(), $name as xs:string) as item()? external;

(:~
 : Returns the size of a JSON Array. The size of an Array is
 : the number of members contained within it.
 :
 : @param $j A JSON Array.
 : @return The number of items in $j.
 :)
declare function jn:size($j as array()) as xs:integer external;

(:~
 : Returns the member of an Array at the specified position (starting from 1).
 : If the position is out of bounds of the array, returns the empty sequence.
 :
 : @param $a A JSON Array.
 : @param $p The position in the array.
 : @return The member at the specified position, or empty sequence.
 :)
(: obsolete - use $a($p) instead :)
declare function jn:member($a as array(), $p as xs:integer) as item()? external;

(:~
 : Creates an object from the specified pairs of another given object.
 : Specifically, for each name in $names, if the object $o has a pair with
 : that name, then a copy of that pair is included in the new object.
 :
 : @param $o A JSON Object.
 : @param $names The names of the pairs to copy out of $o and insert into the new object
 : @return The new object.
 :)
declare function jn:project($o as object(), $names as xs:string*) as object() external;

(:~
 : Returns the members of an Array.
 :
 : @param $a A JSON Array.
 : @return The members of the specified array.
 :)
declare function jn:members($o as array()) as item()* external;

(:~
 : Recursively "flatten" a JSON Array, by replacing any arrays with their
 : members. Equivalent to
 :
 :   define function jn:flatten($arg as array()) {
 :     for $value in jn:values($arg)
 :     return
 :       if ($value instance of array())
 :       then jn:flatten($value)
 :       else $value
 :   };
 :
 : @param $a A JSON Array.
 : @return The flattened version of $a.
 :)
declare function jn:flatten($a as array()) as item()* external;

(:~
 : This function allows dynamic object construction by merging all
 : its object parameters into a single object with a so-called "simple
 : object union". A simple object union creates a new object, the pairs
 : property of which is obtained by accumulating the pairs of all operand
 : objects. An error jerr:JNDY0003 is raised if two pairs with the same
 : name are encountered.
 :
 : @param $o A sequence of objects.
 :
 : @return The simple object union.
 :
 : @error jerr:JNDY0003 if there is a pair collision.
 :)
declare function jn:object($o as object()*) as object() external;

xquery version "1.0";

(:
 : Copyright 2006-2012 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.
:)

(:~
 : This module provides the functions defined by the JSONiq specification,
 : sections 1.7 (Functions) and 1.10 (Update Primitives). JSONiq extends
 : the XQuery specification to also deal with JSON data natively. See
 :
 :     http://www.jsoniq.org/
 :
 : for details.
 :
 : This module depends on having the JSONiq feature enabled in Zorba,
 : i.e., Zorba must be compiled with ZORBA_WITH_JSON.
 :
 : @author Markos Zaharioudakis, Matthias Brantner, Ghislain Fourny
 :)
module namespace jn = "http://www.jsoniq.org/functions";

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

declare namespace jdm = "http://www.jsoniq.org/";

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

declare namespace ver = "http://www.zorba-xquery.com/options/versioning";
declare option ver:module-version "1.0";

(:~
 : This function has the same semantics as fn:parse-xml(), except that
 : it parses the string as JSON (not XML), and returns an Object or
 : Array rather than an XML document.
 :
 : @param $j A string containing a valid JSON text.
 : @return a JSON Object or Array item.
 :)
declare function jn:parse-json($j as xs:string) as json-item()? external;

(:~
 : Returns the names used in the object.
 : The names will be returned in an implementation-defined order
 :
 : @param $o A JSON Object.
 : @return The names of pairs in the object.
 :)
declare function jn:keys($o as object()) as xs:string* external;

(:~
 : Returns the value of a JSON Pair with a given name within a given JSON object.
 : If no such pair exists in the object, returns the empty sequence.
 :
 : @param $o A JSON Object.
 : @param $name The name of the pair whose value is to be retrieved
 : @return the value of specified pair within the given object, or the empty sequence.
 :)
(: obsolete - use $o($name) instead :)
declare function jn:value($o as object(), $name as xs:string) as item()? external;

(:~
 : Returns the size of a JSON Array. The size of an Array is
 : the number of members contained within it.
 :
 : @param $j A JSON Array.
 : @return The number of items in $j.
 :)
declare function jn:size($j as array()) as xs:integer external;

(:~
 : Returns the member of an Array at the specified position (starting from 1).
 : If the position is out of bounds of the array, returns the empty sequence.
 :
 : @param $a A JSON Array.
 : @param $p The position in the array.
 : @return The member at the specified position, or empty sequence.
 :)
(: obsolete - use $a($p) instead :)
declare function jn:member($a as array(), $p as xs:integer) as item()? external;

(:~
 : Creates an object from the specified pairs of another given object.
 : Specifically, for each name in $names, if the object $o has a pair with
 : that name, then a copy of that pair is included in the new object.
 :
 : @param $o A JSON Object.
 : @param $names The names of the pairs to copy out of $o and insert into the new object
 : @return The new object.
 :)
declare function jn:project($o as object(), $names as xs:string*) as object() external;

(:~
 : Returns the members of an Array.
 :
 : @param $a A JSON Array.
 : @return The members of the specified array.
 :)
declare function jn:members($o as array()) as item()* external;

(:~
 : Recursively "flatten" a JSON Array, by replacing any arrays with their
 : members. Equivalent to
 :
 :   define function jn:flatten($arg as array()) {
 :     for $value in jn:values($arg)
 :     return
 :       if ($value instance of array())
 :       then jn:flatten($value)
 :       else $value
 :   };
 :
 : @param $a A JSON Array.
 : @return The flattened version of $a.
 :)
declare function jn:flatten($a as array()) as item()* external;

(:~
 : This function allows dynamic object construction by merging all
 : its object parameters into a single object with a so-called "simple
 : object union". A simple object union creates a new object, the pairs
 : property of which is obtained by accumulating the pairs of all operand
 : objects. An error jerr:JNDY0003 is raised if two pairs with the same
 : name are encountered.
 :
 : @param $o A sequence of objects.
 : @return The simple object union.
 :)
declare function jn:object($o as object()*) as object() external;