[test] Tests for return type of fn:doc

[test] Tests for return type of fn:doc

xquery version "3.1";

(:~
 : Test various F+O functions
 :)
module namespace fnt="http://exist-db.org/xquery/test/fn";

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

declare variable $fnt:composed := 'Åström';
declare variable $fnt:decomposed := 'Åström';

declare
    %test:setUp
function fnt:store() {
    let $col := xmldb:create-collection("/db", "fn-test")
    return
        (
            xmldb:store($col, "test.xml", <books><book/></books>),
            xmldb:store($col, "test.bin", "some binary", "application/octet-stream")
        )
};

declare
    %test:tearDown
function fnt:cleanup() {
    xmldb:remove("/db/fn-test")
};

declare
    %test:args("NFC")
    %test:assertEquals(6, 6)
    %test:args("NFD")
    %test:assertEquals(8, 8)
    %test:args("NFKD")
    %test:assertEquals(8, 8)
    %test:args("NFKC")
    %test:assertEquals(6, 6)
    %test:args("XYZ")
    %test:assertError
function fnt:normalize-unicode($normalization-form as xs:string) {
    count(string-to-codepoints(normalize-unicode($fnt:composed, $normalization-form))),
    count(string-to-codepoints(normalize-unicode($fnt:decomposed, $normalization-form)))
};

declare
    %test:args("test.xml")
    %test:assertEquals("true")
    %test:args("test.bin")
    %test:assertEquals("false")
    %test:args("no-such-file.xml")
    %test:assertEquals("false")
    %test:args("no-such-file.bin")
    %test:assertEquals("false")
function fnt:doc-available($filename as xs:string) {
    fn:doc-available("/db/fn-test/" || $filename)
};

declare
    %test:args("test.xml")
    %test:assertEquals("true")
function fnt:doc($filename as xs:string) {
    fn:doc("/db/fn-test/" || $filename) instance of document-node()
};

declare
    %test:args("test.xml")
    %test:assertEmpty
function fnt:doc($filename as xs:string) {
    fn:doc("/db/fn-test/" || $filename)/book
};

declare
    %test:assertEquals(0, 0)
function fnt:tokenize-empty() {
     count(() => tokenize("\s")),
     count(tokenize((), "\s"))
};

declare
    %test:args("hello adam")
    %test:assertEquals("hello", "adam")
    %test:args("hello   adam")
    %test:assertEquals("hello", "adam")
function fnt:tokenize-onearg($str as xs:string) {
     tokenize($str)
};

xquery version "3.1";

(:~
 : Test various F+O functions
 :)
module namespace fnt="http://exist-db.org/xquery/test/fn";

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

declare variable $fnt:composed := 'Åström';
declare variable $fnt:decomposed := 'Åström';

declare
    %test:setUp
function fnt:store() {
    let $col := xmldb:create-collection("/db", "fn-test")
    return
        (
            xmldb:store($col, "test.xml", <something/>),
            xmldb:store($col, "test.bin", "some binary", "application/octet-stream")
        )
};

declare
    %test:tearDown
function fnt:cleanup() {
    xmldb:remove("/db/fn-test")
};

declare
    %test:args("NFC")
    %test:assertEquals(6, 6)
    %test:args("NFD")
    %test:assertEquals(8, 8)
    %test:args("NFKD")
    %test:assertEquals(8, 8)
    %test:args("NFKC")
    %test:assertEquals(6, 6)
    %test:args("XYZ")
    %test:assertError
function fnt:normalize-unicode($normalization-form as xs:string) {
    count(string-to-codepoints(normalize-unicode($fnt:composed, $normalization-form))),
    count(string-to-codepoints(normalize-unicode($fnt:decomposed, $normalization-form)))
};

declare
    %test:args("test.xml")
    %test:assertEquals("true")
    %test:args("test.bin")
    %test:assertEquals("false")
    %test:args("no-such-file.xml")
    %test:assertEquals("false")
    %test:args("no-such-file.bin")
    %test:assertEquals("false")
function fnt:doc-available($filename as xs:string) {
    fn:doc-available("/db/fn-test/" || $filename)
};

declare
    %test:assertEquals(0, 0)
function fnt:tokenize-empty() {
     count(() => tokenize("\s")),
     count(tokenize((), "\s"))
};

declare
    %test:args("hello adam")
    %test:assertEquals("hello", "adam")
    %test:args("hello   adam")
    %test:assertEquals("hello", "adam")
function fnt:tokenize-onearg($str as xs:string) {
     tokenize($str)
};


[bugfix] Login twice fails (HTML admin interface) - ID: 3146481

http://sourceforge.net/tracker/?func=detail&aid=3146481&group_id=17691&atid=117691

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

xquery version "1.0";
(: $Id$ :)
(:
    Main module of the database administration interface.
:)

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

declare namespace request = "http://exist-db.org/xquery/request";
declare namespace session = "http://exist-db.org/xquery/session";
declare namespace util = "http://exist-db.org/xquery/util";
declare namespace xdb = "http://exist-db.org/xquery/xmldb";

import module namespace status = "http://exist-db.org/xquery/admin-interface/status" at "status.xqm";
import module namespace browse = "http://exist-db.org/xquery/admin-interface/browse" at "browse.xqm";
import module namespace users = "http://exist-db.org/xquery/admin-interface/users" at "users.xqm";
import module namespace xqueries = "http://exist-db.org/xquery/admin-interface/xqueries" at "xqueries.xqm";
import module namespace shut = "http://exist-db.org/xquery/admin-interface/shutdown" at "shutdown.xqm";
import module namespace setup = "http://exist-db.org/xquery/admin-interface/setup" at "setup.xqm";
import module namespace rev="http://exist-db.org/xquery/admin-interface/revisions" at "versions.xqm";
import module namespace backup="http://exist-db.org/xquery/admin-interface/backup" at "backup.xqm";
import module namespace prof="http://exist-db.org/xquery/profiling" at "trace.xqm";
import module namespace grammar="http://exist-db.org/xquery/admin-interface/grammar" at "grammar.xqm";
import module namespace install="http://exist-db.org/xquery/install-tools" at "install.xqm";
import module namespace fundocs="http://exist-db.org/xquery/admin/fundocs" at "fundocs.xqm";
import module namespace repomanager="http://exist-db.org/xquery/admin-interface/repo" at "repo.xqm";
import module namespace indexes="http://exist-db.org/xquery/admin-interface/indexes" at "indexes.xqm";

declare option exist:serialize "method=xhtml media-type=text/html";

(:
    Display the version, SVN revision and user info in the top right corner
:)
declare function admin:info-header() as element()
{

Version: { util:system-property( "product-version" ) }

SVN Revision: { util:system-property( "svn-revision" ) }

Build: {util:system-property("product-build")}

User: { xdb:get-current-user() }

};

(:
    Select the page to show. Every page is defined in its own module
:)
declare function admin:panel() as element()
{
    let $panel := request:get-parameter("panel", "status")[1] return
        if($panel eq "browse") then
        (
            browse:main()
        )
        else if($panel eq "users") then
        (
            users:main()
        )
         else if($panel eq "xqueries") then
        (
            xqueries:main()
        )
        else if($panel eq "shutdown") then
        (
            shut:main()
        )
        else if($panel eq "setup") then
        (
            setup:main()
        )
        else if ($panel eq "fundocs") then
        (
            fundocs:main()
        )
		else if ($panel eq "repo") then
		(
			repomanager:main()
		)
		else if ($panel eq "revisions") then
		(
			rev:main()
		)

		else if ($panel eq "backup") then
		(
		    backup:main()
		)
	    else if ($panel eq "trace") then
	    (
	        prof:main()
        )
        else if ($panel eq "grammar") then
	    (
	        grammar:main()
        )
        else if ($panel eq "install") then
        (
            install:main()
        )
        else if ($panel eq "indexes") then
        (
            indexes:main()
        )
        else
        (
            status:main()
        )
};

declare function admin:panel-header() {
    let $panel := request:get-parameter("panel", "status")[1]
    return
        if ($panel eq "install") then
            install:header()
        else
            <xf:model xmlns:xf="http://www.w3.org/2002/xforms">
            </xf:model>
};

(:~
    Display the login form.
:)
declare function admin:display-login-form() as element()
{

Login

This is a protected resource. Only registered database users can log
        in. If you have not set up any users, login as "admin" and leave the
        password field empty. Note that the "guest" user is not permitted access.

        <form action="{session:encode-url(request:get-uri())}" method="post">

                    Please Login

                    Username:
                    <input name="user" type="text" size="20"/>

                    Password:
                    <input name="pass" type="password" size="20"/>

                    <input type="submit" value="Login"/>

            {
                for $param in request:get-parameter-names()
                return
                    <input type="hidden" name="{$param}"
                        value="{request:get-parameter($param, ())}"/>
            }
        </form>

};

(: main entry point :)
let $userParam := request:get-parameter("user", ())[1]
let $passwdParam := request:get-parameter("pass", ())[1]
let $isLoggedIn :=  if(xdb:get-current-user() eq "guest")then
    (
        (: is this a login attempt? :)
        if($userParam and not(empty($passwdParam)))then
        (
            if($userParam = ("", "guest") )then
            (
                (: prevent the guest user from accessing the admin webapp :)
                false()
            )
            else
            (
                (: try and log the user in :)
                xdb:login( "/db", $userParam, $passwdParam, true() )
            )
        )
        else
        (
            (: prevent the guest user from accessing the admin webapp :)
            false()
        )
    )
    else
    (
        (: if we are already logged in, are we logging out - i.e. set permissions back to guest :)
        if(request:get-parameter("logout",()))then
        (
        	let $null  := xdb:login("/db", "guest", "guest")
            let $inval := session:invalidate()

            return false()
        )
        else
        (
             (: we are already logged in and we are not the guest user :)
            true()
        )
    )
return (
    <?css-conversion no?>,

            <title>eXist Database Administration</title>
            <link type="text/css" href="admin.css" rel="stylesheet"/>
			<link type="text/css" href="styles/prettify.css" rel="stylesheet"/>
			<link type="text/css" href="../scripts/yui/yui-skin.css" rel="stylesheet"/>
            <link rel="shortcut icon" href="../resources/exist_icon_16x16.ico"/>
			<link rel="icon" href="../resources/exist_icon_16x16.png" type="image/png"/>

			{ admin:panel-header() }

                {admin:info-header()}

Select a Page

                    {
                        let $link := session:encode-url(request:get-uri())
                        return

Home

System Status

Browse Collections

User Management

View Running Jobs

Examples Setup

Install Tools

Install Documentation

Backups

Query Profiling

Grammar cache

Package Repository

Browse Indexes

Shutdown

Logout

                    }

                        Logged in as: {xdb:get-current-user()}

                {
                    if($isLoggedIn)then
                    (
                        admin:panel()
                    )
                    else
                    (
                        admin:display-login-form()
                    )
                }

)

xquery version "1.0";
(: $Id$ :)
(:
    Main module of the database administration interface.
:)

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

declare namespace request = "http://exist-db.org/xquery/request";
declare namespace session = "http://exist-db.org/xquery/session";
declare namespace util = "http://exist-db.org/xquery/util";
declare namespace xdb = "http://exist-db.org/xquery/xmldb";

import module namespace status = "http://exist-db.org/xquery/admin-interface/status" at "status.xqm";
import module namespace browse = "http://exist-db.org/xquery/admin-interface/browse" at "browse.xqm";
import module namespace users = "http://exist-db.org/xquery/admin-interface/users" at "users.xqm";
import module namespace xqueries = "http://exist-db.org/xquery/admin-interface/xqueries" at "xqueries.xqm";
import module namespace shut = "http://exist-db.org/xquery/admin-interface/shutdown" at "shutdown.xqm";
import module namespace setup = "http://exist-db.org/xquery/admin-interface/setup" at "setup.xqm";
import module namespace rev="http://exist-db.org/xquery/admin-interface/revisions" at "versions.xqm";
import module namespace backup="http://exist-db.org/xquery/admin-interface/backup" at "backup.xqm";
import module namespace prof="http://exist-db.org/xquery/profiling" at "trace.xqm";
import module namespace grammar="http://exist-db.org/xquery/admin-interface/grammar" at "grammar.xqm";
import module namespace install="http://exist-db.org/xquery/install-tools" at "install.xqm";
import module namespace fundocs="http://exist-db.org/xquery/admin/fundocs" at "fundocs.xqm";
import module namespace repomanager="http://exist-db.org/xquery/admin-interface/repo" at "repo.xqm";
import module namespace indexes="http://exist-db.org/xquery/admin-interface/indexes" at "indexes.xqm";

declare option exist:serialize "method=xhtml media-type=text/html";

(:
    Display the version, SVN revision and user info in the top right corner
:)
declare function admin:info-header() as element()
{

Version: { util:system-property( "product-version" ) }

SVN Revision: { util:system-property( "svn-revision" ) }

Build: {util:system-property("product-build")}

User: { xdb:get-current-user() }

};

(:
    Select the page to show. Every page is defined in its own module
:)
declare function admin:panel() as element()
{
    let $panel := request:get-parameter("panel", "status")[1] return
        if($panel eq "browse") then
        (
            browse:main()
        )
        else if($panel eq "users") then
        (
            users:main()
        )
         else if($panel eq "xqueries") then
        (
            xqueries:main()
        )
        else if($panel eq "shutdown") then
        (
            shut:main()
        )
        else if($panel eq "setup") then
        (
            setup:main()
        )
        else if ($panel eq "fundocs") then
        (
            fundocs:main()
        )
		else if ($panel eq "repo") then
		(
			repomanager:main()
		)
		else if ($panel eq "revisions") then
		(
			rev:main()
		)

		else if ($panel eq "backup") then
		(
		    backup:main()
		)
	    else if ($panel eq "trace") then
	    (
	        prof:main()
        )
        else if ($panel eq "grammar") then
	    (
	        grammar:main()
        )
        else if ($panel eq "install") then
        (
            install:main()
        )
        else if ($panel eq "indexes") then
        (
            indexes:main()
        )
        else
        (
            status:main()
        )
};

declare function admin:panel-header() {
    let $panel := request:get-parameter("panel", "status")[1]
    return
        if ($panel eq "install") then
            install:header()
        else
            <xf:model xmlns:xf="http://www.w3.org/2002/xforms">
            </xf:model>
};

(:~
    Display the login form.
:)
declare function admin:display-login-form() as element()
{

Login

This is a protected resource. Only registered database users can log
        in. If you have not set up any users, login as "admin" and leave the
        password field empty. Note that the "guest" user is not permitted access.

        <form action="{session:encode-url(request:get-uri())}" method="post">

                    Please Login

                    Username:
                    <input name="user" type="text" size="20"/>

                    Password:
                    <input name="pass" type="password" size="20"/>

                    <input type="submit" value="Login"/>

            {
                for $param in request:get-parameter-names()
                return
                    <input type="hidden" name="{$param}"
                        value="{request:get-parameter($param, ())}"/>
            }
        </form>

};

(: main entry point :)
let $isLoggedIn :=  if(xdb:get-current-user() eq "guest")then
    (
        (: is this a login attempt? :)
        if(request:get-parameter("user", ()) and not(empty(request:get-parameter("pass", ()))))then
        (
            if(request:get-parameter("user", ()) eq "guest")then
            (
                (: prevent the guest user from accessing the admin webapp :)
                false()
            )
            else
            (
                (: try and log the user in :)
                xdb:login( "/db", request:get-parameter("user", ()), request:get-parameter("pass", () ), true() )
            )
        )
        else
        (
            (: prevent the guest user from accessing the admin webapp :)
            false()
        )
    )
    else
    (
        (: if we are already logged in, are we logging out - i.e. set permissions back to guest :)
        if(request:get-parameter("logout",()))then
        (
        	let $null  := xdb:login("/db", "guest", "guest")
            let $inval := session:invalidate()

            return false()
        )
        else
        (
             (: we are already logged in and we are not the guest user :)
            true()
        )
    )
return (
    <?css-conversion no?>,

            <title>eXist Database Administration</title>
            <link type="text/css" href="admin.css" rel="stylesheet"/>
			<link type="text/css" href="styles/prettify.css" rel="stylesheet"/>
			<link type="text/css" href="../scripts/yui/yui-skin.css" rel="stylesheet"/>
            <link rel="shortcut icon" href="../resources/exist_icon_16x16.ico"/>
			<link rel="icon" href="../resources/exist_icon_16x16.png" type="image/png"/>

			{ admin:panel-header() }

                {admin:info-header()}

Select a Page

                    {
                        let $link := session:encode-url(request:get-uri())
                        return

Home

System Status

Browse Collections

User Management

View Running Jobs

Examples Setup

Install Tools

Install Documentation

Backups

Query Profiling

Grammar cache

Package Repository

Browse Indexes

Shutdown

Logout

                    }

                        Logged in as: {xdb:get-current-user()}

                {
                    if($isLoggedIn)then
                    (
                        admin:panel()
                    )
                    else
                    (
                        admin:display-login-form()
                    )
                }

)


Added the parameters needed to run the function as external variables.

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

(:~
 : Run every test case that matches a given criterion in a different process
 : and report every test case that HANGS or ends in a SEG FAULT.
 :
 : @author Sorin Nasoi
 :)

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

declare namespace ann =
  "http://www.zorba-xquery.com/annotations";
declare namespace fots =
  "http://www.w3.org/2010/09/qt-fots-catalog";

declare variable $fotsPath as xs:string external := "";
declare variable $dependency as xs:string external := "";
declare variable $testSetPrefixes as xs:string external := "";

(:~
 : Return the exit-code after invoking the FOTS driver with given $params.
 :
 : @param $params additional params.
 : @return the exit-code returned by the invoked application.
 :)
declare %ann:sequential function local:exec(
  $params as xs:string*
) as xs:string {
  variable $pathZorba := normalize-space(xs:string(proc:exec("pwd")/proc:stdout));

  proc:exec(string-join(($pathZorba, "/zorba"),''),
            ("-f", "-q", concat($pathZorba, "/../../test/fots_driver/cli.xq"),
             "-e", concat("fotsPath:=", $fotsPath),
             "-e", "verbose:=false",
             $params
            )
           )/proc:exit-code
};

declare %private %ann:sequential function local:invoke-exec(
  $testSets   as element(fots:test-set)*,
  $baseUri    as xs:anyURI,
  $dependency as xs:string*
) as xs:string* {
  for $testSet in $testSets
    let $testSetDoc := doc(resolve-uri($testSet/@file, $baseUri))
    let $testSetName := xs:string($testSet/@name)
    let $testCaseNames as xs:string* := d:list-test-cases($testSetDoc,
                                                          $dependency)
    return
      for $testCaseName in $testCaseNames
      let $exit-code :=
          trace(local:exec(("-e", "mode:=run-test-case",
                      "-e", concat("testSetName:=",  $testSetName),
                      "-e", concat("testCaseName:=", trace($testCaseName, "processing test case :"))
                     )
                    ),"exit-code")
      where (xs:integer($exit-code) gt xs:integer(134))
      return $testCaseName
};

(:~
 : Return the name of the test cases that return an exit-code > 134.
 :
 : @param $fotsPath path to the FOTS catalog file.
 : @param $testSetPrefixes name/criteria for the test sets.
 : @param $dependency defined dependency.
 : @return the exit-codes returned by the invoked applications.
 :)
declare %ann:sequential function local:list-test-cases(
  $fotsPath         as xs:string,
  $testSetPrefixes  as xs:string*,
  $dependency       as xs:string*
) as xs:string* {
  variable  $doc := doc(resolve-uri($fotsPath)),
            $baseUri:= resolve-uri(util:parent-folder($fotsPath));

  if(string-join($testSetPrefixes,'') = '')
  then local:invoke-exec($doc/fots:catalog/fots:test-set,
                         $baseUri,
                         $dependency)
  else
    for $prefix in $testSetPrefixes
    return
      local:invoke-exec($doc/fots:catalog/fots:test-set[starts-with(@name, $prefix)],
                        $baseUri,
                        $dependency)
};

if( $fotsPath = '')
then fn:error($fots-err:errNA,
              "please set the external variable $fotsPath to point to FOTS catalog.xml file.")
else
concat("'",
       string-join(local:list-test-cases($fotsPath, $testSetPrefixes, $dependency),"', '"),
       "'")

(:
local:exec(
            ("-e", "mode:=run-test-case",
             "-e", concat("testSetName:=",  "misc-HigherOrderFunctions"),
             "-e", concat("testCaseName:=", "xqhof2")
            )
          )
:)

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

(:~
 : Run every test case that matches a given criterion in a different process
 : and report every test case that HANGS or ends in a SEG FAULT.
 :
 : @author Sorin Nasoi
 :)

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

declare namespace ann =
  "http://www.zorba-xquery.com/annotations";
declare namespace fots =
  "http://www.w3.org/2010/09/qt-fots-catalog";

declare variable $pathFOTS as xs:string external :="";

(:~
 : Return the exit-code after invoking the FOTS driver with given $params.
 :
 : @param $params additional params.
 : @return the exit-code returned by the invoked application.
 :)
declare %ann:sequential function local:exec(
  $params as xs:string*
) as xs:string {
  variable $pathZorba := normalize-space(xs:string(proc:exec("pwd")/proc:stdout));

  proc:exec(string-join(($pathZorba, "/zorba"),''),
            ("-f", "-q", concat($pathZorba, "/../../test/fots_driver/cli.xq"),
             "-e", concat("fotsPath:=", $pathFOTS),
             "-e", "verbose:=false",
             $params
            )
           )/proc:exit-code
};

declare %private %ann:sequential function local:invoke-exec(
  $testSets   as element(fots:test-set)*,
  $baseUri    as xs:anyURI,
  $dependency as xs:string*
) as xs:string* {
  for $testSet in $testSets
    let $testSetDoc := doc(resolve-uri($testSet/@file, $baseUri))
    let $testSetName := xs:string($testSet/@name)
    let $testCaseNames as xs:string* := d:list-test-cases($testSetDoc,
                                                          $dependency)
    return
      for $testCaseName in $testCaseNames
      let $exit-code :=
          trace(local:exec(("-e", "mode:=run-test-case",
                      "-e", concat("testSetName:=",  $testSetName),
                      "-e", concat("testCaseName:=", trace($testCaseName, "processing test case :"))
                     )
                    ),"exit-code")
      where (xs:integer($exit-code) gt xs:integer(134))
      return $testCaseName
};

(:~
 : Return the name of the test cases that return an exit-code > 128.
 :
 : @param $fotsPath path to the FOTS catalog file.
 : @param $testSetPrefixes name/criteria for the test sets.
 : @param $dependency defined dependency.
 : @return the exit-codes returned by the invoked applications.
 :)
declare %ann:sequential function local:list-test-cases(
  $fotsPath         as xs:string,
  $testSetPrefixes  as xs:string*,
  $dependency       as xs:string*
) as xs:string* {
  variable  $doc := doc(resolve-uri($fotsPath)),
            $baseUri:= resolve-uri(util:parent-folder($fotsPath));

  if(string-join($testSetPrefixes,'') = '')
  then local:invoke-exec($doc/fots:catalog/fots:test-set,
                         $baseUri,
                         $dependency)
  else
    for $prefix in $testSetPrefixes
    return
      local:invoke-exec($doc/fots:catalog/fots:test-set[starts-with(@name, $prefix)],
                        $baseUri,
                        $dependency)
};

if( $pathFOTS = '')
then fn:error($fots-err:errNA,
              "please set the external variable $pathFOTS to point to FOTS catalog.xml file.")
else
concat("'",
       string-join(local:list-test-cases($pathFOTS, '', 'higherOrderFunctions'),"', '"),
       "'")

(:
local:exec(
            ("-e", "mode:=run-test-case",
             "-e", concat("testSetName:=",  "misc-HigherOrderFunctions"),
             "-e", concat("testCaseName:=", "xqhof2")
            )
          )
:)


Improved reporting of errors through the webapp

(:~
 : DITA for Small Teams
 :
 : Link management application RESTXQ implementation.
 :
 : Copyright (c) 2015 DITA for Small Teams (dita-for-small-teams.org)
 :
 : See the BaseX RESTXQ documentation for details on how the RESTXQ
 : mechanism works.
 :
 :)
module namespace page = 'http://basex.org/modules/web-page';

import module namespace html = 'dba/html' at 'dba/modules/html.xqm';
import module namespace bxutil="http://dita-for-small-teams.org/xquery/modules/basex-utils";
import module namespace lmutil="http://dita-for-small-teams.org/xquery/modules/linkmgmt-utils";
import module namespace df="http://dita-for-small-teams.org/xquery/modules/dita-utils";
import module namespace linkmgr='http://basex.org/modules/linkmgr' at "linkmgrViews.xqm";
import module namespace lmm="http://dita-for-small-teams.org/xquery/modules/linkmgr-model";
import module namespace lmc="http://dita-for-small-teams.org/xquery/modules/linkmgr-controller";
import module namespace dfstcnst="http://dita-for-small-teams.org/xquery/modules/dfst-constants";

(:~
 : This function generates the welcome page.
 : @return HTML page
 :)
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()
  as element(Q{http://www.w3.org/1999/xhtml}html)
{

      <title>DITA for Small Teams Link Manager</title>
      <link rel="stylesheet" type="text/css" href="static/style.css"/>

www.dita-for-small-teams.org

DITA for Small Teams Link Manager

Repositories and Branches

                Repository
                Branches

            {page:listReposAndBranches()}

};

declare
  %rest:path("/repo/{$repo}/{$branch}")
  %rest:query-param("infoMessage",    "{$infoMessage}")
  %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:branchMainView($repo as xs:string, $branch as xs:string, $infoMessage as xs:string?)
  as element(Q{http://www.w3.org/1999/xhtml}html)
{

      <title>DFST {$repo}/{$branch}</title>
      <link rel="stylesheet" type="text/css" href="/static/style.css"/>

www.dita-for-small-teams.org

Git repo {$repo}/{$branch}

      [Update Link Management Indexes]
      [List all links]
      { if ($infoMessage and $infoMessage != '')
           then
{$infoMessage}

           else ()
      }

DITA Maps

                Title
                Path
                Root Map
                Actions

            { if (false())
                 then
No maps found

                 else page:listMapsInBranch($repo, $branch)
            }

DITA Topics

                Title
                Path
                Actions

            { if (false())
                 then
No maps found

                 else page:listTopicsInBranch($repo, $branch)
            }

};

(:~
 : List the databases that represent git repositories and branches
 : within those repositories.
 :
 : Result is a set of HTML table rows.
 :)
 declare function page:listReposAndBranches() as element()* {

    (: Get the repository info as an XML structure :)
    let $repos := bxutil:getGitRepositoryInfos()
    for $repo in $repos (: Sequence of <repo> elements :)
        let $branch := $repo/branch[1]
        let $path := concat($repo/@name, '/', $branch/@name)
        let $target := concat($repo/@name, '_', $branch/@name)
        return (

         {string($repo/@name)}
         {string($branch/@name)}

,
        for $branch in $repo/branch[position() gt 1]
            let $path := concat($repo/@name, '/', $branch/@name)
            let $target := concat($repo/@name, '_', $branch/@name)
            return

               {string($branch/@name)}

        )
        (:

      some repo
      master

,

      develop

    :)
 };

(:~
 : List the DITA maps within the specified branch of the specified repository.
 :
 : Result is a set of HTML table rows.
 :)
 declare function page:listMapsInBranch($repo as xs:string, $branch as xs:string) as element()* {
    let $dbName := bxutil:getDbNameForRepoAndBranch($repo, $branch)
    let $maps := df:getMaps($dbName)
    for $map in $maps
      let $docURI := document-uri($map)
      return

        {df:getTitleText($map/*)}
        {linkmgr:makeLinkToDocSource(document-uri(root($map)))}
        {
          let $resolvedMap as element()? := lmutil:getResolvedMapForMap($map/*)
          return if (not($resolvedMap))
             then "?"
             else if (lmc:isRootMap($map))
                     then '✓'
                     else ''
        }

        [{lmutil:linkToTarget('Navigation Tree', concat('/linkmgr/navtreeView/', document-uri($map)),
         'navtree')}]
        [{lmutil:linkToTarget('Map Tree', concat('/linkmgr/maptreeView/', document-uri($map)), 'maptree')}]
        [{lmutil:linkToTarget('Dependencies', concat('/linkmgr/dependencyView/', document-uri($map)), 'dependencies')}]
        [{lmutil:linkToTarget('Reltables', concat('/linkmgr/reltableView/', document-uri($map)), 'reltables')}]
        [{lmutil:linkToTarget('Key Spaces', concat('/linkmgr/keyspaceView/', document-uri($map)), 'keyspaces')}]
        [{lmutil:linkToTarget('Preview', concat('/linkmgr/docview/', document-uri($map), '/preview'), 'preview')}]
        [{lmutil:linkToTarget('Where Used?', concat('/linkmgr/whereUsedView/', $docURI), 'whereused')}]

 };

(:~
 : List the DITA topics within the specified branch of the specified repository.
 :
 : Result is a set of HTML table rows.
 :)
 declare function page:listTopicsInBranch($repo as xs:string, $branch as xs:string) as element()* {
    let $dbName := bxutil:getDbNameForRepoAndBranch($repo, $branch)
    let $topics := df:getTopicDocs($dbName)
    for $topic in $topics
      let $docURI := document-uri($topic)
      return

        {df:getTitleText($topic/*)}
        {linkmgr:makeLinkToDocSource(document-uri(root($topic)))}
        [{lmutil:linkToTarget('Where Used?', concat('/linkmgr/whereUsedView/', $docURI), 'whereused')}]
            [{lmutil:linkToTarget('Dependencies', concat('/linkmgr/dependencyView/', $docURI), 'dependencies')}]
            [{lmutil:linkToTarget('Preview', concat('/linkmgr/docview/', $docURI, '/preview'), 'preview')}]

 };

(:~
 : This function returns an XML response message.
 : @param $world  string to be included in the response
 : @return response element
 :)
declare
  %rest:path("/linkmgr")
  %rest:GET
  function page:linkmgr(
    )
    as element(response)
{
  <response>
    <title>Linkmgr</title>
    <time>Link manager response: The current time is: { current-time() }</time>
  </response>
};

(: REST API to trigger creation or update of link management indexes. :)
declare
  %updating
  %rest:path("/repo/{$repo}/{$branch}/updateLinkManagementIndexes")
  %rest:GET
  %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:updateLinkManagementIndexes($repo as xs:string, $branch as xs:string) {

  let $contentDbName := bxutil:getDbNameForRepoAndBranch($repo, $branch)
  let $metadataDbName := bxutil:getMetadataDbNameForRepoAndBranch($repo, $branch)
  let $logID := "lmindex-update-log"

  return db:output(web:redirect(concat("/repo/", $repo, "/", $branch, "/updateLMIStage1"),
                                 map { 'contentDbName' : $contentDbName,
                                       'metadataDbName' : $metadataDbName,
                                       'logID' : $logID
                                     }))
};

(: Link management index stage 1: Clear the database and construct all
 : direct link resource records. On return, redirects to stage 2.
 :)
declare
  %updating
  %rest:path("/repo/{$repo}/{$branch}/updateLMIStage1")
  %rest:query-param("contentDbName",  "{$contentDbName}")
  %rest:query-param("metadataDbName", "{$metadataDbName}")
  %rest:query-param("logID",          "{$logID}")
  %rest:GET
  %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:updateStage1($repo as xs:string,
                             $branch as xs:string,
                             $contentDbName as xs:string,
                             $metadataDbName as xs:string,
                             $logID as xs:string) {

      try {
          lmc:updateLinkManagementIndexesStage1($contentDbName, $metadataDbName, $logID),
          db:output(web:redirect(concat("/repo/", $repo, "/", $branch, "/updateLMIStage2"),
                                 map { 'contentDbName' : $contentDbName,
                                       'metadataDbName' : $metadataDbName,
                                       'logID' : $logID
                                     }))
      } catch * {
      (: FIXME: Format the parts of the error message in a useful way :)
          db:output(web:redirect("/error",
                                 map { 'contentDbName' : $contentDbName,
                                       'metadataDbName' : $metadataDbName,
                                       'description' : $err:description,
                                       'module' : $err:module,
                                       'line-number' : $err:line-number,
                                       'column-number' : $err:column-number,
                                       'additional' : $err:additional,
                                       'logID' : $logID

                                     }))
      }

};

(:~
 : Link management index update stage 2: Construct key spaces.
 : On return redirects to stage 3.
 :)
declare
  %updating
  %rest:path("/repo/{$repo}/{$branch}/updateLMIStage2")
  %rest:query-param("contentDbName",  "{$contentDbName}")
  %rest:query-param("metadataDbName", "{$metadataDbName}")
  %rest:query-param("logID",          "{$logID}")
  %rest:GET
  %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:updateStage2($repo as xs:string,
                             $branch as xs:string,
                             $contentDbName as xs:string,
                             $metadataDbName as xs:string,
                             $logID as xs:string
                             ) {
   try {

      lmc:updateLinkManagementIndexesStage2(
         $contentDbName,
         $metadataDbName,
         $logID),
      db:output(web:redirect(concat("/repo/", $repo, "/", $branch, "/updateLMIStage3"),
                                       map { 'contentDbName' : $contentDbName,
                                             'metadataDbName' : $metadataDbName,
                                             'logID' : $logID
                                           }))
    } catch * {
          db:output(web:redirect("/error",
                                 map { 'contentDbName' : $contentDbName,
                                       'metadataDbName' : $metadataDbName,
                                       'description' : $err:description,
                                       'module' : $err:module,
                                       'line-number' : $err:line-number,
                                       'column-number' : $err:column-number,
                                       'additional' : $err:additional,
                                       'logID' : $logID

                                     }))
    }

};

(:~
 : Link management index update stage 3: Construct indirect link
 : where-used records.
 : On return redirects back to the page for the repo and branch.
 :)
declare
  %updating
  %rest:path("/repo/{$repo}/{$branch}/updateLMIStage3")
  %rest:query-param("contentDbName",  "{$contentDbName}")
  %rest:query-param("metadataDbName", "{$metadataDbName}")
  %rest:query-param("logID",          "{$logID}")
  %rest:GET
  %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:updateStage3($repo as xs:string,
                             $branch as xs:string,
                             $contentDbName as xs:string,
                             $metadataDbName as xs:string,
                             $logID as xs:string
                             ) {

    try {

      (lmc:updateLinkManagementIndexesStage3($contentDbName, $metadataDbName, $logID),
       db:output("Link management database updated"))
      (:,
      db:output(web:redirect(concat("/repo/", $repo, "/", $branch),
                                       map { 'infoMessage' : 'Link management indexes updated'
                                           }))
                                           :)

    } catch * {
          db:output(web:redirect("/error",
                                 map { 'contentDbName' : $contentDbName,
                                       'metadataDbName' : $metadataDbName,
                                       'description' : $err:description,
                                       'module' : $err:module,
                                       'line-number' : $err:line-number,
                                       'column-number' : $err:column-number,
                                       'additional' : $err:additional,
                                       'logID' : $logID

                                     }))
    }

};

(:~
 : Generic error report page. This is a placeholder in advance of more sophisticated
 : error reporting infrastructure.
 :)
declare
  %rest:path("/error")
  %rest:query-param("contentDbName",    "{$contentDbName}")
  %rest:query-param("metadataDbName", "{$metadataDbName}")
  %rest:query-param("description", "{$description}")
  %rest:query-param("module", "{$module}")
  %rest:query-param("line-number", "{$line-number}")
  %rest:query-param("column-number", "{$column-number}")
  %rest:query-param("additional", "{$additional}")
  %rest:GET
  %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:reportError($contentDbName as xs:string?,
                            $metadataDbName as xs:string?,
                            $description as xs:string?,
                            $module as xs:string?,
                            $line-number as xs:string?,
                            $column-number as xs:string?,
                            $additional as xs:string?
                           ) {

     <title>Error</title>

Error

Error

content database: {$contentDbName}

Metadata database: {$metadataDbName}

{$module} [{$line-number}:{$column-number}]: {$description}

Trace:

      {
      $additional
      }

};

declare function page:formatLogAsHtml($log) as element() {

  {for $entry in $log/*
       return concat('[', upper-case(name($entry)), ']', ' ', string($entry), '
')
  }

};

(:~
 : DITA for Small Teams
 :
 : Link management application RESTXQ implementation.
 :
 : Copyright (c) 2015 DITA for Small Teams (dita-for-small-teams.org)
 :
 : See the BaseX RESTXQ documentation for details on how the RESTXQ
 : mechanism works.
 :
 :)
module namespace page = 'http://basex.org/modules/web-page';

import module namespace html = 'dba/html' at 'dba/modules/html.xqm';
import module namespace bxutil="http://dita-for-small-teams.org/xquery/modules/basex-utils";
import module namespace lmutil="http://dita-for-small-teams.org/xquery/modules/linkmgmt-utils";
import module namespace df="http://dita-for-small-teams.org/xquery/modules/dita-utils";
import module namespace linkmgr='http://basex.org/modules/linkmgr' at "linkmgrViews.xqm";
import module namespace lmm="http://dita-for-small-teams.org/xquery/modules/linkmgr-model";
import module namespace lmc="http://dita-for-small-teams.org/xquery/modules/linkmgr-controller";
import module namespace dfstcnst="http://dita-for-small-teams.org/xquery/modules/dfst-constants";

(:~
 : This function generates the welcome page.
 : @return HTML page
 :)
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()
  as element(Q{http://www.w3.org/1999/xhtml}html)
{

      <title>DITA for Small Teams Link Manager</title>
      <link rel="stylesheet" type="text/css" href="static/style.css"/>

www.dita-for-small-teams.org

DITA for Small Teams Link Manager

Repositories and Branches

                Repository
                Branches

            {page:listReposAndBranches()}

};

declare
  %rest:path("/repo/{$repo}/{$branch}")
  %rest:query-param("infoMessage",    "{$infoMessage}")
  %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:branchMainView($repo as xs:string, $branch as xs:string, $infoMessage as xs:string?)
  as element(Q{http://www.w3.org/1999/xhtml}html)
{

      <title>DFST {$repo}/{$branch}</title>
      <link rel="stylesheet" type="text/css" href="/static/style.css"/>

www.dita-for-small-teams.org

Git repo {$repo}/{$branch}

      [Update Link Management Indexes]
      [List all links]
      { if ($infoMessage and $infoMessage != '')
           then
{$infoMessage}

           else ()
      }

DITA Maps

                Title
                Path
                Root Map
                Actions

            { if (false())
                 then
No maps found

                 else page:listMapsInBranch($repo, $branch)
            }

DITA Topics

                Title
                Path
                Actions

            { if (false())
                 then
No maps found

                 else page:listTopicsInBranch($repo, $branch)
            }

};

(:~
 : List the databases that represent git repositories and branches
 : within those repositories.
 :
 : Result is a set of HTML table rows.
 :)
 declare function page:listReposAndBranches() as element()* {

    (: Get the repository info as an XML structure :)
    let $repos := bxutil:getGitRepositoryInfos()
    for $repo in $repos (: Sequence of <repo> elements :)
        let $branch := $repo/branch[1]
        let $path := concat($repo/@name, '/', $branch/@name)
        let $target := concat($repo/@name, '_', $branch/@name)
        return (

         {string($repo/@name)}
         {string($branch/@name)}

,
        for $branch in $repo/branch[position() gt 1]
            let $path := concat($repo/@name, '/', $branch/@name)
            let $target := concat($repo/@name, '_', $branch/@name)
            return

               {string($branch/@name)}

        )
        (:

      some repo
      master

,

      develop

    :)
 };

(:~
 : List the DITA maps within the specified branch of the specified repository.
 :
 : Result is a set of HTML table rows.
 :)
 declare function page:listMapsInBranch($repo as xs:string, $branch as xs:string) as element()* {
    let $dbName := bxutil:getDbNameForRepoAndBranch($repo, $branch)
    let $maps := df:getMaps($dbName)
    for $map in $maps
      let $docURI := document-uri($map)
      return

        {df:getTitleText($map/*)}
        {linkmgr:makeLinkToDocSource(document-uri(root($map)))}
        {
          let $resolvedMap as element()? := lmutil:getResolvedMapForMap($map/*)
          return if (not($resolvedMap))
             then "?"
             else if (lmc:isRootMap($map))
                     then '✓'
                     else ''
        }

        [{lmutil:linkToTarget('Navigation Tree', concat('/linkmgr/navtreeView/', document-uri($map)),
         'navtree')}]
        [{lmutil:linkToTarget('Map Tree', concat('/linkmgr/maptreeView/', document-uri($map)), 'maptree')}]
        [{lmutil:linkToTarget('Dependencies', concat('/linkmgr/dependencyView/', document-uri($map)), 'dependencies')}]
        [{lmutil:linkToTarget('Reltables', concat('/linkmgr/reltableView/', document-uri($map)), 'reltables')}]
        [{lmutil:linkToTarget('Key Spaces', concat('/linkmgr/keyspaceView/', document-uri($map)), 'keyspaces')}]
        [{lmutil:linkToTarget('Preview', concat('/linkmgr/docview/', document-uri($map), '/preview'), 'preview')}]
        [{lmutil:linkToTarget('Where Used?', concat('/linkmgr/whereUsedView/', $docURI), 'whereused')}]

 };

(:~
 : List the DITA topics within the specified branch of the specified repository.
 :
 : Result is a set of HTML table rows.
 :)
 declare function page:listTopicsInBranch($repo as xs:string, $branch as xs:string) as element()* {
    let $dbName := bxutil:getDbNameForRepoAndBranch($repo, $branch)
    let $topics := df:getTopicDocs($dbName)
    for $topic in $topics
      let $docURI := document-uri($topic)
      return

        {df:getTitleText($topic/*)}
        {linkmgr:makeLinkToDocSource(document-uri(root($topic)))}
        [{lmutil:linkToTarget('Where Used?', concat('/linkmgr/whereUsedView/', $docURI), 'whereused')}]
            [{lmutil:linkToTarget('Dependencies', concat('/linkmgr/dependencyView/', $docURI), 'dependencies')}]
        [{lmutil:linkToTarget('Preview', concat('/linkmgr/docview/', $docURI, '/preview'), 'preview')}]

 };

(:~
 : This function returns an XML response message.
 : @param $world  string to be included in the response
 : @return response element
 :)
declare
  %rest:path("/linkmgr")
  %rest:GET
  function page:linkmgr(
    )
    as element(response)
{
  <response>
    <title>Linkmgr</title>
    <time>Link manager response: The current time is: { current-time() }</time>
  </response>
};

(: REST API to trigger creation or update of link management indexes. :)
declare
  %updating
  %rest:path("/repo/{$repo}/{$branch}/updateLinkManagementIndexes")
  %rest:GET
  %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:updateLinkManagementIndexes($repo as xs:string, $branch as xs:string) {

  let $contentDbName := bxutil:getDbNameForRepoAndBranch($repo, $branch)
  let $metadataDbName := bxutil:getMetadataDbNameForRepoAndBranch($repo, $branch)
  let $logID := "lmindex-update-log"

  return db:output(web:redirect(concat("/repo/", $repo, "/", $branch, "/updateLMIStage1"),
                                 map { 'contentDbName' : $contentDbName,
                                       'metadataDbName' : $metadataDbName,
                                       'logID' : $logID
                                     }))
};

(: Link management index stage 1: Clear the database and construct all
 : direct link resource records. On return, redirects to stage 2.
 :)
declare
  %updating
  %rest:path("/repo/{$repo}/{$branch}/updateLMIStage1")
  %rest:query-param("contentDbName",  "{$contentDbName}")
  %rest:query-param("metadataDbName", "{$metadataDbName}")
  %rest:query-param("logID",          "{$logID}")
  %rest:GET
  %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:updateStage1($repo as xs:string,
                             $branch as xs:string,
                             $contentDbName as xs:string,
                             $metadataDbName as xs:string,
                             $logID as xs:string) {

      try {
          lmc:updateLinkManagementIndexesStage1($contentDbName, $metadataDbName, $logID),
          db:output(web:redirect(concat("/repo/", $repo, "/", $branch, "/updateLMIStage2"),
                                 map { 'contentDbName' : $contentDbName,
                                       'metadataDbName' : $metadataDbName,
                                       'logID' : $logID
                                     }))
      } catch * {
          db:output(web:redirect("/error",
                                 map { 'contentDbName' : $contentDbName,
                                       'metadataDbName' : $metadataDbName,
                                       'error' : $err:description,
                                       'logID' : $logID

                                     }))
      }

};

(:~
 : Link management index update stage 2: Construct key spaces.
 : On return redirects to stage 3.
 :)
declare
  %updating
  %rest:path("/repo/{$repo}/{$branch}/updateLMIStage2")
  %rest:query-param("contentDbName",  "{$contentDbName}")
  %rest:query-param("metadataDbName", "{$metadataDbName}")
  %rest:query-param("logID",          "{$logID}")
  %rest:GET
  %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:updateStage2($repo as xs:string,
                             $branch as xs:string,
                             $contentDbName as xs:string,
                             $metadataDbName as xs:string,
                             $logID as xs:string
                             ) {
   try {

      lmc:updateLinkManagementIndexesStage2(
         $contentDbName,
         $metadataDbName,
         $logID),
      db:output(web:redirect(concat("/repo/", $repo, "/", $branch, "/updateLMIStage3"),
                                       map { 'contentDbName' : $contentDbName,
                                             'metadataDbName' : $metadataDbName,
                                             'logID' : $logID
                                           }))
    } catch * {
          db:output(web:redirect("/error",
                                 map { 'contentDbName' : $contentDbName,
                                       'metadataDbName' : $metadataDbName,
                                       'error' : $err:description,
                                       'logID' : $logID

                                     }))
    }

};

(:~
 : Link management index update stage 3: Construct indirect link
 : where-used records.
 : On return redirects back to the page for the repo and branch.
 :)
declare
  %updating
  %rest:path("/repo/{$repo}/{$branch}/updateLMIStage3")
  %rest:query-param("contentDbName",  "{$contentDbName}")
  %rest:query-param("metadataDbName", "{$metadataDbName}")
  %rest:query-param("logID",          "{$logID}")
  %rest:GET
  %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:updateStage3($repo as xs:string,
                             $branch as xs:string,
                             $contentDbName as xs:string,
                             $metadataDbName as xs:string,
                             $logID as xs:string
                             ) {

    try {

      (lmc:updateLinkManagementIndexesStage3($contentDbName, $metadataDbName, $logID),
       db:output("Link management database updated"))
      (:,
      db:output(web:redirect(concat("/repo/", $repo, "/", $branch),
                                       map { 'infoMessage' : 'Link management indexes updated'
                                           }))
                                           :)

    } catch * {
          db:output(web:redirect("/error",
                                 map { 'contentDbName' : $contentDbName,
                                       'metadataDbName' : $metadataDbName,
                                       'error' : $err:description,
                                       'logID' : $logID

                                     }))
    }

};

(:~
 : Generic error report page. This is a placeholder in advance of more sophisticated
 : error reporting infrastructure.
 :)
declare
  %rest:path("/error")
  %rest:query-param("contentDbName",    "{$contentDbName}")
  %rest:query-param("metadataDbName", "{$metadataDbName}")
  %rest:query-param("error", "{$error}")
  %rest:GET
  %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:reportError($contentDbName as xs:string?,
                            $metadataDbName as xs:string?,
                            $error as xs:string?
                           ) {

     <title>Error</title>

Error

Error

content database: {$contentDbName}

Metadata database: {$metadataDbName}

Error message: {$error}

};

declare function page:formatLogAsHtml($log) as element() {

  {for $entry in $log/*
       return concat('[', upper-case(name($entry)), ']', ' ', string($entry), '
')
  }

};


- XQDoc fixes

- XQDoc fixes

jsoniq 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,
 : chapter 8 (Function Library).
 : JSONiq extends the XQuery specification to also deal with JSON data natively.
 :
 : @see http://jsoniq.org/
 : @author Ghislain Fourny
 : @project JSONiq/Function Library
 :)
module namespace libjn = "http://jsoniq.org/function-library";

import module namespace jn = "http://jsoniq.org/functions";

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

(:~
 : This function dynamically builds an object, like the {||} syntax, except that
 : it does not throw an error upon pair collision. Instead, it accumulates them
 : into an array, if more than one.
 :
 : @param $items A sequence of items, the objects of which are going to be
 :        accumulated into a single object.
 : @return The accumulated object.
 :)
declare function libjn:accumulate($items as item*) as object
{
  {[ $items ]}
};

(:~
 : This function returns all arrays contained at any depth within a sequence of items.
 :
 : @param $items A sequence of items.
 : @return The descendant arrays of the input sequence.
 :)
declare function libjn:descendant-arrays($items as item*) as array*
{
  for $i in $items
  return libjn:descendant-arrays-priv($i)
};

(:~
 : Helper function for libjn:descendant-arrays()
 :
 : @param $i An item
 : @return The descendant arrays of the item
 :)
declare %private function libjn:descendant-arrays-priv($i as item) as array*
{
  typeswitch ($i)

  case object return
    for $v in libjn:values($i)
    where $v instance of json-item
    return libjn:descendant-arrays-priv($v)

  case array return
    (
    $i,
    for $v in $i[]
    where $v instance of json-item
    return libjn:descendant-arrays-priv($v)
    )

  default return
    ()
};

(:~
 : This function returns all objects contained at any depth within a sequence of items.
 :
 : @param $items A sequence of items.
 : @return The descendant objects of the input sequence.
 :)
declare function libjn:descendant-objects($items as item*) as object*
{
  for $i in $items
  return libjn:descendant-objects-priv($i)
};

(:~
 : Helper function for libjn:descendant-objects()
 :
 : @param $i An item
 : @return The descendant objects of the item
 :)
declare %private function libjn:descendant-objects-priv($i as item) as object*
{
  if ($i instance of object)
  then
    (
      $i,
      for $v in libjn:values($i)
      where $v instance of json-item
      return libjn:descendant-objects-priv($v)
    )
  else if ($i instance of array)
  then
    (
      for $v in $i[]
      where $v instance of json-item
      return libjn:descendant-objects-priv($v)
    )
  else
    ()
};

(:~
 : This function returns all pairs contained at any depth within an sequence of items.
 :
 : @param $o An object.
 : @return All direct and indirect descendant pairs.
 :)
declare function libjn:descendant-pairs($items as item*) as object*
{
  for $i in $items
  return libjn:descendant-pairs-priv($i)
};

(:~
 : Helper function for libjn:descendant-pairs()
 :
 : @param $i An item
 : @return The descendant pairs of the item
 :)
declare function libjn:descendant-pairs-priv($i as item) as object*
{
  typeswitch ($i)

  case $o as object return
    for $k in jn:keys($o)
    return
      (
      { $k : $o($k) },
      libjn:descendant-pairs-priv($o($k))
      )

  case $a as array return
    for $i in $a[]
    where $i instance of json-item
    return libjn:descendant-pairs-priv($i)

  default return
    ()
};

(:~
 : This function returns the intersection of the objects contained in the
 : given sequence of items, aggregating values corresponding to the same key
 : into an array.
 :
 : @param $items A sequence of items.
 : @return The insersection of the objects contained in $items.
 :)
declare function libjn:intersect($items as item*) as object
{
  {|
    let $objects := $items[$$ instance of object]
    for $key in keys(head($objects))
    where every $o in tail($objects)
          satisfies exists(index-of(keys($o), $key))
    return { $key : $objects.$key }
  |}
};

(:~
 : This functions returns all values of all objects contained in a sequence of items.
 :
 : @param $items A sequence of items.
 : @return The values inside the objects of the sequence.
 :)
declare function libjn:values($items as item*) as item*
{
  for $i in $items
  for $k in jn:keys($i)
  return ($i treat as object)($k)
};

jsoniq 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,
 : chapter 8 (Function Library). JSONiq extends
 : the XQuery specification to also deal with JSON data natively. See
 :
 :     http://jsoniq.org/
 :
 : for details.
 :
 : @author Ghislain Fourny
 : @project JSONiq/Function Library
 :)
module namespace libjn = "http://jsoniq.org/function-library";

import module namespace jn = "http://jsoniq.org/functions";

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

(:~
 : This function dynamically builds an object, like the {||} syntax, except that
 : it does not throw an error upon pair collision. Instead, it accumulates them
 : into an array, if more than one.
 :
 : @param $items A sequence of items, the objects of which are going to be
 :        accumulated into a single object.
 : @return The accumulated object.
 :)
declare function libjn:accumulate($items as item*) as object
{
  {[ $items ]}
};

(:~
 : This function returns all arrays contained at any depth within a sequence of items.
 :
 : @param $items A sequence of items.
 : @return The descendant arrays of the input sequence.
 :)
declare function libjn:descendant-arrays($items as item*) as array*
{
  for $i in $items
  return libjn:descendant-arrays-priv($i)
};

(:~
 : Helper function for libjn:descendant-arrays()
 :
 : @param $i An item
 : @return The descendant arrays of the item
 :)
declare %private function libjn:descendant-arrays-priv($i as item) as array*
{
  typeswitch ($i)

  case object return
    for $v in libjn:values($i)
    where $v instance of json-item
    return libjn:descendant-arrays-priv($v)

  case array return
    (
    $i,
    for $v in $i[]
    where $v instance of json-item
    return libjn:descendant-arrays-priv($v)
    )

  default return
    ()
};

(:~
 : This function returns all objects contained at any depth within a sequence of items.
 :
 : @param $items A sequence of items.
 : @return The descendant objects of the input sequence.
 :)
declare function libjn:descendant-objects($items as item*) as object*
{
  for $i in $items
  return libjn:descendant-objects-priv($i)
};

(:~
 : Helper function for libjn:descendant-objects()
 :
 : @param $i An item
 : @return The descendant objects of the item
 :)
declare %private function libjn:descendant-objects-priv($i as item) as object*
{
  if ($i instance of object)
  then
    (
      $i,
      for $v in libjn:values($i)
      where $v instance of json-item
      return libjn:descendant-objects-priv($v)
    )
  else if ($i instance of array)
  then
    (
      for $v in $i[]
      where $v instance of json-item
      return libjn:descendant-objects-priv($v)
    )
  else
    ()
};

(:~
 : This function returns all pairs contained at any depth within an sequence of items.
 :
 : @param $o An object.
 : @return All direct and indirect descendant pairs.
 :)
declare function libjn:descendant-pairs($items as item*) as object*
{
  for $i in $items
  return libjn:descendant-pairs-priv($i)
};

(:~
 : Helper function for libjn:descendant-pairs()
 :
 : @param $i An item
 : @return The descendant pairs of the item
 :)
declare function libjn:descendant-pairs-priv($i as item) as object*
{
  typeswitch ($i)

  case $o as object return
    for $k in jn:keys($o)
    return
      (
      { $k : $o($k) },
      libjn:descendant-pairs-priv($o($k))
      )

  case $a as array return
    for $i in $a[]
    where $i instance of json-item
    return libjn:descendant-pairs-priv($i)

  default return
    ()
};

(:~
 : This function returns the intersection of the objects contained in the
 : given sequence of items, aggregating values corresponding to the same key
 : into an array.
 :
 : @param $items A sequence of items.
 : @return The insersection of the objects contained in $items.
 :)
declare function libjn:intersect($items as item*) as object
{
  {|
    let $objects := $items[$$ instance of object]
    for $key in keys(head($objects))
    where every $o in tail($objects)
          satisfies exists(index-of(keys($o), $key))
    return { $key : $objects.$key }
  |}
};

(:~
 : This functions returns all values of all objects contained in a sequence of items.
 :
 : @param $items A sequence of items.
 : @return The values inside the objects of the sequence.
 :)
declare function libjn:values($items as item*) as item*
{
  for $i in $items
  for $k in jn:keys($i)
  return ($i treat as object)($k)
};


fix to semantics extension

fix to semantics extension

git-svn-id: 2087087167f935058d19856144d26af79f295c86@146864 62cac252-8da6-4816-9e9d-6dc37b19578c

xquery version "1.0-ml";

(: Copyright 2011-2013 MarkLogic Corporation.  All Rights Reserved. :)

module namespace graph = "http://marklogic.com/rest-api/resource/graph";

(: WARNING:
    The semantics model library may change in a future release without notice.
    Do not call the functions of the semantics model library in your own code.
    :)
import module namespace semmod = "http://marklogic.com/rest-api/models/semantics"
    at "/MarkLogic/rest-api/models/semantics-model.xqy";

declare namespace search = "http://marklogic.com/appservices/search";

declare default function namespace "http://www.w3.org/2005/xpath-functions";
declare option xdmp:mapping "false";

declare private function graph:rdf-mime-type(
    $header-name as xs:string,
    $format as xs:string
) as map:map
{
    let $headers := map:map()
    let $_ :=
        if ($format eq "nquad")
        then map:put($headers, "accept", "text/nquad")
        else if ($format eq "html")
             then map:put($headers, "accept", "text/html")
        else if ($format eq "json")
            then map:put($headers, "accept", "application/json")
        else if ($format eq "turtle")
             then map:put($headers, "accept", "text/turtle")
        else if ($format eq "xml")
             then map:put($headers, "accept", "application/rdf+xml")
        else if ($format eq "ntriples")
             then map:put($headers, "accept", "text/plain")
        else map:put($headers, "accept", "text/plain")
    return $headers
};

declare function graph:get(
    $context as map:map,
    $params  as map:map
) as document-node()*
{
    let $output-format := map:get($params, "output-format")
    let $headers := graph:rdf-mime-type("accept", $output-format)
    let $mime := map:put($context, "output-types", $output-format)
    return document {
    	semmod:graph-read($headers, $params)
    }
};

declare function graph:post(
    $context as map:map,
    $params  as map:map,
    $input   as document-node()*
) as document-node()?
{
    let $input-format := map:get($params, "input-format")
    let $headers := graph:rdf-mime-type("content-type", $input-format)
    let $insert := semmod:graph-insert($headers, $params, $input, ())
    let $mime := map:put($context, "output-types", "text/plain")
    return document { text { "OK" } }   (: hack to prevent java from not liking status code :)
};

declare function graph:put(
    $context as map:map,
    $params  as map:map,
    $input   as document-node()*
) as document-node()?
{
    let $input-format := map:get($params, "input-format")
    let $headers := graph:rdf-mime-type("content-type", $input-format)
    let $insert := semmod:graph-replace($headers, $params, $input, ())
    let $mime := map:put($context, "output-types", "text/plain")
    return document { text { "OK" } }   (: hack to prevent java from not liking status code :)
};

declare function graph:delete(
    $context as map:map,
    $params  as map:map
) as document-node()?
{
    let $insert := semmod:graph-delete(map:map(), $params, ())
    return ()
};

declare private function graph:prepare-params(
    $params as map:map
) as empty-sequence()
{
    let $views      := map:get($params,"view")
    let $start      := map:get($params,"start")
    let $pageLength := map:get($params,"pageLength")
    return (
        if ($views = "results") then ()
        else map:put($params,"view",($views,"results")),

        if (empty($start))
        then map:put($params,"start",xs:unsignedLong(1))
        else map:put($params,"start",xs:unsignedLong($start)),

        if (empty($pageLength))
        then map:put($params,"pageLength",xs:unsignedLong(10))
        else map:put($params,"pageLength",xs:unsignedLong($pageLength))
        )
};

xquery version "1.0-ml";

(: Copyright 2011-2013 MarkLogic Corporation.  All Rights Reserved. :)

module namespace graph = "http://marklogic.com/rest-api/resource/graph";

(: WARNING:
    The semantics model library may change in a future release without notice.
    Do not call the functions of the semantics model library in your own code.
    :)
import module namespace semmod = "http://marklogic.com/rest-api/models/semantics"
    at "/MarkLogic/rest-api/models/semantics-model.xqy";

declare namespace search = "http://marklogic.com/appservices/search";

declare default function namespace "http://www.w3.org/2005/xpath-functions";
declare option xdmp:mapping "false";

declare private function graph:rdf-mime-type(
    $header-name as xs:string,
    $format as xs:string
) as map:map
{
    let $headers := map:map()
    let $_ :=
        if ($format eq "nquad")
        then map:put($headers, "accept", "text/nquad")
        else if ($format eq "html")
             then map:put($headers, "accept", "text/html")
        else if ($format eq "json")
            then map:put($headers, "accept", "application/json")
        else if ($format eq "turtle")
             then map:put($headers, "accept", "text/turtle")
        else if ($format eq "xml")
             then map:put($headers, "accept", "application/rdf+xml")
        else if ($format eq "ntriples")
             then map:put($headers, "accept", "text/plain")
        else map:put($headers, "accept", "text/plain")
    return $headers
};

declare function graph:get(
    $context as map:map,
    $params  as map:map
) as document-node()*
{
    let $output-format := map:get($params, "output-format")
    let $headers := graph:rdf-mime-type("accept", $output-format)
    let $mime := map:put($context, "output-types", $output-format)
    return document {
    	semmod:graph-read($headers, $params)
    }
};

declare function graph:post(
    $context as map:map,
    $params  as map:map,
    $input   as document-node()*
) as document-node()?
{
    let $input-format := map:get($params, "input-format")
    let $headers := graph:rdf-mime-type("content-type", $input-format)
    let $insert := semmod:graph-insert($headers, $params, $input)
    let $mime := map:put($context, "output-types", "text/plain")
    return document { text { "OK" } }   (: hack to prevent java from not liking status code :)
};

declare function graph:put(
    $context as map:map,
    $params  as map:map,
    $input   as document-node()*
) as document-node()?
{
    let $input-format := map:get($params, "input-format")
    let $headers := graph:rdf-mime-type("content-type", $input-format)
    let $insert := semmod:graph-replace($headers, $params, $input)
    let $mime := map:put($context, "output-types", "text/plain")
    return document { text { "OK" } }   (: hack to prevent java from not liking status code :)
};

declare function graph:delete(
    $context as map:map,
    $params  as map:map
) as document-node()?
{
    let $insert := semmod:graph-delete(map:map(), $params)
    return ()
};

declare private function graph:prepare-params(
    $params as map:map
) as empty-sequence()
{
    let $views      := map:get($params,"view")
    let $start      := map:get($params,"start")
    let $pageLength := map:get($params,"pageLength")
    return (
        if ($views = "results") then ()
        else map:put($params,"view",($views,"results")),

        if (empty($start))
        then map:put($params,"start",xs:unsignedLong(1))
        else map:put($params,"start",xs:unsignedLong($start)),

        if (empty($pageLength))
        then map:put($params,"pageLength",xs:unsignedLong(10))
        else map:put($params,"pageLength",xs:unsignedLong($pageLength))
        )
};


Refined getting title for maps in resource use docs

(: =====================================================

   DITA Link Manager Model

   Model component of Model/View/Controller. Maintains
   the link management data.

   Provides queries to create and update resource use records,
   dependency indexes, etc.

   These queries are called whenever new content is added to
   given database.

   Author: W. Eliot Kimber

   Copyright (c) 2015 DITA For Small Teams
   Licensed under Apache License 2

   ===================================================== :)

module namespace lmm="http://dita-for-small-teams.org/xquery/modules/linkmgr-model";

declare namespace dfst="http://dita-for-small-teams.org";

import module namespace df="http://dita-for-small-teams.org/xquery/modules/dita-utils";
import module namespace bxutil="http://dita-for-small-teams.org/xquery/modules/basex-utils";
import module namespace lmutil="http://dita-for-small-teams.org/xquery/modules/linkmgmt-utils";
import module namespace dfstcnst="http://dita-for-small-teams.org/xquery/modules/dfst-constants";
import module namespace relpath="http://dita-for-small-teams.org/xquery/modules/relpath-utils";

(: Given an element with an @id value, construct the unique resource key for it.
   For a given element the resource ID is guaranteed to be unique within
   a snapshot (git commit).

   @param elem Element to get resource ID for. Must specify @id attribute.
   @returns Resource key string.

   The resource key is a combination of the absolute URI of the containing
   document, the element's @id attribute value, and other details TBD.

   Note that because DITA can only address elements with IDs this function
   only works for elements with @id attribute values.
 :)
declare function lmm:constructResourceKeyForElement($elem as element()) as xs:string {
  let $targetDocHash := hash:md5(document-uri(root($elem)))
  let $treepos := for $anc in ($elem/ancestor-or-self::*)
                      return string(count($anc | $anc/preceding-sibling::*))
  let $key := string-join($treepos, '.')
  return $targetDocHash || '^' || $key

};

(:~
 : Create or update the link management indexes for the specified repository
 :
 :)
declare %updating function lmm:updateLinkManagementIndexes($dbName as xs:string) {
   (: Query the database to find all links and for each link, record a resource use
      record in the database.

      Every addressible element has a "resource key" which combines the URI of
      the containing document and the element's @id value (only elements with
      @id attributes are addressible.

      The link management indexes are stored under the .dfst directory:

      .dfst/linkmgmt/where-used/

      For each document used there is a directory whose name is the hash
      of the document's URI (which will be unique within a branch.

    :)

    (: Need to handle both context-free links (direct URI references) and
       context-specific links (key references).

       For context-specific links have to process all the maps and construct
       resolved maps to serve as the resolution context. This could also
       be modeled as updating the key spaces but I think it makes more
       sense to have the maps be the focus, from which key spaces are derived,
       rather than making the key spaces the primary focus.

     :)

    let $logID := 'linkMgmtIndex' (: ID of the log to log messages to :)

    let $directLinks := lmutil:findAllDirectLinks($dbName)

    return (try {
        db:delete($dbName, $dfstcnst:where-used-dir)
    } catch * {
        (: FIXME: Log to log doc :)
        db:output(<error>Exception deleting where-used directory "{$dfstcnst:where-used-dir}": {$err:description}</error>)
    },

    (: Now create new resource use records for direct links :)

    lmm:createDirectLinkResourceRecords($dbName, $directLinks, $logID),

    (: Now create resolved maps and key space documents for each of the root maps.
     :)
    lmm:constructKeySpaces($dbName, $logID),

    (: Now create resource use records for all the indirect links: :)
    lmm:createIndirectLinkResourceRecords(
                $dbName,
                lmutil:findAllIndirectLinks($dbName),
                $logID)
    )

};

declare %updating function lmm:createDirectLinkResourceRecords(
        $dbName as xs:string,
        $directLinks as map(*)*,
        $logID as xs:string) {
    for $linkItem in $directLinks
       let $resolveResult := lmutil:resolveDirectLink($linkItem)
       return lmm:createOrUpdateResourceUseRecord(
                   $dbName,
                   $linkItem,
                   $resolveResult,
                   $logID)

};

declare %updating function lmm:createIndirectLinkResourceRecords(
                               $dbName as xs:string,
                               $indirectLinks,
                               $logID as xs:string) {
    for $linkItem in $indirectLinks
        let $resolveResult as map(*) := lmutil:resolveIndirectLink($linkItem)
        return lmm:createOrUpdateResourceUseRecord(
                   $dbName,
                   $linkItem,
                   $resolveResult,
                   $logID)
};

(: Given a link and the resolved result, creates a resource use record for each
   target the link resolved to.

 :)
declare %updating function lmm:createOrUpdateResourceUseRecord(
                              $dbName,
                              $linkItem as map(*),
                              $resolveResult as map(*),
                              $logID as xs:string) {
   let $resolveResult as map(*) := lmutil:resolveDirectLink($linkItem)
   let $targets := $resolveResult('target')
   for $target in $targets
       return lmm:createOrUpdateResourceUseRecordForLinkTarget(
                    $dbName,
                    $linkItem,
                    $target,
                    $logID)
};

(:
 : Creates a use record reflecting the use of the target element by the document
 : that contains the link.
 :
 : The use records are organized into directories, one directory per used document,
 : where the directory name is the MD5 hash of the document URI (just to make a
 : shorter name that won't have any problematic characters).
 :
 : Each use record document's filename is the MD5 hash of the linking document's
 : URI plus the MD5 hash of the linking element.
 :
 : Potential problem: the same element could, in theory, link to the same target in
 : two different ways: as a link and as a conref, although this case is extremely
 : unlikely. Really need a way to distinguish each abstract link represented by
 : a given DITA link-establishing element but the current implementation doesn't
 : provide that abstraction. It could through a refactor. Keeping it simple for now.
 :)
declare %updating function lmm:createOrUpdateResourceUseRecordForLinkTarget(
           $dbName as xs:string,
           $linkItem as map(*),
           $target as element(),
           $logID as xs:string) {
   let $targetDoc := root($target)
   let $targetDocHash := hash:md5(document-uri($targetDoc))
   let $containingDir := concat($dfstcnst:where-used-dir, '/', $targetDocHash, '/')
   let $reskey := lmm:constructResourceKeyForElement($target)
   let $recordFilename := concat('use-record_', $reskey, '.xml')
   let $link := $linkItem('link')
   let $format := if ($link/@format)
                     then string($link/@format)
                     else 'dita'
   let $scope := if ($link/@scope)
                     then string($link/@scope)
                     else 'local'
   (: df:getTitleForLinkElementContainer($link) :)
   let $useRecord :=
     <dfst:useRecord resourceKey="{$reskey}"
                     targetDoc="{document-uri($targetDoc)}"
                     usingDoc="{document-uri(root($link))}"
                     linkType="{df:getBaseLinkType($link)}"
                     linkClass="{string($link/@class)}"
                     format="{$format}"
                     scope="{$scope}"
     >
       <title>{if (df:class($link, 'map/topicref'))
                  then string((root($link)/*/*[df:class(., 'topic/title')] |
                        root($link)/*/@title)[1])
                  else string($link/ancestor::*[df:class(., 'topic/topic')][1]/*[df:class(., 'topic/title')])}</title>
     </dfst:useRecord>
    let $useRecordUri := relpath:newFile($containingDir, $recordFilename)
    return try {
       db:replace($dbName,
                  $useRecordUri,
                  $useRecord),
       (: FIXME: Write record to log doc :)
       db:output(<info>Stored use record "{$useRecordUri}"</info>)
    } catch * {
       (: FIXME: Write record to log doc :)
       db:output(<error>Error storing use record to "{$useRecordUri}": {$err:description}</error>)
    }
};

(:~
 : Construct key spaces from root maps.
 :
 : Finds all the root maps (requires that all direct-link use records are up to date).
 :
 : For each root map, constructs and stored the resolved map and then constructs
 : and stores the keyspace document for that root map.
 :
 :)
declare %updating function lmm:constructKeySpaces($dbName, $logID) {
  (: FIXME: Implement this :)
  db:output("lmm:constructKeySpaces() not implemented")
};
(: End of Module :)

(: =====================================================

   DITA Link Manager Model

   Model component of Model/View/Controller. Maintains
   the link management data.

   Provides queries to create and update resource use records,
   dependency indexes, etc.

   These queries are called whenever new content is added to
   given database.

   Author: W. Eliot Kimber

   Copyright (c) 2015 DITA For Small Teams
   Licensed under Apache License 2

   ===================================================== :)

module namespace lmm="http://dita-for-small-teams.org/xquery/modules/linkmgr-model";

declare namespace dfst="http://dita-for-small-teams.org";

import module namespace df="http://dita-for-small-teams.org/xquery/modules/dita-utils";
import module namespace bxutil="http://dita-for-small-teams.org/xquery/modules/basex-utils";
import module namespace lmutil="http://dita-for-small-teams.org/xquery/modules/linkmgmt-utils";
import module namespace dfstcnst="http://dita-for-small-teams.org/xquery/modules/dfst-constants";
import module namespace relpath="http://dita-for-small-teams.org/xquery/modules/relpath-utils";

(: Given an element with an @id value, construct the unique resource key for it.
   For a given element the resource ID is guaranteed to be unique within
   a snapshot (git commit).

   @param elem Element to get resource ID for. Must specify @id attribute.
   @returns Resource key string.

   The resource key is a combination of the absolute URI of the containing
   document, the element's @id attribute value, and other details TBD.

   Note that because DITA can only address elements with IDs this function
   only works for elements with @id attribute values.
 :)
declare function lmm:constructResourceKeyForElement($elem as element()) as xs:string {
  let $targetDocHash := hash:md5(document-uri(root($elem)))
  let $treepos := for $anc in ($elem/ancestor-or-self::*)
                      return string(count($anc | $anc/preceding-sibling::*))
  let $key := string-join($treepos, '.')
  return $targetDocHash || '^' || $key

};

(:~
 : Create or update the link management indexes for the specified repository
 :
 :)
declare %updating function lmm:updateLinkManagementIndexes($dbName as xs:string) {
   (: Query the database to find all links and for each link, record a resource use
      record in the database.

      Every addressible element has a "resource key" which combines the URI of
      the containing document and the element's @id value (only elements with
      @id attributes are addressible.

      The link management indexes are stored under the .dfst directory:

      .dfst/linkmgmt/where-used/

      For each document used there is a directory whose name is the hash
      of the document's URI (which will be unique within a branch.

    :)

    (: Need to handle both context-free links (direct URI references) and
       context-specific links (key references).

       For context-specific links have to process all the maps and construct
       resolved maps to serve as the resolution context. This could also
       be modeled as updating the key spaces but I think it makes more
       sense to have the maps be the focus, from which key spaces are derived,
       rather than making the key spaces the primary focus.

     :)

    let $logID := 'linkMgmtIndex' (: ID of the log to log messages to :)

    let $directLinks := lmutil:findAllDirectLinks($dbName)

    return (try {
        db:delete($dbName, $dfstcnst:where-used-dir)
    } catch * {
        (: FIXME: Log to log doc :)
        db:output(<error>Exception deleting where-used directory "{$dfstcnst:where-used-dir}": {$err:description}</error>)
    },

    (: Now create new resource use records for direct links :)

    lmm:createDirectLinkResourceRecords($dbName, $directLinks, $logID),

    (: Now create resolved maps and key space documents for each of the root maps.
     :)
    lmm:constructKeySpaces($dbName, $logID),

    (: Now create resource use records for all the indirect links: :)
    lmm:createIndirectLinkResourceRecords(
                $dbName,
                lmutil:findAllIndirectLinks($dbName),
                $logID)
    )

};

declare %updating function lmm:createDirectLinkResourceRecords(
        $dbName as xs:string,
        $directLinks as map(*)*,
        $logID as xs:string) {
    for $linkItem in $directLinks
       let $resolveResult := lmutil:resolveDirectLink($linkItem)
       return lmm:createOrUpdateResourceUseRecord(
                   $dbName,
                   $linkItem,
                   $resolveResult,
                   $logID)

};

declare %updating function lmm:createIndirectLinkResourceRecords(
                               $dbName as xs:string,
                               $indirectLinks,
                               $logID as xs:string) {
    for $linkItem in $indirectLinks
        let $resolveResult as map(*) := lmutil:resolveIndirectLink($linkItem)
        return lmm:createOrUpdateResourceUseRecord(
                   $dbName,
                   $linkItem,
                   $resolveResult,
                   $logID)
};

(: Given a link and the resolved result, creates a resource use record for each
   target the link resolved to.

 :)
declare %updating function lmm:createOrUpdateResourceUseRecord(
                              $dbName,
                              $linkItem as map(*),
                              $resolveResult as map(*),
                              $logID as xs:string) {
   let $resolveResult as map(*) := lmutil:resolveDirectLink($linkItem)
   let $targets := $resolveResult('target')
   for $target in $targets
       return lmm:createOrUpdateResourceUseRecordForLinkTarget(
                    $dbName,
                    $linkItem,
                    $target,
                    $logID)
};

(:
 : Creates a use record reflecting the use of the target element by the document
 : that contains the link.
 :
 : The use records are organized into directories, one directory per used document,
 : where the directory name is the MD5 hash of the document URI (just to make a
 : shorter name that won't have any problematic characters).
 :
 : Each use record document's filename is the MD5 hash of the linking document's
 : URI plus the MD5 hash of the linking element.
 :
 : Potential problem: the same element could, in theory, link to the same target in
 : two different ways: as a link and as a conref, although this case is extremely
 : unlikely. Really need a way to distinguish each abstract link represented by
 : a given DITA link-establishing element but the current implementation doesn't
 : provide that abstraction. It could through a refactor. Keeping it simple for now.
 :)
declare %updating function lmm:createOrUpdateResourceUseRecordForLinkTarget(
           $dbName as xs:string,
           $linkItem as map(*),
           $target as element(),
           $logID as xs:string) {
   let $targetDoc := root($target)
   let $targetDocHash := hash:md5(document-uri($targetDoc))
   let $containingDir := concat($dfstcnst:where-used-dir, '/', $targetDocHash, '/')
   let $reskey := lmm:constructResourceKeyForElement($target)
   let $recordFilename := concat('use-record_', $reskey, '.xml')
   let $link := $linkItem('link')
   let $format := if ($link/@format)
                     then string($link/@format)
                     else 'dita'
   let $scope := if ($link/@scope)
                     then string($link/@scope)
                     else 'local'
   (: df:getTitleForLinkElementContainer($link) :)
   let $useRecord :=
     <dfst:useRecord resourceKey="{$reskey}"
                     targetDoc="{document-uri($targetDoc)}"
                     usingDoc="{document-uri(root($link))}"
                     linkType="{df:getBaseLinkType($link)}"
                     linkClass="{string($link/@class)}"
                     format="{$format}"
                     scope="{$scope}"
     >
       <title>{if (df:class($link, 'map/topicref')) then string(root($link)/*)
                  else string($link/ancestor::*[df:class(., 'topic/topic')][1]/*[df:class(., 'topic/title')])}</title>
     </dfst:useRecord>
    let $useRecordUri := relpath:newFile($containingDir, $recordFilename)
    return try {
       db:replace($dbName,
                  $useRecordUri,
                  $useRecord),
       (: FIXME: Write record to log doc :)
       db:output(<info>Stored use record "{$useRecordUri}"</info>)
    } catch * {
       (: FIXME: Write record to log doc :)
       db:output(<error>Error storing use record to "{$useRecordUri}": {$err:description}</error>)
    }
};

(:~
 : Construct key spaces from root maps.
 :
 : Finds all the root maps (requires that all direct-link use records are up to date).
 :
 : For each root map, constructs and stored the resolved map and then constructs
 : and stores the keyspace document for that root map.
 :
 :)
declare %updating function lmm:constructKeySpaces($dbName, $logID) {
  (: FIXME: Implement this :)
  db:output("lmm:constructKeySpaces() not implemented")
};
(: End of Module :)