Fixed the module dependency graph generation.

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

(:
 : author Sorin Nasoi
 :)

module namespace z = "http://www.zorba-xquery.com/module-dependencies";

declare namespace an = "http://www.zorba-xquery.com/annotations";
declare namespace zm = "http://www.zorba-xquery.com/manifest";

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

import module namespace dot         = "http://www.zorba-xquery.com/modules/image/graphviz";
import module namespace xqd         = "http://www.zorba-xquery.com/modules/xqdoc";
import module namespace xqdoc2html  = "http://www.zorba-xquery.com/modules/xqdoc2xhtml/";
import module namespace pxqdoc      = "http://www.zorba-xquery.com/modules/project_xqdoc";
import module namespace fetch       = "http://www.zorba-xquery.com/modules/fetch";
import module namespace dml         = "http://www.zorba-xquery.com/modules/store/static/collections/dml";
import module namespace ddl         = "http://www.zorba-xquery.com/modules/store/static/collections/ddl";
import module namespace err         = "http://www.zorba-xquery.com/modules/xqdoc2xhtml/error";

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

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

declare variable $z:nodesCollector := <modules/>;

declare variable $z:edgesCollector := <edges/>;

declare variable $z:typeModule          := 'module';
declare variable $z:typeExternalLibrary := 'library';
declare variable $z:typeSchema          := 'schema';

declare variable $z:colors      as xs:string* := ('red'       , 'green');
declare variable $z:moduleTypes as xs:string* := ('Zorba-core', 'External module');

(:~
 :  Collector for the Zorba manifest
 :)
declare %private variable $z:ZorbaManifest := <manifest/>;

(:=========================================================================================================:)
declare variable $z:level1Weight as xs:string* :=
("www.w3.org", "XDM", "store", "introspection", "reflection",
 "external", "xqdoc","data processing", "cryptography", "geo",
 "image", "OAuth", "expath.org", "www.functx.com", "EXPath",
 "Zorba", "debugger", "error", "information extraction", "jsoniq.org",
 "other data sources", "utils", "www.zorba-xquery.com");

declare variable $z:level1Colors as xs:string* :=
("mediumvioletred", "lightsteelblue", "sienna", "dimgray", "slategray",
 "Gold", "moccasin","tan", "RosyBrown", "wheat",
 "LightGreen", "forestgreen", "olivedrab", "darkkhaki", "cornflowerblue",
 "yellow", "Chartreuse", "DarkGoldenRod", "DarkSeaGreen", "DarkSlateBlue ",
 "DodgerBlue", "AntiqueWhite", "Aquamarine");

declare variable $z:collection as xs:QName := xs:QName("z:collection");
declare collection z:collection as node()*;

declare %private function z:get-is-core(
  $moduleUri) as xs:boolean
{
  if(fn:string(data($z:ZorbaManifest/module[@uri= $moduleUri]/@isCore)) = "true") then fn:true()
  else fn:false()
};

declare %an:sequential function z:create-collections($ZorbaBuildFolder as xs:string)
{
  ddl:create($z:collection);

  variable $zorbaManifestPath := concat($ZorbaBuildFolder,
                                        file:directory-separator(),
                                        "ZorbaManifest.xml");

  variable $manifestXML := pxqdoc:load-manifest($zorbaManifestPath);
  variable $moduleManifests := $manifestXML/zm:manifest/zm:module;
  if(count($moduleManifests) eq xs:integer(0)) then ();
  else
  {
    try
    {
      for $module in $moduleManifests
      let $moduleURI := data($module/zm:uri)
      let $moduleFetched := fetch:content($moduleURI, "MODULE")
      let $xqdoc := xqd:xqdoc-content($moduleFetched)
      return
      {
        insert node <module uri="{$moduleURI}"
                            isCore="{data($module/@isCore)}"
                            version="{if (exists(data($module/@version))) then data($module/@version) else ''}"
                            projectRoot="{data($module/zm:projectRoot)}"/> as last into $z:ZorbaManifest;

        dml:apply-insert-nodes($z:collection, $xqdoc);
      }
    }
    catch *
    {
      fn:error($err:UE004,
               concat("Error processing module ",
                      $werr:code,
                      " - ",
                      $werr:description));
    }
  }

  z:fill-nodesCollector();

  z:fill_edgesCollector();
};

declare %an:sequential function z:delete-collections()
{
  dml:delete-nodes(dml:collection(xs:QName("z:collection")));
  ddl:delete(xs:QName("z:collection"));
};

declare %private function z:getModuleProject(
    $moduleUri as xs:string) as xs:string {

    for $docNode in dml:collection(xs:QName("z:collection"))
    let $lModuleUri := data($docNode/xqdoc:module/xqdoc:uri),
        $lModuleProject := $docNode/xqdoc:module/xqdoc:comment/xqdoc:custom[@tag="project"]/text(),
        $lTmp := substring-after($lModuleUri,'http://'),
        $lTmpTok := tokenize($lTmp,'/'),
        $lTmp2 := if(ends-with($lTmp,'/')) then substring($lTmp,1,string-length($lTmp)-1) else string-join(functx:value-except($lTmpTok,$lTmpTok[last()]),'/'),
        $lModuleName    := if(ends-with($lModuleUri,'/')) then $lTmpTok[last()-1] else $lTmpTok[last()],
        $structure      := if(exists($lModuleProject)) then $lModuleProject else $lTmp2
    where fn:string($lModuleUri) eq $moduleUri
    return $structure
};

(:~
 : Fill the $z:nodesCollector with all the available modules.
 :)
declare %an:sequential function z:fill-nodesCollector()
{
  for $docNode in dml:collection(xs:QName("z:collection"))
  let $lModuleUri := data($docNode/xqdoc:module/xqdoc:uri)
  let $structure := z:getModuleProject(fn:string($lModuleUri))
  return
    z:collect-node ($lModuleUri,
                    $structure,
                    fn:string(index-of(dml:collection(xs:QName("z:collection")), $docNode))
                    )
};

declare %private %an:sequential function z:collect-node (
  $moduleURI        as xs:string,
  $lModuleProject   as xs:string,
  $index            as xs:string)
{
  insert node <module uri="{$moduleURI}"
                      moduleProject="{$lModuleProject}"
                      index="{$index}" />
  as last into $z:nodesCollector;
};

declare %private function z:get_subgraphs() as xs:string*
{
    let $subgraphs as xs:string* := distinct-values(
                        for $module in $z:nodesCollector//module
                        return fn:string(data($module/@moduleProject))
                      )
    (: Order the subgraphs based on the Level1Weight :)
    return for $str in $z:level1Weight
           return
             for $tmp in $subgraphs
             where starts-with($tmp, $str)
             order by $tmp
             return $tmp
};

(:~
 : Fill $z:edgesCollector with all the edges (the links between the nodes) for both modules and eternal library dependencies.
 :)
declare %an:sequential function z:fill_edgesCollector()
{
  for $xqdoc in dml:collection(xs:QName("z:collection"))
  return
    (
    (: add imported modules :)
    if (fn:count($xqdoc/xqdoc:imports//xqdoc:import[@type = "library"]) > 0) then
      for $import in $xqdoc/xqdoc:imports//xqdoc:import[@type = "library"]
      let $tmpTo := if(contains(string($import/xqdoc:uri/text()),"#"))
                    then substring-before(string($import/xqdoc:uri/text()),"#")
                    else string($import/xqdoc:uri/text())
      let $from := $z:nodesCollector//module[@uri=data($xqdoc/xqdoc:module/xqdoc:uri)]
      let $to := $z:nodesCollector//module[@uri=$tmpTo]
      return
        z:collect-edge(data($z:nodesCollector//module[@uri = data($xqdoc/xqdoc:module/xqdoc:uri)]/@catUri),
                       fn:string(data($from/@index)),
                       data($xqdoc/xqdoc:module/xqdoc:uri),
                       fn:string(data($to/@index)),
                       $tmpTo,
                       $z:typeModule)
    else
      (),
    (: add external c++ libraries dependencies :)

    if (fn:count($xqdoc/xqdoc:module/xqdoc:comment/xqdoc:custom[@tag="library"]) > 0) then
      for $libraryDependency in $xqdoc/xqdoc:module/xqdoc:comment/xqdoc:custom[@tag="library"]
      let $from := $z:nodesCollector//module[@uri=data($xqdoc/xqdoc:module/xqdoc:uri)]
      let $to := $libraryDependency/*:a
      return
        z:collect-edge(fn:string(data($from/@moduleProject)),
                       fn:string(data($from/@index)),
                       fn:string(data($from/@uri)),
                       concat(fn:data($to),'|',data($to/@href) ),
                       "",
                       $z:typeExternalLibrary)
    else
      ())
};

declare %private %an:sequential function z:collect-edge (
  $catUri   as xs:string?,
  $nameFrom as xs:string?,
  $uriFrom  as xs:string?,
  $nameTo   as xs:string?,
  $uriTo    as xs:string?,
  $type     as xs:string?)
{
  insert node <edge catUri  ="{$catUri}"
                    nameFrom="{$nameFrom}"
                    uriFrom ="{$uriFrom}"
                    nameTo  ="{$nameTo}"
                    uriTo   ="{$uriTo}"
                    type    ="{$type}" />
  as last into $z:edgesCollector;
};

declare function z:test() as xs:string
{
(:
  string-join(
  for $edge in $z:edgesCollector//edge
  return fn:concat($edge/@catUri,"|", $edge/@nameFrom, "|",  $edge/@uriFrom, "|", $edge/@nameTo),"
")
 :)
 (:
  string-join(
  for $module in $z:nodesCollector//module
  return fn:concat($module/@uri,"|", $module/@moduleProject, "|", $module/@catUi),"
")
:)
};

(:~
 : This function returns the module type (see $z:moduleType)
 : based on the existance of the externals functions withing the module and also
 : it takes into account the imported modules.
 :)
declare function z:get_module_type($moduleUri as xs:string) as xs:integer
{
  if(z:get-is-core($moduleUri)) then xs:integer(1)
  else xs:integer(2)
};

(:~
 : Format the properties of a module dot shape (link to a file, tooltip, color of the text showing the module name).
 :)
declare function z:get_shape_properties(
  $ModuleUri as xs:string,
  $lLabel as xs:string) as xs:string
{
  let $file as xs:string := concat(xqdoc2html:get-filename($ModuleUri),".html")
  let $type := z:get_module_type($ModuleUri)
  return
    fn:concat('[URL="../',$file,'" tooltip="(',$z:moduleTypes[$type],') module uri=', $ModuleUri,'" label="',$lLabel,'" fontcolor="', $z:colors[$type] ,'"]')
};

(:~
 : Get the nodes that represent modules based on a provided category URI.
 :)
declare function z:nodes_modules(
  $category as xs:string) as xs:string
{
  let $nodes :=
  string-join(
  for $module in $z:nodesCollector//module
    let $lModuleUri := data($module/@uri)
    let $tok := tokenize($lModuleUri,"/")
    let $lLabel := if(ends-with($lModuleUri,'/')) then $tok[last()-1] else $tok[last()]
    let $lName := concat(data($module/@index),z:get_shape_properties($lModuleUri, $lLabel))
    where fn:string(data($module/@moduleProject)) = $category
    order by $lModuleUri
    return
      $lName,";
    ")
  return
    if($nodes eq '') then ''
    else concat($nodes,';')

};

(:~
 : Get the nodes that represent external library dependencies.
 :)
declare function z:nodes_external_libraries() as xs:string
{
  let $nodes :=
  string-join(
  for $edge in $z:edgesCollector//edge
  let $tok := tokenize(data($edge/@nameTo),'\|')
  let $nodeLabel := replace(replace(tokenize($tok[1],' ')[1],'\+',''),'-','')
  let $link := $tok[2]
  let $node := data($edge/@nameTo)
  let $lName := concat($nodeLabel,'[URL="',$link,'" tooltip="', $tok[1],'" label="',$nodeLabel,'"]')
  where data($edge/@type) eq $z:typeExternalLibrary
  group by $nodeLabel
    return
      $lName,";
    ")
  return
    if($nodes eq '') then ''
    else concat($nodes,';')
};

(:~
 : Get all the edges (the links between the nodes) between modules.
 :)
declare function z:edges_modules() as xs:string
{
let $edges :=
  string-join(
    for $edge in $z:edgesCollector//edge
    where data($edge/@type) eq $z:typeModule
    return concat('    ',$edge/@nameFrom,'->',$edge/@nameTo,'[tooltip="FROM: ',concat($edge/@uriFrom,', TO: ',$edge/@uriTo),'"]')
,';
')
return
  if($edges eq '') then ''
  else concat($edges,';')
};

(:~
 : Get all the edges (the links between the nodes) between modules and externall c++ libraries.
 :)
declare function z:edges_libraries() as xs:string
{
let $edges :=
  string-join(
    for $edge in $z:edgesCollector//edge
    let $tok := tokenize(data($edge/@nameTo),'\|')
    let $nodeLabel := replace(replace(tokenize($tok[1],' ')[1],'\+',''),"-","")
    where data($edge/@type) eq $z:typeExternalLibrary
    return concat('    ',$edge/@nameFrom,'->',$nodeLabel,'[tooltip="FROM: ',
    concat($edge/@uriFrom,', TO: ',$nodeLabel)
    ,'" color="red"]'),
  ';
')
return
  if($edges eq '') then ''
  else concat($edges,';')
};

(:~
 : Generate the cluster for the external c++ libraries.
 :)
declare function z:create_subgraph_libraries() as xs:string
{
  concat('

    subgraph clusterExternalCPPLibs  { style=filled; color=darksalmon; node [shape=box3d, color=red];
    ',
    z:nodes_external_libraries(),'

',
    z:edges_libraries(),'
    label="External C++ Library dependencies"; }')
};

(:~
 : Generate the cluster for a specific category.
 :)
declare function z:create_subgraph(
  $category   as xs:string,
  $subGraphs  as xs:string*,
  $level1     as xs:string*) as xs:string
{
  concat('
    subgraph cluster',
      index-of($level1, $category),
      ' { style=filled; color=',$z:level1Colors[index-of($level1,$category)],'; node [style="filled", color=white];
    ',
    z:create_subgraph-rec($category,
                          for $tmp in $subGraphs
                          where (starts-with($tmp, $category) and (contains($tmp,"/")))
                          return $tmp),'
    ',
z:nodes_modules($category),'
',
'    label="',$category,'"; tooltip="',$category,'"}')
};

declare %private function z:create_subgraph-rec(
  $category   as xs:string,
  $subGraphs  as xs:string*
  ) as xs:string
{
let $lSubGraphs := distinct-values( for $tmp in $subGraphs
                                    return tokenize(substring-after($tmp, concat($category,"/")),"/")[1]
                                    )
return
string-join(
for $sg in $lSubGraphs
let $newSg := concat($category,"/",$sg)
return
concat('
    subgraph cluster',
        translate($newSg," -./",""),'{ style=filled;
    ',
        z:create_subgraph-rec($newSg,
                          for $tmp in $subGraphs
                          where starts-with($tmp, $newSg)
                          return $tmp),'
    ',
z:nodes_modules($newSg),'
',
'    label="',$sg,'"; tooltip="',$newSg,'"}'),'
')
};

(:~
 : Generate the BIG cluster.
 :)
declare function z:create_graph() as xs:string
{
let $subgraphs := z:get_subgraphs()
let $l1 := distinct-values(for $str in $subgraphs return tokenize($str,'/')[1])
return
    concat('digraph G { penwidth=1; pencolor=black; label="Zorba modules dependency graph"; tooltip="Zorba modules dependency graph"
' ,
            string-join(
              for $cat1 in $l1
              return z:create_subgraph( $cat1,
                                        for $val in $subgraphs
                                        where starts-with($val, $cat1)
                                        return $val,
                                        $l1
                                       )
              ,('
  ')), z:create_subgraph_libraries()
    ,"

",
  z:edges_modules(),"
  }"
  )
};

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

(:
 : author Sorin Nasoi
 :)

module namespace z = "http://www.zorba-xquery.com/module-dependencies";

declare namespace an = "http://www.zorba-xquery.com/annotations";
declare namespace zm = "http://www.zorba-xquery.com/manifest";

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

import module namespace dot         = "http://www.zorba-xquery.com/modules/image/graphviz";
import module namespace xqd         = "http://www.zorba-xquery.com/modules/xqdoc";
import module namespace xqdoc2html  = "http://www.zorba-xquery.com/modules/xqdoc2xhtml/";
import module namespace pxqdoc      = "http://www.zorba-xquery.com/modules/project_xqdoc";
import module namespace fetch       = "http://www.zorba-xquery.com/modules/fetch";
import module namespace dml         = "http://www.zorba-xquery.com/modules/store/static/collections/dml";
import module namespace ddl         = "http://www.zorba-xquery.com/modules/store/static/collections/ddl";
import module namespace err         = "http://www.zorba-xquery.com/modules/xqdoc2xhtml/error";

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

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

declare variable $z:nodesCollector := <modules/>;

declare variable $z:edgesCollector := <edges/>;

declare variable $z:typeModule          := 'module';
declare variable $z:typeExternalLibrary := 'library';
declare variable $z:typeSchema          := 'schema';

declare variable $z:colors      as xs:string* := ('red'       , 'green');
declare variable $z:moduleTypes as xs:string* := ('Zorba-core', 'External module');

(:~
 :  Collector for the Zorba manifest
 :)
declare %private variable $z:ZorbaManifest := <manifest/>;

(:=========================================================================================================:)
declare variable $z:level1Weight as xs:string* :=
("www.w3.org", "XDM", "store", "introspection", "reflection",
 "external", "xqdoc","data processing", "programming languages", "excel",
 "cryptography", "geo", "image", "OAuth", "expath.org",
 "www.functx.com", "communication", "debugger", "error", "utils",
 "www.zorba-xquery.com");

declare variable $z:level1Colors as xs:string* :=
("mediumvioletred", "lightsteelblue", "sienna", "dimgray", "slategray",
 "Gold", "moccasin","tan", "RosyBrown", "wheat",
 "LightGreen", "forestgreen", "olivedrab", "darkkhaki", "cornflowerblue",
 "yellow", "Chartreuse", "DarkGoldenRod", "DarkSeaGreen", "DarkSlateBlue ",
 "DodgerBlue");

declare variable $z:collection as xs:QName := xs:QName("z:collection");
declare collection z:collection as node()*;

declare %private function z:get-is-core(
  $moduleUri) as xs:boolean
{
  if(fn:string(data($z:ZorbaManifest/module[@uri= $moduleUri]/@isCore)) = "true") then fn:true()
  else fn:false()
};

declare %an:sequential function z:create-collections($ZorbaBuildFolder as xs:string)
{
  ddl:create($z:collection);

  variable $zorbaManifestPath := concat($ZorbaBuildFolder,
                                        file:directory-separator(),
                                        "ZorbaManifest.xml");

  variable $manifestXML := pxqdoc:load-manifest($zorbaManifestPath);
  variable $moduleManifests := $manifestXML/zm:manifest/zm:module;
  if(count($moduleManifests) eq xs:integer(0)) then ();
  else
  {
    try
    {
      for $module in $moduleManifests
      let $moduleURI := data($module/zm:uri)
      let $moduleFetched := fetch:content($moduleURI, "MODULE")
      let $xqdoc := xqd:xqdoc-content($moduleFetched)
      return
      {
        insert node <module uri="{$moduleURI}"
                            isCore="{data($module/@isCore)}"
                            version="{if (exists(data($module/@version))) then data($module/@version) else ''}"
                            projectRoot="{data($module/zm:projectRoot)}"/> as last into $z:ZorbaManifest;

        dml:apply-insert-nodes($z:collection, $xqdoc);
      }
    }
    catch *
    {
      fn:error($err:UE004,
               concat("Error processing module ",
                      $werr:code,
                      " - ",
                      $werr:description));
    }
  }

  z:fill-nodesCollector();

  z:fill_edgesCollector();
};

declare %an:sequential function z:delete-collections()
{
  dml:delete-nodes(dml:collection(xs:QName("z:collection")));
  ddl:delete(xs:QName("z:collection"));
};

declare %private function z:getModuleProject(
    $moduleUri as xs:string) as xs:string {

    for $docNode in dml:collection(xs:QName("z:collection"))
    let $lModuleUri := data($docNode/xqdoc:module/xqdoc:uri),
        $lModuleProject := $docNode/xqdoc:module/xqdoc:comment/xqdoc:custom[@tag="project"]/text(),
        $lTmp := substring-after($lModuleUri,'http://'),
        $lTmpTok := tokenize($lTmp,'/'),
        $lTmp2 := if(ends-with($lTmp,'/')) then substring($lTmp,1,string-length($lTmp)-1) else string-join(functx:value-except($lTmpTok,$lTmpTok[last()]),'/'),
        $lModuleName    := if(ends-with($lModuleUri,'/')) then $lTmpTok[last()-1] else $lTmpTok[last()],
        $structure      := if(exists($lModuleProject)) then $lModuleProject else $lTmp2
    where fn:string($lModuleUri) eq $moduleUri
    return $structure
};

(:~
 : Fill the $z:nodesCollector with all the available modules.
 :)
declare %an:sequential function z:fill-nodesCollector()
{
  for $docNode in dml:collection(xs:QName("z:collection"))
  let $lModuleUri := data($docNode/xqdoc:module/xqdoc:uri)
  let $structure := z:getModuleProject(fn:string($lModuleUri))
  return
    z:collect-node ($lModuleUri,
                    $structure,
                    fn:string(index-of(dml:collection(xs:QName("z:collection")), $docNode))
                    )
};

declare %private %an:sequential function z:collect-node (
  $moduleURI        as xs:string,
  $lModuleProject   as xs:string,
  $index            as xs:string)
{
  insert node <module uri="{$moduleURI}"
                      moduleProject="{$lModuleProject}"
                      index="{$index}" />
  as last into $z:nodesCollector;
};

declare %private function z:get_subgraphs() as xs:string*
{
    let $subgraphs as xs:string* := distinct-values(
                        for $module in $z:nodesCollector//module
                        return fn:string(data($module/@moduleProject))
                      )
    (: Order the subgraphs based on the Level1Weight :)
    return for $str in $z:level1Weight
           return
             for $tmp in $subgraphs
             where starts-with($tmp, $str)
             order by $tmp
             return $tmp
};

(:~
 : Fill $z:edgesCollector with all the edges (the links between the nodes) for both modules and eternal library dependencies.
 :)
declare %an:sequential function z:fill_edgesCollector()
{
  for $xqdoc in dml:collection(xs:QName("z:collection"))
  return
    (
    (: add imported modules :)
    if (fn:count($xqdoc/xqdoc:imports//xqdoc:import[@type = "library"]) > 0) then
      for $import in $xqdoc/xqdoc:imports//xqdoc:import[@type = "library"]
      let $from := $z:nodesCollector//module[@uri=data($xqdoc/xqdoc:module/xqdoc:uri)]
      let $to := $z:nodesCollector//module[@uri=string($import/xqdoc:uri/text())]
      return
        z:collect-edge(data($z:nodesCollector//module[@uri = data($xqdoc/xqdoc:module/xqdoc:uri)]/@catUri),
                       fn:string(data($from/@index)),
                       data($xqdoc/xqdoc:module/xqdoc:uri),
                       fn:string(data($to/@index)),
                       string($import/xqdoc:uri/text()),
                       $z:typeModule)
    else
      (),
    (: add external c++ libraries dependencies :)

    if (fn:count($xqdoc/xqdoc:module/xqdoc:comment/xqdoc:custom[@tag="library"]) > 0) then
      for $libraryDependency in $xqdoc/xqdoc:module/xqdoc:comment/xqdoc:custom[@tag="library"]
      let $from := $z:nodesCollector//module[@uri=data($xqdoc/xqdoc:module/xqdoc:uri)]
      let $to := $libraryDependency/*:a
      return
        z:collect-edge(fn:string(data($from/@moduleProject)),
                       fn:string(data($from/@index)),
                       fn:string(data($from/@uri)),
                       concat(fn:data($to),'|',data($to/@href) ),
                       "",
                       $z:typeExternalLibrary)
    else
      ())
};

declare %private %an:sequential function z:collect-edge (
  $catUri   as xs:string?,
  $nameFrom as xs:string?,
  $uriFrom  as xs:string?,
  $nameTo   as xs:string?,
  $uriTo    as xs:string?,
  $type     as xs:string?)
{
  insert node <edge catUri  ="{$catUri}"
                    nameFrom="{$nameFrom}"
                    uriFrom ="{$uriFrom}"
                    nameTo  ="{$nameTo}"
                    uriTo   ="{$uriTo}"
                    type    ="{$type}" />
  as last into $z:edgesCollector;
};

declare function z:test() as xs:string
{
(:
  string-join(
  for $edge in $z:edgesCollector//edge
  return fn:concat($edge/@catUri,"|", $edge/@nameFrom, "|",  $edge/@uriFrom, "|", $edge/@nameTo),"
")
 :)
 (:
  string-join(
  for $module in $z:nodesCollector//module
  return fn:concat($module/@uri,"|", $module/@moduleProject, "|", $module/@catUi),"
")
:)
};

(:~
 : This function returns the module type (see $z:moduleType)
 : based on the existance of the externals functions withing the module and also
 : it takes into account the imported modules.
 :)
declare function z:get_module_type($moduleUri as xs:string) as xs:integer
{
  if(z:get-is-core($moduleUri)) then xs:integer(1)
  else xs:integer(2)
};

(:~
 : Format the properties of a module dot shape (link to a file, tooltip, color of the text showing the module name).
 :)
declare function z:get_shape_properties(
  $ModuleUri as xs:string,
  $lLabel as xs:string) as xs:string
{
  let $file as xs:string := concat(xqdoc2html:get-filename($ModuleUri),".html")
  let $type := z:get_module_type($ModuleUri)
  return
    fn:concat('[URL="../',$file,'" tooltip="(',$z:moduleTypes[$type],') module uri=', $ModuleUri,'" label="',$lLabel,'" fontcolor="', $z:colors[$type] ,'"]')
};

(:~
 : Get the nodes that represent modules based on a provided category URI.
 :)
declare function z:nodes_modules(
  $category as xs:string) as xs:string
{
  let $nodes :=
  string-join(
  for $module in $z:nodesCollector//module
    let $lModuleUri := data($module/@uri)
    let $tok := tokenize($lModuleUri,"/")
    let $lLabel := if(ends-with($lModuleUri,'/')) then $tok[last()-1] else $tok[last()]
    let $lName := concat(data($module/@index),z:get_shape_properties($lModuleUri, $lLabel))
    where fn:string(data($module/@moduleProject)) = $category
    order by $lModuleUri
    return
      $lName,";
    ")
  return
    if($nodes eq '') then ''
    else concat($nodes,';')

};

(:~
 : Get the nodes that represent external library dependencies.
 :)
declare function z:nodes_external_libraries() as xs:string
{
  let $nodes :=
  string-join(
  for $edge in $z:edgesCollector//edge
  let $tok := tokenize(data($edge/@nameTo),'\|')
  let $nodeLabel := replace(replace(tokenize($tok[1],' ')[1],'\+',''),'-','')
  let $link := $tok[2]
  let $node := data($edge/@nameTo)
  let $lName := concat($nodeLabel,'[URL="',$link,'" tooltip="', $tok[1],'" label="',$nodeLabel,'"]')
  where data($edge/@type) eq $z:typeExternalLibrary
  group by $nodeLabel
    return
      $lName,";
    ")
  return
    if($nodes eq '') then ''
    else concat($nodes,';')
};

(:~
 : Get all the edges (the links between the nodes) between modules.
 :)
declare function z:edges_modules() as xs:string
{
let $edges :=
  string-join(
    for $edge in $z:edgesCollector//edge
    where data($edge/@type) eq $z:typeModule
    return concat('    ',$edge/@nameFrom,'->',$edge/@nameTo,'[tooltip="FROM: ',concat($edge/@uriFrom,', TO: ',$edge/@uriTo),'"]')
,';
')
return
  if($edges eq '') then ''
  else concat($edges,';')
};

(:~
 : Get all the edges (the links between the nodes) between modules and externall c++ libraries.
 :)
declare function z:edges_libraries() as xs:string
{
let $edges :=
  string-join(
    for $edge in $z:edgesCollector//edge
    let $tok := tokenize(data($edge/@nameTo),'\|')
    let $nodeLabel := replace(replace(tokenize($tok[1],' ')[1],'\+',''),"-","")
    where data($edge/@type) eq $z:typeExternalLibrary
    return concat('    ',$edge/@nameFrom,'->',$nodeLabel,'[tooltip="FROM: ',
    concat($edge/@uriFrom,', TO: ',$nodeLabel)
    ,'" color="red"]'),
  ';
')
return
  if($edges eq '') then ''
  else concat($edges,';')
};

(:~
 : Generate the cluster for the external c++ libraries.
 :)
declare function z:create_subgraph_libraries() as xs:string
{
  concat('

    subgraph clusterExternalCPPLibs  { style=filled; color=darksalmon; node [shape=box3d, color=red];
    ',
    z:nodes_external_libraries(),'

',
    z:edges_libraries(),'
    label="External C++ Library dependencies"; }')
};

(:~
 : Generate the cluster for a specific category.
 :)
declare function z:create_subgraph(
  $category   as xs:string,
  $subGraphs  as xs:string*) as xs:string
{
  concat('
    subgraph cluster',
      index-of($z:level1Weight, $category),
      ' { style=filled; color=',$z:level1Colors[index-of($z:level1Weight,$category)],'; node [style="filled", color=white];
    ',
    z:create_subgraph-rec($category,
                          for $tmp in $subGraphs
                          where (starts-with($tmp, $category) and (contains($tmp,"/")))
                          return $tmp),'
    ',
z:nodes_modules($category),'
',
'    label="',$category,'"; tooltip="',$category,'"}')
};

declare %private function z:create_subgraph-rec(
  $category   as xs:string,
  $subGraphs  as xs:string*
  ) as xs:string
{
let $lSubGraphs := distinct-values( for $tmp in $subGraphs
                                    return tokenize(substring-after($tmp, concat($category,"/")),"/")[1]
                                    )
return
string-join(
for $sg in $lSubGraphs
let $newSg := concat($category,"/",$sg)
return
concat('
    subgraph cluster',
        translate($newSg," -./",""),'{ style=filled;
    ',
        z:create_subgraph-rec($newSg,
                          for $tmp in $subGraphs
                          where starts-with($tmp, $newSg)
                          return $tmp),'
    ',
z:nodes_modules($newSg),'
',
'    label="',$sg,'"; tooltip="',$newSg,'"}'),'
')
};

(:~
 : Generate the BIG cluster.
 :)
declare function z:create_graph() as xs:string
{
let $subgraphs := z:get_subgraphs()
return
    concat('digraph G { penwidth=1; pencolor=black; label="Zorba modules dependency graph"; tooltip="Zorba modules dependency graph"
' ,
            string-join(
              for $cat1 in  $z:level1Weight
              return z:create_subgraph( $cat1,
                                        for $val in $subgraphs
                                        where starts-with($val, $cat1)
                                        return $val
                                       )
              ,('
  ')), z:create_subgraph_libraries()
    ,"

",
  z:edges_modules(),"
  }"
  )
};


Don't generate an error if local:prefix returns an empty sequence.

(:
 : Copyright (C) 2020 Reece H. Dunn
 :
 : 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 "3.1";

declare namespace apidoc = "http://marklogic.com/xdmp/apidoc";
declare namespace map = "http://www.w3.org/2005/xpath-functions/map";
declare namespace xhtml = "http://www.w3.org/1999/xhtml";

declare variable $marklogic-version as xs:string external;

declare variable $language as xs:string external := "xquery"; (: xquery | javascript | rest :)
declare variable $namespace as xs:string external;
declare variable $local-name as xs:string external;

declare variable $apidoc as element(apidoc:apidoc) :=
    typeswitch (.)
    case xs:string return fn:parse-xml(.)/element()
    case document-node() return ./element()
    default return .;

(: documentation helpers --------------------------------------------------- :)

declare function local:apidoc-language($doc as element()) as xs:string {
    ($doc/@class, "xquery")[1]
};

declare function local:prefix($namespaces as map(*), $namespace as xs:string) as xs:string? {
    for $prefix in map:keys($namespaces)
    where map:get($namespaces, $prefix) = $namespace
    return $prefix
};

declare function local:documentation-html($doc as node()) as node()* {
    if ($doc/namespace-uri() = "http://marklogic.com/xdmp/apidoc") then
        let $first :=
            let $node := $doc/node()[position() = 1]
            return if ($node instance of text() and string-length($node/normalize-space()) != 0) then
                ()
            else
                $node
        return if (exists($first) and (some $child in $doc/node() satisfies $child instance of element())) then
            if (local:apidoc-language($doc) = $language) then
                for $child in $doc/node()
                return local:documentation-html($child)
            else ()
        else

{$doc/node()}

    else if ($doc instance of text()) then
        let $text := $doc/normalize-space()
        return if ($text != "") then
            $doc
        else ()
    else if ($doc instance of element() and local:apidoc-language($doc) = $language) then
        element { $doc/local-name() } {
            for $child in $doc/node()
            return local:documentation-html($child)
        }
    else ()
};

(: namespaces -------------------------------------------------------------- :)

declare variable $module-namespaces := map:merge(
    for $import in $apidoc//apidoc:module/apidoc:summary//(*:p|*:pre)
    let $match := fn:analyze-string(($import/*:code, $import)[1], "import module (namespace\s*([a-z0-9]+)\s*=\s*)?""([^""]+)""")/fn:match[1]
    let $prefix := $import/ancestor::apidoc:module/@lib
    let $namespace := $match/fn:group[@nr = "3"]/string()
    where exists($namespace)
    return map:entry($prefix, $namespace)
);

declare variable $xquery-namespaces := map {
    "local": "http://www.w3.org/2005/xquery-local-functions",
    "fn": "http://www.w3.org/2005/xpath-functions",
    "xml": "http://www.w3.org/XML/1998/namespace",
    "xs": "http://www.w3.org/2001/XMLSchema",
    "xsi": "http://www.w3.org/2001/XMLSchema-instance"
};

declare variable $marklogic-5-builtin := map {
    "cts": "http://marklogic.com/cts",
    "dbg": "http://marklogic.com/xdmp/dbg",
    "dir": "http://marklogic.com/xdmp/directory",
    "error": "http://marklogic.com/xdmp/error",
    "map": "http://marklogic.com/xdmp/map",
    "math": "http://marklogic.com/xdmp/math",
    "prof": "http://marklogic.com/xdmp/profile",
    "sec": "http://marklogic.com/security",
    "spell": "http://marklogic.com/xdmp/spell",
    "xdmp": "http://marklogic.com/xdmp"
};

declare variable $marklogic-6-builtin := map {
    "err": "http://www.w3.org/2005/xqt-error"
};

declare variable $marklogic-7-builtin := map {
    "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
    "sc": "http://marklogic.com/xdmp/schema-components",
    "sql": "http://marklogic.com/xdmp/sql"
};

declare variable $marklogic-10-builtin := map {
    "cntk": "http://marklogic.com/cntk",
    "ort": "http://marklogic.com/onnxruntime"
};

declare variable $marklogic-additional := map {
    "as": "http://marklogic.com/xdmp/assignments",
    "db": "http://marklogic.com/xdmp/database",
    "dg": "xdmp:document-get",
    "cl": "http://marklogic.com/xdmp/clusters",
    "exsl": "http://exslt.org/common",
    "extusr": "http://marklogic.com/xdmp/external/user",
    "gr": "http://marklogic.com/xdmp/group",
    "lang": "http://marklogic.com/xdmp/language",
    "list": "http://marklogic.com/manage/package/list",
    "md": "urn:oasis:names:tc:SAML:2.0:metadata",
    "mt": "http://marklogic.com/xdmp/mimetypes",
    "pkg": "http://marklogic.com/manage/package",
    "s": "http://www.w3.org/2009/xpath-functions/analyze-string",
    "sch": "http://purl.oclc.org/dsdl/schematron",
    "x509": "http://marklogic.com/xdmp/x509",
    "xdt": "http://www.w3.org/2004/10/xpath-datatypes",
    "xi": "http://www.w3.org/2001/XInclude"
};

declare variable $namespaces := map:merge((
    $module-namespaces,
    $xquery-namespaces,
    $marklogic-5-builtin,
    $marklogic-6-builtin,
    $marklogic-7-builtin,
    $marklogic-10-builtin,
    $marklogic-additional,
    ()
));

(: functions --------------------------------------------------------------- :)

(: Several functions in the apidocs have duplicate parameter names in some versions, so fix these. :)
declare variable $param-fixes := map {
    "fn:subtract-dateTimes-yielding-dayTimeDuration": ("srcval1", "srcval2"),
    "fn:subtract-dateTimes-yielding-yearMonthDuration": ("srcval1", "srcval2"),
    "geo:region-contains": ("target", "region", "options"),
    "math:fmod": ("x", "y"),
    "math:rank": ("arg1", "arg2", "options")
};

declare variable $functions as element(apidoc:function)* := $apidoc//apidoc:function[
    local:function-language(.) = $language and not(@name = "")
];

declare function local:function-uri($function as element(apidoc:function)) as xs:string {
    ``[http://docs.marklogic.com/`{$marklogic-version}`/`{local:function-name($function)}`]``
};

declare function local:function-language($function as element(apidoc:function)) as xs:string {
    let $bucket := $function/@bucket
    return switch ($bucket)
    case "REST Resources API" return "rest"
    default return local:apidoc-language($function)
};

declare function local:function-name($function as element(apidoc:function)) as xs:string {
    ``[`{$function/@lib}`:`{$function/@name}`]``
};

declare function local:function-parameters($function as element(apidoc:function)) as xs:string* {
    let $names as xs:string* :=
        let $names := map:get($param-fixes, local:function-name($function))
        return if (exists($names)) then
            $names
        else
            $function/apidoc:params/apidoc:param[local:apidoc-language(.) = $language]/@name/string()
    return if (count($names) != count(distinct-values($names))) then
        fn:error(xs:QName("err:XQST0039"),``[Duplicate parameter name in `{local:function-name($function)}`]``)
    else
        $names
};

declare function local:function-arity($function as element(apidoc:function)) as map(*) {
    let $params := $function/apidoc:params/apidoc:param[local:apidoc-language(.) = $language]
    return map {
        "min": count($params[not(@optional = "true")]),
        "max": count($params)
    }
};

declare function local:function-parameter($function as element(apidoc:function), $name as xs:string) as element(apidoc:param) {
    let $names := map:get($param-fixes, local:function-name($function))
    return if (exists($names)) then
        let $i := fn:index-of($names, $name)
        return $function/apidoc:params/apidoc:param[$i]
    else
        $function/apidoc:params/apidoc:param[@name = $name and local:apidoc-language(.) = $language]
};

declare function local:function-parameter-type($function as element(apidoc:function), $name as xs:string) as xs:string {
    let $type := local:function-parameter($function, $name)/@type/string()
    return switch ($type)
    case "atomic type" return "item()*"
    case "xs:string)" return "xs:string"
    case "xs:dateTime? | xs:date? | xs:string?" return "(xs:dateTime?|xs:date?|xs:string?)"
    case "binary())" return "binary()"
    case "element(sec:query-rolesets)*)" return "element(sec:query-rolesets)*"
    case "function()" return "function(*)"
    case "item()* | map:map" return "(item()*|map:map)"
    case "(binary()|node())*" return "(binary()*|node()*)"
    case "(binary()|node())?" return "(binary()?|node()?)"
    case "(element()|map:map)?" case "element()?|map:map?" return "(element()?|map:map?)"
    case "(node()? | map:map)?" return "(node()?|map:map?)"
    case "(cts:order|xs:string)*" return "(cts:order*|xs:string*)"
    case "[xs:string?]" return "xs:string?"
    default return replace($type, ",\.\.\.", "...")
};

declare function local:function-return-type($function as element(apidoc:function)) as xs:string {
    let $type := normalize-space($function/apidoc:return[local:apidoc-language(.) = $language])
    return switch ($type)
    case "An XML representation of the certificate." return "element()?"
    case "element())" case "Element()" return "element()"
    case "element()|map:map" return "(element()|map:map)"
    case "element(xsd:schema)*" return "element(xs:schema)*"
    case "empty-sequence" case "empty sequence" return "empty-sequence()"
    case "xs:string)" return "xs:string"
    case "xs:unsignedLong)" return "xs:unsignedLong"
    case "pkgins:install($pkgname)" return "element(pkg:install-status)"
    case "pkgins:revert($ticket)" return "element(pkg:revert-status)"
    case "query-results-serialize($results)" return "item()*"
    case "The return data type is the data type of the date argument" return "item()"
    case "" return
        switch (local:function-name($function))
        case "admin:appserver-set-output-indent-tabs" return "element(configuration)"
        default return ""
    default return $type
};

declare function local:function-xquery-signatures($function as element(apidoc:function)) as xs:string* {
    let $name := local:function-name($function)
    let $params :=
        for $name in local:function-parameters($function)
        let $type := local:function-parameter-type($function, $name)
        return ``[$`{$name}` as `{$type}`]``
    let $type := local:function-return-type($function)
    let $arity-range := local:function-arity($function)
    for $arity in $arity-range?min to $arity-range?max
    let $params := $params[position() = 1 to $arity]
    return if ($type = "") then
        ``[declare function `{$name}`(`{string-join($params, ", ")}`) external;]``
    else
        ``[declare function `{$name}`(`{string-join($params, ", ")}`) as `{$type}` external;]``
};

declare function local:function-html-signatures($function as element(apidoc:function)) as element(div)+ {
    let $name := local:function-name($function)
    let $params :=
        for $name in local:function-parameters($function)
        let $type := local:function-parameter-type($function, $name)
        return

            ${$name},
             as ,
            {$type}

    let $type := local:function-return-type($function)
    let $arity-range := local:function-arity($function)
    for $arity in $arity-range?min to $arity-range?max
    let $params := $params[position() = 1 to $arity]
    return if (count($params) <= 2) then

            {$name}({
                for $param at $i in $params
                return (
                    if ($i > 1) then ", " else (),
                    $param/*
                )
            }){
                if ($type != "") then (
                     as ,
                    {$type}
                ) else ()
            }

    else

{
                (: JEditorPanel does not support vertical-align on tr/td elements, so use valign instead. :)

                    {$name}(
                    {$params[1]/code[@class="arg"]}
                    {$params[1]/code[not(@class="arg")]},

,
                for $param at $i in $params[position() > 1]
                return

                        {$param/code[@class="arg"]}
                        {
                            $param/code[not(@class="arg")],
                            if ($i = count($params) - 1) then ")" else ","
                        }

            }

};

declare function local:function-html-parameters($function as element(apidoc:function)) as element(table)? {
    let $params := local:function-parameters($function)
    return if (exists($params)) then

{
            for $name in local:function-parameters($function)
            let $type := local:function-parameter-type($function, $name)
            let $summary := local:function-parameter($function, $name)
            return

                    ${$name}
                    {
                        for $child in $summary/node()
                        return local:documentation-html($child)
                    }

        }

    else ()
};

(: query ------------------------------------------------------------------- :)

let $prefix := local:prefix($namespaces, $namespace)
for $function in $functions
where $function/@lib = $prefix and $function/@name = $local-name
return (
    local:function-uri($function),
    fn:serialize(local:documentation-html($function/apidoc:summary)),
    string-join(local:function-html-signatures($function) ! fn:serialize(.), ""),
    fn:serialize(local:function-html-parameters($function)),
    fn:serialize($function/apidoc:privilege ! local:documentation-html(.)),
    fn:serialize($function/apidoc:usage ! local:documentation-html(.)),
    fn:serialize($function/apidoc:example ! local:documentation-html(.)),
    ()
)

(:
 : Copyright (C) 2020 Reece H. Dunn
 :
 : 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 "3.1";

declare namespace apidoc = "http://marklogic.com/xdmp/apidoc";
declare namespace map = "http://www.w3.org/2005/xpath-functions/map";
declare namespace xhtml = "http://www.w3.org/1999/xhtml";

declare variable $marklogic-version as xs:string external;

declare variable $language as xs:string external := "xquery"; (: xquery | javascript | rest :)
declare variable $namespace as xs:string external;
declare variable $local-name as xs:string external;

declare variable $apidoc as element(apidoc:apidoc) :=
    typeswitch (.)
    case xs:string return fn:parse-xml(.)/element()
    case document-node() return ./element()
    default return .;

(: documentation helpers --------------------------------------------------- :)

declare function local:apidoc-language($doc as element()) as xs:string {
    ($doc/@class, "xquery")[1]
};

declare function local:prefix($namespaces as map(*), $namespace as xs:string) as xs:string {
    for $prefix in map:keys($namespaces)
    where map:get($namespaces, $prefix) = $namespace
    return $prefix
};

declare function local:documentation-html($doc as node()) as node()* {
    if ($doc/namespace-uri() = "http://marklogic.com/xdmp/apidoc") then
        let $first :=
            let $node := $doc/node()[position() = 1]
            return if ($node instance of text() and string-length($node/normalize-space()) != 0) then
                ()
            else
                $node
        return if (exists($first) and (some $child in $doc/node() satisfies $child instance of element())) then
            if (local:apidoc-language($doc) = $language) then
                for $child in $doc/node()
                return local:documentation-html($child)
            else ()
        else

{$doc/node()}

    else if ($doc instance of text()) then
        let $text := $doc/normalize-space()
        return if ($text != "") then
            $doc
        else ()
    else if ($doc instance of element() and local:apidoc-language($doc) = $language) then
        element { $doc/local-name() } {
            for $child in $doc/node()
            return local:documentation-html($child)
        }
    else ()
};

(: namespaces -------------------------------------------------------------- :)

declare variable $module-namespaces := map:merge(
    for $import in $apidoc//apidoc:module/apidoc:summary//(*:p|*:pre)
    let $match := fn:analyze-string(($import/*:code, $import)[1], "import module (namespace\s*([a-z0-9]+)\s*=\s*)?""([^""]+)""")/fn:match[1]
    let $prefix := $import/ancestor::apidoc:module/@lib
    let $namespace := $match/fn:group[@nr = "3"]/string()
    where exists($namespace)
    return map:entry($prefix, $namespace)
);

declare variable $xquery-namespaces := map {
    "local": "http://www.w3.org/2005/xquery-local-functions",
    "fn": "http://www.w3.org/2005/xpath-functions",
    "xml": "http://www.w3.org/XML/1998/namespace",
    "xs": "http://www.w3.org/2001/XMLSchema",
    "xsi": "http://www.w3.org/2001/XMLSchema-instance"
};

declare variable $marklogic-5-builtin := map {
    "cts": "http://marklogic.com/cts",
    "dbg": "http://marklogic.com/xdmp/dbg",
    "dir": "http://marklogic.com/xdmp/directory",
    "error": "http://marklogic.com/xdmp/error",
    "map": "http://marklogic.com/xdmp/map",
    "math": "http://marklogic.com/xdmp/math",
    "prof": "http://marklogic.com/xdmp/profile",
    "sec": "http://marklogic.com/security",
    "spell": "http://marklogic.com/xdmp/spell",
    "xdmp": "http://marklogic.com/xdmp"
};

declare variable $marklogic-6-builtin := map {
    "err": "http://www.w3.org/2005/xqt-error"
};

declare variable $marklogic-7-builtin := map {
    "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
    "sc": "http://marklogic.com/xdmp/schema-components",
    "sql": "http://marklogic.com/xdmp/sql"
};

declare variable $marklogic-10-builtin := map {
    "cntk": "http://marklogic.com/cntk",
    "ort": "http://marklogic.com/onnxruntime"
};

declare variable $marklogic-additional := map {
    "as": "http://marklogic.com/xdmp/assignments",
    "db": "http://marklogic.com/xdmp/database",
    "dg": "xdmp:document-get",
    "cl": "http://marklogic.com/xdmp/clusters",
    "exsl": "http://exslt.org/common",
    "extusr": "http://marklogic.com/xdmp/external/user",
    "gr": "http://marklogic.com/xdmp/group",
    "lang": "http://marklogic.com/xdmp/language",
    "list": "http://marklogic.com/manage/package/list",
    "md": "urn:oasis:names:tc:SAML:2.0:metadata",
    "mt": "http://marklogic.com/xdmp/mimetypes",
    "pkg": "http://marklogic.com/manage/package",
    "s": "http://www.w3.org/2009/xpath-functions/analyze-string",
    "sch": "http://purl.oclc.org/dsdl/schematron",
    "x509": "http://marklogic.com/xdmp/x509",
    "xdt": "http://www.w3.org/2004/10/xpath-datatypes",
    "xi": "http://www.w3.org/2001/XInclude"
};

declare variable $namespaces := map:merge((
    $module-namespaces,
    $xquery-namespaces,
    $marklogic-5-builtin,
    $marklogic-6-builtin,
    $marklogic-7-builtin,
    $marklogic-10-builtin,
    $marklogic-additional,
    ()
));

(: functions --------------------------------------------------------------- :)

(: Several functions in the apidocs have duplicate parameter names in some versions, so fix these. :)
declare variable $param-fixes := map {
    "fn:subtract-dateTimes-yielding-dayTimeDuration": ("srcval1", "srcval2"),
    "fn:subtract-dateTimes-yielding-yearMonthDuration": ("srcval1", "srcval2"),
    "geo:region-contains": ("target", "region", "options"),
    "math:fmod": ("x", "y"),
    "math:rank": ("arg1", "arg2", "options")
};

declare variable $functions as element(apidoc:function)* := $apidoc//apidoc:function[
    local:function-language(.) = $language and not(@name = "")
];

declare function local:function-uri($function as element(apidoc:function)) as xs:string {
    ``[http://docs.marklogic.com/`{$marklogic-version}`/`{local:function-name($function)}`]``
};

declare function local:function-language($function as element(apidoc:function)) as xs:string {
    let $bucket := $function/@bucket
    return switch ($bucket)
    case "REST Resources API" return "rest"
    default return local:apidoc-language($function)
};

declare function local:function-name($function as element(apidoc:function)) as xs:string {
    ``[`{$function/@lib}`:`{$function/@name}`]``
};

declare function local:function-parameters($function as element(apidoc:function)) as xs:string* {
    let $names as xs:string* :=
        let $names := map:get($param-fixes, local:function-name($function))
        return if (exists($names)) then
            $names
        else
            $function/apidoc:params/apidoc:param[local:apidoc-language(.) = $language]/@name/string()
    return if (count($names) != count(distinct-values($names))) then
        fn:error(xs:QName("err:XQST0039"),``[Duplicate parameter name in `{local:function-name($function)}`]``)
    else
        $names
};

declare function local:function-arity($function as element(apidoc:function)) as map(*) {
    let $params := $function/apidoc:params/apidoc:param[local:apidoc-language(.) = $language]
    return map {
        "min": count($params[not(@optional = "true")]),
        "max": count($params)
    }
};

declare function local:function-parameter($function as element(apidoc:function), $name as xs:string) as element(apidoc:param) {
    let $names := map:get($param-fixes, local:function-name($function))
    return if (exists($names)) then
        let $i := fn:index-of($names, $name)
        return $function/apidoc:params/apidoc:param[$i]
    else
        $function/apidoc:params/apidoc:param[@name = $name and local:apidoc-language(.) = $language]
};

declare function local:function-parameter-type($function as element(apidoc:function), $name as xs:string) as xs:string {
    let $type := local:function-parameter($function, $name)/@type/string()
    return switch ($type)
    case "atomic type" return "item()*"
    case "xs:string)" return "xs:string"
    case "xs:dateTime? | xs:date? | xs:string?" return "(xs:dateTime?|xs:date?|xs:string?)"
    case "binary())" return "binary()"
    case "element(sec:query-rolesets)*)" return "element(sec:query-rolesets)*"
    case "function()" return "function(*)"
    case "item()* | map:map" return "(item()*|map:map)"
    case "(binary()|node())*" return "(binary()*|node()*)"
    case "(binary()|node())?" return "(binary()?|node()?)"
    case "(element()|map:map)?" case "element()?|map:map?" return "(element()?|map:map?)"
    case "(node()? | map:map)?" return "(node()?|map:map?)"
    case "(cts:order|xs:string)*" return "(cts:order*|xs:string*)"
    case "[xs:string?]" return "xs:string?"
    default return replace($type, ",\.\.\.", "...")
};

declare function local:function-return-type($function as element(apidoc:function)) as xs:string {
    let $type := normalize-space($function/apidoc:return[local:apidoc-language(.) = $language])
    return switch ($type)
    case "An XML representation of the certificate." return "element()?"
    case "element())" case "Element()" return "element()"
    case "element()|map:map" return "(element()|map:map)"
    case "element(xsd:schema)*" return "element(xs:schema)*"
    case "empty-sequence" case "empty sequence" return "empty-sequence()"
    case "xs:string)" return "xs:string"
    case "xs:unsignedLong)" return "xs:unsignedLong"
    case "pkgins:install($pkgname)" return "element(pkg:install-status)"
    case "pkgins:revert($ticket)" return "element(pkg:revert-status)"
    case "query-results-serialize($results)" return "item()*"
    case "The return data type is the data type of the date argument" return "item()"
    case "" return
        switch (local:function-name($function))
        case "admin:appserver-set-output-indent-tabs" return "element(configuration)"
        default return ""
    default return $type
};

declare function local:function-xquery-signatures($function as element(apidoc:function)) as xs:string* {
    let $name := local:function-name($function)
    let $params :=
        for $name in local:function-parameters($function)
        let $type := local:function-parameter-type($function, $name)
        return ``[$`{$name}` as `{$type}`]``
    let $type := local:function-return-type($function)
    let $arity-range := local:function-arity($function)
    for $arity in $arity-range?min to $arity-range?max
    let $params := $params[position() = 1 to $arity]
    return if ($type = "") then
        ``[declare function `{$name}`(`{string-join($params, ", ")}`) external;]``
    else
        ``[declare function `{$name}`(`{string-join($params, ", ")}`) as `{$type}` external;]``
};

declare function local:function-html-signatures($function as element(apidoc:function)) as element(div)+ {
    let $name := local:function-name($function)
    let $params :=
        for $name in local:function-parameters($function)
        let $type := local:function-parameter-type($function, $name)
        return

            ${$name},
             as ,
            {$type}

    let $type := local:function-return-type($function)
    let $arity-range := local:function-arity($function)
    for $arity in $arity-range?min to $arity-range?max
    let $params := $params[position() = 1 to $arity]
    return if (count($params) <= 2) then

            {$name}({
                for $param at $i in $params
                return (
                    if ($i > 1) then ", " else (),
                    $param/*
                )
            }){
                if ($type != "") then (
                     as ,
                    {$type}
                ) else ()
            }

    else

{
                (: JEditorPanel does not support vertical-align on tr/td elements, so use valign instead. :)

                    {$name}(
                    {$params[1]/code[@class="arg"]}
                    {$params[1]/code[not(@class="arg")]},

,
                for $param at $i in $params[position() > 1]
                return

                        {$param/code[@class="arg"]}
                        {
                            $param/code[not(@class="arg")],
                            if ($i = count($params) - 1) then ")" else ","
                        }

            }

};

declare function local:function-html-parameters($function as element(apidoc:function)) as element(table)? {
    let $params := local:function-parameters($function)
    return if (exists($params)) then

{
            for $name in local:function-parameters($function)
            let $type := local:function-parameter-type($function, $name)
            let $summary := local:function-parameter($function, $name)
            return

                    ${$name}
                    {
                        for $child in $summary/node()
                        return local:documentation-html($child)
                    }

        }

    else ()
};

(: query ------------------------------------------------------------------- :)

let $prefix := local:prefix($namespaces, $namespace)
for $function in $functions
where $function/@lib = $prefix and $function/@name = $local-name
return (
    local:function-uri($function),
    fn:serialize(local:documentation-html($function/apidoc:summary)),
    string-join(local:function-html-signatures($function) ! fn:serialize(.), ""),
    fn:serialize(local:function-html-parameters($function)),
    fn:serialize($function/apidoc:privilege ! local:documentation-html(.)),
    fn:serialize($function/apidoc:usage ! local:documentation-html(.)),
    fn:serialize($function/apidoc:example ! local:documentation-html(.)),
    ()
)


- added back the description of the test-case

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

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

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

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

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

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

declare function feedback:check-pass(
  $result           as item()*,
  $testCaseName     as xs:string?,
  $testSetName      as xs:string?,
  $expFailureTC     as element(Test)?,
  $ctestMode        as xs:boolean
) as xs:boolean
{
(: if the exact error code was not found, report the test as 'Pass'
   with an attribute correctError=false :)
  let $resultTestRun as xs:boolean :=
    (empty($result) or
     (exists($result) and
      contains(string-join($result/fots:errors,''), "Expected error:") and
      contains(string-join($result/fots:errors,''), "Found error:")))

  let $expectedFailure as xs:boolean :=
   if(exists($expFailureTC) and
      $ctestMode)
    then fn:true()
    else fn:false()

(:if test PASSED and was expected to FAIL OR test FAILED and wasn't expected to
  FAIL => report as FAIL, a.k.a. regression;
  otherwise report as PASS:)
  return ($resultTestRun eq not($expectedFailure))
};

declare %ann:sequential function feedback:pass(
  $case             as element(fots:test-case),
  $result           as item()*,
  $zorbaQuery       as xs:string,
  $env              as element(fots:environment)?,
  $duration         as xs:dayTimeDuration,
  $verbose          as xs:boolean,
  $expectedFailure  as element(Test)?,
  $ctestMode        as xs:boolean
) as element(fots:test-case)
{
  if (exists($expectedFailure) and $ctestMode) then
    feedback:pass-expected-FOTS-failure($case,
                                        $result,
                                        $zorbaQuery,
                                        $env,
                                        $verbose,
                                        "Test case failed but it is marked with EXPECTED_FOTS_FAILURE in test/fots/CMakeLists.txt")
  else feedback:pass($case,
                     $result,
                     (),
                     ())
};

(:~
 : Feedback on a test case that failed but is marked as EXPECTED_FOTS_FAILURE.
 :
 : @param $case test case.
 : @return the test case after certain information was added.
 :)
declare  %private %ann:sequential function feedback:pass-expected-FOTS-failure(
  $case             as element(fots:test-case),
  $result           as item()*,
  $zorbaQuery       as xs:string,
  $env              as element(fots:environment)?,
  $verbose          as xs:boolean,
  $info             as xs:string
) as element(fots:test-case)
{
  if ($verbose) then
  {
    {
      (insert node attribute result{'pass'} as last into $case,
       insert node attribute info{$info} as last into $case,
       insert node
         <fots:info>
           {$env}
           <fots:query>{$zorbaQuery}</fots:query>
           {$result/fots:expected-result,
            $result/fots:result,
            $result/fots:errors}
         </fots:info>
      as last into $case,
      delete node $case/fots:created,
      delete node $case/fots:modified);

      $case
    }
  }
  else <fots:test-case  name="{data($case/@name)}"
                        result="pass"
                        info="{$info}"/>
};

(:~
 : Gives feedback on a test case run with success.
 :
 : @param $case test case.
 : @param $result test result of running the test case.
 : @return info about test case that passed.
 :)
declare %ann:sequential function feedback:pass(
  $case     as element(fots:test-case),
  $result   as item()*,
  $comment  as xs:string?,
  $info     as xs:string?
) as element(fots:test-case)
{
  if(exists($result/fots:errors)) then
    <fots:test-case  name="{data($case/@name)}"
                     result="wrongError"
                     comment="{$result/fots:errors}"/>
  else
  {
    variable $ret := <fots:test-case name="{data($case/@name)}"
                                     result="pass"/>;

    (if(exists($comment))
     then insert node attribute comment{$comment} as last into $ret
     else (),
     if(exists($info))
     then insert node attribute info{$info} as last into $ret
     else ());

     $ret
  }
};

(:~
 : Gives feedback on a test case run without success.
 :
 : @param $case test case.
 : @return the test case after certain information was added.
 :)
declare %ann:sequential function feedback:fail(
  $case             as element(fots:test-case),
  $result           as item()*,
  $zorbaQuery       as xs:string,
  $testSetName      as xs:string?,
  $env              as element(fots:environment)?,
  $duration         as xs:dayTimeDuration,
  $verbose          as xs:boolean,
  $info             as xs:string?
) as element(fots:test-case)
{
  if ($verbose) then
  {
    {
      (insert node attribute result{'fail'} as last into $case,
       if (exists($info))
       then insert node attribute comment{$info} as last into $case
       else (),
       insert node attribute executionTime{$duration} as last into $case,
       insert node
         <fots:info>
           {$env}
           <fots:query>{$zorbaQuery}</fots:query>
           {$result/fots:expected-result,
            $result/fots:result,
            $result/fots:errors}
         </fots:info>
       as last into $case,
       delete node $case/fots:created,
       delete node $case/fots:modified);

       $case
    }
  }
  else
  {
    variable $ret := <fots:test-case name="{data($case/@name)}"
                                     result="fail"/>;

    (if(exists($info))
     then insert node attribute info{$info} as last into $ret
     else ());

     $ret
  }
};

(:~
 : Gives feedback on a test case that is not run (because its name is listed
 : in the $exceptedTestCases global var).
 :
 : @param $case test case.
 : @param $error the reason for which the test case was not run.
 : @return the test case.
 :)
declare function feedback:not-run(
  $case   as element(fots:test-case),
  $error  as xs:string?
) as element(fots:test-case)
{
  if(exists($error))
  then <fots:test-case name="{$case/@name}" result="notRun" comment="{$error}"/>
  else <fots:test-case name="{$case/@name}" result="notRun"/>
};

(:~
 : Gives feedback on a test case that is not run when dependencies are not met.
 :
 : @param $case test case.
 : @param $dependencyError test error returned by the dependency checking.
 : @return the test case.
 :)
declare function feedback:not-applicable(
  $case             as element(fots:test-case),
  $dependencyError  as xs:string
) as element(fots:test-case)
{
  <fots:test-case name="{data($case/@name)}"
                  result="n/a"
                  comment="{$dependencyError}"/>
};

(:~
 : Gives feedback on a test case that is failing because of an error in FOTS.
 :
 : @param $case test case.
 : @param $comment details about the bug opened on W3C Bugzilla.
 : @return the test case.
 :)
declare function feedback:disputed(
  $case     as element(fots:test-case),
  $comment  as xs:string
) as element(fots:test-case)
{
  <fots:test-case name="{data($case/@name)}"
                  result="disputed"
                  comment="{$comment}"/>
};

(:~
 : Gives feedback on a test case that is not run because it hangs.
 :
 : @param $case test case.
 : @param $comment details about the bug opened on Launchpad.
 : @return the test case.
 :)
declare function feedback:too-big(
  $case     as element(fots:test-case),
  $comment  as xs:string
) as element(fots:test-case)
{
  <fots:test-case name="{data($case/@name)}"
                  result="tooBig"
                  comment="{$comment}"/>
};

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

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

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

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

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

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

declare function feedback:check-pass(
  $result           as item()*,
  $testCaseName     as xs:string?,
  $testSetName      as xs:string?,
  $expFailureTC     as element(Test)?,
  $ctestMode        as xs:boolean
) as xs:boolean
{
(: if the exact error code was not found, report the test as 'Pass'
   with an attribute correctError=false :)
  let $resultTestRun as xs:boolean :=
    (empty($result) or
     (exists($result) and
      contains(string-join($result/fots:errors,''), "Expected error:") and
      contains(string-join($result/fots:errors,''), "Found error:")))

  let $expectedFailure as xs:boolean :=
   if(exists($expFailureTC) and
      $ctestMode)
    then fn:true()
    else fn:false()

(:if test PASSED and was expected to FAIL OR test FAILED and wasn't expected to
  FAIL => report as FAIL, a.k.a. regression;
  otherwise report as PASS:)
  return ($resultTestRun eq not($expectedFailure))
};

declare %ann:sequential function feedback:pass(
  $case             as element(fots:test-case),
  $result           as item()*,
  $zorbaQuery       as xs:string,
  $env              as element(fots:environment)?,
  $duration         as xs:dayTimeDuration,
  $verbose          as xs:boolean,
  $expectedFailure  as element(Test)?,
  $ctestMode        as xs:boolean
) as element(fots:test-case)
{
  if (exists($expectedFailure) and $ctestMode) then
    feedback:pass-expected-FOTS-failure($case,
                                        $result,
                                        $zorbaQuery,
                                        $env,
                                        $verbose,
                                        "Test case failed but it is marked with EXPECTED_FOTS_FAILURE in test/fots/CMakeLists.txt")
  else feedback:pass($case,
                     $result,
                     (),
                     ())
};

(:~
 : Feedback on a test case that failed but is marked as EXPECTED_FOTS_FAILURE.
 :
 : @param $case test case.
 : @return the test case after certain information was added.
 :)
declare  %private %ann:sequential function feedback:pass-expected-FOTS-failure(
  $case             as element(fots:test-case),
  $result           as item()*,
  $zorbaQuery       as xs:string,
  $env              as element(fots:environment)?,
  $verbose          as xs:boolean,
  $info             as xs:string
) as element(fots:test-case)
{
  if ($verbose) then
  {
    {
      (insert node attribute result{'pass'} as last into $case,
       insert node attribute info{$info} as last into $case,
       insert node
         <fots:info>
           {$env}
           <fots:query>{$zorbaQuery}</fots:query>
           {$result/fots:expected-result,
            $result/fots:result,
            $result/fots:errors}
         </fots:info>
      as last into $case,
      delete node $case/fots:description,
      delete node $case/fots:created,
      delete node $case/fots:modified);

      $case
    }
  }
  else <fots:test-case  name="{data($case/@name)}"
                        result="pass"
                        info="{$info}"/>
};

(:~
 : Gives feedback on a test case run with success.
 :
 : @param $case test case.
 : @param $result test result of running the test case.
 : @return info about test case that passed.
 :)
declare %ann:sequential function feedback:pass(
  $case     as element(fots:test-case),
  $result   as item()*,
  $comment  as xs:string?,
  $info     as xs:string?
) as element(fots:test-case)
{
  if(exists($result/fots:errors)) then
    <fots:test-case  name="{data($case/@name)}"
                     result="wrongError"
                     comment="{$result/fots:errors}"/>
  else
  {
    variable $ret := <fots:test-case name="{data($case/@name)}"
                                     result="pass"/>;

    (if(exists($comment))
     then insert node attribute comment{$comment} as last into $ret
     else (),
     if(exists($info))
     then insert node attribute info{$info} as last into $ret
     else ());

     $ret
  }
};

(:~
 : Gives feedback on a test case run without success.
 :
 : @param $case test case.
 : @return the test case after certain information was added.
 :)
declare %ann:sequential function feedback:fail(
  $case             as element(fots:test-case),
  $result           as item()*,
  $zorbaQuery       as xs:string,
  $testSetName      as xs:string?,
  $env              as element(fots:environment)?,
  $duration         as xs:dayTimeDuration,
  $verbose          as xs:boolean,
  $info             as xs:string?
) as element(fots:test-case)
{
  if ($verbose) then
  {
    {
      (insert node attribute result{'fail'} as last into $case,
       if (exists($info))
       then insert node attribute comment{$info} as last into $case
       else (),
       insert node attribute executionTime{$duration} as last into $case,
       insert node
         <fots:info>
           {$env}
           <fots:query>{$zorbaQuery}</fots:query>
           {$result/fots:expected-result,
            $result/fots:result,
            $result/fots:errors}
         </fots:info>
       as last into $case,
       delete node $case/fots:description,
       delete node $case/fots:created,
       delete node $case/fots:modified);

       $case
    }
  }
  else
  {
    variable $ret := <fots:test-case name="{data($case/@name)}"
                                     result="fail"/>;

    (if(exists($info))
     then insert node attribute info{$info} as last into $ret
     else ());

     $ret
  }
};

(:~
 : Gives feedback on a test case that is not run (because its name is listed
 : in the $exceptedTestCases global var).
 :
 : @param $case test case.
 : @param $error the reason for which the test case was not run.
 : @return the test case.
 :)
declare function feedback:not-run(
  $case   as element(fots:test-case),
  $error  as xs:string?
) as element(fots:test-case)
{
  if(exists($error))
  then <fots:test-case name="{$case/@name}" result="notRun" comment="{$error}"/>
  else <fots:test-case name="{$case/@name}" result="notRun"/>
};

(:~
 : Gives feedback on a test case that is not run when dependencies are not met.
 :
 : @param $case test case.
 : @param $dependencyError test error returned by the dependency checking.
 : @return the test case.
 :)
declare function feedback:not-applicable(
  $case             as element(fots:test-case),
  $dependencyError  as xs:string
) as element(fots:test-case)
{
  <fots:test-case name="{data($case/@name)}"
                  result="n/a"
                  comment="{$dependencyError}"/>
};

(:~
 : Gives feedback on a test case that is failing because of an error in FOTS.
 :
 : @param $case test case.
 : @param $comment details about the bug opened on W3C Bugzilla.
 : @return the test case.
 :)
declare function feedback:disputed(
  $case     as element(fots:test-case),
  $comment  as xs:string
) as element(fots:test-case)
{
  <fots:test-case name="{data($case/@name)}"
                  result="disputed"
                  comment="{$comment}"/>
};

(:~
 : Gives feedback on a test case that is not run because it hangs.
 :
 : @param $case test case.
 : @param $comment details about the bug opened on Launchpad.
 : @return the test case.
 :)
declare function feedback:too-big(
  $case     as element(fots:test-case),
  $comment  as xs:string
) as element(fots:test-case)
{
  <fots:test-case name="{data($case/@name)}"
                  result="tooBig"
                  comment="{$comment}"/>
};


Default declarations need to come first.

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

xquery version "1.0";

declare namespace f="http://exist-db.org/xquery/local-functions";
declare namespace dc="http://purl.org/dc/elements/1.1/";

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

declare option exist:serialize "media-type=text/xml";

let $col := xdb:create-collection("/db", "rss")
let $rssUri := xs:anyURI("http://wiki.exist-db.org/exec/rss?snip=start")
let $stored := util:catch("java.lang.Exception",
                   xdb:store($col, "news.rss", $rssUri, "text/xml"),
                   ()
               )
return
    if ($stored) then

        {
            for $item in doc($stored)//item
            return (

                    {substring($item/dc:date, 1, 10)}

,

                {$item/title/text()}
            )
        }

    else

Failed to load news from Wiki...

xquery version "1.0";

declare option exist:serialize "media-type=text/xml";

declare namespace f="http://exist-db.org/xquery/local-functions";
declare namespace dc="http://purl.org/dc/elements/1.1/";

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

let $col := xdb:create-collection("/db", "rss")
let $rssUri := xs:anyURI("http://wiki.exist-db.org/exec/rss?snip=start")
let $stored := util:catch("java.lang.Exception",
                   xdb:store($col, "news.rss", $rssUri, "text/xml"),
                   ()
               )
return
    if ($stored) then

        {
            for $item in doc($stored)//item
            return (

                    {substring($item/dc:date, 1, 10)}

,

                {$item/title/text()}
            )
        }

    else

Failed to load news from Wiki...