Skip to content

Commit adebc77

Browse files
authored
Merge pull request #3644 from dizzzz/feature/add_function_document-uri0
Implement fn:document-uri#0
2 parents 99d278f + 1a5e147 commit adebc77

File tree

3 files changed

+131
-64
lines changed

3 files changed

+131
-64
lines changed

exist-core/src/main/java/org/exist/xquery/functions/fn/FnModule.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,8 @@ public class FnModule extends AbstractInternalModule {
7171
new FunctionDef(FunDistinctValues.signatures[1], FunDistinctValues.class),
7272
new FunctionDef(FunDoc.signature, FunDoc.class),
7373
new FunctionDef(FunDocAvailable.signature, FunDocAvailable.class),
74-
new FunctionDef(FunDocumentURI.signature, FunDocumentURI.class),
74+
new FunctionDef(FunDocumentURI.FS_DOCUMENT_URI_0, FunDocumentURI.class),
75+
new FunctionDef(FunDocumentURI.FS_DOCUMENT_URI_1, FunDocumentURI.class),
7576
new FunctionDef(FunEmpty.signature, FunEmpty.class),
7677
new FunctionDef(FunEncodeForURI.signature, FunEncodeForURI.class),
7778
new FunctionDef(FunEndsWith.signatures[0], FunEndsWith.class),

exist-core/src/main/java/org/exist/xquery/functions/fn/FunDocumentURI.java

Lines changed: 79 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -21,86 +21,103 @@
2121
*/
2222
package org.exist.xquery.functions.fn;
2323

24-
import org.exist.dom.persistent.NodeProxy;
25-
import org.exist.dom.QName;
2624
import org.exist.dom.memtree.DocumentImpl;
25+
import org.exist.dom.persistent.NodeProxy;
2726
import org.exist.xmldb.XmldbURI;
28-
import org.exist.xquery.Cardinality;
29-
import org.exist.xquery.Dependency;
30-
import org.exist.xquery.Function;
31-
import org.exist.xquery.FunctionSignature;
32-
import org.exist.xquery.Profiler;
33-
import org.exist.xquery.XPathException;
34-
import org.exist.xquery.XQueryContext;
35-
import org.exist.xquery.value.AnyURIValue;
36-
import org.exist.xquery.value.FunctionReturnSequenceType;
37-
import org.exist.xquery.value.FunctionParameterSequenceType;
38-
import org.exist.xquery.value.Item;
39-
import org.exist.xquery.value.NodeValue;
40-
import org.exist.xquery.value.Sequence;
41-
import org.exist.xquery.value.SequenceType;
42-
import org.exist.xquery.value.Type;
27+
import org.exist.xquery.*;
28+
import org.exist.xquery.value.*;
29+
30+
import static org.exist.xquery.FunctionDSL.optManyParam;
31+
import static org.exist.xquery.FunctionDSL.returnsOpt;
32+
import static org.exist.xquery.functions.fn.FnModule.functionSignature;
4333

4434
/**
4535
* @author wolf
36+
* @author Dannes
4637
*/
4738
public class FunDocumentURI extends Function {
4839

49-
public final static FunctionSignature signature =
50-
new FunctionSignature(
51-
new QName("document-uri", Function.BUILTIN_FUNCTION_NS),
52-
"Returns the absolute URI of the resource from which the " +
53-
"document node $document-node was constructed. " +
54-
"If none such URI exists returns the empty sequence. " +
55-
"If $document-node is the empty sequence, returns the empty sequence.",
56-
new SequenceType[] {
57-
new FunctionParameterSequenceType("document-node", Type.NODE,
58-
Cardinality.ZERO_OR_ONE, "The document node")
59-
},
60-
new FunctionReturnSequenceType(Type.ANY_URI, Cardinality.ZERO_OR_ONE,
61-
"the document URI of $document-node")
62-
);
63-
64-
public FunDocumentURI(XQueryContext context) {
40+
private static final FunctionParameterSequenceType FS_PARAM_NODE = optManyParam("value", Type.NODE, "The document node.");
41+
42+
private static final String FS_DOCUMENT_URI = "document-uri";
43+
private static final String FS_DESCRIPTION = "Returns the URI of a resource where a document can be found, if available.";
44+
45+
private static final String FS_RETURN_DESCRIPTION = "The URI of a resource.";
46+
static final FunctionSignature FS_DOCUMENT_URI_0 = functionSignature(
47+
FS_DOCUMENT_URI,
48+
FS_DESCRIPTION,
49+
returnsOpt(Type.ANY_URI, FS_RETURN_DESCRIPTION)
50+
);
51+
52+
static final FunctionSignature FS_DOCUMENT_URI_1 = functionSignature(
53+
FS_DOCUMENT_URI,
54+
FS_DESCRIPTION,
55+
returnsOpt(Type.ANY_URI, FS_RETURN_DESCRIPTION),
56+
FS_PARAM_NODE
57+
);
58+
59+
public FunDocumentURI(final XQueryContext context, final FunctionSignature signature) {
6560
super(context, signature);
6661
}
6762

68-
/* (non-Javadoc)
69-
* @see org.exist.xquery.Expression#eval(org.exist.xquery.StaticContext, org.exist.dom.persistent.DocumentSet, org.exist.xquery.value.Sequence, org.exist.xquery.value.Item)
70-
*/
71-
public Sequence eval(Sequence contextSequence, Item contextItem)
72-
throws XPathException {
63+
@Override
64+
public Sequence eval(final Sequence contextSequence, final Item contextItem) throws XPathException {
7365
if (context.getProfiler().isEnabled()) {
7466
context.getProfiler().start(this);
7567
context.getProfiler().message(this, Profiler.DEPENDENCIES,
76-
"DEPENDENCIES", Dependency.getDependenciesName(this.getDependencies()));
77-
if (contextSequence != null)
78-
{context.getProfiler().message(this, Profiler.START_SEQUENCES,
79-
"CONTEXT SEQUENCE", contextSequence);}
80-
if (contextItem != null)
81-
{context.getProfiler().message(this, Profiler.START_SEQUENCES,
82-
"CONTEXT ITEM", contextItem.toSequence());}
68+
"DEPENDENCIES", Dependency.getDependenciesName(this.getDependencies()));
69+
if (contextSequence != null) {
70+
context.getProfiler().message(this, Profiler.START_SEQUENCES, "CONTEXT SEQUENCE", contextSequence);
71+
}
72+
if (contextItem != null) {
73+
context.getProfiler().message(this, Profiler.START_SEQUENCES, "CONTEXT ITEM", contextItem.toSequence());
74+
}
75+
}
76+
77+
final boolean contextItemIsAbsent = (contextItem == null);
78+
final boolean argumentIsOmitted = (getArgumentCount() == 0);
79+
80+
// Error condition
81+
if(argumentIsOmitted && contextItemIsAbsent){
82+
throw new XPathException(this, ErrorCodes.XPDY0002, "Context item is absent.");
8383
}
84-
final Sequence seq = getArgument(0).eval(contextSequence, contextItem);
84+
85+
// Get sequence from contextItem or from context Sequence
86+
final Sequence seq = (argumentIsOmitted)
87+
? contextItem.toSequence()
88+
: getArgument(0).eval(contextSequence, contextItem);
89+
90+
// Rule 1: If $arg is the empty sequence, the function returns the empty sequence.
91+
if(seq.isEmpty()){
92+
return Sequence.EMPTY_SEQUENCE;
93+
}
94+
95+
// Error condition
96+
if (argumentIsOmitted && !Type.subTypeOf(seq.getItemType(), Type.NODE)) {
97+
throw new XPathException(this, ErrorCodes.XPTY0004, "Context item is not a node.");
98+
}
99+
100+
101+
// Error condition: Returns the empty sequence if the node is not a document
85102
Sequence result = Sequence.EMPTY_SEQUENCE;
86-
if (!seq.isEmpty()) {
87-
final NodeValue value = (NodeValue) seq.itemAt(0);
88-
if (value.getImplementationType() == NodeValue.PERSISTENT_NODE) {
89-
final NodeProxy node = (NodeProxy) value;
90-
//Returns the empty sequence if the node is not a document node.
91-
if (node.isDocument()) {
92-
final XmldbURI path = node.getOwnerDocument().getURI();
93-
result = new AnyURIValue(path);
94-
}
95-
} else {
96-
if (value instanceof DocumentImpl &&
97-
((DocumentImpl)value).getDocumentURI() != null) {
98-
result = new AnyURIValue(((DocumentImpl)value).getDocumentURI());
99-
}
103+
104+
final NodeValue value = (NodeValue) seq.itemAt(0);
105+
if (value.getImplementationType() == NodeValue.PERSISTENT_NODE) {
106+
final NodeProxy node = (NodeProxy) value;
107+
if (node.isDocument()) {
108+
final XmldbURI path = node.getOwnerDocument().getURI();
109+
result = new AnyURIValue(path);
110+
}
111+
112+
} else {
113+
if (value instanceof DocumentImpl && ((DocumentImpl) value).getDocumentURI() != null) {
114+
result = new AnyURIValue(((DocumentImpl) value).getDocumentURI());
100115
}
101116
}
102-
if (context.getProfiler().isEnabled())
103-
{context.getProfiler().end(this, "", result);}
117+
118+
if (context.getProfiler().isEnabled()) {
119+
context.getProfiler().end(this, "", result);
120+
}
104121
return result;
105122
}
106123
}

exist-core/src/test/xquery/fn.xql

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ function fnt:cleanup() {
4848
xmldb:remove("/db/fn-test")
4949
};
5050

51-
declare
51+
declare
5252
%test:args("NFC")
5353
%test:assertEquals(6, 6)
5454
%test:args("NFD")
@@ -126,3 +126,52 @@ function fnt:tokenize-onearg($str as xs:string) {
126126
tokenize($str)
127127
};
128128

129+
declare
130+
%test:assertError("err:XPDY0002")
131+
function fnt:document-uri0_noarg() {
132+
document-uri()
133+
};
134+
135+
declare
136+
%test:assertError("err:XPDY0002")
137+
function fnt:document-uri0() {
138+
document-uri(.)
139+
};
140+
141+
declare
142+
%test:assertEquals("/db/fn-test/test.xml")
143+
function fnt:document-uri0_context() {
144+
root(collection('/db/fn-test')//book)/document-uri()
145+
};
146+
147+
declare
148+
%test:assertError("err:XPDY0002")
149+
function fnt:document-uri0_context_empty() {
150+
root(collection('/db/fn-test')//bookies)/document-uri()
151+
};
152+
153+
declare
154+
%test:assertEquals("/db/fn-test/test.xml")
155+
function fnt:document-uri1() {
156+
document-uri(root(collection('/db/fn-test')//book))
157+
};
158+
159+
declare
160+
%test:assertEmpty
161+
function fnt:document-uri1_empty() {
162+
document-uri(root(collection('/db/fn-test')//bookies))
163+
};
164+
165+
declare
166+
%test:assertTrue
167+
function fnt:fn-document-uri-32() {
168+
let $uri := "/db/fn-test/test.xml"
169+
return
170+
fn:contains(fn:doc($uri)/document-uri(), $uri)
171+
};
172+
173+
declare
174+
%test:assertError("err:XPTY0004")
175+
function fnt:document-uri0_no_node_context() {
176+
util:eval-with-context("document-uri()", (), false(), "a")
177+
};

0 commit comments

Comments
 (0)