28
28
import org .exist .security .PermissionDeniedException ;
29
29
import org .exist .storage .lock .Lock ;
30
30
import org .exist .util .LockException ;
31
+ import org .exist .util .PatternFactory ;
31
32
import org .exist .xmldb .XmldbURI ;
32
33
import org .exist .xquery .*;
33
34
import org .exist .xquery .value .*;
@@ -44,10 +45,29 @@ public class FunUriCollection extends BasicFunction {
44
45
45
46
private static final String FN_NAME = "uri-collection" ;
46
47
private static final String FN_DESCRIPTION = "Returns a sequence of xs:anyURI values that represent the URIs in a URI collection." ;
47
- private static final FunctionReturnSequenceType FN_RETURN = returnsOptMany (Type .STRING ,
48
+ private static final FunctionReturnSequenceType FN_RETURN = returnsOptMany (Type .ANY_URI ,
48
49
"the default URI collection, if $arg is not specified or is an empty sequence, " +
49
50
"or the sequence of URIs that correspond to the supplied URI" );
50
- private static final FunctionParameterSequenceType ARG = optParam ("arg" , Type .STRING , "The base-URI property from the static context, or an empty sequence" );
51
+ private static final FunctionParameterSequenceType ARG = optParam ("arg" , Type .STRING ,
52
+ "An xs:string identifying a URI Collection. " +
53
+ "The argument is interpreted as either an absolute xs:anyURI, or a relative xs:anyURI resolved " +
54
+ "against the base-URI property from the static context. In eXist-db this function consults the " +
55
+ "query hierarchy of the database. Query String parameters may be provided to " +
56
+ "control the URIs returned by this function. " +
57
+ "The parameter `match` may be used to provide a Regular Expression against which the result " +
58
+ "sequence of URIs are filtered. " +
59
+ "The parameter `content-type` may be used to determine the Internet Media Type (or generally " +
60
+ "whether XML, Binary, and/or (Sub) Collection) URIs that are returned in the result sequence; " +
61
+ "the special values: 'application/vnd.existdb.collection' includes (Sub) Collections, " +
62
+ "'application/vnd.existdb.document' includes any document, " +
63
+ "'application/vnd.existdb.document+xml' includes only XML documents, and " +
64
+ "'application/vnd.existdb.document+binary' includes only Binary documents. By default, " +
65
+ "`content-type=application/vnd.existdb.collection,application/vnd.existdb.document` " +
66
+ "(i.e. all Collections and Documents). " +
67
+ "The parameter `stable` may be used to determine if the function is deterministic. " +
68
+ "By default `stable=yes` to ensure that the same results are returned by each call within the same " +
69
+ "query."
70
+ );
51
71
public static final FunctionSignature [] FS_URI_COLLECTION_SIGNATURES = functionSignatures (
52
72
FN_NAME ,
53
73
FN_DESCRIPTION ,
@@ -87,13 +107,9 @@ public FunUriCollection(final XQueryContext context, final FunctionSignature sig
87
107
public Sequence eval (final Sequence [] args , final Sequence contextSequence ) throws XPathException {
88
108
final Sequence result ;
89
109
if (args .length == 0 || args [0 ].isEmpty () || args [0 ].toString ().isEmpty ()) {
90
- if (XQueryContext .DEFAULT_URI_COLLECTION != null && XQueryContext .DEFAULT_URI_COLLECTION .length () > 0 ) {
91
- result = new StringValue (XQueryContext .DEFAULT_URI_COLLECTION );
92
- } else {
93
- throw new XPathException (this , ErrorCodes .FODC0002 , "No URI is supplied and default resource collection is absent." );
94
- }
110
+ result = new AnyURIValue (XmldbURI .ROOT_COLLECTION );
95
111
} else {
96
- final List <String > resultUris = new LinkedList <>();
112
+ final List <String > resultUris = new ArrayList <>();
97
113
98
114
final String uriWithQueryString = args [0 ].toString ();
99
115
final int queryStringIndex = uriWithQueryString .indexOf ('?' );
@@ -127,45 +143,53 @@ public Sequence eval(final Sequence[] args, final Sequence contextSequence) thro
127
143
queryStringMap .get (KEY_CONTENT_TYPE ).equals (VALUE_CONTENT_TYPE_DOCUMENT_XML ));
128
144
129
145
try (final Collection collection = context .getBroker ().openCollection (uri , Lock .LockMode .READ_LOCK )) {
130
- if (binaryUrisIncluded || xmlUrisIncluded ) {
131
- final Iterator <DocumentImpl > documentIterator = collection .iterator (context .getBroker ());
132
- while (documentIterator .hasNext ()) {
133
- final DocumentImpl document = documentIterator .next ();
134
- if ((xmlUrisIncluded && !(document instanceof BinaryDocument )) ||
135
- (binaryUrisIncluded && document instanceof BinaryDocument )) {
136
- resultUris .add (document .getURI ().toString ());
146
+ if (collection != null ) {
147
+ if (binaryUrisIncluded || xmlUrisIncluded ) {
148
+ final Iterator <DocumentImpl > documentIterator = collection .iterator (context .getBroker ());
149
+ while (documentIterator .hasNext ()) {
150
+ final DocumentImpl document = documentIterator .next ();
151
+ if ((xmlUrisIncluded && !(document instanceof BinaryDocument )) ||
152
+ (binaryUrisIncluded && document instanceof BinaryDocument )) {
153
+ resultUris .add (document .getURI ().toString ());
154
+ }
137
155
}
138
156
}
139
- }
140
157
141
- if (subcollectionUrisIncluded ) {
142
- final Iterator <XmldbURI > collectionsIterator = collection .collectionIterator (context .getBroker ());
143
- while (collectionsIterator .hasNext ()) {
144
- resultUris .add (uri .append (collectionsIterator .next ()).toString ());
158
+ if (subcollectionUrisIncluded ) {
159
+ final Iterator <XmldbURI > collectionsIterator = collection .collectionIterator (context .getBroker ());
160
+ while (collectionsIterator .hasNext ()) {
161
+ resultUris .add (uri .append (collectionsIterator .next ()).toString ());
162
+ }
145
163
}
164
+ } else {
165
+ throw new XPathException (this , ErrorCodes .FODC0002 , String .format ("Collection \" %s\" not found." , uri ));
146
166
}
147
167
} catch (final LockException | PermissionDeniedException e ) {
148
168
throw new XPathException (this , ErrorCodes .FODC0002 , e );
149
169
}
150
170
151
171
if (queryStringMap .containsKey (KEY_MATCH ) && queryStringMap .get (KEY_MATCH ).length () > 0 ) {
152
- final Pattern pattern = Pattern . compile (queryStringMap .get (KEY_MATCH ));
172
+ final Pattern pattern = PatternFactory . getInstance (). getPattern (queryStringMap .get (KEY_MATCH ));
153
173
final List <String > matchedResultUris = resultUris .stream ().filter (resultUri -> pattern .matcher (resultUri ).find ()).collect (Collectors .toList ());
154
174
if (matchedResultUris .isEmpty ()) {
155
175
result = Sequence .EMPTY_SEQUENCE ;
156
176
} else {
157
177
result = new ValueSequence ();
158
178
for (String resultUri : matchedResultUris ) {
159
- result .add (new StringValue (resultUri ));
179
+ result .add (new AnyURIValue (resultUri ));
160
180
}
161
181
}
162
182
} else {
163
183
result = new ValueSequence ();
164
184
for (String resultUri : resultUris ) {
165
- result .add (new StringValue (resultUri ));
185
+ result .add (new AnyURIValue (resultUri ));
166
186
}
167
187
}
168
- context .getCachedUriCollectionResults ().put (uriWithoutStableQueryString , result );
188
+
189
+ // only store the result if they were not previously stored - otherwise we loose stability!
190
+ if (!context .getCachedUriCollectionResults ().containsKey (uriWithoutStableQueryString )) {
191
+ context .getCachedUriCollectionResults ().put (uriWithoutStableQueryString , result );
192
+ }
169
193
}
170
194
}
171
195
0 commit comments