[ignore] hopefully a fix for the broken link e.g. to documentation.xml in documentation.xml

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

(: 	This is the main controller for the web application. It is called from the
	XQueryURLRewrite filter configured in web.xml. :)
xquery version "1.0";

(:~ -------------------------------------------------------
    Main controller: handles all requests not matched by
    sub-controllers.
    ------------------------------------------------------- :)

declare namespace c="http://exist-db.org/xquery/controller";

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

let $query := request:get-parameter("q", ())
return
	(: redirect webapp root to index.xml :)
    if ($exist:path eq '/') then
		<dispatch xmlns="http://exist.sourceforge.net/NS/exist">
			<redirect url="index.xml"/>
		</dispatch>

	(: ignore Cocoon :)
	else if (matches($exist:path, "/cocoon")) then
		<ignore xmlns="http://exist.sourceforge.net/NS/exist">
            <cache-control cache="yes"/>
		</ignore>

	else if ($exist:resource eq 'applications.xml') then
		<dispatch xmlns="http://exist.sourceforge.net/NS/exist">
			<!-- query results are passed to XSLT servlet via request attribute -->
			<set-attribute name="xquery.attribute" value="model"/>
			<view>
				<forward servlet="XSLTServlet">
					<set-attribute name="xslt.input" value="model"/>
					<set-attribute name="xslt.stylesheet" value="apps/applications.xsl"/>
				</forward>
				<forward servlet="XSLTServlet">
					<set-attribute name="xslt.input" value=""/>
					<set-attribute name="xslt.stylesheet" value="stylesheets/db2html.xsl"/>
				</forward>
			</view>
		</dispatch>

	else if ($exist:resource eq 'articles') then
		<dispatch xmlns="http://exist.sourceforge.net/NS/exist">
			<forward url="xquery/exist-articles.xql">
				<!-- query results are passed to XSLT servlet via request attribute -->
				<set-attribute name="xquery.attribute" value="model"/>
			</forward>
			<view>
				<forward servlet="XSLTServlet">
					<set-attribute name="xslt.input" value="model"/>
					<set-attribute name="xslt.stylesheet" value="stylesheets/db2xhtml.xsl"/>
				</forward>
			</view>
		</dispatch>

    else if ($exist:path = "/xforms/idxconf.xml") then
        let $log := util:log("DEBUG", "in xforms rule") return
        <dispatch xmlns="http://exist.sourceforge.net/NS/exist">
            <forward url="idxconf.xql"/>
			<!--view>
				<forward servlet="XSLTServlet">
					<set-attribute name="xslt.stylesheet" value="xforms/xsltforms/xsltforms.xsl"/>
				</forward>
			</view-->
            <cache-control cache="no"/>
		</dispatch>

	(: the following xml files use different stylesheets :)
	else if ($exist:resource = ('index.xml', 'roadmap.xml', 'facts.xml') or $exist:path = '/examples.xml') then
		let $stylesheet :=
			if ($exist:resource eq 'roadmap.xml') then
				"stylesheets/roadmap.xsl"
			else if ($exist:resource eq "facts.xml") then
				"stylesheets/facts.xsl"
			else
				"stylesheets/doc2html-2.xsl"
		return
			<dispatch xmlns="http://exist.sourceforge.net/NS/exist">
				<view>
					<forward servlet="XSLTServlet">
						<set-attribute name="xslt.stylesheet" value="{$stylesheet}"/>
						<set-attribute name="xslt.exist.version" value="{system:get-version()}-rev{system:get-revision()}-{system:get-build()}"/>
					</forward>
				</view>
				<cache-control cache="no"/>
			</dispatch>

	else if (ends-with($exist:resource, '.xml')) then
		(: 	check if the requested document was selected from a query result.
			if yes, pass it to docs.xql to get the match highlighting. :)
		if ($query) then
				<dispatch xmlns="http://exist.sourceforge.net/NS/exist">
					<forward url="/xquery/docs.xql">
						<add-parameter name="path" value="/db/xqdocs/{$exist:resource}"/>
					</forward>
					<view>
    					<forward servlet="XSLTServlet">
    						<set-attribute name="xslt.stylesheet" value="stylesheets/db2xhtml.xsl"/>
    					    <set-attribute name="xslt.output.media-type" value="text/html"/>
    						<set-attribute name="xslt.output.doctype-public" value="-//W3C//DTD XHTML 1.0 Transitional//EN"/>
    						<set-attribute name="xslt.output.doctype-system" value="resources/xhtml1-transitional.dtd"/>
    						<set-attribute name="xslt.output.add-exist-id" value="all"/>
    					    <set-attribute name="xslt.highlight-matches" value="all"/>
    				        <set-attribute name="xslt.xinclude-path" value=".."/>
    				        <set-attribute name="xslt.root" value="."/>
    					</forward>
    				</view>
				</dispatch>

		else
			<dispatch xmlns="http://exist.sourceforge.net/NS/exist">
				<view>
					<forward servlet="XSLTServlet">
						<set-attribute name="xslt.stylesheet" value="stylesheets/db2xhtml.xsl"/>
					    <set-attribute name="xslt.output.media-type" value="text/html"/>
						<set-attribute name="xslt.output.doctype-public" value="-//W3C//DTD XHTML 1.0 Transitional//EN"/>
						<set-attribute name="xslt.output.doctype-system" value="resources/xhtml1-transitional.dtd"/>
						<set-attribute name="xslt.root" value="."/>
					    {
					        if ($exist:resource eq 'download.xml') then
					            <set-attribute name="xslt.table-of-contents" value="'no'"/>
			                else
			                    ()
	                    }
					</forward>
				</view>
            	<cache-control cache="no"/>
			</dispatch>

	else if (ends-with($exist:path, '/dump')) then
		let $newPath := substring-before($exist:path, '/dump')
		return
        	<dispatch xmlns="http://exist.sourceforge.net/NS/exist">
				<forward url="{$exist:controller}{$newPath}"/>
				<view>
					<forward url="xquery/dump.xql">
					   <set-attribute name="controller" value="{$exist:controller}"/>
					   <set-attribute name="path" value="{$exist:path}"/>
					   <set-attribute name="resource" value="{$exist:resource}"/>
				    </forward>
				</view>
			</dispatch>

	else
		<ignore xmlns="http://exist.sourceforge.net/NS/exist">
            <cache-control cache="yes"/>
		</ignore>

(: 	This is the main controller for the web application. It is called from the
	XQueryURLRewrite filter configured in web.xml. :)
xquery version "1.0";

(:~ -------------------------------------------------------
    Main controller: handles all requests not matched by
    sub-controllers.
    ------------------------------------------------------- :)

declare namespace c="http://exist-db.org/xquery/controller";

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

let $query := request:get-parameter("q", ())
return
	(: redirect webapp root to index.xml :)
    if ($exist:path eq '/') then
		<dispatch xmlns="http://exist.sourceforge.net/NS/exist">
			<redirect url="index.xml"/>
		</dispatch>

	(: ignore Cocoon :)
	else if (matches($exist:path, "/cocoon")) then
		<ignore xmlns="http://exist.sourceforge.net/NS/exist">
            <cache-control cache="yes"/>
		</ignore>

	else if ($exist:resource eq 'applications.xml') then
		<dispatch xmlns="http://exist.sourceforge.net/NS/exist">
			<!-- query results are passed to XSLT servlet via request attribute -->
			<set-attribute name="xquery.attribute" value="model"/>
			<view>
				<forward servlet="XSLTServlet">
					<set-attribute name="xslt.input" value="model"/>
					<set-attribute name="xslt.stylesheet" value="apps/applications.xsl"/>
				</forward>
				<forward servlet="XSLTServlet">
					<set-attribute name="xslt.input" value=""/>
					<set-attribute name="xslt.stylesheet" value="stylesheets/db2html.xsl"/>
				</forward>
			</view>
		</dispatch>

	else if ($exist:resource eq 'articles') then
		<dispatch xmlns="http://exist.sourceforge.net/NS/exist">
			<forward url="xquery/exist-articles.xql">
				<!-- query results are passed to XSLT servlet via request attribute -->
				<set-attribute name="xquery.attribute" value="model"/>
			</forward>
			<view>
				<forward servlet="XSLTServlet">
					<set-attribute name="xslt.input" value="model"/>
					<set-attribute name="xslt.stylesheet" value="stylesheets/db2xhtml.xsl"/>
				</forward>
			</view>
		</dispatch>

    else if ($exist:path = "/xforms/idxconf.xml") then
        let $log := util:log("DEBUG", "in xforms rule") return
        <dispatch xmlns="http://exist.sourceforge.net/NS/exist">
            <forward url="idxconf.xql"/>
			<!--view>
				<forward servlet="XSLTServlet">
					<set-attribute name="xslt.stylesheet" value="xforms/xsltforms/xsltforms.xsl"/>
				</forward>
			</view-->
            <cache-control cache="no"/>
		</dispatch>

	(: the following xml files use different stylesheets :)
	else if ($exist:resource = ('index.xml', 'roadmap.xml', 'facts.xml') or $exist:path = '/examples.xml') then
		let $stylesheet :=
			if ($exist:resource eq 'roadmap.xml') then
				"stylesheets/roadmap.xsl"
			else if ($exist:resource eq "facts.xml") then
				"stylesheets/facts.xsl"
			else
				"stylesheets/doc2html-2.xsl"
		return
			<dispatch xmlns="http://exist.sourceforge.net/NS/exist">
				<view>
					<forward servlet="XSLTServlet">
						<set-attribute name="xslt.stylesheet" value="{$stylesheet}"/>
						<set-attribute name="xslt.exist.version" value="{system:get-version()}-rev{system:get-revision()}-{system:get-build()}"/>
					</forward>
				</view>
				<cache-control cache="no"/>
			</dispatch>

	else if (ends-with($exist:resource, '.xml')) then
		(: 	check if the requested document was selected from a query result.
			if yes, pass it to docs.xql to get the match highlighting. :)
		if ($query) then
				<dispatch xmlns="http://exist.sourceforge.net/NS/exist">
					<forward url="/xquery/docs.xql">
						<add-parameter name="path" value="/db/xqdocs/{$exist:resource}"/>
					</forward>
					<view>
    					<forward servlet="XSLTServlet">
    						<set-attribute name="xslt.stylesheet" value="stylesheets/db2xhtml.xsl"/>
    					    <set-attribute name="xslt.output.media-type" value="text/html"/>
    						<set-attribute name="xslt.output.doctype-public" value="-//W3C//DTD XHTML 1.0 Transitional//EN"/>
    						<set-attribute name="xslt.output.doctype-system" value="resources/xhtml1-transitional.dtd"/>
    						<set-attribute name="xslt.output.add-exist-id" value="all"/>
    					    <set-attribute name="xslt.highlight-matches" value="all"/>
    				        <set-attribute name="xslt.xinclude-path" value=".."/>
    				        <set-attribute name="xslt.root" value="{request:get-context-path()}{$exist:prefix}"/>
    					</forward>
    				</view>
				</dispatch>

		else
			<dispatch xmlns="http://exist.sourceforge.net/NS/exist">
				<view>
					<forward servlet="XSLTServlet">
						<set-attribute name="xslt.stylesheet" value="stylesheets/db2xhtml.xsl"/>
					    <set-attribute name="xslt.output.media-type" value="text/html"/>
						<set-attribute name="xslt.output.doctype-public" value="-//W3C//DTD XHTML 1.0 Transitional//EN"/>
						<set-attribute name="xslt.output.doctype-system" value="resources/xhtml1-transitional.dtd"/>
						<set-attribute name="xslt.root" value="{request:get-context-path()}{$exist:prefix}"/>
					    {
					        if ($exist:resource eq 'download.xml') then
					            <set-attribute name="xslt.table-of-contents" value="'no'"/>
			                else
			                    ()
	                    }
					</forward>
				</view>
            	<cache-control cache="no"/>
			</dispatch>

	else if (ends-with($exist:path, '/dump')) then
		let $newPath := substring-before($exist:path, '/dump')
		return
        	<dispatch xmlns="http://exist.sourceforge.net/NS/exist">
				<forward url="{$exist:controller}{$newPath}"/>
				<view>
					<forward url="xquery/dump.xql">
					   <set-attribute name="controller" value="{$exist:controller}"/>
					   <set-attribute name="path" value="{$exist:path}"/>
					   <set-attribute name="resource" value="{$exist:resource}"/>
				    </forward>
				</view>
			</dispatch>

	else
		<ignore xmlns="http://exist.sourceforge.net/NS/exist">
            <cache-control cache="yes"/>
		</ignore>


Cosmetic change.

Cosmetic change.

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

xquery version "3.0";

(:~
 : Using this module, you can parse JSON data into XML, manipulate it like any
 : other XML data using XQuery, and serialize the result back as JSON.

 :
 : There are many ways to represent JSON data in XML, some loss-less ("round
 : tripable") and some lossy ("one way").  Loss-less representations preserve
 : the JSON data types boolean, number, and null; lossy
 : representations convert all data to strings.
 :

 : For a loss-less representation, this module implements that proposed by
 : John Snelson.
 : For example:
 :

 :   {
 :     "firstName" : "John",
 :     "lastName" : "Smith",
 :     "address" : {
 :       "streetAddress" : "21 2nd Street",
 :       "city" : "New York",
 :       "state" : "NY",
 :       "postalCode" : 10021
 :     },
 :     "phoneNumbers" : [ "212 732-1234", "646 123-4567" ]
 :   }
 :

 : would be represented as:
 :

 :   <json type="object">
 :     <pair name="firstName" type="string">John</pair>
 :     <pair name="lastName" type="string">Smith</pair>
 :     <pair name="address" type="object">
 :       <pair name="streetAddress" type="string">21 2nd Street</pair>
 :       <pair name="city" type="string">New York</pair>
 :       <pair name="state" type="string">NY</pair>
 :       <pair name="postalCode" type="number">10021</pair>
 :     </pair>
 :     <pair name="phoneNumbers" type="array">
 :       <item type="string">212 732-1234</item>
 :       <item type="string">646 123-4567</item>
 :     </pair>
 :   </json>
 :

 : For a lossy representation, this module implements
 : JsonML (the array form).
 : For example:
 :

 :   [ "person",
 :     { "created" : "2006-11-11T19:23",
 :       "modified" : "2006-12-31T23:59" },
 :     [ "firstName", "Robert" ],
 :     [ "lastName", "Smith" ],
 :     [ "address",
 :       { "type" : "home" },
 :       [ "street", "12345 Sixth Ave" ],
 :       [ "city", "Anytown" ],
 :       [ "state", "CA" ],
 :       [ "postalCode", "98765-4321" ]
 :     ]
 :   ]
 :

 : would be represented as:
 :

 :   <person created="2006-11-11T19:23" modified="2006-12-31T23:59">
 :     <firstName>Robert</firstName>
 :     <lastName>Smith</lastName>
 :     <address type="home">
 :       <street>12345 Sixth Ave</street>
 :       <city>Anytown</city>
 :       <state>CA</state>
 :       <postalCode>98765-4321</postalCode>
 :     </address>
 :   </person>
 :

 :
 : @author Paul J. Lucas
 : @project Zorba/Data Converters/JSON
 :)
module namespace jx = "http://zorba.io/modules/json-xml";

declare namespace an = "http://www.zorba-xquery.com/annotations";
declare namespace err = "http://www.w3.org/2005/xqt-errors";
declare namespace zerr = "http://zorba.io/modules/zorba-errors";

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

(:~
 : Converts JSON data into an XDM instance using one of the representations
 : described above.

 :
 : @param $json The JSON data.
 : @param $options The JSON conversion  options, for example:
 :

 : { "json-format" : "JsonML-array" }
 :

 : @return said XDM instance.
 : @example test/rbkt/Queries/zorba/json/json-jsonml_array-parse-01.xq
 :)
declare function jx:json-to-xml( $json as json-item()?, $options as object() )
  as element(*,xs:untyped)*
{
  jx:json-to-xml-internal( $json, $options )
};

(:~
 : Converts JSON data into an XDM instance using the Snelson representation
 : described above.

 :
 : @param $json The JSON data.
 : @return said XDM instance.
 : @example test/rbkt/Queries/zorba/json/json-jsonml_array-parse-01.xq
 :)
declare function jx:json-to-xml( $json as json-item()? )
  as element(*,xs:untyped)*
{
  jx:json-to-xml-internal( $json, { "json-format" : "Snelson" } )
};

(:~
 : Parses JSON data from a string int an XDM instance using one of the
 : representations described above.

 :
 : @param $json The JSON data to parse.
 : @param $options The parsing options, for example:
 :

 : { "json-format" : "JsonML-array" }
 :

 : @return said XDM instance.
 : @error zerr:ZJPE0001 if $json contains an illegal JSON character.
 : @error zerr:ZJPE0002 if $json contains an illegal Unicode code-point.
 : @error zerr:ZJPE0003 if $json contains an illegal JSON character escape.
 : @error zerr:ZJPE0004 if $json contains an illegal JSON literal.
 : @error zerr:ZJPE0005 if $json contains an illegal JSON number.
 : @error zerr:ZJPE0006 if $json is not a valid JSON string.
 : @error zerr:ZJPE0007 if $json contains an unterminated string.
 : @error zerr:ZJPE0008 if $json contains an illegal QName.
 : @example test/rbkt/Queries/zorba/json/json-jsonml_array-parse-01.xq
 :)
declare function jx:json-string-to-xml( $json as xs:string?,
                                        $options as object() )
  as element(*,xs:untyped)*
{
  jx:json-string-to-xml-internal( $json, $options )
};

(:~
 : Parses JSON data from a string and returns an XDM instance using the Snelson
 : representation described above.

 :
 : @param $json The JSON data to parse.
 : @return said XDM instance.
 : @error zerr:ZJPE0001 if $json contains an illegal JSON character.
 : @error zerr:ZJPE0002 if $json contains an illegal Unicode code-point.
 : @error zerr:ZJPE0003 if $json contains an illegal JSON character escape.
 : @error zerr:ZJPE0004 if $json contains an illegal JSON literal.
 : @error zerr:ZJPE0005 if $json contains an illegal JSON number.
 : @error zerr:ZJPE0006 if $json is not a valid JSON string.
 : @error zerr:ZJPE0007 if $json contains an unterminated string.
 : @error zerr:ZJPE0008 if $json contains an illegal QName.
 : @example test/rbkt/Queries/zorba/json/json-snelson-parse-array-01.xq
 :)
declare function jx:json-string-to-xml(
  $json as xs:string?
) as element(*,xs:untyped)*
{
  jx:json-string-to-xml-internal( $json, { "json-format" : "Snelson" } )
};

(:~
 : Serializes an XDM into JSON using one of the representations described
 : above.

 :
 : @param $xml The XDM to serialize.
 : @param $options The serializing options, for example:
 :

 : { "json-format" : "JsonML-array", "whitespace" : "indent" }
 :

 : @return a JSON string.
 : @error zerr:ZJSE0001 if $xml is not a document or element node.
 : @error zerr:ZJSE0002 if $xml contains an element that is missing a required
 : attribute.
 : @error zerr:ZJSE0003 if $xml contains an attribute having an illegal value.
 : @error zerr:ZJSE0004 if $xml contains an illegal element.
 : @error zerr:ZJSE0005 if $xml contains an illegal child element for a JSON
 : type.
 : @error zerr:ZJSE0006 if $xml contains an illegal child element.
 : @error zerr:ZJSE0007 if $xml contains an illegal text node.
 : @error zerr:ZJSE0008 if $xml contains an illegal value for a JSON type.
 : @example test/rbkt/Queries/zorba/json/json-jsonml_array-serialize-01.xq
 :)
declare function jx:xml-to-json-string(
  $xml as item()*,
  $options as object()
) as xs:string
{
  jx:xml-to-json-string-internal( $xml, $options )
};

(:~
 : Serializes an XDM into JSON using one of the representations described
 : above.

 :
 : @param $xml The XDM to serialize.
 : @return a JSON string.
 : @error zerr:ZJSE0001 if $xml is not a document or element node.
 : @error zerr:ZJSE0002 if $xml contains an element that is missing a required
 : attribute.
 : @error zerr:ZJSE0003 if $xml contains an attribute having an illegal value.
 : @error zerr:ZJSE0004 if $xml contains an illegal element.
 : @error zerr:ZJSE0005 if $xml contains an illegal child element for a JSON
 : type.
 : @error zerr:ZJSE0006 if $xml contains an illegal child element.
 : @error zerr:ZJSE0007 if $xml contains an illegal text node.
 : @error zerr:ZJSE0008 if $xml contains an illegal value for a JSON type.
 : @example test/rbkt/Queries/zorba/json/json-snelson-serialize-array-01.xq
 :)
declare function jx:xml-to-json-string(
  $xml as item()*
) as xs:string
{
  jx:xml-to-json-string-internal( $xml, { "json-format" : "Snelson" } )
};

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

declare %private function jx:json-to-xml-internal(
  $json as json-item()?,
  $options as item()?
) as element()* external;

declare %private function jx:json-string-to-xml-internal(
  $json as xs:string?,
  $options as item()?
) as element()* external;

declare %an:streamable %private function jx:xml-to-json-string-internal(
  $xml as item()*,
  $options as item()?
) as xs:string external;

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

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

xquery version "3.0";

(:~
 : Using this module, you can parse JSON data into XML, manipulate it like any
 : other XML data using XQuery, and serialize the result back as JSON.

 :
 : There are many ways to represent JSON data in XML, some loss-less ("round
 : tripable") and some lossy ("one way").  Loss-less representations preserve
 : the JSON data types boolean, number, and null; lossy
 : representations convert all data to strings.
 :

 : For a loss-less representation, this module implements that proposed by
 : John Snelson.
 : For example:
 :

 :   {
 :     "firstName" : "John",
 :     "lastName" : "Smith",
 :     "address" : {
 :       "streetAddress" : "21 2nd Street",
 :       "city" : "New York",
 :       "state" : "NY",
 :       "postalCode" : 10021
 :     },
 :     "phoneNumbers" : [ "212 732-1234", "646 123-4567" ]
 :   }
 :

 : would be represented as:
 :

 :   <json type="object">
 :     <pair name="firstName" type="string">John</pair>
 :     <pair name="lastName" type="string">Smith</pair>
 :     <pair name="address" type="object">
 :       <pair name="streetAddress" type="string">21 2nd Street</pair>
 :       <pair name="city" type="string">New York</pair>
 :       <pair name="state" type="string">NY</pair>
 :       <pair name="postalCode" type="number">10021</pair>
 :     </pair>
 :     <pair name="phoneNumbers" type="array">
 :       <item type="string">212 732-1234</item>
 :       <item type="string">646 123-4567</item>
 :     </pair>
 :   </json>
 :

 : For a lossy representation, this module implements
 : JsonML (the array form).
 : For example:
 :

 :   [ "person",
 :     { "created" : "2006-11-11T19:23",
 :       "modified" : "2006-12-31T23:59" },
 :     [ "firstName", "Robert" ],
 :     [ "lastName", "Smith" ],
 :     [ "address",
 :       { "type" : "home" },
 :       [ "street", "12345 Sixth Ave" ],
 :       [ "city", "Anytown" ],
 :       [ "state", "CA" ],
 :       [ "postalCode", "98765-4321" ]
 :     ]
 :   ]
 :

 : would be represented as:
 :

 :   <person created="2006-11-11T19:23" modified="2006-12-31T23:59">
 :     <firstName>Robert</firstName>
 :     <lastName>Smith</lastName>
 :     <address type="home">
 :       <street>12345 Sixth Ave</street>
 :       <city>Anytown</city>
 :       <state>CA</state>
 :       <postalCode>98765-4321</postalCode>
 :     </address>
 :   </person>
 :

 :
 : @author Paul J. Lucas
 : @project Zorba/Data Converters/JSON
 :)
module namespace jx = "http://zorba.io/modules/json-xml";

declare namespace an = "http://www.zorba-xquery.com/annotations";
declare namespace err = "http://www.w3.org/2005/xqt-errors";
declare namespace zerr = "http://zorba.io/modules/zorba-errors";

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

(:~
 : Converts JSON data into an XDM instance using one of the representations
 : described above.

 :
 : @param $json The JSON data.
 : @param $options The JSON conversion  options, for example:
 :

 : { "json-format" : "JsonML-array" }
 :

 : @return said XDM instance.
 : @example test/rbkt/Queries/zorba/json/json-jsonml_array-parse-01.xq
 :)
declare function jx:json-to-xml( $json as json-item()?, $options as object() )
  as element(*,xs:untyped)*
{
  jx:json-to-xml-internal( $json, $options )
};

(:~
 : Converts JSON data into an XDM instance using the Snelson representation
 : described above.

 :
 : @param $json The JSON data.
 : @return said XDM instance.
 : @example test/rbkt/Queries/zorba/json/json-jsonml_array-parse-01.xq
 :)
declare function jx:json-to-xml( $json as json-item()? )
  as element(*,xs:untyped)*
{
  jx:json-to-xml-internal( $json, { "json-format" : "Snelson" } )
};

(:~
 : Parses JSON data from a string int an XDM instance using one of the
 : representations described above.

 :
 : @param $json The JSON data to parse.
 : @param $options The parsing options, for example:
 :

 : { "json-format" : "JsonML-array" }
 :

 : @return said XDM instance.
 : @error zerr:ZJPE0001 if $json contains an illegal JSON character.
 : @error zerr:ZJPE0002 if $json contains an illegal Unicode code-point.
 : @error zerr:ZJPE0003 if $json contains an illegal JSON character escape.
 : @error zerr:ZJPE0004 if $json contains an illegal JSON literal.
 : @error zerr:ZJPE0005 if $json contains an illegal JSON number.
 : @error zerr:ZJPE0006 if $json is not a valid JSON string.
 : @error zerr:ZJPE0007 if $json contains an unterminated string.
 : @error zerr:ZJPE0008 if $json contains an illegal QName.
 : @example test/rbkt/Queries/zorba/json/json-jsonml_array-parse-01.xq
 :)
declare function jx:json-string-to-xml( $json as xs:string?,
                                        $options as object() )
  as element(*,xs:untyped)*
{
  jx:json-string-to-xml-internal( $json, $options )
};

(:~
 : Parses JSON data from a string and returns an XDM instance using the Snelson
 : representation described above.

 :
 : @param $json The JSON data to parse.
 : @return said XDM instance.
 : @error zerr:ZJPE0001 if $json contains an illegal JSON character.
 : @error zerr:ZJPE0002 if $json contains an illegal Unicode code-point.
 : @error zerr:ZJPE0003 if $json contains an illegal JSON character escape.
 : @error zerr:ZJPE0004 if $json contains an illegal JSON literal.
 : @error zerr:ZJPE0005 if $json contains an illegal JSON number.
 : @error zerr:ZJPE0006 if $json is not a valid JSON string.
 : @error zerr:ZJPE0007 if $json contains an unterminated string.
 : @error zerr:ZJPE0008 if $json contains an illegal QName.
 : @example test/rbkt/Queries/zorba/json/json-snelson-parse-array-01.xq
 :)
declare function jx:json-string-to-xml(
  $json as xs:string?
) as element(*,xs:untyped)*
{
  jx:json-string-to-xml-internal( $json, { "json-format" : "Snelson" } )
};

(:~
 : Serializes an XDM into JSON using one of the representations described
 : above.

 :
 : @param $xml The XDM to serialize.
 : @param $options The serializing options, for example:
 :

 : { "json-format" : "JsonML-array", "whitespace" : "indent" }
 :

 : @return a JSON string.
 : @error zerr:ZJSE0001 if $xml is not a document or element node.
 : @error zerr:ZJSE0002 if $xml contains an element that is missing a required
 : attribute.
 : @error zerr:ZJSE0003 if $xml contains an attribute having an illegal value.
 : @error zerr:ZJSE0004 if $xml contains an illegal element.
 : @error zerr:ZJSE0005 if $xml contains an illegal child element for a JSON
 : type.
 : @error zerr:ZJSE0006 if $xml contains an illegal child element.
 : @error zerr:ZJSE0007 if $xml contains an illegal text node.
 : @error zerr:ZJSE0008 if $xml contains an illegal value for a JSON type.
 : @example test/rbkt/Queries/zorba/json/json-jsonml_array-serialize-01.xq
 :)
declare function jx:xml-to-json-string(
  $xml as item()*,
  $options as object()
) as xs:string
{
  jx:xml-to-json-string-internal( $xml, $options )
};

(:~
 : Serializes an XDM into JSON using one of the representations described
 : above.

 :
 : @param $xml The XDM to serialize.
 : @return a JSON string.
 : @error zerr:ZJSE0001 if $xml is not a document or element node.
 : @error zerr:ZJSE0002 if $xml contains an element that is missing a required
 : attribute.
 : @error zerr:ZJSE0003 if $xml contains an attribute having an illegal value.
 : @error zerr:ZJSE0004 if $xml contains an illegal element.
 : @error zerr:ZJSE0005 if $xml contains an illegal child element for a JSON
 : type.
 : @error zerr:ZJSE0006 if $xml contains an illegal child element.
 : @error zerr:ZJSE0007 if $xml contains an illegal text node.
 : @error zerr:ZJSE0008 if $xml contains an illegal value for a JSON type.
 : @example test/rbkt/Queries/zorba/json/json-snelson-serialize-array-01.xq
 :)
declare function jx:xml-to-json-string(
  $xml as item()*
) as xs:string
{
  jx:xml-to-json-string-internal($xml, { "json-format" : "Snelson" })
};

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

declare %private function jx:json-to-xml-internal(
  $json as json-item()?,
  $options as item()?
) as element()* external;

declare %private function jx:json-string-to-xml-internal(
  $json as xs:string?,
  $options as item()?
) as element()* external;

declare %an:streamable %private function jx:xml-to-json-string-internal(
  $xml as item()*,
  $options as item()?
) as xs:string external;

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


[MOD] XQuery: aligned with new higher-order function signatures

(:~
 : Conversion module for creating JUnit tests from the QT3TS test suite.
 : @author Leo Woerteler
 : @version 0.1
 :)
module namespace qt3ts='http://www.basex.org/modules/qt3ts';

import module namespace qt3ts-java='http://www.basex.org/modules/qt3ts/java'
  at 'qt3ts-java.xqm';

declare default element namespace 'http://www.w3.org/2010/09/qt-fots-catalog';

(:~
 : Main entry point.
 : @param $path path to the test-suite
 : @param $test-sets to be converted
 : @param $package Java package
 : @param $out-path path for the generated Java files
 : @return nothing
 :)
declare function qt3ts:to-junit(
  $path as xs:string,
  $test-sets as xs:string*,
  $package as xs:string,
  $out-path as xs:string,
  $supports as function(xs:string, xs:string*) as xs:boolean
) as empty-sequence() {
  let $catalog := doc($path || file:dir-separator() || 'catalog.xml'),
      $root    := replace(base-uri($catalog), 'catalog\.xml$', ''),
      $files   := qt3ts:get-files($root, $catalog),
      $envs    := qt3ts:environments(map:new(), $catalog/catalog/environment, $files)
  let $files   :=
        qt3ts-java:test-suites(
          $package,
          map:new((
            for $name in (if($test-sets = '*') then $catalog//test-set/@name else $test-sets)
            let $test-set := $catalog//test-set[@name = $name]
            return
              if(not($test-set)) then qt3ts:debug('Test-set not found', $name)
              else qt3ts:test-set($test-set, $root, $package, $supports, $envs)
          ))
        )
  for $name in map:keys($files)
  let $path := $out-path || file:dir-separator() || $name || '.java'
  return (
    file:create-dir(replace($path, '[^/\\]+$', '')),
    file:write($path, $files($name), map{ 'method' := 'text' })
  )
};

(:~
 : Processes a test-set.
 : @param $test-set the test-set element
 : @param $package Java package
 : @param $out-path path for the generated Java files
 : @return full class name
 :)
declare function qt3ts:test-set(
  $test-set as element(test-set),
  $root as xs:string,
  $package as xs:string,
  $supports as function(xs:string, xs:string*) as xs:boolean,
  $envs as map(*)
) as map(*)? {
  let $name := $test-set/@name/string(),
      $dir  := replace($test-set/@file, '/[^/]+$', ''),
      $path := $root || $test-set/@file,
      $doc  := doc($path),
      $file := qt3ts:get-files($root, $doc),
      $envs := qt3ts:environments($envs, $doc/test-set/environment, $file),
      $desc := if($doc/test-set/description)
                 then $doc/test-set/description/string()
                 else 'Test-set ' || $name || '.'
  let $tests := map:new((
    for $test-case in $doc/test-set/test-case
    where every $dep in $test-case/dependency
      satisfies $supports($dep/@type, tokenize($dep/@value, ' '))
    return map{ $test-case/@name := qt3ts:test-case($test-set, $test-case, $root, $envs, $file) }
  ))
  return
    if($doc/*/dependency[not($supports(@type, tokenize(@value, ' ')))])
    then qt3ts:debug('Test-set skipped because of dependency', $name)
    else if(map:size($tests) eq 0)
    then qt3ts:debug('Test-set skipped', $name)
    else
      let $ts := qt3ts-java:test-set($package, $dir, $name, $desc, $tests)
      return if(contains($ts[2], ' void '))
        then map:entry($dir || '/' || $ts[1], $ts[2])
        else  () (: qt3ts:debug('Test-set skipped after serialization', $name) :)
};

declare function qt3ts:test-case(
  $test-set as element(test-set),
  $test-case as element(test-case),
  $root as xs:string,
  $envs as map(*),
  $file as map(*)
) as map(*) {
  let $desc := $test-case/description/string(),
      $env := $test-case/environment,
      $mods := map:new((
          for $mod in $test-case/module
          return map:entry(
            xs:anyURI($mod/@uri/string()),
            $file($mod/@file)
          )
        )),
      $map :=
        if($env) then
          if($env/@ref) then
            if(map:contains($envs, $env/@ref))
              then $envs($env/@ref)
              else (
                qt3ts:debug('Couldn''t find environment',
                  $test-set/@name || ', ' || $env/@ref),
                map{}
              )
          else qt3ts:environment($env, $file)
        else map{},
      $is_file := exists($test-case/test/@file),
      $test := if($is_file)
        then $file($test-case/test/@file)
        else $test-case/test/string(),
      $result := qt3ts:result($test-case/result/*, $file, $root),
      $xq30 :=
        every $dep in $test-case/dependency[@type = 'spec']
        satisfies
          some $spec in $dep/tokenize(@value, ' ')
          satisfies contains(trace($spec, 'spec'), '30') or contains($spec, '+')
  return map{
    'description' := $desc,
    'modules'     := $mods,
    'environment' := $map,
    'is_file'     := $is_file,
    'test'        := $test,
    'result'      := $result,
    'xq10'        := not($xq30)
  }
};

declare function qt3ts:result(
  $assert as element(),
  $file as map(*),
  $root as xs:string
) as map(*) {
  let $name := $assert/local-name()
  return map:entry($name,
    switch($name)
      case 'assert-true' return ()
      case 'assert-false' return ()
      case 'assert-empty' return ()
      case 'assert' return $assert/string()
      case 'assert-eq' return $assert/string()
      case 'assert-string-value' return map{
          'normalize-space' := $assert/@normalize-space = ('true', '1'),
          'result'          := $assert/string()
        }
      case 'assert-permutation' return $assert/string()
      case 'assert-type' return $assert/string()
      case 'assert-deep-eq' return $assert/string()
      case 'assert-count' return xs:integer($assert/string())
      case 'error' return $assert/@code/string()
      case 'assert-serialization-error' return $assert/@code/string()
      case 'assert-xml' return map{
          'ignore-prefixes' := $assert/@ignore-prefixes = ('true', '1'),
          'result' := if($assert/@file)
            then
              let $path := $root || $file($assert/@file)
              return file:read-text($path, 'UTF-8')
            else $assert/string()
        }
      case 'serialization-matches' return map{
          'flags' := ($assert/@flags/string(), '')[1],
          'regex' := $assert/text()/string()
        }
      case 'all-of' return map($assert/*, qt3ts:result(?, $file, $root))
      case 'any-of' return map($assert/*, qt3ts:result(?, $file, $root))
      default return qt3ts:debug('Unknown assertion', $name)
  )
};

declare function qt3ts:environments(
  $map as map(*),
  $envs as element(environment)*,
  $file as map(*)
) as map(*) {
  map:new(
    ($map,
      for $env in $envs
      return if($env/@name)
        then map:entry($env/@name, qt3ts:environment($env, $file))
        else qt3ts:debug('Environment has no name', serialize($env))
    )
  )
};

declare function qt3ts:environment(
  $envs as element(environment),
  $file as map(*)
) as map(*)* {
  for $env in $envs/*
  let $name := xs:string($env/local-name())
  return switch($name)
    case 'source'
      return map:entry($name, map{
        'role' := $env/@role/string(),
        'file' := $file($env/@file/string()),
        'validation' := $env/@validation/string(),
        'uri' := xs:anyURI($env/@uri/string())
      })
    case 'namespace'
      return map:entry($name, map{
        'prefix' := $env/@prefix/string(),
        'uri' := xs:anyURI($env/@uri/string())
      })
    case 'collation'
      return map:entry($name, map{
        'uri' := xs:anyURI($env/@uri/string()),
        'default' := $env/@default = 'true'
      })
    case 'collection'
      return map:entry($name, map{
        'uri' := xs:anyURI($env/@uri/string()),
        'docs' := map:new((
          for $src in $env/source
          return map:entry($src/@file, map{
            'role' := $src/@role/string(),
            'file' := $file($src/@file/string()),
            'validation' := $src/@validation/string(),
            'uri' := xs:anyURI($src/@uri/string())
          })
        ))
      })
    case 'decimal-format'
      return map:entry($name, map:new((
        for $att in $env/@*
        return map:entry($att/name(), $att/string())
      )))
    case 'resource'
      return map:entry($name, map{
        'file' := $file($env/@file/string()),
        'media-type' := $env/@media-type/string(),
        'encoding' := $env/@encoding/string(),
        'uri' := xs:anyURI($env/@uri/string())
      })
    case 'schema'
      return map:entry($name, map{
        'uri' := xs:anyURI($env/@uri/string()),
        'file' := $file($env/@file/string())
      })
    case 'static-base-uri'
      return map:entry($name, xs:anyURI($env/@uri/string()))
    case 'param'
      return map:entry($name, map{
        'name' := $env/@name/string(),
        'select' := $env/@select/string(),
        'as' := $env/@as/string(),
        'declared' := $env/@declared != 'false'
      })
    case 'context-item'
      return map:entry($name, $env/@select/string())
    default
      return qt3ts:debug('Unknown environment parameter', $env)
};

declare function qt3ts:get-files(
  $root as xs:string,
  $node as node()
)  as map(*) {
  map:new((
    let $root-len := string-length(file:path-to-native(substring($root, 9))) + 2,
        $uri := base-uri($node)
    for $file in $node//@file
    let $uri := replace($uri, '[^/]+$', $file)
    group by $path := replace(substring(
      file:path-to-native(substring($uri, 9)), $root-len), '\\', '/')
    return
      for $file in $file
      return map:entry($file, $path)
  ))
};

declare function qt3ts:insert-with(
  $combine as function(item()*, item()*) as item()*,
  $map as map(*),
  $key as item(),
  $value as item()*
) as map(*) {
  map:new(
    ( $map,
      map:entry(
        $key,
        if(map:contains($map, $key))
          then $combine(map:get($map, $key), $value)
          else $value
      )
    )
  )
};

declare function qt3ts:debug(
  $msg as xs:string,
  $itm as item()
) as empty-sequence() {
  trace($itm, $msg || ': ')[2]
};

(:~
 : Conversion module for creating JUnit tests from the QT3TS test suite.
 : @author Leo Woerteler
 : @version 0.1
 :)
module namespace qt3ts='http://www.basex.org/modules/qt3ts';

import module namespace qt3ts-java='http://www.basex.org/modules/qt3ts/java'
  at 'qt3ts-java.xqm';

declare default element namespace 'http://www.w3.org/2010/09/qt-fots-catalog';

(:~
 : Main entry point.
 : @param $path path to the test-suite
 : @param $test-sets to be converted
 : @param $package Java package
 : @param $out-path path for the generated Java files
 : @return nothing
 :)
declare function qt3ts:to-junit(
  $path as xs:string,
  $test-sets as xs:string*,
  $package as xs:string,
  $out-path as xs:string,
  $supports as function(xs:string, xs:string*) as xs:boolean
) as empty-sequence() {
  let $catalog := doc($path || file:dir-separator() || 'catalog.xml'),
      $root    := replace(base-uri($catalog), 'catalog\.xml$', ''),
      $files   := qt3ts:get-files($root, $catalog),
      $envs    := qt3ts:environments(map:new(), $catalog/catalog/environment, $files)
  let $files   :=
        qt3ts-java:test-suites(
          $package,
          map:new((
            for $name in (if($test-sets = '*') then $catalog//test-set/@name else $test-sets)
            let $test-set := $catalog//test-set[@name = $name]
            return
              if(not($test-set)) then qt3ts:debug('Test-set not found', $name)
              else qt3ts:test-set($test-set, $root, $package, $supports, $envs)
          ))
        )
  for $name in map:keys($files)
  let $path := $out-path || file:dir-separator() || $name || '.java'
  return (
    file:create-dir(replace($path, '[^/\\]+$', '')),
    file:write($path, $files($name), map{ 'method' := 'text' })
  )
};

(:~
 : Processes a test-set.
 : @param $test-set the test-set element
 : @param $package Java package
 : @param $out-path path for the generated Java files
 : @return full class name
 :)
declare function qt3ts:test-set(
  $test-set as element(test-set),
  $root as xs:string,
  $package as xs:string,
  $supports as function(xs:string, xs:string*) as xs:boolean,
  $envs as map(*)
) as map(*)? {
  let $name := $test-set/@name/string(),
      $dir  := replace($test-set/@file, '/[^/]+$', ''),
      $path := $root || $test-set/@file,
      $doc  := doc($path),
      $file := qt3ts:get-files($root, $doc),
      $envs := qt3ts:environments($envs, $doc/test-set/environment, $file),
      $desc := if($doc/test-set/description)
                 then $doc/test-set/description/string()
                 else 'Test-set ' || $name || '.'
  let $tests := map:new((
    for $test-case in $doc/test-set/test-case
    where every $dep in $test-case/dependency
      satisfies $supports($dep/@type, tokenize($dep/@value, ' '))
    return map{ $test-case/@name := qt3ts:test-case($test-set, $test-case, $root, $envs, $file) }
  ))
  return
    if($doc/*/dependency[not($supports(@type, tokenize(@value, ' ')))])
    then qt3ts:debug('Test-set skipped because of dependency', $name)
    else if(map:size($tests) eq 0)
    then qt3ts:debug('Test-set skipped', $name)
    else
      let $ts := qt3ts-java:test-set($package, $dir, $name, $desc, $tests)
      return if(contains($ts[2], ' void '))
        then map:entry($dir || '/' || $ts[1], $ts[2])
        else  () (: qt3ts:debug('Test-set skipped after serialization', $name) :)
};

declare function qt3ts:test-case(
  $test-set as element(test-set),
  $test-case as element(test-case),
  $root as xs:string,
  $envs as map(*),
  $file as map(*)
) as map(*) {
  let $desc := $test-case/description/string(),
      $env := $test-case/environment,
      $mods := map:new((
          for $mod in $test-case/module
          return map:entry(
            xs:anyURI($mod/@uri/string()),
            $file($mod/@file)
          )
        )),
      $map :=
        if($env) then
          if($env/@ref) then
            if(map:contains($envs, $env/@ref))
              then $envs($env/@ref)
              else (
                qt3ts:debug('Couldn''t find environment',
                  $test-set/@name || ', ' || $env/@ref),
                map{}
              )
          else qt3ts:environment($env, $file)
        else map{},
      $is_file := exists($test-case/test/@file),
      $test := if($is_file)
        then $file($test-case/test/@file)
        else $test-case/test/string(),
      $result := qt3ts:result($test-case/result/*, $file, $root),
      $xq30 :=
        every $dep in $test-case/dependency[@type = 'spec']
        satisfies
          some $spec in $dep/tokenize(@value, ' ')
          satisfies contains(trace($spec, 'spec'), '30') or contains($spec, '+')
  return map{
    'description' := $desc,
    'modules'     := $mods,
    'environment' := $map,
    'is_file'     := $is_file,
    'test'        := $test,
    'result'      := $result,
    'xq10'        := not($xq30)
  }
};

declare function qt3ts:result(
  $assert as element(),
  $file as map(*),
  $root as xs:string
) as map(*) {
  let $name := $assert/local-name()
  return map:entry($name,
    switch($name)
      case 'assert-true' return ()
      case 'assert-false' return ()
      case 'assert-empty' return ()
      case 'assert' return $assert/string()
      case 'assert-eq' return $assert/string()
      case 'assert-string-value' return map{
          'normalize-space' := $assert/@normalize-space = ('true', '1'),
          'result'          := $assert/string()
        }
      case 'assert-permutation' return $assert/string()
      case 'assert-type' return $assert/string()
      case 'assert-deep-eq' return $assert/string()
      case 'assert-count' return xs:integer($assert/string())
      case 'error' return $assert/@code/string()
      case 'assert-serialization-error' return $assert/@code/string()
      case 'assert-xml' return map{
          'ignore-prefixes' := $assert/@ignore-prefixes = ('true', '1'),
          'result' := if($assert/@file)
            then
              let $path := $root || $file($assert/@file)
              return file:read-text($path, 'UTF-8')
            else $assert/string()
        }
      case 'serialization-matches' return map{
          'flags' := ($assert/@flags/string(), '')[1],
          'regex' := $assert/text()/string()
        }
      case 'all-of' return map(qt3ts:result(?, $file, $root), $assert/*)
      case 'any-of' return map(qt3ts:result(?, $file, $root), $assert/*)
      default return qt3ts:debug('Unknown assertion', $name)
  )
};

declare function qt3ts:environments(
  $map as map(*),
  $envs as element(environment)*,
  $file as map(*)
) as map(*) {
  map:new(
    ($map,
      for $env in $envs
      return if($env/@name)
        then map:entry($env/@name, qt3ts:environment($env, $file))
        else qt3ts:debug('Environment has no name', serialize($env))
    )
  )
};

declare function qt3ts:environment(
  $envs as element(environment),
  $file as map(*)
) as map(*)* {
  for $env in $envs/*
  let $name := xs:string($env/local-name())
  return switch($name)
    case 'source'
      return map:entry($name, map{
        'role' := $env/@role/string(),
        'file' := $file($env/@file/string()),
        'validation' := $env/@validation/string(),
        'uri' := xs:anyURI($env/@uri/string())
      })
    case 'namespace'
      return map:entry($name, map{
        'prefix' := $env/@prefix/string(),
        'uri' := xs:anyURI($env/@uri/string())
      })
    case 'collation'
      return map:entry($name, map{
        'uri' := xs:anyURI($env/@uri/string()),
        'default' := $env/@default = 'true'
      })
    case 'collection'
      return map:entry($name, map{
        'uri' := xs:anyURI($env/@uri/string()),
        'docs' := map:new((
          for $src in $env/source
          return map:entry($src/@file, map{
            'role' := $src/@role/string(),
            'file' := $file($src/@file/string()),
            'validation' := $src/@validation/string(),
            'uri' := xs:anyURI($src/@uri/string())
          })
        ))
      })
    case 'decimal-format'
      return map:entry($name, map:new((
        for $att in $env/@*
        return map:entry($att/name(), $att/string())
      )))
    case 'resource'
      return map:entry($name, map{
        'file' := $file($env/@file/string()),
        'media-type' := $env/@media-type/string(),
        'encoding' := $env/@encoding/string(),
        'uri' := xs:anyURI($env/@uri/string())
      })
    case 'schema'
      return map:entry($name, map{
        'uri' := xs:anyURI($env/@uri/string()),
        'file' := $file($env/@file/string())
      })
    case 'static-base-uri'
      return map:entry($name, xs:anyURI($env/@uri/string()))
    case 'param'
      return map:entry($name, map{
        'name' := $env/@name/string(),
        'select' := $env/@select/string(),
        'as' := $env/@as/string(),
        'declared' := $env/@declared != 'false'
      })
    case 'context-item'
      return map:entry($name, $env/@select/string())
    default
      return qt3ts:debug('Unknown environment parameter', $env)
};

declare function qt3ts:get-files(
  $root as xs:string,
  $node as node()
)  as map(*) {
  map:new((
    let $root-len := string-length(file:path-to-native(substring($root, 9))) + 2,
        $uri := base-uri($node)
    for $file in $node//@file
    let $uri := replace($uri, '[^/]+$', $file)
    group by $path := replace(substring(
      file:path-to-native(substring($uri, 9)), $root-len), '\\', '/')
    return
      for $file in $file
      return map:entry($file, $path)
  ))
};

declare function qt3ts:insert-with(
  $combine as function(item()*, item()*) as item()*,
  $map as map(*),
  $key as item(),
  $value as item()*
) as map(*) {
  map:new(
    ( $map,
      map:entry(
        $key,
        if(map:contains($map, $key))
          then $combine(map:get($map, $key), $value)
          else $value
      )
    )
  )
};

declare function qt3ts:debug(
  $msg as xs:string,
  $itm as item()
) as empty-sequence() {
  trace($itm, $msg || ': ')[2]
};


remove unnecessary annotations and clean-up indentation

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

module namespace guestbook = "http://www.28msec.com/templates/guestbook/guestbook";

import module namespace ddl = "http://www.zorba-xquery.com/modules/store/static/collections/ddl";
import module namespace dml = "http://www.zorba-xquery.com/modules/store/static/collections/dml";

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

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

declare function guestbook:list()
{
  let $entries := dml:collection($guestbook:entries)
  let $num_entries := fn:count($entries)
  return
    if($num_entries = 0)
    then

No entries, yet.

    else
      for $entry in $entries[position() gt $num_entries - 5]
      return  1
};

declare function guestbook:window() {
  let $x := 1 to 1000
  for tumbling window $w in $x
  start at $s when fn:true()
  end at $e when $e - $s eq 51
  return <window>{ $w }</window>
};

declare %ann:sequential function guestbook:init() {
  ddl:create($guestbook:entries);
};

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

module namespace guestbook = "http://www.28msec.com/templates/guestbook/guestbook";

import module namespace ddl = "http://www.zorba-xquery.com/modules/store/static/collections/ddl";
import module namespace dml = "http://www.zorba-xquery.com/modules/store/static/collections/dml";

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

declare %ann:ordered collection guestbook:entries as node()*;
declare variable $guestbook:entries as xs:QName := xs:QName("guestbook:entries");

declare %ann:sequential function guestbook:list()
{
  let $entries := dml:collection($guestbook:entries)
  let $num_entries := fn:count($entries)
  return
    if($num_entries = 0)
    then

No entries, yet.

    else
      for $entry in $entries[position() gt $num_entries - 5]
      return  1
};

declare function guestbook:window() {
 let $x := 1 to 1000
 for tumbling window $w in $x
 start at $s when fn:true()
 end at $e when $e - $s eq 51
 return <window>{ $w }</window>
};

declare %ann:sequential function guestbook:init() {
  ddl:create($guestbook:entries);
};


Update BaseX Web module

Update BaseX Web module

xquery version "3.1";
(:~
 : BaseX Web Module functions
 :
 : @see http://docs.basex.org/wiki/Web_Module
 :)
module namespace web = "http://basex.org/modules/web";

declare namespace a = "http://reecedunn.co.uk/xquery/annotations";
declare namespace o = "http://reecedunn.co.uk/xquery/options";

declare namespace rest = "http://exquery.org/ns/restxq";

declare option o:requires "basex/8.1";

declare %a:since("basex", "8.1") function web:content-type($path as xs:string) as xs:string external;
declare %a:restrict-until("$parameters", "basex", "8.2", "map(*)")
        %a:restrict-until("$parameters", "basex", "9.0", "map(item(), item()*)")
        %a:restrict-since("$parameters", "basex", "8.5", "map(*)")
        %a:since("basex", "8.1") function web:create-url($url as xs:string, $parameters as map(*)) as xs:string external;
declare %a:since("basex", "9.2") function web:create-url($url as xs:string, $parameters as map(*), $anchor as xs:string) as xs:string external;
declare %a:since("basex", "8.2") function web:encode-url($string as xs:string) as xs:string external;
declare %a:since("basex", "8.2") function web:decode-url($string as xs:string) as xs:string external;
declare %a:since("basex", "8.1") function web:redirect($location as xs:string) as element(rest:response) external;
declare %a:since("basex", "8.1") function web:redirect($location as xs:string, $parameters as map(*)) as element(rest:response) external;
declare %a:since("basex", "9.2") function web:redirect($location as xs:string, $parameters as map(*), $anchor as xs:string) as element(rest:response) external;
declare %a:since("basex", "8.1") function web:response-header() as element(rest:response) external;
declare %a:restrict-until("$output", "basex", "9.0", "map(*)")
        %a:since("basex", "8.1") function web:response-header($output as map(*)?) as element(rest:response) external;
declare %a:restrict-until("$output", "basex", "9.0", "map(*)")
        %a:restrict-until("$headers", "basex", "8.2", "map(*)")
        %a:restrict-until("$headers", "basex", "9.0", "map(xs:string, xs:string)")
        %a:since("basex", "8.1") function web:response-header($output as map(*)?, $headers as map(*)?) as element(rest:response) external;
declare %a:since("basex", "9.0") function web:response-header($output as map(*)?, $headers as map(*)?, $atts as map(*)?) as element(rest:response) external;
declare %a:since("basex", "9.3") function web:error($status as xs:integer, $message as xs:string) as none external;
declare %a:since("basex", "9.3") function web:forward($path as xs:string) as element(rest:forward) external;
declare %a:since("basex", "9.3") function web:forward($path as xs:string, $parameters as map(*)) as element(rest:forward) external;

xquery version "3.1";
(:~
 : BaseX Web Module functions
 :
 : @see http://docs.basex.org/wiki/Web_Module
 :)
module namespace web = "http://basex.org/modules/web";

declare namespace a = "http://reecedunn.co.uk/xquery/annotations";
declare namespace o = "http://reecedunn.co.uk/xquery/options";

declare namespace rest = "http://exquery.org/ns/restxq";

declare option o:requires "basex/8.1";

declare %a:since("basex", "8.1") function web:content-type($path as xs:string) as xs:string external;
declare %a:restrict-until("$parameters", "basex", "8.2", "map(*)")
        %a:restrict-until("$parameters", "basex", "9.0", "map(item(), item()*)")
        %a:restrict-since("$parameters", "basex", "8.5", "map(*)")
        %a:since("basex", "8.1") function web:create-url($url as xs:string, $parameters as map(*)) as xs:string external;
declare %a:since("basex", "8.2") function web:encode-url($string as xs:string) as xs:string external;
declare %a:since("basex", "8.2") function web:decode-url($string as xs:string) as xs:string external;
declare %a:since("basex", "8.1") function web:redirect($location as xs:string) as element(rest:response) external;
declare %a:since("basex", "8.1") function web:redirect($location as xs:string, $parameters as map(*)) as element(rest:response) external;
declare %a:since("basex", "8.1") function web:response-header() as element(rest:response) external;
declare %a:restrict-until("$output", "basex", "9.0", "map(*)")
        %a:since("basex", "8.1") function web:response-header($output as map(*)?) as element(rest:response) external;
declare %a:restrict-until("$output", "basex", "9.0", "map(*)")
        %a:restrict-until("$headers", "basex", "8.2", "map(*)")
        %a:restrict-until("$headers", "basex", "9.0", "map(xs:string, xs:string)")
        %a:since("basex", "8.1") function web:response-header($output as map(*)?, $headers as map(*)?) as element(rest:response) external;
declare %a:since("basex", "9.0") function web:response-header($output as map(*)?, $headers as map(*)?, $atts as map(*)?) as element(rest:response) external;