[apps biblo]  Here is a revised version of the save.  It has been modified to save the entire instance on "new" and then update the document after the store.  Still in development but new saves are now working.

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

xquery version "1.0";

(: XQuery script to save a new MODS record from an incomming HTTP POST :)

import module namespace style = "http://exist-db.org/mods-style" at "../../../modules/style.xqm";

declare namespace xf="http://www.w3.org/2002/xforms";
declare namespace xforms="http://www.w3.org/2002/xforms";
declare namespace ev="http://www.w3.org/2001/xml-events";
declare namespace mods="http://www.loc.gov/mods/v3";
declare default element namespace "http://www.loc.gov/mods/v3";

let $title := 'MODS Record Save'

(: this service takes an incomming POST and saves the appropriate records :)
(: note that if the incomming mods:identifier in missing a new file will automatically be created :)
(: the user must have write and update access to the data collection :)

let $app-collection := $style:db-path-to-app
let $data-collection := $style:db-path-to-app-data

(: this is where the form "POSTS" documents to this XQuery using the POST method of a submission :)
let $item := request:get-data()

(: check to see if we have an indentifier in the incomming post :)
let $incomming-id := $item/mods:identifier/text()

(: if we do not have an ID then save the full template file in a new file with a UUID as the file name :)
let $file-id :=
if ( string-length($incomming-id) = 0 )
   then
        let $new-id := util:uuid()
        let $file-name := concat($new-id, '.xml')
        let $new-instance-path := concat($app-collection, '/edit/new-instance.xml')
        let $new-instance := doc($new-instance-path)
        let $login := xmldb:login($data-collection, 'admin', 'admin123')
        let $store := xmldb:store($data-collection, $file-name, $new-instance)
        let $debug-message := concat('New mods record', $file-name, 'Created OK')
        let $log := util:log-app('DEBUG', 'mods-editor', $debug-message)
        return $new-id
   else $incomming-id

(: else we are doing an update to an existing file :)

let $file-to-update := concat($file-id, '.xml')
let $file-path := concat($data-collection, '/', $file-to-update)
let $doc := doc($file-path)/mods:mods

(: If the incoming has any part then we update it in the document :)
let $titleInfo := if ($item/mods:titleInfo) then update replace $doc/mods:titleInfo with $item/mods:titleInfo else ()
let $name := if ($item/name) then update replace $doc/name with $item/name else ()
let $originInfo := if ($item/originInfo) then update replace $doc/originInfo with $item/originInfo else ()
let $physicalDescription := if ($item/physicalDescription) then update replace $doc/physicalDescription with $item/physicalDescription else ()
let $targetAudience := if ($item/targetAudience) then update replace $doc/targetAudience with $item/targetAudience else ()
let $language := if ($item/language) then update replace $doc/part5 with $item/part5 else ()
let $typeOfResource := if ($item/typeOfResource) then update replace $doc/part5 with $item/part5 else ()
let $genre := if ($item/genre) then update replace $doc/part5 with $item/part5 else ()
let $subject := if ($item/subject) then update replace $doc/subject with $item/subject else ()
let $classification := if ($item/classification) then update replace $doc/classification with $item/classification else ()
let $abstract := if ($item/abstract) then update replace $doc/abstract with $item/abstract else ()
let $table-of-contents := if ($item/table-of-contents) then update replace $doc/table-of-contents with $item/table-of-contents else ()
let $note := if ($item/note) then update replace $doc/note with $item/note else ()
let $related := if ($item/related) then update replace $doc/related with $item/related else ()
let $identifier := if ($item/identifier) then update replace $doc/identifier with $item/identifier else ()
let $record-info := if ($item/record-info) then update replace $doc/record-info with $item/record-info else ()
let $access-condition := if ($item/access-condition) then update replace $doc/access-condition with $item/access-condition

return
<results>
  <message>Update Status = OK</message>
</results>

xquery version "1.0";

(: XQuery script to save a new MODS record from an incomming HTTP POST :)

import module namespace style = "http://exist-db.org/mods-style" at "../../../modules/style.xqm";

declare namespace xf="http://www.w3.org/2002/xforms";
declare namespace xforms="http://www.w3.org/2002/xforms";
declare namespace ev="http://www.w3.org/2001/xml-events";
declare namespace mods="http://www.loc.gov/mods/v3";
declare default element namespace "http://www.loc.gov/mods/v3";

let $title := 'MODS Record Save'

(: this service takes an incomming POST and saves the appropriate records :)
(: note that if the incomming mods:identifier in missing a new file will automatically be created :)
(: the user must have write and update access to the data collection :)

let $app-collection := $style:db-path-to-app
let $data-collection := $style:db-path-to-app-data

(: this is where the form "POSTS" documents to this XQuery using the POST method of a submission :)
let $item := request:get-data()

(: check to see if we have an indentifier in the incomming post :)
let $incomming-id := $item/mods:identifier/text()

return
if ( string-length($incomming-id) = 0 )
   then
        let $new-id := util:uuid()
        let $file-name := concat($new-id, '.xml')
        let $login := xmldb:login($data-collection, 'admin', '')
        let $store := xmldb:store($data-collection, $file-name, $item)
        let $message := <results><message> file {$file-name} created OK </message></results>
        return $message
   else

(: else we are doing an update to an existing file :)

let $file-to-update := concat($incomming-id, '.xml')
let $file-path := concat($data-collection, '/', $file-to-update)
let $doc := doc($file-path)/mods:mods

(: If the incoming has any part then we update it in the document :)
let $titleInfo := if ($item/mods:titleInfo) then update replace $doc/mods:titleInfo with $item/mods:titleInfo else ()

(:
let $name := if ($item/name) then update replace $doc/name with $item/name else ()
let $originInfo := if ($item/originInfo) then update replace $doc/originInfo with $item/originInfo else ()
let $physicalDescription := if ($item/physicalDescription) then update replace $doc/physicalDescription with $item/physicalDescription else ()
let $targetAudience := if ($item/targetAudience) then update replace $doc/targetAudience with $item/targetAudience else ()
let $language := if ($item/language) then update replace $doc/part5 with $item/part5 else ()
let $typeOfResource := if ($item/typeOfResource) then update replace $doc/part5 with $item/part5 else ()
let $genre := if ($item/genre) then update replace $doc/part5 with $item/part5 else ()
let $subject := if ($item/subject) then update replace $doc/subject with $item/subject else ()
let $classification := if ($item/classification) then update replace $doc/classification with $item/classification else ()
let $abstract := if ($item/abstract) then update replace $doc/abstract with $item/abstract else ()
let $table-of-contents := if ($item/table-of-contents) then update replace $doc/table-of-contents with $item/table-of-contents else ()
let $note := if ($item/note) then update replace $doc/note with $item/note else ()
let $related := if ($item/related) then update replace $doc/related with $item/related else ()
let $identifier := if ($item/identifier) then update replace $doc/identifier with $item/identifier else ()
let $record-info := if ($item/record-info) then update replace $doc/record-info with $item/record-info else ()
let $access-condition := if ($item/access-condition) then update replace $doc/access-condition with $item/access-condition
:)

return
<results>
  <message>Save Status = OK</message>
</results>


adding zero-or-one function calls could fix the test

(: doc("document_1.xml")/site/people/person[string-length(translate(concat(address/street,address/city,address/country,address/zipcode)," ","")) > 30] :)
doc("document_1.xml")/site/people/person[string-length(translate(concat(zero-or-one(address/street),zero-or-one(address/city),zero-or-one(address/country),zero-or-one(address/zipcode))," ","")) > 30]


Cosmetic changes to documentation of xqxq:query-plan and xqxq:load-from-query-plan functions.

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

(:~
 : This module contains functions to compile and evaluate XQuery
 : programs. Also, it contains function that allow to parameterize
 : the static or dynamic evaluation phase.
 :
 : @author Juan Zacarias
 : @project Zorba/Programming Languages/XQuery
 :)
module namespace xqxq = 'http://www.zorba-xquery.com/modules/xqxq';

declare namespace an = "http://www.zorba-xquery.com/annotations";
declare namespace ver = "http://www.zorba-xquery.com/options/versioning";
declare namespace op = "http://www.zorba-xquery.com/options/features";
declare namespace f = "http://www.zorba-xquery.com/features";

declare option ver:module-version "1.0";

(:~
 : The function prepares a given XQuery program for execution.
 : If the program was successfully compiled, the function returns an
 : identifier as xs:anyURI. This URI can be passed to other functions
 : of this module (e.g. to actually evaluate the program). The URI
 : is opaque and its lilfetime is bound by the lifetime of the XQuery
 : program that invoked this function. Further reference or uses
 : of the identifier lead to unexpected results.
 :
 : Successfully prepared queries need to be deleted by passing the resulting
 : identifier to the xqxq:delete-query function of this module.
 :
 : @param $main-module-text the XQuery program that should be prepared.
 :   The program needs to be a XQuery main module.
 :
 : @return an identifier for the compiled program that can be passed
 :   as arguments to other functions of this module.
 :
 : @error any (static or type) error that may be raised during the compilation
 : of the query. For example, err:XPST0003 if the given XQuery program could
 : not be parsed.
 :)
declare %an:sequential function xqxq:prepare-main-module($main-module-text as xs:string) as
  xs:anyURI external;

(:~
 : The function prepares a given XQuery program for execution.
 : If the program was successfully compiled, the function returns an
 : identifier as xs:anyURI. This URI can be passed to other functions
 : of this module (e.g. to actually evaluate the program). The URI
 : is opaque and its lilfetime is bound by the lifetime of the XQuery
 : program that invoked this function. Further reference or uses
 : of the identifier lead to unexpected results.
 :
 : Important notes regarding the second and third parameters of the function:
 : --------------------------------------------------------------------------
 :
 : These parameters allow you to specify a URL resolver and a URI mapper
 : for Zorba to use when executing this query. See
 : http://www.zorba-xquery.com/html/documentation/2.7.0/zorba/uriresolvers
 :
 : The second parameter is a function item for a URL
 : resolver. The URL resolver function must recive 2 parameters:
 :   A $namespace as xs:string that will contain the url to be resolved.
 :   A $entity as xs:string that will contain the type of resolving needed;
 :   this can be 2 values "module" and "schema".
 : The function must return an empty sequence when the specified $namespace
 : or $entity are not the ones to be resolved.
 :
 : Example:
 :
 : declare function mymod:url-resolver($namespace as xs:string, $entity as xs:string)
 : {
 :  if($namespace = 'http://test.xq')
 :  then "module namespace test = 'http://test'; declare function test:foo(){'foo'};"
 :  else ()
 : };
 :
 : The URL resolver function's namespace, name, and parameter naming are
 : not restricted by XQXQ.
 :
 : The URL resolver function's return type is not restricted, it could be a string, a sequence,
 : a node, etc. All the outputs types are to be serialized as a string.
 :
 : The third parameter is a function item for a URI mapper.
 : The URI mapper function, just like the URL resolver, receives 2 parameters:
 :   A $namespace as xs:string that will contain the URI to be mapped.
 :   A $entity as xs:string that will contain the type of resolving needed;
 :   this can be 2 values "module" and "schema".
 : The URI mapper must return an empty sequence when the specified $namesapce or $entity
 : are not to be mapped. Unlike the URL resolver this function must return a sequence of strings.
 :
 : Example:
 :
 : declare function mymod:uri-mapper($namespace as xs:string, $entity as xs:string)
 : {
 :  if($namespace = 'http://test')
 :  then ("http://www.zorba-xquery.com/test", "http://foo.com/schema/test")
 :  else ()
 : };
 :
 : The URI mapper function's namespace, name, and parameter naming are
 : not restricted by XQXQ.
 :
 : In order to pass the above URL resolver and URI mapper to this function,
 : use the following syntax:
 :
 :   variable $queryID := xqxq:prepare-main-module("..query text..",
 :      mymod:url-resolver#2, mymod:uri-mapper#2);
 :
 : That is, the QName of the function followed by "#2". This is XQuery
 : "higher-order function" syntax, meaning the function with the specified
 : QName which takes two arguments. Since URL resolvers and URI mappers
 : must take two arguments, both will always be specified with "#2".
 :
 : Note that parameters 2 and 3 should be declared as follows:
 :    as function($url as xs:string, $entity as xs:string) as item()
 :    as function($uri as xs:string, $entity as xs:string) as xs:string*
 : However Zorba's implementation of higher-order functions (HOF) is not
 : yet complete enough to allow for this. When Zorba's HOF implementation
 : is complete this function signature will be changed.
 :
 : Both the URL resolver and URI mapper functions are optional, meaning you
 : may pass the empty-sequence () for either.
 :
 : Successfully prepared queries need to be deleted by passing the resulting
 : identifier to the xqxq:delete-query function of this module.
 :
 : @param $main-module-text the XQuery program that should be prepared.
 :   The program needs to be a XQuery main module.
 :
 : @param $resolver the URL resolver function.
 :
 : @param $mapper the URI mapper function.
 :
 : @return an identifier for the compiled program that can be passed
 :   as arguments to other functions of this module.
 :
 : @error any (static or type) error that may be raised during the compilation
 : of the query. For example, err:XPST0003 if the given XQuery program could
 : not be parsed.
 :)
declare %an:sequential function xqxq:prepare-main-module($main-module-text as xs:string, $resolver as item()?, $mapper as item()?) as
  xs:anyURI external;

(:~
 : This function compiles a given XQuery library module. It can be used
 : to compile-check a module.
 :
 : @param $library-module-text the XQuery library module that should
 :  be prepared.
 :
 : @return the function is declared as sequential.It returns the
 :  empty-sequence.
 :
 : @error any (static or type) error that may be raised during the compilation
 : of the library module. For example, err:XPST0003 if the given XQuery library
 : module could not be parsed.
 :)
declare %an:sequential function xqxq:prepare-library-module($library-module-text as xs:string) as
  empty-sequence() external ;

(:~
 : The function tests if the context-item is bound for the
 : execution of the query referred to by the given query identifier.
 :
 : @param $query-key the identifier for a compiled query
 :
 : @return true if the context-item is bound, false otherwise.
 :
 : @error xqxq:NoQueryMatch if no query with the given identifier
 :   was prepared.
 :)
declare function xqxq:is-bound-context-item($query-key as xs:anyURI)
  as xs:boolean  external;

(:~
 : The function tests if the given variable is bound for the
 : execution of the query referred to by the given query identifier.
 :
 : @param $query-key the identifier for a compiled query
 : @param $var-name the name of the variable
 :
 : @return true if the variable is bound, false otherwise.
 :
 : @error xqxq:NoQueryMatch if no query with the given identifier
 :   was prepared.
 : @error xqxq:UndeclaredVariable if the given variable is not declared
 :   in the query.
 :)
declare function xqxq:is-bound-variable($query-key as xs:anyURI, $var-name as
  xs:QName) as xs:boolean  external;

(:~
 : The function returns the names of the external variables that
 : are declared in the given query (either in the main module or
 : in any of the imported library modules).
 :
 : @param $query-key the identifier for a compiled query
 :
 : @return the sequence of names of the said external variables.
 :
 : @error xqxq:NoQueryMatch if no query with the given identifier
 :   was prepared.
 :)
declare function xqxq:external-variables($query-key as xs:anyURI) as
  xs:QName* external ;

(:~
 : The function tests if the query identified by the given key
 : is an updating query.
 :
 : @param $query-key the identifier for a compiled query
 :
 : @return true if the query is an updating query, false otherwise.
 :
 : @error xqxq:NoQueryMatch if no query with the given identifier
 :   was prepared.
 :)
declare function xqxq:is-updating($query-key as xs:anyURI) as
  xs:boolean external;

(:~
 : The function tests if the query identified by the given key
 : is sequential query.
 :
 : @param $query-key the identifier for a compiled query
 :
 : @return true if the query is a sequential, false otherwise.
 :
 : @error xqxq:NoQueryMatch if no query with the given identifier
 :   was prepared.
 :)
declare function xqxq:is-sequential($query-key as xs:anyURI) as
  xs:boolean external;

(:~
 : This function binds the context-item of the prepared query
 : identified by the given key to the $dot argument.
 :
 : @param $query-key the identifier for a compiled query
 : @param $dot the context item to bind
 :
 : @return the function has side effects and returns the empty
 :   sequence.
 :
 : @error xqxq:NoQueryMatch if no query with the given identifier
 :   was prepared.
 :)
declare %an:sequential function xqxq:bind-context-item($query-key as xs:anyURI,
  $dot as item()) as empty-sequence() external ;

(:~
 : This function binds the variable with name $name of
 : the prepared query identified by $query-key to the given sequence.
 :
 : @param $query-key the identifier for a compiled query
 : @param $name the name of the external variable to bind
 : @param $value the sequence to which the external variable $name
 :  should be bound
 :
 : @return the function has side effects and returns the empty
 :   sequence.
 :
 : @error xqxq:NoQueryMatch if no query with the given identifier
 :   was prepared.
 : @error xqxq:UndeclaredVariable if the given variable is not declared
 :   in the query.
 :)
declare %an:sequential function xqxq:bind-variable($query-key as xs:anyURI,
  $var as xs:QName, $value as item()*) as empty-sequence() external ;

(:~
 : Evaluates the given prepared query and returns the result
 : of the evaluation. The query must not be sequential or
 : updating.
 :
 : @param $query-key the identifier for a compiled query
 :
 : @return the result of evaluating the given query
 :
 : @error xqxq:NoQueryMatch if no query with the given identifier
 :   was prepared.
 :
 : @error xqxq:QueryIsUpdating if the query is an updating query.
 :
 : @error xqxq:QueryIsSequential if the query is sequential.
 :
 : @error any dynamic error that is raised by evaluating the
 :   given query.
 :
 :)
declare function xqxq:evaluate($query-key as xs:anyURI) as item()* external;

(:~
 : Evaluates the given prepared query and applies the updates
 : computed by this query. The query must be an updating query.
 :
 : @param $query-key the identifier for a compiled query
 :
 : @return the function has side effects because it applies
 :  the updates of the query. It returns the empty sequence.
 :
 : @error xqxq:NoQueryMatch if no query with the given identifier
 :   was prepared.
 :
 : @error xqxq:QueryNotUpdating if the query is not an updating query.
 :
 : @error xqxq:QueryIsSequential if the query is sequential.
 :
 : @error any dynamic error that is raised by evaluating the
 :   given query or applying its updates.
 :
 :)
declare updating function xqxq:evaluate-updating($query-key as xs:anyURI) external;

(:~
 : Evaluates the given prepared query and returns the result
 : of the evaluation. The query must be sequential.
 :
 : @param $query-key the identifier for a compiled query
 :
 : @return the result of evaluating the query.
 :
 : @error xqxq:NoQueryMatch if no query with the given identifier
 :   was prepared.
 :
 : @error xqxq:QueryNotSequential if the query is not sequential.
 :
 : @error xqxq:QueryIsUpdating if the query is an updating query.
 :
 : @error any dynamic error that is raised by evaluating the
 :   given query.
 :
 :)
declare %an:sequential function xqxq:evaluate-sequential($query-key as
  xs:string) as item()* external;

(:~
 : Deletes the prepared query associated with the given identifier.
 : After the query is deleted, the corresponding identifier should
 : not be used as argument to any of the functions of this module.
 :
 : @param $query-key the identifier for a compiled query
 :
 : @return the function has side effects and returns the empty sequence.
 :
 : @error xqxq:NoQueryMatch if no query with the given identifier
 :   was prepared.
 :
 :)
declare %an:sequential function xqxq:delete-query($query-key as xs:anyURI) as
  empty-sequence() external;

(:~
 : This function returns the value of a variable that is bound in the
 : given query.
 :
 : @param $query-key the identifier of a compiled query.
 : @param $var-name the name of the variable whose value should be returned.
 :
 : @return the value bound to the given variable.
 :
 : @error xqxq:NoQueryMatch if no query with the given identifier
 :   was prepared.
 : @error xqxq:UndeclaredVariable if the given variable is not declared
 :   in the query.
 : @error xqxq:UnboundVariable if the given variable doesn't have a value.
 :)
declare function xqxq:variable-value($query-key as xs:anyURI, $var-name as
  xs:QName) as item()* external;

(:~
 : Returns the compiled query identified by the given query-key
 : as binary data.
 :
 : @param $query-key the identifier of a compiled query.
 :
 : @return the query as xs:base64Binary.
 :
 : @error xqxq:NoQueryMatch if no query with the given identifier
 :   was prepared.
 : @error xqxq:QueryPlanError if there is an error serializing the query.
 :)
declare function xqxq:query-plan($query-key as xs:anyURI)
  as xs:base64Binary external;

(:~
 : The function loads a given XQuery program for execution from a
 : xs:base64Binary query plan, obtained through the xqxq:query-plan function.
 : If the program was successfully loaded, the function returns an
 : identifier as xs:anyURI. This URI can be passed to other functions
 : of this module (e.g. to actually evaluate the program). The URI
 : is opaque and its lilfetime is bound by the lifetime of the XQuery
 : program that invoked this function. Further reference or uses
 : of the identifier lead to unexpected results.
 :
 : Successfully prepared queries need to be deleted by passing the resulting
 : identifier to the xqxq:delete-query function of this module.
 :
 : @param $main-module-text the XQuery program that should be prepared.
 :   The program needs to be a XQuery main module.
 :
 : @return an identifier for the compiled program that can be passed
 :   as arguments to other functions of this module.
 :
 : @error any (static or type) error that may be raised during the compilation
 : of the query. For example, err:XPST0003 if the given XQuery program could
 : not be parsed.
 :)
declare function xqxq:load-from-query-plan($plan as xs:base64Binary)
  as xs:anyURI external;

(:~
 : The function loads a given XQuery program for execution from a
 : xs:base64Binary query plan, obtained through the xqxq:query-plan function.
 : If the program was successfully loaded, the function returns an
 : identifier as xs:anyURI. This URI can be passed to other functions
 : of this module (e.g. to actually evaluate the program). The URI
 : is opaque and its lilfetime is bound by the lifetime of the XQuery
 : program that invoked this function. Further reference or uses
 : of the identifier lead to unexpected results.
 :
 : For important notes regarding the second and third parameters of the
 : function, review the comments in xqxq:prepare-main-module#3.
 :
 : Successfully prepared queries need to be deleted by passing the resulting
 : identifier to the xqxq:delete-query function of this module.
 :
 : @param $main-module-text the XQuery program that should be prepared.
 :   The program needs to be a XQuery main module.
 :
 : @param $resolver the URL resolver function.
 :
 : @param $mapper the URI mapper function.
 :
 : @return an identifier for the compiled program that can be passed
 :   as arguments to other functions of this module.
 :
 : @error any (static or type) error that may be raised during the compilation
 : of the query. For example, err:XPST0003 if the given XQuery program could
 : not be parsed.
 :)
declare function xqxq:load-from-query-plan($plan as xs:base64Binary,
  $resolver as item()?, $mapper as item()?) as xs:anyURI external;

(:~
 : Internal helper function. Only necessary because of incomplete HOF
 : support in Zorba.
 :)
declare %private function xqxq:hof-invoker($hof as item(),
  $ns as xs:string, $entity as xs:string) as item()*
{
   $hof($ns, $entity)
};

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

(:~
 : This module contains functions to compile and evaluate XQuery
 : programs. Also, it contains function that allow to parameterize
 : the static or dynamic evaluation phase.
 :
 : @author Juan Zacarias
 : @project Zorba/Programming Languages/XQuery
 :)
module namespace xqxq = 'http://www.zorba-xquery.com/modules/xqxq';

declare namespace an = "http://www.zorba-xquery.com/annotations";
declare namespace ver = "http://www.zorba-xquery.com/options/versioning";
declare namespace op = "http://www.zorba-xquery.com/options/features";
declare namespace f = "http://www.zorba-xquery.com/features";

declare option ver:module-version "1.0";

(:~
 : The function prepares a given XQuery program for execution.
 : If the program was successfully compiled, the function returns an
 : identifier as xs:anyURI. This URI can be passed to other functions
 : of this module (e.g. to actually evaluate the program). The URI
 : is opaque and its lilfetime is bound by the lifetime of the XQuery
 : program that invoked this function. Further reference or uses
 : of the identifier lead to unexpected results.
 :
 : Successfully prepared queries need to be deleted by passing the resulting
 : identifier to the xqxq:delete-query function of this module.
 :
 : @param $main-module-text the XQuery program that should be prepared.
 :   The program needs to be a XQuery main module.
 :
 : @return an identifier for the compiled program that can be passed
 :   as arguments to other functions of this module.
 :
 : @error any (static or type) error that may be raised during the compilation
 : of the query. For example, err:XPST0003 if the given XQuery program could
 : not be parsed.
 :)
declare %an:sequential function xqxq:prepare-main-module($main-module-text as xs:string) as
  xs:anyURI external;

(:~
 : The function prepares a given XQuery program for execution.
 : If the program was successfully compiled, the function returns an
 : identifier as xs:anyURI. This URI can be passed to other functions
 : of this module (e.g. to actually evaluate the program). The URI
 : is opaque and its lilfetime is bound by the lifetime of the XQuery
 : program that invoked this function. Further reference or uses
 : of the identifier lead to unexpected results.
 :
 : Important notes regarding the second and third parameters of the function:
 : --------------------------------------------------------------------------
 :
 : These parameters allow you to specify a URL resolver and a URI mapper
 : for Zorba to use when executing this query. See
 : http://www.zorba-xquery.com/html/documentation/2.7.0/zorba/uriresolvers
 :
 : The second parameter is a function item for a URL
 : resolver. The URL resolver function must recive 2 parameters:
 :   A $namespace as xs:string that will contain the url to be resolved.
 :   A $entity as xs:string that will contain the type of resolving needed;
 :   this can be 2 values "module" and "schema".
 : The function must return an empty sequence when the specified $namespace
 : or $entity are not the ones to be resolved.
 :
 : Example:
 :
 : declare function mymod:url-resolver($namespace as xs:string, $entity as xs:string)
 : {
 :  if($namespace = 'http://test.xq')
 :  then "module namespace test = 'http://test'; declare function test:foo(){'foo'};"
 :  else ()
 : };
 :
 : The URL resolver function's namespace, name, and parameter naming are
 : not restricted by XQXQ.
 :
 : The URL resolver function's return type is not restricted, it could be a string, a sequence,
 : a node, etc. All the outputs types are to be serialized as a string.
 :
 : The third parameter is a function item for a URI mapper.
 : The URI mapper function, just like the URL resolver, receives 2 parameters:
 :   A $namespace as xs:string that will contain the URI to be mapped.
 :   A $entity as xs:string that will contain the type of resolving needed;
 :   this can be 2 values "module" and "schema".
 : The URI mapper must return an empty sequence when the specified $namesapce or $entity
 : are not to be mapped. Unlike the URL resolver this function must return a sequence of strings.
 :
 : Example:
 :
 : declare function mymod:uri-mapper($namespace as xs:string, $entity as xs:string)
 : {
 :  if($namespace = 'http://test')
 :  then ("http://www.zorba-xquery.com/test", "http://foo.com/schema/test")
 :  else ()
 : };
 :
 : The URI mapper function's namespace, name, and parameter naming are
 : not restricted by XQXQ.
 :
 : In order to pass the above URL resolver and URI mapper to this function,
 : use the following syntax:
 :
 :   variable $queryID := xqxq:prepare-main-module("..query text..",
 :      mymod:url-resolver#2, mymod:uri-mapper#2);
 :
 : That is, the QName of the function followed by "#2". This is XQuery
 : "higher-order function" syntax, meaning the function with the specified
 : QName which takes two arguments. Since URL resolvers and URI mappers
 : must take two arguments, both will always be specified with "#2".
 :
 : Note that parameters 2 and 3 should be declared as follows:
 :    as function($url as xs:string, $entity as xs:string) as item()
 :    as function($uri as xs:string, $entity as xs:string) as xs:string*
 : However Zorba's implementation of higher-order functions (HOF) is not
 : yet complete enough to allow for this. When Zorba's HOF implementation
 : is complete this function signature will be changed.
 :
 : Both the URL resolver and URI mapper functions are optional, meaning you
 : may pass the empty-sequence () for either.
 :
 : Successfully prepared queries need to be deleted by passing the resulting
 : identifier to the xqxq:delete-query function of this module.
 :
 : @param $main-module-text the XQuery program that should be prepared.
 :   The program needs to be a XQuery main module.
 :
 : @param $resolver the URL resolver function.
 :
 : @param $mapper the URI mapper function.
 :
 : @return an identifier for the compiled program that can be passed
 :   as arguments to other functions of this module.
 :
 : @error any (static or type) error that may be raised during the compilation
 : of the query. For example, err:XPST0003 if the given XQuery program could
 : not be parsed.
 :)
declare %an:sequential function xqxq:prepare-main-module($main-module-text as xs:string, $resolver as item()?, $mapper as item()?) as
  xs:anyURI external;

(:~
 : This function compiles a given XQuery library module. It can be used
 : to compile-check a module.
 :
 : @param $library-module-text the XQuery library module that should
 :  be prepared.
 :
 : @return the function is declared as sequential.It returns the
 :  empty-sequence.
 :
 : @error any (static or type) error that may be raised during the compilation
 : of the library module. For example, err:XPST0003 if the given XQuery library
 : module could not be parsed.
 :)
declare %an:sequential function xqxq:prepare-library-module($library-module-text as xs:string) as
  empty-sequence() external ;

(:~
 : The function tests if the context-item is bound for the
 : execution of the query referred to by the given query identifier.
 :
 : @param $query-key the identifier for a compiled query
 :
 : @return true if the context-item is bound, false otherwise.
 :
 : @error xqxq:NoQueryMatch if no query with the given identifier
 :   was prepared.
 :)
declare function xqxq:is-bound-context-item($query-key as xs:anyURI)
  as xs:boolean  external;

(:~
 : The function tests if the given variable is bound for the
 : execution of the query referred to by the given query identifier.
 :
 : @param $query-key the identifier for a compiled query
 : @param $var-name the name of the variable
 :
 : @return true if the variable is bound, false otherwise.
 :
 : @error xqxq:NoQueryMatch if no query with the given identifier
 :   was prepared.
 : @error xqxq:UndeclaredVariable if the given variable is not declared
 :   in the query.
 :)
declare function xqxq:is-bound-variable($query-key as xs:anyURI, $var-name as
  xs:QName) as xs:boolean  external;

(:~
 : The function returns the names of the external variables that
 : are declared in the given query (either in the main module or
 : in any of the imported library modules).
 :
 : @param $query-key the identifier for a compiled query
 :
 : @return the sequence of names of the said external variables.
 :
 : @error xqxq:NoQueryMatch if no query with the given identifier
 :   was prepared.
 :)
declare function xqxq:external-variables($query-key as xs:anyURI) as
  xs:QName* external ;

(:~
 : The function tests if the query identified by the given key
 : is an updating query.
 :
 : @param $query-key the identifier for a compiled query
 :
 : @return true if the query is an updating query, false otherwise.
 :
 : @error xqxq:NoQueryMatch if no query with the given identifier
 :   was prepared.
 :)
declare function xqxq:is-updating($query-key as xs:anyURI) as
  xs:boolean external;

(:~
 : The function tests if the query identified by the given key
 : is sequential query.
 :
 : @param $query-key the identifier for a compiled query
 :
 : @return true if the query is a sequential, false otherwise.
 :
 : @error xqxq:NoQueryMatch if no query with the given identifier
 :   was prepared.
 :)
declare function xqxq:is-sequential($query-key as xs:anyURI) as
  xs:boolean external;

(:~
 : This function binds the context-item of the prepared query
 : identified by the given key to the $dot argument.
 :
 : @param $query-key the identifier for a compiled query
 : @param $dot the context item to bind
 :
 : @return the function has side effects and returns the empty
 :   sequence.
 :
 : @error xqxq:NoQueryMatch if no query with the given identifier
 :   was prepared.
 :)
declare %an:sequential function xqxq:bind-context-item($query-key as xs:anyURI,
  $dot as item()) as empty-sequence() external ;

(:~
 : This function binds the variable with name $name of
 : the prepared query identified by $query-key to the given sequence.
 :
 : @param $query-key the identifier for a compiled query
 : @param $name the name of the external variable to bind
 : @param $value the sequence to which the external variable $name
 :  should be bound
 :
 : @return the function has side effects and returns the empty
 :   sequence.
 :
 : @error xqxq:NoQueryMatch if no query with the given identifier
 :   was prepared.
 : @error xqxq:UndeclaredVariable if the given variable is not declared
 :   in the query.
 :)
declare %an:sequential function xqxq:bind-variable($query-key as xs:anyURI,
  $var as xs:QName, $value as item()*) as empty-sequence() external ;

(:~
 : Evaluates the given prepared query and returns the result
 : of the evaluation. The query must not be sequential or
 : updating.
 :
 : @param $query-key the identifier for a compiled query
 :
 : @return the result of evaluating the given query
 :
 : @error xqxq:NoQueryMatch if no query with the given identifier
 :   was prepared.
 :
 : @error xqxq:QueryIsUpdating if the query is an updating query.
 :
 : @error xqxq:QueryIsSequential if the query is sequential.
 :
 : @error any dynamic error that is raised by evaluating the
 :   given query.
 :
 :)
declare function xqxq:evaluate($query-key as xs:anyURI) as item()* external;

(:~
 : Evaluates the given prepared query and applies the updates
 : computed by this query. The query must be an updating query.
 :
 : @param $query-key the identifier for a compiled query
 :
 : @return the function has side effects because it applies
 :  the updates of the query. It returns the empty sequence.
 :
 : @error xqxq:NoQueryMatch if no query with the given identifier
 :   was prepared.
 :
 : @error xqxq:QueryNotUpdating if the query is not an updating query.
 :
 : @error xqxq:QueryIsSequential if the query is sequential.
 :
 : @error any dynamic error that is raised by evaluating the
 :   given query or applying its updates.
 :
 :)
declare updating function xqxq:evaluate-updating($query-key as xs:anyURI) external;

(:~
 : Evaluates the given prepared query and returns the result
 : of the evaluation. The query must be sequential.
 :
 : @param $query-key the identifier for a compiled query
 :
 : @return the result of evaluating the query.
 :
 : @error xqxq:NoQueryMatch if no query with the given identifier
 :   was prepared.
 :
 : @error xqxq:QueryNotSequential if the query is not sequential.
 :
 : @error xqxq:QueryIsUpdating if the query is an updating query.
 :
 : @error any dynamic error that is raised by evaluating the
 :   given query.
 :
 :)
declare %an:sequential function xqxq:evaluate-sequential($query-key as
  xs:string) as item()* external;

(:~
 : Deletes the prepared query associated with the given identifier.
 : After the query is deleted, the corresponding identifier should
 : not be used as argument to any of the functions of this module.
 :
 : @param $query-key the identifier for a compiled query
 :
 : @return the function has side effects and returns the empty sequence.
 :
 : @error xqxq:NoQueryMatch if no query with the given identifier
 :   was prepared.
 :
 :)
declare %an:sequential function xqxq:delete-query($query-key as xs:anyURI) as
  empty-sequence() external;

(:~
 : This function returns the value of a variable that is bound in the
 : given query.
 :
 : @param $query-key the identifier of a compiled query.
 : @param $var-name the name of the variable whose value should be returned.
 :
 : @return the value bound to the given variable.
 :
 : @error xqxq:NoQueryMatch if no query with the given identifier
 :   was prepared.
 : @error xqxq:UndeclaredVariable if the given variable is not declared
 :   in the query.
 : @error xqxq:UnboundVariable if the given variable doesn't have a value.
 :)
declare function xqxq:variable-value($query-key as xs:anyURI, $var-name as
  xs:QName) as item()* external;

(:~
 : Returns the compiled query identified by the given query-key serialized
 : as binary data.
 :
 : @param $query-key the identifier of a compiled query.
 :
 : @return the query serialized as xs:Base64Binary.
 :
 : @error xqxq:NoQueryMatch if no query with the given identifier
 :   was prepared.
 : @error xqxq:UndeclaredVariable if the given variable is not declared
 :   in the query.
 : @error xqxq:UnboundVariable if the given variable doesn't have a value.
 : @error xqxq:QueryPlanError if there is an error serializing the query.
 :)
declare function xqxq:query-plan($query-key as xs:anyURI)
  as xs:base64Binary external;

(:~
 : The function loads a given XQuery program for execution from a serialized
 : xs:Base64Binary query plan, obtained through the xqxq:query-plan function.
 : If the program was successfully loaded, the function returns an
 : identifier as xs:anyURI. This URI can be passed to other functions
 : of this module (e.g. to actually evaluate the program). The URI
 : is opaque and its lilfetime is bound by the lifetime of the XQuery
 : program that invoked this function. Further reference or uses
 : of the identifier lead to unexpected results.
 :
 : Successfully prepared queries need to be deleted by passing the resulting
 : identifier to the xqxq:delete-query function of this module.
 :
 : @param $main-module-text the XQuery program that should be prepared.
 :   The program needs to be a XQuery main module.
 :
 : @return an identifier for the compiled program that can be passed
 :   as arguments to other functions of this module.
 :
 : @error any (static or type) error that may be raised during the compilation
 : of the query. For example, err:XPST0003 if the given XQuery program could
 : not be parsed.
 :)
declare function xqxq:load-from-query-plan($plan as xs:base64Binary)
  as xs:anyURI external;

(:~
 : The function loads a given XQuery program for execution from a serialized
 : xs:Base64Binary query plan, obtained through the xqxq:query-plan function.
 : If the program was successfully loaded, the function returns an
 : identifier as xs:anyURI. This URI can be passed to other functions
 : of this module (e.g. to actually evaluate the program). The URI
 : is opaque and its lilfetime is bound by the lifetime of the XQuery
 : program that invoked this function. Further reference or uses
 : of the identifier lead to unexpected results.
 :
 : For important notes regarding the second and third parameters of the
 : function, review the comments in xqxq:prepare-main-module#3.
 :
 : Successfully prepared queries need to be deleted by passing the resulting
 : identifier to the xqxq:delete-query function of this module.
 :
 : @param $main-module-text the XQuery program that should be prepared.
 :   The program needs to be a XQuery main module.
 :
 : @param $resolver the URL resolver function.
 :
 : @param $mapper the URI mapper function.
 :
 : @return an identifier for the compiled program that can be passed
 :   as arguments to other functions of this module.
 :
 : @error any (static or type) error that may be raised during the compilation
 : of the query. For example, err:XPST0003 if the given XQuery program could
 : not be parsed.
 :)
declare function xqxq:load-from-query-plan($plan as xs:base64Binary,
  $resolver as item()?, $mapper as item()?) as xs:anyURI external;

(:~
 : Internal helper function. Only necessary because of incomplete HOF
 : support in Zorba.
 :)
declare %private function xqxq:hof-invoker($hof as item(),
  $ns as xs:string, $entity as xs:string) as item()*
{
   $hof($ns, $entity)
};


syntax fix

syntax fix

let $x := 'a'
return
try {
    $x cast as integer
} catch (err:XPST0051) {
  "Cast failed: item does not have a known, atomic type"
}

let $x := 'a'
try {
    $x cast as integer
} catch (err:XPST0051) {
  "Cast failed: item does not have a known, atomic type"
}


DHFPROD-3112: update scope of directory query

(:
  Copyright 2012-2019 MarkLogic Corporation

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

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

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

module namespace job = "http://marklogic.com/data-hub/job-lib";

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

declare option xdmp:mapping "false";

declare variable $SUCCESS-KEY := "success";
declare variable $FAILED-KEY  := "failed";

(:
 : Given a list of job-ids, delete those jobs and any traces associated with them.
 :
 : ML 9.0-1.1 is the only DHF-supported version that does not support the <update> option
 : for xdmp:invoke-function. (<transaction-mode> has been deprecated.)
 :)
declare function job:delete-jobs-and-traces($job-ids as xs:string*)
{
  let $job-results := job:delete-jobs($job-ids)
  let $options :=
    <options xmlns="xdmp:eval">
      {
        if (xdmp:version() = "9.0-1.1") then
          <transaction-mode>update-auto-commit</transaction-mode>
        else
          <update>true</update>
      }
      <isolation>different-transaction</isolation>
      <database>{xdmp:database($config:TRACE-DATABASE)}</database>
      <ignore-amps>{fn:true()}</ignore-amps>
    </options>
  let $deleted-traces :=
    xdmp:invoke-function(
      function() {
        job:delete-traces($job-ids)
      },
      $options
    )
  return
    document {
      object-node {
        "totalCount": fn:count(map:get($job-results, $SUCCESS-KEY)),
        "errorCount": fn:count(map:get($job-results, $FAILED-KEY)),
        "deletedJobs": json:to-array(map:get($job-results, $SUCCESS-KEY)),
        "deletedTraces": json:to-array($deleted-traces),
        "failedJobs": json:to-array(map:get($job-results, $FAILED-KEY)),
        "failedTraces": json:array(),
        "errors": json:array()
      }
    }
};

declare private function job:delete-jobs($job-ids as xs:string*) as map:map
{
  let $results := map:map()
  let $_ :=
    for $id in $job-ids
    let $uri := cts:uris((), (),
                cts:and-query(
                  ( cts:directory-query("/jobs/", "infinity"), cts:json-property-value-query("jobId", $id))))
    return
      if (fn:doc-available($uri)) then (
        xdmp:document-delete($uri),
        map:put($results, $SUCCESS-KEY, (map:get($results, $SUCCESS-KEY), $id))
      )
      else
        map:put($results, $FAILED-KEY, (map:get($results, $FAILED-KEY), $id))
  return $results
};

(:
 : tracing document includes:
 : <jobId>be3ae963-2cc7-4bcb-b71a-b25e585b7df5</jobId>
 : <traceId>11614584719163504615</traceId>
:)
declare private function job:delete-traces($job-ids as xs:string*) as xs:string*
{
  for $id in $job-ids
  for $trace in /trace[jobId = $id]/traceId
  let $_ := xdmp:document-delete(xdmp:node-uri($trace))
  return $trace/fn:string()
};

(:
  Copyright 2012-2019 MarkLogic Corporation

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

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

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

module namespace job = "http://marklogic.com/data-hub/job-lib";

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

declare option xdmp:mapping "false";

declare variable $SUCCESS-KEY := "success";
declare variable $FAILED-KEY  := "failed";

(:
 : Given a list of job-ids, delete those jobs and any traces associated with them.
 :
 : ML 9.0-1.1 is the only DHF-supported version that does not support the <update> option
 : for xdmp:invoke-function. (<transaction-mode> has been deprecated.)
 :)
declare function job:delete-jobs-and-traces($job-ids as xs:string*)
{
  let $job-results := job:delete-jobs($job-ids)
  let $options :=
    <options xmlns="xdmp:eval">
      {
        if (xdmp:version() = "9.0-1.1") then
          <transaction-mode>update-auto-commit</transaction-mode>
        else
          <update>true</update>
      }
      <isolation>different-transaction</isolation>
      <database>{xdmp:database($config:TRACE-DATABASE)}</database>
      <ignore-amps>{fn:true()}</ignore-amps>
    </options>
  let $deleted-traces :=
    xdmp:invoke-function(
      function() {
        job:delete-traces($job-ids)
      },
      $options
    )
  return
    document {
      object-node {
        "totalCount": fn:count(map:get($job-results, $SUCCESS-KEY)),
        "errorCount": fn:count(map:get($job-results, $FAILED-KEY)),
        "deletedJobs": json:to-array(map:get($job-results, $SUCCESS-KEY)),
        "deletedTraces": json:to-array($deleted-traces),
        "failedJobs": json:to-array(map:get($job-results, $FAILED-KEY)),
        "failedTraces": json:array(),
        "errors": json:array()
      }
    }
};

declare private function job:delete-jobs($job-ids as xs:string*) as map:map
{
  let $results := map:map()
  let $_ :=
    for $id in $job-ids
    let $uri := cts:uris((), (),
                cts:and-query(
                    ( cts:directory-query("/jobs/"), cts:json-property-value-query("jobId", $id))))
    return
      if (fn:doc-available($uri)) then (
        xdmp:document-delete($uri),
        map:put($results, $SUCCESS-KEY, (map:get($results, $SUCCESS-KEY), $id))
      )
      else
        map:put($results, $FAILED-KEY, (map:get($results, $FAILED-KEY), $id))
  return $results
};

(:
 : tracing document includes:
 : <jobId>be3ae963-2cc7-4bcb-b71a-b25e585b7df5</jobId>
 : <traceId>11614584719163504615</traceId>
:)
declare private function job:delete-traces($job-ids as xs:string*) as xs:string*
{
  for $id in $job-ids
  for $trace in /trace[jobId = $id]/traceId
  let $_ := xdmp:document-delete(xdmp:node-uri($trace))
  return $trace/fn:string()
};


Add generateCloseImpl and generateResetImpl attributes to the iterator element.

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

import module namespace gen = "http://www.zorba-xquery.com/internal/gen" at "utils.xq";

declare function local:get-iterators($XMLdoc) as xs:string
{
  (: generate the declaration for all iterators separated by a newline :)
  string-join (
    for $iter in $XMLdoc//zorba:iterator
    return
      local:process-iterator($iter),
  $gen:newline )
};

declare function local:process-iterator($iter) as xs:string
{
  (: name of the state or PlanIteratorState if no explicit state is required :)
  let $stateName as xs:string :=
    if(exists($iter/zorba:state))
    then
      concat($iter/@name, 'State')
    else
      'PlanIteratorState'
  return concat(
    if ( exists($iter/@preprocessorGuard) )
    then
      concat($iter/@preprocessorGuard, $gen:newline)
    else "",

    (: preceding comment; prefixed with *'s :)
    '/**', $gen:newline,
    ' * ',
    fn:replace($iter/zorba:description/text(),
               "\n",
               "
 *"),
    $gen:newline,
    ' * ', "Author: ", fn:normalize-space($iter/zorba:description/@author),
    $gen:newline,
    ' */', $gen:newline,

    (: generate an explicit state if required :)
    if (exists($iter/zorba:state))
    then
      local:process-state($iter/zorba:state, $stateName)
    else (),

    (: generate the iterator declaration :)
    local:iterator($iter, $iter/@name, $stateName),

    if ( exists($iter/@preprocessorGuard) )
    then
      concat("#endif", $gen:newline)
    else ""

  ) (: concat :)
};

declare function local:process-state($state, $stateName as xs:string) as xs:string
{
  string-join(('class ',$stateName,' : public PlanIteratorState',$gen:newline, '{', $gen:newline,
  if(exists($state//zorba:member)) then 'public:' else (),
  string-join(($gen:newline,for $member in $state//zorba:member return
  string-join(($gen:indent,string($member/@type),' ',string($member/@name),'; //',string($member/@brief),$gen:newline),''))
  ,''),$gen:newline,

  concat($gen:indent, $stateName, '();', $gen:newline, $gen:newline),

  concat($gen:indent, '~', $stateName, '();', $gen:newline, $gen:newline),

  (: generate init and reset decl :)
  concat($gen:indent,'void init(PlanState&);',$gen:newline,
         $gen:indent,'void reset(PlanState&);',$gen:newline),
  '};',$gen:newline, $gen:newline),'')
};

declare function local:arity($iter) as xs:string
{
  let $arity := lower-case($iter/@arity)
  return
    if ( $arity eq "unary" )
    then 'Unary'
    else if ( $arity eq "binary" )
    then 'Binary'
    else if ( $arity eq "noary" )
    then 'Noary'
    else 'Nary'
};

declare function local:children-decl($iter) as xs:string
{
  let $arity := lower-case($iter/@arity)
  return
    if ( $arity eq "unary" )
      then concat(',', $gen:newline, gen:indent(2), 'PlanIter_t& child')
    else if ( $arity eq "binary" )
      then concat(',', $gen:newline, gen:indent(2), 'PlanIter_t& child1, PlanIter_t& child2')
    else if ( $arity eq "noary" )
    then ''
    else concat(',', $gen:newline, gen:indent(2), 'std::vector<PlanIter_t>& children')

};

declare function local:children-args($iter) as xs:string
{
  let $arity := lower-case($iter/@arity)
  return
    if ( $arity eq "unary" )
    then ', child'
    else if ( $arity eq "binary" )
    then ', child1, child2'
    else if ( $arity eq "noary" )
    then ''
    else ', children'

};
declare function local:constructor($iter, $name as xs:string, $base as xs:string) as xs:string
{
  concat(
    gen:indent(), $name, '(',
    $gen:newline,
    gen:indent(2), 'static_context* sctx,',
    $gen:newline,
    gen:indent(2), 'const QueryLoc& loc',
    local:children-decl($iter),
    local:add-constructor-param($iter),
    ')',
    $gen:newline, gen:indent(2), ': ',
    $gen:newline, gen:indent(2), $base, '(sctx, loc', local:children-args($iter),
    local:add-constructor-param-2($iter),
    $gen:newline, gen:indent(1), '{}',
    $gen:newline, $gen:newline
  )
};

declare function local:iterator($iter, $name as xs:string, $state as xs:string) as xs:string
{
  let $base := concat(local:arity($iter), 'BaseIterator<', $name, ', ', $state, '>')
  return
    concat ( 'class ', $name, ' : ', 'public ', $base,
             $gen:newline, '{ ',
             $gen:newline,

    local:add-protected($iter),

    (: begin serialization, TODO: move implementation to cpp file :)
    'public:',
    $gen:newline,
    gen:indent(), 'SERIALIZABLE_CLASS(',$name,');',
    $gen:newline, $gen:newline,
    gen:indent(), 'SERIALIZABLE_CLASS_CONSTRUCTOR2T(', $name,',',
    $gen:newline,
    gen:indent(2), $base, ');', $gen:newline, $gen:newline,
    gen:indent(), 'void serialize( ::zorba::serialization::Archiver& ar)',
    if ( not(exists($iter/@generateSerialize)) or
         $iter/@generateSerialize eq 'true')
    then concat(
      $gen:newline,
      gen:indent(), '{',
      $gen:newline,
      gen:indent(2), 'serialize_baseclass(ar,',
      $gen:newline,
      gen:indent(2),'(', $base, '*)this);',
      $gen:newline,
      local:add-arch($iter),
      gen:indent(),'}'
    )
    else ";",
    $gen:newline, $gen:newline,
    (: end serialization :)

    local:constructor($iter, $name, $base),

    local:add-destructor($iter),
    local:add-getter($iter),
    local:add-setter($iter),
    if ($iter/zorba:method) then
      local:add-methods($iter)
    else (),
    $gen:indent,'void accept(PlanIterVisitor& v) const;',$gen:newline,$gen:newline,
    $gen:indent,'bool nextImpl(store::Item_t& result, PlanState& aPlanState) const;',$gen:newline,
    if( $iter/@generateResetImpl eq 'true') then
      concat($gen:newline, gen:indent(), 'void resetImpl(PlanState&) const;', $gen:newline)
    else (),
    if( $iter/@generateCloseImpl eq 'true') then
      concat($gen:newline, gen:indent(), 'void closeImpl(PlanState&);', $gen:newline)
    else (),
    '};',
    $gen:newline,$gen:newline
    )
};

declare function local:add-methods ($iter) as xs:string?
{
  fn:concat("public:", $gen:newline,
    fn:string-join(
      for $method in $iter/zorba:method
      return fn:concat(
        $gen:indent,
        $method/@return,
        " ",
        $method/@name,
        "(",
        local:get-method-arguments($method),
        ")",
        if ($method/@const eq "true") then
          " const;"
        else ";"), $gen:newline),
      $gen:newline)
};

declare function local:get-method-arguments($method) as xs:string?
{
  fn:string-join(
    for $param in $method/zorba:param
    return fn:concat(
      $param/@type,
      " ",
      $param/@name),
    ", ")
};

declare function local:add-destructor($iter) as xs:string?
{
  fn:concat($gen:indent, "virtual ~", $iter/@name, "();", $gen:newline, $gen:newline)
};

declare function local:add-getter($iter) as xs:string?
{
  if (count($iter/zorba:member) > 0) then
  string-join(for $member in $iter/zorba:member return
  if(exists($member/@getterName)) then
  string-join(($gen:indent,$member/@type,' ',$member/@getterName,'() const { return ',$member/@name,'; }',$gen:newline,$gen:newline),'')
  else () ,'') else ()
};

declare function local:add-setter($iter) as xs:string?
{
  if (count($iter/zorba:member) > 0)
  then
    string-join(for $member in $iter/zorba:member
                return
                  if(exists($member/@setterName))
                  then
                    string-join(($gen:indent, 'void ', $member/@setterName,
                                 '(', $member/@type, ' aValue) { ',
                                 $member/@name, '= aValue; }', $gen:newline,
                                 $gen:newline),'')
                  else
                    () ,
                '')
  else
    ()
};

declare function local:add-constructor-param-2($iter) as xs:string?
{
  let $member := $iter/zorba:member
  let $param := $iter/zorba:constructor/zorba:parameter
  return
    if (count($member) > 0)
    then
      string-join((')',
                   for $i in (1 to count($member))
                   return
                     string-join((',', $gen:newline, gen:indent(2),
                                  $member[$i]/@name, '(', $param[$i]/@name, ')'
                                 ),'')
                  ),'')
  else
    ')'
};

declare function local:add-constructor-param($iter) as xs:string?
{
  if (count($iter/zorba:constructor/zorba:parameter > 0))
  then
    string-join((for
                   $param in
                   $iter/zorba:constructor/zorba:parameter
                 return
                   string-join((',', $gen:newline, gen:indent(2),
                                $param/@type, ' ', $param/@name,
                                if (exists($param/@defaultValue) and
                                    $param/@defaultValue ne '')
                                then
                                string-join((' = ',$param/@defaultValue),'')
                                else
                                ()
                               ),'')
                ),'')
  else
    ()
};

declare function local:add-protected($iter) as xs:string?
{
  if(count($iter/zorba:member) > 0) then
  string-join(('protected:',$gen:newline,for $member in $iter/zorba:member return
  string-join(($gen:indent,$member/@type,' ',$member/@name,'; //',$member/@brief,$gen:newline),'')),'')
  else ()
};

declare function local:add-arch($iter) as xs:string?
{
  if(count($iter/zorba:member) > 0) then
    string-join(($gen:newline,
                 for $member in $iter/zorba:member
                 return
                 string-join((gen:indent(2),
                              'ar & ',
                              $member/@name,
                              ';',
                              $gen:newline),'')),
                '')
  else ()
};

declare function local:get-decl($XMLdoc, $ns as xs:string) as xs:string
{
  string-join(for $tmp in $XMLdoc//zorba:fwd-decl[@ns=$ns]
  return string-join((if ($ns ne 'zorba') then $gen:indent else '','class ', $tmp/text(), ';'),''),$gen:newline)
};

declare function local:get-fwd-decl($XMLdoc) as xs:string*
{
  let $namespaces as xs:string* := distinct-values(for $ns in $XMLdoc//zorba:fwd-decl return xs:string($ns/@ns))
  return if(empty($namespaces)) then ''
  else
  (for $ns in $namespaces return
  if($ns eq 'zorba') then local:get-decl($XMLdoc, 'zorba')
  else string-join(('namespace ', $ns, '{',$gen:newline, local:get-decl($XMLdoc, $ns) ,$gen:newline,'}'),''))
};

declare function local:includes($XMLdoc) as xs:string*
{
  '#include "common/shared_types.h"',
  if ( exists($XMLdoc/zorba:iterators/zorba:iterator[ @arity eq 'unary' ]) )
  then '#include "runtime/base/unarybase.h"'
  else '',
  if ( exists($XMLdoc/zorba:iterators/zorba:iterator[ @arity eq 'binary' ]) )
  then '#include "runtime/base/binarybase.h"'
  else '',
  if ( exists($XMLdoc/zorba:iterators/zorba:iterator[ @arity eq 'noary' ]) )
  then '#include "runtime/base/noarybase.h"'
  else '',
  if ( not(exists($XMLdoc/zorba:iterators/zorba:iterator/@arity)) or
       exists($XMLdoc/zorba:iterators/zorba:iterator[ @arity eq 'nary' ]) )
  then '#include "runtime/base/narybase.h"'
  else '',

  for $include in $XMLdoc//zorba:header/zorba:include[@form='Angle-bracket']
  return
    concat('#include <', $include/text(), '>'),

  for $include in $XMLdoc//zorba:header/zorba:include[@form='Quoted']
  return
    concat('#include "', $include/text(), '"')
};

declare variable $input external;

declare variable $name  as xs:string external;

string-join((gen:add-copyright(),
             gen:add-guard-open(replace($name,'/','_')),
             $gen:newline,
             local:includes($input),
             $gen:newline,
             'namespace zorba {',
             local:get-fwd-decl($input),
             local:get-iterators($input),
             '}',
             gen:add-guard-close(),
             '/*',
             ' * Local variables:',
             ' * mode: c++',
             ' * End:',
             ' */'),
            $gen:newline),
$gen:newline

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

import module namespace gen = "http://www.zorba-xquery.com/internal/gen" at "utils.xq";

declare function local:get-iterators($XMLdoc) as xs:string
{
  (: generate the declaration for all iterators separated by a newline :)
  string-join (
    for $iter in $XMLdoc//zorba:iterator
    return
      local:process-iterator($iter),
  $gen:newline )
};

declare function local:process-iterator($iter) as xs:string
{
  (: name of the state or PlanIteratorState if no explicit state is required :)
  let $stateName as xs:string :=
    if(exists($iter/zorba:state))
    then
      concat($iter/@name, 'State')
    else
      'PlanIteratorState'
  return concat(
    if ( exists($iter/@preprocessorGuard) )
    then
      concat($iter/@preprocessorGuard, $gen:newline)
    else "",

    (: preceding comment; prefixed with *'s :)
    '/**', $gen:newline,
    ' * ',
    fn:replace($iter/zorba:description/text(),
               "\n",
               "
 *"),
    $gen:newline,
    ' * ', "Author: ", fn:normalize-space($iter/zorba:description/@author),
    $gen:newline,
    ' */', $gen:newline,

    (: generate an explicit state if required :)
    if (exists($iter/zorba:state))
    then
      local:process-state($iter/zorba:state, $stateName)
    else (),

    (: generate the iterator declaration :)
    local:iterator($iter, $iter/@name, $stateName),

    if ( exists($iter/@preprocessorGuard) )
    then
      concat("#endif", $gen:newline)
    else ""

  ) (: concat :)
};

declare function local:process-state($state, $stateName as xs:string) as xs:string
{
  string-join(('class ',$stateName,' : public PlanIteratorState',$gen:newline, '{', $gen:newline,
  if(exists($state//zorba:member)) then 'public:' else (),
  string-join(($gen:newline,for $member in $state//zorba:member return
  string-join(($gen:indent,string($member/@type),' ',string($member/@name),'; //',string($member/@brief),$gen:newline),''))
  ,''),$gen:newline,

  concat($gen:indent, $stateName, '();', $gen:newline, $gen:newline),

  concat($gen:indent, '~', $stateName, '();', $gen:newline, $gen:newline),

  (: generate init and reset decl :)
  concat($gen:indent,'void init(PlanState&);',$gen:newline,
         $gen:indent,'void reset(PlanState&);',$gen:newline),
  '};',$gen:newline, $gen:newline),'')
};

declare function local:arity($iter) as xs:string
{
  let $arity := lower-case($iter/@arity)
  return
    if ( $arity eq "unary" )
    then 'Unary'
    else if ( $arity eq "binary" )
    then 'Binary'
    else if ( $arity eq "noary" )
    then 'Noary'
    else 'Nary'
};

declare function local:children-decl($iter) as xs:string
{
  let $arity := lower-case($iter/@arity)
  return
    if ( $arity eq "unary" )
      then concat(',', $gen:newline, gen:indent(2), 'PlanIter_t& child')
    else if ( $arity eq "binary" )
      then concat(',', $gen:newline, gen:indent(2), 'PlanIter_t& child1, PlanIter_t& child2')
    else if ( $arity eq "noary" )
    then ''
    else concat(',', $gen:newline, gen:indent(2), 'std::vector<PlanIter_t>& children')

};

declare function local:children-args($iter) as xs:string
{
  let $arity := lower-case($iter/@arity)
  return
    if ( $arity eq "unary" )
    then ', child'
    else if ( $arity eq "binary" )
    then ', child1, child2'
    else if ( $arity eq "noary" )
    then ''
    else ', children'

};
declare function local:constructor($iter, $name as xs:string, $base as xs:string) as xs:string
{
  concat(
    gen:indent(), $name, '(',
    $gen:newline,
    gen:indent(2), 'static_context* sctx,',
    $gen:newline,
    gen:indent(2), 'const QueryLoc& loc',
    local:children-decl($iter),
    local:add-constructor-param($iter),
    ')',
    $gen:newline, gen:indent(2), ': ',
    $gen:newline, gen:indent(2), $base, '(sctx, loc', local:children-args($iter),
    local:add-constructor-param-2($iter),
    $gen:newline, gen:indent(1), '{}',
    $gen:newline, $gen:newline
  )
};

declare function local:iterator($iter, $name as xs:string, $state as xs:string) as xs:string
{
  let $base := concat(local:arity($iter), 'BaseIterator<', $name, ', ', $state, '>')
  return
    concat ( 'class ', $name, ' : ', 'public ', $base,
             $gen:newline, '{ ',
             $gen:newline,

    local:add-protected($iter),

    (: begin serialization, TODO: move implementation to cpp file :)
    'public:',
    $gen:newline,
    gen:indent(), 'SERIALIZABLE_CLASS(',$name,');',
    $gen:newline, $gen:newline,
    gen:indent(), 'SERIALIZABLE_CLASS_CONSTRUCTOR2T(', $name,',',
    $gen:newline,
    gen:indent(2), $base, ');', $gen:newline, $gen:newline,
    gen:indent(), 'void serialize( ::zorba::serialization::Archiver& ar)',
    if ( not(exists($iter/@generateSerialize)) or
         $iter/@generateSerialize eq 'true')
    then concat(
      $gen:newline,
      gen:indent(), '{',
      $gen:newline,
      gen:indent(2), 'serialize_baseclass(ar,',
      $gen:newline,
      gen:indent(2),'(', $base, '*)this);',
      $gen:newline,
      local:add-arch($iter),
      gen:indent(),'}'
    )
    else ";",
    $gen:newline, $gen:newline,
    (: end serialization :)

    local:constructor($iter, $name, $base),

    local:add-destructor($iter),
    local:add-getter($iter),
    local:add-setter($iter),
    if ($iter/zorba:method) then
      local:add-methods($iter)
    else (),
    $gen:indent,'void accept(PlanIterVisitor& v) const;',$gen:newline,$gen:newline,
    $gen:indent,'bool nextImpl(store::Item_t& result, PlanState& aPlanState) const;',$gen:newline,'};',$gen:newline,$gen:newline
    )
};

declare function local:add-methods ($iter) as xs:string?
{
  fn:concat("public:", $gen:newline,
    fn:string-join(
      for $method in $iter/zorba:method
      return fn:concat(
        $gen:indent,
        $method/@return,
        " ",
        $method/@name,
        "(",
        local:get-method-arguments($method),
        ")",
        if ($method/@const eq "true") then
          " const;"
        else ";"), $gen:newline),
      $gen:newline)
};

declare function local:get-method-arguments($method) as xs:string?
{
  fn:string-join(
    for $param in $method/zorba:param
    return fn:concat(
      $param/@type,
      " ",
      $param/@name),
    ", ")
};

declare function local:add-destructor($iter) as xs:string?
{
  fn:concat($gen:indent, "virtual ~", $iter/@name, "();", $gen:newline, $gen:newline)
};

declare function local:add-getter($iter) as xs:string?
{
  if (count($iter/zorba:member) > 0) then
  string-join(for $member in $iter/zorba:member return
  if(exists($member/@getterName)) then
  string-join(($gen:indent,$member/@type,' ',$member/@getterName,'() const { return ',$member/@name,'; }',$gen:newline,$gen:newline),'')
  else () ,'') else ()
};

declare function local:add-setter($iter) as xs:string?
{
  if (count($iter/zorba:member) > 0)
  then
    string-join(for $member in $iter/zorba:member
                return
                  if(exists($member/@setterName))
                  then
                    string-join(($gen:indent, 'void ', $member/@setterName,
                                 '(', $member/@type, ' aValue) { ',
                                 $member/@name, '= aValue; }', $gen:newline,
                                 $gen:newline),'')
                  else
                    () ,
                '')
  else
    ()
};

declare function local:add-constructor-param-2($iter) as xs:string?
{
  let $member := $iter/zorba:member
  let $param := $iter/zorba:constructor/zorba:parameter
  return
    if (count($member) > 0)
    then
      string-join((')',
                   for $i in (1 to count($member))
                   return
                     string-join((',', $gen:newline, gen:indent(2),
                                  $member[$i]/@name, '(', $param[$i]/@name, ')'
                                 ),'')
                  ),'')
  else
    ')'
};

declare function local:add-constructor-param($iter) as xs:string?
{
  if (count($iter/zorba:constructor/zorba:parameter > 0))
  then
    string-join((for
                   $param in
                   $iter/zorba:constructor/zorba:parameter
                 return
                   string-join((',', $gen:newline, gen:indent(2),
                                $param/@type, ' ', $param/@name,
                                if (exists($param/@defaultValue) and
                                    $param/@defaultValue ne '')
                                then
                                string-join((' = ',$param/@defaultValue),'')
                                else
                                ()
                               ),'')
                ),'')
  else
    ()
};

declare function local:add-protected($iter) as xs:string?
{
  if(count($iter/zorba:member) > 0) then
  string-join(('protected:',$gen:newline,for $member in $iter/zorba:member return
  string-join(($gen:indent,$member/@type,' ',$member/@name,'; //',$member/@brief,$gen:newline),'')),'')
  else ()
};

declare function local:add-arch($iter) as xs:string?
{
  if(count($iter/zorba:member) > 0) then
    string-join(($gen:newline,
                 for $member in $iter/zorba:member
                 return
                 string-join((gen:indent(2),
                              'ar & ',
                              $member/@name,
                              ';',
                              $gen:newline),'')),
                '')
  else ()
};

declare function local:get-decl($XMLdoc, $ns as xs:string) as xs:string
{
  string-join(for $tmp in $XMLdoc//zorba:fwd-decl[@ns=$ns]
  return string-join((if ($ns ne 'zorba') then $gen:indent else '','class ', $tmp/text(), ';'),''),$gen:newline)
};

declare function local:get-fwd-decl($XMLdoc) as xs:string*
{
  let $namespaces as xs:string* := distinct-values(for $ns in $XMLdoc//zorba:fwd-decl return xs:string($ns/@ns))
  return if(empty($namespaces)) then ''
  else
  (for $ns in $namespaces return
  if($ns eq 'zorba') then local:get-decl($XMLdoc, 'zorba')
  else string-join(('namespace ', $ns, '{',$gen:newline, local:get-decl($XMLdoc, $ns) ,$gen:newline,'}'),''))
};

declare function local:includes($XMLdoc) as xs:string*
{
  '#include "common/shared_types.h"',
  if ( exists($XMLdoc/zorba:iterators/zorba:iterator[ @arity eq 'unary' ]) )
  then '#include "runtime/base/unarybase.h"'
  else '',
  if ( exists($XMLdoc/zorba:iterators/zorba:iterator[ @arity eq 'binary' ]) )
  then '#include "runtime/base/binarybase.h"'
  else '',
  if ( exists($XMLdoc/zorba:iterators/zorba:iterator[ @arity eq 'noary' ]) )
  then '#include "runtime/base/noarybase.h"'
  else '',
  if ( not(exists($XMLdoc/zorba:iterators/zorba:iterator/@arity)) or
       exists($XMLdoc/zorba:iterators/zorba:iterator[ @arity eq 'nary' ]) )
  then '#include "runtime/base/narybase.h"'
  else '',

  for $include in $XMLdoc//zorba:header/zorba:include[@form='Angle-bracket']
  return
    concat('#include <', $include/text(), '>'),

  for $include in $XMLdoc//zorba:header/zorba:include[@form='Quoted']
  return
    concat('#include "', $include/text(), '"')
};

declare variable $input external;

declare variable $name  as xs:string external;

string-join((gen:add-copyright(),
             gen:add-guard-open(replace($name,'/','_')),
             $gen:newline,
             local:includes($input),
             $gen:newline,
             'namespace zorba {',
             local:get-fwd-decl($input),
             local:get-iterators($input),
             '}',
             gen:add-guard-close(),
             '/*',
             ' * Local variables:',
             ' * mode: c++',
             ' * End:',
             ' */'),
            $gen:newline),
$gen:newline