Changed xs:string* to xs:string.

Changed xs:string* to xs:string.

xquery version "3.0";

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

(:~
 : This module implements the file API EXPath specification available at http://expath.org/spec/file
 : @author Gabriel Petrovay, Matthias Brantner, Markus Pilman
 : @see http://expath.org/spec/file
 : @project EXPath/EXPath File Module
 :)
module namespace file = "http://expath.org/ns/file";

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

(:~
 : Appends a sequence of Base64 items as binary to a file. If the file pointed
 : by
$file
 does not exist, a new file will be created.
 :
 : @param $file The path/URI of the file to write the content to.
 : @param $content The content to be written to the file.
 : @return The empty sequence.
 : @error file:FOFL0004 If
$file
 points to a directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:sequential
function file:append-binary( $file as xs:string, $content as xs:base64Binary )
  as empty-sequence() external;

(:~
 : Appends a sequence of string items to a file.
 :
 : @param $file The path/URI of the file to write the content to.
 : @param $content The content to be written to the file.
 : @param $encoding The character encoding to append $content as.
 : @return The empty sequence.
 : @error file:FOFL0004 If
$file
 points to a directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:sequential
function file:append-text( $file as xs:string, $content as xs:string,
                           $encoding as xs:string )
  as empty-sequence() external;

(:~
 : Appends a sequence of string items to a file.
 :
 : @param $file The path/URI of the file to write the content to.
 : @param $content The content to be written to the file.
 : @return The empty sequence.
 : @error file:FOFL0004 If
$file
 points to a directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:sequential
function file:append-text( $file as xs:string, $content as xs:string )
  as empty-sequence()
{
  file:append-text( $file, $content, "UTF-8" )
};

(:~
 : Appends a sequence of string items to a file, each followed by a
 : platform-dependent newline character(s).
 :
 : @param $file The path/URI of the file to write the content to.
 : @param $content The content to be written to the file.
 : @param $encoding The character encoding to append $content as.
 : @return The empty sequence.
 : @error file:FOFL0004 If
$file
 points to a directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:sequential
function file:append-text-lines( $file as xs:string, $content as xs:string*,
                                 $encoding as xs:string )
  as empty-sequence() external;

(:~
 : Appends a sequence of string to a file, each followed by a
 : platform-dependent newline character(s), using the UTF-8 character encoding.
 :
 : @param $file The path/URI of the file to write the content to.
 : @param $content The content to be written to the file.
 : @return The empty sequence.
 : @error file:FOFL0004 If
$file
 points to a directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:sequential
function file:append-text-lines( $file as xs:string, $content as xs:string* )
  as empty-sequence()
{
  file:append-text-lines( $file, $content, "UTF-8" )
};

(:~
 : Returns the last component from the
$path
, deleting any
 : trailing directory-separator characters. If
$path
 consists
 : entirely directory-separator characters, the empty string is returned. If
 :
$path
 is the empty string, the string
"."
 is returned,
 : signifying the current directory.
 :
 : No path existence check is made.
 :
 : @param $path A file path/URI.
 : @return The base name of this file.
 :)
declare function file:base-name( $path as xs:string )
  as xs:string external;

(:~
 : Returns the last component from the
$path
, deleting any
 : trailing directory-separator characters and the
$suffix
. If path
 : consists entirely directory-separator characters, the empty string is
 : returned. If path is the empty string, the string
"."
 is
 : returned, signifying the current directory.
 :
 : No path existence check is made.
 :
 : The
$suffix
 can be used for example to eliminate file extensions.
 :
 : @param $path A file path/URI.
 : @param $suffix A suffix which should get deleted from the result.
 : @return The base-name of $path with a deleted $suffix.
 :)
declare function file:base-name( $path as xs:string, $suffix as xs:string )
  as xs:string
{
  let $res := file:base-name($path)
  return
    if (fn:ends-with($res, $suffix) and $res ne ".") then
      fn:substring($res, 1, fn:string-length($res) - fn:string-length($suffix))
    else
      $res
};

(:~
 : Copies a file or a directory given a source and a destination path/URI.
 :
 : @param $source The path/URI of the file or directory to copy.
 : @param $destination The destination path/URI.
 : @return The empty sequence.
 : @error file:FOFL0001 If the
$source
 path does not exist.
 : @error file:FOFL0002 If the computed destination points to a file system item
 :    with a different type than the
$source
.
 : @error file:FOFL0003 If
$destination
 does not exist and it's
 :    parent directory does not exist either.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:nondeterministic %an:sequential
function file:copy( $source as xs:string, $destination as xs:string )
  as empty-sequence()
{
  if (file:exists($source)) then
    if (file:is-directory($source)) then
      file:copy-directory-impl($source, $destination)
    else
      file:copy-file-impl($source, $destination)
  else
    fn:error(xs:QName("file:FOFL0001"), fn:concat("The source path does not exists: ", $source))
};

(:~
 : Creates a directory.
 :
 : The operation is will create all the missing parent directories from the
 : given path.
 :
 : @param $dir The path/URI denoting the directory to be created.
 : @return The empty sequence.
 : @error file:FOFL0002 If the directory cannot be created because of an already
 :    existing file.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:sequential function
file:create-directory( $dir as xs:string )
  as empty-sequence() external;

(:~
 : Deletes a file or a directory from the file system.
 :
 : If
$path
 points to a directory the directory will be deleted
 : recursively.
 :
 : @param $path The path/URI of the file or directory to delete.
 : @return The empty sequence.
 : @error file:FOFL0001 If the
$path
 path does not exist.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:nondeterministic %an:sequential
function file:delete( $path as xs:string )
  as empty-sequence()
{
  if (file:exists($path,false())) then
    if (not(file:is-symlink($path)) and file:is-directory($path)) then
      file:delete-directory-impl($path)
    else
      file:delete-file-impl($path)
  else
    fn:error(xs:QName("file:FOFL0001"), fn:concat("The path does not exists: ", $path))
};

(:~
 : This function returns the value of the operating system specific directory
 : separator. For example,
/
 on Unix-based systems and
\

 : on Windows systems.
 :
 : @return The operating system specific directory separator.
 :)
declare function file:directory-separator()
  as xs:string external;

(:~
 : This function is the converse of
file:base-name
. It returns a
 : string denoting the parent directory of the
$path
. Any trailing
 : directory-separator characters are not counted as part of the directory
 : name. If path is the empty string or contains no directory-separator string,
 :
"."
 is returned, signifying the current directory.
 :
 : No path existence check is made.
 :
 : @param $path The filename, of which the dirname should be get.
 : @return The name of the directory the file is in.
 :)
declare function file:dir-name( $path as xs:string )
  as xs:string external;

(:~
 : Tests if a path/URI is already used in the file system.
 :
 : @param $path The path/URI to test.
 : @param $follow-symlinks if true, follows symbolic links.
 : @return true if $path points to an existing file system item.
 :)
declare %an:nondeterministic
function file:exists( $path as xs:string, $follow-symlinks as xs:boolean )
  as xs:boolean external;

(:~
 : Tests if a path/URI is already used in the file system.
 :
 : @param $path The path/URI to test.
 : @return true if $path points to an existing file system item;
 : for symbolic links, retuns true if the linked-to item exists.
 :)
declare %an:nondeterministic
function file:exists( $path as xs:string )
  as xs:boolean
{
  file:exists( $path, true() )
};

(:~
 : A helper function that performs a trivial (not complete) glob to regex
 : pattern translation.
 :
 : @param $pattern The glob pattern.
 : @return A regex pattern corresponding to the glob pattern provided.
 :)
declare function file:glob-to-regex( $pattern as xs:string )
  as xs:string
{
  let $pattern := fn:replace($pattern, '(\.|\[|\]|\\|/|\||\-|\^|\$|\?|\*|\+|\{|\}|\(|\))','\\$1')
  let $pattern := fn:replace($pattern, '\\\?', '.')
  let $pattern := fn:replace($pattern, '\\\*', '.*')
  return fn:concat( "^", $pattern, "$" )
};

(:~
 : Tests if a path/URI points to a directory. On Unix-based systems, the root
 : and the volume roots are considered directories.
 :
 : @param $path The path/URI to test.
 : @return true if $path points to a directory; for symbolic
 : links, returns true if the linked-to item is a directory.
 :)
declare %an:nondeterministic
function file:is-directory( $path as xs:string )
  as xs:boolean external;

(:~
 : Tests if a path/URI points to a file.
 :
 : @param $path The path/URI to test.
 : @return true if $path points to a file; for symbolic links,
 : returns true if the linked-to item is a file.
 :)
declare %an:nondeterministic
function file:is-file( $path as xs:string )
  as xs:boolean external;

(:~
 : Tests if a path/URI points to symbolic link.  This works on all Unix-based
 : systems as well as Windows Vista or later.
 :
 : @param $path The path/URI to test.
 : @return true if $path points to a symbolic link.
 :)
declare %an:nondeterministic
function file:is-symlink( $path as xs:string )
  as xs:boolean external;

(:~
 : Retrieves the timestamp of the last modification of the file system item
 : pointed by the path/URI.
 :
 : @param $path The file system item to read the last modification
 :    timestamp from.
 : @return The date and time of the last modification of the item.
 : @error file:FOFL0001 If the
$path
 does not exist.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:nondeterministic function file:last-modified( $path as xs:string )
  as xs:dateTime external;

(:~
 : Lists the file system items in a certain directory.
 :
 : The operation is equivalent to calling:
 :
file:list($dir, fn:false())
.
 :
 : @param $dir The path/URI of the directory to retrieve the children from.
 : @return The sequence of names of the direct children.
 : @error file:FOFL0003 If
$dir
 does not point to an existing directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:nondeterministic
function file:list( $dir as xs:string )
  as xs:string* external;

(:~
 : Lists the file system items in a certain directory. The order of the items
 : in the resulting sequence is not defined. The "." and ".." items are not
 : returned. The returned paths are relative to the provided
$path
.
 :
 : If
$recursive
 evaluates to
fn:true()
, the operation
 : will recurse in the subdirectories.
 :
 : @param $dir The path/URI to retrieve the children from.
 : @param $recursive A boolean flag indicating whether the operation should be
 :    performed recursively.
 : @return A sequence of relative paths.
 : @error file:FOFL0003 If
$dir
 does not point to an existing directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:nondeterministic
function file:list( $path as xs:string, $recursive as xs:boolean )
  as xs:string*
{
  for $f in file:list($path)
  let $full := fn:concat($path, file:directory-separator(), $f)
  return (
    $f,
    if ($recursive and file:is-directory($full)) then
      for $child in file:list($full, $recursive)
      return fn:concat($f, file:directory-separator(), $child)
    else
      ()
  )
};

(:~
 : Lists all files matching the given pattern in a given directory.
 : The order of the items in the result is not defined.
 : The "." and ".." items are not considered for the match.
 : The file paths are relative to the provided $path.
 :
 : The pattern is a glob expression supporting:
 :

 :

*
 for matching any number of unknown characters

 :

?
 for matching one unknown character

 :

 :
 : @param $path The path/URI to retrieve the children from.
 : @param $recursive A boolean flag indicating whether the operation should be
 :    performed recursively.
 : @param $pattern The file name pattern.
 : @return A sequence of file names matching the pattern.
 : @error file:FOFL0003 If
$dir
 does not point to an existing directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:nondeterministic
function file:list( $path as xs:string, $recursive as xs:boolean,
                    $pattern as xs:string )
  as xs:string*
{
  for $file in file:list( $path, $recursive )
  let $name := file:base-name( $file )
  return
    if ( fn:matches( $name, file:glob-to-regex( $pattern ) ) ) then
      $file
    else
      ()
};

(:~
 : Moves a file or directory given a source and a destination paths/URIs.
 :
 : @param $source The path/URI of the file to move.
 : @param $destination The destination path/URI.
 : @return The empty sequence.
 : @error file:FOFL0001 If the
$source
 path does not exist.
 : @error file:FOFL0002 If
$source
 points to a directory and
 :
$destination
 points to an existing file.
 : @error file:FOFL0003 If
$destination
 does not exist and its
 : parent directory does not exist either.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:sequential
function file:move( $source as xs:string, $destination as xs:string )
  as empty-sequence()
{
  file:copy( $source, $destination );
  file:delete( $source );
};

(:~
 : This function returns the value of the operating system specific path
 : separator. For example,
:
 on Unix-based systems and
;

 : on Windows systems.
 :
 : @return The operating system specific path separator.
 :)
declare function file:path-separator()
  as xs:string external;

(:~
 : Transforms a URI, an absolute path, or relative path to a native path on the
 : running platform.
 :
 : No path existence check is made.
 :
 : @param $path The uri or path to normalize.
 : @return The native path corresponding to
$path
.
 : @error file:FOFL9999 If an error occurs while trying to obtain the native
 : path.
 :)
declare function file:path-to-native( $path as xs:string )
  as xs:string external;

(:~
 : Transforms a file system path into a URI with the file:// scheme. If the
 : path is relative, it is first resolved against the current working
 : directory.
 :
 : No path existence check is made.
 :
 : @param $path The path to transform.
 : @return The file URI corresponding to
path
.
 :)
declare function file:path-to-uri( $path as xs:string )
  as xs:anyURI external;

(:~
 : Reads the content of a file and returns a Base64 representation of the
 : content.
 :
 : @param $file The file to read.
 : @return The content of the file as Base64.
 : @error file:FOFL0001 If the
$source
 path does not exist.
 : @error file:FOFL0004 If
$source
 points to a directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:nondeterministic
function file:read-binary( $file as xs:string )
  as xs:base64Binary external;

(:~
 : Reads the content of a file using the specified encoding and returns a
 : string representation of the content.
 :
 : @param $file The file to read.
 : @param $encoding The encoding used when reading the file.
 : If compiled with ICU, then Zorba supports any encoding that ICU supports;
 : otherwise Zorba only supports ASCII and UTF-8.
 : The encoding parameter is case insensitive.
 : @return The content of the file as string.
 : @error file:FOFL0001 If the
$source
 path does not exist.
 : @error file:FOFL0004 If
$source
 points to a directory.
 : @error file:FOFL0006 If
$encoding
 is not supported.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:nondeterministic
function file:read-text( $file as xs:string, $encoding as xs:string )
  as xs:string external;

(:~
 : Reads the content of a file and returns a string representation of the
 : content.
 :
 : The operation is equivalent to calling:
 :
file:read-text($file, "UTF-8")
.
 :
 : @param $file The file to read.
 : @return The content of the file as string.
 : @error file:FOFL0001 If the
$source
 path does not exist.
 : @error file:FOFL0004 If
$source
 points to a directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:nondeterministic
function file:read-text( $file as xs:string )
  as xs:string
{
  file:read-text( $file, "UTF-8" )
};

(:~
 : Reads the content of a file and returns a sequence of strings representing
 : the lines in the content of the file.
 :
 : The operation is equivalent to calling:
 :
file:read-text-lines($file, "UTF-8")
.
 :
 : @param $file The file to read.
 : @return The content of the file as a sequence of strings.
 : @error file:FOFL0001 If the
$source
 path does not exist.
 : @error file:FOFL0004 If
$source
 points to a directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:nondeterministic
function file:read-text-lines( $file as xs:string )
  as xs:string*
{
  file:read-text-lines( $file, "UTF-8" )
};

(:~
 : Reads the content of a file using the specified encoding and returns a
 : sequence of strings representing the lines in the content of the file.
 :
 : This implementation considers the LF (
) character as the line
 : separator. If a resulting line ends with the CR (
) character, this is
 : trimmed as well. This implementation will uniformly treat LF and CRLF as
 : line separators.
 :
 : @param $file The file to read.
 : @param $encoding The encoding used when reading the file.
 : If compiled with ICU, then Zorba supports any encoding that ICU supports;
 : otherwise Zorba only supports ASCII and UTF-8.
 : The encoding parameter is case insensitive.
 : @return The content of the file as a sequence of strings.
 : @error file:FOFL0001 If the
$source
 path does not exist.
 : @error file:FOFL0004 If
$source
 points to a directory.
 : @error file:FOFL0006 If
$encoding
 is not supported.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:nondeterministic
function file:read-text-lines( $file as xs:string, $encoding as xs:string )
  as xs:string* external;

(:~
 : Transforms a relative path/URI into an absolute operating system path by
 : resolving it against the current working directory.
 :
 : No path existence check is made.
 :
 : @param $path The path/URI to transform.
 : @return The operating system file path.
 :)
declare function file:resolve-path( $path as xs:string )
  as xs:string external;

(:~
 : Retrieves the size of a file.
 :
 : @param $file The file get the size.
 : @return An integer representing the size in bytes of the file.
 : @error file:FOFL0001 If the
$file
 does not exist.
 : @error file:FOFL0004 If the
$file
 points to a directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:nondeterministic function file:size( $file as xs:string )
  as xs:integer external;

(:~
 : Writes a sequence of Base64 items as binary to a file.
 :
 : @param $file The path/URI of the file to write the content to.
 : @param $content The content to be written to the file.
 : @return The empty sequence.
 : @error file:FOFL0004 If
$file
 points to a directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:sequential
function file:write-binary( $file as xs:string, $content as xs:base64Binary )
  as empty-sequence() external;

(:~
 : Writes a sequence of strings to a file.
 :
 : @param $file The path/URI of the file to write the content to.
 : @param $content The content to be written to the file.
 : @param $encoding The character encoding to write $content as.
 : @return The empty sequence.
 : @error file:FOFL0004 If
$file
 points to a directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:sequential
function file:write-text( $file as xs:string, $content as xs:string,
                          $encoding as xs:string )
  as empty-sequence() external;

(:~
 : Writes a sequence of strings to a file using the UTF-8 character encoding.
 :
 : @param $file The path/URI of the file to write the content to.
 : @param $content The content to be written to the file.
 : @return The empty sequence.
 : @error file:FOFL0004 If
$file
 points to a directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:sequential
function file:write-text( $file as xs:string, $content as xs:string )
  as empty-sequence()
{
  file:write-text( $file, $content, "UTF-8" )
};

(:~
 : Writes a sequence of strings to a file, each followed by a
 : platform-dependent newline character(s).
 :
 : @param $file The path/URI of the file to write the content to.
 : @param $content The content to be written to the file.
 : @param $encoding The character encoding to write $content as.
 : @return The empty sequence.
 : @error file:FOFL0004 If
$file
 points to a directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:sequential
function file:write-text-lines( $file as xs:string, $content as xs:string*,
                                $encoding as xs:string )
  as empty-sequence() external;

(:~
 : Writes a sequence of strings to a file, each followed by a
 : platform-dependent newline character(s), using the UTF-8 character encoding.
 :
 : @param $file The path/URI of the file to write the content to.
 : @param $content The content to be written to the file.
 : @return The empty sequence.
 : @error file:FOFL0004 If
$file
 points to a directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:sequential
function file:write-text-lines( $file as xs:string, $content as xs:string* )
  as empty-sequence()
{
  file:write-text-lines( $file, $content, "UTF-8" )
};

(:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::)

(:~
 : Copies a file given a source and a destination path/URI.
 :
 : @param $sourceFile The path/URI of the file to copy.
 : @param $destination The destination path/URI.
 : @return The empty sequence.
 : @error file:FOFL0001 If the
$source
 path does not exist.
 : @error file:FOFL0002 If the computed destination points to directory.
 : @error file:FOFL0003 If
$destination
 does not exist and it's
 :    parent directory does not exist either.
 : @error file:FOFL0004 If
$sourceFile
 points to a directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %private %an:sequential
function file:copy-file-impl( $sourceFile as xs:string,
                              $destination as xs:string )
  as empty-sequence() external;

(:~
 : Copies a source directory recursively to a destination path/URI.
 :
 : @param $sourceDir The path/URI of the directory to copy.
 : @param $destination The destination path/URI.
 : @return The empty sequence.
 : @error file:FOFL0001 If the
$source
 path does not exist.
 : @error file:FOFL0002 If
$destination
 points to an existing file.
 : @error file:FOFL0003 If
$destination
 does not exist and it's
 :    parent directory does not exist either.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %private %an:nondeterministic %an:sequential
function file:copy-directory-impl( $sourceDir as xs:string,
                                   $destination as xs:string )
  as empty-sequence()
{
  if (file:is-file($destination)) then
    fn:error(xs:QName("file:FOFL0002"), fn:concat("The specified destination path already exists: ", $destination))
  else if (fn:not(file:exists($destination))) then
    let $dirname := file:dir-name($destination)
    return
      if (fn:not(file:exists($dirname))) then
        fn:error(xs:QName("file:FOFL0003"), fn:concat("The destination directory does not exist: ", $dirname))
      else
        {
          file:create-directory($destination);
          file:copy-directory-content($sourceDir, $destination)
        }
  else
    let $basename := file:base-name($sourceDir)
    let $newdir := fn:concat($destination, file:directory-separator(), $basename)
    return
      {
        file:create-directory($newdir);
        file:copy-directory-content($sourceDir, $newdir)
      }
};

(:~
 : This is an internal function that copies an entire source directory to an
 : destination directory. The caller to this function must make sure that both
 : the source and destination point to existing directories.
 :
 : @param $sourceDir The existing source directory.
 : @param $destinationDir The existing destination directory.
 : @return The empty sequence.
 :)
declare %private %an:nondeterministic %an:sequential
function file:copy-directory( $sourceDir as xs:string,
                              $destinationDir as xs:string )
  as empty-sequence()
{
  let $name := file:base-name($sourceDir)
  let $destDir := fn:concat($destinationDir, file:directory-separator(), $name)
  return
    {
      file:create-directory($destDir);

      for $item in file:list($sourceDir)
      let $fullSrcPath := fn:concat($sourceDir, file:directory-separator(), $item)
      let $fullDestPath := fn:concat($destDir, file:directory-separator(), $item)
      return
        if (file:is-directory($fullSrcPath)) then
          file:copy-directory($fullSrcPath, $fullDestPath)
        else
          file:copy($fullSrcPath, $fullDestPath)
    }
};

(:~
 : Copies the content of a given directory to an existing destination
 : directory.
 :
 : @param $sourceDir The path/URI of the directory to copy the content from.
 : @param $destination The destination directory path/URI.
 : @return The empty sequence.
 : @error file:FOFL0001 If the
$source
 path does not exist.
 : @error file:FOFL0003 If
$destination
 directory does not exist.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %private %an:nondeterministic %an:sequential
function file:copy-directory-content( $sourceDir as xs:string,
                                      $destination as xs:string )
  as empty-sequence()
{
  if (file:is-directory($destination)) then
    for $item in file:list($sourceDir)
    let $fullPath := fn:concat($sourceDir, file:directory-separator(), $item)
    return
      file:copy($fullPath, $destination)
  else
    fn:error(xs:QName("file:FOFL0003"), fn:concat("The specified destination directory does not exist: ", $destination))
};

(:~
 : Deletes a file from the file system.
 :
 : @param $file The path/URI of the file to delete.
 : @return The empty sequence.
 : @error file:FOFL0001 If the
$file
 path does not exist.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %private %an:sequential
function file:delete-file-impl( $file as xs:string )
  as empty-sequence() external;

(:~
 : Deletes a directory from the file system.
 :
 : @param $dir The path/URI of the directory to delete.
 : @return The empty sequence.
 : @error file:FOFL0001 If the
$dir
 path does not exist.
 : @error file:FOFL0003 If
$dir
 does not point to a directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %private %an:nondeterministic %an:sequential
function file:delete-directory-impl( $dir as xs:string )
  as empty-sequence()
{
  for $item in file:list($dir)
  let $fullPath := fn:concat($dir, file:directory-separator(), $item)
  return
    if (file:is-directory($fullPath)) then
      file:delete-directory-impl($fullPath);
    else
      file:delete-file-impl($fullPath);

  file:delete-file-impl($dir)
};

(: vim:set et sw=2 ts=2: :)

xquery version "3.0";

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

(:~
 : This module implements the file API EXPath specification available at http://expath.org/spec/file
 : @author Gabriel Petrovay, Matthias Brantner, Markus Pilman
 : @see http://expath.org/spec/file
 : @project EXPath/EXPath File Module
 :)
module namespace file = "http://expath.org/ns/file";

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

(:~
 : Appends a sequence of Base64 items as binary to a file. If the file pointed
 : by
$file
 does not exist, a new file will be created.
 :
 : @param $file The path/URI of the file to write the content to.
 : @param $content The content to be written to the file.
 : @return The empty sequence.
 : @error file:FOFL0004 If
$file
 points to a directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:sequential
function file:append-binary( $file as xs:string, $content as xs:base64Binary )
  as empty-sequence() external;

(:~
 : Appends a sequence of string items to a file.
 :
 : @param $file The path/URI of the file to write the content to.
 : @param $content The content to be written to the file.
 : @param $encoding The character encoding to append $content as.
 : @return The empty sequence.
 : @error file:FOFL0004 If
$file
 points to a directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:sequential
function file:append-text( $file as xs:string, $content as xs:string*,
                           $encoding as xs:string )
  as empty-sequence() external;

(:~
 : Appends a sequence of string items to a file.
 :
 : @param $file The path/URI of the file to write the content to.
 : @param $content The content to be written to the file.
 : @return The empty sequence.
 : @error file:FOFL0004 If
$file
 points to a directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:sequential
function file:append-text( $file as xs:string, $content as xs:string* )
  as empty-sequence()
{
  file:append-text( $file, $content, "UTF-8" )
};

(:~
 : Appends a sequence of string items to a file, each followed by a
 : platform-dependent newline character(s).
 :
 : @param $file The path/URI of the file to write the content to.
 : @param $content The content to be written to the file.
 : @param $encoding The character encoding to append $content as.
 : @return The empty sequence.
 : @error file:FOFL0004 If
$file
 points to a directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:sequential
function file:append-text-lines( $file as xs:string, $content as xs:string*,
                                 $encoding as xs:string )
  as empty-sequence() external;

(:~
 : Appends a sequence of string to a file, each followed by a
 : platform-dependent newline character(s), using the UTF-8 character encoding.
 :
 : @param $file The path/URI of the file to write the content to.
 : @param $content The content to be written to the file.
 : @return The empty sequence.
 : @error file:FOFL0004 If
$file
 points to a directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:sequential
function file:append-text-lines( $file as xs:string, $content as xs:string* )
  as empty-sequence()
{
  file:append-text-lines( $file, $content, "UTF-8" )
};

(:~
 : Returns the last component from the
$path
, deleting any
 : trailing directory-separator characters. If
$path
 consists
 : entirely directory-separator characters, the empty string is returned. If
 :
$path
 is the empty string, the string
"."
 is returned,
 : signifying the current directory.
 :
 : No path existence check is made.
 :
 : @param $path A file path/URI.
 : @return The base name of this file.
 :)
declare function file:base-name( $path as xs:string )
  as xs:string external;

(:~
 : Returns the last component from the
$path
, deleting any
 : trailing directory-separator characters and the
$suffix
. If path
 : consists entirely directory-separator characters, the empty string is
 : returned. If path is the empty string, the string
"."
 is
 : returned, signifying the current directory.
 :
 : No path existence check is made.
 :
 : The
$suffix
 can be used for example to eliminate file extensions.
 :
 : @param $path A file path/URI.
 : @param $suffix A suffix which should get deleted from the result.
 : @return The base-name of $path with a deleted $suffix.
 :)
declare function file:base-name( $path as xs:string, $suffix as xs:string )
  as xs:string
{
  let $res := file:base-name($path)
  return
    if (fn:ends-with($res, $suffix) and $res ne ".") then
      fn:substring($res, 1, fn:string-length($res) - fn:string-length($suffix))
    else
      $res
};

(:~
 : Copies a file or a directory given a source and a destination path/URI.
 :
 : @param $source The path/URI of the file or directory to copy.
 : @param $destination The destination path/URI.
 : @return The empty sequence.
 : @error file:FOFL0001 If the
$source
 path does not exist.
 : @error file:FOFL0002 If the computed destination points to a file system item
 :    with a different type than the
$source
.
 : @error file:FOFL0003 If
$destination
 does not exist and it's
 :    parent directory does not exist either.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:nondeterministic %an:sequential
function file:copy( $source as xs:string, $destination as xs:string )
  as empty-sequence()
{
  if (file:exists($source)) then
    if (file:is-directory($source)) then
      file:copy-directory-impl($source, $destination)
    else
      file:copy-file-impl($source, $destination)
  else
    fn:error(xs:QName("file:FOFL0001"), fn:concat("The source path does not exists: ", $source))
};

(:~
 : Creates a directory.
 :
 : The operation is will create all the missing parent directories from the
 : given path.
 :
 : @param $dir The path/URI denoting the directory to be created.
 : @return The empty sequence.
 : @error file:FOFL0002 If the directory cannot be created because of an already
 :    existing file.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:sequential function
file:create-directory( $dir as xs:string )
  as empty-sequence() external;

(:~
 : Deletes a file or a directory from the file system.
 :
 : If
$path
 points to a directory the directory will be deleted
 : recursively.
 :
 : @param $path The path/URI of the file or directory to delete.
 : @return The empty sequence.
 : @error file:FOFL0001 If the
$path
 path does not exist.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:nondeterministic %an:sequential
function file:delete( $path as xs:string )
  as empty-sequence()
{
  if (file:exists($path,false())) then
    if (not(file:is-symlink($path)) and file:is-directory($path)) then
      file:delete-directory-impl($path)
    else
      file:delete-file-impl($path)
  else
    fn:error(xs:QName("file:FOFL0001"), fn:concat("The path does not exists: ", $path))
};

(:~
 : This function returns the value of the operating system specific directory
 : separator. For example,
/
 on Unix-based systems and
\

 : on Windows systems.
 :
 : @return The operating system specific directory separator.
 :)
declare function file:directory-separator()
  as xs:string external;

(:~
 : This function is the converse of
file:base-name
. It returns a
 : string denoting the parent directory of the
$path
. Any trailing
 : directory-separator characters are not counted as part of the directory
 : name. If path is the empty string or contains no directory-separator string,
 :
"."
 is returned, signifying the current directory.
 :
 : No path existence check is made.
 :
 : @param $path The filename, of which the dirname should be get.
 : @return The name of the directory the file is in.
 :)
declare function file:dir-name( $path as xs:string )
  as xs:string external;

(:~
 : Tests if a path/URI is already used in the file system.
 :
 : @param $path The path/URI to test.
 : @param $follow-symlinks if true, follows symbolic links.
 : @return true if $path points to an existing file system item.
 :)
declare %an:nondeterministic
function file:exists( $path as xs:string, $follow-symlinks as xs:boolean )
  as xs:boolean external;

(:~
 : Tests if a path/URI is already used in the file system.
 :
 : @param $path The path/URI to test.
 : @return true if $path points to an existing file system item;
 : for symbolic links, retuns true if the linked-to item exists.
 :)
declare %an:nondeterministic
function file:exists( $path as xs:string )
  as xs:boolean
{
  file:exists( $path, true() )
};

(:~
 : A helper function that performs a trivial (not complete) glob to regex
 : pattern translation.
 :
 : @param $pattern The glob pattern.
 : @return A regex pattern corresponding to the glob pattern provided.
 :)
declare function file:glob-to-regex( $pattern as xs:string )
  as xs:string
{
  let $pattern := fn:replace($pattern, '(\.|\[|\]|\\|/|\||\-|\^|\$|\?|\*|\+|\{|\}|\(|\))','\\$1')
  let $pattern := fn:replace($pattern, '\\\?', '.')
  let $pattern := fn:replace($pattern, '\\\*', '.*')
  return fn:concat( "^", $pattern, "$" )
};

(:~
 : Tests if a path/URI points to a directory. On Unix-based systems, the root
 : and the volume roots are considered directories.
 :
 : @param $path The path/URI to test.
 : @return true if $path points to a directory; for symbolic
 : links, returns true if the linked-to item is a directory.
 :)
declare %an:nondeterministic
function file:is-directory( $path as xs:string )
  as xs:boolean external;

(:~
 : Tests if a path/URI points to a file.
 :
 : @param $path The path/URI to test.
 : @return true if $path points to a file; for symbolic links,
 : returns true if the linked-to item is a file.
 :)
declare %an:nondeterministic
function file:is-file( $path as xs:string )
  as xs:boolean external;

(:~
 : Tests if a path/URI points to symbolic link.  This works on all Unix-based
 : systems as well as Windows Vista or later.
 :
 : @param $path The path/URI to test.
 : @return true if $path points to a symbolic link.
 :)
declare %an:nondeterministic
function file:is-symlink( $path as xs:string )
  as xs:boolean external;

(:~
 : Retrieves the timestamp of the last modification of the file system item
 : pointed by the path/URI.
 :
 : @param $path The file system item to read the last modification
 :    timestamp from.
 : @return The date and time of the last modification of the item.
 : @error file:FOFL0001 If the
$path
 does not exist.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:nondeterministic function file:last-modified( $path as xs:string )
  as xs:dateTime external;

(:~
 : Lists the file system items in a certain directory.
 :
 : The operation is equivalent to calling:
 :
file:list($dir, fn:false())
.
 :
 : @param $dir The path/URI of the directory to retrieve the children from.
 : @return The sequence of names of the direct children.
 : @error file:FOFL0003 If
$dir
 does not point to an existing directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:nondeterministic
function file:list( $dir as xs:string )
  as xs:string* external;

(:~
 : Lists the file system items in a certain directory. The order of the items
 : in the resulting sequence is not defined. The "." and ".." items are not
 : returned. The returned paths are relative to the provided
$path
.
 :
 : If
$recursive
 evaluates to
fn:true()
, the operation
 : will recurse in the subdirectories.
 :
 : @param $dir The path/URI to retrieve the children from.
 : @param $recursive A boolean flag indicating whether the operation should be
 :    performed recursively.
 : @return A sequence of relative paths.
 : @error file:FOFL0003 If
$dir
 does not point to an existing directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:nondeterministic
function file:list( $path as xs:string, $recursive as xs:boolean )
  as xs:string*
{
  for $f in file:list($path)
  let $full := fn:concat($path, file:directory-separator(), $f)
  return (
    $f,
    if ($recursive and file:is-directory($full)) then
      for $child in file:list($full, $recursive)
      return fn:concat($f, file:directory-separator(), $child)
    else
      ()
  )
};

(:~
 : Lists all files matching the given pattern in a given directory.
 : The order of the items in the result is not defined.
 : The "." and ".." items are not considered for the match.
 : The file paths are relative to the provided $path.
 :
 : The pattern is a glob expression supporting:
 :

 :

*
 for matching any number of unknown characters

 :

?
 for matching one unknown character

 :

 :
 : @param $path The path/URI to retrieve the children from.
 : @param $recursive A boolean flag indicating whether the operation should be
 :    performed recursively.
 : @param $pattern The file name pattern.
 : @return A sequence of file names matching the pattern.
 : @error file:FOFL0003 If
$dir
 does not point to an existing directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:nondeterministic
function file:list( $path as xs:string, $recursive as xs:boolean,
                    $pattern as xs:string )
  as xs:string*
{
  for $file in file:list( $path, $recursive )
  let $name := file:base-name( $file )
  return
    if ( fn:matches( $name, file:glob-to-regex( $pattern ) ) ) then
      $file
    else
      ()
};

(:~
 : Moves a file or directory given a source and a destination paths/URIs.
 :
 : @param $source The path/URI of the file to move.
 : @param $destination The destination path/URI.
 : @return The empty sequence.
 : @error file:FOFL0001 If the
$source
 path does not exist.
 : @error file:FOFL0002 If
$source
 points to a directory and
 :
$destination
 points to an existing file.
 : @error file:FOFL0003 If
$destination
 does not exist and its
 : parent directory does not exist either.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:sequential
function file:move( $source as xs:string, $destination as xs:string )
  as empty-sequence()
{
  file:copy( $source, $destination );
  file:delete( $source );
};

(:~
 : This function returns the value of the operating system specific path
 : separator. For example,
:
 on Unix-based systems and
;

 : on Windows systems.
 :
 : @return The operating system specific path separator.
 :)
declare function file:path-separator()
  as xs:string external;

(:~
 : Transforms a URI, an absolute path, or relative path to a native path on the
 : running platform.
 :
 : No path existence check is made.
 :
 : @param $path The uri or path to normalize.
 : @return The native path corresponding to
$path
.
 : @error file:FOFL9999 If an error occurs while trying to obtain the native
 : path.
 :)
declare function file:path-to-native( $path as xs:string )
  as xs:string external;

(:~
 : Transforms a file system path into a URI with the file:// scheme. If the
 : path is relative, it is first resolved against the current working
 : directory.
 :
 : No path existence check is made.
 :
 : @param $path The path to transform.
 : @return The file URI corresponding to
path
.
 :)
declare function file:path-to-uri( $path as xs:string )
  as xs:anyURI external;

(:~
 : Reads the content of a file and returns a Base64 representation of the
 : content.
 :
 : @param $file The file to read.
 : @return The content of the file as Base64.
 : @error file:FOFL0001 If the
$source
 path does not exist.
 : @error file:FOFL0004 If
$source
 points to a directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:nondeterministic
function file:read-binary( $file as xs:string )
  as xs:base64Binary external;

(:~
 : Reads the content of a file using the specified encoding and returns a
 : string representation of the content.
 :
 : @param $file The file to read.
 : @param $encoding The encoding used when reading the file.
 : If compiled with ICU, then Zorba supports any encoding that ICU supports;
 : otherwise Zorba only supports ASCII and UTF-8.
 : The encoding parameter is case insensitive.
 : @return The content of the file as string.
 : @error file:FOFL0001 If the
$source
 path does not exist.
 : @error file:FOFL0004 If
$source
 points to a directory.
 : @error file:FOFL0006 If
$encoding
 is not supported.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:nondeterministic
function file:read-text( $file as xs:string, $encoding as xs:string )
  as xs:string external;

(:~
 : Reads the content of a file and returns a string representation of the
 : content.
 :
 : The operation is equivalent to calling:
 :
file:read-text($file, "UTF-8")
.
 :
 : @param $file The file to read.
 : @return The content of the file as string.
 : @error file:FOFL0001 If the
$source
 path does not exist.
 : @error file:FOFL0004 If
$source
 points to a directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:nondeterministic
function file:read-text( $file as xs:string )
  as xs:string
{
  file:read-text( $file, "UTF-8" )
};

(:~
 : Reads the content of a file and returns a sequence of strings representing
 : the lines in the content of the file.
 :
 : The operation is equivalent to calling:
 :
file:read-text-lines($file, "UTF-8")
.
 :
 : @param $file The file to read.
 : @return The content of the file as a sequence of strings.
 : @error file:FOFL0001 If the
$source
 path does not exist.
 : @error file:FOFL0004 If
$source
 points to a directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:nondeterministic
function file:read-text-lines( $file as xs:string )
  as xs:string*
{
  file:read-text-lines( $file, "UTF-8" )
};

(:~
 : Reads the content of a file using the specified encoding and returns a
 : sequence of strings representing the lines in the content of the file.
 :
 : This implementation considers the LF (
) character as the line
 : separator. If a resulting line ends with the CR (
) character, this is
 : trimmed as well. This implementation will uniformly treat LF and CRLF as
 : line separators.
 :
 : @param $file The file to read.
 : @param $encoding The encoding used when reading the file.
 : If compiled with ICU, then Zorba supports any encoding that ICU supports;
 : otherwise Zorba only supports ASCII and UTF-8.
 : The encoding parameter is case insensitive.
 : @return The content of the file as a sequence of strings.
 : @error file:FOFL0001 If the
$source
 path does not exist.
 : @error file:FOFL0004 If
$source
 points to a directory.
 : @error file:FOFL0006 If
$encoding
 is not supported.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:nondeterministic
function file:read-text-lines( $file as xs:string, $encoding as xs:string )
  as xs:string* external;

(:~
 : Transforms a relative path/URI into an absolute operating system path by
 : resolving it against the current working directory.
 :
 : No path existence check is made.
 :
 : @param $path The path/URI to transform.
 : @return The operating system file path.
 :)
declare function file:resolve-path( $path as xs:string )
  as xs:string external;

(:~
 : Retrieves the size of a file.
 :
 : @param $file The file get the size.
 : @return An integer representing the size in bytes of the file.
 : @error file:FOFL0001 If the
$file
 does not exist.
 : @error file:FOFL0004 If the
$file
 points to a directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:nondeterministic function file:size( $file as xs:string )
  as xs:integer external;

(:~
 : Writes a sequence of Base64 items as binary to a file.
 :
 : @param $file The path/URI of the file to write the content to.
 : @param $content The content to be written to the file.
 : @return The empty sequence.
 : @error file:FOFL0004 If
$file
 points to a directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:sequential
function file:write-binary( $file as xs:string, $content as xs:base64Binary )
  as empty-sequence() external;

(:~
 : Writes a sequence of strings to a file.
 :
 : @param $file The path/URI of the file to write the content to.
 : @param $content The content to be written to the file.
 : @param $encoding The character encoding to write $content as.
 : @return The empty sequence.
 : @error file:FOFL0004 If
$file
 points to a directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:sequential
function file:write-text( $file as xs:string, $content as xs:string*,
                          $encoding as xs:string )
  as empty-sequence() external;

(:~
 : Writes a sequence of strings to a file using the UTF-8 character encoding.
 :
 : @param $file The path/URI of the file to write the content to.
 : @param $content The content to be written to the file.
 : @return The empty sequence.
 : @error file:FOFL0004 If
$file
 points to a directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:sequential
function file:write-text( $file as xs:string, $content as xs:string* )
  as empty-sequence()
{
  file:write-text( $file, $content, "UTF-8" )
};

(:~
 : Writes a sequence of strings to a file, each followed by a
 : platform-dependent newline character(s).
 :
 : @param $file The path/URI of the file to write the content to.
 : @param $content The content to be written to the file.
 : @param $encoding The character encoding to write $content as.
 : @return The empty sequence.
 : @error file:FOFL0004 If
$file
 points to a directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:sequential
function file:write-text-lines( $file as xs:string, $content as xs:string*,
                                $encoding as xs:string )
  as empty-sequence() external;

(:~
 : Writes a sequence of strings to a file, each followed by a
 : platform-dependent newline character(s), using the UTF-8 character encoding.
 :
 : @param $file The path/URI of the file to write the content to.
 : @param $content The content to be written to the file.
 : @return The empty sequence.
 : @error file:FOFL0004 If
$file
 points to a directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %an:sequential
function file:write-text-lines( $file as xs:string, $content as xs:string* )
  as empty-sequence()
{
  file:write-text-lines( $file, $content, "UTF-8" )
};

(:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::)

(:~
 : Copies a file given a source and a destination path/URI.
 :
 : @param $sourceFile The path/URI of the file to copy.
 : @param $destination The destination path/URI.
 : @return The empty sequence.
 : @error file:FOFL0001 If the
$source
 path does not exist.
 : @error file:FOFL0002 If the computed destination points to directory.
 : @error file:FOFL0003 If
$destination
 does not exist and it's
 :    parent directory does not exist either.
 : @error file:FOFL0004 If
$sourceFile
 points to a directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %private %an:sequential
function file:copy-file-impl( $sourceFile as xs:string,
                              $destination as xs:string )
  as empty-sequence() external;

(:~
 : Copies a source directory recursively to a destination path/URI.
 :
 : @param $sourceDir The path/URI of the directory to copy.
 : @param $destination The destination path/URI.
 : @return The empty sequence.
 : @error file:FOFL0001 If the
$source
 path does not exist.
 : @error file:FOFL0002 If
$destination
 points to an existing file.
 : @error file:FOFL0003 If
$destination
 does not exist and it's
 :    parent directory does not exist either.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %private %an:nondeterministic %an:sequential
function file:copy-directory-impl( $sourceDir as xs:string,
                                   $destination as xs:string )
  as empty-sequence()
{
  if (file:is-file($destination)) then
    fn:error(xs:QName("file:FOFL0002"), fn:concat("The specified destination path already exists: ", $destination))
  else if (fn:not(file:exists($destination))) then
    let $dirname := file:dir-name($destination)
    return
      if (fn:not(file:exists($dirname))) then
        fn:error(xs:QName("file:FOFL0003"), fn:concat("The destination directory does not exist: ", $dirname))
      else
        {
          file:create-directory($destination);
          file:copy-directory-content($sourceDir, $destination)
        }
  else
    let $basename := file:base-name($sourceDir)
    let $newdir := fn:concat($destination, file:directory-separator(), $basename)
    return
      {
        file:create-directory($newdir);
        file:copy-directory-content($sourceDir, $newdir)
      }
};

(:~
 : This is an internal function that copies an entire source directory to an
 : destination directory. The caller to this function must make sure that both
 : the source and destination point to existing directories.
 :
 : @param $sourceDir The existing source directory.
 : @param $destinationDir The existing destination directory.
 : @return The empty sequence.
 :)
declare %private %an:nondeterministic %an:sequential
function file:copy-directory( $sourceDir as xs:string,
                              $destinationDir as xs:string )
  as empty-sequence()
{
  let $name := file:base-name($sourceDir)
  let $destDir := fn:concat($destinationDir, file:directory-separator(), $name)
  return
    {
      file:create-directory($destDir);

      for $item in file:list($sourceDir)
      let $fullSrcPath := fn:concat($sourceDir, file:directory-separator(), $item)
      let $fullDestPath := fn:concat($destDir, file:directory-separator(), $item)
      return
        if (file:is-directory($fullSrcPath)) then
          file:copy-directory($fullSrcPath, $fullDestPath)
        else
          file:copy($fullSrcPath, $fullDestPath)
    }
};

(:~
 : Copies the content of a given directory to an existing destination
 : directory.
 :
 : @param $sourceDir The path/URI of the directory to copy the content from.
 : @param $destination The destination directory path/URI.
 : @return The empty sequence.
 : @error file:FOFL0001 If the
$source
 path does not exist.
 : @error file:FOFL0003 If
$destination
 directory does not exist.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %private %an:nondeterministic %an:sequential
function file:copy-directory-content( $sourceDir as xs:string,
                                      $destination as xs:string )
  as empty-sequence()
{
  if (file:is-directory($destination)) then
    for $item in file:list($sourceDir)
    let $fullPath := fn:concat($sourceDir, file:directory-separator(), $item)
    return
      file:copy($fullPath, $destination)
  else
    fn:error(xs:QName("file:FOFL0003"), fn:concat("The specified destination directory does not exist: ", $destination))
};

(:~
 : Deletes a file from the file system.
 :
 : @param $file The path/URI of the file to delete.
 : @return The empty sequence.
 : @error file:FOFL0001 If the
$file
 path does not exist.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %private %an:sequential
function file:delete-file-impl( $file as xs:string )
  as empty-sequence() external;

(:~
 : Deletes a directory from the file system.
 :
 : @param $dir The path/URI of the directory to delete.
 : @return The empty sequence.
 : @error file:FOFL0001 If the
$dir
 path does not exist.
 : @error file:FOFL0003 If
$dir
 does not point to a directory.
 : @error file:FOFL9999 If any other error occurs.
 :)
declare %private %an:nondeterministic %an:sequential
function file:delete-directory-impl( $dir as xs:string )
  as empty-sequence()
{
  for $item in file:list($dir)
  let $fullPath := fn:concat($dir, file:directory-separator(), $item)
  return
    if (file:is-directory($fullPath)) then
      file:delete-directory-impl($fullPath);
    else
      file:delete-file-impl($fullPath);

  file:delete-file-impl($dir)
};

(: vim:set et sw=2 ts=2: :)


Key update per #345

Key update per #345

xquery version '1.0-ml';
module namespace srcHasMorePropTgt-from-srcHasMorePropSrc
    = 'http://marklogic.com/srcHasMoreProp/srcHasMorePropTgt-0.0.2-from-srcHasMorePropSrc-0.0.1';

import module namespace es = 'http://marklogic.com/entity-services'
    at '/MarkLogic/entity-services/entity-services.xqy';

declare option xdmp:mapping 'false';

(:
 This module was generated by MarkLogic Entity Services.
 Its purpose is to create instances of entity types
 defined in
 srcHasMorePropTgt, version 0.0.2
 from documents that were persisted according to model
 srcHasMorePropSrc, version 0.0.1

 For usage and extension points, see the Entity Services Developer's Guide

 https://docs.marklogic.com/guide/entity-services

 Generated at timestamp: 2017-09-11T14:44:34.975693-07:00

 Target Model srcHasMorePropTgt-0.0.2 Info:

 Type Customer:
    primaryKey: CustomerID, ( in source: CustomerID )
    required: None, ( in source: None )
    range indexes: None, ( in source: None )
    word lexicons: None, ( in source: None )

 Type Product:
    primaryKey: ProductName, ( in source: ProductName )
    required: None, ( in source: None )
    range indexes: None, ( in source: None )
    word lexicons: None, ( in source: None )

:)

(:~
 : Creates a map:map instance representation of the target
 : entity type Customer from an envelope document
 : containing a source entity instance, that is, instance data
 : of type Customer, version 0.0.1.
 : @param $source  An Entity Services envelope document (<es:envelope>)
 :  or a canonical XML instance of type Customer.
 : @return A map:map instance that holds the data for Customer,
 :  version 0.0.2.
 :)

declare function srcHasMorePropTgt-from-srcHasMorePropSrc:convert-instance-Customer(
    $source as node()
) as map:map
{
    let $source-node := es:init-translation-source($source, 'Customer')

    let $CustomerID := $source-node/CustomerID ! xs:string(.)
    let $CompanyName := $source-node/CompanyName ! xs:string(.)
    let $Country := $source-node/Country ! xs:string(.)
    let $ContactName := $source-node/ContactName ! xs:string(.)

    return
    json:object()
    =>map:with("$type", "Customer")
    (: Copy attachments from source document to the target :)
    =>es:copy-attachments($source-node)
    (: The following lines are generated from the "Customer" entity type. :)
    =>   map:with('CustomerID',   $CustomerID)
    =>es:optional('CompanyName',   $CompanyName)
    =>es:optional('Country',   $Country)
    (: The following properties are in the source, but not the target:
    =>es:optional('NO TARGET',     $ContactName)
  :)

};

(:~
 : Creates a map:map instance representation of the target
 : entity type Product from an envelope document
 : containing a source entity instance, that is, instance data
 : of type Product, version 0.0.1.
 : @param $source  An Entity Services envelope document (<es:envelope>)
 :  or a canonical XML instance of type Product.
 : @return A map:map instance that holds the data for Product,
 :  version 0.0.2.
 :)

declare function srcHasMorePropTgt-from-srcHasMorePropSrc:convert-instance-Product(
    $source as node()
) as map:map
{
    let $source-node := es:init-translation-source($source, 'Product')

    let $ProductName := $source-node/ProductName ! xs:string(.)
    let $UnitPrice := $source-node/UnitPrice ! xs:integer(.)
    let $SupplierID := $source-node/SupplierID ! xs:integer(.)
    let $Discontinued := $source-node/Discontinued ! xs:string(.)

    return
    json:object()
    =>map:with("$type", "Product")
    (: Copy attachments from source document to the target :)
    =>es:copy-attachments($source-node)
    (: The following lines are generated from the "Product" entity type. :)
    =>   map:with('ProductName',   $ProductName)
    =>es:optional('UnitPrice',   $UnitPrice)
    =>es:optional('SupplierID',   $SupplierID)
    (: The following properties are in the source, but not the target:
    =>es:optional('NO TARGET',      $Discontinued)
  :)

};

xquery version '1.0-ml';
module namespace srcHasMorePropTgt-from-srcHasMorePropSrc
    = 'http://marklogic.com/srcHasMoreProp/srcHasMorePropTgt-0.0.2-from-srcHasMorePropSrc-0.0.1';

import module namespace es = 'http://marklogic.com/entity-services'
    at '/MarkLogic/entity-services/entity-services.xqy';

declare option xdmp:mapping 'false';

(:
 This module was generated by MarkLogic Entity Services.
 Its purpose is to create instances of entity types
 defined in
 srcHasMorePropTgt, version 0.0.2
 from documents that were persisted according to model
 srcHasMorePropSrc, version 0.0.1

 For usage and extension points, see the Entity Services Developer's Guide

 https://docs.marklogic.com/guide/entity-services

 Generated at timestamp: 2017-07-12T17:05:46.384628-07:00

 Target Model srcHasMorePropTgt-0.0.2 Info:

 Type Customer:
    primaryKey: CustomerID, ( in source: CustomerID )
    required: None, ( in source: None )
    range indexes: None, ( in source: None )
    word lexicons: None, ( in source: None )

 Type Product:
    primaryKey: ProductName, ( in source: ProductName )
    required: None, ( in source: None )
    range indexes: None, ( in source: None )
    word lexicons: None, ( in source: None )

:)

(:~
 : Creates a map:map instance representation of the target
 : entity type Customer from an envelope document
 : containing a source entity instance, that is, instance data
 : of type Customer, version 0.0.1.
 : @param $source  An Entity Services envelope document (<es:envelope>)
 :  or a canonical XML instance of type Customer.
 : @return A map:map instance that holds the data for Customer,
 :  version 0.0.2.
 :)

declare function srcHasMorePropTgt-from-srcHasMorePropSrc:convert-instance-Customer(
    $source as node()
) as map:map
{
    let $source-node := es:init-translation-source($source, 'Customer')

    let $CustomerID := $source-node/CustomerID ! xs:string(.)
    let $CompanyName := $source-node/CompanyName ! xs:string(.)
    let $Country := $source-node/Country ! xs:string(.)

    return
    json:object()
    =>map:with("$type", "Customer")
    (: Copy attachments from source document to the target :)
    =>es:copy-attachments($source-node)
    (: The following lines are generated from the "Customer" entity type. :)
    =>   map:with('CustomerID',  $CustomerID)
    =>es:optional('CompanyName',  $CompanyName)
    =>es:optional('Country',  $Country)
    (: The following properties are in the source, but not the target:
    =>es:optional('NO TARGET',    $ContactName)
  :)

};

(:~
 : Creates a map:map instance representation of the target
 : entity type Product from an envelope document
 : containing a source entity instance, that is, instance data
 : of type Product, version 0.0.1.
 : @param $source  An Entity Services envelope document (<es:envelope>)
 :  or a canonical XML instance of type Product.
 : @return A map:map instance that holds the data for Product,
 :  version 0.0.2.
 :)

declare function srcHasMorePropTgt-from-srcHasMorePropSrc:convert-instance-Product(
    $source as node()
) as map:map
{
    let $source-node := es:init-translation-source($source, 'Product')

    let $ProductName := $source-node/ProductName ! xs:string(.)
    let $UnitPrice := $source-node/UnitPrice ! xs:integer(.)
    let $SupplierID := $source-node/SupplierID ! xs:integer(.)

    return
    json:object()
    =>map:with("$type", "Product")
    (: Copy attachments from source document to the target :)
    =>es:copy-attachments($source-node)
    (: The following lines are generated from the "Product" entity type. :)
    =>   map:with('ProductName',  $ProductName)
    =>es:optional('UnitPrice',  $UnitPrice)
    =>es:optional('SupplierID',  $SupplierID)
    (: The following properties are in the source, but not the target:
    =>es:optional('NO TARGET',     $Discontinued)
  :)

};


Complete numeric value BaseX binary module function signatures

xquery version "3.0";
(:~
: BaseX binary module functions
:
:)
module namespace bin = "http://expath.org/ns/binary";

import module namespace a = "http://reecedunn.co.uk/xquery/annotations" at "res://reecedunn.co.uk/xquery/annotations.xqy";

declare %a:since("basex", "9.0") function bin:hex($in as xs:string?) as xs:base64Binary? external;
declare %a:since("basex", "9.0") function bin:bin($in as xs:string?) as xs:base64Binary? external;
declare %a:since("basex", "9.0") function bin:octal($in as xs:string?) as xs:base64Binary? external;
declare %a:since("basex", "9.0") function bin:to-octets($in as xs:base64Binary) as xs:integer* external;
declare %a:since("basex", "9.0") function bin:from-octets($in as xs:integer*) as xs:base64Binary external;
declare %a:since("basex", "9.0") function bin:length($in as xs:base64Binary) as xs:integer external;
declare %a:since("basex", "9.0") function bin:part($in as xs:base64Binary?, $offset as xs:integer) as xs:base64Binary? external;
declare %a:since("basex", "9.0") function bin:part($in as xs:base64Binary?, $offset as xs:integer, $size as xs:integer) as xs:base64Binary? external;
declare %a:since("basex", "9.0") function bin:join($in as xs:base64Binary*) as xs:base64Binary external;
declare %a:since("basex", "9.0") function bin:insert-before($in as xs:base64Binary?, $offset as xs:integer, $extra as xs:base64Binary?) as xs:base64Binary external;
declare %a:since("basex", "9.0") function bin:pad-left($in as xs:base64Binary?, $size as xs:integer) as xs:base64Binary? external;
declare %a:since("basex", "9.0") function bin:pad-left($in as xs:base64Binary?, $size as xs:integer, $octect as xs:integer) as xs:base64Binary? external;
declare %a:since("basex", "9.0") function bin:pad-right($in as xs:base64Binary?, $size as xs:integer) as xs:base64Binary? external;
declare %a:since("basex", "9.0") function bin:pad-right($in as xs:base64Binary?, $size as xs:integer, $octect as xs:integer) as xs:base64Binary? external;
declare %a:since("basex", "9.0") function bin:find($in as xs:base64Binary?, $offset as xs:integer, $search as xs:base64Binary) as xs:integer? external;
declare %a:since("basex", "9.0") function bin:decode-string($in as xs:base64Binary?, $encoding as xs:string) as xs:string? external;
declare %a:since("basex", "9.0") function bin:decode-string($in as xs:base64Binary?, $encoding as xs:string, $offset as xs:integer) as xs:string? external;
declare %a:since("basex", "9.0") function bin:decode-string($in as xs:base64Binary?, $encoding as xs:string, $offset as xs:integer, $size as xs:integer) as xs:string? external;
declare %a:since("basex", "9.0") function bin:encode-string($in as xs:string?, $encoding as xs:string) as xs:base64Binary? external;
declare %a:since("basex", "9.0") function bin:pack-double($in as xs:double) as xs:base64Binary external;
declare %a:since("basex", "9.0") function bin:pack-double($in as xs:double, $octet-order as xs:string) as xs:base64Binary external;
declare %a:since("basex", "9.0") function bin:pack-float($in as xs:float) as xs:base64Binary external;
declare %a:since("basex", "9.0") function bin:pack-float($in as xs:float, $octet-order as xs:string) as xs:base64Binary external;
declare %a:since("basex", "9.0") function bin:pack-integer($in as xs:integer, $size as xs:integer) as xs:base64Binary external;
declare %a:since("basex", "9.0") function bin:pack-integer($in as xs:integer, $size as xs:integer, $octet-order as xs:string) as xs:base64Binary external;
declare %a:since("basex", "9.0") function bin:unpack-double($in as xs:base64Binary, $offset as xs:integer) as xs:double external;
declare %a:since("basex", "9.0") function bin:unpack-double($in as xs:base64Binary, $offset as xs:integer, $octet-order as xs:string) as xs:double external;
declare %a:since("basex", "9.0") function bin:unpack-float($in as xs:base64Binary, $offset as xs:integer) as xs:float external;
declare %a:since("basex", "9.0") function bin:unpack-float($in as xs:base64Binary, $offset as xs:integer, $octet-order as xs:string) as xs:float external;
declare %a:since("basex", "9.0") function bin:unpack-integer($in as xs:base64Binary, $offset as xs:integer, $size as xs:integer) as xs:integer external;
declare %a:since("basex", "9.0") function bin:unpack-integer($in as xs:base64Binary, $offset as xs:integer, $size as xs:integer, $octet-order as xs:string) as xs:integer external;
declare %a:since("basex", "9.0") function bin:unpack-unsigned-integer($in as xs:base64Binary, $offset as xs:integer, $size as xs:integer) as xs:integer external;
declare %a:since("basex", "9.0") function bin:unpack-unsigned-integer($in as xs:base64Binary, $offset as xs:integer, $size as xs:integer, $octet-order as xs:string) as xs:integer external;


Annotate the EXPath zip functions with versioned type restrictions.


[xproc.preparsing] added a default pipeline name if none is supplied

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

xquery version "1.0" encoding "UTF-8";
module namespace naming = "http://xproc.net/xproc/naming";
(: ------------------------------------------------------------------------------------

	naming.xqm - manages the first pass parsing of xproc pipeline, providing the output
	in topological order and cross referencing step with defined functional signatures.

---------------------------------------------------------------------------------------- :)

(: XProc Namespace Declaration :)
declare namespace p="http://www.w3.org/ns/xproc";
declare namespace c="http://www.w3.org/ns/xproc-step";
declare namespace err="http://www.w3.org/ns/xproc-error";
declare namespace xsl="http://www.w3.org/1999/XSL/Transform";
declare namespace xproc = "http://xproc.net/xproc";
declare namespace xxq-error = "http://xproc.net/xproc/error";

(: Module Imports :)
import module namespace const = "http://xproc.net/xproc/const";
import module namespace u = "http://xproc.net/xproc/util";
import module namespace std = "http://xproc.net/xproc/std";
import module namespace opt = "http://xproc.net/xproc/opt";
import module namespace ext = "http://xproc.net/xproc/ext";

(: -------------------------------------------------------------------------- :)
(: returns step from std, opt and ext step definitions :)
(: -------------------------------------------------------------------------- :)
declare function naming:get-step($stepname as xs:string,$declarestep) {
    $const:std-steps/p:declare-step[@type=$stepname],
    $const:opt-steps/p:declare-step[@type=$stepname],
    $const:ext-steps/p:declare-step[@type=$stepname],
    $declarestep/@type
};

(: -------------------------------------------------------------------------- :)
(: returns step type :)
(: -------------------------------------------------------------------------- :)
declare function naming:type($stepname as xs:string,$is_declare-step) as xs:string {

    let $stdstep := $const:std-steps/p:declare-step[@type=$stepname]
    let $optstep := $const:opt-steps/p:declare-step[@type=$stepname]
    let $extstep := $const:ext-steps/p:declare-step[@type=$stepname]
    let $component :=$const:comp-steps//xproc:element[@type=$stepname]

    let $stdstepexists := exists($stdstep)
    let $optstepexists := exists($optstep)
    let $extstepexists := exists($extstep)
    let $compexists := exists($component)
    return
        if ($optstepexists) then
            'opt'
        else if($extstepexists) then
            'ext'
        else if($stdstepexists) then
            'std'
        else if($compexists) then
            'xproc'
        else if($is_declare-step) then
          string(substring-before($is_declare-step/@type,':'))
        else
          u:staticError('err:XS0044', concat($stepname,":",$stepname,' has no visible declaration'))
};

                     (: --------------------------------------------------------------------------- :)
                                                                              (: PREPARSE I ROUTINES:)
                     (: --------------------------------------------------------------------------- :)

declare function naming:preparse-options($allstep,$step,$stepname){
    for $option in $allstep
        return

            if ($step/p:with-option[@name=$option/@name] and $step/@*[name(.)=$option/@name]) then

               u:staticError('err:XS0027', concat($stepname,":",$step/@name,' option:',$option/@name,' duplicate options'))

            else if ($option/@required eq 'true' and $option/@select) then

               u:staticError('err:XS0017', concat($stepname,":",$step/@name,' option:',$option/@name,' duplicate options'))

            else if ($step/p:with-option[@name=$option/@name]) then

               <p:with-option name="{$option/@name}" select="{$step/p:with-option[@name=$option/@name]/@select}"/>

            else if($step/@*[name(.)=$option/@name]) then

               <p:with-option name="{$option/@name}" select="{concat("'",$step/@*[name(.)=$option/@name],"'")}"/>

            else if($option/@select) then

               <p:with-option name="{$option/@name}" select="{$option/@select}"/>

            else if(not($step/p:with-option[@name=$option/@name] and $step/@*[name(.)=$option/@name]) and $option/@required eq 'true') then

                u:staticError('err:XS0018', concat($stepname,":",$step/@name,' option:',$option/@name,' is required and seems to be missing or incorrect'))

            else
                (: TODO: may have to throw additional errors before this :)
                <p:with-option name="{$option/@name}" select="{$option/@default}"/>

};

declare function naming:preparse-input-bindings($allstep,$step,$allbindings){

if ($allbindings eq 'all') then
    $step/p:input
else
    for $binding in $allstep
       let $currentport := $step/*[@port=$binding/@port]
        return
            element {node-name($binding)} {
               $binding/@port,
               $binding/@primary,
               $binding/@kind,
               $binding/@sequence,

               if($currentport/@select='') then
                  attribute select{$binding/@select}
               else if($currentport/@select) then
                  $currentport/@select
               else
                  attribute select{$binding/@select},
                   $currentport/*
               }
};

declare function naming:preparse-output-bindings($allstep,$step,$allbindings){

if ($allbindings eq 'all') then
    $step/p:output
else
    for $binding in $allstep
       let $currentport := $step/*[@port=$binding/@port]
        return
            element {node-name($binding)} {
               $binding/@port,
               $binding/@primary,
               $binding/@kind,
               $binding/@sequence,
               if($currentport/@select='') then
                  attribute select{$binding/@select}
               else if($currentport/@select) then
                  $currentport/@select
               else
                  attribute select{$binding/@select},
                   $currentport/*
               }
};

declare function naming:generate-step($step,$stepname,$allstep){
if($allstep/@xproc:support) then
    element {node-name($step)} {
        attribute name{$step/@name},

		(: TODO - need to fix input/output preparse bindings :)
        naming:preparse-input-bindings($allstep/p:*[@primary='true'],$step,$allstep/@xproc:bindings),
        naming:preparse-output-bindings($allstep/p:*[@primary='false'],$step,$allstep/@xproc:bindings),
        naming:preparse-options($allstep/p:option,$step,$stepname)
   }

else
	u:xprocxqError('xxq-error:XXQ0002',concat($stepname,":",$step/@name,u:serialize($step,$const:TRACE_SERIALIZE)))

};

declare function naming:generate-component($xproc,$allcomp,$step,$stepname){
if($allcomp/@xproc:support) then
        element {node-name($step)} {
            if ($allcomp/@xproc:step = "true") then (attribute name{$step/@name},$step/@*[not(name(.) eq 'name')]) else  $step/@*,

            (: TODO - will need to fixup top level input/output ports :)
            <ext:pre>
				{naming:preparse-output-bindings($allcomp/p:*[@primary='true'],$step,())}
			</ext:pre>,

            naming:explicitnames(document{$step/*})
        }
else
	u:xprocxqError('xxq-error:XXQ0001',concat($stepname,":",$step/@name,u:serialize($step,$const:TRACE_SERIALIZE)))

};

declare function naming:pipeline-step-sort($unsorted, $sorted, $pipelinename )  {
    if (empty($unsorted)) then
        ($sorted)
    else
        let $allnodes := $unsorted [ every $id in p:input[@primary eq 'true'][@port eq 'source']/p:pipe/@step satisfies ($id = $sorted/@name or $id=$pipelinename)]
    return
        if ($allnodes) then
            naming:pipeline-step-sort( $unsorted except $allnodes, ($sorted, $allnodes ),$pipelinename)
        else
            ()
};

declare function naming:explicitnames($xproc as item()){

if(empty($xproc/*)) then
    ()
else
    let $pipelinename := $xproc/@name
    let $explicitnames :=

        for $step at $count in $xproc/*
            let $stepname := name($step)

            let $is_declare-step := $xproc/p:declare-step[@type=$stepname]
            let $is_step := naming:get-step($stepname,$is_declare-step)
            let $is_component := u:get-comp($stepname)

            return
               if ($is_declare-step) then
			   (: TODO - must re-enable generate-declare-step at some point :)
					()
               else if($is_step) then
                    naming:generate-step($step,$stepname,$is_step)
               else if ($is_component) then
                    naming:generate-component($xproc,$is_component,$step,$stepname)
               else
                    (: throws error on unknown element in pipeline namespace :)
                    u:staticError('err:XS0044', concat("static error during explicit naming pass:  ",$stepname,":",$step/@name,u:serialize($step,$const:TRACE_SERIALIZE)))
    return

		let $sorted := naming:pipeline-step-sort($explicitnames,(),$pipelinename)
		return
        	if(empty($pipelinename))then
    			$sorted
        	else
            	<p:declare-step name="{$pipelinename}">
                {
                    $sorted
                }
                <ext:post name="{$pipelinename}!">
					{$xproc/p:serialization/@*}
                    <p:input port="source" primary="true"/>
                    <p:output primary="true" port="stdout" select="/"/>
                </ext:post>
            	</p:declare-step>
};

(: -------------------------------------------------------------------------- :)
(: Fix up top level input/output with ext:pre                                 :)
(: -------------------------------------------------------------------------- :)
declare function naming:fixup($xproc as item(),$stdin){

let $pipeline := $xproc/p:*[name(.) = "p:pipeline" or name(.) ="p:declare-step"]
let $steps := <p:declare-step >
               <ext:pre name="!{$pipeline/@name}">
            {
            if ($pipeline/p:input[@primary='true']) then
                    $pipeline/p:input[@primary='true']
            else
                    <p:input port="source"
                             kind="document"
                             primary="true"
                             select="{if(empty($pipeline/p:input[@port='source']/@select)) then '/' else $pipeline/p:input[@port='source']/@select}"
                             sequence="{$pipeline/p:input[@port='source']/@sequence}">
                    {if($stdin) then
                        <p:pipe step="{$pipeline/@name}" port="stdin"/>
                    else
                        $pipeline/p:input[@port='source'][@primary="true"]
                    }
                    </p:input>,

            <p:output port="result" primary="true" select="{if ($pipeline/p:output[@port='result']/@select) then $pipeline/p:output[@port='result']/@select else '/' }"/>,

            $pipeline/p:input[@primary='false'],

            $pipeline/p:output[@primary='false']
            }
           </ext:pre>
            {
               for $import in $xproc/p:import
                return
                    if (doc-available($import/@href)) then
                          doc($import/@href)
                    else
                          u:dynamicError('XD0002',"cannot import pipeline document ")
            }

            {$pipeline/*[not(name(.)="p:input")][not(name(.)="p:output")]}

</p:declare-step>
return
    <p:declare-step name="{if ($pipeline/@name) then $pipeline/@name else 'pipeline'}">
        {$steps/*}
    </p:declare-step>
};

xquery version "1.0" encoding "UTF-8";
module namespace naming = "http://xproc.net/xproc/naming";
(: ------------------------------------------------------------------------------------

	naming.xqm - manages the first pass parsing of xproc pipeline, providing the output
	in topological order and cross referencing step with defined functional signatures.

---------------------------------------------------------------------------------------- :)

(: XProc Namespace Declaration :)
declare namespace p="http://www.w3.org/ns/xproc";
declare namespace c="http://www.w3.org/ns/xproc-step";
declare namespace err="http://www.w3.org/ns/xproc-error";
declare namespace xsl="http://www.w3.org/1999/XSL/Transform";
declare namespace xproc = "http://xproc.net/xproc";
declare namespace xxq-error = "http://xproc.net/xproc/error";

(: Module Imports :)
import module namespace const = "http://xproc.net/xproc/const";
import module namespace u = "http://xproc.net/xproc/util";
import module namespace std = "http://xproc.net/xproc/std";
import module namespace opt = "http://xproc.net/xproc/opt";
import module namespace ext = "http://xproc.net/xproc/ext";

(: -------------------------------------------------------------------------- :)
(: returns step from std, opt and ext step definitions :)
(: -------------------------------------------------------------------------- :)
declare function naming:get-step($stepname as xs:string,$declarestep) {
    $const:std-steps/p:declare-step[@type=$stepname],
    $const:opt-steps/p:declare-step[@type=$stepname],
    $const:ext-steps/p:declare-step[@type=$stepname],
    $declarestep/@type
};

(: -------------------------------------------------------------------------- :)
(: returns step type :)
(: -------------------------------------------------------------------------- :)
declare function naming:type($stepname as xs:string,$is_declare-step) as xs:string {

    let $stdstep := $const:std-steps/p:declare-step[@type=$stepname]
    let $optstep := $const:opt-steps/p:declare-step[@type=$stepname]
    let $extstep := $const:ext-steps/p:declare-step[@type=$stepname]
    let $component :=$const:comp-steps//xproc:element[@type=$stepname]

    let $stdstepexists := exists($stdstep)
    let $optstepexists := exists($optstep)
    let $extstepexists := exists($extstep)
    let $compexists := exists($component)
    return
        if ($optstepexists) then
            'opt'
        else if($extstepexists) then
            'ext'
        else if($stdstepexists) then
            'std'
        else if($compexists) then
            'xproc'
        else if($is_declare-step) then
          string(substring-before($is_declare-step/@type,':'))
        else
          u:staticError('err:XS0044', concat($stepname,":",$stepname,' has no visible declaration'))
};

                     (: --------------------------------------------------------------------------- :)
                                                                              (: PREPARSE I ROUTINES:)
                     (: --------------------------------------------------------------------------- :)

declare function naming:preparse-options($allstep,$step,$stepname){
    for $option in $allstep
        return

            if ($step/p:with-option[@name=$option/@name] and $step/@*[name(.)=$option/@name]) then

               u:staticError('err:XS0027', concat($stepname,":",$step/@name,' option:',$option/@name,' duplicate options'))

            else if ($option/@required eq 'true' and $option/@select) then

               u:staticError('err:XS0017', concat($stepname,":",$step/@name,' option:',$option/@name,' duplicate options'))

            else if ($step/p:with-option[@name=$option/@name]) then

               <p:with-option name="{$option/@name}" select="{$step/p:with-option[@name=$option/@name]/@select}"/>

            else if($step/@*[name(.)=$option/@name]) then

               <p:with-option name="{$option/@name}" select="{concat("'",$step/@*[name(.)=$option/@name],"'")}"/>

            else if($option/@select) then

               <p:with-option name="{$option/@name}" select="{$option/@select}"/>

            else if(not($step/p:with-option[@name=$option/@name] and $step/@*[name(.)=$option/@name]) and $option/@required eq 'true') then

                u:staticError('err:XS0018', concat($stepname,":",$step/@name,' option:',$option/@name,' is required and seems to be missing or incorrect'))

            else
                (: TODO: may have to throw additional errors before this :)
                <p:with-option name="{$option/@name}" select="{$option/@default}"/>

};

declare function naming:preparse-input-bindings($allstep,$step,$allbindings){

if ($allbindings eq 'all') then
    $step/p:input
else
    for $binding in $allstep
       let $currentport := $step/*[@port=$binding/@port]
        return
            element {node-name($binding)} {
               $binding/@port,
               $binding/@primary,
               $binding/@kind,
               $binding/@sequence,

               if($currentport/@select='') then
                  attribute select{$binding/@select}
               else if($currentport/@select) then
                  $currentport/@select
               else
                  attribute select{$binding/@select},
                   $currentport/*
               }
};

declare function naming:preparse-output-bindings($allstep,$step,$allbindings){

if ($allbindings eq 'all') then
    $step/p:output
else
    for $binding in $allstep
       let $currentport := $step/*[@port=$binding/@port]
        return
            element {node-name($binding)} {
               $binding/@port,
               $binding/@primary,
               $binding/@kind,
               $binding/@sequence,
               if($currentport/@select='') then
                  attribute select{$binding/@select}
               else if($currentport/@select) then
                  $currentport/@select
               else
                  attribute select{$binding/@select},
                   $currentport/*
               }
};

declare function naming:generate-step($step,$stepname,$allstep){
if($allstep/@xproc:support) then
    element {node-name($step)} {
        attribute name{$step/@name},

		(: TODO - need to fix input/output preparse bindings :)
        naming:preparse-input-bindings($allstep/p:*[@primary='true'],$step,$allstep/@xproc:bindings),
        naming:preparse-output-bindings($allstep/p:*[@primary='false'],$step,$allstep/@xproc:bindings),
        naming:preparse-options($allstep/p:option,$step,$stepname)
   }

else
	u:xprocxqError('xxq-error:XXQ0002',concat($stepname,":",$step/@name,u:serialize($step,$const:TRACE_SERIALIZE)))

};

declare function naming:generate-component($xproc,$allcomp,$step,$stepname){
if($allcomp/@xproc:support) then
        element {node-name($step)} {
            if ($allcomp/@xproc:step = "true") then (attribute name{$step/@name},$step/@*[not(name(.) eq 'name')]) else  $step/@*,

            (: TODO - will need to fixup top level input/output ports :)
            <ext:pre>
				{naming:preparse-output-bindings($allcomp/p:*[@primary='true'],$step,())}
			</ext:pre>,

            naming:explicitnames(document{$step/*})
        }
else
	u:xprocxqError('xxq-error:XXQ0001',concat($stepname,":",$step/@name,u:serialize($step,$const:TRACE_SERIALIZE)))

};

declare function naming:pipeline-step-sort($unsorted, $sorted, $pipelinename )  {
    if (empty($unsorted)) then
        ($sorted)
    else
        let $allnodes := $unsorted [ every $id in p:input[@primary eq 'true'][@port eq 'source']/p:pipe/@step satisfies ($id = $sorted/@name or $id=$pipelinename)]
    return
        if ($allnodes) then
            naming:pipeline-step-sort( $unsorted except $allnodes, ($sorted, $allnodes ),$pipelinename)
        else
            ()
};

declare function naming:explicitnames($xproc as item()){

if(empty($xproc/*)) then
    ()
else
    let $pipelinename := $xproc/@name
    let $explicitnames :=

        for $step at $count in $xproc/*
            let $stepname := name($step)

            let $is_declare-step := $xproc/p:declare-step[@type=$stepname]
            let $is_step := naming:get-step($stepname,$is_declare-step)
            let $is_component := u:get-comp($stepname)

            return
               if ($is_declare-step) then
			   (: TODO - must re-enable generate-declare-step at some point :)
					()
               else if($is_step) then
                    naming:generate-step($step,$stepname,$is_step)
               else if ($is_component) then
                    naming:generate-component($xproc,$is_component,$step,$stepname)
               else
                    (: throws error on unknown element in pipeline namespace :)
                    u:staticError('err:XS0044', concat("static error during explicit naming pass:  ",$stepname,":",$step/@name,u:serialize($step,$const:TRACE_SERIALIZE)))
    return

		let $sorted := naming:pipeline-step-sort($explicitnames,(),$pipelinename)
		return
        	if(empty($pipelinename))then
    			$sorted
        	else
            	<p:declare-step name="{$pipelinename}">
                {
                    $sorted
                }
                <ext:post name="{$pipelinename}!">
					{$xproc/p:serialization/@*}
                    <p:input port="source" primary="true"/>
                    <p:output primary="true" port="stdout" select="/"/>
                </ext:post>
            	</p:declare-step>
};

(: -------------------------------------------------------------------------- :)
(: Fix up top level input/output with ext:pre                                 :)
(: -------------------------------------------------------------------------- :)
declare function naming:fixup($xproc as item(),$stdin){

let $pipeline := $xproc/p:*[name(.) = "p:pipeline" or name(.) ="p:declare-step"]
let $steps := <p:declare-step >
               <ext:pre name="!{$pipeline/@name}">
            {
            if ($pipeline/p:input[@primary='true']) then
                    $pipeline/p:input[@primary='true']
            else
                    <p:input port="source"
                             kind="document"
                             primary="true"
                             select="{if(empty($pipeline/p:input[@port='source']/@select)) then '/' else $pipeline/p:input[@port='source']/@select}"
                             sequence="{$pipeline/p:input[@port='source']/@sequence}">
                    {if($stdin) then
                        <p:pipe step="{$pipeline/@name}" port="stdin"/>
                    else
                        $pipeline/p:input[@port='source'][@primary="true"]
                    }
                    </p:input>,

            <p:output port="result" primary="true" select="{if ($pipeline/p:output[@port='result']/@select) then $pipeline/p:output[@port='result']/@select else '/' }"/>,

            $pipeline/p:input[@primary='false'],

            $pipeline/p:output[@primary='false']
            }
           </ext:pre>
            {
               for $import in $xproc/p:import
                return
                    if (doc-available($import/@href)) then
                          doc($import/@href)
                    else
                          u:dynamicError('XD0002',"cannot import pipeline document ")
            }

            {$pipeline/*[not(name(.)="p:input")][not(name(.)="p:output")]}

</p:declare-step>
return
    <p:declare-step name="{$pipeline/@name}">
        {$steps/*}
    </p:declare-step>
};


Fixed namespace

Fixed namespace

module namespace renjin = 'http://github.com/james-jw/xq-renjin';
import module namespace r = 'org.jw.basex.renjin.xq-renjin-module';

(: Provided an R object, returns a map to traverse the object :)
declare function renjin:map($item) {(
    for $name in r:names($item)
    let $value := $item($name)
    return map {
      $name:
        if(r:is-r($value)) then renjin:map($value)
        else if(count($value) > 1) then
          let $rows := r:rownames($item) return
          if(count($rows) > 0) then map:merge(
            for $row at $i in $rows return
            map { $row : $value[$i] }
          )
          else array { $value }
        else $value
    }
  ) => map:merge()
};

(: Initializes an R engine :)
declare function renjin:init() as function(*) { r:init() };

(: Returns whether an object represents an R object or not. :)
declare function renjin:is($item) as xs:boolean { r:is-r($item) };

(: Column names:)
declare function renjin:names($item) as xs:string* { r:names($item) };

(: Row names :)
declare function renjin:rownames($item) as xs:string* { r:rownames($item) };

(: Executes an R expression within the R engine provided
 : @return The R engine provided. Useful in chaining.
 :)
declare function renjin:run($renjin, $expression as xs:string) as function(*) { r:run($renjin, $expression) };

(: Exeutes an R expression within the R engine provided, returning its value
 : @return The result of the R execution as an R object
 :)
declare function renjin:eval($renjin, $expression as xs:string, $map as xs:boolean) as item()* {
  let $out := r:eval($renjin, $expression)
  return if($map and r:is-r($out)) then renjin:map($out) else $out
};

(: Exeutes an R expression within the R engine provided, returning its value
 : @return The result of the R execution as a map
 :)
declare function renjin:eval($renjin, $expression as xs:string) {
  renjin:eval($renjin, $expression, true())
};

(: Adds a variables with the provided name and value to the R engine :)
declare function renjin:put($renjin, $var-name as xs:string, $expression as xs:string) as function(*) {
  r:put($renjin, $var-name, $expression)
};

module namespace renjin = 'http://xq-renjin';
import module namespace r = 'org.jw.basex.renjin.xq-renjin-module';

(: Provided an R object, returns a map to traverse the object :)
declare function renjin:map($item) {(
    for $name in r:names($item)
    let $value := $item($name)
    return map {
      $name:
        if(r:is-r($value)) then renjin:map($value)
        else if(count($value) > 1) then
          let $rows := r:rownames($item) return
          if(count($rows) > 0) then map:merge(
            for $row at $i in $rows return
            map { $row : $value[$i] }
          )
          else array { $value }
        else $value
    }
  ) => map:merge()
};

(: Initializes an R engine :)
declare function renjin:init() as function(*) { r:init() };

(: Returns whether an object represents an R object or not. :)
declare function renjin:is($item) as xs:boolean { r:is-r($item) };

(: Column names:)
declare function renjin:names($item) as xs:string* { r:names($item) };

(: Row names :)
declare function renjin:rownames($item) as xs:string* { r:rownames($item) };

(: Executes an R expression within the R engine provided
 : @return The R engine provided. Useful in chaining.
 :)
declare function renjin:run($renjin, $expression as xs:string) as function(*) { r:run($renjin, $expression) };

(: Exeutes an R expression within the R engine provided, returning its value
 : @return The result of the R execution as an R object
 :)
declare function renjin:eval($renjin, $expression as xs:string, $map as xs:boolean) as item()* {
  let $out := r:eval($renjin, $expression)
  return if($map and r:is-r($out)) then renjin:map($out) else $out
};

(: Exeutes an R expression within the R engine provided, returning its value
 : @return The result of the R execution as a map
 :)
declare function renjin:eval($renjin, $expression as xs:string) {
  renjin:eval($renjin, $expression, true())
};

(: Adds a variables with the provided name and value to the R engine :)
declare function renjin:put($renjin, $var-name as xs:string, $expression as xs:string) as function(*) {
  r:put($renjin, $var-name, $expression)
};