Fix bug in XQDoc generation (duplicate documentation title).

module namespace doc2html = "http://www.zorba-xquery.com/modules/xqdoc2html";

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

import module namespace xqdg = "http://www.zorba-xquery.com/modules/internal/xqdoc-xhtml" at "xqdoc-xhtml.xq";

import schema namespace xqdoc = "http://www.xqdoc.org/1.0" at "xqdoc-1.0.xsd";

declare copy-namespaces preserve, inherit;

(:~
 : This variable contains the relative paths to directories that contain
 : example code fragments that are included.
 :)
declare variable $doc2html:examplePath as xs:string* := (
    'test/rbkt/Queries/zorba',
    'test/update/Queries',
    'test/rbkt/ExpQueryResults/zorba',
    'test/update/ExpectedTestResults'
);

declare variable $doc2html:table :=
;

declare variable $doc2html:stepBacks as xs:string := "../../../../../../../../../../../../../../../../../../../../";

(:~
 : This variable contains the to the index.html main page
 :)

declare variable $doc2html:indexCollector := <modules/>;

declare function doc2html:getFileName($moduleURI as xs:string) as xs:string
{
    replace(
        replace($moduleURI, "/", "_"),
        "http:__",
        ""
    )
};

declare sequential function doc2html:clearFolder($folderPath as xs:string, $pattern as xs:string) as xs:string*
{
  if(fn:not(file:exists($folderPath))) then
    ""
  else
    for $file in file:files($folderPath, $pattern, fn:true())
    let $filePath := fn:concat($folderPath, file:path-separator(), $file)
    return
      try {
        file:remove($filePath);
        concat("
  SUCCESS: ", $filePath);
      } catch * ($error_code) {
        concat("
  FAILED: ", $filePath)
      }
};

(:~
 : This function generates the XQDoc XML for all the modules found in $modulesPath
 : and writes the resulting XML documents in $targetPath. The hierarchy is preserved.
 :
 : @param $modulePath where to search for modules recursively.
 : @param $targetPath where to generate the XQDoc XML documents.
 : @return The function returns a string sequence with a status messge for each
 :         module processed.
 :)
declare sequential function doc2html:generateXQDocXml(
  $modulesPath as xs:string, $xqdocXmlPath as xs:string
) as xs:string* {
  for $filedirs in tokenize($modulesPath, ";")
  for $file in file:files($filedirs, "\.xq$", fn:true())
  let $filePath := fn:concat($filedirs, file:path-separator(), $file)
  let $xqdoc := xqd:xqdoc(file:path-to-uri($filePath))
  let $moduleDoc := $xqdoc/xqdoc:module
  let $moduleName := $moduleDoc/xqdoc:name
  let $moduleUri := $moduleDoc/xqdoc:uri
  return
    try {
      file:mkdirs($xqdocXmlPath, false());
      let $x := fn:count(file:files($xqdocXmlPath, "\.xml$", fn:true())) + 1
      let $xqdocFileName := fn:concat($xqdocXmlPath, file:path-separator(), doc2html:getFileName($moduleUri), ".xml")
      return
      file:write($xqdocFileName, $xqdoc, <s method="xml" indent="yes" />/@*);
      concat("
SUCCESS: ", $moduleUri, " (", ()(:$xqdocFileName:), ")");
    } catch * ($error_code) {
      concat("
FAILED: ", $moduleUri, " (", ()(:$xqdocFileName:), ")")
    }
};

(:~
 : This function removes the internal functionality from an XQDoc XML node.
 :
 : @param $xqdoc The XQDdoc XML node to remove the functionality from.
 : @return The xqdoc node without the internal functionality.
 :)
declare sequential function doc2html:removeInternalFunctionality($xqdoc as node()) as node() {
  delete node $xqdoc/xqdoc:functions/xqdoc:function[fn:starts-with(fn:data(xqdoc:comment/xqdoc:description), "Helper function")];
  $xqdoc;
};

(:~
 : This function generates the XQDoc XHTML pages for all the XQDoc XML
 : documents found in $xqdocPath and writes the resulting pages to in
 : $targetPath. The hierarchy is preserved.
 :
 : @param $indexPath path to the XHTML index page.
 :)
declare sequential function doc2html:generateXQDocXhtml(
  $indexPath      as xs:string, $xqdocXmlPath   as xs:string,
  $xqdocXhtmlPath as xs:string, $leftMenu as element(menu),
  $modulesPath    as xs:string, $menu as element(ul)
) as xs:string* {
  let $indexHtmlDoc := file:read-xml($indexPath)
  return
  for $file in file:files($xqdocXmlPath, "\.xml$", fn:true())
  let $stepsFromIndex := fn:count(fn:tokenize($file, fn:concat("\", file:path-separator()))) - 2
  let $xmlFilePath := concat($xqdocXmlPath, file:path-separator(), $file)
  let $xhtmlFilePath := concat($xqdocXhtmlPath, file:path-separator(), fn:replace($file, "\.xml$", ".html"))
  let $xhtmlFileDir :=
    let $segments := fn:tokenize($xhtmlFilePath, fn:concat("\", file:path-separator()))
    let $lastSegm := $segments[fn:count($segments)]
    return fn:substring-before($xhtmlFilePath, $lastSegm)
  return
    try {
      let $xqdoc := doc2html:removeInternalFunctionality(file:read-xml($xmlFilePath)/xqdoc:xqdoc)
      let $moduleDoc := $xqdoc/xqdoc:module
      let $moduleName := $moduleDoc/xqdoc:name
      let $moduleUri := $moduleDoc/xqdoc:uri
      let $menu := doc2html:createLeftMenu($menu, $moduleUri, $leftMenu)
      let $xhtml := xqdg:doc($xqdoc, $menu)
      return block {
        file:mkdirs($xhtmlFileDir, false());

        doc2html:configure-xhtml($xhtml, $stepsFromIndex, $modulesPath);

        file:write($xhtmlFilePath, $xhtml, <s method="xhtml" indent="yes" />/@*);

        concat("
SUCCESS: ", $moduleUri, " (", $xhtmlFilePath, ")");
      }
    } catch * ($error_code) {
      concat("
FAILED: ", $xhtmlFilePath)
    }
};

(:~
 : This function gathers the names of the XQDoc XHTML pages for all the XQDoc XML
 : documents found in $xqdocXmlPath. The hierarchy is preserved.
 :)
declare sequential function doc2html:gatherModules(
  $xqdocXmlPath as xs:string
){
  for $file in file:files($xqdocXmlPath, "\.xml$", fn:true())
  let $xmlFilePath := concat($xqdocXmlPath, file:path-separator(), $file)
  let $xhtmlRelativeFilePath := fn:replace($file, "\.xml$", ".html")
  let $xhtmlRelativeFilePathSlash := fn:replace($xhtmlRelativeFilePath, fn:concat("\", file:path-separator()), "/")
  return
  try {
      let $xqdoc := doc2html:removeInternalFunctionality(file:read-xml($xmlFilePath)/xqdoc:xqdoc)
      let $moduleDoc := $xqdoc/xqdoc:module
      let $moduleUri := $moduleDoc/xqdoc:uri
      return block {
        doc2html:collectModule($moduleDoc, $xhtmlRelativeFilePath);
        concat("
SUCCESS: ", $moduleUri, " (", $xmlFilePath, ")");
      }
    } catch * ($error_code) {
      concat("
FAILED: ", $xmlFilePath)
    }
};

declare sequential function doc2html:collectModule ($module, $relativeFileName as xs:string) {
    let $moduleName := $module/xqdoc:name,
        $moduleUri := $module/xqdoc:uri
    return
        insert node <module uri="{$moduleUri/text()}" file="{$relativeFileName}" /> as last into $doc2html:indexCollector;
    ();
};

(:~
 : This function returns the left menu needed in the index.html and also all the XHTML module pages.
 :
 :)
declare sequential function doc2html:createLeftMenu($menu, $moduleUri, $categories)
{
  for $cat1 in $categories/category
  let $last1 := if(fn:exists($cat1/@last)) then fn:boolean(fn:data($cat1/@last)) else fn:false()
  return block {
    doc2html:createCategory($menu, $moduleUri, $cat1, $last1);
    for $cat2 in $cat1/category
    let $last2 := if(fn:exists($cat2/@last)) then fn:boolean(fn:data($cat2/@last)) else fn:false()
    let $menu2 := $menu/li[fn:last()]/ul
    return block {
      doc2html:createCategory($menu2, $moduleUri, $cat2, $last2);
      for $cat3 in $cat2/category
      let $last3 := if(fn:exists($cat3/@last)) then fn:boolean(fn:data($cat3/@last)) else fn:false()
      let $menu3 := $menu/li[fn:last()]/ul/li[fn:last()]/ul
      return block {
        doc2html:createCategory($menu3, $moduleUri, $cat3, $last3);
      };
    };
  };

  $menu;

};

(:~
 : This is a helper function used to add a category and the modules belonging to it to a node in the menu
 :
 : @param $menu the node in the menu where the new category will be added as the last node
 : @param $moduleUri the name of this module will be highlighted in red as this is the current selected item in the menu
 : @param $category the node in the $leftMenu variable that will be added to the $menu
 : @param $last bool indicating if this is the last item in the $menu node needed in order to set the treeview control
 :)
declare sequential function doc2html:createCategory($menu, $moduleUri, $category, $last as xs:boolean)
{
  block {
  if($last) then
    insert nodes

{' '}

    {data($category/@name)}

    {
     for $module in $doc2html:indexCollector/module
      order by fn:data($module/@uri)
      return block {
        if(fn:starts-with(fn:string($module/@uri),data($category/@uri))
           and fn:not(fn:contains (fn:substring-after(data($module/@uri),fn:concat(data($category/@uri),'/')),'/'))
           and fn:not($module/@uri = $moduleUri)) then

{fn:substring-after(data($module/@uri),fn:concat(data($category/@uri),'/'))}

        else if(fn:starts-with(fn:string($module/@uri),data($category/@uri))
                and fn:not(fn:contains (fn:substring-after(data($module/@uri),fn:concat(data($category/@uri),'/')),'/'))
                and ($module/@uri = $moduleUri)) then

{fn:substring-after(data($module/@uri),fn:concat(data($category/@uri),'/'))}

        else ()
        };
    }

    as last into $menu
  else
    insert nodes

{' '}

    {data($category/@name)}

    {
      for $module in $doc2html:indexCollector/module
      order by fn:data($module/@uri)
      return block {
        if(fn:starts-with(fn:string($module/@uri),data($category/@uri))
           and fn:not(fn:contains (fn:substring-after(data($module/@uri),fn:concat(data($category/@uri),'/')),'/'))
           and fn:not($module/@uri = $moduleUri)) then

{fn:substring-after(data($module/@uri),fn:concat(data($category/@uri),'/'))}

        else if(fn:starts-with(fn:string($module/@uri),data($category/@uri))
                and fn:not(fn:contains (fn:substring-after(data($module/@uri),fn:concat(data($category/@uri),'/')),'/'))
                and ($module/@uri = $moduleUri)) then

{fn:substring-after(data($module/@uri),fn:concat(data($category/@uri),'/'))}

        else ()
        };
    }

    as last into $menu
    };

   $menu;
};

declare sequential function doc2html:getFilePath (
  $filename as xs:string, $modulesPath as xs:string
) as xs:string* {
  let $zorbaPath := replace($modulesPath,"/modules","")
  for $relPath in $doc2html:examplePath
  let $path := concat($zorbaPath,'/',$relPath)
  let $test := file:files($path,concat("^",$filename,"$"),fn:true())[1]
  return
  if (fn:exists($test)) then
    $test
  else
    ()
};

declare function doc2html:get-path-to-index ($stepsBack) as xs:string
{
  (: Error prone: stepsBack > stepBacks :)
  fn:substring($doc2html:stepBacks, 1, 3 * $stepsBack)
};

declare sequential function doc2html:configure-xhtml (
  $xhtml, $stepsFromIndex as xs:integer,
  $modulesPath as xs:string
) {
  let $pathToIndex := doc2html:get-path-to-index($stepsFromIndex)
  return
  (: replace the <test> nodes with eiter links to the actual files or inline the actual files based on the 'type' attribute value :)
  for $file in $xhtml//*:include
  let $type := $file/@type
  let $path := doc2html:getFilePath($file/text(), $modulesPath)
  let $read_text := file:read-text($path)
  return block {
  (: at the moment there is no way to ship the files defined as links with the packages/installers
    if (matches($type, "link")) then
      replace node $file with {$path}
    else :)
    if (matches($type, "inline")) then
      replace node $file with
        if (fn:exists($path)) then

 {$path}:

{$read_text}

        else

No example matching pattern '{$file/text()}' was found. Please update 'examplePath' variable.

        else ()
  };

  (: replace the function type description with images :)
  let $xquSpec := "http://www.w3.org/TR/xquery-update-10/",
      $xqsSpec := "http://www.w3.org/TR/xquery-sx-10/#dt-sequential-function",
      $xq11Spec := "http://www.w3.org/TR/xquery-11/#FunctionDeclns"
  let $pathToIndex := doc2html:get-path-to-index($stepsFromIndex)
  let $imagesPath := concat($pathToIndex, "images/")
  for $typeTd in $xhtml//*:td
  where $typeTd/@class eq "type"
  let $type := $typeTd/text()
  return block {
    if (matches($type, "updating")) then
      replace node $typeTd/text() with

    else if (matches($type, "sequential")) then
      replace node $typeTd/text() with

    else if (matches($type, "nondeterministic")) then
      replace node $typeTd/text() with

    else ();
  };

  $xhtml;
};

declare sequential function doc2html:createModuleTable($leftMenu as element(menu)) {
  for $cat1 in $leftMenu/category
  return block {
    doc2html:createModuleHelper($doc2html:table, $cat1);
    for $cat2 in $cat1/category
    let $table2 := $doc2html:table/li[fn:last()]/ul
    return block {
      doc2html:createModuleHelper($table2, $cat2);
      for $cat3 in $cat2/category
      let $table3 := $doc2html:table/li[fn:last()]/ul/li[fn:last()]/ul
      return block {
        doc2html:createModuleHelper($table3, $cat3);
      };
    };
  };

  $doc2html:table;
};

declare sequential function doc2html:createModuleHelper($table, $category)
{
  block {
    insert nodes
{data($category/@name)}

    {
     for $module in $doc2html:indexCollector/module
      order by fn:data($module/@uri)
      return
        if(fn:starts-with(fn:string($module/@uri),data($category/@uri))
           and fn:not(fn:contains (fn:substring-after(data($module/@uri),fn:concat(data($category/@uri),'/')),'/'))) then

{fn:substring-after(data($module/@uri),fn:concat(data($category/@uri),'/'))}

        else ()
    }

    as last into $table
    };

   $table;
};

(:~
 : This function reads, updates and returns the new index.html.
 :
 : @param $indexPath Where to search for XML XQDoc documents recursively.
 : @return The content of the new index.html.
 :)
declare sequential function doc2html:generateIndexHtml($indexPath as xs:string, $menu, $modules) as document-node() {
    let $indexHtmlDoc := file:read-xml($indexPath)
    return block {
        insert nodes $menu
        as last into $indexHtmlDoc/*:html/*:body/*:div[@id='main']/*:div[@id='leftMenu'];

        let $module_list := $indexHtmlDoc/*:html/*:body/*:div[@id='main']/*:div[@id='rightcontent']/*:div[@id='module_list']
        return
          if ($module_list)
          then
            insert node $modules as last into $module_list
          else
            ();

        $indexHtmlDoc;
    }
};

declare sequential function doc2html:main(
  $modulePath as xs:string, $xqdocBuildPath as xs:string,
  $indexHtmlPath as xs:string
) {
  let $leftMenu :=
  <menu>
  <category name="http://expath.org/ns" uri="http://expath.org/ns" />
  <category name="http://www.zorba-xquery.com/modules" uri="http://www.zorba-xquery.com/modules">
    <category name="email" uri="http://www.zorba-xquery.com/modules/email" />
    <category name="excel" uri="http://www.zorba-xquery.com/modules/excel" />
    <category name="image" uri="http://www.zorba-xquery.com/modules/image" />
    <category name="introspection" uri="http://www.zorba-xquery.com/modules/introspection" />
    <category name="security" uri="http://www.zorba-xquery.com/modules/security" />
    <category name="oauth" uri="http://www.zorba-xquery.com/modules/oauth" />
    <category name="webservices" uri="http://www.zorba-xquery.com/modules/webservices" last="true">
      <category name="google" uri="http://www.zorba-xquery.com/modules/webservices/google" />
      <category name="yahoo" uri="http://www.zorba-xquery.com/modules/webservices/yahoo" last="true" />
    </category>
  </category>
  <category name="http://www.w3.org/2005" uri="http://www.w3.org/2005" last="true"/>
  </menu>
  let $menu :=

      XQuery Libraries

  return doc2html:main($menu, $leftMenu, $modulePath, $xqdocBuildPath, $indexHtmlPath)
};

declare sequential function doc2html:main(
  $menu as element(ul),  $leftMenu as element(menu),
  $modulePath as xs:string, $xqdocBuildPath as xs:string,
  $indexHtmlPath as xs:string
) {
  declare $xqdocXmlPath   as xs:string := fn:concat($xqdocBuildPath, file:path-separator(), "xml");
  declare $xqdocXhtmlPath as xs:string := fn:concat($xqdocBuildPath, file:path-separator(), "xhtml");

  (: generate the XQDoc XML for all the modules :)
  if (file:mkdirs($xqdocXmlPath, false())) then
  (
    doc2html:clearFolder($xqdocXmlPath,"\.xml$"),
    doc2html:generateXQDocXml($modulePath, $xqdocXmlPath)
  )
  else
    error()
  ;

  (: generate the XQDoc XHTML for all the modules :)
  let $absoluteXhtmlDir := concat($xqdocBuildPath, "/xhtml")
  return
  if (file:mkdirs($absoluteXhtmlDir, false())) then
  (
    doc2html:clearFolder($xqdocXhtmlPath,"xqdoc\.html$"),
    doc2html:gatherModules($xqdocXmlPath),
    doc2html:generateXQDocXhtml($indexHtmlPath, $xqdocXmlPath, $xqdocXhtmlPath, $leftMenu, $modulePath, $menu)
  )
  else
    error()
  ;

  let $menu := doc2html:createLeftMenu($menu, "index.html", $leftMenu)
  let $modules := doc2html:createModuleTable($leftMenu)
  let $doc := doc2html:generateIndexHtml($indexHtmlPath, $menu, $modules)
  return doc2html:configure-xhtml($doc/*:html, 0, $modulePath)
  ;
};

module namespace doc2html = "http://www.zorba-xquery.com/modules/xqdoc2html";

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

import module namespace xqdg = "http://www.zorba-xquery.com/modules/internal/xqdoc-xhtml" at "xqdoc-xhtml.xq";

import schema namespace xqdoc = "http://www.xqdoc.org/1.0" at "xqdoc-1.0.xsd";

declare copy-namespaces preserve, inherit;

(:~
 : This variable contains the relative paths to directories that contain
 : example code fragments that are included.
 :)
declare variable $doc2html:examplePath as xs:string* := (
    'test/rbkt/Queries/zorba',
    'test/update/Queries',
    'test/rbkt/ExpQueryResults/zorba',
    'test/update/ExpectedTestResults'
);

declare variable $doc2html:table :=
;

declare variable $doc2html:stepBacks as xs:string := "../../../../../../../../../../../../../../../../../../../../";

(:~
 : This variable contains the to the index.html main page
 :)

declare variable $doc2html:indexCollector := <modules/>;

declare function doc2html:getFileName($moduleURI as xs:string) as xs:string
{
    replace(
        replace($moduleURI, "/", "_"),
        "http:__",
        ""
    )
};

declare sequential function doc2html:clearFolder($folderPath as xs:string, $pattern as xs:string) as xs:string*
{
  if(fn:not(file:exists($folderPath))) then
    ""
  else
    for $file in file:files($folderPath, $pattern, fn:true())
    let $filePath := fn:concat($folderPath, file:path-separator(), $file)
    return
      try {
        file:remove($filePath);
        concat("
  SUCCESS: ", $filePath);
      } catch * ($error_code) {
        concat("
  FAILED: ", $filePath)
      }
};

(:~
 : This function generates the XQDoc XML for all the modules found in $modulesPath
 : and writes the resulting XML documents in $targetPath. The hierarchy is preserved.
 :
 : @param $modulePath where to search for modules recursively.
 : @param $targetPath where to generate the XQDoc XML documents.
 : @return The function returns a string sequence with a status messge for each
 :         module processed.
 :)
declare sequential function doc2html:generateXQDocXml(
  $modulesPath as xs:string, $xqdocXmlPath as xs:string
) as xs:string* {
  for $filedirs in tokenize($modulesPath, ";")
  for $file in file:files($filedirs, "\.xq$", fn:true())
  let $filePath := fn:concat($filedirs, file:path-separator(), $file)
  let $xqdoc := xqd:xqdoc(file:path-to-uri($filePath))
  let $moduleDoc := $xqdoc/xqdoc:module
  let $moduleName := $moduleDoc/xqdoc:name
  let $moduleUri := $moduleDoc/xqdoc:uri
  return
    try {
      file:mkdirs($xqdocXmlPath, false());
      let $x := fn:count(file:files($xqdocXmlPath, "\.xml$", fn:true())) + 1
      let $xqdocFileName := fn:concat($xqdocXmlPath, file:path-separator(), doc2html:getFileName($moduleUri), ".xml")
      return
      file:write($xqdocFileName, $xqdoc, <s method="xml" indent="yes" />/@*);
      concat("
SUCCESS: ", $moduleUri, " (", ()(:$xqdocFileName:), ")");
    } catch * ($error_code) {
      concat("
FAILED: ", $moduleUri, " (", ()(:$xqdocFileName:), ")")
    }
};

(:~
 : This function removes the internal functionality from an XQDoc XML node.
 :
 : @param $xqdoc The XQDdoc XML node to remove the functionality from.
 : @return The xqdoc node without the internal functionality.
 :)
declare sequential function doc2html:removeInternalFunctionality($xqdoc as node()) as node() {
  delete node $xqdoc/xqdoc:functions/xqdoc:function[fn:starts-with(fn:data(xqdoc:comment/xqdoc:description), "Helper function")];
  $xqdoc;
};

(:~
 : This function generates the XQDoc XHTML pages for all the XQDoc XML
 : documents found in $xqdocPath and writes the resulting pages to in
 : $targetPath. The hierarchy is preserved.
 :
 : @param $indexPath path to the XHTML index page.
 :)
declare sequential function doc2html:generateXQDocXhtml(
  $indexPath      as xs:string, $xqdocXmlPath   as xs:string,
  $xqdocXhtmlPath as xs:string, $leftMenu as element(menu),
  $modulesPath    as xs:string
) as xs:string* {
  let $indexHtmlDoc := file:read-xml($indexPath)
  return
  for $file in file:files($xqdocXmlPath, "\.xml$", fn:true())
  let $stepsFromIndex := fn:count(fn:tokenize($file, fn:concat("\", file:path-separator()))) - 2
  let $xmlFilePath := concat($xqdocXmlPath, file:path-separator(), $file)
  let $xhtmlFilePath := concat($xqdocXhtmlPath, file:path-separator(), fn:replace($file, "\.xml$", ".html"))
  let $xhtmlFileDir :=
    let $segments := fn:tokenize($xhtmlFilePath, fn:concat("\", file:path-separator()))
    let $lastSegm := $segments[fn:count($segments)]
    return fn:substring-before($xhtmlFilePath, $lastSegm)
  return
    try {
      let $xqdoc := doc2html:removeInternalFunctionality(file:read-xml($xmlFilePath)/xqdoc:xqdoc)
      let $moduleDoc := $xqdoc/xqdoc:module
      let $moduleName := $moduleDoc/xqdoc:name
      let $moduleUri := $moduleDoc/xqdoc:uri
      let $menu :=
XQuery Libraries

      let $menu := doc2html:createLeftMenu($menu, $moduleUri, $leftMenu)
      let $xhtml := xqdg:doc($xqdoc, $menu)
      return block {
        file:mkdirs($xhtmlFileDir, false());

        doc2html:configure-xhtml($xhtml, $stepsFromIndex, $modulesPath);

        file:write($xhtmlFilePath, $xhtml, <s method="xhtml" indent="yes" />/@*);

        concat("
SUCCESS: ", $moduleUri, " (", $xhtmlFilePath, ")");
      }
    } catch * ($error_code) {
      concat("
FAILED: ", $xhtmlFilePath)
    }
};

(:~
 : This function gathers the names of the XQDoc XHTML pages for all the XQDoc XML
 : documents found in $xqdocXmlPath. The hierarchy is preserved.
 :)
declare sequential function doc2html:gatherModules(
  $xqdocXmlPath as xs:string
){
  for $file in file:files($xqdocXmlPath, "\.xml$", fn:true())
  let $xmlFilePath := concat($xqdocXmlPath, file:path-separator(), $file)
  let $xhtmlRelativeFilePath := fn:replace($file, "\.xml$", ".html")
  let $xhtmlRelativeFilePathSlash := fn:replace($xhtmlRelativeFilePath, fn:concat("\", file:path-separator()), "/")
  return
  try {
      let $xqdoc := doc2html:removeInternalFunctionality(file:read-xml($xmlFilePath)/xqdoc:xqdoc)
      let $moduleDoc := $xqdoc/xqdoc:module
      let $moduleUri := $moduleDoc/xqdoc:uri
      return block {
        doc2html:collectModule($moduleDoc, $xhtmlRelativeFilePath);
        concat("
SUCCESS: ", $moduleUri, " (", $xmlFilePath, ")");
      }
    } catch * ($error_code) {
      concat("
FAILED: ", $xmlFilePath)
    }
};

declare sequential function doc2html:collectModule ($module, $relativeFileName as xs:string) {
    let $moduleName := $module/xqdoc:name,
        $moduleUri := $module/xqdoc:uri
    return
        insert node <module uri="{$moduleUri/text()}" file="{$relativeFileName}" /> as last into $doc2html:indexCollector;
    ();
};

(:~
 : This function returns the left menu needed in the index.html and also all the XHTML module pages.
 :
 :)
declare sequential function doc2html:createLeftMenu($menu, $moduleUri, $categories)
{
  for $cat1 in $categories/category
  let $last1 := if(fn:exists($cat1/@last)) then fn:boolean(fn:data($cat1/@last)) else fn:false()
  return block {
    doc2html:createCategory($menu, $moduleUri, $cat1, $last1);
    for $cat2 in $cat1/category
    let $last2 := if(fn:exists($cat2/@last)) then fn:boolean(fn:data($cat2/@last)) else fn:false()
    let $menu2 := $menu/li[fn:last()]/ul
    return block {
      doc2html:createCategory($menu2, $moduleUri, $cat2, $last2);
      for $cat3 in $cat2/category
      let $last3 := if(fn:exists($cat3/@last)) then fn:boolean(fn:data($cat3/@last)) else fn:false()
      let $menu3 := $menu/li[fn:last()]/ul/li[fn:last()]/ul
      return block {
        doc2html:createCategory($menu3, $moduleUri, $cat3, $last3);
      };
    };
  };

  $menu;

};

(:~
 : This is a helper function used to add a category and the modules belonging to it to a node in the menu
 :
 : @param $menu the node in the menu where the new category will be added as the last node
 : @param $moduleUri the name of this module will be highlighted in red as this is the current selected item in the menu
 : @param $category the node in the $leftMenu variable that will be added to the $menu
 : @param $last bool indicating if this is the last item in the $menu node needed in order to set the treeview control
 :)
declare sequential function doc2html:createCategory($menu, $moduleUri, $category, $last as xs:boolean)
{
  block {
  if($last) then
    insert nodes

{' '}

    {data($category/@name)}

    {
     for $module in $doc2html:indexCollector/module
      order by fn:data($module/@uri)
      return block {
        if(fn:starts-with(fn:string($module/@uri),data($category/@uri))
           and fn:not(fn:contains (fn:substring-after(data($module/@uri),fn:concat(data($category/@uri),'/')),'/'))
           and fn:not($module/@uri = $moduleUri)) then

{fn:substring-after(data($module/@uri),fn:concat(data($category/@uri),'/'))}

        else if(fn:starts-with(fn:string($module/@uri),data($category/@uri))
                and fn:not(fn:contains (fn:substring-after(data($module/@uri),fn:concat(data($category/@uri),'/')),'/'))
                and ($module/@uri = $moduleUri)) then

{fn:substring-after(data($module/@uri),fn:concat(data($category/@uri),'/'))}

        else ()
        };
    }

    as last into $menu
  else
    insert nodes

{' '}

    {data($category/@name)}

    {
      for $module in $doc2html:indexCollector/module
      order by fn:data($module/@uri)
      return block {
        if(fn:starts-with(fn:string($module/@uri),data($category/@uri))
           and fn:not(fn:contains (fn:substring-after(data($module/@uri),fn:concat(data($category/@uri),'/')),'/'))
           and fn:not($module/@uri = $moduleUri)) then

{fn:substring-after(data($module/@uri),fn:concat(data($category/@uri),'/'))}

        else if(fn:starts-with(fn:string($module/@uri),data($category/@uri))
                and fn:not(fn:contains (fn:substring-after(data($module/@uri),fn:concat(data($category/@uri),'/')),'/'))
                and ($module/@uri = $moduleUri)) then

{fn:substring-after(data($module/@uri),fn:concat(data($category/@uri),'/'))}

        else ()
        };
    }

    as last into $menu
    };

   $menu;
};

declare sequential function doc2html:getFilePath (
  $filename as xs:string, $modulesPath as xs:string
) as xs:string* {
  let $zorbaPath := replace($modulesPath,"/modules","")
  for $relPath in $doc2html:examplePath
  let $path := concat($zorbaPath,'/',$relPath)
  let $test := file:files($path,concat("^",$filename,"$"),fn:true())[1]
  return
  if (fn:exists($test)) then
    $test
  else
    ()
};

declare function doc2html:get-path-to-index ($stepsBack) as xs:string
{
  (: Error prone: stepsBack > stepBacks :)
  fn:substring($doc2html:stepBacks, 1, 3 * $stepsBack)
};

declare sequential function doc2html:configure-xhtml (
  $xhtml, $stepsFromIndex as xs:integer,
  $modulesPath as xs:string
) {
  let $pathToIndex := doc2html:get-path-to-index($stepsFromIndex)
  return
  (: replace the <test> nodes with eiter links to the actual files or inline the actual files based on the 'type' attribute value :)
  for $file in $xhtml//*:include
  let $type := $file/@type
  let $path := doc2html:getFilePath($file/text(), $modulesPath)
  let $read_text := file:read-text($path)
  return block {
  (: at the moment there is no way to ship the files defined as links with the packages/installers
    if (matches($type, "link")) then
      replace node $file with {$path}
    else :)
    if (matches($type, "inline")) then
      replace node $file with
        if (fn:exists($path)) then

 {$path}:

{$read_text}

        else

No example matching pattern '{$file/text()}' was found. Please update 'examplePath' variable.

        else ()
  };

  (: replace the function type description with images :)
  let $xquSpec := "http://www.w3.org/TR/xquery-update-10/",
      $xqsSpec := "http://www.w3.org/TR/xquery-sx-10/#dt-sequential-function",
      $xq11Spec := "http://www.w3.org/TR/xquery-11/#FunctionDeclns"
  let $pathToIndex := doc2html:get-path-to-index($stepsFromIndex)
  let $imagesPath := concat($pathToIndex, "images/")
  for $typeTd in $xhtml//*:td
  where $typeTd/@class eq "type"
  let $type := $typeTd/text()
  return block {
    if (matches($type, "updating")) then
      replace node $typeTd/text() with

    else if (matches($type, "sequential")) then
      replace node $typeTd/text() with

    else if (matches($type, "nondeterministic")) then
      replace node $typeTd/text() with

    else ();
  };

  $xhtml;
};

declare sequential function doc2html:createModuleTable($leftMenu as element(menu)) {
  for $cat1 in $leftMenu/category
  return block {
    doc2html:createModuleHelper($doc2html:table, $cat1);
    for $cat2 in $cat1/category
    let $table2 := $doc2html:table/li[fn:last()]/ul
    return block {
      doc2html:createModuleHelper($table2, $cat2);
      for $cat3 in $cat2/category
      let $table3 := $doc2html:table/li[fn:last()]/ul/li[fn:last()]/ul
      return block {
        doc2html:createModuleHelper($table3, $cat3);
      };
    };
  };

  $doc2html:table;
};

declare sequential function doc2html:createModuleHelper($table, $category)
{
  block {
    insert nodes
{data($category/@name)}

    {
     for $module in $doc2html:indexCollector/module
      order by fn:data($module/@uri)
      return
        if(fn:starts-with(fn:string($module/@uri),data($category/@uri))
           and fn:not(fn:contains (fn:substring-after(data($module/@uri),fn:concat(data($category/@uri),'/')),'/'))) then

{fn:substring-after(data($module/@uri),fn:concat(data($category/@uri),'/'))}

        else ()
    }

    as last into $table
    };

   $table;
};

(:~
 : This function reads, updates and returns the new index.html.
 :
 : @param $indexPath Where to search for XML XQDoc documents recursively.
 : @return The content of the new index.html.
 :)
declare sequential function doc2html:generateIndexHtml($indexPath as xs:string, $menu, $modules) as document-node() {
    let $indexHtmlDoc := file:read-xml($indexPath)
    return block {
        insert nodes $menu
        as last into $indexHtmlDoc/*:html/*:body/*:div[@id='main']/*:div[@id='leftMenu'];

        let $module_list := $indexHtmlDoc/*:html/*:body/*:div[@id='main']/*:div[@id='rightcontent']/*:div[@id='module_list']
        return
          if ($module_list)
          then
            insert node $modules as last into $module_list
          else
            ();

        $indexHtmlDoc;
    }
};

declare sequential function doc2html:main(
  $modulePath as xs:string, $xqdocBuildPath as xs:string,
  $indexHtmlPath as xs:string
) {
  let $leftMenu :=
  <menu>
  <category name="http://expath.org/ns" uri="http://expath.org/ns" />
  <category name="http://www.zorba-xquery.com/modules" uri="http://www.zorba-xquery.com/modules">
    <category name="email" uri="http://www.zorba-xquery.com/modules/email" />
    <category name="excel" uri="http://www.zorba-xquery.com/modules/excel" />
    <category name="image" uri="http://www.zorba-xquery.com/modules/image" />
    <category name="introspection" uri="http://www.zorba-xquery.com/modules/introspection" />
    <category name="security" uri="http://www.zorba-xquery.com/modules/security" />
    <category name="oauth" uri="http://www.zorba-xquery.com/modules/oauth" />
    <category name="webservices" uri="http://www.zorba-xquery.com/modules/webservices" last="true">
      <category name="google" uri="http://www.zorba-xquery.com/modules/webservices/google" />
      <category name="yahoo" uri="http://www.zorba-xquery.com/modules/webservices/yahoo" last="true" />
    </category>
  </category>
  <category name="http://www.w3.org/2005" uri="http://www.w3.org/2005" last="true"/>
  </menu>
  let $menu :=

      XQuery Libraries

  return doc2html:main($menu, $leftMenu, $modulePath, $xqdocBuildPath, $indexHtmlPath)
};

declare sequential function doc2html:main(
  $menu as element(ul),  $leftMenu as element(menu),
  $modulePath as xs:string, $xqdocBuildPath as xs:string,
  $indexHtmlPath as xs:string
) {
  declare $xqdocXmlPath   as xs:string := fn:concat($xqdocBuildPath, file:path-separator(), "xml");
  declare $xqdocXhtmlPath as xs:string := fn:concat($xqdocBuildPath, file:path-separator(), "xhtml");

  (: generate the XQDoc XML for all the modules :)
  if (file:mkdirs($xqdocXmlPath, false())) then
  (
    doc2html:clearFolder($xqdocXmlPath,"\.xml$"),
    doc2html:generateXQDocXml($modulePath, $xqdocXmlPath)
  )
  else
    error()
  ;

  (: generate the XQDoc XHTML for all the modules :)
  let $absoluteXhtmlDir := concat($xqdocBuildPath, "/xhtml")
  return
  if (file:mkdirs($absoluteXhtmlDir, false())) then
  (
    doc2html:clearFolder($xqdocXhtmlPath,"xqdoc\.html$"),
    doc2html:gatherModules($xqdocXmlPath),
    doc2html:generateXQDocXhtml($indexHtmlPath, $xqdocXmlPath, $xqdocXhtmlPath, $leftMenu, $modulePath)
  )
  else
    error()
  ;

  let $menu := doc2html:createLeftMenu($menu, "index.html", $leftMenu)
  let $modules := doc2html:createModuleTable($leftMenu)
  let $doc := doc2html:generateIndexHtml($indexHtmlPath, $menu, $modules)
  return doc2html:configure-xhtml($doc/*:html, 0, $modulePath)
  ;
};


DHFPROD-3068: inputFlow =/= runflow

DHFPROD-3068: inputFlow =/= runflow

(:
  Copyright 2012-2019 MarkLogic Corporation

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
:)
xquery version "1.0-ml";

module namespace runFlow = "http://marklogic.com/rest-api/transform/ml:inputFlow";

import module namespace consts = "http://marklogic.com/data-hub/consts"
at "/data-hub/4/impl/consts.xqy";

import module namespace debug = "http://marklogic.com/data-hub/debug"
at "/data-hub/4/impl/debug-lib.xqy";

import module namespace flow = "http://marklogic.com/data-hub/flow-lib"
at "/data-hub/4/impl/flow-lib.xqy";

import module namespace trace = "http://marklogic.com/data-hub/trace"
at "/data-hub/4/impl/trace-lib.xqy";

import module namespace perf = "http://marklogic.com/data-hub/perflog-lib"
at "/data-hub/4/impl/perflog-lib.xqy";

declare namespace hub = "http://marklogic.com/data-hub";

declare namespace rapi = "http://marklogic.com/rest-api";

declare %rapi:transaction-mode("query") function inputFlow:transform(
  $context as map:map,
  $params as map:map,
  $content as document-node()
) as document-node()
{
  debug:dump-env("input-flow:transform"),

  perf:log('/transforms/input-flow:transform', function() {
    let $job-id := (map:get($params, "job-id"), sem:uuid-string())[1]
    let $entity-name := map:get($params, 'entity-name')
    let $flow-name := map:get($params, 'flow-name')
    let $uri := map:get($context, 'uri')
    let $flow := flow:get-flow($entity-name, $flow-name, $consts:INPUT_FLOW)
    let $_ :=
      if ($flow) then ()
      else
        fn:error((),"RESTAPI-SRVEXERR", ("404","MISSING_FLOW", "The specified flow " || $entity-name || ":" || $flow-name || " is missing."))

    (: configure the options :)
    let $options as map:map := (
      map:get($params, "options") ! xdmp:unquote(.)/object-node(),
      map:map()
    )[1]
    let $_ := flow:set-default-options($options, $flow)

    let $mainFunc := flow:get-main($flow/hub:main)
    (: this can throw, but we want the REST API to know about problems, so let it :)
    let $envelope := flow:run-flow(
      $job-id, $flow, $uri, $content, $options, $mainFunc
    )

    (: write the trace for the current identifier :)
    let $item-context := map:get($flow:context-queue, $uri)
    let $_ := trace:write-trace($item-context)
    return
      document { $envelope }
  })
};

(:
  Copyright 2012-2019 MarkLogic Corporation

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
:)
xquery version "1.0-ml";

module namespace runFlow = "http://marklogic.com/rest-api/transform/ml:inputFlow";

import module namespace consts = "http://marklogic.com/data-hub/consts"
at "/data-hub/4/impl/consts.xqy";

import module namespace debug = "http://marklogic.com/data-hub/debug"
at "/data-hub/4/impl/debug-lib.xqy";

import module namespace flow = "http://marklogic.com/data-hub/flow-lib"
at "/data-hub/4/impl/flow-lib.xqy";

import module namespace trace = "http://marklogic.com/data-hub/trace"
at "/data-hub/4/impl/trace-lib.xqy";

import module namespace perf = "http://marklogic.com/data-hub/perflog-lib"
at "/data-hub/4/impl/perflog-lib.xqy";

declare namespace hub = "http://marklogic.com/data-hub";

declare namespace rapi = "http://marklogic.com/rest-api";

declare %rapi:transaction-mode("query") function runFlow:transform(
  $context as map:map,
  $params as map:map,
  $content as document-node()
) as document-node()
{
  debug:dump-env("run-flow:transform"),

  perf:log('/transforms/run-flow:transform', function() {
    let $job-id := (map:get($params, "job-id"), sem:uuid-string())[1]
    let $entity-name := map:get($params, 'entity-name')
    let $flow-name := map:get($params, 'flow-name')
    let $uri := map:get($context, 'uri')
    let $flow := flow:get-flow($entity-name, $flow-name, $consts:INPUT_FLOW)
    let $_ :=
      if ($flow) then ()
      else
        fn:error((),"RESTAPI-SRVEXERR", ("404","MISSING_FLOW", "The specified flow " || $entity-name || ":" || $flow-name || " is missing."))

    (: configure the options :)
    let $options as map:map := (
      map:get($params, "options") ! xdmp:unquote(.)/object-node(),
      map:map()
    )[1]
    let $_ := flow:set-default-options($options, $flow)

    let $mainFunc := flow:get-main($flow/hub:main)
    (: this can throw, but we want the REST API to know about problems, so let it :)
    let $envelope := flow:run-flow(
      $job-id, $flow, $uri, $content, $options, $mainFunc
    )

    (: write the trace for the current identifier :)
    let $item-context := map:get($flow:context-queue, $uri)
    let $_ := trace:write-trace($item-context)
    return
      document { $envelope }
  })
};


fixup csd_webap_ui

fixup csd_webap_ui

(:~
: This is a module conttaining configuration options for the openinfoman webap
: @version 1.0
: @see https://github.com/openhie/openinfoman @see http://ihe.net
:
:)

module namespace csd_webui = "https://github.com/openhie/openinfoman/csd_webui";
import module namespace functx = "http://www.functx.com";
import module namespace csd_webconf =  "https://github.com/openhie/openinfoman/csd_webconf";
import module namespace request = "http://exquery.org/ns/request";

declare function csd_webui:generateURL() {
  csd_webui:generateURL(())
};

declare function csd_webui:generateURL($end) {
  let $cend :=
    string-join(
      for $e in $end
      return replace(replace(string($e),'^/+',''),'/+$','')
      ,'/')

  let $cbegin :=
    if (functx:all-whitespace($csd_webconf:baseurl))
    then concat(request:scheme(),"://",request:hostname(),":",request:port())
    else replace($csd_webconf:baseurl, '/+$', '')

  return concat($cbegin,'/',$cend)

};

declare function csd_webui:nocache($response)
{(
  <rest:response>
    <http:response >
      <http:header name="Cache-Control" value="must-revalidate,no-cache,no-store"/>
    </http:response>
  </rest:response>
  ,
  $response
)};

declare function csd_webui:redirect() as element(restxq:redirect) {
  csd_webui:redirect(())
};

declare function csd_webui:redirect($end ) as element(restxq:redirect)
{
  <restxq:redirect>{ csd_webui:generateURL($end }</restxq:redirect>
};

declare function csd_webui:redirect_out() as element(restxq:redirect) {
  csd_webui:redirect_out(())
};

declare function csd_webui:redirect_out($end ) as element(restxq:redirect)
{
  db:output(<restxq:redirect>{ csd_webui:generateURL($end }</restxq:redirect>)
};

declare function csd_webui:wrapper($content) {
  csd_webui:wrapper($content,())
};

declare function csd_webui:wrapper($content,$headers) {

    <link href="{$csd_webui:generateURL('static/bootstrap/css/bootstrap.css')}" rel="stylesheet"/>
    <link href="{$csd_webui:generateURL('static/bootstrap/css/bootstrap-theme.css')}" rel="stylesheet"/>
    <link rel="shortcut icon" href="{$csd_webui:generateURL(,'static/favicon.ico')}"/>

(:~
: This is a module conttaining configuration options for the openinfoman webap
: @version 1.0
: @see https://github.com/openhie/openinfoman @see http://ihe.net
:
:)

module namespace csd_webui = "https://github.com/openhie/openinfoman/csd_webui";
import module namespace csd_webconf =  "https://github.com/openhie/openinfoman/csd_webconf";
import module namespace request = "http://exquery.org/ns/request";

declare function csd_webui:generateURL() {
  csd_webui:generateURL(())
};

declare function csd_webui:generateURL($end) {
  let $cend :=
    string-join(
      for $e in $end
      return replace(replace(string($e),'^/+',''),'/+$','')
      ,'/')

  let $cbegin :=
    if (functx:all-whitespace($csd_webconf:baseurl))
    then concat(request:scheme(),"://",request:hostname(),":",request:port())
    else replace($csd_webconf:baseurl, '/+$', '')

  return concat($cbegin,'/',$cend)

};

declare function csd_webui:nocache($response)
{(
  <rest:response>
    <http:response >
      <http:header name="Cache-Control" value="must-revalidate,no-cache,no-store"/>
    </http:response>
  </rest:response>
  ,
  $response
)};

declare function csd_webui:redirect() as element(restxq:redirect) {
  csd_webui:redirect(())
};

declare function csd_webui:redirect($end ) as element(restxq:redirect)
{
  <restxq:redirect>{ csd_webui:generateURL($end }</restxq:redirect>
};

declare function csd_webui:redirect_out() as element(restxq:redirect) {
  csd_webui:redirect_out(())
};

declare function csd_webui:redirect_out($end ) as element(restxq:redirect)
{
  db:output(<restxq:redirect>{ csd_webui:generateURL($end }</restxq:redirect>)
};

declare function csd_webui:wrapper($content) {
  csd_webui:wrapper($content,())
};

declare function csd_webui:wrapper($content,$headers) {

    <link href="{$csd_webui:generateURL('static/bootstrap/css/bootstrap.css')}" rel="stylesheet"/>
    <link href="{$csd_webui:generateURL('static/bootstrap/css/bootstrap-theme.css')}" rel="stylesheet"/>
    <link rel="shortcut icon" href="{$csd_webui:generateURL(,'static/favicon.ico')}"/>


Add some tests to ensure optimizers handles parenthesized context of filter step properly.

xquery version "3.0";

(:~
 : Test all kinds of XQuery expressions to see if optimizer does properly
 : analyze them and indexes are used in fully optimized manner.
 :
 : Expressions use the @test:stats annotation to retrieve execution statistics
 : for each test function.
 :)
module namespace ot="http://exist-db.org/xquery/optimizer/test";

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

declare variable $ot:COLLECTION_CONFIG :=
    <collection xmlns="http://exist-db.org/collection-config/1.0">
        <index xmlns:xs="http://www.w3.org/2001/XMLSchema">
            <lucene>
                <text qname="name"/>
            </lucene>
            <create qname="name" type="xs:string"/>
        </index>
    </collection>;

declare variable $ot:DATA :=
    <test>
        <address id="muh">
            <name>Berta Muh</name>
            <street>Wiesenweg 14</street>
            <city code="65463">Almweide</city>
        </address>
        <address id="rüssel">
            <name>Rudi Rüssel</name>
            <street>Elefantenweg 67</street>
            <city code="65428">Rüsselsheim</city>
        </address>
        <address id="amsel">
            <name>Albert Amsel</name>
            <street>Birkenstraße 77</street>
            <city code="76878">Waldstadt</city>
        </address>
        <address id="reh">
            <name>Pü Reh</name>
            <street>Am Waldrand 4</street>
            <city code="89283">Wiesental</city>
        </address>
    </test>;

declare variable $ot:COLLECTION_NAME := "optimizertest";
declare variable $ot:COLLECTION := "/db/" || $ot:COLLECTION_NAME;

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

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

declare
    %test:stats
    %test:args("Rudi Rüssel")
    %test:assertXPath("$result//stats:index[@type = 'range'][@optimization = 2]")
function ot:optimize-simple-comparison($name as xs:string) {
    collection($ot:COLLECTION)//address[name = $name]/city/text()
};

declare
    %test:stats
    %test:args("Rüsselsheim")
    %test:assertXPath("$result//stats:index[@type = 'range'][@optimization = 0]")
function ot:no-optimize-simple-comparison($name as xs:string) {
    collection($ot:COLLECTION)//address[city = $name]/city/text()
};

declare
    %test:stats
    %test:args("Rudi Rüssel")
    %test:assertXPath("$result//stats:index[@type = 'range'][@optimization = 2]")
function ot:optimize-sequence($name as xs:string) {
    (collection($ot:COLLECTION)//address[name = $name]/city/text(), "xxx")
};

declare
    %test:stats
    %test:args("Rudi Rüssel")
    %test:assertXPath("$result//stats:index[@type = 'range'][@optimization = 2]")
function ot:optimize-let($name as xs:string) {
    let $city := collection($ot:COLLECTION)//address[name = $name]/city/text()
    return
        $city
};

declare
    %test:stats
    %test:args("Rüsselsheim")
    %test:assertXPath("$result//stats:index[@type = 'range'][@optimization = 0]")
function ot:no-optimize-let($name as xs:string) {
    let $city := collection($ot:COLLECTION)//address[city = $name]/city/text()
    return
        $city
};

declare
    %test:stats
    %test:args("Rudi Rüssel")
    %test:assertXPath("$result//stats:index[@type = 'range'][@optimization = 2]")
function ot:optimize-for($name as xs:string) {
    for $city in collection($ot:COLLECTION)//address[name = $name]/city/text()
    return
        $city
};

declare
    %test:stats
    %test:args("Rüsselsheim")
    %test:assertXPath("$result//stats:index[@type = 'range'][@optimization = 0]")
function ot:no-optimize-for($name as xs:string) {
    for $city in collection($ot:COLLECTION)//address[city = $name]/city/text()
    return
        $city
};

declare
    %test:stats
    %test:args("Rudi Rüssel")
    %test:assertXPath("$result//stats:index[@type = 'range'][@optimization = 2]")
function ot:optimize-if-then($name as xs:string) {
    if (1 = 1) then
        collection($ot:COLLECTION)//address[name = $name]/city/text()
    else
        ()
};

declare
    %test:stats
    %test:args("Rudi Rüssel")
    %test:assertXPath("$result//stats:index[@type = 'range'][@optimization = 2]")
function ot:optimize-if-else($name as xs:string) {
    if (1 = 2) then
        ()
    else
        collection($ot:COLLECTION)//address[name = $name]/city/text()
};

declare %private function ot:find-by-name($name as xs:string) {
    collection($ot:COLLECTION)//address[name = $name]/city/text()
};

declare %private function ot:find-by-city($name as xs:string) {
    collection($ot:COLLECTION)//address[city = $name]/city/text()
};

declare
    %test:stats
    %test:args("Rudi Rüssel")
    %test:assertXPath("$result//stats:index[@type = 'range'][@optimization = 2]")
function ot:optimize-function-call($name as xs:string) {
    ot:find-by-name($name)
};

declare
    %test:stats
    %test:args("Rudi Rüssel")
    %test:assertXPath("$result//stats:index[@type = 'range'][@optimization = 2]")
function ot:no-optimize-function-call($name as xs:string) {
    ot:find-by-name($name)
};

declare
    %test:stats
    %test:args("Rudi Rüssel")
    %test:assertXPath("$result//stats:index[@type = 'range'][@optimization = 2]")
function ot:optimize-try($name as xs:string) {
    try {
        collection($ot:COLLECTION)//address[name = $name]/city/text()
    } catch * {
        ()
    }
};

declare
    %test:stats
    %test:args("Rudi Rüssel")
    %test:assertXPath("$result//stats:index[@type = 'range'][@optimization = 2]")
function ot:optimize-catch($name as xs:string) {
    try {
        xs:int("abc")
    } catch * {
        collection($ot:COLLECTION)//address[name = $name]/city/text()
    }
};

declare
    %test:stats
    %test:args("Rudi Rüssel")
    %test:assertXPath("$result//stats:index[@type = 'range'][@optimization = 2]")
function ot:optimize-attribute-enclosed($name as xs:string) {

};

declare
    %test:stats
    %test:args("Rüsselsheim")
    %test:assertXPath("$result//stats:index[@type = 'range'][@optimization = 0]")
function ot:no-optimize-attribute-enclosed($name as xs:string) {

};

declare
    %test:stats
    %test:args("Rudi Rüssel")
    %test:assertXPath("$result//stats:index[@type = 'range'][@optimization = 2]")
function ot:optimize-element-enclosed($name as xs:string) {
    {collection($ot:COLLECTION)//address[name = $name]/city/text()}
};

declare
    %test:stats
    %test:args("Rudi Rüssel")
    %test:assertXPath("$result//stats:index[@type = 'range'][@optimization = 2]")
function ot:optimize-element-dynamic-enclosed($name as xs:string) {
    element a {
        collection($ot:COLLECTION)//address[name = $name]/city/text()
    }
};

declare
    %test:stats
    %test:args("Rudi Rüssel")
    %test:assertXPath("$result//stats:index[@type = 'range'][@optimization = 2]")
function ot:optimize-attribute-dynamic-enclosed($name as xs:string) {

    {
        attribute title {
            collection($ot:COLLECTION)//address[name = $name]/city/text()
        }
    }

};

declare
    %test:stats
    %test:args("Rudi Rüssel")
    %test:assertXPath("$result//stats:index[@type = 'range'][@optimization = 2]")
function ot:optimize-inline-function($name as xs:string) {
    let $f := function() {
        collection($ot:COLLECTION)//address[name = $name]/city/text()
    }
    return
        $f()
};

declare
    %test:stats
    %test:args("Rudi Rüssel")
    %test:assertXPath("$result//stats:index[@type = 'range'][@optimization = 2]")
function ot:optimize-inline-function-enclosed-attribute($name as xs:string) {
    let $f := function() {
        collection($ot:COLLECTION)//address[name = $name]/city/text()
    }
    return

};

declare
    %test:stats
    %test:args("Rudi Rüssel")
    %test:assertXPath("$result//stats:index[@type = 'range'][@optimization = 2]")
function ot:optimize-inline-function-enclosed($name as xs:string) {
    let $f := function() {
        collection($ot:COLLECTION)//address[name = $name]/city/text()
    }
    return
        {$f()}
};

declare
    %test:stats
    %test:args("Rudi Rüssel")
    %test:assertXPath("$result//stats:index[@type = 'range'][@optimization = 2]")
function ot:optimize-function-reference($name as xs:string) {
    let $f := ot:find-by-name#1
    return
        $f($name)
};

declare
    %test:stats
    %test:args("Rudi Rüssel")
    %test:assertXPath("$result//stats:index[@type = 'range'][@optimization = 2]")
function ot:optimize-function-map($name as xs:string) {
    let $f := ot:find-by-name#1
    return
        map($f, $name)
};

declare
    %test:stats
    %test:args("Rudi Rüssel")
    %test:assertXPath("$result//stats:index[@type = 'range'][@optimization = 2]")
function ot:optimize-function-partial($name as xs:string) {
    let $f1 := function($foo, $name) {
        ot:find-by-name($name)
    }
    let $f2 := $f1("xxx", ?)
    return
        $f2($name)
};

declare
    %test:stats
    %test:args(1, "Rudi Rüssel")
    %test:assertXPath("$result//stats:index[@type = 'range'][@optimization = 2]")
    %test:args(2, "Rüsselsheim")
    %test:assertXPath("$result//stats:index[@type = 'range'][@optimization = 0]")
    %test:args(3, "Rudi Rüssel")
    %test:assertXPath("$result//stats:index[@type = 'range'][@optimization = 2]")
function ot:optimize-switch($case as xs:int, $name as xs:string) {
    switch($case)
        case 1 return
            collection($ot:COLLECTION)//address[name = $name]/city/text()
        case 2 return
            collection($ot:COLLECTION)//address[city = $name]/city/text()
        default return
            collection($ot:COLLECTION)//address[name = $name]/city/text()
};

declare
    %test:stats
    %test:args("", "Rudi Rüssel")
    %test:assertXPath("$result//stats:index[@type = 'range'][@optimization = 2]")
    %test:args("", "Rüsselsheim")
    %test:assertXPath("$result//stats:index[@type = 'range'][@optimization = 0]")
    %test:args("<c/>", "Rudi Rüssel")
    %test:assertXPath("$result//stats:index[@type = 'range'][@optimization = 2]")
function ot:optimize-typeswitch($case as element(), $name as xs:string) {
    typeswitch($case)
        case element(a) return
            collection($ot:COLLECTION)//address[name = $name]/city/text()
        case element(b) return
            collection($ot:COLLECTION)//address[city = $name]/city/text()
        default return
            collection($ot:COLLECTION)//address[name = $name]/city/text()
};

declare
    %test:stats
    %test:args("Rudi Rüssel")
    %test:assertXPath("$result//stats:index[@type = 'range'][@optimization = 2]")
function ot:optimize-map($name as xs:string) {
    let $map := map {
        "key" := collection($ot:COLLECTION)//address[name = $name]/city/text()
    }
    return
        $map("key")
};

declare
    %test:stats
    %test:args("Rudi Rüssel")
    %test:assertXPath("$result//stats:index[@type = 'range'][@optimization = 2]")
function ot:optimize-map-entry($name as xs:string) {
    let $map := map:entry(
        "key", collection($ot:COLLECTION)//address[name = $name]/city/text()
    )
    return
        $map("key")
};

declare
    %test:stats
    %test:args("Rudi Rüssel")
    %test:assertXPath("$result//stats:function[@calls = 1]")
function ot:optimize-self($name as xs:string) {
    collection($ot:COLLECTION)//address/name[self::name = $name]
};

declare
    %test:stats
    %test:args("Rudi Rüssel")
    %test:assertXPath("$result//stats:function[@calls = 1]")
function ot:optimize-self-element($name as xs:string) {
    collection($ot:COLLECTION)//address/name[self::* = $name]
};

declare
    %test:stats
    %test:args("Rudi Rüssel")
    %test:assertXPath("$result//stats:index[@calls = 1]")
function ot:optimize-grouped-context($name as xs:string) {
    collection($ot:COLLECTION)//(name)[ft:query(., $name)]
};

declare
    %test:stats
    %test:args("Rudi Rüssel")
    %test:assertXPath("$result//stats:index[@calls = 1]")
function ot:optimize-grouped-context2($name as xs:string) {
    collection($ot:COLLECTION)//(name|foo)[ft:query(., $name)]
};

xquery version "3.0";

(:~
 : Test all kinds of XQuery expressions to see if optimizer does properly
 : analyze them and indexes are used in fully optimized manner.
 :
 : Expressions use the @test:stats annotation to retrieve execution statistics
 : for each test function.
 :)
module namespace ot="http://exist-db.org/xquery/optimizer/test";

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

declare variable $ot:COLLECTION_CONFIG :=
    <collection xmlns="http://exist-db.org/collection-config/1.0">
        <index xmlns:xs="http://www.w3.org/2001/XMLSchema">
            <create qname="name" type="xs:string"/>
        </index>
    </collection>;

declare variable $ot:DATA :=
    <test>
        <address id="muh">
            <name>Berta Muh</name>
            <street>Wiesenweg 14</street>
            <city code="65463">Almweide</city>
        </address>
        <address id="rüssel">
            <name>Rudi Rüssel</name>
            <street>Elefantenweg 67</street>
            <city code="65428">Rüsselsheim</city>
        </address>
        <address id="amsel">
            <name>Albert Amsel</name>
            <street>Birkenstraße 77</street>
            <city code="76878">Waldstadt</city>
        </address>
        <address id="reh">
            <name>Pü Reh</name>
            <street>Am Waldrand 4</street>
            <city code="89283">Wiesental</city>
        </address>
    </test>;

declare variable $ot:COLLECTION_NAME := "optimizertest";
declare variable $ot:COLLECTION := "/db/" || $ot:COLLECTION_NAME;

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

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

declare
    %test:stats
    %test:args("Rudi Rüssel")
    %test:assertXPath("$result//stats:index[@type = 'range'][@optimization = 2]")
function ot:optimize-simple-comparison($name as xs:string) {
    collection($ot:COLLECTION)//address[name = $name]/city/text()
};

declare
    %test:stats
    %test:args("Rüsselsheim")
    %test:assertXPath("$result//stats:index[@type = 'range'][@optimization = 0]")
function ot:no-optimize-simple-comparison($name as xs:string) {
    collection($ot:COLLECTION)//address[city = $name]/city/text()
};

declare
    %test:stats
    %test:args("Rudi Rüssel")
    %test:assertXPath("$result//stats:index[@type = 'range'][@optimization = 2]")
function ot:optimize-sequence($name as xs:string) {
    (collection($ot:COLLECTION)//address[name = $name]/city/text(), "xxx")
};

declare
    %test:stats
    %test:args("Rudi Rüssel")
    %test:assertXPath("$result//stats:index[@type = 'range'][@optimization = 2]")
function ot:optimize-let($name as xs:string) {
    let $city := collection($ot:COLLECTION)//address[name = $name]/city/text()
    return
        $city
};

declare
    %test:stats
    %test:args("Rüsselsheim")
    %test:assertXPath("$result//stats:index[@type = 'range'][@optimization = 0]")
function ot:no-optimize-let($name as xs:string) {
    let $city := collection($ot:COLLECTION)//address[city = $name]/city/text()
    return
        $city
};

declare
    %test:stats
    %test:args("Rudi Rüssel")
    %test:assertXPath("$result//stats:index[@type = 'range'][@optimization = 2]")
function ot:optimize-for($name as xs:string) {
    for $city in collection($ot:COLLECTION)//address[name = $name]/city/text()
    return
        $city
};

declare
    %test:stats
    %test:args("Rüsselsheim")
    %test:assertXPath("$result//stats:index[@type = 'range'][@optimization = 0]")
function ot:no-optimize-for($name as xs:string) {
    for $city in collection($ot:COLLECTION)//address[city = $name]/city/text()
    return
        $city
};

declare
    %test:stats
    %test:args("Rudi Rüssel")
    %test:assertXPath("$result//stats:index[@type = 'range'][@optimization = 2]")
function ot:optimize-if-then($name as xs:string) {
    if (1 = 1) then
        collection($ot:COLLECTION)//address[name = $name]/city/text()
    else
        ()
};

declare
    %test:stats
    %test:args("Rudi Rüssel")
    %test:assertXPath("$result//stats:index[@type = 'range'][@optimization = 2]")
function ot:optimize-if-else($name as xs:string) {
    if (1 = 2) then
        ()
    else
        collection($ot:COLLECTION)//address[name = $name]/city/text()
};

declare %private function ot:find-by-name($name as xs:string) {
    collection($ot:COLLECTION)//address[name = $name]/city/text()
};

declare %private function ot:find-by-city($name as xs:string) {
    collection($ot:COLLECTION)//address[city = $name]/city/text()
};

declare
    %test:stats
    %test:args("Rudi Rüssel")
    %test:assertXPath("$result//stats:index[@type = 'range'][@optimization = 2]")
function ot:optimize-function-call($name as xs:string) {
    ot:find-by-name($name)
};

declare
    %test:stats
    %test:args("Rudi Rüssel")
    %test:assertXPath("$result//stats:index[@type = 'range'][@optimization = 2]")
function ot:no-optimize-function-call($name as xs:string) {
    ot:find-by-name($name)
};

declare
    %test:stats
    %test:args("Rudi Rüssel")
    %test:assertXPath("$result//stats:index[@type = 'range'][@optimization = 2]")
function ot:optimize-try($name as xs:string) {
    try {
        collection($ot:COLLECTION)//address[name = $name]/city/text()
    } catch * {
        ()
    }
};

declare
    %test:stats
    %test:args("Rudi Rüssel")
    %test:assertXPath("$result//stats:index[@type = 'range'][@optimization = 2]")
function ot:optimize-catch($name as xs:string) {
    try {
        xs:int("abc")
    } catch * {
        collection($ot:COLLECTION)//address[name = $name]/city/text()
    }
};

declare
    %test:stats
    %test:args("Rudi Rüssel")
    %test:assertXPath("$result//stats:index[@type = 'range'][@optimization = 2]")
function ot:optimize-attribute-enclosed($name as xs:string) {

};

declare
    %test:stats
    %test:args("Rüsselsheim")
    %test:assertXPath("$result//stats:index[@type = 'range'][@optimization = 0]")
function ot:no-optimize-attribute-enclosed($name as xs:string) {

};

declare
    %test:stats
    %test:args("Rudi Rüssel")
    %test:assertXPath("$result//stats:index[@type = 'range'][@optimization = 2]")
function ot:optimize-element-enclosed($name as xs:string) {
    {collection($ot:COLLECTION)//address[name = $name]/city/text()}
};

declare
    %test:stats
    %test:args("Rudi Rüssel")
    %test:assertXPath("$result//stats:index[@type = 'range'][@optimization = 2]")
function ot:optimize-element-dynamic-enclosed($name as xs:string) {
    element a {
        collection($ot:COLLECTION)//address[name = $name]/city/text()
    }
};

declare
    %test:stats
    %test:args("Rudi Rüssel")
    %test:assertXPath("$result//stats:index[@type = 'range'][@optimization = 2]")
function ot:optimize-attribute-dynamic-enclosed($name as xs:string) {

    {
        attribute title {
            collection($ot:COLLECTION)//address[name = $name]/city/text()
        }
    }

};

declare
    %test:stats
    %test:args("Rudi Rüssel")
    %test:assertXPath("$result//stats:index[@type = 'range'][@optimization = 2]")
function ot:optimize-inline-function($name as xs:string) {
    let $f := function() {
        collection($ot:COLLECTION)//address[name = $name]/city/text()
    }
    return
        $f()
};

declare
    %test:stats
    %test:args("Rudi Rüssel")
    %test:assertXPath("$result//stats:index[@type = 'range'][@optimization = 2]")
function ot:optimize-inline-function-enclosed-attribute($name as xs:string) {
    let $f := function() {
        collection($ot:COLLECTION)//address[name = $name]/city/text()
    }
    return

};

declare
    %test:stats
    %test:args("Rudi Rüssel")
    %test:assertXPath("$result//stats:index[@type = 'range'][@optimization = 2]")
function ot:optimize-inline-function-enclosed($name as xs:string) {
    let $f := function() {
        collection($ot:COLLECTION)//address[name = $name]/city/text()
    }
    return
        {$f()}
};

declare
    %test:stats
    %test:args("Rudi Rüssel")
    %test:assertXPath("$result//stats:index[@type = 'range'][@optimization = 2]")
function ot:optimize-function-reference($name as xs:string) {
    let $f := ot:find-by-name#1
    return
        $f($name)
};

declare
    %test:stats
    %test:args("Rudi Rüssel")
    %test:assertXPath("$result//stats:index[@type = 'range'][@optimization = 2]")
function ot:optimize-function-map($name as xs:string) {
    let $f := ot:find-by-name#1
    return
        map($f, $name)
};

declare
    %test:stats
    %test:args("Rudi Rüssel")
    %test:assertXPath("$result//stats:index[@type = 'range'][@optimization = 2]")
function ot:optimize-function-partial($name as xs:string) {
    let $f1 := function($foo, $name) {
        ot:find-by-name($name)
    }
    let $f2 := $f1("xxx", ?)
    return
        $f2($name)
};

declare
    %test:stats
    %test:args(1, "Rudi Rüssel")
    %test:assertXPath("$result//stats:index[@type = 'range'][@optimization = 2]")
    %test:args(2, "Rüsselsheim")
    %test:assertXPath("$result//stats:index[@type = 'range'][@optimization = 0]")
    %test:args(3, "Rudi Rüssel")
    %test:assertXPath("$result//stats:index[@type = 'range'][@optimization = 2]")
function ot:optimize-switch($case as xs:int, $name as xs:string) {
    switch($case)
        case 1 return
            collection($ot:COLLECTION)//address[name = $name]/city/text()
        case 2 return
            collection($ot:COLLECTION)//address[city = $name]/city/text()
        default return
            collection($ot:COLLECTION)//address[name = $name]/city/text()
};

declare
    %test:stats
    %test:args("", "Rudi Rüssel")
    %test:assertXPath("$result//stats:index[@type = 'range'][@optimization = 2]")
    %test:args("", "Rüsselsheim")
    %test:assertXPath("$result//stats:index[@type = 'range'][@optimization = 0]")
    %test:args("<c/>", "Rudi Rüssel")
    %test:assertXPath("$result//stats:index[@type = 'range'][@optimization = 2]")
function ot:optimize-typeswitch($case as element(), $name as xs:string) {
    typeswitch($case)
        case element(a) return
            collection($ot:COLLECTION)//address[name = $name]/city/text()
        case element(b) return
            collection($ot:COLLECTION)//address[city = $name]/city/text()
        default return
            collection($ot:COLLECTION)//address[name = $name]/city/text()
};

declare
    %test:stats
    %test:args("Rudi Rüssel")
    %test:assertXPath("$result//stats:index[@type = 'range'][@optimization = 2]")
function ot:optimize-map($name as xs:string) {
    let $map := map {
        "key" := collection($ot:COLLECTION)//address[name = $name]/city/text()
    }
    return
        $map("key")
};

declare
    %test:stats
    %test:args("Rudi Rüssel")
    %test:assertXPath("$result//stats:index[@type = 'range'][@optimization = 2]")
function ot:optimize-map-entry($name as xs:string) {
    let $map := map:entry(
        "key", collection($ot:COLLECTION)//address[name = $name]/city/text()
    )
    return
        $map("key")
};


[bug] fixed deployment of xquery files

[bug] fixed deployment of xquery files

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

xquery version "1.0";

module namespace repomanager="http://exist-db.org/xquery/admin-interface/repo";

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

declare variable $repomanager:coll := "/db/system/repo";
declare variable $repomanager:repo-uri := if (request:get-parameter("repository-url", ())) then
              request:get-parameter("repository-url", ())
            else
              "http://demo.exist-db.org/exist/repo/public/all/";

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

	<entry>
		{
		if ($path eq tokenize($path, "/")[last()]) then
		  (<name>{tokenize($path, "/")[last()]}</name>,
           <path>{$path}</path>)
		else
		  (<name>{tokenize($path, "/")[last()]}</name>,
		  <path>{
		  let $segments := tokenize($path,"/")[not(position() eq last())]
		  for $segment in $segments
		  return

		    <segment>{$segment}</segment>

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

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

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

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

declare function repomanager:publicrepo() as element()
{
let $package-url := request:get-parameter("package-url", ())
return

Actions:

                {
                if (ends-with($package-url,'.xar')) then
                    (
uploaded package: {$package-url}
,

                    let $http-response := httpclient:get(xs:anyURI($package-url), false(), ())
                    let $name := tokenize($package-url, "/")[last()]
                    return
                    let $package-mimetype := "application/xar",
                    $package-data := xs:base64Binary($http-response/httpclient:body/text())
                    return
                    xmldb:store($repomanager:coll, $name, $package-data, $package-mimetype)
                    )
                else

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

                }

};

declare function repomanager:upload() as element()
{
    let $name := request:get-parameter("name", ()),
    $repocol :=  if (collection($repomanager:coll)) then () else xmldb:create-collection('/db/system','repo'),
    $docName := if($name) then $name else request:get-uploaded-file-name("upload"),
    $file := request:get-uploaded-file-data("upload") return

Actions:

                {
                if ($docName) then
                    (
uploaded package: {$docName}
,

                    xdb:decode-uri(xs:anyURI(xdb:store($repomanager:coll, xdb:encode-uri($docName), $file)))
                    )
                else

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

                }

};

declare function repomanager:deploy() as element()
{
    let $name := request:get-parameter("name", ())
     return

Actions:

deployed application: {$name}

                {
                    let $xar := util:binary-doc(concat($repomanager:coll,'/',$name,'.xar'))
                    let $application := compression:unzip($xar, util:function(xs:QName("local:entry-filter"), 3), (),  util:function(xs:QName("local:entry-data-deploy"), 4), ())
                    let $repo := $application//repo:meta
                    (: let $createrootcoll := xmldb:create-collection() :)
                    return

                        {
                        for $entry in $application
                        return

                         {if ($entry/type eq 'folder') then
                            let $p := $entry/path/segment/node()
                            let $l := $entry/path/segment[last()]/node()
                            let $p1 := string-join($p,'/')
                            let $colpath := concat('/db/',$p1)
                            return
                                 ('folder created: ',xmldb:create-collection(substring-before($colpath,$l), $l))
                         else
                            let $p := $entry/path/segment/node()
                            let $l := $entry/path/segment[last()]/node()
                            let $p1 := string-join($p,'/')
                            let $colpath := concat('/db/',$p1,'/')
                            return
                                if ($entry/name eq 'repo.xml' or $entry/name eq 'expath-pkg.xml')then
                                 ('repo meta file ignored: ', $entry/name)
                                else if (ends-with($entry/name,'.xml') or contains($entry/name,'.htm')) then
                                 ('file created: ',
                                    xmldb:store($colpath, $entry/name/node(), $entry/data/node())
                                 )
                                else
                                 ('file created: ',
                                    xmldb:store($colpath, $entry/name/node(), util:base64-decode($entry/data/text()))
                                 )

                        }

                        }

                }

};

declare function repomanager:activate() as element()
{
    let $name := request:get-parameter("name", ()),
    $hostname := request:get-hostname(),            (:  pkg-repo.jar needs http or file scheme :)
    $port := request:get-server-port()
     return

Actions:

activated package: {$name}

                {
                    repo:install(concat('http://',$hostname,':',$port,'/exist/rest',$repomanager:coll,'/',$name,'.xar'))
                }

};

declare function repomanager:deactivate() as element()
{
    let $name := request:get-parameter("name", ())
     return

Actions:

deactivated package: {$name}

                {
                    repo:remove($name)
                }

};

declare function repomanager:remove() as element()
{
    let $name := request:get-parameter("name", ())
     return

Actions:

removed package: {$name}

                {xmldb:remove($repomanager:coll,concat($name,'.xar'))}

};

declare function repomanager:process-action() as element()*
{
    let $action := request:get-parameter("action", ()) return
        util:catch("java.lang.Exception",
            if($action eq "remove") then
            (
                repomanager:remove()
            )
            else if($action eq "activate") then
            (
                repomanager:activate()
            )
            else if($action eq "deactivate") then
            (
                repomanager:deactivate()
            )
            else if($action eq "Upload Package") then
            (
                repomanager:upload()
            )
            else if($action eq "deploy") then
            (
                repomanager:deploy()
            )
            else if($action eq "Download from Public Repository") then
            (
                repomanager:publicrepo()
            )else(),

                An error occurred while processing the action:

                {$util:exception-message}

        )
};

declare function repomanager:main() as element() {
    let $action := lower-case(request:get-parameter("action", "set repository"))
    let $repocol :=  if (collection($repomanager:coll)) then () else xmldb:create-collection('/db/system','repo')

    return

Package Repository

        <form method="POST" enctype="multipart/form-data">
        { repomanager:process-action() }

                Name
                Description
                Date Installed
                Status
                Action

         {
         let $files := if (collection($repomanager:coll)) then collection($repomanager:coll)/util:document-name(.) else ()
         let $repos := repo:list()
         return

            for $file in $files[contains(.,'.xar')]
            let $package-name := substring-before($file,'.xar')
            let $xar := util:binary-doc(concat($repomanager:coll,'/',$file))

            let $meta := compression:unzip($xar, util:function(xs:QName("local:entry-filter"), 3), (),  util:function(xs:QName("local:entry-data"), 4), ())
            let $package := $meta//package:package
            let $repo := $meta//repo:meta

            let $installed := exists($repos[. eq $package-name])
            return

                {$package-name}

                {$repo//repo:type}
                {$repo//repo:description}

                {xmldb:last-modified($repomanager:coll, concat($package-name,'.xar'))}

                {if ($installed) then
                    Active
                else
                    Inactive
                }

                {
                if($installed) then

                    ( deactivate,
                      if ($repo//repo:type eq 'application') then
                        ( ' | ',deploy )
                     else
                        ()
                     )
                 else
                 ( activate,' | ',
                 remove
                 )
                }

            }

                Important: You must restart eXistdb to pick up any changes in repository.

                        <input type="submit" name="action" value="Download from Public Repository"/>

                        <select size="5" width="200" style="width:340px" name="package-url">
                           {
                            let $packages := httpclient:get(xs:anyURI($repomanager:repo-uri),false(),())//httpclient:body/node()
                             return
                             for $package in $packages//package[contains(url/.,'.xar')]
                             return
                                <option value="{$package/url}">{tokenize($package/url, "/")[last()]}</option>
                           }
                        </select>

                         Public Repository URL :

                        <input name="repository-url" size="40" value="{$repomanager:repo-uri}"/>
                        <input type="submit" name="action" value="set repository"/>

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

                Note: You can upload example .xar packages located under webapp/repo/packages
          </form>

};

xquery version "1.0";

module namespace repomanager="http://exist-db.org/xquery/admin-interface/repo";

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

declare variable $repomanager:coll := "/db/system/repo";
declare variable $repomanager:repo-uri := if (request:get-parameter("repository-url", ())) then
              request:get-parameter("repository-url", ())
            else
              "http://demo.exist-db.org/exist/repo/public/all/";

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

	<entry>
		{
		if ($path eq tokenize($path, "/")[last()]) then
		  (<name>{tokenize($path, "/")[last()]}</name>,
           <path>{$path}</path>)
		else
		  (<name>{tokenize($path, "/")[last()]}</name>,
		  <path>{
		  let $segments := tokenize($path,"/")[not(position() eq last())]
		  for $segment in $segments
		  return

		    <segment>{$segment}</segment>

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

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

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

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

declare function repomanager:publicrepo() as element()
{
let $package-url := request:get-parameter("package-url", ())
return

Actions:

                {
                if (ends-with($package-url,'.xar')) then
                    (
uploaded package: {$package-url}
,

                    let $http-response := httpclient:get(xs:anyURI($package-url), false(), ())
                    let $name := tokenize($package-url, "/")[last()]
                    return
                    let $package-mimetype := "application/xar",
                    $package-data := xs:base64Binary($http-response/httpclient:body/text())
                    return
                    xmldb:store($repomanager:coll, $name, $package-data, $package-mimetype)
                    )
                else

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

                }

};

declare function repomanager:upload() as element()
{
    let $name := request:get-parameter("name", ()),
    $repocol :=  if (collection($repomanager:coll)) then () else xmldb:create-collection('/db/system','repo'),
    $docName := if($name) then $name else request:get-uploaded-file-name("upload"),
    $file := request:get-uploaded-file-data("upload") return

Actions:

                {
                if ($docName) then
                    (
uploaded package: {$docName}
,

                    xdb:decode-uri(xs:anyURI(xdb:store($repomanager:coll, xdb:encode-uri($docName), $file)))
                    )
                else

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

                }

};

declare function repomanager:deploy() as element()
{
    let $name := request:get-parameter("name", ())
     return

Actions:

deployed application: {$name}

                {
                    let $xar := util:binary-doc(concat($repomanager:coll,'/',$name,'.xar'))
                    let $application := compression:unzip($xar, util:function(xs:QName("local:entry-filter"), 3), (),  util:function(xs:QName("local:entry-data-deploy"), 4), ())
                    let $repo := $application//repo:meta
                    (: let $createrootcoll := xmldb:create-collection() :)
                    return

                        {
                        for $entry in $application
                        return

                         {if ($entry/type eq 'folder') then
                            let $p := $entry/path/segment/node()
                            let $l := $entry/path/segment[last()]/node()
                            let $p1 := string-join($p,'/')
                            let $colpath := concat('/db/',$p1)
                            return
                                 ('folder created: ',xmldb:create-collection(substring-before($colpath,$l), $l))
                         else
                            let $p := $entry/path/segment/node()
                            let $l := $entry/path/segment[last()]/node()
                            let $p1 := string-join($p,'/')
                            let $colpath := concat('/db/',$p1,'/')
                            return
                                if ($entry/name eq 'repo.xml' or $entry/name eq 'expath-pkg.xml')then
                                 ('repo meta file ignored: ', $entry/name)
                                else
                                 ('file created: ',

                                    xmldb:store($colpath, $entry/name/node(), $entry/data/node())
                                 )

                        }

                        }

                }

};

declare function repomanager:activate() as element()
{
    let $name := request:get-parameter("name", ()),
    $hostname := request:get-hostname(),            (:  pkg-repo.jar needs http or file scheme :)
    $port := request:get-server-port()
     return

Actions:

activated package: {$name}

                {
                    repo:install(concat('http://',$hostname,':',$port,'/exist/rest',$repomanager:coll,'/',$name,'.xar'))
                }

};

declare function repomanager:deactivate() as element()
{
    let $name := request:get-parameter("name", ())
     return

Actions:

deactivated package: {$name}

                {
                    repo:remove($name)
                }

};

declare function repomanager:remove() as element()
{
    let $name := request:get-parameter("name", ())
     return

Actions:

removed package: {$name}

                {xmldb:remove($repomanager:coll,concat($name,'.xar'))}

};

declare function repomanager:process-action() as element()*
{
    let $action := request:get-parameter("action", ()) return
        util:catch("java.lang.Exception",
            if($action eq "remove") then
            (
                repomanager:remove()
            )
            else if($action eq "activate") then
            (
                repomanager:activate()
            )
            else if($action eq "deactivate") then
            (
                repomanager:deactivate()
            )
            else if($action eq "Upload Package") then
            (
                repomanager:upload()
            )
            else if($action eq "deploy") then
            (
                repomanager:deploy()
            )
            else if($action eq "Download from Public Repository") then
            (
                repomanager:publicrepo()
            )else(),

                An error occurred while processing the action:

                {$util:exception-message}

        )
};

declare function repomanager:main() as element() {
    let $action := lower-case(request:get-parameter("action", "set repository"))
    let $repocol :=  if (collection($repomanager:coll)) then () else xmldb:create-collection('/db/system','repo')

    return

Package Repository

        <form method="POST" enctype="multipart/form-data">
        { repomanager:process-action() }

                Name
                Description
                Date Installed
                Status
                Action

         {
         let $files := if (collection($repomanager:coll)) then collection($repomanager:coll)/util:document-name(.) else ()
         let $repos := repo:list()
         return

            for $file in $files[contains(.,'.xar')]
            let $package-name := substring-before($file,'.xar')
            let $xar := util:binary-doc(concat($repomanager:coll,'/',$file))

            let $meta := compression:unzip($xar, util:function(xs:QName("local:entry-filter"), 3), (),  util:function(xs:QName("local:entry-data"), 4), ())
            let $package := $meta//package:package
            let $repo := $meta//repo:meta

            let $installed := exists($repos[. eq $package-name])
            return

                {$package-name}

                {$repo//repo:type}
                {$repo//repo:description}

                {xmldb:last-modified($repomanager:coll, concat($package-name,'.xar'))}

                {if ($installed) then
                    Active
                else
                    Inactive
                }

                {
                if($installed) then

                    ( deactivate,
                      if ($repo//repo:type eq 'application') then
                        ( ' | ',deploy )
                     else
                        ()
                     )
                 else
                 ( activate,' | ',
                 remove
                 )
                }

            }

                Important: You must restart eXistdb to pick up any changes in repository.

                        <input type="submit" name="action" value="Download from Public Repository"/>

                        <select size="5" width="200" style="width:340px" name="package-url">
                           {
                            let $packages := httpclient:get(xs:anyURI($repomanager:repo-uri),false(),())//httpclient:body/node()
                             return
                             for $package in $packages//package[contains(url/.,'.xar')]
                             return
                                <option value="{$package/url}">{tokenize($package/url, "/")[last()]}</option>
                           }
                        </select>

                         Public Repository URL :

                        <input name="repository-url" size="40" value="{$repomanager:repo-uri}"/>
                        <input type="submit" name="action" value="set repository"/>

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

                Note: You can upload example .xar packages located under webapp/repo/packages
          </form>

};


[ignore] reformatting the  controller query

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

(: 	This is the main controller for the web application. It is called from the
	XQueryURLRewrite filter configured in web.xml. :)
xquery version "1.0";

(:~ -------------------------------------------------------
    Main controller: handles all requests not matched by
    sub-controllers.
    ------------------------------------------------------- :)

declare namespace c="http://exist-db.org/xquery/controller";

import module namespace request="http://exist-db.org/xquery/request";
import module namespace xdb = "http://exist-db.org/xquery/xmldb";

let $query := request:get-parameter("q", ())
return
	(: redirect webapp root to index.xml :)
    if ($exist:path eq '/') then
		<dispatch xmlns="http://exist.sourceforge.net/NS/exist">
			<redirect url="index.xml"/>
		</dispatch>

	(: ignore Cocoon :)
	else if (matches($exist:path, "/cocoon")) then
		<ignore xmlns="http://exist.sourceforge.net/NS/exist">
            <cache-control cache="yes"/>
		</ignore>

	else if ($exist:resource eq 'applications.xml') then
		<dispatch xmlns="http://exist.sourceforge.net/NS/exist">
			<!-- query results are passed to XSLT servlet via request attribute -->
			<set-attribute name="xquery.attribute" value="model"/>
			<view>
				<forward servlet="XSLTServlet">
					<set-attribute name="xslt.input" value="model"/>
					<set-attribute name="xslt.stylesheet" value="apps/applications.xsl"/>
				</forward>
				<forward servlet="XSLTServlet">
					<set-attribute name="xslt.input" value=""/>
					<set-attribute name="xslt.stylesheet" value="stylesheets/db2html.xsl"/>
				</forward>
			</view>
		</dispatch>

	else if ($exist:resource eq 'articles') then
		<dispatch xmlns="http://exist.sourceforge.net/NS/exist">
			<forward url="xquery/exist-articles.xql">
				<!-- query results are passed to XSLT servlet via request attribute -->
				<set-attribute name="xquery.attribute" value="model"/>
			</forward>
			<view>
				<forward servlet="XSLTServlet">
					<set-attribute name="xslt.input" value="model"/>
					<set-attribute name="xslt.stylesheet" value="stylesheets/db2xhtml.xsl"/>
				</forward>
			</view>
		</dispatch>

    else if ($exist:path = "/xforms/idxconf.xml") then
        let $log := util:log("DEBUG", "in xforms rule") return
        <dispatch xmlns="http://exist.sourceforge.net/NS/exist">
            <forward url="idxconf.xql"/>
			<!--view>
				<forward servlet="XSLTServlet">
					<set-attribute name="xslt.stylesheet" value="xforms/xsltforms/xsltforms.xsl"/>
				</forward>
			</view-->
            <cache-control cache="no"/>
		</dispatch>

	(: the following xml files use different stylesheets :)
	else if ($exist:resource = ('index.xml', 'roadmap.xml', 'facts.xml') or $exist:path = '/examples.xml') then
		let $stylesheet :=
			if ($exist:resource eq 'roadmap.xml') then
				"stylesheets/roadmap.xsl"
			else if ($exist:resource eq "facts.xml") then
				"stylesheets/facts.xsl"
			else
				"stylesheets/doc2html-2.xsl"
		return
			<dispatch xmlns="http://exist.sourceforge.net/NS/exist">
				<view>
					<forward servlet="XSLTServlet">
						<set-attribute name="xslt.stylesheet" value="{$stylesheet}"/>
						<set-attribute name="xslt.exist.version" value="{system:get-version()}-rev{system:get-revision()}-{system:get-build()}"/>
					</forward>
				</view>
				<cache-control cache="no"/>
			</dispatch>

	else if (ends-with($exist:resource, '.xml')) then
		(: 	check if the requested document was selected from a query result.
			if yes, pass it to docs.xql to get the match highlighting. :)
		if ($query) then
				<dispatch xmlns="http://exist.sourceforge.net/NS/exist">
					<forward url="/xquery/docs.xql">
						<add-parameter name="path" value="/db/xqdocs/{$exist:resource}"/>
					</forward>
					<view>
    					<forward servlet="XSLTServlet">
    						<set-attribute name="xslt.stylesheet" value="stylesheets/db2xhtml.xsl"/>
    					    <set-attribute name="xslt.output.media-type" value="text/html"/>
    						<set-attribute name="xslt.output.doctype-public" value="-//W3C//DTD XHTML 1.0 Transitional//EN"/>
    						<set-attribute name="xslt.output.doctype-system" value="resources/xhtml1-transitional.dtd"/>
    						<set-attribute name="xslt.output.add-exist-id" value="all"/>
    					    <set-attribute name="xslt.highlight-matches" value="all"/>
    				        <set-attribute name="xslt.xinclude-path" value=".."/>
    				        <set-attribute name="xslt.root" value="{request:get-context-path()}{$exist:prefix}"/>
    					</forward>
    				</view>
				</dispatch>

		else
			<dispatch xmlns="http://exist.sourceforge.net/NS/exist">
				<view>
					<forward servlet="XSLTServlet">
						<set-attribute name="xslt.stylesheet" value="stylesheets/db2xhtml.xsl"/>
					    <set-attribute name="xslt.output.media-type" value="text/html"/>
						<set-attribute name="xslt.output.doctype-public" value="-//W3C//DTD XHTML 1.0 Transitional//EN"/>
						<set-attribute name="xslt.output.doctype-system" value="resources/xhtml1-transitional.dtd"/>
						<set-attribute name="xslt.root" value="{request:get-context-path()}{$exist:prefix}"/>
					    {
					        if ($exist:resource eq 'download.xml') then
					            <set-attribute name="xslt.table-of-contents" value="'no'"/>
			                else
			                    ()
	                    }
					</forward>
				</view>
            	<cache-control cache="no"/>
			</dispatch>

	else if (ends-with($exist:path, '/dump')) then
		let $newPath := substring-before($exist:path, '/dump')
		return
        	<dispatch xmlns="http://exist.sourceforge.net/NS/exist">
				<forward url="{$exist:controller}{$newPath}"/>
				<view>
					<forward url="xquery/dump.xql">
					   <set-attribute name="controller" value="{$exist:controller}"/>
					   <set-attribute name="path" value="{$exist:path}"/>
					   <set-attribute name="resource" value="{$exist:resource}"/>
				    </forward>
				</view>
			</dispatch>

	else
		<ignore xmlns="http://exist.sourceforge.net/NS/exist">
            <cache-control cache="yes"/>
		</ignore>