updated http-client documentation

updated http-client documentation

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

(:~
 :

 : This module provides an implementation of the
 : EXPath Http Client.
 : It provides functions for making HTTP requests and is a superset of the
 : module specified by EXPath.
 : Specifically, it implements the http:send-request() functions
 : as specified by EXPath. Moreover, it adds an additional function
 : http:read() (with several arities for the sake of ease).
 :

 :
 :

 : In general, both functions take a description of the HTTP request to make
 : as parameter, execute the request, and return a representation of the HTTP
 : response.
 :

 :
 :

 : The http:send-request() functions are declared as sequential.
 : Sequential functions are allowed to have side effects. For example, most probably,
 : an HTTP POST request is a request that has side effects because it adds/changes
 : a remote resource. Such functions are an extension of XQuery and are specified in
 : the XQuery Scripting Extension.
 : Sequential functions are only allowed to be invoked in certain places (e.g.
 : only from functions which are declared as sequential themselves).
 : In contrast, the http:read() functions are declared as
 : nondeterministic (see XQuery 1.1).
 : HTTP requests performed using these functions are not allowed to have
 : side effects.
 :

 :
 :

 : For almost all functions (except read with one parameter),
 : the request is represented by an http:request
 : element, specifying the URI, the HTTP method, the headers, and the body
 : content (for POST and PUT methods) of the HTTP request to make.
 :

 :
 :

 : The response is returned as a sequence of one or more items. The first
 : one is an http:response element with quite the same
 : structure as an http:request, but without the content itself.
 : The content is returned as the second item (or several items in case of
 : a multipart response) as a string, a document node, or a binary item.
 : This depends on the content-type returned.
 : Specifically, the rules are as follows:
 :
 :

 :
A document node is returned if the media type has a MIME type of
 :     text/xml, application/xml, text/xml-external-parsed-entity, or
 :     application/xml-external-parsed-entity, as defined in [RFC 3023]
 :     (except that application/xml-dtd is considered a text media type).
 :     MIME types ending by +xml are also XML media types.

 :
A document node is returned if the media type has a MIME type of
 :      text/html. In order to be able to make HTML parseable, tidy is automatically
 :      invoked. If you want to prevent that, you can also set your own content-type
 :      by setting the override-media-type attribute in the request element.
 :       For tidying, the following options
 :       will be used:
 :

 :
TidyXmlOut=yes

 :
TidyDoctypeMode=TidyDoctypeOmit

 :
TidyQuoteNbsp=yes

 :
TidyCharEncoding="utf8"

 :
TidyNewline="LF"

 :

 :

 :
A xs:string item is returned if the media type has a text MIME type,
 :     i.e. beginning with text/.

 :
A xs:base64Binary item is returned for all the other media types.

 :

 :

 :
 : Simple GET Request
 :
 :
 :

 :   http:read(
 :      <http:request href="www.example.com" method="get"/>
 :   )
 :

 :
 :
 : returns
 :
 :
 :

 :   <response xmlns="http://expath.org/ns/http-client" status="200" message="OK">
 :     <header name="Content-Type" value="text/html; charset=UTF-8"/>
 :     <header name="Content-Length" value="574"/>
 :     ...
 :     <body media-type="text/html"/>
 :   </response>
 :   <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
 :   <html>
 :     <head>
 :       <meta http-equiv="Content-Type"
 :       content="text/html; charset=utf-8" />
 :       <title>Example Web Page</title>
 :     </head>
 :     <body>
 :       <p>You have reached this web page by typing "example.com",
 :       "example.net", or "example.org" into your web browser.</p>
 :       <p>These domain names are reserved for use in documentation and are
 :       Not available for registration. See
 :       <a href="http://www.rfc-editor.org/rfc/rfc2606.txt">RFC 2606</a>,
 :       Section 3.</p>
 :     </body>
 :   </html>
 :

 :
 :
 : Simple POST Request
 :
 :

 : Here is a simple example which sends a text content by making an HTTP POST
 : request.
 :

 :
 :
 :

 :   http:send-request(
 :      http:request href="..." method="post">
 :         <http:body content-type="text/plain">
 :            Hello, world!
 :         </http:body>
 :      </http:request>
 :   )
 :

 :
 :
 : The response of this request can look as follows:
 :
 :

 :
 :   <http:response status="200" message="Ok">
 :      <http:header name="Content-Type" value="text/html"/>
 :      <http:header name="Server" value="Apache ..."/>
 :      ...
 :      <http:body content-type="text/html"/>
 :   </http:response>
 :
 :

 :
 :

 : The structure of a request element is defined in the schema that is imported
 : by this module. The details are described in the
 : specification.
 : Analogously, the response element is also described in this
 : specification.
 :

 :
 : @author Markus Pilman
 :)
module namespace http = "http://expath.org/ns/http-client";

import module namespace httpclientimpl = "http://expath.org/ns/httpclientimpl";
import module namespace err = "http://expath.org/ns/error";

import schema namespace https = "http://expath.org/ns/http-client";

(:~
 : This function sends an HTTP request and returns the corresponding response.
 :
 :

 : This function is declared as sequential (see XQuery Scripting).
 : Sequential functions are allowed to have side effects. For example, most probably,
 : an HTTP POST request is a request that has side effects because it adds/changes
 : a remote resource.
 :

 :
 : @param $request Contains the various parameters of the request.
 :   See the
 :   specification.
 :   for a full description of the structure of this element.
 : @param $href is the HTTP or HTTPS URI to send the request to. It must be a valid
 :  xs:anyURI, but is declared as a string to be able to pass literal strings
 :  (without requiring to explicitly cast it to an xs:anyURI.)
 : @param $content is the request body content, for HTTP methods that can
 :  contain a body in the request (i.e. POST and PUT). It is an error, if this
 :  param is not the empty sequence for methods other then DELETE, GET, HEAD
 :  and OPTIONS.
 : @return a sequence of items, where the first item is a element of type
 :  http:responseType. The response element is also described in the
 :  specification.
 :  If there is one (or several, in case of multipart) response body, the response bodies
 :  are the next items in the sequence.
 :)
declare sequential function http:send-request(
  $request as element(https:request)?,
  $href as xs:string?,
  $bodies as item()*) as item()+ {
  if (http:check-params($request, $href, $bodies))
  then
    let $req := if ($request) then validate { http:set-content-type($request) } else ()
    let $result := httpclientimpl:http-send-request-impl($req,
                                                         $href,
                                                         $bodies)
    return (validate {$result[1]}, fn:subsequence($result, 2))
  else ()
};

(:~
 : Function for convenience.
 :
 : The result of this function is equal to calling the send-request function
 : as follows:
 :
 :
 : http:send-request($request, (), ())
 :
 :
 : @see documentation of send-request with three parameters.
 :
 : @param $request see request parameter of the sequential
 :  send-request function with three parameters.
 : @return see return value of the sequential
 :  send-request function with three parameters.
 :)
declare sequential function http:send-request (
  $request as element(https:request)) as item()+ {
  http:send-request($request, (), ())
};

(:~
 : Function for convenience.
 :
 : The result of this function is equal to calling the send-request function
 : as follows:
 :
 :
 : http:send-request($request, $href, ())
 :
 :
 : @see documentation of send-request with three parameters.
 :
 : @param $request see request parameter of the sequential
 :  send-request function with three parameters.
 : @param $href see href parameter of the sequential
 :  send-request function with three parameters.
 : @return see return of
 :  send-request
 :)
declare sequential function http:send-request(
  $request as element(https:request)?,
  $href as xs:string?) as item()+ {
  http:send-request($request, $href, ())
};

(:~
 : This function sends an HTTP request and returns the corresponding response.
 :
 :

 : This function is not sequential. Usually, this function should be used
 : for retrieving resources using HTTP GET or HEAD (i.e. HTTP requests which
 : are known to not have any side effects.
 :

 :
 : @param $request Contains the various parameters of the request.
 :   See the
 :   specification.
 :   for a full description of the structure of this element.
 : @param $href is the HTTP or HTTPS URI to send the request to. It must be a valid
 :  xs:anyURI, but is declared as a string to be able to pass literal strings
 :  (without requiring to explicitly cast it to an xs:anyURI.)
 : @param $bodies is the request body content, for HTTP methods that can
 :  contain a body in the request (i.e. POST and PUT). It is an error, if this
 :  param is not the empty sequence for methods other then DELETE, GET, HEAD
 :  and OPTIONS.
 : @return a sequence of items, where the first item is a element of type
 :  http:responseType. The response element is also described in the
 :  specification.
 :  If there is one (or several, in case of multipart) response body, the response bodies
 :  are the next items in the sequence.
 :)
declare function http:read(
  $request as element(https:request)?,
  $href as xs:string?,
  $bodies as item()*) as item()+ {
  if (http:check-params($request, $href, $bodies))
  then
    let $req := if ($request) then validate { http:set-content-type($request) } else ()
    let $result := httpclientimpl:http-read-impl($req, $href, $bodies)
    return (validate {$result[1]}, fn:subsequence($result, 2))
  else ()
};

(:~
 : Function for convenience.
 :
 : The result of this function is equal to calling the read function as follows:
 :
 :
 : http:read($request, $href, ())
 :
 :
 : @see documentation of read
 :      with three parameters.
 :
 : @param $request see parameter $request of read with three parameters
 : @param $href see parameter $href of read with three parameters
 : @return see return of read with three parameters
 :)
declare function http:read(
  $request as element(https:request)?,
  $href as xs:string?) as item()+ {
  http:read($request, $href, ())
};

(:~
 : Function for convenience.
 :
 : The result of this function is equal to calling the read function
 : as follows:
 :
 :
 : http:read((), $href, ())
 :
 :
 : @see documentation of read with three parameters.
 :
 : @param $href see parameter $href of read with three parameters
 : @return see return of read with three parameters
 :)
declare function http:read(
  $href as xs:string) as item()+ {
  http:read((), $href, ())
};

(:~
 : Helper function.
 :
 : This function takes an https:body element, copies it, and
 : adds a method attribute to the copy if there is none
 : in the original element.
 :
 : @param $body is the original https:body element
 : @return a https:body element with a corresponding @method
 :   attribute
 :)
declare function http:create-body (
  $body as element(https:body))
    as element(https:body) {
  <https:body>{$body/@*}
  {
    if ($body/@method) then
      ()
    else
      attribute method {
        if ($body/@media-type eq "text/xml" or
            $body/@media-type eq "application/xml" or
            $body/@media-type eq "text/xml-external-parsed-entity" or
            $body/@media-type eq "application/xml-external-parsed-entity") then
              "xml"
         else if ($body/@media-type eq "text/html") then "html"
         else if (fn:starts-with($body/@media-type/data(.), "text/")) then "text"
         else "binary"
      }
  }
  {$body/node()}
  </https:body>
};

(:~
 : Helper function.
 :
 : This function takes an https:multipart element, copies it and
 : adds a @method attribute to all body elements which don't have
 : one.
 :
 : @param $multipart the original https:multipart
 : @return a copy of $multipart with all $multipart/body/@method set
 :)
declare function http:create-multipart (
  $multipart as element(https:multipart))
    as element(https:multipart) {
  validate {
    <https:multipart>{$multipart/@*}
    {
    for $x in $multipart/node()
    return
      typeswitch($x)
        case element(https:body) return http:create-body($x)
        default return $x
    }
    </https:multipart>
  }
};

(:~
 : Helper function.
 :
 : This adds a default method attribute to all body elements which
 : don't contain a method attribute.
 :
 : @param $request The request which need to be copied.
 : @return A copy of $request with all request//body/@method set.
 :)
declare function http:set-content-type(
  $request as element(https:request)?)
    as element(https:request)? {
  if ($request) then
    validate
    {
      <http:request>{$request/@*}
      {
      for $x in $request/node()
      return
        typeswitch($x)
          case element(https:body) return http:create-body($x)
          case element(https:multipart) return http:create-multipart($x)
          default return $x
      }
      </http:request>
    }
  else ()
};

(:~
 : Private helper function used internally by this module.
 :
 : This function checks if the request, href, and bodies parameters
 : are consistent.
 :
 : @param $request The request which needs to be checked.
 : @param $href The href which needs to be checked.
 : @param $bodies The bodies element which needs to be checked.
 : @return true if the parameters are consistent. Otherwise,
 :         this function raises an error.
 :)
declare function http:check-params(
  $request as element(https:request)?,
  $href as xs:string?,
  $bodies as item()*) as xs:boolean {
  if (fn:empty($href) and fn:empty($request)) then
    fn:error($err:HCV01, "Not all required arguments are set.")
  else if ($href eq "") then
    fn:error($err:HCV01,
      "The href value is set to the empty string")
  else if (not(count($request//body[not(exists(node()))]) eq count($bodies))) then
    fn:error($err:HCV01,
      "The number of bodies without children is not equal the size of the bodies sequence")
  else
    fn:true()
};

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

(:~
 :

 : The module provides functions making HTTP request.
 : Basically, the modules defines two functions, http:send-request()
 : and http:read() (both with several arities for the sake of ease
 : of use.) In general, these functions take a description of the HTTP request
 : to make as parameter, execute the request, and return a representation of
 : the HTTP response.
 :

 :
 :

 : The http:send-request() functions are declared as sequential.
 : Sequential functions are allowed to have side effects. For example, most probably,
 : an HTTP POST request is a request that has side effects because it adds/changes
 : a remote resource.
 : In contrast, the http:read() functions are declared as nondeterministic.
 : HTTP requests performed using these functions are not allowed to have
 : side-effects.
 :

 :
 :

 : For all functions, the request is represented by an http:request
 : element, specifying the URI, the HTTP method, the headers, and the body
 : content (for POST and PUT methods).
 :

 :
 :

 : The response is returned as a sequence of one or more items. The first
 : one is an http:response element with quite the same
 : structure as an http:request, but without the content itself.
 : The content is returned as the second item (or several items in case of
 : a multipart response) as a string, a document node, or a binary item.
 : This depends on the content-type returned.
 : Specifically, the rules are as follows:
 :
 :

 :
A document node is returned if the media type has a MIME type of
 :     text/xml, application/xml, text/xml-external-parsed-entity, or
 :     application/xml-external-parsed-entity, as defined in [RFC 3023]
 :     (except that application/xml-dtd is considered a text media type).
 :     MIME types ending by +xml are also XML media types.

 :
A document node is returned if the media type has a MIME type of
 :      text/html. In order to be able to make HTML parseable, tidy is automatically
 :      invoked. If you want to prevent that, you can also set your own content-type
 :      by setting the override-media-type attribute in the request element.
 :       For tidying, the following options
 :       will be used:
 :

 :
TidyXmlOut=yes

 :
TidyDoctypeMode=TidyDoctypeOmit

 :
TidyQuoteNbsp=yes

 :
TidyCharEncoding="utf8"

 :
TidyNewline="LF"

 :

 :

 :
A xs:string item is returned if the media type has a text MIME type,
 :     i.e. beginning with text/.

 :
A xs:base64Binary item is returned for all the other media types.

 :

 :

 :
 :

 : Here is a simple example of call in XQuery, which sends a text content
 : by making an HTTP POST request.
 :

 :
 :
 :

 :   http:send-request(
 :      http:request href="..." method="post">
 :         <http:body content-type="text/plain">
 :            Hello, world!
 :         </http:body>
 :      </http:request>
 :   )
 :

 :
 :
 : The response of this request can look as follows:
 :
 :

 :
 :   <http:response status="200" message="Ok">
 :      <http:header name="Content-Type" value="text/html"/>
 :      <http:header name="Server" value="Apache ..."/>
 :      ...
 :      <http:body content-type="text/html"/>
 :   </http:response>
 :
 :

 :
 :

 : The structure of a request element is defined in the schema that is imported
 : by this module. The details are described in the
 : specification.
 : Analogously, the response element is also described in this
 : specification.
 :

 :
 :

 : Please note, that this module is an extension to the module specified
 : by EXPath because it adds some convenience functions and the nondeterministic
 : read() functions.
 :

 :
 : @author Markus Pilman
 :)
module namespace http = "http://expath.org/ns/http-client";

import module namespace httpclientimpl = "http://expath.org/ns/httpclientimpl";
import module namespace err = "http://expath.org/ns/error";

import schema namespace https = "http://expath.org/ns/http-client";

(:~
 : This function sends an HTTP request and returns the corresponding response.
 :
 :

 : This function is declared as sequential (see XQuery Scripting).
 : Sequential functions are allowed to have side effects. For example, most probably,
 : an HTTP POST request is a request that has side effects because it adds/changes
 : a remote resource.
 :

 :
 : @param $request Contains the various parameters of the request.
 :   See the
 :   specification.
 :   for a full description of the structure of this element.
 : @param $href is the HTTP or HTTPS URI to send the request to. It must be a valid
 :  xs:anyURI, but is declared as a string to be able to pass literal strings
 :  (without requiring to explicitly cast it to an xs:anyURI.)
 : @param $content is the request body content, for HTTP methods that can
 :  contain a body in the request (i.e. POST and PUT). It is an error, if this
 :  param is not the empty sequence for methods other then DELETE, GET, HEAD
 :  and OPTIONS.
 : @return a sequence of items, where the first item is a element of type
 :  http:responseType. The response element is also described in the
 :  specification.
 :  If there is one (or several, in case of multipart) response body, the response bodies
 :  are the next items in the sequence.
 :)
declare sequential function http:send-request(
  $request as element(https:request)?,
  $href as xs:string?,
  $bodies as item()*) as item()+ {
  if (http:check-params($request, $href, $bodies))
  then
    let $req := if ($request) then validate { http:set-content-type($request) } else ()
    let $result := httpclientimpl:http-send-request-impl($req,
                                                         $href,
                                                         $bodies)
    return (validate {$result[1]}, fn:subsequence($result, 2))
  else ()
};

(:~
 : Function for convenience.
 :
 : The result of this function is equal to calling the send-request function
 : as follows:
 :
 :
 : http:send-request($request, (), ())
 :
 :
 : @see documentation of send-request with three parameters.
 :
 : @param $request see request parameter of the sequential
 :  send-request function with three parameters.
 : @return see return value of the sequential
 :  send-request function with three parameters.
 :)
declare sequential function http:send-request (
  $request as element(https:request)) as item()+ {
  http:send-request($request, (), ())
};

(:~
 : Function for convenience.
 :
 : The result of this function is equal to calling the send-request function
 : as follows:
 :
 :
 : http:send-request($request, $href, ())
 :
 :
 : @see documentation of send-request with three parameters.
 :
 : @param $request see request parameter of the sequential
 :  send-request function with three parameters.
 : @param $href see href parameter of the sequential
 :  send-request function with three parameters.
 : @return see return of
 :  send-request
 :)
declare sequential function http:send-request(
  $request as element(https:request)?,
  $href as xs:string?) as item()+ {
  http:send-request($request, $href, ())
};

(:~
 : This function sends an HTTP request and returns the corresponding response.
 :
 :

 : This function is not sequential. Usually, this function should be used
 : for retrieving resources using HTTP GET or HEAD (i.e. HTTP requests which
 : are known to not have any side effects.
 :

 :
 : @param $request Contains the various parameters of the request.
 :   See the
 :   specification.
 :   for a full description of the structure of this element.
 : @param $href is the HTTP or HTTPS URI to send the request to. It must be a valid
 :  xs:anyURI, but is declared as a string to be able to pass literal strings
 :  (without requiring to explicitly cast it to an xs:anyURI.)
 : @param $content is the request body content, for HTTP methods that can
 :  contain a body in the request (i.e. POST and PUT). It is an error, if this
 :  param is not the empty sequence for methods other then DELETE, GET, HEAD
 :  and OPTIONS.
 : @return a sequence of items, where the first item is a element of type
 :  http:responseType. The response element is also described in the
 :  specification.
 :  If there is one (or several, in case of multipart) response body, the response bodies
 :  are the next items in the sequence.
 :)
declare function http:read(
  $request as element(https:request)?,
  $href as xs:string?,
  $bodies as item()*) as item()+ {
  if (http:check-params($request, $href, $bodies))
  then
    let $req := if ($request) then validate { http:set-content-type($request) } else ()
    let $result := httpclientimpl:http-read-impl($req, $href, $bodies)
    return (validate {$result[1]}, fn:subsequence($result, 2))
  else ()
};

(:~
 : Function for convenience.
 :
 : The result of this function is equal to calling the read function as follows:
 :
 :
 : http:read($request, $href, ())
 :
 :
 : @see documentation of read
 :      with three parameters.
 :
 : @param $request see parameter $request of read with three parameters
 : @param href see parameter $href of read with three parameters
 : @return see return of read with three parameters
 :)
declare function http:read(
  $request as element(https:request)?,
  $href as xs:string?) as item()+ {
  http:read($request, $href, ())
};

(:~
 : Function for convenience.
 :
 : The result of this function is equal to calling the read function
 : as follows:
 :
 :
 : http:read((), $href, ())
 :
 :
 : @see documentation of read with three parameters.
 :
 : @param $request see parameter $request of read with three parameters
 : @return see return of read with three parameters
 :)
declare function http:read(
  $href as xs:string) as item()+ {
  http:read((), $href, ())
};

(:~
 : Helper function.
 :
 : This function takes an https:body element, copies it, and
 : adds a method attribute to the copy if there is none
 : in the original element.
 :
 : @param $body is the original https:body element
 : @return a https:body element with a corresponding @method
 :   attribute
 :)
declare function http:create-body (
  $body as element(https:body))
    as element(https:body) {
  <https:body>{$body/@*}
  {
    if ($body/@method) then
      ()
    else
      attribute method {
        if ($body/@media-type eq "text/xml" or
            $body/@media-type eq "application/xml" or
            $body/@media-type eq "text/xml-external-parsed-entity" or
            $body/@media-type eq "application/xml-external-parsed-entity") then
              "xml"
         else if ($body/@media-type eq "text/html") then "html"
         else if (fn:starts-with($body/@media-type/data(.), "text/")) then "text"
         else "binary"
      }
  }
  {$body/node()}
  </https:body>
};

(:~
 : Helper function.
 :
 : This function takes an https:multipart element, copies it and
 : adds a @method attribute to all body elements which don't have
 : one.
 :
 : @param $multipart the original https:multipart
 : @return a copy of $multipart with all $multipart/body/@method set
 :)
declare function http:create-multipart (
  $multipart as element(https:multipart))
    as element(https:multipart) {
  validate {
    <https:multipart>{$multipart/@*}
    {
    for $x in $multipart/node()
    return
      typeswitch($x)
        case element(https:body) return http:create-body($x)
        default return $x
    }
    </https:multipart>
  }
};

(:~
 : Helper function.
 :
 : This adds a default method attribute to all body elements which
 : don't contain a method attribute.
 :
 : @param $request The request which need to be copied.
 : @return A copy of $request with all request//body/@method set.
 :)
declare function http:set-content-type(
  $request as element(https:request)?)
    as element(https:request)? {
  if ($request) then
    validate
    {
      <http:request>{$request/@*}
      {
      for $x in $request/node()
      return
        typeswitch($x)
          case element(https:body) return http:create-body($x)
          case element(https:multipart) return http:create-multipart($x)
          default return $x
      }
      </http:request>
    }
  else ()
};

(:~
 : Private helper function used internally by this module.
 :
 : This function checks if the request, href, and bodies parameters
 : are consistent.
 :
 : @param $request The request which needs to be checked.
 : @param $href The href which needs to be checked.
 : @param $href The bodies element which needs to be checked.
 : @return true if the parameters are consistent. Otherwise,
 :         this function raises an error.
 :)
declare function http:check-params(
  $request as element(https:request)?,
  $href as xs:string?,
  $bodies as item()*) as xs:boolean {
  if (fn:empty($href) and fn:empty($request)) then
    fn:error($err:HCV01, "Not all required arguments are set.")
  else if ($href eq "") then
    fn:error($err:HCV01,
      "The href value is set to the empty string")
  else if (not(count($request//body[not(exists(node()))]) eq count($bodies))) then
    fn:error($err:HCV01,
      "The number of bodies without children is not equal the size of the bodies sequence")
  else
    fn:true()
};


Fix bug in path redirect

Fix bug in path redirect

xquery version "1.0" encoding "UTF-8";
(:~
  eXist Webapp URL rewriting controller for the eXist book browser
:)
(:============================================================================:)

(: eXist URL rewriting controller external variables: :)
declare variable $exist:path external;
declare variable $exist:resource external;
declare variable $exist:controller external;
declare variable $exist:prefix external;
declare variable $exist:root external;

(:============================================================================:)
(:== DO THE REDIRECTS/FORWARDS: :)

  (: See if we have an empty path. If so, redirect straight to the browser page: :)
  (: Take care to distinguish between a path ending in a / or not! :)
  if ($exist:path eq '')
    then
      <dispatch xmlns="http://exist.sourceforge.net/NS/exist">
        <redirect url="exist-book/book-browser/browse"/>
      </dispatch>
  else if ($exist:path eq '/')
    then
      <dispatch xmlns="http://exist.sourceforge.net/NS/exist">
        <redirect url="book-browser/browse"/>
      </dispatch>

  (: If it has no extension or .xq, assume its an XQuery script and pass control to this: :)
  else if (not(contains($exist:resource, '.')) or ends-with($exist:resource, '.xq'))
    then
      let $forward-url as xs:string := concat($exist:controller, $exist:path)
      let $full-forward-url as xs:string :=
       if (not(contains($exist:resource, '.')))
         then concat($forward-url, '.xq')
         else $forward-url
      return
        <dispatch xmlns="http://exist.sourceforge.net/NS/exist">
          <forward url="{$full-forward-url}"/>
        </dispatch>

  (: Anything else, pass through: :)
  else
    <dispatch xmlns="http://exist.sourceforge.net/NS/exist">
      <cache-control cache="yes"/>
    </dispatch>

(:============================================================================:)

xquery version "1.0" encoding "UTF-8";
(:~
  eXist Webapp URL rewriting controller for the eXist book browser
:)
(:============================================================================:)

(: eXist URL rewriting controller external variables: :)
declare variable $exist:path external;
declare variable $exist:resource external;
declare variable $exist:controller external;
declare variable $exist:prefix external;
declare variable $exist:root external;

(:============================================================================:)
(:== DO THE REDIRECTS/FORWARDS: :)

  (: See if we have an empty path. If so, redirect straight to the browser page: :)
  (: Take care to distinguish between a path ending in a / or not! :)
  if ($exist:path eq '')
    then
      <dispatch xmlns="http://exist.sourceforge.net/NS/exist">
        <redirect url="eXist-book/book-browser/browse"/>
      </dispatch>
  else if ($exist:path eq '/')
    then
      <dispatch xmlns="http://exist.sourceforge.net/NS/exist">
        <redirect url="book-browser/browse"/>
      </dispatch>

  (: If it has no extension or .xq, assume its an XQuery script and pass control to this: :)
  else if (not(contains($exist:resource, '.')) or ends-with($exist:resource, '.xq'))
    then
      let $forward-url as xs:string := concat($exist:controller, $exist:path)
      let $full-forward-url as xs:string :=
       if (not(contains($exist:resource, '.')))
         then concat($forward-url, '.xq')
         else $forward-url
      return
        <dispatch xmlns="http://exist.sourceforge.net/NS/exist">
          <forward url="{$full-forward-url}"/>
        </dispatch>

  (: Anything else, pass through: :)
  else
    <dispatch xmlns="http://exist.sourceforge.net/NS/exist">
      <cache-control cache="yes"/>
    </dispatch>

(:============================================================================:)


[bugfix] .xconf needs to be loaded *before* the document to be indexed.

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

xquery version "1.0";
(: $Id$ :)
(:
    Module: examples setup
:)

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

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

declare function setup:main() as element()
{

Examples Setup

        {
let $action := request:get-parameter("action", ()) return
		if($action) then
		                (
		                    if($action eq "Import Example Data") then
		                    (
		                        setup:importLocal()
		                    )
		                    else if($action eq "Import Remote Files") then
		                    (
		                        setup:importFromURLs()
		                    )
		                    else
		                    (
		                        setup:report()
		                    )
		                )
		                else
		                (
		                    setup:select()
		                )

        }

};

declare function setup:importLocal() as element()+
{
    let $home := system:get-exist-home(),
    $pathSep := util:system-property("file.separator"),
    $dir := if (doc-available(concat("file:///", $home, "/exist-samples/examples.xml"))) then
        (
            concat($home, $pathSep, "exist-samples")
        )
        else if(ends-with($home, "WEB-INF")) then
        (
            concat(substring-before($home, "WEB-INF"), "samples")
        )
        else
        (
            concat($home, $pathSep, "samples")
        )
    return (
                setup:create-collection("/db", "shakespeare"),
                setup:create-collection("/db/shakespeare", "plays"),
                setup:store-files("/db/shakespeare/plays", $dir,  "shakespeare/*.xml", "text/xml"),
                setup:store-files("/db/shakespeare/plays", $dir, "shakespeare/*.xsl", "text/xml"),
                setup:store-files("/db/shakespeare/plays", $dir, "shakespeare/*.css", "text/css"),
                setup:create-collection("/db", "xinclude"),
                setup:create-collection("/db/xinclude", "styles"),
                setup:store-files("/db/xinclude", $dir, "xinclude/*.xsl", "text/xml"),
                setup:store-files("/db/xinclude", $dir, "xinclude/*.xml", "text/xml"),
                setup:store-files("/db/xinclude", $dir, "xinclude/*.xq", "application/xquery"),
                setup:store-files("/db/xinclude", $dir, "xinclude/*.jpg", "image/jpeg"),
                setup:store-files("/db/xinclude/styles", $dir, "xinclude/styles/*", "text/css"),
                setup:create-collection("/db/xinclude", "scripts"),
                setup:create-collection("/db/xinclude/scripts", "syntax"),
                setup:store-files("/db/xinclude/scripts/syntax", $dir, "xinclude/scripts/syntax/*.js", "application/x-javascript"),
                setup:create-collection("/db", "library"),
                setup:store-files("/db/library", $dir, "*.rdf", "text/xml"),
                setup:create-collection("/db", "mods"),
                setup:store-files("/db/mods", $dir, "mods/*.xml", "text/xml"),
                setup:store-files("/db", $dir, "*.xml", "text/xml"),
                setup:create-collection("/db/system/config", "db"),
                setup:create-collection("/db/system/config/db", "mondial"),
                setup:store-files("/db/system/config/db/mondial", $dir, "mondial.xconf", "text/xml")
    )
};

declare function setup:importFromURLs() as element()+
{
    (
        setup:report(),

Actions:

            {
                let $includeXmlad := request:get-parameter("xmlad", ()),
                $includeMondial := request:get-parameter("mondial", ()),
 				$includeLocal := request:get-parameter("local", ()) return
                (
					if($includeLocal) then
					( setup:importLocal()
					)
					else (),
                    if($includeXmlad) then
                    (
                        setup:create-collection("/db", "xmlad"),
                        setup:load-URL("/db/xmlad", xs:anyURI("http://surfnet.dl.sourceforge.net/sourceforge/xmlad/xmlad.xml"), "xmlad.xml")
                    )else (),
                    if($includeMondial) then
                    (
                        setup:create-collection("/db", "mondial"),
                        setup:load-URL("/db/mondial", xs:anyURI("http://www.dbis.informatik.uni-goettingen.de/Mondial/mondial-europe.xml"), "mondial.xml")
                    )else ()
                )
            }

    )
};

declare function setup:load-URL($collection as xs:string, $url as xs:anyURI, $docName as xs:string) as element()
{
    let $x := xdb:store($collection, $docName, $url) return

File {$docName} imported from url: {$url}

};

declare function setup:store-files($collection as xs:string, $home as xs:string, $patterns as xs:string, $mimeType as xs:string) as element()*
{
    let $stored := xdb:store-files-from-pattern($collection, $home, $patterns, $mimeType)
    for $doc in $stored return

Uploaded: {$doc}

};

declare function setup:create-collection($parent as xs:string, $name as xs:string) as element()
{
    let $col := xdb:create-collection($parent, $name) return

Created collection: {$col}

};

declare function setup:select() as element()
{
    <form action="{session:encode-url(request:get-uri())}" method="post">

eXist ships with a number of XQuery examples. Some of these
        require certain documents to be stored in the database. The XQuery examples also use some
		XML data not included with the distribution which can be downloaded by selecting
		the checkboxes below.

		<input type="checkbox" name="local" checked="true"/>
		eXist-db shipped files

        <input type="checkbox" name="xmlad"/>
        The XML Acronym Demystifier
        (approx. 384K)

        <input type="checkbox" name="mondial"/>
        The Mondial Database (Europe)
        (approx. 410K)

<input type="submit" name="action" value="Import Remote Files"/>
        <input type="submit" name="action" value="Skip"/>

        <input type="hidden" name="panel" value="setup"/>
    </form>
};

declare function setup:report() as element()
{

Files have been loaded. You can now go to the
    examples page.

};

xquery version "1.0";
(: $Id$ :)
(:
    Module: examples setup
:)

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

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

declare function setup:main() as element()
{

Examples Setup

        {
let $action := request:get-parameter("action", ()) return
		if($action) then
		                (
		                    if($action eq "Import Example Data") then
		                    (
		                        setup:importLocal()
		                    )
		                    else if($action eq "Import Remote Files") then
		                    (
		                        setup:importFromURLs()
		                    )
		                    else
		                    (
		                        setup:report()
		                    )
		                )
		                else
		                (
		                    setup:select()
		                )

        }

};

declare function setup:importLocal() as element()+
{
    let $home := system:get-exist-home(),
    $pathSep := util:system-property("file.separator"),
    $dir := if (doc-available(concat("file:///", $home, "/exist-samples/examples.xml"))) then
        (
            concat($home, $pathSep, "exist-samples")
        )
        else if(ends-with($home, "WEB-INF")) then
        (
            concat(substring-before($home, "WEB-INF"), "samples")
        )
        else
        (
            concat($home, $pathSep, "samples")
        )
    return (
                setup:create-collection("/db", "shakespeare"),
                setup:create-collection("/db/shakespeare", "plays"),
                setup:store-files("/db/shakespeare/plays", $dir,  "shakespeare/*.xml", "text/xml"),
                setup:store-files("/db/shakespeare/plays", $dir, "shakespeare/*.xsl", "text/xml"),
                setup:store-files("/db/shakespeare/plays", $dir, "shakespeare/*.css", "text/css"),
                setup:create-collection("/db", "xinclude"),
                setup:create-collection("/db/xinclude", "styles"),
                setup:store-files("/db/xinclude", $dir, "xinclude/*.xsl", "text/xml"),
                setup:store-files("/db/xinclude", $dir, "xinclude/*.xml", "text/xml"),
                setup:store-files("/db/xinclude", $dir, "xinclude/*.xq", "application/xquery"),
                setup:store-files("/db/xinclude", $dir, "xinclude/*.jpg", "image/jpeg"),
                setup:store-files("/db/xinclude/styles", $dir, "xinclude/styles/*", "text/css"),
                setup:create-collection("/db/xinclude", "scripts"),
                setup:create-collection("/db/xinclude/scripts", "syntax"),
                setup:store-files("/db/xinclude/scripts/syntax", $dir, "xinclude/scripts/syntax/*.js", "application/x-javascript"),
                setup:create-collection("/db", "library"),
                setup:store-files("/db/library", $dir, "*.rdf", "text/xml"),
                setup:create-collection("/db", "mods"),
                setup:store-files("/db/mods", $dir, "mods/*.xml", "text/xml"),
                setup:store-files("/db", $dir, "*.xml", "text/xml"),
                setup:create-collection("/db/system/config", "db"),
                setup:create-collection("/db/system/config/db", "mondial"),
                setup:store-files("/db/system/config/db/mondial", $dir, "mondial.xconf", "text/xml")
    )
};

declare function setup:importFromURLs() as element()+
{
    (
        setup:report(),

Actions:

            {
                let $includeXmlad := request:get-parameter("xmlad", ()),
                $includeMondial := request:get-parameter("mondial", ()),
 				$includeLocal := request:get-parameter("local", ()) return
                (
                    if($includeXmlad) then
                    (
                        setup:create-collection("/db", "xmlad"),
                        setup:load-URL("/db/xmlad", xs:anyURI("http://surfnet.dl.sourceforge.net/sourceforge/xmlad/xmlad.xml"), "xmlad.xml")
                    )else (),
                    if($includeMondial) then
                    (
                        setup:create-collection("/db", "mondial"),
                        setup:load-URL("/db/mondial", xs:anyURI("http://www.dbis.informatik.uni-goettingen.de/Mondial/mondial-europe.xml"), "mondial.xml")
                    )else (),
					if($includeLocal) then
					( setup:importLocal()
					)
					else ()
                )
            }

    )
};

declare function setup:load-URL($collection as xs:string, $url as xs:anyURI, $docName as xs:string) as element()
{
    let $x := xdb:store($collection, $docName, $url) return

File {$docName} imported from url: {$url}

};

declare function setup:store-files($collection as xs:string, $home as xs:string, $patterns as xs:string, $mimeType as xs:string) as element()*
{
    let $stored := xdb:store-files-from-pattern($collection, $home, $patterns, $mimeType)
    for $doc in $stored return

Uploaded: {$doc}

};

declare function setup:create-collection($parent as xs:string, $name as xs:string) as element()
{
    let $col := xdb:create-collection($parent, $name) return

Created collection: {$col}

};

declare function setup:select() as element()
{
    <form action="{session:encode-url(request:get-uri())}" method="post">

eXist ships with a number of XQuery examples. Some of these
        require certain documents to be stored in the database. The XQuery examples also use some
		XML data not included with the distribution which can be downloaded by selecting
		the checkboxes below.

		<input type="checkbox" name="local" checked="true"/>
		eXist-db shipped files

        <input type="checkbox" name="xmlad"/>
        The XML Acronym Demystifier
        (approx. 384K)

        <input type="checkbox" name="mondial"/>
        The Mondial Database (Europe)
        (approx. 410K)

<input type="submit" name="action" value="Import Remote Files"/>
        <input type="submit" name="action" value="Skip"/>

        <input type="hidden" name="panel" value="setup"/>
    </form>
};

declare function setup:report() as element()
{

Files have been loaded. You can now go to the
    examples page.

};


[bugfix] numeric is not a XDM type, should be xs:numeric

xquery version "3.0";

module namespace fd="http://exist-db.org/xquery/test/format-numbers";

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

(:~
 : A picture-string must not contain more than one instance of the
 : pattern-separator character
 :)
declare
    %test:args("0;0;0")
    %test:assertError("FODF1310")
function fd:invalid-picture-multiple-pattern-separator($picture) {
    format-number(12345.6, $picture)
};

(:~
 : A sub-picture must not contain more than one instance of the
 : decimal-separator character
 :)
declare
    %test:args("0.0.0")
    %test:assertError("FODF1310")
    %test:args("0.0.0;0.0")
    %test:assertError("FODF1310")
    %test:args("0.0;0.0.0")
    %test:assertError("FODF1310")
function fd:invalid-picture-multiple-decimal-separator($picture) {
    format-number(12345.6, $picture)
};

(:~
 : A sub-picture must not contain more than one instance of the percent
 : or per-mille characters, and it must not contain one of each
 :)
declare
    %test:args("0%%")
    %test:assertError("FODF1310")
    %test:args("0%%;0")
    %test:assertError("FODF1310")
    %test:args("0;0%%")
    %test:assertError("FODF1310")
    %test:args("0‰‰;0")
    %test:assertError("FODF1310")
    %test:args("0;0‰‰")
    %test:assertError("FODF1310")
    %test:args("0;0‰‰")
    %test:assertError("FODF1310")
    %test:args("0%‰")
    %test:assertError("FODF1310")
    %test:args("0‰%")
    %test:assertError("FODF1310")
    %test:args("0%‰;0")
    %test:assertError("FODF1310")
    %test:args("0‰%;0")
    %test:assertError("FODF1310")
    %test:args("0;0%‰")
    %test:assertError("FODF1310")
    %test:args("0;0‰%")
    %test:assertError("FODF1310")
function fd:invalid-picture-multiple-percent-permille($picture) {
    format-number(12345.6, $picture)
};

(:~
 : The mantissa part of a sub-picture must contain at least one character that
 : is either an optional digit character or a member of the decimal digit family
 :)
declare
    %test:args(".")
    %test:assertError("FODF1310")
    %test:args("0.0;.")
    %test:assertError("FODF1310")
    %test:args(".;0.0")
    %test:assertError("FODF1310")
function fd:invalid-picture-mantissa($picture) {
    format-number(12345.6, $picture)
};

(:~
 : A sub-picture must not contain a passive character that is preceded by an
 : active character and that is followed by another active character.
 :)
declare
    %test:args("#a#;#")
    %test:assertError("FODF1310")
    %test:args("#;#a#")
    %test:assertError("FODF1310")
function fd:invalid-picture-active-passive-active($picture) {
    format-number(12345.6, $picture)
};

(:~
 : A sub-picture must not contain a grouping-separator character that appears
 : adjacent to a decimal-separator character, or in the absence of
 : decimal-separator character, at the end of the integer part.
 :)
declare
    %test:args("###,.;#")
    %test:assertError("FODF1310")
    %test:args("#;###,.")
    %test:assertError("FODF1310")
    %test:args("###.,;#")
    %test:assertError("FODF1310")
    %test:args("#;###.,")
    %test:assertError("FODF1310")
    %test:args("###,")
    %test:assertError("FODF1310")
    %test:args("###,;#")
    %test:assertError("FODF1310")
    %test:args("#;###,")
    %test:assertError("FODF1310")
function fd:invalid-picture-grouping-separator-1($picture) {
    format-number(12345.6, $picture)
};

(:~
 : A sub-picture must not contain two adjacent instances of the
 : grouping-separator character.
 :)
declare
    %test:args("###,,###;#")
    %test:assertError("FODF1310")
    %test:args("#;###,,###")
    %test:assertError("FODF1310")
function fd:invalid-picture-grouping-separator-2($picture) {
    format-number(12345.6, $picture)
};

(:~
 : The integer part of a sub-picture must not contain a member of the
 : decimal digit family that is followed by an instance of the
 : optional digit character. The fractional part of a sub-picture
 : must not contain an instance of the optional digit character that
 : is followed by a member of the decimal digit family.
 :)
declare
    %test:args("1#;#")
    %test:assertError("FODF1310")
    %test:args("#;1#")
    %test:assertError("FODF1310")
    %test:args("##.#1;#")
    %test:assertError("FODF1310")
    %test:args("#;##.#1")
    %test:assertError("FODF1310")
function fd:invalid-picture-decimal-digit-and-optional-digit($picture) {
    format-number(12345.6, $picture)
};

(:~
 : A character that matches the exponent-separator property is treated as an
 : exponent-separator-sign if it is both preceded and followed within the
 : sub-picture by an active character. Otherwise, it is treated as a passive
 : character. A sub-picture must not contain more than one character that is
 : treated as an exponent-separator-sign.
 :)
declare
    %test:args("###ee##;#")
    %test:assertError("FODF1310")
    %test:args("#;###ee##")
    %test:assertError("FODF1310")
function fd:invalid-picture-exponent-separator-1($picture) {
    format-number(12345.6, $picture)
};

(:~
 : A sub-picture that contains a percent or per-mille character must not
 : contain a character treated as an exponent-separator-sign.
 :)
declare
    %test:args("###e%##;#")
    %test:assertError("FODF1310")
    %test:args("#;###e%##")
    %test:assertError("FODF1310")
    %test:args("###e‰##;#")
    %test:assertError("FODF1310")
    %test:args("#;###e‰##")
    %test:assertError("FODF1310")
    %test:args("#.#%e;#")
    %test:assertError("FODF1310")
    %test:args("#;#.#%e")
    %test:assertError("FODF1310")
    %test:args("#.#‰e;#")
    %test:assertError("FODF1310")
    %test:args("#;#.#‰e")
    %test:assertError("FODF1310")
function fd:invalid-picture-exponent-separator-2($picture) {
    format-number(12345.6, $picture)
};

(:~
 : If a sub-picture contains a character treated as an exponent-separator-sign
 : then this must be followed by one or more characters that are members of
 : the decimal digit family, and it must not be followed by any active
 : character that is not a member of the decimal digit family.
 :)
declare
    %test:args("##e.;#")
    %test:assertError("FODF1310")
    %test:args("#;##e.")
    %test:assertError("FODF1310")
function fd:invalid-picture-exponent-separator-3($picture) {
    format-number(12345.6, $picture)
};

declare
    %test:pending("It is not clear why this should be invalid according to the XQ3.1 spec")
    %test:args("12345.6")
    %test:assertError("FODF1310")
function fd:invalid-picture($number as xs:numeric) {
    format-number($number, "#.###,00")
};

declare
    %test:args(1000000)
    %test:args("000,000,000")
    %test:assertEquals("001,000,000")
function fd:grouping-integer($number as xs:integer, $picture) {
    format-number($number, $picture)
};

declare
    %test:args(1.234567890)
    %test:args("0.000,000,000")
    %test:assertEquals("1.234,567,890")
function fd:grouping-fraction($number as xs:double, $picture) {
    format-number($number, $picture)
};

declare
    %test:args("12345.6")
    %test:args("#,###.##")
    %test:assertEquals("12,345.6")
function fd:simple-number-1($number as numeric, $picture) {
    format-number($number, $picture)
};

declare
    %test:args(87504.4812)
    %test:args("000,000.000000")
    %test:assertEquals("087,504.481200")
function fd:simple-number-2($number, $picture) {
    format-number($number, $picture)
};

declare
    %test:args(12345.6)
    %test:args("#.###,##")
    %test:assertEquals("12345.6")
function fd:simple-number-3($number as numeric, $picture) {
    format-number($number, $picture)
};

declare
    %test:args(14)
    %test:args("00%")
    %test:assertEquals("1400%")
function fd:simple-number-integer-percent($number as xs:integer, $picture) {
    format-number($number, $picture)
};

declare
    %test:args(0.14)
    %test:args("01%")
    %test:assertEquals("14%")
function fd:simple-number-fractional-percent($number as xs:double, $picture) {
    format-number($number, $picture)
};

declare
    %test:args("#0")
    %test:assertEquals("0")
    %test:args("#0.0")
    %test:assertEquals("0.0")
    %test:args("#0.00")
    %test:assertEquals("0.00")
    %test:args("#0.000")
    %test:assertEquals("0.000")
    %test:args("#0.0#")
    %test:assertEquals("0.0")
function fd:decimal-zeros($picture as xs:string) {
    format-number(0, $picture)
};

xquery version "3.0";

module namespace fd="http://exist-db.org/xquery/test/format-numbers";

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

(:~
 : A picture-string must not contain more than one instance of the
 : pattern-separator character
 :)
declare
    %test:args("0;0;0")
    %test:assertError("FODF1310")
function fd:invalid-picture-multiple-pattern-separator($picture) {
    format-number(12345.6, $picture)
};

(:~
 : A sub-picture must not contain more than one instance of the
 : decimal-separator character
 :)
declare
    %test:args("0.0.0")
    %test:assertError("FODF1310")
    %test:args("0.0.0;0.0")
    %test:assertError("FODF1310")
    %test:args("0.0;0.0.0")
    %test:assertError("FODF1310")
function fd:invalid-picture-multiple-decimal-separator($picture) {
    format-number(12345.6, $picture)
};

(:~
 : A sub-picture must not contain more than one instance of the percent
 : or per-mille characters, and it must not contain one of each
 :)
declare
    %test:args("0%%")
    %test:assertError("FODF1310")
    %test:args("0%%;0")
    %test:assertError("FODF1310")
    %test:args("0;0%%")
    %test:assertError("FODF1310")
    %test:args("0‰‰;0")
    %test:assertError("FODF1310")
    %test:args("0;0‰‰")
    %test:assertError("FODF1310")
    %test:args("0;0‰‰")
    %test:assertError("FODF1310")
    %test:args("0%‰")
    %test:assertError("FODF1310")
    %test:args("0‰%")
    %test:assertError("FODF1310")
    %test:args("0%‰;0")
    %test:assertError("FODF1310")
    %test:args("0‰%;0")
    %test:assertError("FODF1310")
    %test:args("0;0%‰")
    %test:assertError("FODF1310")
    %test:args("0;0‰%")
    %test:assertError("FODF1310")
function fd:invalid-picture-multiple-percent-permille($picture) {
    format-number(12345.6, $picture)
};

(:~
 : The mantissa part of a sub-picture must contain at least one character that
 : is either an optional digit character or a member of the decimal digit family
 :)
declare
    %test:args(".")
    %test:assertError("FODF1310")
    %test:args("0.0;.")
    %test:assertError("FODF1310")
    %test:args(".;0.0")
    %test:assertError("FODF1310")
function fd:invalid-picture-mantissa($picture) {
    format-number(12345.6, $picture)
};

(:~
 : A sub-picture must not contain a passive character that is preceded by an
 : active character and that is followed by another active character.
 :)
declare
    %test:args("#a#;#")
    %test:assertError("FODF1310")
    %test:args("#;#a#")
    %test:assertError("FODF1310")
function fd:invalid-picture-active-passive-active($picture) {
    format-number(12345.6, $picture)
};

(:~
 : A sub-picture must not contain a grouping-separator character that appears
 : adjacent to a decimal-separator character, or in the absence of
 : decimal-separator character, at the end of the integer part.
 :)
declare
    %test:args("###,.;#")
    %test:assertError("FODF1310")
    %test:args("#;###,.")
    %test:assertError("FODF1310")
    %test:args("###.,;#")
    %test:assertError("FODF1310")
    %test:args("#;###.,")
    %test:assertError("FODF1310")
    %test:args("###,")
    %test:assertError("FODF1310")
    %test:args("###,;#")
    %test:assertError("FODF1310")
    %test:args("#;###,")
    %test:assertError("FODF1310")
function fd:invalid-picture-grouping-separator-1($picture) {
    format-number(12345.6, $picture)
};

(:~
 : A sub-picture must not contain two adjacent instances of the
 : grouping-separator character.
 :)
declare
    %test:args("###,,###;#")
    %test:assertError("FODF1310")
    %test:args("#;###,,###")
    %test:assertError("FODF1310")
function fd:invalid-picture-grouping-separator-2($picture) {
    format-number(12345.6, $picture)
};

(:~
 : The integer part of a sub-picture must not contain a member of the
 : decimal digit family that is followed by an instance of the
 : optional digit character. The fractional part of a sub-picture
 : must not contain an instance of the optional digit character that
 : is followed by a member of the decimal digit family.
 :)
declare
    %test:args("1#;#")
    %test:assertError("FODF1310")
    %test:args("#;1#")
    %test:assertError("FODF1310")
    %test:args("##.#1;#")
    %test:assertError("FODF1310")
    %test:args("#;##.#1")
    %test:assertError("FODF1310")
function fd:invalid-picture-decimal-digit-and-optional-digit($picture) {
    format-number(12345.6, $picture)
};

(:~
 : A character that matches the exponent-separator property is treated as an
 : exponent-separator-sign if it is both preceded and followed within the
 : sub-picture by an active character. Otherwise, it is treated as a passive
 : character. A sub-picture must not contain more than one character that is
 : treated as an exponent-separator-sign.
 :)
declare
    %test:args("###ee##;#")
    %test:assertError("FODF1310")
    %test:args("#;###ee##")
    %test:assertError("FODF1310")
function fd:invalid-picture-exponent-separator-1($picture) {
    format-number(12345.6, $picture)
};

(:~
 : A sub-picture that contains a percent or per-mille character must not
 : contain a character treated as an exponent-separator-sign.
 :)
declare
    %test:args("###e%##;#")
    %test:assertError("FODF1310")
    %test:args("#;###e%##")
    %test:assertError("FODF1310")
    %test:args("###e‰##;#")
    %test:assertError("FODF1310")
    %test:args("#;###e‰##")
    %test:assertError("FODF1310")
    %test:args("#.#%e;#")
    %test:assertError("FODF1310")
    %test:args("#;#.#%e")
    %test:assertError("FODF1310")
    %test:args("#.#‰e;#")
    %test:assertError("FODF1310")
    %test:args("#;#.#‰e")
    %test:assertError("FODF1310")
function fd:invalid-picture-exponent-separator-2($picture) {
    format-number(12345.6, $picture)
};

(:~
 : If a sub-picture contains a character treated as an exponent-separator-sign
 : then this must be followed by one or more characters that are members of
 : the decimal digit family, and it must not be followed by any active
 : character that is not a member of the decimal digit family.
 :)
declare
    %test:args("##e.;#")
    %test:assertError("FODF1310")
    %test:args("#;##e.")
    %test:assertError("FODF1310")
function fd:invalid-picture-exponent-separator-3($picture) {
    format-number(12345.6, $picture)
};

declare
    %test:pending("It is not clear why this should be invalid according to the XQ3.1 spec")
    %test:args("12345.6")
    %test:assertError("FODF1310")
function fd:invalid-picture($number as numeric) {
    format-number($number, "#.###,00")
};

declare
    %test:args(1000000)
    %test:args("000,000,000")
    %test:assertEquals("001,000,000")
function fd:grouping-integer($number as xs:integer, $picture) {
    format-number($number, $picture)
};

declare
    %test:args(1.234567890)
    %test:args("0.000,000,000")
    %test:assertEquals("1.234,567,890")
function fd:grouping-fraction($number as xs:double, $picture) {
    format-number($number, $picture)
};

declare
    %test:args("12345.6")
    %test:args("#,###.##")
    %test:assertEquals("12,345.6")
function fd:simple-number-1($number as numeric, $picture) {
    format-number($number, $picture)
};

declare
    %test:args(87504.4812)
    %test:args("000,000.000000")
    %test:assertEquals("087,504.481200")
function fd:simple-number-2($number, $picture) {
    format-number($number, $picture)
};

declare
    %test:args(12345.6)
    %test:args("#.###,##")
    %test:assertEquals("12345.6")
function fd:simple-number-3($number as numeric, $picture) {
    format-number($number, $picture)
};

declare
    %test:args(14)
    %test:args("00%")
    %test:assertEquals("1400%")
function fd:simple-number-integer-percent($number as xs:integer, $picture) {
    format-number($number, $picture)
};

declare
    %test:args(0.14)
    %test:args("01%")
    %test:assertEquals("14%")
function fd:simple-number-fractional-percent($number as xs:double, $picture) {
    format-number($number, $picture)
};

declare
    %test:args("#0")
    %test:assertEquals("0")
    %test:args("#0.0")
    %test:assertEquals("0.0")
    %test:args("#0.00")
    %test:assertEquals("0.00")
    %test:args("#0.000")
    %test:assertEquals("0.000")
    %test:args("#0.0#")
    %test:assertEquals("0.0")
function fd:decimal-zeros($picture as xs:string) {
    format-number(0, $picture)
};


Revert "Fix position query for probes."

Revert "Fix position query for probes."

This reverts commit 2b242afb153555a99bcefa96b9c26d607a7fc0ad.

; datasetProbeValues
(fn [dataset samples probes]
  (let [probemap (:probemap (car (query {:select [:probemap]
                                         :from [:dataset]
                                         :where [:= :name dataset]})))
        position (if probemap
                    (xena-query {:select ["name" "position"] :from [probemap] :where [:in :name probes]})
                    nil)]
    [position
     (fetch [{:table dataset
              :columns probes
              :samples samples}])]))

; datasetProbeValues
(fn [dataset samples probes]
  (let [probemap (:probemap (car (query {:select [:probemap]
                                         :from [:dataset]
                                         :where [:= :name dataset]})))
        position (if probemap
                    ((xena-query {:select ["name" "position"]
                                  :from [probemap]
                                  :where [:in "name" probes]}) "position")
                    nil)]
    [position
     (fetch [{:table dataset
              :columns probes
              :samples samples}])]))