1
2
3
4
5
6
7
8
9
10
11
12
13
14 package eu.fbk.rdfpro;
15
16 import java.math.BigDecimal;
17 import java.math.BigInteger;
18 import java.util.Date;
19 import java.util.GregorianCalendar;
20 import java.util.Objects;
21 import java.util.UUID;
22 import java.util.regex.Pattern;
23
24 import javax.annotation.Nullable;
25 import javax.xml.datatype.DatatypeConfigurationException;
26 import javax.xml.datatype.DatatypeFactory;
27 import javax.xml.datatype.XMLGregorianCalendar;
28
29 import org.openrdf.model.BNode;
30 import org.openrdf.model.Literal;
31 import org.openrdf.model.URI;
32 import org.openrdf.model.Value;
33 import org.openrdf.model.datatypes.XMLDatatypeUtil;
34 import org.openrdf.model.vocabulary.RDF;
35 import org.openrdf.model.vocabulary.XMLSchema;
36 import org.openrdf.query.algebra.evaluation.ValueExprEvaluationException;
37 import org.openrdf.query.algebra.evaluation.function.BooleanCast;
38 import org.openrdf.query.algebra.evaluation.function.DateTimeCast;
39 import org.openrdf.query.algebra.evaluation.function.DecimalCast;
40 import org.openrdf.query.algebra.evaluation.function.DoubleCast;
41 import org.openrdf.query.algebra.evaluation.function.FloatCast;
42 import org.openrdf.query.algebra.evaluation.function.Function;
43 import org.openrdf.query.algebra.evaluation.function.IntegerCast;
44 import org.openrdf.query.algebra.evaluation.function.datetime.Day;
45 import org.openrdf.query.algebra.evaluation.function.datetime.Hours;
46 import org.openrdf.query.algebra.evaluation.function.datetime.Minutes;
47 import org.openrdf.query.algebra.evaluation.function.datetime.Month;
48 import org.openrdf.query.algebra.evaluation.function.datetime.Now;
49 import org.openrdf.query.algebra.evaluation.function.datetime.Seconds;
50 import org.openrdf.query.algebra.evaluation.function.datetime.Timezone;
51 import org.openrdf.query.algebra.evaluation.function.datetime.Tz;
52 import org.openrdf.query.algebra.evaluation.function.datetime.Year;
53 import org.openrdf.query.algebra.evaluation.function.hash.MD5;
54 import org.openrdf.query.algebra.evaluation.function.hash.SHA1;
55 import org.openrdf.query.algebra.evaluation.function.hash.SHA256;
56 import org.openrdf.query.algebra.evaluation.function.hash.SHA384;
57 import org.openrdf.query.algebra.evaluation.function.hash.SHA512;
58 import org.openrdf.query.algebra.evaluation.function.numeric.Abs;
59 import org.openrdf.query.algebra.evaluation.function.numeric.Ceil;
60 import org.openrdf.query.algebra.evaluation.function.numeric.Floor;
61 import org.openrdf.query.algebra.evaluation.function.numeric.Rand;
62 import org.openrdf.query.algebra.evaluation.function.numeric.Round;
63 import org.openrdf.query.algebra.evaluation.function.rdfterm.StrDt;
64 import org.openrdf.query.algebra.evaluation.function.rdfterm.StrLang;
65 import org.openrdf.query.algebra.evaluation.function.string.Concat;
66 import org.openrdf.query.algebra.evaluation.function.string.Contains;
67 import org.openrdf.query.algebra.evaluation.function.string.EncodeForUri;
68 import org.openrdf.query.algebra.evaluation.function.string.LowerCase;
69 import org.openrdf.query.algebra.evaluation.function.string.Replace;
70 import org.openrdf.query.algebra.evaluation.function.string.StrAfter;
71 import org.openrdf.query.algebra.evaluation.function.string.StrBefore;
72 import org.openrdf.query.algebra.evaluation.function.string.StrEnds;
73 import org.openrdf.query.algebra.evaluation.function.string.StrLen;
74 import org.openrdf.query.algebra.evaluation.function.string.StrStarts;
75 import org.openrdf.query.algebra.evaluation.function.string.Substring;
76 import org.openrdf.query.algebra.evaluation.function.string.UpperCase;
77 import org.openrdf.query.algebra.evaluation.util.QueryEvaluationUtil;
78
79 import eu.fbk.rdfpro.GroovyProcessor.GroovyLiteral;
80 import eu.fbk.rdfpro.util.Hash;
81 import eu.fbk.rdfpro.util.Statements;
82
83 final class SparqlFunctions {
84
85 private static final DatatypeFactory DATATYPE_FACTORY;
86
87 static {
88 try {
89 DATATYPE_FACTORY = DatatypeFactory.newInstance();
90 } catch (final DatatypeConfigurationException ex) {
91 throw new Error("Could not instantiate javax.xml.datatype.DatatypeFactory", ex);
92 }
93 }
94
95 public static boolean isiri(final Object arg) {
96 return toRDF(arg) instanceof URI;
97 }
98
99 public static boolean isblank(final Object arg) {
100 return toRDF(arg) instanceof BNode;
101 }
102
103 public static boolean isliteral(final Object arg) {
104 return toRDF(arg) instanceof Literal;
105 }
106
107 public static boolean isnumeric(final Object arg) {
108 final Value value = toRDF(arg);
109 if (value instanceof Literal) {
110 final URI datatype = ((Literal) value).getDatatype();
111 return XMLDatatypeUtil.isNumericDatatype(datatype);
112 }
113 return false;
114 }
115
116 public static String str(final Object arg) {
117 final Value value = toRDF(arg);
118 if (value instanceof URI || value instanceof Literal) {
119 return value.stringValue();
120 }
121 throw new IllegalArgumentException("str() argument is not an URI or literal");
122 }
123
124 public static String lang(final Object arg) {
125 final Value value = toRDF(arg);
126 if (value instanceof Literal) {
127 final String lang = ((Literal) value).getLanguage();
128 return lang == null ? "" : lang;
129 }
130 throw new IllegalArgumentException("lang() argument is not a literal");
131 }
132
133 public static URI datatype(final Object arg) {
134 final Value value = toRDF(arg);
135 if (value instanceof Literal) {
136 final Literal literal = (Literal) value;
137 final URI datatype = literal.getDatatype();
138 if (datatype != null) {
139 return datatype;
140 } else if (literal.getLanguage() != null) {
141 return RDF.LANGSTRING;
142 } else {
143 return XMLSchema.STRING;
144 }
145 }
146 throw new IllegalArgumentException("datatype() argument is not a literal");
147 }
148
149 public static URI iri(final Object arg) {
150 if (arg instanceof URI) {
151 return (URI) arg;
152 }
153 Objects.requireNonNull(arg);
154 return Statements.VALUE_FACTORY.createURI(arg.toString());
155 }
156
157 public static BNode bnode() {
158 return Statements.VALUE_FACTORY.createBNode();
159 }
160
161 public static BNode bnode(final Object arg) {
162 if (arg instanceof BNode) {
163 return (BNode) arg;
164 }
165 Objects.requireNonNull(arg);
166 return Statements.VALUE_FACTORY.createBNode(Hash.murmur3(arg.toString()).toString());
167 }
168
169 public static Literal strdt(final Object value, final Object datatype) {
170 return (Literal) evaluate(new StrDt(), value, datatype);
171 }
172
173 public static Literal strlang(@Nullable final Object value, @Nullable final Object lang) {
174 return (Literal) evaluate(new StrLang(), value, lang);
175 }
176
177 public static URI uuid() {
178 return Statements.VALUE_FACTORY.createURI("urn:uuid:" + UUID.randomUUID().toString());
179 }
180
181 public static String struuid() {
182 return UUID.randomUUID().toString();
183 }
184
185 public static int strlen(final Object arg) {
186 return ((Literal) evaluate(new StrLen(), arg)).intValue();
187 }
188
189 public static Literal substr(final Object string, final Object from) {
190 return (Literal) evaluate(new Substring(), string, from);
191 }
192
193 public static Literal substr(final Object string, final Object from, final Object length) {
194 return (Literal) evaluate(new Substring(), string, from);
195 }
196
197 public static Literal ucase(final Object arg) {
198 return (Literal) evaluate(new UpperCase(), arg);
199 }
200
201 public static Literal lcase(final Object arg) {
202 return (Literal) evaluate(new LowerCase(), arg);
203 }
204
205 public static boolean strstarts(final Object arg1, final Object arg2) {
206 return ((Literal) evaluate(new StrStarts(), arg1, arg2)).booleanValue();
207 }
208
209 public static boolean strends(final Object arg1, final Object arg2) {
210 return ((Literal) evaluate(new StrEnds(), arg1, arg2)).booleanValue();
211 }
212
213 public static boolean contains(final Object arg1, final Object arg2) {
214 return ((Literal) evaluate(new Contains(), arg1, arg2)).booleanValue();
215 }
216
217 public static Literal strbefore(final Object arg1, final Object arg2) {
218 return (Literal) evaluate(new StrBefore(), arg1, arg2);
219 }
220
221 public static Literal strafter(final Object arg1, final Object arg2) {
222 return (Literal) evaluate(new StrAfter(), arg1, arg2);
223 }
224
225 public static String encode_for_uri(final Object arg) {
226 return ((Literal) evaluate(new EncodeForUri(), arg)).stringValue();
227 }
228
229 public static Literal concat(final Object... args) {
230 return (Literal) evaluate(new Concat(), args);
231 }
232
233 public static boolean langmatches(final Object languageTag, final Object languageRange) {
234
235 final Value tagValue = toRDF(languageTag);
236 final Value rangeValue = toRDF(languageRange);
237
238 if (QueryEvaluationUtil.isSimpleLiteral(tagValue)
239 && QueryEvaluationUtil.isSimpleLiteral(rangeValue)) {
240
241 final String tag = ((Literal) tagValue).getLabel();
242 final String range = ((Literal) rangeValue).getLabel();
243
244 if (range.equals("*")) {
245 return tag.length() > 0;
246 } else if (tag.length() == range.length()) {
247 return tag.equalsIgnoreCase(range);
248 } else if (tag.length() > range.length()) {
249 final String prefix = tag.substring(0, range.length());
250 return prefix.equalsIgnoreCase(range) && tag.charAt(range.length()) == '-';
251 } else {
252 return false;
253 }
254 }
255
256 throw new IllegalArgumentException("LANGMATCHES() cannot be applied to " + languageTag
257 + ", " + languageRange);
258 }
259
260 public static boolean regex(final Object text, final Object pattern) {
261 return regex(text, pattern, "");
262 }
263
264 public static boolean regex(final Object text, final Object pattern,
265 @Nullable final Object flags) {
266
267 final Value textValue = toRDF(text);
268 final Value patternValue = toRDF(pattern);
269 final Value flagsValue = toRDF(flags);
270
271 if (QueryEvaluationUtil.isStringLiteral(textValue)
272 && QueryEvaluationUtil.isSimpleLiteral(patternValue)
273 && QueryEvaluationUtil.isSimpleLiteral(flagsValue)) {
274
275 final String textStr = ((Literal) textValue).getLabel();
276 final String patternStr = ((Literal) patternValue).getLabel();
277 final String flagsStr = ((Literal) flagsValue).getLabel();
278
279 int f = 0;
280 for (final char c : flagsStr.toCharArray()) {
281 switch (c) {
282 case 's':
283 f |= Pattern.DOTALL;
284 break;
285 case 'm':
286 f |= Pattern.MULTILINE;
287 break;
288 case 'i':
289 f |= Pattern.CASE_INSENSITIVE;
290 break;
291 case 'x':
292 f |= Pattern.COMMENTS;
293 break;
294 case 'd':
295 f |= Pattern.UNIX_LINES;
296 break;
297 case 'u':
298 f |= Pattern.UNICODE_CASE;
299 break;
300 default:
301 throw new IllegalArgumentException("REGEX() flag not valid: " + c);
302 }
303 }
304 return Pattern.compile(patternStr, f).matcher(textStr).find();
305 }
306
307 throw new IllegalArgumentException("REGEX() arguments not valid: " + text + ", " + pattern
308 + (flags == null ? "" : ", " + flags));
309 }
310
311 public static Literal replace(final Object arg, final Object pattern, final Object replacement) {
312 return replace(arg, pattern, replacement, "");
313 }
314
315 public static Literal replace(final Object arg, final Object pattern,
316 final Object replacement, final Object flags) {
317 return (Literal) evaluate(new Replace(), arg, pattern, replacement, flags);
318 }
319
320 public static double abs(final Object arg) {
321 return ((Literal) evaluate(new Abs(), arg)).doubleValue();
322 }
323
324 public static long round(final Object arg) {
325 return ((Literal) evaluate(new Round(), arg)).longValue();
326 }
327
328 public static long ceil(final Object arg) {
329 return ((Literal) evaluate(new Ceil(), arg)).longValue();
330 }
331
332 public static long floor(final Object arg) {
333 return ((Literal) evaluate(new Floor(), arg)).longValue();
334 }
335
336 public static double rand() {
337 return ((Literal) evaluate(new Rand())).doubleValue();
338 }
339
340 public static Literal now() {
341 return (Literal) evaluate(new Now());
342 }
343
344 public static int year(final Object arg) {
345 return ((Literal) evaluate(new Year(), arg)).intValue();
346 }
347
348 public static int month(final Object arg) {
349 return ((Literal) evaluate(new Month(), arg)).intValue();
350 }
351
352 public static int day(final Object arg) {
353 return ((Literal) evaluate(new Day(), arg)).intValue();
354 }
355
356 public static int hours(final Object arg) {
357 return ((Literal) evaluate(new Hours(), arg)).intValue();
358 }
359
360 public static int minutes(final Object arg) {
361 return ((Literal) evaluate(new Minutes(), arg)).intValue();
362 }
363
364 public static double seconds(final Object arg) {
365 return ((Literal) evaluate(new Seconds(), arg)).doubleValue();
366 }
367
368 public static Literal timezone(final Object arg) {
369 return (Literal) evaluate(new Timezone(), arg);
370 }
371
372 public static String tz(final Object arg) {
373 return ((Literal) evaluate(new Tz(), arg)).stringValue();
374 }
375
376 public static String md5(final Object arg) {
377 return ((Literal) evaluate(new MD5(), arg)).stringValue();
378 }
379
380 public static String sha1(final Object arg) {
381 return ((Literal) evaluate(new SHA1(), arg)).stringValue();
382 }
383
384 public static String sha256(final Object arg) {
385 return ((Literal) evaluate(new SHA256(), arg)).stringValue();
386 }
387
388 public static String sha384(final Object arg) {
389 return ((Literal) evaluate(new SHA384(), arg)).stringValue();
390 }
391
392 public static String sha512(final Object arg) {
393 return ((Literal) evaluate(new SHA512(), arg)).stringValue();
394 }
395
396 public static boolean bool(final Object arg) {
397 return ((Literal) evaluate(new BooleanCast(), arg)).booleanValue();
398 }
399
400 public static double dbl(final Object arg) {
401 return ((Literal) evaluate(new DoubleCast(), arg)).doubleValue();
402 }
403
404 public static float flt(final Object arg) {
405 return ((Literal) evaluate(new FloatCast(), arg)).floatValue();
406 }
407
408 public static BigDecimal dec(final Object arg) {
409 return ((Literal) evaluate(new DecimalCast(), arg)).decimalValue();
410 }
411
412 public static BigInteger integer(final Object arg) {
413 return ((Literal) evaluate(new IntegerCast(), arg)).integerValue();
414 }
415
416 public static Literal dt(final Object arg) {
417 return (Literal) evaluate(new DateTimeCast(), arg);
418 }
419
420 public static Value evaluate(final Function function, final Object... args) {
421 final Value[] values = new Value[args.length];
422 for (int i = 0; i < args.length; i++) {
423 values[i] = toRDF(args[i]);
424 }
425 try {
426 return function.evaluate(Statements.VALUE_FACTORY, values);
427 } catch (final ValueExprEvaluationException ex) {
428 throw new IllegalArgumentException(ex);
429 }
430 }
431
432 public static Value toRDF(final Object object) {
433 if (object instanceof Value) {
434 return (Value) object;
435 } else if (object instanceof Long) {
436 return new GroovyLiteral(object.toString(), XMLSchema.LONG);
437 } else if (object instanceof Integer) {
438 return new GroovyLiteral(object.toString(), XMLSchema.INT);
439 } else if (object instanceof Short) {
440 return new GroovyLiteral(object.toString(), XMLSchema.SHORT);
441 } else if (object instanceof Byte) {
442 return new GroovyLiteral(object.toString(), XMLSchema.BYTE);
443 } else if (object instanceof Double) {
444 return new GroovyLiteral(object.toString(), XMLSchema.DOUBLE);
445 } else if (object instanceof Float) {
446 return new GroovyLiteral(object.toString(), XMLSchema.FLOAT);
447 } else if (object instanceof Boolean) {
448 return new GroovyLiteral(object.toString(), XMLSchema.BOOLEAN);
449 } else if (object instanceof XMLGregorianCalendar) {
450 final XMLGregorianCalendar c = (XMLGregorianCalendar) object;
451 return new GroovyLiteral(c.toXMLFormat(), XMLDatatypeUtil.qnameToURI(c
452 .getXMLSchemaType()));
453 } else if (object instanceof Date) {
454 final GregorianCalendar c = new GregorianCalendar();
455 c.setTime((Date) object);
456 final XMLGregorianCalendar xc = DATATYPE_FACTORY.newXMLGregorianCalendar(c);
457 return new GroovyLiteral(xc.toXMLFormat(), XMLDatatypeUtil.qnameToURI(xc
458 .getXMLSchemaType()));
459 } else if (object instanceof CharSequence) {
460 return new GroovyLiteral(object.toString(), XMLSchema.STRING);
461 } else if (object != null) {
462 return new GroovyLiteral(object.toString());
463 }
464 throw new NullPointerException();
465 }
466
467 }