Fix make doc target when multiple version of the same module exists. Approved: Sorin Marian Nasoi, Matthias Brantner

xquery version "3.0";

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

(:~
 : Function library providing transformation functions from XQDoc to XHTML.
 :
 : @author Gabriel Petrovay, Sorin Nasoi
 : @project xqdoc/xqdoc2xhtml
 :)
module namespace xqdoc2html  = "http://www.zorba-xquery.com/modules/xqdoc2xhtml/";

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

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 pxqdoc = "http://www.zorba-xquery.com/modules/project_xqdoc";
import module namespace fetch = "http://www.zorba-xquery.com/modules/fetch";

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

import schema namespace xqdoc = "http://www.xqdoc.org/1.0";
import schema namespace output = "http://www.w3.org/2010/xslt-xquery-serialization";

declare namespace an = "http://www.zorba-xquery.com/annotations";
declare namespace werr = "http://www.w3.org/2005/xqt-errors";
declare namespace z = "http://www.zorba-xquery.com/manifest";

declare copy-namespaces preserve, inherit;

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

(:~
 : the name of the folder where the modules will be copied for the XQDoc documentation
 :)
declare %private variable $xqdoc2html:moduleFolderName as xs:string := "modules";

(:~
 : the name of the folder where the examples will be copied for the XQDoc documentation
 :)
declare %private variable $xqdoc2html:exampleFolderName as xs:string := "examples";

(:~
 :  The QName of the collection containing the XQDoc XML's
 :)
declare %private variable $xqdoc2html:collection as xs:QName := xs:QName("xqdoc2html:collection");
declare collection xqdoc2html:collection as node()*;

(:~
 :  Collector of the entries in the left menu
 :)
declare %private variable $xqdoc2html:menuEntries := <entries/>;

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

(:~
 :  The level 1 wights for the categories in the left menu (this gives the order of the Level1 items)
 :)
declare %private variable $xqdoc2html: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");

(:~
 : This variable contains all the schemas imported by the modules
 :)
declare %private variable $xqdoc2html:schemasCollector := <schemas/>;

(:~
 : The serialization parameters for XML serialization.
 :)
declare %private variable $xqdoc2html:serParamXml :=
  <output:serialization-parameters>
    <output:method value="xml"/>
    <output:indent value="yes"/>
  </output:serialization-parameters>;
(:~
 : The serialization parameters for XHTML serialization.
 :)
declare %private variable $xqdoc2html:serParamXhtml:=
  <output:serialization-parameters>
    <output:method value="xhtml"/>
    <output:indent value="yes"/>
  </output:serialization-parameters>;
(:~
 : The serialization parameters for text serialization.
 :)
declare %private variable $xqdoc2html:serParamText :=
  <output:serialization-parameters>
    <output:method value="text"/>
  </output:serialization-parameters>;

(:~
 : This is the name of the function index XHTML page.
 :)
declare %private variable $xqdoc2html:functionIndexPageName as xs:string := "function_index.html";

(:_____________________________________________________________________________________________________:)

(:~
 : This function adds a new record into the $collector.
 : @param $schemaURI the schema URI.
 : @param $fileName the schema file name.
 : @param $collector the name of the collector.
 : @return empty sequence.
 :)
declare %private %an:sequential function xqdoc2html:collect-schema (
  $schemaURI as xs:string,
  $fileName as xs:string,
  $collector)
{
  insert node <schema uri="{$schemaURI}" file="{$fileName}" /> as last into $collector;
};

(:~
 : This function returns a sequence containing all the distinct items
 : that appear in $arg1 but not in $arg2, in an arbitrary order.
 :
 : @param $arg1 first sequence.
 : @param $arg2 second sequence.
 : @return $arg1-$arg2.
 :)
declare %private function  xqdoc2html:value-except(
  $arg1 as xs:anyAtomicType*,
  $arg2 as xs:anyAtomicType*) as xs:anyAtomicType*
{
  fn:distinct-values($arg1[not(.=$arg2)])
};

(:~
 : This function returns a sequence containing all the distinct items
 : that appear in both $arg1 and $arg2, in an arbitrary order.
 :
 : @param $arg1 first sequence.
 : @param $arg2 second sequence.
 : @return $arg1 intersect $arg2.
 :)
declare %private function  xqdoc2html:value-intersect(
  $arg1 as xs:anyAtomicType*,
  $arg2 as xs:anyAtomicType*) as xs:anyAtomicType*
{
  fn:distinct-values($arg1[.=$arg2])
};

(:~
 : The function gathers all the files with the given extensions from the provided $sourcePath
 : and copies them to $destinationPath. The folder structure is not preserved (all files
 : are saved on the same folder level independent of their source folder structure).
 :
 : @param $sourcePath where to search for modules recursively.
 : @param $destinationPath destination folder for the files.
 : @param $extensions The file types to copy as a sequence of extensions strings.
 :    E.g. ("cpp", "h", "xml")
 : @return The empty sequence.
 :)
declare %private %an:sequential function xqdoc2html:gather-and-copy(
  $sourcePath       as xs:string,
  $destinationPath  as xs:string,
  $extensions       as xs:string+)
{
  if(fn:not(file:exists($destinationPath))) then
    ()
  else
    for $extension in $extensions

    for $file in file:list($sourcePath, fn:true(), fn:concat("*.", $extension))

    let $fileName :=
    fn:tokenize($file, fn:concat("\", file:directory-separator()))[last()]

    let $fileSourcePath := fn:concat($sourcePath, file:directory-separator(), $file)

    let $fileDestinationPath :=
    fn:concat($destinationPath, file:directory-separator(), $fileName)

    return
      file:copy($fileSourcePath,$fileDestinationPath)

};

(:~
 : Returns the string resulting from replacing the directory-separators (i.e. // ) with '_'
 :
 : @param $moduleURI the path to the module URI.
 : @return the string resulting from replacing the directory-separators (i.e. // ) with '_'.
 :
 :)
declare function xqdoc2html:get-filename($moduleURI as xs:string) as xs:string {
  let $lmodule := if(fn:ends-with($moduleURI,"/")) then fn:concat($moduleURI,"index") else $moduleURI
  return
    replace(
      replace($lmodule, "/", "_"),
      "http:__",
      ""
  )
};

(:~
 : The function gathers all the files with the given extensions from the provided $sourcePath
 : and copies them to $destinationPath. The folder structure is not preserved.
 :
 : @param $sourcePath where to search for modules recursively.
 : @param $destinationPath destination folder for the files.
 : @param $extensions The sequence of file types to copy (e.g. ("cpp", "h", "xml")).
 : @return The empty sequence.
 :)
declare %private %an:sequential function xqdoc2html:copy-files(
  $sourcePath       as xs:string,
  $destinationPath  as xs:string,
  $extensions       as xs:string+)
{
  file:create-directory($destinationPath);
  xqdoc2html:gather-and-copy($sourcePath, $destinationPath, $extensions);
};

(:_____________________________________________________________________________________________________:)

(:~
 : The function copies all the files under the following folders
 : from $xhtmlRequisitesPath to $xqdocBuildPath/xhtml folder:
 :

 :
schemas

 :
images

 :
java scripts

 :
css stylesheets

 :
templates

 :
xq.src folders

 :

 :
 : @param $xhtmlRequisitesPath the folder containing the images, lib, styles and templates folders.
 : @param $xqdocBuildPath where to generate the XQDoc XML documents.
 : @return Empty sequence.
 :)
declare %an:sequential function xqdoc2html:copy-xhtml-requisites(
  $xhtmlRequisitesPath  as xs:string,
  $xqdocBuildPath       as xs:string) as empty-sequence()
{
  let $xhtmlPath      := fn:concat($xqdocBuildPath, file:directory-separator(), "xhtml"),
      $xmlPath        := fn:concat($xqdocBuildPath, file:directory-separator(), "xml"),
      $schemasPath    := fn:concat($xhtmlPath,      file:directory-separator(), "schemas"),
      $imagesPath     := fn:concat($xhtmlPath,      file:directory-separator(), "images"),
      $libPath        := fn:concat($xhtmlPath,      file:directory-separator(), "lib"),
      $cssPath        := fn:concat($xhtmlPath,      file:directory-separator(), "styles"),
      $templatePath   := fn:concat($xhtmlRequisitesPath,
                                   file:directory-separator(), "templates",
                                   file:directory-separator(), "main.html")
  return
    {
      if(file:exists($xhtmlPath)) then
        file:delete($xhtmlPath);
      else ();

      file:create-directory($xhtmlPath);

      xqdoc2html:copy-files($xhtmlRequisitesPath, $imagesPath ,("gif", "png", "svg"));
      xqdoc2html:copy-files($xhtmlRequisitesPath, $libPath    ,"js");
      xqdoc2html:copy-files($xhtmlRequisitesPath, $cssPath    ,"css");

      file:create-directory($schemasPath);
    }
};

(:_____________________________________________________________________________________________________:)

declare %private %an:sequential function xqdoc2html:create-general-menu()
{
  {
    variable $allStructures     := for $entry in $xqdoc2html:menuEntries/entry return $entry/@structure;
    variable $dvStructures      := distinct-values($allStructures);
    variable $structures        := for $entry in $dvStructures order by lower-case($entry) return $entry;
    variable $leftMenuFunction  := xqdoc2html:create-left-menu();

    xqdoc2html:create-module-table( $structures,
                                    $leftMenuFunction)
  }
};

declare %private %an:sequential function xqdoc2html:create-collection-categories (
$collectionName as xs:QName,
$xqdocXmlPath as xs:string)
{
  ddl:create($collectionName);

  (: gather all the XQDoc XML's :)
  for $xqdocRelPath in file:list($xqdocXmlPath, fn:false(), "*.xml")
  let $path := fn:concat($xqdocXmlPath, file:directory-separator(), $xqdocRelPath )
  let $xqdoc := fn:parse-xml(file:read-text($path))
  return dml:apply-insert-nodes($collectionName, $xqdoc);
};

declare %private %an:sequential function xqdoc2html:collectZorbaManifestEntries(
  $zorbaManifestPath as xs:string,
  $xqdocBuildPath as xs:string)
{
  if(not(file:is-file($zorbaManifestPath))) then
  {
    variable $message := fn:concat("The file <ZorbaManifest.xml> was not found: <", $zorbaManifestPath, ">. Suggestion: run 'cmake' in your build folder such that ZorbaManifest.xml is regenerated.");
    fn:error($err:UE004, $message);
  }
  else
  try
  {
    variable $manifestXML := fn:parse-xml(file:read-text($zorbaManifestPath));

    variable $moduleManifests := $manifestXML/z:manifest/z:module;

    for $module in $moduleManifests
    let $uri := $module/z:uri/text()
    group by $uri
    let $module := if(count($module) gt 1) then $module[@version = max($module/@version)] else $module
    return
      insert node <module uri="{$uri}"
                          isCore="{data($module/@isCore)}"
                          version="{if (exists(data($module/@version))) then data($module/@version) else ''}"
                          projectRoot="{data($module/z:projectRoot)}"/> as last into $xqdoc2html:ZorbaManifest;
  }
  catch *
  {
    fn:error(fn:concat("The file <",$zorbaManifestPath,"> does not have the correct structure."));
  }
};

(:~
 : This function creates the XQDoc XMLs and from them the XQDoc XHTMLs.
 :
 : @param $zorbaManifestPath location of ZorbaManifest.xml.
 : @param $xqdocBuildPath where to output the XQDoc XMLs and XHTMLs.
 : @param $indexHtmlPath where to load the template for the index.html.
 : @param $zorbaVersion Zorba version.
 : @param $xhtmlRequisitesPath the path where the XHTML requisites are stored.
 : @return Empty sequence.
 :)
declare %an:sequential function xqdoc2html:main(
  $zorbaManifestPath as xs:string,
  $xqdocBuildPath as xs:string,
  $indexHtmlPath  as xs:string,
  $zorbaVersion   as xs:string,
  $xhtmlRequisitesPath as xs:string) as empty-sequence()
{
  (: fill out $xqdoc2html:ZorbaManifest :)
  xqdoc2html:collectZorbaManifestEntries($zorbaManifestPath, $xqdocBuildPath);

  variable $xqdocXmlPath as xs:string :=
  fn:concat($xqdocBuildPath, file:directory-separator(), "xml");

  variable $xqdocXhtmlPath as xs:string :=
  fn:concat($xqdocBuildPath, file:directory-separator(), "xhtml");

  (: if there is no main.html template we can not proceed further :)
  if(not(file:exists($indexHtmlPath))) then
    fn:error($err:UE002, "No 'main.html' template was found.");
  else
    ();

  (: start generate the XQDoc XHTML for all the modules :)
  xqdoc2html:create-collection-categories (xs:QName("xqdoc2html:collection"), $xqdocXmlPath);

  trace(xs:string(1)," collect-menu-entries ...");
  xqdoc2html:collect-menu-entries();

  (: copy the left menu inside menu.html page :)
  trace(xs:string(2)," create-general-menu ...");
  variable $leftMenuIndex := xqdoc2html:create-general-menu();
  variable $generalLeftMenu :=
{$leftMenuIndex}
;

  xqdoc2html:generate-xqdoc-xhtml($generalLeftMenu, $xhtmlRequisitesPath, $xqdocXhtmlPath);

  variable $functionIndex :=
  xqdoc2html:generate-function-index-xhtml( $generalLeftMenu,
                                            $indexHtmlPath,
                                            fn:concat($xqdocXhtmlPath,
                                              file:directory-separator(),
                                              $xqdoc2html:functionIndexPageName));

  variable $doc :=
    xqdoc2html:generate-index-html($indexHtmlPath,
                                   $generalLeftMenu,
                                   $leftMenuIndex,
                                   $zorbaVersion);

  file:write(fn:concat($xqdocXhtmlPath, file:directory-separator(), "index.html"),
             $doc,
             $xqdoc2html:serParamXhtml);

  dml:delete-nodes(dml:collection(xs:QName("xqdoc2html:collection")));
  ddl:delete(xs:QName("xqdoc2html:collection"));

  (:delete modules_new.svg if it exists:)
  variable $SVGGraph := fn:concat($xhtmlRequisitesPath,
                        file:directory-separator(),
                        "images",
                        file:directory-separator(),
                        "modules_new.svg");

  if(file:exists($SVGGraph)) then
        file:delete($SVGGraph);
  else ();
};

declare %private function xqdoc2html:get-project-root(
  $moduleUri as xs:string
  ) as xs:string
{
  xs:string(data($xqdoc2html:ZorbaManifest/module[@uri= $moduleUri]/@projectRoot))
};

declare %private function xqdoc2html:get-is-core(
  $moduleUri) as xs:boolean
{
  xs:boolean(data($xqdoc2html:ZorbaManifest/module[@uri= $moduleUri]/@isCore))
};

declare %private function xqdoc2html:get-module-version(
  $moduleUri) as xs:string?
{
  data($xqdoc2html:ZorbaManifest/module[@uri= $moduleUri]/@version)
};

(:~
 : This function embeds some content into XML or XQuery.
 :
 : @param $content the content.
 : @param $type either XML or XQuery are supported.
 : @return A xhtml page.
 :)
declare %private function xqdoc2html:create-xhtml-wrapper(
  $content,
  $type as xs:string)
{

    <meta content="text/html; charset=UTF-8" http-equiv="content-type" />
    <meta content="public" http-equiv="CACHE-CONTROL" />
    <meta content="-1" http-equiv="Expires" />
    <link rel="stylesheet" type="text/css" href="../styles/main.css" />

    <link type="text/css" rel="stylesheet" href="../styles/shCore.css"/>
    <link type="text/css" rel="stylesheet" href="../styles/shThemeDefault.css"/>
    <link type="text/css" rel="stylesheet" href="../styles/shThemeXQuery.css"/>

{$content}

};

declare %private %an:sequential function xqdoc2html:copy-schemas(
  $schemas,
  $xqdocXhtmlPath as xs:string
)
{
  for $schema in $schemas
  let $schemaURI := data($schema/xqdoc:uri)
  return
    (: if the schema does not already exists :)
    if(empty($xqdoc2html:schemasCollector/schema[@uri=$schemaURI])) then
    {
      variable $schemaName := concat(xqdoc2html:get-filename($schemaURI), ".html");
      xqdoc2html:collect-schema($schemaURI, $schemaName, $xqdoc2html:schemasCollector);

      variable $schemaContent := fetch:content($schemaURI, "SCHEMA");
      variable $schemaPath := concat($xqdocXhtmlPath,
                                     file:directory-separator(),
                                     "schemas",
                                     file:directory-separator(),
                                     $schemaName);
      file:write($schemaPath,  xqdoc2html:create-xhtml-wrapper($schemaContent,"xml"), $xqdoc2html:serParamXhtml);
    }
    else ();
};

(:~
 : This function reads the XQDoc XML and then invokes the creation of the XHTML
 : for all the modules corresponding to the XML's found in $xqdocXmlPath
 : and writes the resulting XHTML's in $xqdocXhtmlPath.
 : The hierarchy is not preserved.
 :
 : @param $xhtmlRequisitesPath location of the XHTML prerequisites.
 : @param $xqdocXhtmlPath where to generate the XQDoc XHTML documents.
 : @param $zorbaPath path to zorba source dir
 : @return A string sequence with a status message for each processed module.
 :)
declare %private %an:sequential function xqdoc2html:generate-xqdoc-xhtml(
  $generalLeftMenu,
  $xhtmlRequisitesPath  as xs:string,
  $xqdocXhtmlPath       as xs:string
  ) as xs:string*
{
  variable $modulePath  := fn:concat($xqdocXhtmlPath, file:directory-separator(), $xqdoc2html:moduleFolderName);
  variable $examplesFolderDestination := fn:concat($xqdocXhtmlPath, file:directory-separator(),$xqdoc2html:exampleFolderName);
  file:create-directory($modulePath);
  file:create-directory($examplesFolderDestination);

  for $docNode in dml:collection(xs:QName("xqdoc2html:collection"))
  let $moduleDoc := $docNode/xqdoc:xqdoc/xqdoc:module
  let $moduleName := $moduleDoc/xqdoc:name
  let $moduleUri := xs:string(data($moduleDoc/xqdoc:uri))
  let $getFilename := xqdoc2html:get-filename($moduleUri)
  let $xhtmlRelativeFilePath := fn:concat($getFilename, ".html")
  return
  try
  {
    if(($moduleUri = "http://www.w3.org/2005/xpath-functions") or
       ($moduleUri = "http://www.w3.org/2005/xpath-functions/math") or
       ($moduleUri = "http://www.functx.com/")) then
    {
      (:these modules never change, they have no tests, no external functions, and so on:)
      (:just read the content from the templates folder and compute the left menu:)
      variable $templatesPath := concat($xhtmlRequisitesPath, file:directory-separator(), 'templates');
      variable $xhtmlSource := concat($templatesPath, file:directory-separator(), $xhtmlRelativeFilePath);

      variable $xhtml := xqdoc2html:add-left-menu($generalLeftMenu, $xhtmlSource);
      variable $xhtmlFilePath := fn:concat($xqdocXhtmlPath, file:directory-separator(), $xhtmlRelativeFilePath);
      file:write($xhtmlFilePath, $xhtml, $xqdoc2html:serParamXhtml);

      (: copy the .xq module to the xhtml/modules folder :)
      variable $moduleContent := fetch:content($moduleUri, "MODULE");
      variable $destination := fn:concat($modulePath, file:directory-separator(), pxqdoc:get-filename($moduleUri),".html");
      file:write( $destination,
                xqdoc2html:create-xhtml-wrapper($moduleContent,"xquery"),
                $xqdoc2html:serParamXhtml);
    }
    else
    {
      (: replace the inlined examples with actual XQuery code :)
      variable $examplesPath := xqdoc2html:get-project-root($moduleUri);
      variable $xqdoc2 := xqdoc2html:configure-xml($docNode/xqdoc:xqdoc, $examplesPath, $xqdocXhtmlPath);

      (: copy the examples listed in the .xq file into the xhtml/examples folder :)
      xqdoc2html:copy-examples($xqdoc2, $examplesFolderDestination, $examplesPath);

      (: copy the .xq module to the xhtml/modules folder :)
      variable $moduleContent := fetch:content($moduleUri, "MODULE");
      variable $destination := fn:concat($modulePath, file:directory-separator(), pxqdoc:get-filename($moduleUri),".html");
      file:write( $destination,
                xqdoc2html:create-xhtml-wrapper($moduleContent,"xquery"),
                $xqdoc2html:serParamXhtml);

      (: copy the schemas that are imported by this module :)
      variable $schemaImports := $docNode/xqdoc:xqdoc/xqdoc:imports/xqdoc:import[@type="schema"];
      if(count($schemaImports) ne xs:integer(0)) then
        xqdoc2html:copy-schemas($docNode/xqdoc:xqdoc/xqdoc:imports/xqdoc:import[@type="schema"], $xqdocXhtmlPath);
      else ();

      if($xqdoc2/xqdoc:module/@type = "library") then
      {
        variable $templatePath := fn:concat($xhtmlRequisitesPath, file:directory-separator(),"templates",file:directory-separator(),"main.html");
        variable $xhtml := xqdoc2html:doc($xqdoc2, $generalLeftMenu, $templatePath, $xqdocXhtmlPath);
        variable $xhtmlFilePath := fn:concat($xqdocXhtmlPath, file:directory-separator(), $getFilename,".html");
        file:write($xhtmlFilePath, $xhtml, $xqdoc2html:serParamXhtml);
      }
      else ();
    }
  }
  catch *
  {
    fn:error($err:UE004, fn:concat("FAILED: ", $moduleUri, " Message: ", $werr:description));
  }
};

(:~
 : This function does some additional processing for the $xml like
 : replacing examples with actual links to files.
 :
 : @param $xqdoc the node containing the XQDoc XML.
 : @param $examplePath string with the paths where the examples are kept
 :        separated by
;
.
 : @param $xqdocXhtmlPath where to generate the XQDoc XHTML documents.
 : @return the processed $xhtml.
 :)
declare %private %an:sequential function xqdoc2html:configure-xml (
  $xqdoc,
  $examplePath as xs:string,
  $xqdocXhtmlPath as xs:string)
{
  (: replace the inlineExamples with actual inline code :)
  for $inlineExample in $xqdoc//*:inlineexample
  return
  {
    variable $exampleSource := xqdoc2html:resolve-file-path(fn:data($inlineExample/@href), $examplePath);
    variable $exampleContent := file:read-text($exampleSource);
    replace node $inlineExample with

{text{$exampleContent}}
;
  }

  $xqdoc
};

(:~
 : This method copies all the examples and inlineexamples to the $xqdocXhtmlPath/examples folder.
 :
 : @param $xqdoc the node containing the XQDoc XML.
 : @param $xqdocXhtmlPath location where the resulting Xhtml will be saved on disk.
 : @param $examplePath string with the paths where the examples are kept separated by ; .
 : @return The created XHTML page.
 :)
declare %private %an:sequential function xqdoc2html:copy-examples(
  $xqdoc,
  $examplesFolderDestination as xs:string,
  $examplePath as xs:string)
{
  (:copy the @examples:)
  {
    variable $noExamples := count($xqdoc/xqdoc:functions/xqdoc:function/xqdoc:comment/xqdoc:custom[@tag="example"]);
    (:if($noExamples ne xs:integer(0)) then
      trace($noExamples,
          " number of examples processed");
    else ();:)
    for $example in $xqdoc/xqdoc:functions/xqdoc:function/xqdoc:comment/xqdoc:custom[@tag="example"]
    let $exampleText := xqdoc2html:get-example-filename($example/text())
    let $exampleDestination := fn:concat($examplesFolderDestination, "/", replace($exampleText,'.xq','.html'))
    return
    {
      variable $exampleSource := fn:concat($examplePath, "/", $example/text());
      if(not(file:is-file($exampleSource))) then
        fn:error($err:UE009, fn:concat("Copy example from <", $exampleSource,"> to <", $exampleDestination, "> failed."))
      else
      try {
        xqdoc2html:copy-example($exampleSource, $exampleDestination, $examplePath)
      } catch *  {
        fn:error($err:UE009, fn:concat("Copy example from <", $exampleSource,"> to <", $exampleDestination, "> failed. Error code: ", $werr:code, " Message: ", $werr:description))
      }

    };
  }
};

(:~
 : This function resolves a relative file path given a directory path. The
 : function will fail throwing an error if the destination directory does not
 : exist or if the resulting path does not point to an existing file.
 :
 : @param $relativeFilePath the module file name.
 : @param $directoryPath string with the paths where the examples are kept
 :        separated by
;
.
 : @return The full path of the file to be resolved.
 :)
declare %private %an:nondeterministic function xqdoc2html:resolve-file-path(
  $relativeFilePath as xs:string,
  $directoryPath as xs:string
) as xs:string
{
  (: $directoryPath is zorba_folder/test :)
  if (file:is-directory($directoryPath)) then
    let $fullPath := fn:concat($directoryPath, file:directory-separator(), $relativeFilePath)
    return
      if (file:is-file($fullPath)) then
        $fullPath
      else
        fn:error($err:UE008, fn:concat("The file <", $relativeFilePath, "> ",
          "was not found in the directory <", $directoryPath, ">"))
  else
    fn:error($err:UE010, fn:concat("The path <", $directoryPath, "> must point to an existing directory:"))
};

declare %private %an:sequential function xqdoc2html:copy-example(
  $exampleSource as xs:string,
  $exampleDestination as xs:string,
  $examplePath as xs:string)
{
  (:file:copy($exampleSource,$exampleDestination) :)
  let $search-queries := fn:concat("rbkt[", file:directory-separator(), file:directory-separator(), "//]Queries")
  let $exampleContent := file:read-text( $exampleSource )
  return
  if (fn:not(fn:matches($exampleSource, "[.]xq$")) or
      fn:not(fn:matches($exampleSource, $search-queries))) then
    file:write( $exampleDestination,
                xqdoc2html:create-xhtml-wrapper($exampleContent,"xquery"),
                $xqdoc2html:serParamXhtml)
  else if (fn:matches($exampleContent, "output", "i")) then
    file:write( $exampleDestination,
                xqdoc2html:create-xhtml-wrapper($exampleContent,"xquery"),
                $xqdoc2html:serParamXhtml)
  else
    let $specSource := fn:replace($exampleSource, "[.]xq$", ".spec")
    return
    if (fn:matches($specSource, "[.]spec$") and file:is-file($specSource))
    then
    {
      variable $specContent :=  file:read-text( $specSource );

      variable $specContent2 := fn:replace($specContent,
                                          "\$RBKT_SRC_DIR",
                                          fn:concat($examplePath,"/rbkt"));

      variable $specLines := fn:tokenize($specContent2, "[\n\r]+");

      variable $specArgs := xqdoc2html:parse-spec-args($exampleSource, $specLines);

      variable $specResults := xqdoc2html:parse-spec-results($exampleSource,
                                                            $specLines);

      variable $exampleContent := fn:concat($exampleContent, "

(: Example configuration (taken from zorba testsuite):

", $specContent2, "

---------------------------------------------------------------------------------------
", $specArgs, "

---------------------------------------------------------------------------------------

", $specResults, "
");
          if (fn:string-length($specResults) eq 0)
          then
          {
            if (fn:not(matches($specContent2, "Error")))
            then
            {
              variable $replace-exp-result := "rbkt/ExpQueryResults";

              variable $exp-result-path :=
              fn:replace($exampleSource, $search-queries, $replace-exp-result);

              variable $exp-result := fn:replace($exp-result-path, "[.]xq$", ".xml.res");

              if (($exp-result-path ne $exampleSource) and file:is-file($exp-result))
              then
              {
                variable $output-content := file:read-text ($exp-result);
                variable $exampleContent := fn:concat ($exampleContent, "
Expected output:

",
$output-content,
"

:)
");
                file:write($exampleDestination,
                           xqdoc2html:create-xhtml-wrapper($exampleContent,"xquery"),
                           $xqdoc2html:serParamXhtml);
              }
              else
              {
                fn:error($err:UE008, fn:concat("The example <", $exampleSource,"> does not have expected output. Add the example input and expected output by hand in the example, in a commentary that should also include the word 'output'."));
              }
            }
            else
            {
              variable $exampleContent := fn:concat($exampleContent, "

Test returns an error code.
:)
");
              file:write($exampleDestination,
                         xqdoc2html:create-xhtml-wrapper($exampleContent,"xquery"),
                         $xqdoc2html:serParamXhtml);
            }
          }
          else
          {
            variable $exampleContent := fn:concat($exampleContent, "

:)
");
          file:write($exampleDestination,
                     xqdoc2html:create-xhtml-wrapper($exampleContent,"xquery"),
                     $xqdoc2html:serParamXhtml);
          }
        }
        else
        {
          variable $replace-exp-result := "rbkt/ExpQueryResults";

          variable $exp-result-path := fn:replace($exampleSource, $search-queries, $replace-exp-result);

          variable $exp-result := fn:replace($exp-result-path, "[.]xq$", ".xml.res");

          if (($exp-result-path ne $exampleSource) and file:is-file ($exp-result))
          then
          {
            variable $output-content := file:read-text ($exp-result);

            variable $exampleContent := fn:concat ($exampleContent, "

(:

Expected output:

", $output-content, "

:)
");
            file:write($exampleDestination,
                       xqdoc2html:create-xhtml-wrapper($exampleContent,"xquery"),
                       $xqdoc2html:serParamXhtml);
          }
          else
          {
            fn:error($err:UE008, fn:concat("The example <", $exampleSource,"> does not have expected output. Add the example input and expected output by hand in the example, in a commentary that should also include the word 'output'."));
          }
        }
};

declare %private %an:sequential function xqdoc2html:parse-spec-args(
  $exampleSource as xs:string,
  $specLines as xs:string*) as xs:string
{
  if(fn:empty($specLines)) then
    ""
  else
    let $specLine := $specLines[1]
    return
    if(fn:matches($specLine, "Args:")) then
      let $arg_split := fn:substring-after($specLine, "-x")
      return
      if(fn:string-length($arg_split) eq 0) then string-join($specLines, " ")
      else
        let $var_value := fn:tokenize($arg_split, "=")
        let $var_name := fn:normalize-space(fn:replace($var_value[1], ":$", ""))
        let $val_path := fn:normalize-space($var_value[2])
        return
        if(fn:matches($val_path, "[.]xml$")) then
          let $var_text := file:read-text( $val_path )
          let $var_size := fn:string-length($var_text)
          return
            if( $var_size lt 2048 ) then
              fn:concat("
Example input xml for variable $", $var_name, ":
",$var_text,
"
",
              xqdoc2html:parse-spec-args($exampleSource, fn:subsequence($specLines, 2)))
            else
              fn:error($err:UE008, fn:concat("The input xml for variable $", $var_name, "in example <", $exampleSource,"> is bigger than 2KB.
              Add the example input and expected output by hand in the example, in a commentary that should also include the word 'output'."))
        else
          xqdoc2html:parse-spec-args($exampleSource, fn:subsequence($specLines, 2))
    else
      xqdoc2html:parse-spec-args($exampleSource, fn:subsequence($specLines, 2))

};

declare %private %an:sequential function xqdoc2html:load-expected-results(
  $result_split as xs:string*) as xs:string
{
  if(fn:empty($result_split)) then
    ""
  else
    let $result_path := $result_split[1]
    let $result_text := file:read-text( $result_path )
    let $nonlast_result := if(fn:count($result_split) gt 1) then "
Or equivalent
" else ""
    return
    fn:concat("Expected output:

",$result_text,
"
",
$nonlast_result,
 xqdoc2html:load-expected-results(fn:subsequence($result_split, 2)))
};

declare %private %an:sequential function xqdoc2html:parse-spec-results(
  $exampleSource as xs:string,
  $specLines as xs:string*) as xs:string
{
  if(fn:empty($specLines)) then
    ""
  else
    let $specLine := $specLines[1]
    return
    if(fn:matches($specLine, "Result:")) then
      let $result_split := fn:tokenize(fn:normalize-space(fn:substring-after($specLine, "Result:")), "[ \t]")
      return
      if(fn:count($result_split) lt 1) then
        ""
      else
        xqdoc2html:load-expected-results($result_split)
    else
      xqdoc2html:parse-spec-results($exampleSource, fn:subsequence($specLines, 2))

};

(:~
 : This function returns true if there are external functions declared.
 : @param $xqdoc the xqdoc functions.
 : @return a xs:boolean if there are external functions declared.
 :)
declare %private function xqdoc2html:contains-external-functions (
  $xqdoc) as xs:boolean
{
  let $ext := count(for $sig in $xqdoc/xqdoc:functions/xqdoc:function/xqdoc:signature
                    return
                      if(ends-with($sig/text(),'external')) then 1
                      else ())
  let $func := count ($xqdoc/xqdoc:functions/xqdoc:function/xqdoc:signature)
  return
    (($func gt 0) and ($ext gt 0))
};

declare %private function xqdoc2html:get-example-filename($examplePath as xs:string) as xs:string
{
  if (fn:contains($examplePath,"/")) then
    fn:replace($examplePath,"/","_")
  else if (fn:contains($examplePath,"\")) then
    fn:replace($examplePath,"\","_")
  else
    $examplePath
};

declare  %private function xqdoc2html:get-example-filename-link($examplePath as xs:string) as xs:string
{
  fn:tokenize($examplePath,fn:concat("\",file:directory-separator()))[last()]
};

declare %private function xqdoc2html:add-images(
  $type as xs:string)
{
  let $ZorbaOptAndAnn := "../../html/options_and_annotations.html",
      $xquSpec := "http://www.w3.org/TR/xquery-update-10/#dt-updating-function",
      $xqsSpec := "../../html/scripting_spec.html",
      $xq11Spec := "http://www.w3.org/TR/xquery-11/#FunctionDeclns",
      $xqExternal := "http://www.w3.org/TR/xquery-30/#dt-external-function",
      $imagesPath := "images/"
  return

      {if(contains($type, "updating")) then

       else ()}
       {if(contains($type, "sequential")) then

       else ()}
       {if(contains($type, "nondeterministic ")) then

       else ()}
       {if(contains($type, "variadic")) then

       else ()}
       {if(contains($type, "streamable")) then
        <a  href="{$ZorbaOptAndAnn}" title="A function annotated with the http://www.zorba-xquery.com/annotations:streamable annotation is
        a function that may return an xs:string item whose content is streamed. Such a string is called a streamable string.
        Such strings have the advantage that their contents doesn't need to be materialized in memory.
        If a function consuming such a string is able to process the string in a streaming fashion,
        this allows for processing of strings with a virtually infinite length.
        However, the disadvantage is that a streamable string can only be consumed exactly once.
        If a streamable string is consumed more than once, an error is raised.
        In order to enable multiple consumers of a streamable string, the string:materialize function can be used
        to materialize the entire contents in an (regular) xs:string item." target="_blank">
       else ()}
       {if(contains($type, "external")) then

       else ()}

};

(:~
 : This function creates the XQDoc XHTML.
 :
 : @param $xqdoc the node containing the XQDoc XML.
 : @param $menu the menu.
 : @param $templatePath the path to the main.html template.
 : @param $xqdocXhtmlPath location where the resulting Xhtml will be saved on disk.
 : @param $examplePath string with the paths where the examples are kept separated by ; .
 : @return The created XHTML page.
 :)
declare %private %an:sequential function xqdoc2html:add-left-menu(
  $menu,
  $templatePath as xs:string) {
  let $doc := fn:parse-xml(file:read-text($templatePath))
  return {
    insert nodes $menu
    as last into $doc/*:html/*:body/*:div[@id='main']/*:div[@id='leftMenu'];

    $doc
  }
};

(:~
 : This function creates the XQDoc XHTML.
 :
 : @param $xqdoc the node containing the XQDoc XML.
 : @param $menu the menu.
 : @param $templatePath the path to the main.html template.
 : @param $xqdocXhtmlPath location where the resulting Xhtml will be saved on disk.
 : @param $examplePath string with the paths where the examples are kept separated by ; .
 : @return The created XHTML page.
 :)
declare %private %an:sequential function xqdoc2html:doc(
  $xqdoc,
  $menu,
  $templatePath as xs:string,
  $xqdocXhtmlPath as xs:string) {
  let $doc := fn:parse-xml(file:read-text($templatePath))
  return {
    insert node <title>Documentation for {xqdoc2html:module-uri($xqdoc)}</title>
    as first into $doc/*:html/*:head;

    insert nodes $menu
    as last into $doc/*:html/*:body/*:div[@id='main']/*:div[@id='leftMenu'];

    let $right_content := $doc/*:html/*:body/*:div[@id='main']/*:div[@id='rightcontent']
    return
      if ($right_content)
      then
        insert nodes xqdoc2html:body($xqdoc, $xqdocXhtmlPath)
        as last into $right_content;
      else
        ();

    $doc
  }
};

(:~
 : Get the XQDoc module URI.
 :
 : @param $xqdoc the node containing the XQDoc XML.
 : @return The module URI.
 :)
declare %private function xqdoc2html:module-uri($xqdoc) as xs:string
{
  $xqdoc/xqdoc:module/xqdoc:uri/text()
};

(:~
 : This function creates the XQDoc XHTML body.
 :
 : @param $xqdoc the node containing the XQDoc XML.
 : @param $xqdocXhtmlPath location where the resulting Xhtml will be saved on disk.
 : @return The 'body' of the XHTML.
 :)
declare %private %an:nondeterministic function xqdoc2html:body(
  $xqdoc,
  $xqdocXhtmlPath as xs:string)
{
  (: use only the functions not marked as %private :)
  let $functions := for $function in $xqdoc/xqdoc:functions/xqdoc:function
                    where xqdoc2html:function-is-not-private($function)
                    return $function
  let $moduleUri := xqdoc2html:module-uri($xqdoc)
  let $isZorbaCore as xs:boolean := xqdoc2html:get-is-core($moduleUri)
  let $modulePrefix as xs:string := if(count($functions) ne xs:integer(0)) then
  substring-before($xqdoc/xqdoc:functions/xqdoc:function[1]/xqdoc:name/text(),':') else
  if($xqdoc/xqdoc:variables/xqdoc:variable) then
  substring-before($xqdoc/xqdoc:variables/xqdoc:variable[1]/xqdoc:uri/text(),':') else
  "modNS"
  return
  (
{$moduleUri}{if ($isZorbaCore) then  else () }
,
    xqdoc2html:module-description($moduleUri, $modulePrefix, $xqdoc/xqdoc:module),
    xqdoc2html:module-resources($xqdocXhtmlPath, xqdoc2html:module-uri($xqdoc)),
    xqdoc2html:module-dependencies($xqdoc),
    xqdoc2html:module-external-specifications($xqdoc/xqdoc:module),
    xqdoc2html:module-namespaces(
      $xqdoc/xqdoc:module/xqdoc:custom[@tag="namespaces"]
    ),
    xqdoc2html:module-variables($xqdoc/xqdoc:variables),
    xqdoc2html:module-function-summary($functions),
    xqdoc2html:functions($functions, $xqdocXhtmlPath)
  )
};

(:~
 : Create the module description and module annotations.
 :
 : @param $module the node containing the XQDoc XML module.
 : @return the XHTML for the module description and module annotations.
 :)
declare %private function xqdoc2html:module-description($moduleUri as xs:string, $modulePrefix as xs:string, $module) {
    (
Module Description
,
      Before using any of the functions below please remember to import the module namespace:

import module namespace {$modulePrefix} = "{$moduleUri}";

      ,
      {
        if(xqdoc2html:get-is-core($moduleUri)) then () else
        (Please note that this module does not belong to the core of the Zorba XQuery engine. Please check this resource about this module import.,
,
)
      },
     xqdoc2html:description($module/xqdoc:comment),
     xqdoc2html:annotations-module($module/xqdoc:comment),
     if(($moduleUri = "http://www.w3.org/2005/xpath-functions") or
        ($moduleUri = "http://www.w3.org/2005/xpath-functions/math") or
        ($moduleUri = "http://www.functx.com/")) then ()
     else
      (
XQuery version and encoding for this module:
,

xquery version "{$module/xqdoc:comment/xqdoc:custom[@tag='XQuery version']/text()}" encoding "{$module/xqdoc:comment/xqdoc:custom[@tag='encoding']/text()}";
)
     ),
     let $modVersion := xqdoc2html:get-module-version($moduleUri)
     return
     if($modVersion = "") then ()
     else
     (
Zorba version for this module:
,

The latest version of this module is {$modVersion}. For more information about module versioning in Zorba please check out this resource.

     )
};

(:~
 : Create the XHTML for all description annotations.
 :
 : @param $comment the part of the XQDoc file holding the annotations.
 : @return the XHTML for the description annotations.
 :)
declare %private function xqdoc2html:description($comment) {

{if ($comment/xqdoc:description) then
         $comment/xqdoc:description/node()
       else
         "No description available."}

};

(:~
 : Create the XHTML for all module annotations except these ones:
 :

 :
description

 :
param

 :
return

 :
error

 :
deprecated

 :
see

 :
library

 :

 :
 : @param $comment the part of the XQDoc file holding the module annotations.
 : @return the XHTML for the module annotations.
 :)
declare %private function xqdoc2html:annotations-module($comment) {
  let $annotations := $comment/xqdoc:*[not((local-name(.) = ("description", "param", "return", "error", "deprecated", "see", "project", "custom")))]
  return
    for $annotation in $annotations
    let $annName := local-name($annotation)
    return
    (

{
        concat(upper-case(substring($annName, 1, 1)), substring($annName, 2), ":")
      }
,

{$annotation/node()}

    )
};

(:~
 : Create the XHTML for the module resources.
 :
 : @param $moduleUri the module URI.
 : @param $indexCollector the modules names part of the left menu.
 : @return the XHTML for the 'Module Resources'.
 :)
declare %private %an:nondeterministic function xqdoc2html:module-resources(
  $xqdocXhtmlPath as xs:string,
  $moduleUri as xs:string)
{
  let $folder := xqdoc2html:get-filename($moduleUri)
  return
    (
Module Resources
,

the XQuery module can be found here.

)
};

(:~
 : Create the XHTML for the module dependencies (imported modules, schemas).
 :
 : @param $xqdoc the node containing the XQDoc XML.
 : @return the XHTML for the 'Module Dependencies'.
 :)
declare %private function xqdoc2html:module-dependencies(
    $xqdoc) {
  if (fn:count($xqdoc/xqdoc:imports/xqdoc:import) > 0 or
      fn:count($xqdoc/xqdoc:module/xqdoc:comment/xqdoc:custom[@tag="library"]) > 0) then
    (
Module Dependencies
,
    xqdoc2html:imports($xqdoc))
  else
    ()
};

(:~
 : Create the items for the module dependencies (imported modules, schemas).
 :
 : @param $xqdoc the node containing the XQDoc XML.
 : @return the XHTML for the 'Module Dependencies'.
 :)
declare %private function xqdoc2html:imports(
    $xqdoc) {
  (
  if (fn:count($xqdoc/xqdoc:imports//xqdoc:import[@type = "library"]) > 0) then

Imported modules:

    {
      for $import in $xqdoc/xqdoc:imports//xqdoc:import[@type = "library"]
      return

        {string($import/xqdoc:uri/text())}

    }

  else (),
  if (fn:count($xqdoc/xqdoc:module/xqdoc:comment/xqdoc:custom[@tag="library"]) > 0) then

External C++ library dependencies:

      {
      for $library in $xqdoc/xqdoc:module/xqdoc:comment/xqdoc:custom[@tag="library"]
        return

{$library/node()}

      }

  else (),
  if (fn:count($xqdoc/xqdoc:imports//xqdoc:import[@type = "schema"]) > 0) then
    (
Imported schemas:

    {
      for $import in $xqdoc/xqdoc:imports//xqdoc:import[@type = "schema"]
      return
        if (exists($xqdoc2html:schemasCollector/schema[@uri=$import/xqdoc:uri/text()])) then
        {
          let $file := $xqdoc2html:schemasCollector/schema[@uri=$import/xqdoc:uri/text()]/@file,
              $filePath := fn:concat("schemas",file:directory-separator(),$file)
          return

{string($import/xqdoc:uri/text())}

        }
        else

{string($import/xqdoc:uri/text())}

    }

,

Please note that the schemas are not automatically imported in the modules that import this module.

    In order to import and use the schemas, please add:

    {
    let $namespaces := $xqdoc/xqdoc:module//xqdoc:custom[@tag = "namespaces"]
    return
    string-join(
    for $import in $xqdoc/xqdoc:imports//xqdoc:import[@type = "schema"]
      return
       concat('import schema namespace ',
              $namespaces//xqdoc:namespace[(@uri = $import/xqdoc:uri/text()) and (@isSchema = "true")]/@prefix,
              ' =  "',
              string($import/xqdoc:uri/text()),'";'),"
")
    }

)
  else ()
  )
};

(:~
 : Create the items for the Related Documentation
 : ('see' annotations appearing in the module description part).
 :
 : @param $module the node containing the XQDoc XML module.
 : @return the XHTML for the 'Related Documentation'.
 :)
declare %private function xqdoc2html:module-external-specifications($module) {
  if(fn:count($module/xqdoc:comment/xqdoc:*[(local-name(.) = ("see"))]) >0) then
    (
Related Documentation
,

For more details please also see:

    {
      let $annotations := $module/xqdoc:comment/xqdoc:*[(local-name(.) = ("see"))]
      return
        for $annotation in $annotations
        return
          if(fn:count($annotation/node()) eq 1) then

{xqdoc2html:replace-with-a-html-tag(xs:string($annotation/node()))}

          else

{$annotation/node()}

    }

)
  else
  ()
};

(:~
 : Create the module variables XHTML.
 :
 : @param $variables the node containing the XQDoc XML variables.
 : @return the XHTML for the module variables.
 :)
declare %private function xqdoc2html:module-variables($variables)
{
  let $noPublicVariables :=
    count(for $variable in $variables/xqdoc:variable
          where (count($variable/xqdoc:annotations/xqdoc:annotation[@localname="private"]) = 0)
          return $variable)
  return
  if($noPublicVariables eq xs:integer(0)) then ()
  else
   (
Variables
,

   {
      for $variable in $variables/xqdoc:variable
      let $name := $variable/xqdoc:uri/text()
      let $type := $variable/xqdoc:comment/xqdoc:custom[@tag="type"]/text()
      let $isExternal := $variable/xqdoc:comment/xqdoc:custom[@tag="isExternal"]/text()
      let $an := string-join((for $annotation in $variable/xqdoc:annotations/xqdoc:annotation
                               return data($annotation/@localname),
                               if($isExternal) then "external" else ""," ")," ")

      where (count($variable/xqdoc:annotations/xqdoc:annotation[@localname="private"]) = 0)
      order by $name
      return (

              {xqdoc2html:add-images($an)}
              ${$name}
                  {if(exists($type)) then concat(" as ",$type) else ""}
                  {if(exists($isExternal)) then " external" else ()}

                  {normalize-space(xqdoc2html:description($variable/xqdoc:comment))}

             )
    }

)
};

(:~
 : Create the module namespaces XHTML.
 :
 : @param $namespaces the custom node containing the XQDoc XML namespaces.
 : @return the XHTML for the module namespaces.
 :)
declare %private function xqdoc2html:module-namespaces($namespaces)
{
  if($namespaces/xqdoc:namespace) then
    (
Namespaces
,

    {
      for $namespace in $namespaces/xqdoc:namespace
      let $prefix := data($namespace/@prefix)
      let $uri := data($namespace/@uri)
      (:where empty($variable/xqdoc:invoked) :)
      order by $prefix
      return (

              {$prefix}
              {$uri}

             )
    }
)
  else
    ()
};

(:~
 : Create the functions summary XHTML table.
 :
 : @param $functions the node containing the XQDoc XML functions.
 : @return the XHTML for the module function summary.
 :)
declare %private function xqdoc2html:module-function-summary($functions)
{

Function Summary
,
  if(count($functions)) then

{
      for $function in $functions
      let $name := fn:substring-after($function/xqdoc:name/text(),':'),
          $signature := $function/xqdoc:signature/text(),
          $param-number := $function/@arity,
          $isDeprecated := fn:exists($function/xqdoc:comment/xqdoc:deprecated),
          $description := data($function/xqdoc:comment/xqdoc:description),
          $shortDescription := if(not(fn:substring-before($description,".") = "")) then
                               fn:concat(fn:substring-before($description,"."),".") else ""
      order by $name, $param-number
      return
        let $type := replace(normalize-space(substring-after(substring-before($signature, "function"), "declare")),"%",""),
            $isExternal := ends-with($signature, "external"),
            $paramsAndReturn := substring-after($signature,concat(':',$name)),
            $external := if(ends-with($signature,"external")) then "external" else ""
        return

            {xqdoc2html:add-images(concat($type," ",$external))}

              <tt>{
                if ($isDeprecated) then
                  {$name}
                else
                  {$name}
              }{xqdoc2html:split-function-signature($paramsAndReturn)}
{$shortDescription}
              </tt>

    }

  else

No public functions declared.

};

(:~
 : Pretty print the function signature.
 :
 : @param $signature the function signature.
 : @return the XHTML for the function signature after reformatting was done.
 :)
declare %private function xqdoc2html:split-function-signature($signature as xs:string) {
  let $line1 := substring-before($signature, "(")
  let $rest := substring-after($signature, "(")
  let $params :=
    (: if the function has parameter :)
    if (matches($rest, "\$")) then
      let $tmp := substring-before($rest, ") as ")
      return
        (: if we don't have a return type specified :)
        if ($tmp eq "") then
          (: en external function declaration :)
          if (ends-with($rest, ") external")) then
            substring-before($rest, ") external")
          (: no external function :)
          else
            substring-before($rest, ")")
        (: the return type is specified :)
        else
          $tmp
    (: no parameters :)
    else
      ""
  let $after := substring-after($signature, concat($params, ")"))
  let $parts := fn:tokenize($line1,' ')
  let $line11 := fn:string-join(for $part in $parts return $part, ' ')
  let $line12 := fn:substring-after($line1, normalize-space($line11))
  return (
    $line11,{$line12}," (
",
    for $param at $pos in tokenize($params, "\$")
    let $nsParam := normalize-space($param)
    let $param1 := if(contains($nsParam,"as")) then fn:substring-before($nsParam," as ")
                   else if(contains($nsParam,",")) then fn:substring-before($nsParam,",")
                   else if(contains($nsParam,")")) then fn:substring-before($nsParam,")")
                   else $nsParam
    let $param2 := fn:substring-after($nsParam, $param1)
    where $pos > 1
    return (
      {concat("            $", $param1)},$param2,"
"),
    concat(")", $after)
  )
};

(:~
 : Create the details for all the functions.
 :
 : @param $functions the node containing the XQDoc XML functions.
 : @param $xqdocXhtmlPath location where the resulting Xhtml will be saved on disk.
 : @return the XHTML for the function details.
 :)
declare %private %an:nondeterministic function xqdoc2html:functions($functions, $xqdocXhtmlPath) {
    if(count($functions)) then (

Functions
,
      for $function in $functions
      let $name := fn:substring-after($function/xqdoc:name/text(),':'),
          $signature := $function/xqdoc:signature/text(),
          $param-number := $function/@arity,
          $comment := $function/xqdoc:comment,
          $isDeprecated := fn:exists($comment/xqdoc:deprecated)
      order by $name, $param-number
      return (

{
          if ($isDeprecated) then
            {xqdoc2html:module-function-link($name, $signature)}
          else
            xqdoc2html:module-function-link($name, $signature)
        }
,
        if ($isDeprecated) then

Deprecated{
            if (exists($comment/xqdoc:deprecated/node())) then
              (" - ", $comment/xqdoc:deprecated/node())
            else
              ()
          }

        else
          (),

{xqdoc2html:split-function-signature($signature)}
,
        xqdoc2html:description($comment),
        xqdoc2html:function-parameters($comment),
        xqdoc2html:function-return($comment),
        xqdoc2html:errors($comment),
        xqdoc2html:annotations($comment),
        xqdoc2html:annotations-author($comment),
        xqdoc2html:annotations-see($comment),
        xqdoc2html:annotations-example($comment, $xqdocXhtmlPath),

)
       )
    else ()
};

(:~
 : Append in the beginning of the function name
 : updating, sequential, nondeterministic is applicable.
 :
 : @param $name the function name.
 : @param $signature the signature of the function.
 : @return a new string for the function name.
 :)
declare %private function xqdoc2html:module-function-link($name as xs:string, $signature) {

let $lcSignature := fn:lower-case($signature)
return
(
  xqdoc2html:add-images(string-join((
  if(contains($lcSignature, 'updating')) then
    'updating' else (),

  if(contains($lcSignature, 'sequential')) then
    'sequential' else (),

  if(contains($lcSignature, 'nondeterministic')) then
    'nondeterministic' else (),

  if(contains($lcSignature, 'non-deterministic')) then
    'nondeterministic' else (),

  if(contains($lcSignature, 'variadic')) then
    'variadic' else (),

  if(contains($lcSignature, 'streamable')) then
    'streamable' else (),
  if(ends-with($signature, 'external')) then
    'external' else ())," ")),

  $name
)
};

(:~
 : Return the function parameters.
 :
 : @param $comment the part of the XQDoc file holding the function parameters.
 : @return the XHTML for the function parameters.
 :)
declare %private function xqdoc2html:function-parameters($comment) {
  let $params := $comment/xqdoc:param
  return
    if (exists($params)) then
      (
Parameters:
,

      {for $param in $params return
{$param/node()}
}

)
    else ()
};

(:~
 : Create the XHTML for all function return values.
 :
 : @param $comment the part of the XQDoc file holding the function annotations.
 : @return the XHTML for the function return values.
 :)
declare %private function xqdoc2html:function-return($comment) {
  let $return := $comment/xqdoc:return
  return
    if (exists($return)) then (

Returns:
,

{$return/node()}

    ) else ()
};

(:~
 : Return the possible function errors.
 :
 : @param $comment the part of the XQDoc file holding the function errors.
 : @return the XHTML for the function errors.
 :)
declare %private function xqdoc2html:errors($comment) {
  let $errors := $comment/xqdoc:error
  return
    if (exists($errors)) then
      (
Errors:
,

      {for $error in $errors return
{data($error)}
}

)
    else ()
};

declare %private function xqdoc2html:annotations-author($comment) {
  let $authors := $comment/xqdoc:*[(local-name(.) = ("author"))]
  return
  if (exists($authors)) then (

Author:
,

{string-join(for $author in $authors return $author/node(),', ')}

    ) else ()
};

(:~
 : Create the XHTML for all function annotations except these ones:
 :

 :
description

 :
param

 :
return

 :
error

 :
deprecated

 :
see

 :
example

 :
.
 :
 : @param $comment the part of the XQDoc file holding the function annotations.
 : @return the XHTML for the function annotations.
 :)
declare %private function xqdoc2html:annotations($comment) {
  let $annotations := $comment/xqdoc:*[not((local-name(.) = ("description", "param", "return", "error", "deprecated", "see", "custom", "author")))]
  return
    for $annotation in $annotations
    let $annName := local-name($annotation)
    return
    (

{
        concat(upper-case(substring($annName, 1, 1)), substring($annName, 2), ":")
      }
,

{$annotation/node()}

    )
};

declare %private function xqdoc2html:replace-with-a-html-tag(
  $text as xs:string)
{
 (: this is meant to be used only for the @see tags at this moment :)
 let $containsHttp := starts-with(lower-case($text), "http://"),
     $isModuleURI as xs:boolean := exists($xqdoc2html:ZorbaManifest/module[@uri= $text])
 return
  if($containsHttp) then
  (
    if($isModuleURI) then
      {$text}
    else
      {$text}
  )
  else $text
};

(:~
 : This function groups together all the @see annotations.
 :
 : @param $comment the part of the XQDoc file holding the function annotations.
 : @return the XHTML for the function 'see' annotations.
 :)
declare %private function xqdoc2html:annotations-see($comment) {
  let $see := $comment/xqdoc:*[local-name(.) = ("see")]
  return
    if (count($see) = 0) then ()
    else
(: **********************************************************     :)
(: this hack should be replaced with links everywhere in text     :)
(: replace the @see nodes that start with http:// with HTML a tag :)
    (
See:
,

    {
    for $annotation in $see
    return
      if(fn:count($annotation/node()) eq 1) then

{xqdoc2html:replace-with-a-html-tag(xs:string($annotation/node()))}

      else

{$annotation/node()}

    }

    )
(: **********************************************************     :)
};

(:~
 : This function groups together all the @example annotations.
 :
 : @param $comment the part of the XQDoc file holding the function annotations.
 : @param $xqdocXhtmlPath location where the resulting Xhtml will be saved on disk.
 : @return the XHTML for the function 'example' annotations.
 :)
declare %private %an:nondeterministic function xqdoc2html:annotations-example($comment, $xqdocXhtmlPath) {
  let $example := $comment//xqdoc:custom[@tag="example"]
  return
    if (count($example) = 0) then ()
    else
    (
Examples:
,

    {for $annotation in $example
    let $exampleFileName := fn:concat("examples", file:directory-separator(),replace(xqdoc2html:get-example-filename($annotation/node()),".xq",".html"))
    return
      if(file:exists(fn:concat($xqdocXhtmlPath, file:directory-separator(), $exampleFileName))) then

{xqdoc2html:get-example-filename-link($annotation/node())}

      else

{fn:concat("The example with filename '",$annotation/node(), "' was not found.")}
}

    )
};

declare %private function xqdoc2html:function-is-not-private($function)
{
  not(exists($function//xqdoc:annotation[@localname = 'private']))
};

(:~
 : This function generates the XQDoc function index.
 : @return Xhtml section with the function index content.
 :)
declare %private function xqdoc2html:generate-function-index()
{

{
  let $alphabet := ("A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z")
  let $letters := distinct-values(
                  for $docNode in dml:collection(xs:QName("xqdoc2html:collection"))
                  for $function in $docNode/xqdoc:xqdoc/xqdoc:functions//xqdoc:function
                    let $functionName := substring-after($function/xqdoc:name/text(),':')
                    where xqdoc2html:function-is-not-private($function)
                  return upper-case(substring($functionName,1,1)))
  return
    (for $letter in $alphabet
    order by $letter
    return
      if(exists(index-of($letters,$letter))) then
        {$letter}
      else
        {upper-case($letter)},

    for $letter in $letters
      order by $letter
      return
      (

{$letter}

           Up

,

,

          {
            for $docNode in dml:collection(xs:QName("xqdoc2html:collection"))
            for $function in $docNode/xqdoc:xqdoc/xqdoc:functions//xqdoc:function
            let $module := $docNode/xqdoc:xqdoc/xqdoc:module,
                $moduleUri := $module/xqdoc:uri/text(),
                $file := fn:concat(pxqdoc:get-filename($moduleUri),'.html'),
                $functionName := fn:substring-after($function/xqdoc:name/text(),':'),
                $signature := $function/xqdoc:signature/text(),
                $arity :=  $function/@arity,
                $isDeprecated := fn:exists($function/xqdoc:comment/xqdoc:deprecated),
                $isExternal := ends-with($signature, "external"),
                $paramsAndReturn :=
                  let $searchCrit := concat(":", $functionName)
                  return
                    if ($isExternal)then
                      normalize-space(substring-before(substring-after($signature, $searchCrit), "external"))
                    else normalize-space(substring-after($signature, $searchCrit))
            where (starts-with($functionName, lower-case($letter)) and
                   xqdoc2html:function-is-not-private($function))
            order by $functionName, $arity
            return

                  <tt>{
                    if ($isDeprecated eq fn:true()) then
                      {$functionName}
                    else
                      {$functionName}
                  }{$paramsAndReturn}
Module Namespace: {$moduleUri}</tt>

          }

     )
     )
}

};

(:~
 : This function generates the XQDoc function index page.
 : @param $indexFunctionLeft the menu containing the links to the generated Xhtml files.
 : @param $templatePath the path to the main.html template.
 : @return The content of the function index page.
 :)
declare %private %an:sequential function xqdoc2html:generate-function-index-xhtml(
  $indexFunctionLeft,
  $templatePath as xs:string,
  $functionIndexPath as xs:string
) {
  let $indexHtmlDoc := fn:parse-xml(file:read-text($templatePath))
  return {
      insert node <title>Function index</title>
      as first into $indexHtmlDoc/*:html/*:head;

      insert nodes $indexFunctionLeft
      as last into $indexHtmlDoc/*:html/*:body/*:div[@id='main']/*:div[@id='leftMenu'];

      let $right_content := $indexHtmlDoc/*:html/*:body/*:div[@id='main']/*:div[@id='rightcontent']
      return
        if ($right_content)
        then
          insert nodes
            (
Indexed Library Module Functions
,
            xqdoc2html:generate-function-index())
          as last into $right_content;
        else
          ();

    (:$indexHtmlDoc:)
    file:write($functionIndexPath,
               $indexHtmlDoc,
               $xqdoc2html:serParamXhtml);
  }
};

declare %private %an:sequential function xqdoc2html:collect-entry (
  $href as xs:string,
  $name as xs:string,
  $structure as xs:string,
  $pureXQuery as xs:string,
  $moduleURI as xs:string)
{
  insert node <entry  href="{$href}"
                      name="{$name}"
                      structure="{$structure}"
                      pureXQuery="{$pureXQuery}"
                      moduleURI="{$moduleURI}" /> as last into $xqdoc2html:menuEntries;
};

declare %private %an:sequential function xqdoc2html:collect-menu-entries()
{
  for $docNode in dml:collection(xs:QName("xqdoc2html:collection"))
      let $module := $docNode/xqdoc:xqdoc/xqdoc:module,
          $lModuleProject := $module/xqdoc:comment/xqdoc:custom[@tag="project"]/text(),
          $lModuleUri     := $module/xqdoc:uri/text(),
          $lXHTMLFileName := pxqdoc:get-filename($lModuleUri),
          $lPureXquery    := not(xqdoc2html:contains-external-functions($docNode/xqdoc:xqdoc)),
          $lTmp := substring-after($lModuleUri,'http://'),
          $lTmpTok := tokenize($lTmp,'/'),
          $lTmp2 := if(ends-with($lTmp,'/')) then substring($lTmp,1,string-length($lTmp)-1) else string-join(xqdoc2html: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
  order by $lModuleProject, $lModuleUri
    return
      xqdoc2html:collect-entry( $lXHTMLFileName,
                                $lModuleName,
                                $structure,
                                xs:string($lPureXquery),
                                $lModuleUri)
};

(:~
 : This function add the subcategories in the menu.
 :
 : @param $table the parent module category.
 : @param $category the category.
 : @param $moduleUri module URI.
 : @return $table after the subcategory together with the modules were added to it.
 :)
declare %private %an:sequential function xqdoc2html:create-module-helper(
  $table,
  $category as xs:string,
  $currentCategory as xs:string
  )
{
  {
    (:insert nodes
{$category}
:)
    insert nodes
{$category}

    {
      for $entry in $xqdoc2html:menuEntries/entry
      let $isZorbaCore as xs:boolean := xqdoc2html:get-is-core(data($entry/@moduleURI))
      order by $entry/@structure
      where ($entry/@structure eq $currentCategory)
      return

          {data($entry/@name)}
          {
            if(xs:boolean(data($entry/@pureXQuery))) then ()
            else
              (E)
          }
          {
            if($isZorbaCore) then

            else ()
          }

    }

    as last into $table
  };

  $table
};

declare %private function xqdoc2html:get-distinct-children (
  $level1 as xs:string*,
  $currentCat as xs:string
) as xs:string*
{
  let $lTmp :=  for $cat in $level1
                where starts-with($cat,concat($currentCat,'/'))
                return substring-after($cat, concat($currentCat,'/'))
  return distinct-values(for $str in $lTmp return tokenize($str,'/')[1])
};

(:~
 : Recursive helper
 :)
declare %private %an:sequential function xqdoc2html:create-module-table-rec(
  $level1 as xs:string*,
  $leveln as xs:string*,
  $curentCat as xs:string,
  $table,
  $mustOrder as xs:boolean) {
  if(empty($leveln)) then ()
  else
    if($mustOrder) then
      for $cat in $leveln
      order by lower-case($cat)
      return {
        variable $lCurentCat := if($curentCat eq "") then $cat else concat($curentCat,'/',$cat);
        xqdoc2html:create-module-helper($table, $cat, $lCurentCat);
        xqdoc2html:create-module-table-rec($level1, xqdoc2html:get-distinct-children($level1,$lCurentCat), $lCurentCat,$table/li[fn:last()]/ul, fn:true())
      }
    else
      for $cat in $leveln
      return {
        variable $lCurentCat := if($curentCat eq "") then $cat else concat($curentCat,'/',$cat);
        xqdoc2html:create-module-helper($table, $cat, $lCurentCat);
        xqdoc2html:create-module-table-rec($level1, xqdoc2html:get-distinct-children($level1,$lCurentCat), $lCurentCat,$table/li[fn:last()]/ul, fn:true())
      }
};

declare %private function xqdoc2html:order-level1-entries
($entries as xs:string*) as xs:string*
{
 (: show first the items in the $xqdoc2html:level1Weight that were also found in the modules :)
 let $defined := for $str in $xqdoc2html:level1Weight return xqdoc2html:value-intersect($str, $entries)

 (: if there are any other level 1 items found in the modules, show them also ordered alphabetically:)
 let $undefined := xqdoc2html:value-except($entries, $defined)

 return ($defined, for $str in $undefined order by $str return $str)
};

(:~
 : This function creates the categories in the menu.
 :
 : @param $leftMenu the menu.
 : @param $root the root.
 : @param $moduleUri module URI.
 : @return $root after the subcategories were added to it.
 :)
declare %private %an:sequential function xqdoc2html:create-module-table(
  $level1 as xs:string*,
  $root)
{
  {
    variable $leveln := distinct-values(for $str in $level1 return tokenize($str,'/')[1]);

    xqdoc2html:create-module-table-rec($level1, xqdoc2html:order-level1-entries($leveln), "", $root, fn:false());

    $root
  }
};

(:~
 : This function creates menu header provided $pageName page.
 :
 : @param $leftMenu the menu.
 : @param $pageName the name of the function index XHTML page.
 : @return $root after the subcategories were added to it.
 :)
declare %private function xqdoc2html:create-left-menu()
{

All Modules

All Functions

};

(:
declare %private %an:sequential function xqdoc2html:create-specialized-left-menu(
  $generalLeftMenu,
  $moduleUri as xs:string)
{
  {
    for $docNode in dml:collection(xs:QName("xqdoc2html:collection"))
      let $module := $docNode/xqdoc:xqdoc/xqdoc:module,
          $lModuleUri     := $module/xqdoc:uri/text(),
          $lPureXquery    := not(xqdoc2html:contains-external-functions($docNode/xqdoc:xqdoc))
    where $lModuleUri = $moduleUri

    for $node in $generalLeftMenu//li
    let $isZorbaCore as xs:boolean := xs:boolean(dml:collection(xs:QName("xqdoc2html:collectionConfig"))/module[@moduleURI=$moduleUri]/@isCore)
    where $node/a[@title eq $moduleUri]
    return
    replace node $node with

      {$node/a/text()}
      {
        if($lPureXquery) then ()
        else
          (E)
      }
      {
        if($isZorbaCore) then

        else ()
      }
;
  }

  $generalLeftMenu
};
:)

(:~
 : This function reads, updates and returns the new index.html.
 :
 : @param $templatePath the path to the 'main.html' template.
 : @param $menu the menu.
 : @param $modules the modules.
 : @param $zorbaVersion Zorba version.
 : @return The content of the new index.html.
 :)
declare %private %an:sequential function xqdoc2html:generate-index-html(
  $templatePath as xs:string,
  $menu,
  $modules,
  $zorbaVersion as xs:string) as document-node()
{
    let $indexHtmlDoc := fn:parse-xml(file:read-text($templatePath))
    return {
        insert node <title>XQuery Modules Documentation</title>
        as first into $indexHtmlDoc/*:html/*:head;

        insert nodes $menu
        as last into $indexHtmlDoc/*:html/*:body/*:div[@id='main']/*:div[@id='leftMenu'];

        let $right_content := $indexHtmlDoc/*:html/*:body/*:div[@id='main']/*:div[@id='rightcontent']
        return
          if ($right_content)
          then
            insert nodes
              (
Zorba XQuery Processor {$zorbaVersion}
,

This document contains a list of all the core and non core Zorba modules.
,

Please check out Modules in Zorba documentation page.
,

The core Zorba modules are annotated below with this small image: .
,

The superscript (E) denotes modules that have external functions. Those are not portable to other XQuery processors.
,

Please send us comments in case you would like to suggest us adding a particular XQuery module, or if you would like to donate an existing third party XQuery module.
,
              $modules)
            as last into $right_content;
          else
            ();

        $indexHtmlDoc
    }
};

xquery version "3.0";

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

(:~
 : Function library providing transformation functions from XQDoc to XHTML.
 :
 : @author Gabriel Petrovay, Sorin Nasoi
 : @project xqdoc/xqdoc2xhtml
 :)
module namespace xqdoc2html  = "http://www.zorba-xquery.com/modules/xqdoc2xhtml/";

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

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 pxqdoc = "http://www.zorba-xquery.com/modules/project_xqdoc";
import module namespace fetch = "http://www.zorba-xquery.com/modules/fetch";

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

import schema namespace xqdoc = "http://www.xqdoc.org/1.0";
import schema namespace output = "http://www.w3.org/2010/xslt-xquery-serialization";

declare namespace an = "http://www.zorba-xquery.com/annotations";
declare namespace werr = "http://www.w3.org/2005/xqt-errors";
declare namespace z = "http://www.zorba-xquery.com/manifest";

declare copy-namespaces preserve, inherit;

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

(:~
 : the name of the folder where the modules will be copied for the XQDoc documentation
 :)
declare %private variable $xqdoc2html:moduleFolderName as xs:string := "modules";

(:~
 : the name of the folder where the examples will be copied for the XQDoc documentation
 :)
declare %private variable $xqdoc2html:exampleFolderName as xs:string := "examples";

(:~
 :  The QName of the collection containing the XQDoc XML's
 :)
declare %private variable $xqdoc2html:collection as xs:QName := xs:QName("xqdoc2html:collection");
declare collection xqdoc2html:collection as node()*;

(:~
 :  Collector of the entries in the left menu
 :)
declare %private variable $xqdoc2html:menuEntries := <entries/>;

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

(:~
 :  The level 1 wights for the categories in the left menu (this gives the order of the Level1 items)
 :)
declare %private variable $xqdoc2html: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");

(:~
 : This variable contains all the schemas imported by the modules
 :)
declare %private variable $xqdoc2html:schemasCollector := <schemas/>;

(:~
 : The serialization parameters for XML serialization.
 :)
declare %private variable $xqdoc2html:serParamXml :=
  <output:serialization-parameters>
    <output:method value="xml"/>
    <output:indent value="yes"/>
  </output:serialization-parameters>;
(:~
 : The serialization parameters for XHTML serialization.
 :)
declare %private variable $xqdoc2html:serParamXhtml:=
  <output:serialization-parameters>
    <output:method value="xhtml"/>
    <output:indent value="yes"/>
  </output:serialization-parameters>;
(:~
 : The serialization parameters for text serialization.
 :)
declare %private variable $xqdoc2html:serParamText :=
  <output:serialization-parameters>
    <output:method value="text"/>
  </output:serialization-parameters>;

(:~
 : This is the name of the function index XHTML page.
 :)
declare %private variable $xqdoc2html:functionIndexPageName as xs:string := "function_index.html";

(:_____________________________________________________________________________________________________:)

(:~
 : This function adds a new record into the $collector.
 : @param $schemaURI the schema URI.
 : @param $fileName the schema file name.
 : @param $collector the name of the collector.
 : @return empty sequence.
 :)
declare %private %an:sequential function xqdoc2html:collect-schema (
  $schemaURI as xs:string,
  $fileName as xs:string,
  $collector)
{
  insert node <schema uri="{$schemaURI}" file="{$fileName}" /> as last into $collector;
};

(:~
 : This function returns a sequence containing all the distinct items
 : that appear in $arg1 but not in $arg2, in an arbitrary order.
 :
 : @param $arg1 first sequence.
 : @param $arg2 second sequence.
 : @return $arg1-$arg2.
 :)
declare %private function  xqdoc2html:value-except(
  $arg1 as xs:anyAtomicType*,
  $arg2 as xs:anyAtomicType*) as xs:anyAtomicType*
{
  fn:distinct-values($arg1[not(.=$arg2)])
};

(:~
 : This function returns a sequence containing all the distinct items
 : that appear in both $arg1 and $arg2, in an arbitrary order.
 :
 : @param $arg1 first sequence.
 : @param $arg2 second sequence.
 : @return $arg1 intersect $arg2.
 :)
declare %private function  xqdoc2html:value-intersect(
  $arg1 as xs:anyAtomicType*,
  $arg2 as xs:anyAtomicType*) as xs:anyAtomicType*
{
  fn:distinct-values($arg1[.=$arg2])
};

(:~
 : The function gathers all the files with the given extensions from the provided $sourcePath
 : and copies them to $destinationPath. The folder structure is not preserved (all files
 : are saved on the same folder level independent of their source folder structure).
 :
 : @param $sourcePath where to search for modules recursively.
 : @param $destinationPath destination folder for the files.
 : @param $extensions The file types to copy as a sequence of extensions strings.
 :    E.g. ("cpp", "h", "xml")
 : @return The empty sequence.
 :)
declare %private %an:sequential function xqdoc2html:gather-and-copy(
  $sourcePath       as xs:string,
  $destinationPath  as xs:string,
  $extensions       as xs:string+)
{
  if(fn:not(file:exists($destinationPath))) then
    ()
  else
    for $extension in $extensions

    for $file in file:list($sourcePath, fn:true(), fn:concat("*.", $extension))

    let $fileName :=
    fn:tokenize($file, fn:concat("\", file:directory-separator()))[last()]

    let $fileSourcePath := fn:concat($sourcePath, file:directory-separator(), $file)

    let $fileDestinationPath :=
    fn:concat($destinationPath, file:directory-separator(), $fileName)

    return
      file:copy($fileSourcePath,$fileDestinationPath)

};

(:~
 : Returns the string resulting from replacing the directory-separators (i.e. // ) with '_'
 :
 : @param $moduleURI the path to the module URI.
 : @return the string resulting from replacing the directory-separators (i.e. // ) with '_'.
 :
 :)
declare function xqdoc2html:get-filename($moduleURI as xs:string) as xs:string {
  let $lmodule := if(fn:ends-with($moduleURI,"/")) then fn:concat($moduleURI,"index") else $moduleURI
  return
    replace(
      replace($lmodule, "/", "_"),
      "http:__",
      ""
  )
};

(:~
 : The function gathers all the files with the given extensions from the provided $sourcePath
 : and copies them to $destinationPath. The folder structure is not preserved.
 :
 : @param $sourcePath where to search for modules recursively.
 : @param $destinationPath destination folder for the files.
 : @param $extensions The sequence of file types to copy (e.g. ("cpp", "h", "xml")).
 : @return The empty sequence.
 :)
declare %private %an:sequential function xqdoc2html:copy-files(
  $sourcePath       as xs:string,
  $destinationPath  as xs:string,
  $extensions       as xs:string+)
{
  file:create-directory($destinationPath);
  xqdoc2html:gather-and-copy($sourcePath, $destinationPath, $extensions);
};

(:_____________________________________________________________________________________________________:)

(:~
 : The function copies all the files under the following folders
 : from $xhtmlRequisitesPath to $xqdocBuildPath/xhtml folder:
 :

 :
schemas

 :
images

 :
java scripts

 :
css stylesheets

 :
templates

 :
xq.src folders

 :

 :
 : @param $xhtmlRequisitesPath the folder containing the images, lib, styles and templates folders.
 : @param $xqdocBuildPath where to generate the XQDoc XML documents.
 : @return Empty sequence.
 :)
declare %an:sequential function xqdoc2html:copy-xhtml-requisites(
  $xhtmlRequisitesPath  as xs:string,
  $xqdocBuildPath       as xs:string) as empty-sequence()
{
  let $xhtmlPath      := fn:concat($xqdocBuildPath, file:directory-separator(), "xhtml"),
      $xmlPath        := fn:concat($xqdocBuildPath, file:directory-separator(), "xml"),
      $schemasPath    := fn:concat($xhtmlPath,      file:directory-separator(), "schemas"),
      $imagesPath     := fn:concat($xhtmlPath,      file:directory-separator(), "images"),
      $libPath        := fn:concat($xhtmlPath,      file:directory-separator(), "lib"),
      $cssPath        := fn:concat($xhtmlPath,      file:directory-separator(), "styles"),
      $templatePath   := fn:concat($xhtmlRequisitesPath,
                                   file:directory-separator(), "templates",
                                   file:directory-separator(), "main.html")
  return
    {
      if(file:exists($xhtmlPath)) then
        file:delete($xhtmlPath);
      else ();

      file:create-directory($xhtmlPath);

      xqdoc2html:copy-files($xhtmlRequisitesPath, $imagesPath ,("gif", "png", "svg"));
      xqdoc2html:copy-files($xhtmlRequisitesPath, $libPath    ,"js");
      xqdoc2html:copy-files($xhtmlRequisitesPath, $cssPath    ,"css");

      file:create-directory($schemasPath);
    }
};

(:_____________________________________________________________________________________________________:)

declare %private %an:sequential function xqdoc2html:create-general-menu()
{
  {
    variable $allStructures     := for $entry in $xqdoc2html:menuEntries/entry return $entry/@structure;
    variable $dvStructures      := distinct-values($allStructures);
    variable $structures        := for $entry in $dvStructures order by lower-case($entry) return $entry;
    variable $leftMenuFunction  := xqdoc2html:create-left-menu();

    xqdoc2html:create-module-table( $structures,
                                    $leftMenuFunction)
  }
};

declare %private %an:sequential function xqdoc2html:create-collection-categories (
$collectionName as xs:QName,
$xqdocXmlPath as xs:string)
{
  ddl:create($collectionName);

  (: gather all the XQDoc XML's :)
  for $xqdocRelPath in file:list($xqdocXmlPath, fn:false(), "*.xml")
  let $path := fn:concat($xqdocXmlPath, file:directory-separator(), $xqdocRelPath )
  let $xqdoc := fn:parse-xml(file:read-text($path))
  return dml:apply-insert-nodes($collectionName, $xqdoc);
};

declare %private %an:sequential function xqdoc2html:collectZorbaManifestEntries(
  $zorbaManifestPath as xs:string,
  $xqdocBuildPath as xs:string)
{
  if(not(file:is-file($zorbaManifestPath))) then
  {
    variable $message := fn:concat("The file <ZorbaManifest.xml> was not found: <", $zorbaManifestPath, ">. Suggestion: run 'cmake' in your build folder such that ZorbaManifest.xml is regenerated.");
    fn:error($err:UE004, $message);
  }
  else
  try
  {
    variable $manifestXML := fn:parse-xml(file:read-text($zorbaManifestPath));

    variable $moduleManifests := $manifestXML/z:manifest/z:module;

    for $module in $moduleManifests
    return
      insert node <module uri="{data($module/z:uri)}"
                          isCore="{data($module/@isCore)}"
                          version="{if (exists(data($module/@version))) then data($module/@version) else ''}"
                          projectRoot="{data($module/z:projectRoot)}"/> as last into $xqdoc2html:ZorbaManifest;
  }
  catch *
  {
    fn:error(fn:concat("The file <",$zorbaManifestPath,"> does not have the correct structure."));
  }
};

(:~
 : This function creates the XQDoc XMLs and from them the XQDoc XHTMLs.
 :
 : @param $zorbaManifestPath location of ZorbaManifest.xml.
 : @param $xqdocBuildPath where to output the XQDoc XMLs and XHTMLs.
 : @param $indexHtmlPath where to load the template for the index.html.
 : @param $zorbaVersion Zorba version.
 : @param $xhtmlRequisitesPath the path where the XHTML requisites are stored.
 : @return Empty sequence.
 :)
declare %an:sequential function xqdoc2html:main(
  $zorbaManifestPath as xs:string,
  $xqdocBuildPath as xs:string,
  $indexHtmlPath  as xs:string,
  $zorbaVersion   as xs:string,
  $xhtmlRequisitesPath as xs:string) as empty-sequence()
{
  (: fill out $xqdoc2html:ZorbaManifest :)
  xqdoc2html:collectZorbaManifestEntries($zorbaManifestPath, $xqdocBuildPath);

  variable $xqdocXmlPath as xs:string :=
  fn:concat($xqdocBuildPath, file:directory-separator(), "xml");

  variable $xqdocXhtmlPath as xs:string :=
  fn:concat($xqdocBuildPath, file:directory-separator(), "xhtml");

  (: if there is no main.html template we can not proceed further :)
  if(not(file:exists($indexHtmlPath))) then
    fn:error($err:UE002, "No 'main.html' template was found.");
  else
    ();

  (: start generate the XQDoc XHTML for all the modules :)
  xqdoc2html:create-collection-categories (xs:QName("xqdoc2html:collection"), $xqdocXmlPath);

  trace(xs:string(1)," collect-menu-entries ...");
  xqdoc2html:collect-menu-entries();

  (: copy the left menu inside menu.html page :)
  trace(xs:string(2)," create-general-menu ...");
  variable $leftMenuIndex := xqdoc2html:create-general-menu();
  variable $generalLeftMenu :=
{$leftMenuIndex}
;

  xqdoc2html:generate-xqdoc-xhtml($generalLeftMenu, $xhtmlRequisitesPath, $xqdocXhtmlPath);

  variable $functionIndex :=
  xqdoc2html:generate-function-index-xhtml( $generalLeftMenu,
                                            $indexHtmlPath,
                                            fn:concat($xqdocXhtmlPath,
                                              file:directory-separator(),
                                              $xqdoc2html:functionIndexPageName));

  variable $doc :=
    xqdoc2html:generate-index-html($indexHtmlPath,
                                   $generalLeftMenu,
                                   $leftMenuIndex,
                                   $zorbaVersion);

  file:write(fn:concat($xqdocXhtmlPath, file:directory-separator(), "index.html"),
             $doc,
             $xqdoc2html:serParamXhtml);

  dml:delete-nodes(dml:collection(xs:QName("xqdoc2html:collection")));
  ddl:delete(xs:QName("xqdoc2html:collection"));

  (:delete modules_new.svg if it exists:)
  variable $SVGGraph := fn:concat($xhtmlRequisitesPath,
                        file:directory-separator(),
                        "images",
                        file:directory-separator(),
                        "modules_new.svg");

  if(file:exists($SVGGraph)) then
        file:delete($SVGGraph);
  else ();
};

declare %private function xqdoc2html:get-project-root(
  $moduleUri as xs:string
  ) as xs:string
{
  xs:string(data($xqdoc2html:ZorbaManifest/module[@uri= $moduleUri]/@projectRoot))
};

declare %private function xqdoc2html:get-is-core(
  $moduleUri) as xs:boolean
{
  xs:boolean(data($xqdoc2html:ZorbaManifest/module[@uri= $moduleUri]/@isCore))
};

declare %private function xqdoc2html:get-module-version(
  $moduleUri) as xs:string?
{
  data($xqdoc2html:ZorbaManifest/module[@uri= $moduleUri]/@version)
};

(:~
 : This function embeds some content into XML or XQuery.
 :
 : @param $content the content.
 : @param $type either XML or XQuery are supported.
 : @return A xhtml page.
 :)
declare %private function xqdoc2html:create-xhtml-wrapper(
  $content,
  $type as xs:string)
{

    <meta content="text/html; charset=UTF-8" http-equiv="content-type" />
    <meta content="public" http-equiv="CACHE-CONTROL" />
    <meta content="-1" http-equiv="Expires" />
    <link rel="stylesheet" type="text/css" href="../styles/main.css" />

    <link type="text/css" rel="stylesheet" href="../styles/shCore.css"/>
    <link type="text/css" rel="stylesheet" href="../styles/shThemeDefault.css"/>
    <link type="text/css" rel="stylesheet" href="../styles/shThemeXQuery.css"/>

{$content}

};

declare %private %an:sequential function xqdoc2html:copy-schemas(
  $schemas,
  $xqdocXhtmlPath as xs:string
)
{
  for $schema in $schemas
  let $schemaURI := data($schema/xqdoc:uri)
  return
    (: if the schema does not already exists :)
    if(empty($xqdoc2html:schemasCollector/schema[@uri=$schemaURI])) then
    {
      variable $schemaName := concat(xqdoc2html:get-filename($schemaURI), ".html");
      xqdoc2html:collect-schema($schemaURI, $schemaName, $xqdoc2html:schemasCollector);

      variable $schemaContent := fetch:content($schemaURI, "SCHEMA");
      variable $schemaPath := concat($xqdocXhtmlPath,
                                     file:directory-separator(),
                                     "schemas",
                                     file:directory-separator(),
                                     $schemaName);
      file:write($schemaPath,  xqdoc2html:create-xhtml-wrapper($schemaContent,"xml"), $xqdoc2html:serParamXhtml);
    }
    else ();
};

(:~
 : This function reads the XQDoc XML and then invokes the creation of the XHTML
 : for all the modules corresponding to the XML's found in $xqdocXmlPath
 : and writes the resulting XHTML's in $xqdocXhtmlPath.
 : The hierarchy is not preserved.
 :
 : @param $xhtmlRequisitesPath location of the XHTML prerequisites.
 : @param $xqdocXhtmlPath where to generate the XQDoc XHTML documents.
 : @param $zorbaPath path to zorba source dir
 : @return A string sequence with a status message for each processed module.
 :)
declare %private %an:sequential function xqdoc2html:generate-xqdoc-xhtml(
  $generalLeftMenu,
  $xhtmlRequisitesPath  as xs:string,
  $xqdocXhtmlPath       as xs:string
  ) as xs:string*
{
  variable $modulePath  := fn:concat($xqdocXhtmlPath, file:directory-separator(), $xqdoc2html:moduleFolderName);
  variable $examplesFolderDestination := fn:concat($xqdocXhtmlPath, file:directory-separator(),$xqdoc2html:exampleFolderName);
  file:create-directory($modulePath);
  file:create-directory($examplesFolderDestination);

  for $docNode in dml:collection(xs:QName("xqdoc2html:collection"))
  let $moduleDoc := $docNode/xqdoc:xqdoc/xqdoc:module
  let $moduleName := $moduleDoc/xqdoc:name
  let $moduleUri := xs:string(data($moduleDoc/xqdoc:uri))
  let $getFilename := xqdoc2html:get-filename($moduleUri)
  let $xhtmlRelativeFilePath := fn:concat($getFilename, ".html")
  return
  try
  {
    if(($moduleUri = "http://www.w3.org/2005/xpath-functions") or
       ($moduleUri = "http://www.w3.org/2005/xpath-functions/math") or
       ($moduleUri = "http://www.functx.com/")) then
    {
      (:these modules never change, they have no tests, no external functions, and so on:)
      (:just read the content from the templates folder and compute the left menu:)
      variable $templatesPath := concat($xhtmlRequisitesPath, file:directory-separator(), 'templates');
      variable $xhtmlSource := concat($templatesPath, file:directory-separator(), $xhtmlRelativeFilePath);

      variable $xhtml := xqdoc2html:add-left-menu($generalLeftMenu, $xhtmlSource);
      variable $xhtmlFilePath := fn:concat($xqdocXhtmlPath, file:directory-separator(), $xhtmlRelativeFilePath);
      file:write($xhtmlFilePath, $xhtml, $xqdoc2html:serParamXhtml);

      (: copy the .xq module to the xhtml/modules folder :)
      variable $moduleContent := fetch:content($moduleUri, "MODULE");
      variable $destination := fn:concat($modulePath, file:directory-separator(), pxqdoc:get-filename($moduleUri),".html");
      file:write( $destination,
                xqdoc2html:create-xhtml-wrapper($moduleContent,"xquery"),
                $xqdoc2html:serParamXhtml);
    }
    else
    {
      (: replace the inlined examples with actual XQuery code :)
      variable $examplesPath := xqdoc2html:get-project-root($moduleUri);
      variable $xqdoc2 := xqdoc2html:configure-xml($docNode/xqdoc:xqdoc, $examplesPath, $xqdocXhtmlPath);

      (: copy the examples listed in the .xq file into the xhtml/examples folder :)
      xqdoc2html:copy-examples($xqdoc2, $examplesFolderDestination, $examplesPath);

      (: copy the .xq module to the xhtml/modules folder :)
      variable $moduleContent := fetch:content($moduleUri, "MODULE");
      variable $destination := fn:concat($modulePath, file:directory-separator(), pxqdoc:get-filename($moduleUri),".html");
      file:write( $destination,
                xqdoc2html:create-xhtml-wrapper($moduleContent,"xquery"),
                $xqdoc2html:serParamXhtml);

      (: copy the schemas that are imported by this module :)
      variable $schemaImports := $docNode/xqdoc:xqdoc/xqdoc:imports/xqdoc:import[@type="schema"];
      if(count($schemaImports) ne xs:integer(0)) then
        xqdoc2html:copy-schemas($docNode/xqdoc:xqdoc/xqdoc:imports/xqdoc:import[@type="schema"], $xqdocXhtmlPath);
      else ();

      if($xqdoc2/xqdoc:module/@type = "library") then
      {
        variable $templatePath := fn:concat($xhtmlRequisitesPath, file:directory-separator(),"templates",file:directory-separator(),"main.html");
        variable $xhtml := xqdoc2html:doc($xqdoc2, $generalLeftMenu, $templatePath, $xqdocXhtmlPath);
        variable $xhtmlFilePath := fn:concat($xqdocXhtmlPath, file:directory-separator(), $getFilename,".html");
        file:write($xhtmlFilePath, $xhtml, $xqdoc2html:serParamXhtml);
      }
      else ();
    }
  }
  catch *
  {
    fn:error($err:UE004, fn:concat("FAILED: ", $moduleUri, " Message: ", $werr:description));
  }
};

(:~
 : This function does some additional processing for the $xml like
 : replacing examples with actual links to files.
 :
 : @param $xqdoc the node containing the XQDoc XML.
 : @param $examplePath string with the paths where the examples are kept
 :        separated by
;
.
 : @param $xqdocXhtmlPath where to generate the XQDoc XHTML documents.
 : @return the processed $xhtml.
 :)
declare %private %an:sequential function xqdoc2html:configure-xml (
  $xqdoc,
  $examplePath as xs:string,
  $xqdocXhtmlPath as xs:string)
{
  (: replace the inlineExamples with actual inline code :)
  for $inlineExample in $xqdoc//*:inlineexample
  return
  {
    variable $exampleSource := xqdoc2html:resolve-file-path(fn:data($inlineExample/@href), $examplePath);
    variable $exampleContent := file:read-text($exampleSource);
    replace node $inlineExample with

{text{$exampleContent}}
;
  }

  $xqdoc
};

(:~
 : This method copies all the examples and inlineexamples to the $xqdocXhtmlPath/examples folder.
 :
 : @param $xqdoc the node containing the XQDoc XML.
 : @param $xqdocXhtmlPath location where the resulting Xhtml will be saved on disk.
 : @param $examplePath string with the paths where the examples are kept separated by ; .
 : @return The created XHTML page.
 :)
declare %private %an:sequential function xqdoc2html:copy-examples(
  $xqdoc,
  $examplesFolderDestination as xs:string,
  $examplePath as xs:string)
{
  (:copy the @examples:)
  {
    variable $noExamples := count($xqdoc/xqdoc:functions/xqdoc:function/xqdoc:comment/xqdoc:custom[@tag="example"]);
    (:if($noExamples ne xs:integer(0)) then
      trace($noExamples,
          " number of examples processed");
    else ();:)
    for $example in $xqdoc/xqdoc:functions/xqdoc:function/xqdoc:comment/xqdoc:custom[@tag="example"]
    let $exampleText := xqdoc2html:get-example-filename($example/text())
    let $exampleDestination := fn:concat($examplesFolderDestination, "/", replace($exampleText,'.xq','.html'))
    return
    {
      variable $exampleSource := fn:concat($examplePath, "/", $example/text());
      if(not(file:is-file($exampleSource))) then
        fn:error($err:UE009, fn:concat("Copy example from <", $exampleSource,"> to <", $exampleDestination, "> failed."))
      else
      try {
        xqdoc2html:copy-example($exampleSource, $exampleDestination, $examplePath)
      } catch *  {
        fn:error($err:UE009, fn:concat("Copy example from <", $exampleSource,"> to <", $exampleDestination, "> failed. Error code: ", $werr:code, " Message: ", $werr:description))
      }

    };
  }
};

(:~
 : This function resolves a relative file path given a directory path. The
 : function will fail throwing an error if the destination directory does not
 : exist or if the resulting path does not point to an existing file.
 :
 : @param $relativeFilePath the module file name.
 : @param $directoryPath string with the paths where the examples are kept
 :        separated by
;
.
 : @return The full path of the file to be resolved.
 :)
declare %private %an:nondeterministic function xqdoc2html:resolve-file-path(
  $relativeFilePath as xs:string,
  $directoryPath as xs:string
) as xs:string
{
  (: $directoryPath is zorba_folder/test :)
  if (file:is-directory($directoryPath)) then
    let $fullPath := fn:concat($directoryPath, file:directory-separator(), $relativeFilePath)
    return
      if (file:is-file($fullPath)) then
        $fullPath
      else
        fn:error($err:UE008, fn:concat("The file <", $relativeFilePath, "> ",
          "was not found in the directory <", $directoryPath, ">"))
  else
    fn:error($err:UE010, fn:concat("The path <", $directoryPath, "> must point to an existing directory:"))
};

declare %private %an:sequential function xqdoc2html:copy-example(
  $exampleSource as xs:string,
  $exampleDestination as xs:string,
  $examplePath as xs:string)
{
  (:file:copy($exampleSource,$exampleDestination) :)
  let $search-queries := fn:concat("rbkt[", file:directory-separator(), file:directory-separator(), "//]Queries")
  let $exampleContent := file:read-text( $exampleSource )
  return
  if (fn:not(fn:matches($exampleSource, "[.]xq$")) or
      fn:not(fn:matches($exampleSource, $search-queries))) then
    file:write( $exampleDestination,
                xqdoc2html:create-xhtml-wrapper($exampleContent,"xquery"),
                $xqdoc2html:serParamXhtml)
  else if (fn:matches($exampleContent, "output", "i")) then
    file:write( $exampleDestination,
                xqdoc2html:create-xhtml-wrapper($exampleContent,"xquery"),
                $xqdoc2html:serParamXhtml)
  else
    let $specSource := fn:replace($exampleSource, "[.]xq$", ".spec")
    return
    if (fn:matches($specSource, "[.]spec$") and file:is-file($specSource))
    then
    {
      variable $specContent :=  file:read-text( $specSource );

      variable $specContent2 := fn:replace($specContent,
                                          "\$RBKT_SRC_DIR",
                                          fn:concat($examplePath,"/rbkt"));

      variable $specLines := fn:tokenize($specContent2, "[\n\r]+");

      variable $specArgs := xqdoc2html:parse-spec-args($exampleSource, $specLines);

      variable $specResults := xqdoc2html:parse-spec-results($exampleSource,
                                                            $specLines);

      variable $exampleContent := fn:concat($exampleContent, "

(: Example configuration (taken from zorba testsuite):

", $specContent2, "

---------------------------------------------------------------------------------------
", $specArgs, "

---------------------------------------------------------------------------------------

", $specResults, "
");
          if (fn:string-length($specResults) eq 0)
          then
          {
            if (fn:not(matches($specContent2, "Error")))
            then
            {
              variable $replace-exp-result := "rbkt/ExpQueryResults";

              variable $exp-result-path :=
              fn:replace($exampleSource, $search-queries, $replace-exp-result);

              variable $exp-result := fn:replace($exp-result-path, "[.]xq$", ".xml.res");

              if (($exp-result-path ne $exampleSource) and file:is-file($exp-result))
              then
              {
                variable $output-content := file:read-text ($exp-result);
                variable $exampleContent := fn:concat ($exampleContent, "
Expected output:

",
$output-content,
"

:)
");
                file:write($exampleDestination,
                           xqdoc2html:create-xhtml-wrapper($exampleContent,"xquery"),
                           $xqdoc2html:serParamXhtml);
              }
              else
              {
                fn:error($err:UE008, fn:concat("The example <", $exampleSource,"> does not have expected output. Add the example input and expected output by hand in the example, in a commentary that should also include the word 'output'."));
              }
            }
            else
            {
              variable $exampleContent := fn:concat($exampleContent, "

Test returns an error code.
:)
");
              file:write($exampleDestination,
                         xqdoc2html:create-xhtml-wrapper($exampleContent,"xquery"),
                         $xqdoc2html:serParamXhtml);
            }
          }
          else
          {
            variable $exampleContent := fn:concat($exampleContent, "

:)
");
          file:write($exampleDestination,
                     xqdoc2html:create-xhtml-wrapper($exampleContent,"xquery"),
                     $xqdoc2html:serParamXhtml);
          }
        }
        else
        {
          variable $replace-exp-result := "rbkt/ExpQueryResults";

          variable $exp-result-path := fn:replace($exampleSource, $search-queries, $replace-exp-result);

          variable $exp-result := fn:replace($exp-result-path, "[.]xq$", ".xml.res");

          if (($exp-result-path ne $exampleSource) and file:is-file ($exp-result))
          then
          {
            variable $output-content := file:read-text ($exp-result);

            variable $exampleContent := fn:concat ($exampleContent, "

(:

Expected output:

", $output-content, "

:)
");
            file:write($exampleDestination,
                       xqdoc2html:create-xhtml-wrapper($exampleContent,"xquery"),
                       $xqdoc2html:serParamXhtml);
          }
          else
          {
            fn:error($err:UE008, fn:concat("The example <", $exampleSource,"> does not have expected output. Add the example input and expected output by hand in the example, in a commentary that should also include the word 'output'."));
          }
        }
};

declare %private %an:sequential function xqdoc2html:parse-spec-args(
  $exampleSource as xs:string,
  $specLines as xs:string*) as xs:string
{
  if(fn:empty($specLines)) then
    ""
  else
    let $specLine := $specLines[1]
    return
    if(fn:matches($specLine, "Args:")) then
      let $arg_split := fn:substring-after($specLine, "-x")
      return
      if(fn:string-length($arg_split) eq 0) then string-join($specLines, " ")
      else
        let $var_value := fn:tokenize($arg_split, "=")
        let $var_name := fn:normalize-space(fn:replace($var_value[1], ":$", ""))
        let $val_path := fn:normalize-space($var_value[2])
        return
        if(fn:matches($val_path, "[.]xml$")) then
          let $var_text := file:read-text( $val_path )
          let $var_size := fn:string-length($var_text)
          return
            if( $var_size lt 2048 ) then
              fn:concat("
Example input xml for variable $", $var_name, ":
",$var_text,
"
",
              xqdoc2html:parse-spec-args($exampleSource, fn:subsequence($specLines, 2)))
            else
              fn:error($err:UE008, fn:concat("The input xml for variable $", $var_name, "in example <", $exampleSource,"> is bigger than 2KB.
              Add the example input and expected output by hand in the example, in a commentary that should also include the word 'output'."))
        else
          xqdoc2html:parse-spec-args($exampleSource, fn:subsequence($specLines, 2))
    else
      xqdoc2html:parse-spec-args($exampleSource, fn:subsequence($specLines, 2))

};

declare %private %an:sequential function xqdoc2html:load-expected-results(
  $result_split as xs:string*) as xs:string
{
  if(fn:empty($result_split)) then
    ""
  else
    let $result_path := $result_split[1]
    let $result_text := file:read-text( $result_path )
    let $nonlast_result := if(fn:count($result_split) gt 1) then "
Or equivalent
" else ""
    return
    fn:concat("Expected output:

",$result_text,
"
",
$nonlast_result,
 xqdoc2html:load-expected-results(fn:subsequence($result_split, 2)))
};

declare %private %an:sequential function xqdoc2html:parse-spec-results(
  $exampleSource as xs:string,
  $specLines as xs:string*) as xs:string
{
  if(fn:empty($specLines)) then
    ""
  else
    let $specLine := $specLines[1]
    return
    if(fn:matches($specLine, "Result:")) then
      let $result_split := fn:tokenize(fn:normalize-space(fn:substring-after($specLine, "Result:")), "[ \t]")
      return
      if(fn:count($result_split) lt 1) then
        ""
      else
        xqdoc2html:load-expected-results($result_split)
    else
      xqdoc2html:parse-spec-results($exampleSource, fn:subsequence($specLines, 2))

};

(:~
 : This function returns true if there are external functions declared.
 : @param $xqdoc the xqdoc functions.
 : @return a xs:boolean if there are external functions declared.
 :)
declare %private function xqdoc2html:contains-external-functions (
  $xqdoc) as xs:boolean
{
  let $ext := count(for $sig in $xqdoc/xqdoc:functions/xqdoc:function/xqdoc:signature
                    return
                      if(ends-with($sig/text(),'external')) then 1
                      else ())
  let $func := count ($xqdoc/xqdoc:functions/xqdoc:function/xqdoc:signature)
  return
    (($func gt 0) and ($ext gt 0))
};

declare %private function xqdoc2html:get-example-filename($examplePath as xs:string) as xs:string
{
  if (fn:contains($examplePath,"/")) then
    fn:replace($examplePath,"/","_")
  else if (fn:contains($examplePath,"\")) then
    fn:replace($examplePath,"\","_")
  else
    $examplePath
};

declare  %private function xqdoc2html:get-example-filename-link($examplePath as xs:string) as xs:string
{
  fn:tokenize($examplePath,fn:concat("\",file:directory-separator()))[last()]
};

declare %private function xqdoc2html:add-images(
  $type as xs:string)
{
  let $ZorbaOptAndAnn := "../../html/options_and_annotations.html",
      $xquSpec := "http://www.w3.org/TR/xquery-update-10/#dt-updating-function",
      $xqsSpec := "../../html/scripting_spec.html",
      $xq11Spec := "http://www.w3.org/TR/xquery-11/#FunctionDeclns",
      $xqExternal := "http://www.w3.org/TR/xquery-30/#dt-external-function",
      $imagesPath := "images/"
  return

      {if(contains($type, "updating")) then

       else ()}
       {if(contains($type, "sequential")) then

       else ()}
       {if(contains($type, "nondeterministic ")) then

       else ()}
       {if(contains($type, "variadic")) then

       else ()}
       {if(contains($type, "streamable")) then
        <a  href="{$ZorbaOptAndAnn}" title="A function annotated with the http://www.zorba-xquery.com/annotations:streamable annotation is
        a function that may return an xs:string item whose content is streamed. Such a string is called a streamable string.
        Such strings have the advantage that their contents doesn't need to be materialized in memory.
        If a function consuming such a string is able to process the string in a streaming fashion,
        this allows for processing of strings with a virtually infinite length.
        However, the disadvantage is that a streamable string can only be consumed exactly once.
        If a streamable string is consumed more than once, an error is raised.
        In order to enable multiple consumers of a streamable string, the string:materialize function can be used
        to materialize the entire contents in an (regular) xs:string item." target="_blank">
       else ()}
       {if(contains($type, "external")) then

       else ()}

};

(:~
 : This function creates the XQDoc XHTML.
 :
 : @param $xqdoc the node containing the XQDoc XML.
 : @param $menu the menu.
 : @param $templatePath the path to the main.html template.
 : @param $xqdocXhtmlPath location where the resulting Xhtml will be saved on disk.
 : @param $examplePath string with the paths where the examples are kept separated by ; .
 : @return The created XHTML page.
 :)
declare %private %an:sequential function xqdoc2html:add-left-menu(
  $menu,
  $templatePath as xs:string) {
  let $doc := fn:parse-xml(file:read-text($templatePath))
  return {
    insert nodes $menu
    as last into $doc/*:html/*:body/*:div[@id='main']/*:div[@id='leftMenu'];

    $doc
  }
};

(:~
 : This function creates the XQDoc XHTML.
 :
 : @param $xqdoc the node containing the XQDoc XML.
 : @param $menu the menu.
 : @param $templatePath the path to the main.html template.
 : @param $xqdocXhtmlPath location where the resulting Xhtml will be saved on disk.
 : @param $examplePath string with the paths where the examples are kept separated by ; .
 : @return The created XHTML page.
 :)
declare %private %an:sequential function xqdoc2html:doc(
  $xqdoc,
  $menu,
  $templatePath as xs:string,
  $xqdocXhtmlPath as xs:string) {
  let $doc := fn:parse-xml(file:read-text($templatePath))
  return {
    insert node <title>Documentation for {xqdoc2html:module-uri($xqdoc)}</title>
    as first into $doc/*:html/*:head;

    insert nodes $menu
    as last into $doc/*:html/*:body/*:div[@id='main']/*:div[@id='leftMenu'];

    let $right_content := $doc/*:html/*:body/*:div[@id='main']/*:div[@id='rightcontent']
    return
      if ($right_content)
      then
        insert nodes xqdoc2html:body($xqdoc, $xqdocXhtmlPath)
        as last into $right_content;
      else
        ();

    $doc
  }
};

(:~
 : Get the XQDoc module URI.
 :
 : @param $xqdoc the node containing the XQDoc XML.
 : @return The module URI.
 :)
declare %private function xqdoc2html:module-uri($xqdoc) as xs:string
{
  $xqdoc/xqdoc:module/xqdoc:uri/text()
};

(:~
 : This function creates the XQDoc XHTML body.
 :
 : @param $xqdoc the node containing the XQDoc XML.
 : @param $xqdocXhtmlPath location where the resulting Xhtml will be saved on disk.
 : @return The 'body' of the XHTML.
 :)
declare %private %an:nondeterministic function xqdoc2html:body(
  $xqdoc,
  $xqdocXhtmlPath as xs:string)
{
  (: use only the functions not marked as %private :)
  let $functions := for $function in $xqdoc/xqdoc:functions/xqdoc:function
                    where xqdoc2html:function-is-not-private($function)
                    return $function
  let $moduleUri := xqdoc2html:module-uri($xqdoc)
  let $isZorbaCore as xs:boolean := xqdoc2html:get-is-core($moduleUri)
  let $modulePrefix as xs:string := if(count($functions) ne xs:integer(0)) then
  substring-before($xqdoc/xqdoc:functions/xqdoc:function[1]/xqdoc:name/text(),':') else
  if($xqdoc/xqdoc:variables/xqdoc:variable) then
  substring-before($xqdoc/xqdoc:variables/xqdoc:variable[1]/xqdoc:uri/text(),':') else
  "modNS"
  return
  (
{$moduleUri}{if ($isZorbaCore) then  else () }
,
    xqdoc2html:module-description($moduleUri, $modulePrefix, $xqdoc/xqdoc:module),
    xqdoc2html:module-resources($xqdocXhtmlPath, xqdoc2html:module-uri($xqdoc)),
    xqdoc2html:module-dependencies($xqdoc),
    xqdoc2html:module-external-specifications($xqdoc/xqdoc:module),
    xqdoc2html:module-namespaces(
      $xqdoc/xqdoc:module/xqdoc:custom[@tag="namespaces"]
    ),
    xqdoc2html:module-variables($xqdoc/xqdoc:variables),
    xqdoc2html:module-function-summary($functions),
    xqdoc2html:functions($functions, $xqdocXhtmlPath)
  )
};

(:~
 : Create the module description and module annotations.
 :
 : @param $module the node containing the XQDoc XML module.
 : @return the XHTML for the module description and module annotations.
 :)
declare %private function xqdoc2html:module-description($moduleUri as xs:string, $modulePrefix as xs:string, $module) {
    (
Module Description
,
      Before using any of the functions below please remember to import the module namespace:

import module namespace {$modulePrefix} = "{$moduleUri}";

      ,
      {
        if(xqdoc2html:get-is-core($moduleUri)) then () else
        (Please note that this module does not belong to the core of the Zorba XQuery engine. Please check this resource about this module import.,
,
)
      },
     xqdoc2html:description($module/xqdoc:comment),
     xqdoc2html:annotations-module($module/xqdoc:comment),
     if(($moduleUri = "http://www.w3.org/2005/xpath-functions") or
        ($moduleUri = "http://www.w3.org/2005/xpath-functions/math") or
        ($moduleUri = "http://www.functx.com/")) then ()
     else
      (
XQuery version and encoding for this module:
,

xquery version "{$module/xqdoc:comment/xqdoc:custom[@tag='XQuery version']/text()}" encoding "{$module/xqdoc:comment/xqdoc:custom[@tag='encoding']/text()}";
)
     ),
     let $modVersion := xqdoc2html:get-module-version($moduleUri)
     return
     if($modVersion = "") then ()
     else
     (
Zorba version for this module:
,

The latest version of this module is {$modVersion}. For more information about module versioning in Zorba please check out this resource.

     )
};

(:~
 : Create the XHTML for all description annotations.
 :
 : @param $comment the part of the XQDoc file holding the annotations.
 : @return the XHTML for the description annotations.
 :)
declare %private function xqdoc2html:description($comment) {

{if ($comment/xqdoc:description) then
         $comment/xqdoc:description/node()
       else
         "No description available."}

};

(:~
 : Create the XHTML for all module annotations except these ones:
 :

 :
description

 :
param

 :
return

 :
error

 :
deprecated

 :
see

 :
library

 :

 :
 : @param $comment the part of the XQDoc file holding the module annotations.
 : @return the XHTML for the module annotations.
 :)
declare %private function xqdoc2html:annotations-module($comment) {
  let $annotations := $comment/xqdoc:*[not((local-name(.) = ("description", "param", "return", "error", "deprecated", "see", "project", "custom")))]
  return
    for $annotation in $annotations
    let $annName := local-name($annotation)
    return
    (

{
        concat(upper-case(substring($annName, 1, 1)), substring($annName, 2), ":")
      }
,

{$annotation/node()}

    )
};

(:~
 : Create the XHTML for the module resources.
 :
 : @param $moduleUri the module URI.
 : @param $indexCollector the modules names part of the left menu.
 : @return the XHTML for the 'Module Resources'.
 :)
declare %private %an:nondeterministic function xqdoc2html:module-resources(
  $xqdocXhtmlPath as xs:string,
  $moduleUri as xs:string)
{
  let $folder := xqdoc2html:get-filename($moduleUri)
  return
    (
Module Resources
,

the XQuery module can be found here.

)
};

(:~
 : Create the XHTML for the module dependencies (imported modules, schemas).
 :
 : @param $xqdoc the node containing the XQDoc XML.
 : @return the XHTML for the 'Module Dependencies'.
 :)
declare %private function xqdoc2html:module-dependencies(
    $xqdoc) {
  if (fn:count($xqdoc/xqdoc:imports/xqdoc:import) > 0 or
      fn:count($xqdoc/xqdoc:module/xqdoc:comment/xqdoc:custom[@tag="library"]) > 0) then
    (
Module Dependencies
,
    xqdoc2html:imports($xqdoc))
  else
    ()
};

(:~
 : Create the items for the module dependencies (imported modules, schemas).
 :
 : @param $xqdoc the node containing the XQDoc XML.
 : @return the XHTML for the 'Module Dependencies'.
 :)
declare %private function xqdoc2html:imports(
    $xqdoc) {
  (
  if (fn:count($xqdoc/xqdoc:imports//xqdoc:import[@type = "library"]) > 0) then

Imported modules:

    {
      for $import in $xqdoc/xqdoc:imports//xqdoc:import[@type = "library"]
      return

        {string($import/xqdoc:uri/text())}

    }

  else (),
  if (fn:count($xqdoc/xqdoc:module/xqdoc:comment/xqdoc:custom[@tag="library"]) > 0) then

External C++ library dependencies:

      {
      for $library in $xqdoc/xqdoc:module/xqdoc:comment/xqdoc:custom[@tag="library"]
        return

{$library/node()}

      }

  else (),
  if (fn:count($xqdoc/xqdoc:imports//xqdoc:import[@type = "schema"]) > 0) then
    (
Imported schemas:

    {
      for $import in $xqdoc/xqdoc:imports//xqdoc:import[@type = "schema"]
      return
        if (exists($xqdoc2html:schemasCollector/schema[@uri=$import/xqdoc:uri/text()])) then
        {
          let $file := $xqdoc2html:schemasCollector/schema[@uri=$import/xqdoc:uri/text()]/@file,
              $filePath := fn:concat("schemas",file:directory-separator(),$file)
          return

{string($import/xqdoc:uri/text())}

        }
        else

{string($import/xqdoc:uri/text())}

    }

,

Please note that the schemas are not automatically imported in the modules that import this module.

    In order to import and use the schemas, please add:

    {
    let $namespaces := $xqdoc/xqdoc:module//xqdoc:custom[@tag = "namespaces"]
    return
    string-join(
    for $import in $xqdoc/xqdoc:imports//xqdoc:import[@type = "schema"]
      return
       concat('import schema namespace ',
              $namespaces//xqdoc:namespace[(@uri = $import/xqdoc:uri/text()) and (@isSchema = "true")]/@prefix,
              ' =  "',
              string($import/xqdoc:uri/text()),'";'),"
")
    }

)
  else ()
  )
};

(:~
 : Create the items for the Related Documentation
 : ('see' annotations appearing in the module description part).
 :
 : @param $module the node containing the XQDoc XML module.
 : @return the XHTML for the 'Related Documentation'.
 :)
declare %private function xqdoc2html:module-external-specifications($module) {
  if(fn:count($module/xqdoc:comment/xqdoc:*[(local-name(.) = ("see"))]) >0) then
    (
Related Documentation
,

For more details please also see:

    {
      let $annotations := $module/xqdoc:comment/xqdoc:*[(local-name(.) = ("see"))]
      return
        for $annotation in $annotations
        return
          if(fn:count($annotation/node()) eq 1) then

{xqdoc2html:replace-with-a-html-tag(xs:string($annotation/node()))}

          else

{$annotation/node()}

    }

)
  else
  ()
};

(:~
 : Create the module variables XHTML.
 :
 : @param $variables the node containing the XQDoc XML variables.
 : @return the XHTML for the module variables.
 :)
declare %private function xqdoc2html:module-variables($variables)
{
  let $noPublicVariables :=
    count(for $variable in $variables/xqdoc:variable
          where (count($variable/xqdoc:annotations/xqdoc:annotation[@localname="private"]) = 0)
          return $variable)
  return
  if($noPublicVariables eq xs:integer(0)) then ()
  else
   (
Variables
,

   {
      for $variable in $variables/xqdoc:variable
      let $name := $variable/xqdoc:uri/text()
      let $type := $variable/xqdoc:comment/xqdoc:custom[@tag="type"]/text()
      let $isExternal := $variable/xqdoc:comment/xqdoc:custom[@tag="isExternal"]/text()
      let $an := string-join((for $annotation in $variable/xqdoc:annotations/xqdoc:annotation
                               return data($annotation/@localname),
                               if($isExternal) then "external" else ""," ")," ")

      where (count($variable/xqdoc:annotations/xqdoc:annotation[@localname="private"]) = 0)
      order by $name
      return (

              {xqdoc2html:add-images($an)}
              ${$name}
                  {if(exists($type)) then concat(" as ",$type) else ""}
                  {if(exists($isExternal)) then " external" else ()}

                  {normalize-space(xqdoc2html:description($variable/xqdoc:comment))}

             )
    }

)
};

(:~
 : Create the module namespaces XHTML.
 :
 : @param $namespaces the custom node containing the XQDoc XML namespaces.
 : @return the XHTML for the module namespaces.
 :)
declare %private function xqdoc2html:module-namespaces($namespaces)
{
  if($namespaces/xqdoc:namespace) then
    (
Namespaces
,

    {
      for $namespace in $namespaces/xqdoc:namespace
      let $prefix := data($namespace/@prefix)
      let $uri := data($namespace/@uri)
      (:where empty($variable/xqdoc:invoked) :)
      order by $prefix
      return (

              {$prefix}
              {$uri}

             )
    }
)
  else
    ()
};

(:~
 : Create the functions summary XHTML table.
 :
 : @param $functions the node containing the XQDoc XML functions.
 : @return the XHTML for the module function summary.
 :)
declare %private function xqdoc2html:module-function-summary($functions)
{

Function Summary
,
  if(count($functions)) then

{
      for $function in $functions
      let $name := fn:substring-after($function/xqdoc:name/text(),':'),
          $signature := $function/xqdoc:signature/text(),
          $param-number := $function/@arity,
          $isDeprecated := fn:exists($function/xqdoc:comment/xqdoc:deprecated),
          $description := data($function/xqdoc:comment/xqdoc:description),
          $shortDescription := if(not(fn:substring-before($description,".") = "")) then
                               fn:concat(fn:substring-before($description,"."),".") else ""
      order by $name, $param-number
      return
        let $type := replace(normalize-space(substring-after(substring-before($signature, "function"), "declare")),"%",""),
            $isExternal := ends-with($signature, "external"),
            $paramsAndReturn := substring-after($signature,concat(':',$name)),
            $external := if(ends-with($signature,"external")) then "external" else ""
        return

            {xqdoc2html:add-images(concat($type," ",$external))}

              <tt>{
                if ($isDeprecated) then
                  {$name}
                else
                  {$name}
              }{xqdoc2html:split-function-signature($paramsAndReturn)}
{$shortDescription}
              </tt>

    }

  else

No public functions declared.

};

(:~
 : Pretty print the function signature.
 :
 : @param $signature the function signature.
 : @return the XHTML for the function signature after reformatting was done.
 :)
declare %private function xqdoc2html:split-function-signature($signature as xs:string) {
  let $line1 := substring-before($signature, "(")
  let $rest := substring-after($signature, "(")
  let $params :=
    (: if the function has parameter :)
    if (matches($rest, "\$")) then
      let $tmp := substring-before($rest, ") as ")
      return
        (: if we don't have a return type specified :)
        if ($tmp eq "") then
          (: en external function declaration :)
          if (ends-with($rest, ") external")) then
            substring-before($rest, ") external")
          (: no external function :)
          else
            substring-before($rest, ")")
        (: the return type is specified :)
        else
          $tmp
    (: no parameters :)
    else
      ""
  let $after := substring-after($signature, concat($params, ")"))
  let $parts := fn:tokenize($line1,' ')
  let $line11 := fn:string-join(for $part in $parts return $part, ' ')
  let $line12 := fn:substring-after($line1, normalize-space($line11))
  return (
    $line11,{$line12}," (
",
    for $param at $pos in tokenize($params, "\$")
    let $nsParam := normalize-space($param)
    let $param1 := if(contains($nsParam,"as")) then fn:substring-before($nsParam," as ")
                   else if(contains($nsParam,",")) then fn:substring-before($nsParam,",")
                   else if(contains($nsParam,")")) then fn:substring-before($nsParam,")")
                   else $nsParam
    let $param2 := fn:substring-after($nsParam, $param1)
    where $pos > 1
    return (
      {concat("            $", $param1)},$param2,"
"),
    concat(")", $after)
  )
};

(:~
 : Create the details for all the functions.
 :
 : @param $functions the node containing the XQDoc XML functions.
 : @param $xqdocXhtmlPath location where the resulting Xhtml will be saved on disk.
 : @return the XHTML for the function details.
 :)
declare %private %an:nondeterministic function xqdoc2html:functions($functions, $xqdocXhtmlPath) {
    if(count($functions)) then (

Functions
,
      for $function in $functions
      let $name := fn:substring-after($function/xqdoc:name/text(),':'),
          $signature := $function/xqdoc:signature/text(),
          $param-number := $function/@arity,
          $comment := $function/xqdoc:comment,
          $isDeprecated := fn:exists($comment/xqdoc:deprecated)
      order by $name, $param-number
      return (

{
          if ($isDeprecated) then
            {xqdoc2html:module-function-link($name, $signature)}
          else
            xqdoc2html:module-function-link($name, $signature)
        }
,
        if ($isDeprecated) then

Deprecated{
            if (exists($comment/xqdoc:deprecated/node())) then
              (" - ", $comment/xqdoc:deprecated/node())
            else
              ()
          }

        else
          (),

{xqdoc2html:split-function-signature($signature)}
,
        xqdoc2html:description($comment),
        xqdoc2html:function-parameters($comment),
        xqdoc2html:function-return($comment),
        xqdoc2html:errors($comment),
        xqdoc2html:annotations($comment),
        xqdoc2html:annotations-author($comment),
        xqdoc2html:annotations-see($comment),
        xqdoc2html:annotations-example($comment, $xqdocXhtmlPath),

)
       )
    else ()
};

(:~
 : Append in the beginning of the function name
 : updating, sequential, nondeterministic is applicable.
 :
 : @param $name the function name.
 : @param $signature the signature of the function.
 : @return a new string for the function name.
 :)
declare %private function xqdoc2html:module-function-link($name as xs:string, $signature) {

let $lcSignature := fn:lower-case($signature)
return
(
  xqdoc2html:add-images(string-join((
  if(contains($lcSignature, 'updating')) then
    'updating' else (),

  if(contains($lcSignature, 'sequential')) then
    'sequential' else (),

  if(contains($lcSignature, 'nondeterministic')) then
    'nondeterministic' else (),

  if(contains($lcSignature, 'non-deterministic')) then
    'nondeterministic' else (),

  if(contains($lcSignature, 'variadic')) then
    'variadic' else (),

  if(contains($lcSignature, 'streamable')) then
    'streamable' else (),
  if(ends-with($signature, 'external')) then
    'external' else ())," ")),

  $name
)
};

(:~
 : Return the function parameters.
 :
 : @param $comment the part of the XQDoc file holding the function parameters.
 : @return the XHTML for the function parameters.
 :)
declare %private function xqdoc2html:function-parameters($comment) {
  let $params := $comment/xqdoc:param
  return
    if (exists($params)) then
      (
Parameters:
,

      {for $param in $params return
{$param/node()}
}

)
    else ()
};

(:~
 : Create the XHTML for all function return values.
 :
 : @param $comment the part of the XQDoc file holding the function annotations.
 : @return the XHTML for the function return values.
 :)
declare %private function xqdoc2html:function-return($comment) {
  let $return := $comment/xqdoc:return
  return
    if (exists($return)) then (

Returns:
,

{$return/node()}

    ) else ()
};

(:~
 : Return the possible function errors.
 :
 : @param $comment the part of the XQDoc file holding the function errors.
 : @return the XHTML for the function errors.
 :)
declare %private function xqdoc2html:errors($comment) {
  let $errors := $comment/xqdoc:error
  return
    if (exists($errors)) then
      (
Errors:
,

      {for $error in $errors return
{data($error)}
}

)
    else ()
};

declare %private function xqdoc2html:annotations-author($comment) {
  let $authors := $comment/xqdoc:*[(local-name(.) = ("author"))]
  return
  if (exists($authors)) then (

Author:
,

{string-join(for $author in $authors return $author/node(),', ')}

    ) else ()
};

(:~
 : Create the XHTML for all function annotations except these ones:
 :

 :
description

 :
param

 :
return

 :
error

 :
deprecated

 :
see

 :
example

 :
.
 :
 : @param $comment the part of the XQDoc file holding the function annotations.
 : @return the XHTML for the function annotations.
 :)
declare %private function xqdoc2html:annotations($comment) {
  let $annotations := $comment/xqdoc:*[not((local-name(.) = ("description", "param", "return", "error", "deprecated", "see", "custom", "author")))]
  return
    for $annotation in $annotations
    let $annName := local-name($annotation)
    return
    (

{
        concat(upper-case(substring($annName, 1, 1)), substring($annName, 2), ":")
      }
,

{$annotation/node()}

    )
};

declare %private function xqdoc2html:replace-with-a-html-tag(
  $text as xs:string)
{
 (: this is meant to be used only for the @see tags at this moment :)
 let $containsHttp := starts-with(lower-case($text), "http://"),
     $isModuleURI as xs:boolean := exists($xqdoc2html:ZorbaManifest/module[@uri= $text])
 return
  if($containsHttp) then
  (
    if($isModuleURI) then
      {$text}
    else
      {$text}
  )
  else $text
};

(:~
 : This function groups together all the @see annotations.
 :
 : @param $comment the part of the XQDoc file holding the function annotations.
 : @return the XHTML for the function 'see' annotations.
 :)
declare %private function xqdoc2html:annotations-see($comment) {
  let $see := $comment/xqdoc:*[local-name(.) = ("see")]
  return
    if (count($see) = 0) then ()
    else
(: **********************************************************     :)
(: this hack should be replaced with links everywhere in text     :)
(: replace the @see nodes that start with http:// with HTML a tag :)
    (
See:
,

    {
    for $annotation in $see
    return
      if(fn:count($annotation/node()) eq 1) then

{xqdoc2html:replace-with-a-html-tag(xs:string($annotation/node()))}

      else

{$annotation/node()}

    }

    )
(: **********************************************************     :)
};

(:~
 : This function groups together all the @example annotations.
 :
 : @param $comment the part of the XQDoc file holding the function annotations.
 : @param $xqdocXhtmlPath location where the resulting Xhtml will be saved on disk.
 : @return the XHTML for the function 'example' annotations.
 :)
declare %private %an:nondeterministic function xqdoc2html:annotations-example($comment, $xqdocXhtmlPath) {
  let $example := $comment//xqdoc:custom[@tag="example"]
  return
    if (count($example) = 0) then ()
    else
    (
Examples:
,

    {for $annotation in $example
    let $exampleFileName := fn:concat("examples", file:directory-separator(),replace(xqdoc2html:get-example-filename($annotation/node()),".xq",".html"))
    return
      if(file:exists(fn:concat($xqdocXhtmlPath, file:directory-separator(), $exampleFileName))) then

{xqdoc2html:get-example-filename-link($annotation/node())}

      else

{fn:concat("The example with filename '",$annotation/node(), "' was not found.")}
}

    )
};

declare %private function xqdoc2html:function-is-not-private($function)
{
  not(exists($function//xqdoc:annotation[@localname = 'private']))
};

(:~
 : This function generates the XQDoc function index.
 : @return Xhtml section with the function index content.
 :)
declare %private function xqdoc2html:generate-function-index()
{

{
  let $alphabet := ("A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z")
  let $letters := distinct-values(
                  for $docNode in dml:collection(xs:QName("xqdoc2html:collection"))
                  for $function in $docNode/xqdoc:xqdoc/xqdoc:functions//xqdoc:function
                    let $functionName := substring-after($function/xqdoc:name/text(),':')
                    where xqdoc2html:function-is-not-private($function)
                  return upper-case(substring($functionName,1,1)))
  return
    (for $letter in $alphabet
    order by $letter
    return
      if(exists(index-of($letters,$letter))) then
        {$letter}
      else
        {upper-case($letter)},

    for $letter in $letters
      order by $letter
      return
      (

{$letter}

           Up

,

,

          {
            for $docNode in dml:collection(xs:QName("xqdoc2html:collection"))
            for $function in $docNode/xqdoc:xqdoc/xqdoc:functions//xqdoc:function
            let $module := $docNode/xqdoc:xqdoc/xqdoc:module,
                $moduleUri := $module/xqdoc:uri/text(),
                $file := fn:concat(pxqdoc:get-filename($moduleUri),'.html'),
                $functionName := fn:substring-after($function/xqdoc:name/text(),':'),
                $signature := $function/xqdoc:signature/text(),
                $arity :=  $function/@arity,
                $isDeprecated := fn:exists($function/xqdoc:comment/xqdoc:deprecated),
                $isExternal := ends-with($signature, "external"),
                $paramsAndReturn :=
                  let $searchCrit := concat(":", $functionName)
                  return
                    if ($isExternal)then
                      normalize-space(substring-before(substring-after($signature, $searchCrit), "external"))
                    else normalize-space(substring-after($signature, $searchCrit))
            where (starts-with($functionName, lower-case($letter)) and
                   xqdoc2html:function-is-not-private($function))
            order by $functionName, $arity
            return

                  <tt>{
                    if ($isDeprecated eq fn:true()) then
                      {$functionName}
                    else
                      {$functionName}
                  }{$paramsAndReturn}
Module Namespace: {$moduleUri}</tt>

          }

     )
     )
}

};

(:~
 : This function generates the XQDoc function index page.
 : @param $indexFunctionLeft the menu containing the links to the generated Xhtml files.
 : @param $templatePath the path to the main.html template.
 : @return The content of the function index page.
 :)
declare %private %an:sequential function xqdoc2html:generate-function-index-xhtml(
  $indexFunctionLeft,
  $templatePath as xs:string,
  $functionIndexPath as xs:string
) {
  let $indexHtmlDoc := fn:parse-xml(file:read-text($templatePath))
  return {
      insert node <title>Function index</title>
      as first into $indexHtmlDoc/*:html/*:head;

      insert nodes $indexFunctionLeft
      as last into $indexHtmlDoc/*:html/*:body/*:div[@id='main']/*:div[@id='leftMenu'];

      let $right_content := $indexHtmlDoc/*:html/*:body/*:div[@id='main']/*:div[@id='rightcontent']
      return
        if ($right_content)
        then
          insert nodes
            (
Indexed Library Module Functions
,
            xqdoc2html:generate-function-index())
          as last into $right_content;
        else
          ();

    (:$indexHtmlDoc:)
    file:write($functionIndexPath,
               $indexHtmlDoc,
               $xqdoc2html:serParamXhtml);
  }
};

declare %private %an:sequential function xqdoc2html:collect-entry (
  $href as xs:string,
  $name as xs:string,
  $structure as xs:string,
  $pureXQuery as xs:string,
  $moduleURI as xs:string)
{
  insert node <entry  href="{$href}"
                      name="{$name}"
                      structure="{$structure}"
                      pureXQuery="{$pureXQuery}"
                      moduleURI="{$moduleURI}" /> as last into $xqdoc2html:menuEntries;
};

declare %private %an:sequential function xqdoc2html:collect-menu-entries()
{
  for $docNode in dml:collection(xs:QName("xqdoc2html:collection"))
      let $module := $docNode/xqdoc:xqdoc/xqdoc:module,
          $lModuleProject := $module/xqdoc:comment/xqdoc:custom[@tag="project"]/text(),
          $lModuleUri     := $module/xqdoc:uri/text(),
          $lXHTMLFileName := pxqdoc:get-filename($lModuleUri),
          $lPureXquery    := not(xqdoc2html:contains-external-functions($docNode/xqdoc:xqdoc)),
          $lTmp := substring-after($lModuleUri,'http://'),
          $lTmpTok := tokenize($lTmp,'/'),
          $lTmp2 := if(ends-with($lTmp,'/')) then substring($lTmp,1,string-length($lTmp)-1) else string-join(xqdoc2html: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
  order by $lModuleProject, $lModuleUri
    return
      xqdoc2html:collect-entry( $lXHTMLFileName,
                                $lModuleName,
                                $structure,
                                xs:string($lPureXquery),
                                $lModuleUri)
};

(:~
 : This function add the subcategories in the menu.
 :
 : @param $table the parent module category.
 : @param $category the category.
 : @param $moduleUri module URI.
 : @return $table after the subcategory together with the modules were added to it.
 :)
declare %private %an:sequential function xqdoc2html:create-module-helper(
  $table,
  $category as xs:string,
  $currentCategory as xs:string
  )
{
  {
    (:insert nodes
{$category}
:)
    insert nodes
{$category}

    {
      for $entry in $xqdoc2html:menuEntries/entry
      let $isZorbaCore as xs:boolean := xqdoc2html:get-is-core(data($entry/@moduleURI))
      order by $entry/@structure
      where ($entry/@structure eq $currentCategory)
      return

          {data($entry/@name)}
          {
            if(xs:boolean(data($entry/@pureXQuery))) then ()
            else
              (E)
          }
          {
            if($isZorbaCore) then

            else ()
          }

    }

    as last into $table
  };

  $table
};

declare %private function xqdoc2html:get-distinct-children (
  $level1 as xs:string*,
  $currentCat as xs:string
) as xs:string*
{
  let $lTmp :=  for $cat in $level1
                where starts-with($cat,concat($currentCat,'/'))
                return substring-after($cat, concat($currentCat,'/'))
  return distinct-values(for $str in $lTmp return tokenize($str,'/')[1])
};

(:~
 : Recursive helper
 :)
declare %private %an:sequential function xqdoc2html:create-module-table-rec(
  $level1 as xs:string*,
  $leveln as xs:string*,
  $curentCat as xs:string,
  $table,
  $mustOrder as xs:boolean) {
  if(empty($leveln)) then ()
  else
    if($mustOrder) then
      for $cat in $leveln
      order by lower-case($cat)
      return {
        variable $lCurentCat := if($curentCat eq "") then $cat else concat($curentCat,'/',$cat);
        xqdoc2html:create-module-helper($table, $cat, $lCurentCat);
        xqdoc2html:create-module-table-rec($level1, xqdoc2html:get-distinct-children($level1,$lCurentCat), $lCurentCat,$table/li[fn:last()]/ul, fn:true())
      }
    else
      for $cat in $leveln
      return {
        variable $lCurentCat := if($curentCat eq "") then $cat else concat($curentCat,'/',$cat);
        xqdoc2html:create-module-helper($table, $cat, $lCurentCat);
        xqdoc2html:create-module-table-rec($level1, xqdoc2html:get-distinct-children($level1,$lCurentCat), $lCurentCat,$table/li[fn:last()]/ul, fn:true())
      }
};

declare %private function xqdoc2html:order-level1-entries
($entries as xs:string*) as xs:string*
{
 (: show first the items in the $xqdoc2html:level1Weight that were also found in the modules :)
 let $defined := for $str in $xqdoc2html:level1Weight return xqdoc2html:value-intersect($str, $entries)

 (: if there are any other level 1 items found in the modules, show them also ordered alphabetically:)
 let $undefined := xqdoc2html:value-except($entries, $defined)

 return ($defined, for $str in $undefined order by $str return $str)
};

(:~
 : This function creates the categories in the menu.
 :
 : @param $leftMenu the menu.
 : @param $root the root.
 : @param $moduleUri module URI.
 : @return $root after the subcategories were added to it.
 :)
declare %private %an:sequential function xqdoc2html:create-module-table(
  $level1 as xs:string*,
  $root)
{
  {
    variable $leveln := distinct-values(for $str in $level1 return tokenize($str,'/')[1]);

    xqdoc2html:create-module-table-rec($level1, xqdoc2html:order-level1-entries($leveln), "", $root, fn:false());

    $root
  }
};

(:~
 : This function creates menu header provided $pageName page.
 :
 : @param $leftMenu the menu.
 : @param $pageName the name of the function index XHTML page.
 : @return $root after the subcategories were added to it.
 :)
declare %private function xqdoc2html:create-left-menu()
{

All Modules

All Functions

};

(:
declare %private %an:sequential function xqdoc2html:create-specialized-left-menu(
  $generalLeftMenu,
  $moduleUri as xs:string)
{
  {
    for $docNode in dml:collection(xs:QName("xqdoc2html:collection"))
      let $module := $docNode/xqdoc:xqdoc/xqdoc:module,
          $lModuleUri     := $module/xqdoc:uri/text(),
          $lPureXquery    := not(xqdoc2html:contains-external-functions($docNode/xqdoc:xqdoc))
    where $lModuleUri = $moduleUri

    for $node in $generalLeftMenu//li
    let $isZorbaCore as xs:boolean := xs:boolean(dml:collection(xs:QName("xqdoc2html:collectionConfig"))/module[@moduleURI=$moduleUri]/@isCore)
    where $node/a[@title eq $moduleUri]
    return
    replace node $node with

      {$node/a/text()}
      {
        if($lPureXquery) then ()
        else
          (E)
      }
      {
        if($isZorbaCore) then

        else ()
      }
;
  }

  $generalLeftMenu
};
:)

(:~
 : This function reads, updates and returns the new index.html.
 :
 : @param $templatePath the path to the 'main.html' template.
 : @param $menu the menu.
 : @param $modules the modules.
 : @param $zorbaVersion Zorba version.
 : @return The content of the new index.html.
 :)
declare %private %an:sequential function xqdoc2html:generate-index-html(
  $templatePath as xs:string,
  $menu,
  $modules,
  $zorbaVersion as xs:string) as document-node()
{
    let $indexHtmlDoc := fn:parse-xml(file:read-text($templatePath))
    return {
        insert node <title>XQuery Modules Documentation</title>
        as first into $indexHtmlDoc/*:html/*:head;

        insert nodes $menu
        as last into $indexHtmlDoc/*:html/*:body/*:div[@id='main']/*:div[@id='leftMenu'];

        let $right_content := $indexHtmlDoc/*:html/*:body/*:div[@id='main']/*:div[@id='rightcontent']
        return
          if ($right_content)
          then
            insert nodes
              (
Zorba XQuery Processor {$zorbaVersion}
,

This document contains a list of all the core and non core Zorba modules.
,

Please check out Modules in Zorba documentation page.
,

The core Zorba modules are annotated below with this small image: .
,

The superscript (E) denotes modules that have external functions. Those are not portable to other XQuery processors.
,

Please send us comments in case you would like to suggest us adding a particular XQuery module, or if you would like to donate an existing third party XQuery module.
,
              $modules)
            as last into $right_content;
          else
            ();

        $indexHtmlDoc
    }
};