Scope and statically known namespaces in XQuery - xquery

consider a library module ctx
xquery version "3.1";
module namespace ctx="module/ctx";
declare function ctx:resolve (
$ctx as function(xs:string) as xs:QName
) as function(xs:string, xs:integer) as function(*)? {
function ($name as xs:string, $arity as xs:integer) as function(*)? {
function-lookup($ctx($name), $arity)
}
};
and a library module a
xquery version "3.1";
module namespace a="module/a";
declare function a:f () { "a:f" };
And a main module
xquery version "3.1";
import module namespace a="module/a" at "a.xqm";
import module namespace ctx="module/ctx" at "ctx.xqm";
ctx:resolve(xs:QName(?))("a:f", 0)()
Is it safe to assume that the function reference returned by xs:QName(?) will keep the context in which the namespace a is declared so that the main module will output "a:f"?
This does work in eXist-db (tested on 5.3.0) but I am not sure if this code is portable to other XQuery 3.1 processors.
---- UPDATE ----
What does not work in eXist-db 5.3.0 is
import module namespace ctx="module/ctx" at "ctx.xqm";
declare namespace app = "app";
declare function app:f () { "app:f" };
ctx:resolve(xs:QName(?))("app:f", 0)()

It's an excellent question.
If you used a named function reference xs:QName#1, then you would find the answer in ยง3.1.6:
Furthermore, if the function returned by the evaluation of a
NamedFunctionRef has an implementation-dependent implementation, then
the implementation of this function is associated with the static
context of this NamedFunctionRef expression and with the dynamic
context in which the NamedFunctionRef is evaluated.
That's rather arcane language, but what it means is that the function you get back uses the static and dynamic context of the named function reference itself (and not the context at the point where the function is actually called).
For a partial function application xs:QName(?) the language is even more impenetrable:
Implementation: The implementation of F. If this is not an XQuery 3.1
expression then the new function's implementation is associated with a
static context and a dynamic context in one of two ways: if F's
implementation is already associated with contexts, then those are
used; otherwise, SC and DC are used.
I think the spec is assuming (without justification) that built in functions like xs:QName will have an implementation that is not an XQuery 3.1 expression, so the second sentence applies. I really don't know quite what it intends by the phrase "already associated with contexts" - perhaps it's concerned with the case where F is already a partially applied function. But in any case, I'm reasonably sure that the intent is that "SC and DC are used" - that is, xs:QName(?) works exactly like xs:QName#1, it uses the static and dynamic context at the point where the expression xs:QName(?) is evaluated.
You're quite right to be concerned that you can't assume that what existing products do is what the spec says must happen. But in this case, I think they are getting it right.

Is it safe to assume that the function reference returned by xs:QName(?) will keep the context in which the namespace a is declared so that the main module will output "a:f"?
Tested successfully with BaseX 10.3.
Had to fix ctx as it was deemed syntactically incorrect by BaseX (missing as <Type>) for the result of ctx:resolve(). Just added as function(*)? . Here is the corrected code:
xquery version "3.1";
module namespace ctx="module/ctx";
declare function ctx:resolve (
$ctx as function(xs:string) as xs:QName
) as function(xs:string, xs:integer) as function(*)? {
function ($name as xs:string, $arity as xs:integer) as function(*)? {
function-lookup($ctx($name), $arity)
}
};

Related

Xquery replace an element value that has a prefix

I'm a newbie with Xquery. An post already exists on this query but I'm having problems when the XML has a prefix as follows:
Source XML:
enter code here
<?xml version="1.0" encoding="UTF-8"?>
<so:category>
<bo:catid>1</bo:catid>
<bo:cattext>sport</bo:cattext>
</so:category>
Xquery to change value that was provided in another post:
declare namespace local = "http://example.org";
declare namespace bo = "http://example1.org";
declare function local:copy-replace($element as element()) {
if ($element/self::bo:catid)
then <bo:catid>test</bo:catid>
else element {node-name($element)}
{$element/#*,
for $child in $element/node()
return if ($child instance of element())
then local:copy-replace($child)
else $child
}
};
local:copy-replace(/*)
I have a prefix for elements in my XML document. So when I execute the query
I get the following error:
ERROR - The prefix "bo" for element "bo:catid" is not bound.
I'm not sure how to handle the prefix & have searched for related subject on the internet. However, I cannot resolve this issue with information provided & need to know what I'm doing wrong.
Your XML is well-formed according to the XML 1.0 recommendation, but it is not namespace-well-formed according to XML Namespaces 1.0 (because it uses namespace prefixes that are not bound to any namespace URI). Most tools in the XML eco-system will only handle namespace-well-formed XML, and this is an absolute requirement for using XQuery.
Declaring namespace prefixes in the query doesn't get away from the requirement to have namespace-well-formed input.

How can advance google closure compilation be used with ES6 classes and arbitrary defineProperty?

I maintain a data flow library that allows programmers to define new properties during instantiation, then does neat things at run-time with both property reads and writes, all transparently thanks to JS defineProperty. Sample usage, where TagSession is defined with the ES6 class keyword:
const sithApp = new TagSession( null, 'SithTrakSession',
{
obiTrakker: cF( c => new WebSocket('ws://localhost:4000')
.onmessage = msg => c.md.obiLoc = JSON.parse(msg.data)),
obiLoc: cI( null),
sithIds: cI([-1,-2,3616,-3,-4])
});
I can now write code where the map keywords are transparent accessors:
function SithTrak () {
return div({class: "app-container"},
h1({
class: "css-planet-monitor",
content: cF(c => "Obi-Wan currently on " +
(sithApp.obiLoc ?
sithApp.obiLoc.name : "...dunno"))
}))
}
This works great uncompiled and with Google Closure SIMPLE_OPTIMIZATION, but ADVANCED_COMPILATION warns (and the output fails) about, eg:
WARNING - Property obiLoc never defined on TagSession
withObi: cF( c=> c.md.info && sithApp.obiLoc
I have looked at all the annotations that might apply, but nothing seems suited to such a dynamic capability.
Am I missing something obvious, or is this combo of dynamism and optimization asking too much?
Dynamic properties added with this method would require using a bracket access for ADVANCED mode: sithApp['obiLoc']. In ADVANCED mode, the compiler must know about all properties accessed via the dot nation at compile time.
Since it isn't known that these properties are defined on the class you are going to get type warnings, bit it shouldn't break your code.
You can add declarations to silence the type warnings:
/** #type {?} */
TagSession.prototype.objLoc;
In other cases, you might be able to use #lends but I don't think this will work here as the types provided might not match the expected type of the property value. But there isn't enough context to be sure:
/** #lends {TagSession.prototype} */ ({
obiTrakker: ...,
obiLoc: ...,
sithIds: ...
})

Creating a new instance of a KClass

I have a Kotlin class whose primary (and only) constructor is empty.
I have a reference to this class:
val kClass: KClass<MyClass> = MyClass::class
How do I create an instance of this class using reflection?
In Java I would do myClass.newInstance() but it seems in Kotlin I need to find the constructor first:
kClass.constructors.first().call()
I have seen mention of primaryConstructor in some bug reports but it's not showing up in my IDE.
In your case, Java reflection might be enough: you can use MyClass::class.java and create a new instance in the same way as you would with Java reflection (see #IngoKegel's answer).
But in case there's more than one constructor and you really need to get the primary one (not the default no-arg one), use the primaryConstructor extension function of a KClass<T>. It is a part of Kotlin reflection, which is not shipped within kotlin-stdlib.
To use it, you have to add kotlin-reflect as a dependency, e.g. a in Gradle project:
dependencies {
compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
}
Assuming that there is ext.kotlin_version, otherwise replace $kotlin_version with the version you use.
Then you will be able to use primaryConstructor, for example:
fun <T : Any> construct(kClass: KClass<T>): T? {
val ctor = kClass.primaryConstructor
return if (ctor != null && ctor.parameters.isEmpty())
ctor.call() else
null
}
You can use the Java class to create new instance:
MyClass::class.java.newInstance()
For those checking this question now, since Kotlin 1.1 there's also createInstance() extension method on KClass
Much like the accepted answer, this function works only in case class has an empty constructor or constructor with all default arguments.
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.reflect.full/create-instance.html
Expanding on Alexeys Answer, to include a primary constructor call with parameters:
/* Example class with no-args constructor */
class MyClass
/* Example class requiring parameters */
class MyClassWithParams(parameter1: String, parameter2: MyClass)
val myKClass: KClass<MyClass> = MyClass::class
val myKClassWithParameters: KClass<MyClassWithParams> = MyClassWithParams::class
/* We can create an object by calling createInstance when no constructor parameters are required as explained in other answers. */
val myObject: MyClass = myKClass.createInstance()
/* To create an object with parameters, we need to get the constructor first, and call it with the parameters instead, similarly to how we would do in Java. */
val myObjectWithParameters: MyClassWithParams? =
myKClassWithParameters.primaryConstructor?.call(
"StringParameter", myObject
)

Writing one XQuery script in exist-db with support for multiple output formats

This is a followup question to
Getting hold of tag content in XQuery 3.0 (exist-db)
Assume that such an xquery script should be able to return the result as XML, JSON or HTML base on a query parameter like
http://host/exist/rest/db/myscript.xql?mode=xml|html|json
I know how to change the serializer from XML -> JSON and how to apply
an XSLT transformation using transform:transform().
What is the best approach for encapsulating the XML generation for result and then transforming it based on the request parameter into one of output formats?
xquery version "3.0";
module namespace services = "http://my/services";
import module namespace transform = "http://exist-db.org/xquery/transform";
declare namespace rest = "http://exquery.org/ns/restxq";
declare namespace output = "http://www.w3.org/2010/xslt-xquery-serialization";
declare
%rest:GET
%rest:path("/data.html")
%output:media-type("text/html")
%output:method("html5")
function services:home() {
transform:transform(services:example1(), doc("/db/my-xml-to-html.xslt"), ())
};
declare
%rest:GET
%rest:path("/data.json")
%rest:produces("application/json")
%output:method("json")
function services:home-json() {
services:example1()
};
declare
%rest:GET
%rest:path("/data.xml")
%rest:produces("application/xml")
function services:home-xml() {
services:example1()
};
declare
%private
function services:example1() {
<some>
<data>hello world</data>
</some>
};
I would suggest taking a look at RESTXQ in eXist, as this allows you to easily control the result format based on content negotiation or any other HTTP parameters or headers. For example using content negotiation to produce XML, JSON and HTML manifestations:
xquery version "3.0";
module namespace services = "http://my/services";
import module namespace transform = "http://exist-db.org/xquery/transform";
declare namespace rest = "http://exquery.org/ns/restxq";
declare namespace output = "http://www.w3.org/2010/xslt-xquery-serialization";
declare
%rest:GET
%rest:path("/data")
%rest:produces("text/html")
%output:method("html5")
function services:home() {
transform:transform(services:example1(), doc("/db/my-xml-to-html.xslt"), ())
};
declare
%rest:GET
%rest:path("/data")
%rest:produces("application/json")
%output:method("json")
function services:home-json() {
services:example1()
};
declare
%rest:GET
%rest:path("/data")
%rest:produces("application/xml")
function services:home-xml() {
services:example1()
};
declare
%private
function services:example1() {
<some>
<data>here</data>
</some>
};

function return type issue

According to the w3c
An ElementTest is used to match an element node by its name and/or type annotation. An ElementTest may take any of the following forms. In these forms, ElementName need not be present in the in-scope element declarations, but TypeName must be present in the in-scope schema types [err:XPST0008]. Note that substitution groups do not affect the semantics of ElementTest.
...
element(*, TypeName) matches an element node regardless of its name, if derives-from(AT, TypeName ) is true, where AT is the type annotation of the element node, and the nilled property of the node is false.
I have this function
import schema namespace cdm-base="http://cdm.basic.upc.com" at "file:///Workspace/peal/peal40/trunk/common/schema/cdm-basic.xsd";
declare function local:matchType(
$input as element()
) as element(*,cdm-base:ProductComponent..) {
<cdm-base:product xsi:type="cdm-base:ProductComponent" />
};
which while I am typing returns the error:
F [Saxon-EE XQuery 9.3.0.5] Required item type of result of function local:matchType() is element(*, ProductComponent); supplied value has item type element({http://cdm.basic.upc.com}product, {http://www.w3.org/2001/XMLSchema}untyped)
I may mistaken, but the type is actually cdm-base:ProductComponent and not untyped.
I don't get where the issue is...
I am Using Oxygen 13.0 with Saxon EE 9.3.0.5
Saxon is indeed correct here, all directly constructed ("inline") elements have the type xs:untyped (or xs:anyType if the construction mode is set to preserve).
The xsi:type element is meaningless until the element has been validated against your schemas. The easiest way to do this is to wrap the element in a validate expression:
import schema namespace cdm-base="http://cdm.basic.upc.com" at "file:///Workspace/peal/peal40/trunk/common/schema/cdm-basic.xsd";
declare function local:matchType(
$input as element())
as element(*,cdm-base:ProductComponent)
{
validate { <cdm-base:product xsi:type="cdm-base:ProductComponent" /> }
};
Note that in XQuery 3.0, if you do not actually need the xsi:type attribute then you can validate the element as a particular type:
import schema namespace cdm-base="http://cdm.basic.upc.com" at "file:///Workspace/peal/peal40/trunk/common/schema/cdm-basic.xsd";
declare function local:matchType(
$input as element())
as element(*,cdm-base:ProductComponent)
{
validate type cdm-base:ProductComponent { <cdm-base:product /> }
};

Resources