[test] Further tests

[test] Further tests

(:
 : eXist-db Open Source Native XML Database
 : Copyright (C) 2001 The eXist-db Authors
 :
 : info@exist-db.org
 : http://www.exist-db.org
 :
 : This library is free software; you can redistribute it and/or
 : modify it under the terms of the GNU Lesser General Public
 : License as published by the Free Software Foundation; either
 : version 2.1 of the License, or (at your option) any later version.
 :
 : This library is distributed in the hope that it will be useful,
 : but WITHOUT ANY WARRANTY; without even the implied warranty of
 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 : Lesser General Public License for more details.
 :
 : You should have received a copy of the GNU Lesser General Public
 : License along with this library; if not, write to the Free Software
 : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 :)
xquery version "3.1";

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

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

declare %private function mt:getMapFixture () {
    map {
        "Su" : "Sunday",
        "Mo" : "Monday",
        "Tu" : "Tuesday",
        "We" : "Wednesday",
        "Th" : "Thursday",
        "Fr" : "Friday",
        "Sa" : "Saturday"
    }
};

declare variable $mt:daysOfWeek := mt:getMapFixture();

declare variable $mt:integerKeys := map {
    1 : "Sunday",
    2 : "Monday",
    3 : "Tuesday",
    4 : "Wednesday",
    5 : "Thursday",
    6 : "Friday",
    7 : "Saturday"
};

declare variable $mt:mapOfSequences := map {0: (), 1 : ("One", "Two") };

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

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

declare
    %test:assertError("err:XPST0003")
function mt:draftSyntaxNotAllowed () {
    (: this needs to be evaled, to catch the static compilation error :)
    util:eval('map { 1 := 1 }')
};

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

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

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

declare
    %test:assertXPath("count($result) = 8")
    %test:assertXPath("8 = $result")
function mt:createFromTwoMaps() {
    let $map := map:merge(($mt:integerKeys, map { 8 : "Caturday" }))
    return
        map:keys($map)
};

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

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

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

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

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

declare
    %test:assertEquals("Sunday", "Tuesday", "Thursday", "Saturday")
function mt:for-each() {
    map:for-each($mt:integerKeys, function($key, $value) {
        if ($key mod 2) then ($value) else ()
    })
};

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

    return
        $nm?b
};

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

(:~
 : TODO(AR) implicit behaviour of map:merge according to XQ3.1 specification should be use-first not use-last
:)
declare
    %test:assertEquals("Saturday", "Caturday")
function mt:merge-duplicate-keys-use-last-implicit-1() {
    let $specialWeek := map:merge(($mt:integerKeys, map { 7 : "Caturday" }))
    return
        ($mt:integerKeys(7), $specialWeek(7))
};

(:~
 : TODO(AR) implicit behaviour of map:merge according to XQ3.1 specification should be use-first not use-last
:)
declare
    %test:assertEquals("Saturday", "Saturday")
function mt:merge-duplicate-keys-use-last-implicit-2() {
    let $specialWeek := map:merge((map { 7 : "Caturday" }, $mt:integerKeys))
    return
        ($mt:integerKeys(7), $specialWeek(7))
};

declare
    %test:assertEmpty
function mt:mapEmptyValue() {
    let $map := $mt:mapOfSequences
    return
        $map(0)
};

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

declare
    %test:assertEquals("Heinz", "Roland", "Uschi", "Verona")
function mt:orderBzKeyTypeDate () {
    let $map := map {
        xs:date("1975-03-19"): "Uschi",
        xs:date("1980-01-22"): "Verona",
        xs:date("1960-06-14"): "Heinz",
        xs:date("1963-10-21"): "Roland"
    }

    for $key in map:keys($map)
    let $name := $map($key)
    order by $name ascending
    return
        $name
};

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

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

declare
    %test:assertEquals("Fr", "Mo", "Sa", "Su", "Th", "Tu", "We")
function mt:keysInFor() {
    for $day in map:keys(mt:getMapFixture())
    order by $day ascending
    return
        $day
};

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

declare
    %test:assertFalse
function mt:remove() {
    let $map := mt:getMapFixture()
    return
        map:contains(map:remove($map, "Tu"), "Tu")
};

declare
    %test:assertEquals("Fr", "Mo", "Sa", "Su", "Th")
function mt:remove2() {
    let $map := mt:getMapFixture()
    for $day in map:keys(map:remove(map:remove($map, "We"), "Tu"))
    order by $day
    return
        $day
};

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

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

declare
    %test:assertEquals("Sa")
function mt:removeSequenceOfStringKeys() {
    mt:getMapFixture()
        => map:remove(("Mo", "Tu", "We", "Th", "Fr", "Su"))
        => map:keys()
};

declare
    %test:assertEquals("Sa")
function mt:removeSequenceOfStringKeysWithNonExistent() {
    mt:getMapFixture()
        => map:remove(("Mo", "Tu", "We", "Th", "Fr", "Su", "Xx"))
        => map:keys()
};

declare
    %test:assertEquals(7)
function mt:removeRangeOfIntegerKeys() {
    $mt:integerKeys
        => map:remove(1 to 6)
        => map:keys()
};

declare
    %test:assertEquals(7)
function mt:removeIntegerKeysWithNonExistent() {
    $mt:integerKeys
        => map:remove((1, 2, 3, 4, 5, 6, 10))
        => map:keys()
};

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

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

declare
    %test:assertTrue
function mt:containsSingleKey() {
    map:contains(map { "Su": "Sunday" }, "Su")
};

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

declare
    %test:assertEquals("false", "true")
function mt:immutabilityMerge() {
    let $original := mt:getMapFixture()
    let $derived := map:merge(($original, map:entry("Ca", "Caturday")))
    return (
        map:contains($original, "Ca"),
        map:contains($derived, "Ca")
    )
};

declare
    %test:assertEquals("true", "false")
function mt:immutabilityRemove() {
    let $original := mt:getMapFixture()
    let $derived := map:remove($original, "Fr")
    return (
        map:contains($original, "Fr"),
        map:contains($derived, "Fr")
    )
};

declare
    %test:assertEquals("false", "true")
function mt:immutabilityPut() {
    let $original := mt:getMapFixture()
    let $derived := map:put($original, "Ca", "Caturday")
    return (
        map:contains($original, "Ca"),
        map:contains($derived, "Ca")
    )
};

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

declare
    %test:assertEquals(1)
function mt:lookupParenthesized() {
    let $f := function () { "one" }
    let $map := map { "one": 1 }
    return
        $map($f())
};

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

declare
    %test:assertTrue
function mt:lookupWildcard() {
    let $expected := map:keys($mt:daysOfWeek) ! $mt:daysOfWeek(.)
    let $actual := $mt:daysOfWeek?*
    return
        count($actual) eq count($expected)
        and
        (every $day in $actual satisfies $day = $expected)
};

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

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

declare
    %test:name("for-each on a map with a single entry caused NPE")
    %test:assertEquals("k")
function mt:single-entry-map() {
    let $map := map:entry("k", ())
    return
        map:for-each($map, function($k, $v) { $k })
};

declare
    %test:assertEquals(5)
function mt:qname() {
    let $a := 1
    let $m := map { $a: fn:string-length("hello") }
    return
        $m?1
};

declare
    %test:assertEmpty
function mt:no-such-entry() {
    map:get(map {"foo": "bar"}, "baz")
};

declare
    %test:assertEqualsPermutation("uk", "nl", "se", "de")
function mt:multi-merge() {
    map:merge((
  	    map { "adam": "uk" },
  	    map { "dannes": "nl" },
  	    map { "leif": "se" },
  	    map { "wolfgang": "de" }
  	))?*
};

(:
  immutability tests for https://github.com/eXist-db/exist/issues/3724
:)
declare variable $mt:test-key-one := 1;
declare variable $mt:test-key-two := 2;
declare variable $mt:test-key-three := 3;
declare function mt:create-test-map() {
    map {
        $mt:test-key-one : true(),
        $mt:test-key-two : true()
    }
};

declare function mt:create-test-map2() {
    map {
        $mt:test-key-three : true()
    }
};

declare
    %test:assertEquals("true", "true")
function mt:immutable-put-then-put() {
    let $extended := map:put(mt:create-test-map(), $mt:test-key-two, false())
    let $expected := $extended($mt:test-key-one)
    let $result := map:put($extended, $mt:test-key-one, false())
    return
        (
            $expected eq $extended($mt:test-key-one),
            $expected ne $result($mt:test-key-one)
        )
};

declare
    %test:assertEquals("true", "true")
function mt:immutable-put-then-remove() {
    let $extended := map:put(mt:create-test-map(), $mt:test-key-two, false())
    let $expected := $extended($mt:test-key-one)
    let $result := map:remove($extended, $mt:test-key-one)
    return
        (
            $expected eq $extended($mt:test-key-one),
            empty($result($mt:test-key-one))
        )
};

declare
    %test:assertEquals("true", "true")
function mt:immutable-put-then-merge() {
    let $extended := map:put(mt:create-test-map(), $mt:test-key-two, false())
    let $expected := $extended($mt:test-key-one)
    let $result := map:merge(($extended, map { $mt:test-key-one : false() }))
    return
        (
            $expected eq $extended($mt:test-key-one),
            $expected ne $result($mt:test-key-one)
        )
};

declare
    %test:assertEquals("true", "true")
function mt:immutable-remove-then-put() {
    let $removed := map:remove(mt:create-test-map(), $mt:test-key-two)
    let $expected := $removed($mt:test-key-one)
    let $result := map:put($removed, $mt:test-key-one, false())
    return
        (
            $expected eq $removed($mt:test-key-one),
            $expected ne $result($mt:test-key-one)
        )
};

declare
    %test:assertEquals("true", 1, "true", "true", 0)
function mt:immutable-remove-then-remove() {
    let $removed := map:remove(mt:create-test-map(), $mt:test-key-two)
    let $expected := $removed($mt:test-key-one)
    let $result := map:remove($removed, $mt:test-key-one)
    return
        (
            fn:empty($removed($mt:test-key-two)),
            map:size($removed),
            $expected eq $removed($mt:test-key-one),
            fn:empty($result($mt:test-key-one)),
            map:size($result)
        )
};

declare
    %test:assertEquals("true", "true")
function mt:immutable-remove-then-merge() {
    let $removed := map:remove(mt:create-test-map(), $mt:test-key-two)
    let $expected := $removed($mt:test-key-one)
    let $result := map:merge(($removed, map { $mt:test-key-one : false() }))
    return
        (
            $expected eq $removed($mt:test-key-one),
            $expected ne $result($mt:test-key-one)
        )
};

declare
    %test:assertEquals("true", "true")
function mt:immutable-merge-then-put() {
    let $merged := map:merge(mt:create-test-map())
    let $expected := $merged($mt:test-key-one)
    let $result := map:put($merged, $mt:test-key-one, false())
    return
        (
            $expected eq $merged($mt:test-key-one),
            $expected ne $result($mt:test-key-one)
        )
};

declare
    %test:assertEquals(2, "true", "true", 1)
function mt:immutable-merge-then-remove() {
    let $merged := map:merge(mt:create-test-map())
    let $expected := $merged($mt:test-key-one)
    let $result := map:remove($merged, $mt:test-key-one)
    return
        (
            map:size($merged),
            $expected eq $merged($mt:test-key-one),
            fn:empty($result($mt:test-key-one)),
            map:size($result)
        )
};

declare
    %test:assertEquals("true", "true")
function mt:immutable-merge-then-merge() {
    let $merged := map:merge(mt:create-test-map())
    let $expected := $merged($mt:test-key-one)
    let $result := map:merge(($merged, map { $mt:test-key-one : false() }))
    return
        (
            $expected eq $merged($mt:test-key-one),
            $expected ne $result($mt:test-key-one)
        )
};

declare
    %test:assertEquals("true", "true")
function mt:immutable-merge2-then-put() {
    let $merged := map:merge((mt:create-test-map(), mt:create-test-map2()))
    let $expected := $merged($mt:test-key-one)
    let $result := map:put($merged, $mt:test-key-one, false())
    return
        (
            $expected eq $merged($mt:test-key-one),
            $expected ne $result($mt:test-key-one)
        )
};

declare
    %test:assertEquals(3, "true", "true", 2)
function mt:immutable-merge2-then-remove() {
    let $merged := map:merge((mt:create-test-map(), mt:create-test-map2()))
    let $expected := $merged($mt:test-key-one)
    let $result := map:remove($merged, $mt:test-key-one)
    return
        (
            map:size($merged),
            $expected eq $merged($mt:test-key-one),
            fn:empty($result($mt:test-key-one)),
            $expected ne $result($mt:test-key-one),
            map:size($result)
        )
};

(:~
 : TODO(AR) implicit behaviour of map:merge according to XQ3.1 specification should be use-first not use-last,
 :          therefore the result should be ("true", "true") instead
:)
declare
    %test:assertEquals("true", "false")
function mt:immutable-merge2-then-merge() {
    let $merged := map:merge((mt:create-test-map(), mt:create-test-map2()))
    let $expected := $merged($mt:test-key-one)
    let $result := map:merge((map { $mt:test-key-one : false() }, $merged))
    return
        (
            $expected eq $merged($mt:test-key-one),
            $expected ne $result($mt:test-key-one)
        )
};
declare
    %test:assertEquals("true", "true")
function mt:immutable-merge-duplicates-then-put() {
    let $merged := map:merge((mt:create-test-map(), mt:create-test-map()))
    let $expected := $merged($mt:test-key-one)
    let $result := map:put($merged, $mt:test-key-one, false())
    return
        (
            $expected eq $merged($mt:test-key-one),
            $expected ne $result($mt:test-key-one)
        )
};

declare
    %test:assertEquals(2, "true", "true", 1)
function mt:immutable-merge-duplicates-then-remove() {
    let $merged := map:merge((mt:create-test-map(), mt:create-test-map()))
    let $expected := $merged($mt:test-key-one)
    let $result := map:remove($merged, $mt:test-key-one)
    return
        (
            map:size($merged),
            $expected eq $merged($mt:test-key-one),
            fn:empty($result($mt:test-key-one)),
            map:size($result)
        )
};

(:~
 : TODO(AR) implicit behaviour of map:merge according to XQ3.1 specification should be use-first not use-last,
 :          therefore the result should be ("true", "true") instead
:)
declare
    %test:assertEquals("true", "false")
function mt:immutable-merge-duplicates-then-merge() {
    let $merged := map:merge((mt:create-test-map(), mt:create-test-map()))
    let $expected := $merged($mt:test-key-one)
    let $result := map:merge((map { $mt:test-key-one : false() }, $merged))
    return
        (
            $expected eq $merged($mt:test-key-one),
            $expected ne $result($mt:test-key-one)
        )
};

(:
 : eXist-db Open Source Native XML Database
 : Copyright (C) 2001 The eXist-db Authors
 :
 : info@exist-db.org
 : http://www.exist-db.org
 :
 : This library is free software; you can redistribute it and/or
 : modify it under the terms of the GNU Lesser General Public
 : License as published by the Free Software Foundation; either
 : version 2.1 of the License, or (at your option) any later version.
 :
 : This library is distributed in the hope that it will be useful,
 : but WITHOUT ANY WARRANTY; without even the implied warranty of
 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 : Lesser General Public License for more details.
 :
 : You should have received a copy of the GNU Lesser General Public
 : License along with this library; if not, write to the Free Software
 : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 :)
xquery version "3.1";

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

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

declare %private function mt:getMapFixture () {
    map {
        "Su" : "Sunday",
        "Mo" : "Monday",
        "Tu" : "Tuesday",
        "We" : "Wednesday",
        "Th" : "Thursday",
        "Fr" : "Friday",
        "Sa" : "Saturday"
    }
};

declare variable $mt:daysOfWeek := mt:getMapFixture();

declare variable $mt:integerKeys := map {
    1 : "Sunday",
    2 : "Monday",
    3 : "Tuesday",
    4 : "Wednesday",
    5 : "Thursday",
    6 : "Friday",
    7 : "Saturday"
};

declare variable $mt:mapOfSequences := map {0: (), 1 : ("One", "Two") };

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

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

declare
    %test:assertError("err:XPST0003")
function mt:draftSyntaxNotAllowed () {
    (: this needs to be evaled, to catch the static compilation error :)
    util:eval('map { 1 := 1 }')
};

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

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

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

declare
    %test:assertXPath("count($result) = 8")
    %test:assertXPath("8 = $result")
function mt:createFromTwoMaps() {
    let $map := map:merge(($mt:integerKeys, map { 8 : "Caturday" }))
    return
        map:keys($map)
};

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

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

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

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

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

declare
    %test:assertEquals("Sunday", "Tuesday", "Thursday", "Saturday")
function mt:for-each() {
    map:for-each($mt:integerKeys, function($key, $value) {
        if ($key mod 2) then ($value) else ()
    })
};

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

    return
        $nm?b
};

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

(:~
 : TODO(AR) implicit behaviour of map:merge according to XQ3.1 specification should be use-first not use-last
:)
declare
    %test:assertEquals("Saturday", "Caturday")
function mt:merge-duplicate-keys-use-last-implicit-1() {
    let $specialWeek := map:merge(($mt:integerKeys, map { 7 : "Caturday" }))
    return
        ($mt:integerKeys(7), $specialWeek(7))
};

(:~
 : TODO(AR) implicit behaviour of map:merge according to XQ3.1 specification should be use-first not use-last
:)
declare
    %test:assertEquals("Saturday", "Saturday")
function mt:merge-duplicate-keys-use-last-implicit-2() {
    let $specialWeek := map:merge((map { 7 : "Caturday" }, $mt:integerKeys))
    return
        ($mt:integerKeys(7), $specialWeek(7))
};

declare
    %test:assertEmpty
function mt:mapEmptyValue() {
    let $map := $mt:mapOfSequences
    return
        $map(0)
};

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

declare
    %test:assertEquals("Heinz", "Roland", "Uschi", "Verona")
function mt:orderBzKeyTypeDate () {
    let $map := map {
        xs:date("1975-03-19"): "Uschi",
        xs:date("1980-01-22"): "Verona",
        xs:date("1960-06-14"): "Heinz",
        xs:date("1963-10-21"): "Roland"
    }

    for $key in map:keys($map)
    let $name := $map($key)
    order by $name ascending
    return
        $name
};

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

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

declare
    %test:assertEquals("Fr", "Mo", "Sa", "Su", "Th", "Tu", "We")
function mt:keysInFor() {
    for $day in map:keys(mt:getMapFixture())
    order by $day ascending
    return
        $day
};

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

declare
    %test:assertFalse
function mt:remove() {
    let $map := mt:getMapFixture()
    return
        map:contains(map:remove($map, "Tu"), "Tu")
};

declare
    %test:assertEquals("Fr", "Mo", "Sa", "Su", "Th")
function mt:remove2() {
    let $map := mt:getMapFixture()
    for $day in map:keys(map:remove(map:remove($map, "We"), "Tu"))
    order by $day
    return
        $day
};

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

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

declare
    %test:assertEquals("Sa")
function mt:removeSequenceOfStringKeys() {
    mt:getMapFixture()
        => map:remove(("Mo", "Tu", "We", "Th", "Fr", "Su"))
        => map:keys()
};

declare
    %test:assertEquals("Sa")
function mt:removeSequenceOfStringKeysWithNonExistent() {
    mt:getMapFixture()
        => map:remove(("Mo", "Tu", "We", "Th", "Fr", "Su", "Xx"))
        => map:keys()
};

declare
    %test:assertEquals(7)
function mt:removeRangeOfIntegerKeys() {
    $mt:integerKeys
        => map:remove(1 to 6)
        => map:keys()
};

declare
    %test:assertEquals(7)
function mt:removeIntegerKeysWithNonExistent() {
    $mt:integerKeys
        => map:remove((1, 2, 3, 4, 5, 6, 10))
        => map:keys()
};

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

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

declare
    %test:assertTrue
function mt:containsSingleKey() {
    map:contains(map { "Su": "Sunday" }, "Su")
};

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

declare
    %test:assertEquals("false", "true")
function mt:immutabilityMerge() {
    let $original := mt:getMapFixture()
    let $derived := map:merge(($original, map:entry("Ca", "Caturday")))
    return (
        map:contains($original, "Ca"),
        map:contains($derived, "Ca")
    )
};

declare
    %test:assertEquals("true", "false")
function mt:immutabilityRemove() {
    let $original := mt:getMapFixture()
    let $derived := map:remove($original, "Fr")
    return (
        map:contains($original, "Fr"),
        map:contains($derived, "Fr")
    )
};

declare
    %test:assertEquals("false", "true")
function mt:immutabilityPut() {
    let $original := mt:getMapFixture()
    let $derived := map:put($original, "Ca", "Caturday")
    return (
        map:contains($original, "Ca"),
        map:contains($derived, "Ca")
    )
};

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

declare
    %test:assertEquals(1)
function mt:lookupParenthesized() {
    let $f := function () { "one" }
    let $map := map { "one": 1 }
    return
        $map($f())
};

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

declare
    %test:assertTrue
function mt:lookupWildcard() {
    let $expected := map:keys($mt:daysOfWeek) ! $mt:daysOfWeek(.)
    let $actual := $mt:daysOfWeek?*
    return
        count($actual) eq count($expected)
        and
        (every $day in $actual satisfies $day = $expected)
};

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

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

declare
    %test:name("for-each on a map with a single entry caused NPE")
    %test:assertEquals("k")
function mt:single-entry-map() {
    let $map := map:entry("k", ())
    return
        map:for-each($map, function($k, $v) { $k })
};

declare
    %test:assertEquals(5)
function mt:qname() {
    let $a := 1
    let $m := map { $a: fn:string-length("hello") }
    return
        $m?1
};

declare
    %test:assertEmpty
function mt:no-such-entry() {
    map:get(map {"foo": "bar"}, "baz")
};

declare
    %test:assertEqualsPermutation("uk", "nl", "se", "de")
function mt:multi-merge() {
    map:merge((
  	    map { "adam": "uk" },
  	    map { "dannes": "nl" },
  	    map { "leif": "se" },
  	    map { "wolfgang": "de" }
  	))?*
};

(:
  immutability tests for https://github.com/eXist-db/exist/issues/3724
:)
declare variable $mt:test-key-one := 1;
declare variable $mt:test-key-two := 2;
declare function mt:create-test-map() {
    map {
        $mt:test-key-one : true(),
        $mt:test-key-two : true()
    }
};

declare
    %test:assertEquals("true", "true")
function mt:immutable-put-then-put() {
    let $extended := map:put(mt:create-test-map(), $mt:test-key-two, false())
    let $expected := $extended($mt:test-key-one)
    let $result := map:put($extended, $mt:test-key-one, false())
    return
        (
            $expected eq $extended($mt:test-key-one),
            $expected ne $result($mt:test-key-one)
        )
};

declare
    %test:assertEquals("true", "true")
function mt:immutable-put-then-remove() {
    let $extended := map:put(mt:create-test-map(), $mt:test-key-two, false())
    let $expected := $extended($mt:test-key-one)
    let $result := map:remove($extended, $mt:test-key-one)
    return
        (
            $expected eq $extended($mt:test-key-one),
            empty($result($mt:test-key-one))
        )
};

declare
    %test:assertEquals("true", "true")
function mt:immutable-put-then-merge() {
    let $extended := map:put(mt:create-test-map(), $mt:test-key-two, false())
    let $expected := $extended($mt:test-key-one)
    let $result := map:merge(($extended, map { $mt:test-key-one : false() }))
    return
        (
            $expected eq $extended($mt:test-key-one),
            $expected ne $result($mt:test-key-one)
        )
};

declare
    %test:assertEquals("true", "true")
function mt:immutable-remove-then-put() {
    let $removed := map:remove(mt:create-test-map(), $mt:test-key-two)
    let $expected := $removed($mt:test-key-one)
    let $result := map:put($removed, $mt:test-key-one, false())
    return
        (
            $expected eq $removed($mt:test-key-one),
            $expected ne $result($mt:test-key-one)
        )
};

declare
    %test:assertEquals("true", 1, "true", "true", 0)
function mt:immutable-remove-then-remove() {
    let $removed := map:remove(mt:create-test-map(), $mt:test-key-two)
    let $expected := $removed($mt:test-key-one)
    let $result := map:remove($removed, $mt:test-key-one)
    return
        (
            fn:empty($removed($mt:test-key-two)),
            map:size($removed),
            $expected eq $removed($mt:test-key-one),
            fn:empty($result($mt:test-key-one)),
            map:size($result)
        )
};

declare
    %test:assertEquals("true", "true")
function mt:immutable-remove-then-merge() {
    let $removed := map:remove(mt:create-test-map(), $mt:test-key-two)
    let $expected := $removed($mt:test-key-one)
    let $result := map:merge(($removed, map { $mt:test-key-one : false() }))
    return
        (
            $expected eq $removed($mt:test-key-one),
            $expected ne $result($mt:test-key-one)
        )
};

declare
    %test:assertEquals("true", "true")
function mt:immutable-merge-then-put() {
    let $merged := map:merge(mt:create-test-map())
    let $expected := $merged($mt:test-key-one)
    let $result := map:put($merged, $mt:test-key-one, false())
    return
        (
            $expected eq $merged($mt:test-key-one),
            $expected ne $result($mt:test-key-one)
        )
};

declare
    %test:assertEquals(2, "true", "true", 1)
function mt:immutable-merge-then-remove() {
    let $merged := map:merge(mt:create-test-map())
    let $expected := $merged($mt:test-key-one)
    let $result := map:remove($merged, $mt:test-key-one)
    return
        (
            map:size($merged),
            $expected eq $merged($mt:test-key-one),
            fn:empty($result($mt:test-key-one)),
            map:size($result)
        )
};

declare
    %test:assertEquals("true", "true")
function mt:immutable-merge-then-merge() {
    let $merged := map:merge(mt:create-test-map())
    let $expected := $merged($mt:test-key-one)
    let $result := map:merge(($merged, map { $mt:test-key-one : false() }))
    return
        (
            $expected eq $merged($mt:test-key-one),
            $expected ne $result($mt:test-key-one)
        )
};


Fix tests

Fix tests

xquery version "3.1";

module namespace parse="http://exist-db.org/mongodb/test/parse";

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

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

import module namespace bson = "http://exist-db.org/mongrel/bson"
                at "java:org.exist.mongodb.xquery.BSonModule";

declare variable $parse:serializeOptions := <output:serialization-parameters>
                          <output:method>json</output:method>
                          <output:indent>false</output:indent>
                      </output:serialization-parameters>;

(: ----------------------------
 : Actual tests below this line
 : ----------------------------:)

(:
 : Parser test using a string with simple value
 :)

declare
    %test:assertEquals('{"x":1}')
function parse:simple_json() {
     let $data := bson:pa rse("{ x : 1 }")
     return
         serialize($data,$parse:serializeOptions)
};

declare
    %test:assertEquals('{"x":1,"y":2,"z":3}')
function parse:larger_json() {
     let $data := bson:parse("{ x : 1 , y : 2 , z : 3 }")
     return
         serialize($data, $parse:serializeOptions)
};

declare
    %test:assertEquals('{"name":{"first":"John","last":"Backus"},"awards":[{"by":"IEEE Computer Society","year":1967,"award":"W.W. McDowell Award"},{"by":"National Academy of Engineering","year":1993,"award":"Draper Prize"}],"_id":1,"contribs":["Fortran","ALGOL","Backus-Naur Form","FP"]}')
function parse:complex_json() {
     let $data := bson:parse('{
"_id" : 1,
"name" : { "first" : "John", "last" : "Backus" },
"contribs" : [ "Fortran", "ALGOL", "Backus-Naur Form", "FP" ],
"awards" : [
           {
             "award" : "W.W. McDowell Award",
             "year" : 1967,
             "by" : "IEEE Computer Society"
           },
           { "award" : "Draper Prize",
             "year" : 1993,
             "by" : "National Academy of Engineering"
           }
]
}
')
     return
         serialize($data, $parse:serializeOptions)
};

xquery version "3.1";

module namespace json="http://exist-db.org/mongodb/test/json";

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

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

import module namespace bson = "http://exist-db.org/mongrel/bson"
                at "java:org.exist.mongodb.xquery.BSonModule";

declare variable $json:serializeOptions := <output:serialization-parameters>
                          <output:method>json</output:method>
                          <output:indent>false</output:indent>
                      </output:serialization-parameters>;

(: ----------------------------
 : Actual tests below this line
 : ----------------------------:)

(:
 : Parser test using a string with simple value
 :)

declare
    %test:assertEquals('{"x":1}')
function json:simple_json() {
     let $data := bson:parse("{ x : 1 }")
     return
         serialize($data,$json:serializeOptions)
};

declare
    %test:assertEquals('{"x":1,"y":2,"z":3}')
function json:larger_json() {
     let $data := bson:parse("{ x : 1 , y : 2 , z : 3 }")
     return
         serialize($data, $json:serializeOptions)
};

declare
    %test:assertEquals('{"name":{"first":"John","last":"Backus"},"awards":[{"by":"IEEE Computer Society","year":1967,"award":"W.W. McDowell Award"},{"by":"National Academy of Engineering","year":1993,"award":"Draper Prize"}],"_id":1,"contribs":["Fortran","ALGOL","Backus-Naur Form","FP"]}')
function json:complex_json() {
     let $data := bson:parse('{
"_id" : 1,
"name" : { "first" : "John", "last" : "Backus" },
"contribs" : [ "Fortran", "ALGOL", "Backus-Naur Form", "FP" ],
"awards" : [
           {
             "award" : "W.W. McDowell Award",
             "year" : 1967,
             "by" : "IEEE Computer Society"
           },
           { "award" : "Draper Prize",
             "year" : 1993,
             "by" : "National Academy of Engineering"
           }
]
}
')
     return
         serialize($data, $json:serializeOptions)
};


Inital working local and namespaces.

Inital working local and namespaces.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


Use type annotations for the BaseX CSV module.

xquery version "3.1";
(:~
 : BaseX CSV Module functions
 :
 : @see http://docs.basex.org/wiki/CSV_Module
 :)
module namespace csv = "http://basex.org/modules/csv";

declare namespace a = "http://reecedunn.co.uk/xquery/annotations";
declare namespace o = "http://reecedunn.co.uk/xquery/options";

declare option o:requires "basex/7.7.2";

declare type csv-string = (
    %a:since("basex", "7.7.2") %a:until("basex", "9.1") for xs:string |
    %a:since("basex", "9.1") for xs:string?
);

declare type csv-result = (
    %a:since("basex", "7.7.2") %a:until("basex", "7.8") for element(csv) |
    %a:since("basex", "7.8") %a:until("basex", "9.1") for document-node(element(csv)) |
    %a:since("basex", "9.1") for item()?
);

declare type csv-options = (
    %a:since("basex", "7.7.2") %a:until("basex", "8.2.1") for item() |
    %a:since("basex", "8.2.1") %a:until("basex", "8.6.7") for map(xs:string, item()) |
    %a:since("basex", "8.6.7.1") for map(*)?
);

declare type csv-data = (
    %a:since("basex", "7.7.2") %a:until("basex", "8.6.7") for node() |
    %a:since("basex", "8.6.7") for item()?
);

declare %a:since("basex", "7.7.2") function csv:parse($string as csv-string) as csv-result external;
declare %a:since("basex", "7.7.2") function csv:parse($string as csv-string, $options as csv-options) as csv-result external;
declare %a:since("basex", "7.7.2") function csv:serialize($input as csv-data) as xs:string external;
declare %a:since("basex", "7.7.2") function csv:serialize($input as csv-data, $options as csv-options) as xs:string external;

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

declare namespace a = "http://reecedunn.co.uk/xquery/annotations";
declare namespace o = "http://reecedunn.co.uk/xquery/options";

declare option o:requires "basex/7.7.2";

declare %a:since("basex", "7.7.2") function csv:parse($string as xs:string? (: $string [7.7.2] as xs:string [9.1] as xs:string?:)) as item()? (: [7.7.2] as element(csv) [7.8] as document-node(element(csv)) [9.1] as item()? :) external;
declare %a:since("basex", "7.7.2") function csv:parse($string as xs:string? (: $string [7.7.2] as xs:string [9.1] as xs:string?	:), $options as map(*)? (: $options [7.7.2] as item() [8.2.1] as map(xs:string, item()) [8.6.7] as map(*)? :)) as item()? (: [7.7.2] as element(csv) [7.8] as document-node(element(csv)) [9.1] as item()? :) external;
declare %a:since("basex", "7.7.2") function csv:serialize($input as item()? (:$input [7.7.2] as node() [8.6.7] as item()? :)) as xs:string external;
declare %a:since("basex", "7.7.2") function csv:serialize($input as item()? (:$input [7.7.2] as node() [8.6.7] as item()? :), $options as map(*)? (: $options [7.7.2] as item() [8.2.1] as map(xs:string, item()) [8.6.7] as map(*)? :)) as xs:string external;


extending restxq

extending restxq

module namespace fhir = "http://hl7.org/fhir/patient";
declare namespace http = "http://www.exquery.com/http";
declare namespace rest = "http://www.exquery.com/restxq";
declare namespace bgdm = "http://bagridb.com/bagri-xdm";
declare namespace p = "http://hl7.org/fhir";

declare
  %rest:GET
  %rest:path("/{id}")
  %rest:produces("application/fhir+xml")
(:  %rest:query-param("_format", "{$format}", "") :)
  %rest:query-param("_summary", "{$summary}", "")
function fhir:get-patient-by-id($id as xs:string, (: $format as xs:string?, :) $summary as xs:string?) as element()* {
  let $itr := collection("Patients")/p:Patient[p:id/@value = $id]
  return
    if ($itr) then
      for $ptn in $itr
        return
          (<rest:response>
             <http:response status="200">
             {if ($ptn/p:Patient/p:meta/p:versionId/@value) then (
               let $loc := "/Patient/" || $id || "/_history/" || $ptn/p:Patient/p:meta/p:versionId/@value
               <http:header name="ETag" value="{$ptn/p:Patient/p:meta/p:versionId/@value}"/>
(:               <http:header name="Content-Location" value="/Patient/{$id}/_history/{$ptn/p:Patient/p:meta/p:versionId/@value}"/> :)
              ) else (
               let $loc := "/Patient/" || $id
(:               <http:header name="Content-Location" value="/Patient/{$id}"/> :)
              )}
               <http:header name="Content-Location" value="{$loc}"/>
               <http:header name="Last-Modified" value="{$ptn/p:Patient/p:meta/p:lastUpdated/@value}"/>
             </http:response>
           </rest:response>, $ptn)
    else
      <rest:response>
        <http:response status="404" message="Patient with id={$id} was not found."/>
      </rest:response>
(: TODO: add summary.. do we really need it on a single Patient resource? :)
};

declare
  %rest:GET
  %rest:path("/{id}/_history/{vid}")
  %rest:produces("application/fhir+xml")
  %rest:query-param("_format", "{$format}", "")
function fhir:get-patient-by-id-version($id as xs:string, $vid as xs:string, $format as xs:string?) as element()? {
  collection("Patients")/p:Patient[p:id/@value = $id and p:meta/p:versionId/@value = $vid]
};

declare
  %rest:GET
  %rest:produces("application/fhir+xml")
  %rest:query-param("identifier", "{$identifier}")
  %rest:query-param("birthdate", "{$birthdate}")
  %rest:query-param("gender", "{$gender}")
  %rest:query-param("name", "{$name}")
  %rest:query-param("telecom", "{$telecom}")
function fhir:get-patients($identifier as xs:string?, $birthdate as xs:date?, $gender as xs:string?, $name as xs:string?, $telecom as xs:string?) as element()* {

  let $itr := collection("Patients")/p:Patient[
	(not(exists($gender)) or p:gender/@value = $gender)
    and (not(exists($birthdate)) or p:birthDate/@value = $birthdate)
    and (not(exists($name)) or contains(data(p:text), $name))
    and (not(exists($identifier)) or contains(p:identifier/p:value/@value, $identifier))
    and (not(exists($telecom)) or contains(string-join(p:telecom/p:value/@value, " "), $telecom))]

  return
    <Bundle xmlns="http://hl7.org/fhir">
      <id value="{bgdm:get-uuid()}" />
      <meta>
        <lastUpdated value="{current-dateTime()}" />
      </meta>
      <type value="searchset" />
      <total value="{count($itr)}" />
      <link>
        <relation value="self" />
        <url value="http://bagridb.com/Patient/search?name=test" />
      </link>
      {for $ptn in $itr
       return
         <entry>
           <resource>{$ptn}</resource>
         </entry>
      }
    </Bundle>
};

declare
  %rest:POST
  %rest:path("_search")
  %rest:produces("application/fhir+xml")
  %rest:form-param("identifier", "{$identifier}")
  %rest:form-param("birthdate", "{$birthdate}")
  %rest:form-param("gender", "{$gender}")
  %rest:form-param("name", "{$name}")
  %rest:form-param("telecom", "{$telecom}")
function fhir:search-patients($identifier as xs:string?, $birthdate as xs:date?, $gender as xs:token?, $name as xs:string?, $telecom as xs:string?) as element()* {

  let $itr := collection("Patients")/p:Patient[
	(not(exists($gender)) or p:gender/@value = $gender)
    and (not(exists($birthdate)) or p:birthDate/@value = $birthdate)
    and (not(exists($name)) or contains(data(p:text), $name))
    and (not(exists($identifier)) or contains(p:identifier/p:value/@value, $identifier))
    and (not(exists($telecom)) or contains(string-join(p:telecom/p:value/@value, " "), $telecom))]

  return
    <Bundle xmlns="http://hl7.org/fhir">
      <id value="{bgdm:get-uuid()}" />
      <meta>
        <lastUpdated value="{current-dateTime()}" />
      </meta>
      <type value="searchset" />
      <total value="{count($itr)}" />
      <link>
        <relation value="self" />
        <url value="http://bagridb.com/Patient/search?name=test" />
      </link>
      {for $ptn in $itr
       return
         <entry>
           <resource>{$ptn}</resource>
         </entry>
      }
    </Bundle>
};

declare
  %rest:POST
  %rest:consumes("application/fhir+xml")
  %rest:produces("application/fhir+xml")
  %rest:query-param("_format", "{$format}", "")
function fhir:create-patient($content as xs:string, $format as xs:string?) as element()? {
  let $doc := parse-xml($content)
  let $uri := xs:string($doc/p:Patient/p:id/@value) || ".xml"
  let $uri := bgdm:store-document(xs:anyURI($uri), $content, ())
  let $content := bgdm:get-document-content($uri)
  let $doc := parse-xml($content)
  return $doc/p:Patient
};

declare
  %rest:PUT
  %rest:path("/{id}")
  %rest:consumes("application/fhir+xml")
  %rest:produces("application/fhir+xml")
  %rest:query-param("_format", "{$format}", "")
function fhir:update-patient($id as xs:string, $content as xs:string, $format as xs:string?) as element()? {
  for $uri in fhir:get-patient-uri($id)
  let $uri2 := bgdm:store-document($uri, $content, ())
  let $content2 := bgdm:get-document-content($uri2, ())
  let $doc := parse-xml($content2)
  return $doc/p:Patient
};

declare
  %rest:DELETE
  %rest:path("/{id}")
function fhir:delete-patient($id as xs:string) as item()? {
  for $uri in fhir:get-patient-uri($id)
  return bgdm:remove-document($uri)
};

declare
  %private
function fhir:get-patient-uri($id as xs:string) as xs:anyURI? {
  let $query :=
' declare namespace p = "http://hl7.org/fhir";
  declare variable $id external;

  for $ptn in fn:collection("Patients")/p:Patient
  where $ptn/p:id/@value = $id
  return $ptn'

  let $uri := bgdm:query-document-uris($query, ("id", $id), ())
  return xs:anyURI($uri)
};

declare
  %private
function fhir:get-patient-uris($params as xs:string*) as xs:anyURI* {
  let $prolog :=
' declare namespace p = "http://hl7.org/fhir";
  declare variable $id external;
'

  let $query :=
' for $ptn in fn:collection("Patients")/p:Patient
  where $ptn/p:id/@value = $id
  return $ptn'

  let $query := $prolog || $query
  let $uri := bgdm:query-document-uris($query, $params, ())
  return xs:anyURI($uri)
};

(:

Search Control Parameters:

_summary	string	    ( ,   )	true | false (false is default)

Patient
birthdate	date	  	Patient.birthDate
gender	token	 	Patient.gender
name	string	A server defined search that may match any of the string fields in the HumanName, including family, give, prefix, suffix, suffix, and/or text	Patient.name
telecom	token	      	Patient.telecom
identifier	Identifier	Patient.active

inject; exp: "Patients"; env: net.sf.saxon.query.QueryModule@42dfc; construct: 0; qName: null
inject; exp: collection("Patients"); env: net.sf.saxon.query.QueryModule@42dfc; construct: 2009; qName: fn:collection
inject; exp: $ptn; env: net.sf.saxon.query.QueryModule@42dfc; construct: 2014; qName: null
injectClause; traget: for $ptn in homCheck((collection("Patients"))/child::element(Q{http://hl7.org/fhir}Patient)); env: net.sf.saxon.query.QueryModule@42dfc
injectClause; traget: where (homCheck((homCheck($ptn/child::element(Q{http://hl7.org/fhir}id)))/attribute::attribute(Q{}value))) = $Q{}id; env: net.sf.saxon.query.QueryModule@42dfc

:)

module namespace fhir = "http://hl7.org/fhir/patient";
declare namespace rest = "http://www.exquery.com/restxq";
declare namespace bgdm = "http://bagridb.com/bagri-xdm";
declare namespace p = "http://hl7.org/fhir";

declare
  %rest:GET
  %rest:path("/{id}")
  %rest:produces("application/fhir+xml")
(:  %rest:query-param("_format", "{$format}", "") :)
  %rest:query-param("_summary", "{$summary}", "")
function fhir:get-patient-by-id($id as xs:string, (: $format as xs:string?, :) $summary as xs:string?) as element()? {
  collection("Patients")/p:Patient[p:id/@value = $id]
(: TODO: add summary.. do we really need it on a single Patient resource? :)
};

declare
  %rest:GET
  %rest:path("/{id}/_history/{vid}")
  %rest:produces("application/fhir+xml")
  %rest:query-param("_format", "{$format}", "")
function fhir:get-patient-by-id-version($id as xs:string, $vid as xs:string, $format as xs:string?) as element()? {
  collection("Patients")/p:Patient[p:id/@value = $id and p:meta/p:versionId/@value = $vid]
};

declare
  %rest:GET
  %rest:produces("application/fhir+xml")
  %rest:query-param("identifier", "{$identifier}")
  %rest:query-param("birthdate", "{$birthdate}")
  %rest:query-param("gender", "{$gender}")
  %rest:query-param("name", "{$name}")
  %rest:query-param("telecom", "{$telecom}")
function fhir:get-patients($identifier as xs:string?, $birthdate as xs:date?, $gender as xs:string?, $name as xs:string?, $telecom as xs:string?) as element()* {

  let $itr := collection("Patients")/p:Patient[
	(not(exists($gender)) or p:gender/@value = $gender)
    and (not(exists($birthdate)) or p:birthDate/@value = $birthdate)
    and (not(exists($name)) or contains(data(p:text), $name))
    and (not(exists($identifier)) or contains(p:identifier/p:value/@value, $identifier))
    and (not(exists($telecom)) or contains(string-join(p:telecom/p:value/@value, " "), $telecom))]

  return
    <Bundle xmlns="http://hl7.org/fhir">
      <id value="{bgdm:get-uuid()}" />
      <meta>
        <lastUpdated value="{current-dateTime()}" />
      </meta>
      <type value="searchset" />
      <total value="{count($itr)}" />
      <link>
        <relation value="self" />
        <url value="http://bagridb.com/Patient/search?name=test" />
      </link>
      {for $ptn in $itr
       return
         <entry>
           <resource>{$ptn}</resource>
         </entry>
      }
    </Bundle>
};

declare
  %rest:POST
  %rest:path("_search")
  %rest:produces("application/fhir+xml")
  %rest:form-param("identifier", "{$identifier}")
  %rest:form-param("birthdate", "{$birthdate}")
  %rest:form-param("gender", "{$gender}")
  %rest:form-param("name", "{$name}")
  %rest:form-param("telecom", "{$telecom}")
function fhir:search-patients($identifier as xs:string?, $birthdate as xs:date?, $gender as xs:token?, $name as xs:string?, $telecom as xs:string?) as element()* {

  let $itr := collection("Patients")/p:Patient[
	(not(exists($gender)) or p:gender/@value = $gender)
    and (not(exists($birthdate)) or p:birthDate/@value = $birthdate)
    and (not(exists($name)) or contains(data(p:text), $name))
    and (not(exists($identifier)) or contains(p:identifier/p:value/@value, $identifier))
    and (not(exists($telecom)) or contains(string-join(p:telecom/p:value/@value, " "), $telecom))]

  return
    <Bundle xmlns="http://hl7.org/fhir">
      <id value="{bgdm:get-uuid()}" />
      <meta>
        <lastUpdated value="{current-dateTime()}" />
      </meta>
      <type value="searchset" />
      <total value="{count($itr)}" />
      <link>
        <relation value="self" />
        <url value="http://bagridb.com/Patient/search?name=test" />
      </link>
      {for $ptn in $itr
       return
         <entry>
           <resource>{$ptn}</resource>
         </entry>
      }
    </Bundle>
};

declare
  %rest:POST
  %rest:consumes("application/fhir+xml")
  %rest:produces("application/fhir+xml")
  %rest:query-param("_format", "{$format}", "")
function fhir:create-patient($content as xs:string, $format as xs:string?) as element()? {
  let $doc := parse-xml($content)
  let $uri := xs:string($doc/p:Patient/p:id/@value) || ".xml"
  let $uri := bgdm:store-document(xs:anyURI($uri), $content, ())
  let $content := bgdm:get-document-content($uri)
  let $doc := parse-xml($content)
  return $doc/p:Patient
};

declare
  %rest:PUT
  %rest:path("/{id}")
  %rest:consumes("application/fhir+xml")
  %rest:produces("application/fhir+xml")
  %rest:query-param("_format", "{$format}", "")
function fhir:update-patient($id as xs:string, $content as xs:string, $format as xs:string?) as element()? {
  for $uri in fhir:get-patient-uri($id)
  let $uri2 := bgdm:store-document($uri, $content, ())
  let $content2 := bgdm:get-document-content($uri2, ())
  let $doc := parse-xml($content2)
  return $doc/p:Patient
};

declare
  %rest:DELETE
  %rest:path("/{id}")
function fhir:delete-patient($id as xs:string) as item()? {
  for $uri in fhir:get-patient-uri($id)
  return bgdm:remove-document($uri)
};

declare
  %private
function fhir:get-patient-uri($id as xs:string) as xs:anyURI? {
  let $query :=
' declare namespace p = "http://hl7.org/fhir";
  declare variable $id external;

  for $ptn in fn:collection("Patients")/p:Patient
  where $ptn/p:id/@value = $id
  return $ptn'

  let $uri := bgdm:query-document-uris($query, ("id", $id), ())
  return xs:anyURI($uri)
};

declare
  %private
function fhir:get-patient-uris($params as xs:string*) as xs:anyURI* {
  let $prolog :=
' declare namespace p = "http://hl7.org/fhir";
  declare variable $id external;
'

  let $query :=
' for $ptn in fn:collection("Patients")/p:Patient
  where $ptn/p:id/@value = $id
  return $ptn'

  let $query := $prolog || $query
  let $uri := bgdm:query-document-uris($query, $params, ())
  return xs:anyURI($uri)
};

(:

Search Control Parameters:

_summary	string	    ( ,   )	true | false (false is default)

Patient
birthdate	date	  	Patient.birthDate
gender	token	 	Patient.gender
name	string	A server defined search that may match any of the string fields in the HumanName, including family, give, prefix, suffix, suffix, and/or text	Patient.name
telecom	token	      	Patient.telecom
identifier	Identifier	Patient.active

inject; exp: "Patients"; env: net.sf.saxon.query.QueryModule@42dfc; construct: 0; qName: null
inject; exp: collection("Patients"); env: net.sf.saxon.query.QueryModule@42dfc; construct: 2009; qName: fn:collection
inject; exp: $ptn; env: net.sf.saxon.query.QueryModule@42dfc; construct: 2014; qName: null
injectClause; traget: for $ptn in homCheck((collection("Patients"))/child::element(Q{http://hl7.org/fhir}Patient)); env: net.sf.saxon.query.QueryModule@42dfc
injectClause; traget: where (homCheck((homCheck($ptn/child::element(Q{http://hl7.org/fhir}id)))/attribute::attribute(Q{}value))) = $Q{}id; env: net.sf.saxon.query.QueryModule@42dfc

:)


Add testcaser for issue concat operator in attributes

xquery version "3.0";

module namespace concat="http://exist-db.org/xquery/test/string-concatenation";

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

declare variable $concat:XML :=

            <c>CC1</c>
            <c>CC2</c>

    ;

declare
    %test:assertEquals("ungrateful")
function concat:strings() {
    ('un' ||  'grateful')
};

declare
    %test:assertEquals("The result is 22.5")
function concat:numbers() {
    ("The result is " ||  22.5)
};

declare
    %test:assertEquals("unGRATEFUL")
function concat:function-call() {
    ("un" ||  upper-case("grateful"))
};

declare
    %test:assertEquals("The result is ")
function concat:empty-sequence1() {
    ("The result is " ||  ())
};

declare
    %test:assertTrue
function concat:empty-sequence2() {
    (() ||  ()) instance of xs:string
};

declare
    %test:assertTrue
function concat:empty-sequence3() {
    (() ||  ()) eq ""
};

declare
    %test:assertEquals(1)
function concat:empty-strings() {
    count("" ||  "")
};

declare
    %test:assertEquals("zzzzz123")
function concat:nested() {
    (("zzz" || "zz") || "123")
};

declare
    %test:assertEquals("ungrateful death")
function concat:multiple() {
    ('un' ||  () || 'grateful' || ' ' || "death")
};

declare
    %test:assertEquals("12-16")
function concat:precedence1() {
    12 || 34 - 50
};

declare
    %test:assertTrue
function concat:precedence2() {
    "1234" eq 12 || 34
};

declare
    %test:assertTrue
function concat:nodes() {
    $concat:XML/b/c[1] || 2 eq "CC12"
};

declare
    %test:assertError("FOTY0013")
function concat:bad-argument() {
    ("abc" || "abc" ||  fn:concat#3)
};

(: declare
    %test:assertEquals("")
function concat:attributes1() {

}; :)

xquery version "3.0";

module namespace concat="http://exist-db.org/xquery/test/string-concatenation";

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

declare variable $concat:XML :=
    <a>
        <b>
            <c>CC1</c>
            <c>CC2</c>
        </b>
    </a>;

declare
    %test:assertEquals("ungrateful")
function concat:strings() {
    ('un' ||  'grateful')
};

declare
    %test:assertEquals("The result is 22.5")
function concat:numbers() {
    ("The result is " ||  22.5)
};

declare
    %test:assertEquals("unGRATEFUL")
function concat:function-call() {
    ("un" ||  upper-case("grateful"))
};

declare
    %test:assertEquals("The result is ")
function concat:empty-sequence1() {
    ("The result is " ||  ())
};

declare
    %test:assertTrue
function concat:empty-sequence2() {
    (() ||  ()) instance of xs:string
};

declare
    %test:assertTrue
function concat:empty-sequence3() {
    (() ||  ()) eq ""
};

declare
    %test:assertEquals(1)
function concat:empty-strings() {
    count("" ||  "")
};

declare
    %test:assertEquals("zzzzz123")
function concat:nested() {
    (("zzz" || "zz") || "123")
};

declare
    %test:assertEquals("ungrateful death")
function concat:multiple() {
    ('un' ||  () || 'grateful' || ' ' || "death")
};

declare
    %test:assertEquals("12-16")
function concat:precedence1() {
    12 || 34 - 50
};

declare
    %test:assertTrue
function concat:precedence2() {
    "1234" eq 12 || 34
};

declare
    %test:assertTrue
function concat:nodes() {
    $concat:XML/b/c[1] || 2 eq "CC12"
};

declare
    %test:assertError("FOTY0013")
function concat:bad-argument() {
    ("abc" || "abc" ||  fn:concat#3)
};