1 package eu.fbk.knowledgestore;
2
3 import java.util.Arrays;
4 import java.util.Collection;
5 import java.util.List;
6 import java.util.Map;
7 import java.util.Set;
8 import java.util.regex.Matcher;
9 import java.util.regex.Pattern;
10
11 import javax.annotation.Nullable;
12
13 import com.google.common.base.MoreObjects;
14 import com.google.common.base.Preconditions;
15 import com.google.common.collect.ImmutableList;
16 import com.google.common.collect.ImmutableMap;
17 import com.google.common.collect.ImmutableSet;
18 import com.google.common.collect.Iterables;
19 import com.google.common.collect.Lists;
20 import com.google.common.collect.Maps;
21 import com.google.common.collect.Sets;
22
23 import org.openrdf.model.Statement;
24 import org.openrdf.model.URI;
25 import org.openrdf.query.BindingSet;
26
27 import eu.fbk.knowledgestore.Outcome.Status;
28 import eu.fbk.knowledgestore.data.Criteria;
29 import eu.fbk.knowledgestore.data.Data;
30 import eu.fbk.knowledgestore.data.Handler;
31 import eu.fbk.knowledgestore.data.ParseException;
32 import eu.fbk.knowledgestore.data.Record;
33 import eu.fbk.knowledgestore.data.Representation;
34 import eu.fbk.knowledgestore.data.Stream;
35 import eu.fbk.knowledgestore.data.XPath;
36 import eu.fbk.knowledgestore.vocabulary.KS;
37
38
39
40
41
42
43
44
45
46 public abstract class Operation {
47
48 private final Map<String, String> inheritedNamespaces;
49
50 Map<String, String> namespaces;
51
52 Long timeout;
53
54 Operation(@Nullable final Map<String, String> inheritedNamespaces) {
55 this.inheritedNamespaces = inheritedNamespaces != null ? inheritedNamespaces
56 : ImmutableMap.<String, String>of();
57 this.namespaces = this.inheritedNamespaces;
58 this.timeout = null;
59 }
60
61
62
63
64
65
66
67
68
69 public synchronized Operation timeout(@Nullable final Long timeout) {
70 this.timeout = timeout == null || timeout > 0 ? timeout : null;
71 return this;
72 }
73
74
75
76
77
78
79
80
81
82
83 public synchronized Operation namespaces(@Nullable final Map<String, String> namespaces) {
84 this.namespaces = namespaces == null ? this.inheritedNamespaces : Data.newNamespaceMap(
85 namespaces, this.inheritedNamespaces);
86 return this;
87 }
88
89
90
91 static URI checkType(final URI type) {
92 Preconditions.checkNotNull(type, "No type specified");
93 if (!type.equals(KS.RESOURCE) && !type.equals(KS.MENTION) && !type.equals(KS.ENTITY)
94 && !type.equals(KS.AXIOM)) {
95 throw new IllegalArgumentException("Invalid type: " + type);
96 }
97 return type;
98 }
99
100 static XPath conditionFor(final URI property, final Object... allowedValues) {
101 final String namespace = property.getNamespace();
102 final String prefix = MoreObjects.firstNonNull(
103 Data.namespaceToPrefix(namespace, Data.getNamespaceMap()), "ns");
104 return XPath.parse(
105 String.format("with %s: <%s> : %s:%s = {}", prefix, namespace, prefix,
106 property.getLocalName()), allowedValues);
107 }
108
109 static <T> Handler<T> handlerFor(@Nullable final Collection<? super T> collection) {
110 if (collection == null) {
111 return null;
112 } else {
113 return new Handler<T>() {
114
115 @Override
116 public void handle(final T element) {
117 if (element != null) {
118 collection.add(element);
119 }
120 }
121
122 };
123 }
124 }
125
126 @Nullable
127 static XPath merge(final Iterable<XPath> conditions) {
128 return conditions == null || Iterables.isEmpty(conditions) ? null : XPath.compose("and",
129 (Object[]) Iterables.toArray(conditions, XPath.class));
130 }
131
132 static <T> List<T> add(@Nullable final List<T> list, final T element) {
133 if (list == null) {
134 return ImmutableList.of(element);
135 } else {
136 final List<T> tmp = Lists.newArrayList(list);
137 tmp.add(element);
138 return ImmutableList.copyOf(tmp);
139 }
140 }
141
142 static <T> Set<T> add(@Nullable final Set<T> set, final T element) {
143 if (set == null) {
144 return ImmutableSet.of(element);
145 } else {
146 final List<T> tmp = Lists.newArrayList(set);
147 tmp.add(element);
148 return ImmutableSet.copyOf(tmp);
149 }
150 }
151
152
153
154
155
156
157
158
159
160
161
162 public abstract static class Download extends Operation {
163
164 private final URI resourceID;
165
166 @Nullable
167 private Set<String> mimeTypes;
168
169 private boolean caching;
170
171
172
173
174
175
176
177
178
179 protected Download(@Nullable final Map<String, String> inheritedNamespaces,
180 final URI resourceID) {
181 super(inheritedNamespaces);
182 this.resourceID = Preconditions.checkNotNull(resourceID, "Null resource ID");
183 this.mimeTypes = null;
184 this.caching = true;
185 }
186
187 @Override
188 public Download timeout(@Nullable final Long timeout) {
189 return (Download) super.timeout(timeout);
190 }
191
192 @Override
193 public Download namespaces(@Nullable final Map<String, String> namespaces) {
194 return (Download) super.namespaces(namespaces);
195 }
196
197
198
199
200
201
202
203
204
205 public final synchronized Download accept(@Nullable final String... mimeTypes) {
206 return accept(mimeTypes == null ? null : Arrays.asList(mimeTypes));
207 }
208
209
210
211
212
213
214
215
216
217 public final synchronized Download accept(
218 @Nullable final Iterable<? extends String> mimeTypes) {
219 this.mimeTypes = mimeTypes == null ? null : Sets.newLinkedHashSet(mimeTypes);
220 return this;
221 }
222
223
224
225
226
227
228
229
230 public final synchronized Download caching(final boolean caching) {
231 this.caching = caching;
232 return this;
233 }
234
235
236
237
238
239
240
241
242
243 public final synchronized Representation exec() throws OperationException {
244 return doExec(this.timeout, this.resourceID, this.mimeTypes, this.caching);
245 }
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262 @Nullable
263 protected abstract Representation doExec(@Nullable Long timeout, URI resourceID,
264 @Nullable Set<String> mimeTypes, boolean caching) throws OperationException;
265
266 }
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325 public abstract static class Upload extends Operation {
326
327 private final URI resourceID;
328
329 @Nullable
330 private Representation representation;
331
332 protected Upload(final Map<String, String> inheritedNamespaces, final URI id) {
333 super(inheritedNamespaces);
334 this.resourceID = Preconditions.checkNotNull(id, "Null resource ID");
335 this.representation = null;
336 }
337
338 @Override
339 public Upload timeout(@Nullable final Long timeout) {
340 return (Upload) super.timeout(timeout);
341 }
342
343 @Override
344 public Upload namespaces(@Nullable final Map<String, String> namespaces) {
345 return (Upload) super.namespaces(namespaces);
346 }
347
348
349
350
351
352
353
354
355
356 public final synchronized Upload representation(
357 @Nullable final Representation representation) {
358 this.representation = representation;
359 return this;
360 }
361
362
363
364
365
366
367
368
369 public final synchronized Outcome exec() throws OperationException {
370 return doExec(this.timeout, this.resourceID, this.representation);
371 }
372
373 protected abstract Outcome doExec(@Nullable Long timeout, URI resourceID,
374 @Nullable Representation representation) throws OperationException;
375
376 }
377
378 public abstract static class Count extends Operation {
379
380 private final URI type;
381
382 @Nullable
383 private List<XPath> conditions;
384
385 @Nullable
386 private Set<URI> ids;
387
388 protected Count(final Map<String, String> namespaces, final URI type) {
389 super(namespaces);
390 this.type = checkType(type);
391 this.conditions = null;
392 this.ids = null;
393 }
394
395 @Override
396 public Count timeout(@Nullable final Long timeout) {
397 return (Count) super.timeout(timeout);
398 }
399
400 @Override
401 public Count namespaces(@Nullable final Map<String, String> namespaces) {
402 return (Count) super.namespaces(namespaces);
403 }
404
405 public final synchronized Count conditions(@Nullable final XPath... conditions) {
406 return conditions(conditions == null ? null : Arrays.asList(conditions));
407 }
408
409 public final synchronized Count conditions(
410 @Nullable final Iterable<? extends XPath> conditions) {
411 this.conditions = conditions == null ? null : ImmutableList.copyOf(conditions);
412 return this;
413 }
414
415 public final synchronized Count condition(final String condition,
416 final Object... arguments) throws ParseException {
417 this.conditions = add(this.conditions,
418 XPath.parse(this.namespaces, condition, arguments));
419 return this;
420 }
421
422 public final synchronized Count condition(final URI property,
423 final Object... allowedValues) {
424 this.conditions = add(this.conditions, conditionFor(property, allowedValues));
425 return this;
426 }
427
428 public final synchronized Count ids(@Nullable final URI... ids) {
429 return ids(ids == null ? null : Arrays.asList(ids));
430 }
431
432 public final synchronized Count ids(@Nullable final Iterable<? extends URI> ids) {
433 this.ids = ids == null ? null : ImmutableSet.copyOf(ids);
434 return this;
435 }
436
437 public final synchronized long exec() throws OperationException {
438 return doExec(this.timeout, this.type, merge(this.conditions), this.ids);
439 }
440
441 protected abstract long doExec(@Nullable Long timeout, URI type,
442 @Nullable XPath condition, @Nullable Set<URI> ids) throws OperationException;
443
444 }
445
446 public abstract static class Retrieve extends Operation {
447
448 private final URI type;
449
450 @Nullable
451 private List<XPath> conditions;
452
453 @Nullable
454 private Set<URI> ids;
455
456 @Nullable
457 private Set<URI> properties;
458
459 @Nullable
460 private Long offset;
461
462 @Nullable
463 private Long limit;
464
465 protected Retrieve(final Map<String, String> namespaces, final URI type) {
466 super(namespaces);
467 this.type = checkType(type);
468 this.conditions = null;
469 this.ids = null;
470 this.properties = null;
471 this.offset = null;
472 this.limit = null;
473 }
474
475 @Override
476 public Retrieve timeout(@Nullable final Long timeout) {
477 return (Retrieve) super.timeout(timeout);
478 }
479
480 @Override
481 public Retrieve namespaces(@Nullable final Map<String, String> namespaces) {
482 return (Retrieve) super.namespaces(namespaces);
483 }
484
485 public final synchronized Retrieve conditions(@Nullable final XPath... conditions) {
486 return conditions(conditions == null ? null : Arrays.asList(conditions));
487 }
488
489 public final synchronized Retrieve conditions(
490 @Nullable final Iterable<? extends XPath> conditions) {
491 this.conditions = conditions == null ? null : ImmutableList.copyOf(conditions);
492 return this;
493 }
494
495 public final synchronized Retrieve condition(final String condition,
496 final Object... arguments) throws ParseException {
497 this.conditions = add(this.conditions,
498 XPath.parse(this.namespaces, condition, arguments));
499 return this;
500 }
501
502 public final synchronized Retrieve condition(final URI property,
503 final Object... allowedValues) {
504 this.conditions = add(this.conditions, conditionFor(property, allowedValues));
505 return this;
506 }
507
508 public final synchronized Retrieve ids(@Nullable final URI... ids) {
509 return ids(ids == null ? null : Arrays.asList(ids));
510 }
511
512 public final synchronized Retrieve ids(@Nullable final Iterable<? extends URI> ids) {
513 this.ids = ids == null ? null : ImmutableSet.copyOf(ids);
514 return this;
515 }
516
517
518
519 public final synchronized Retrieve properties(@Nullable final URI... properties) {
520 return properties(properties == null ? null : Arrays.asList(properties));
521 }
522
523 public final synchronized Retrieve properties(
524 @Nullable final Iterable<? extends URI> properties) {
525 this.properties = properties == null ? null : ImmutableSet.copyOf(properties);
526 return this;
527 }
528
529 public final synchronized Retrieve offset(@Nullable final Long offset) {
530 this.offset = offset == null || offset <= 0 ? null : offset;
531 return this;
532 }
533
534 public final synchronized Retrieve limit(@Nullable final Long limit) {
535 this.limit = limit == null || limit <= 0 ? null : limit;
536 return this;
537 }
538
539 public final synchronized Stream<Record> exec() throws OperationException {
540 return doExec(this.timeout, this.type, merge(this.conditions), this.ids,
541 this.properties, this.offset, this.limit);
542 }
543
544 protected abstract Stream<Record> doExec(@Nullable final Long timeout, final URI type,
545 @Nullable final XPath condition, @Nullable final Set<URI> ids,
546 @Nullable final Set<URI> properties, @Nullable Long offset, @Nullable Long limit)
547 throws OperationException;
548
549 }
550
551 public abstract static class Create extends Operation {
552
553 private final URI type;
554
555 @Nullable
556 private Stream<? extends Record> records;
557
558 protected Create(final Map<String, String> namespaces, final URI type) {
559 super(namespaces);
560 this.type = checkType(type);
561 this.records = null;
562 }
563
564 @Override
565 public Create timeout(@Nullable final Long timeout) {
566 return (Create) super.timeout(timeout);
567 }
568
569 @Override
570 public Create namespaces(@Nullable final Map<String, String> namespaces) {
571 return (Create) super.namespaces(namespaces);
572 }
573
574 public final synchronized Create records(@Nullable final Record... records) {
575 this.records = records == null ? null : Stream.create(records);
576 return this;
577 }
578
579 public final synchronized Create records(
580 @Nullable final Iterable<? extends Record> records) {
581 this.records = records == null ? null : Stream.create(records);
582 return this;
583 }
584
585 public final synchronized Outcome exec() throws OperationException {
586 return doExec(this.timeout, this.type, this.records, null);
587 }
588
589
590
591
592
593 public final synchronized Outcome exec(@Nullable final Handler<? super Outcome> handler)
594 throws OperationException {
595 return doExec(this.timeout, this.type, this.records, handler);
596 }
597
598 public final synchronized Outcome exec(
599 @Nullable final Collection<? super Outcome> collection) throws OperationException {
600 return doExec(this.timeout, this.type, this.records, handlerFor(collection));
601 }
602
603 protected abstract Outcome doExec(@Nullable Long timeout, final URI type,
604 @Nullable final Stream<? extends Record> records,
605 @Nullable final Handler<? super Outcome> handler) throws OperationException;
606
607 }
608
609 public abstract static class Merge extends Operation {
610
611 private final URI type;
612
613 @Nullable
614 private Stream<? extends Record> records;
615
616 @Nullable
617 private Criteria criteria;
618
619 protected Merge(final Map<String, String> namespaces, final URI type) {
620 super(namespaces);
621 this.type = checkType(type);
622 this.records = null;
623 this.criteria = null;
624 }
625
626 @Override
627 public Merge timeout(@Nullable final Long timeout) {
628 return (Merge) super.timeout(timeout);
629 }
630
631 @Override
632 public Merge namespaces(@Nullable final Map<String, String> namespaces) {
633 return (Merge) super.namespaces(namespaces);
634 }
635
636 public final synchronized Merge records(@Nullable final Record... records) {
637 this.records = records == null ? null : Stream.create(records);
638 return this;
639 }
640
641 public final synchronized Merge records(
642 @Nullable final Iterable<? extends Record> records) {
643 this.records = records == null ? null : Stream.create(records);
644 return this;
645 }
646
647 public final synchronized Merge criteria(@Nullable final Criteria... criteria) {
648 return criteria(criteria == null ? null : Arrays.asList(criteria));
649 }
650
651 public final synchronized Merge criteria(
652 @Nullable final Iterable<? extends Criteria> criteria) {
653 this.criteria = criteria == null || Iterables.isEmpty(criteria) ? null : Criteria
654 .compose(Iterables.toArray(criteria, Criteria.class));
655 return this;
656 }
657
658 public final synchronized Merge criteria(@Nullable final String criteria)
659 throws ParseException {
660 this.criteria = criteria == null ? null : Criteria.parse(criteria, this.namespaces);
661 return this;
662 }
663
664 public final synchronized Outcome exec() throws OperationException {
665 return doExec(this.timeout, this.type, this.records, this.criteria, null);
666 }
667
668 public final synchronized Outcome exec(@Nullable final Handler<? super Outcome> handler)
669 throws OperationException {
670 return doExec(this.timeout, this.type, this.records, this.criteria, handler);
671 }
672
673 public final synchronized Outcome exec(
674 @Nullable final Collection<? super Outcome> collection) throws OperationException {
675 return doExec(this.timeout, this.type, this.records, this.criteria,
676 handlerFor(collection));
677 }
678
679 protected abstract Outcome doExec(@Nullable Long timeout, URI type,
680 @Nullable Stream<? extends Record> stream, @Nullable Criteria criteria,
681 @Nullable Handler<? super Outcome> handler) throws OperationException;
682
683 }
684
685 public abstract static class Update extends Operation {
686
687 private final URI type;
688
689 @Nullable
690 private List<XPath> conditions;
691
692 @Nullable
693 private Set<URI> ids;
694
695 @Nullable
696 private Record record;
697
698 @Nullable
699 private Criteria criteria;
700
701 protected Update(final Map<String, String> namespaces, final URI type) {
702 super(namespaces);
703 this.type = checkType(type);
704 this.conditions = null;
705 this.ids = null;
706 this.record = null;
707 this.criteria = null;
708 }
709
710 @Override
711 public Update timeout(@Nullable final Long timeout) {
712 return (Update) super.timeout(timeout);
713 }
714
715 @Override
716 public Update namespaces(@Nullable final Map<String, String> namespaces) {
717 return (Update) super.namespaces(namespaces);
718 }
719
720 public final synchronized Update conditions(@Nullable final XPath... conditions) {
721 return conditions(conditions == null ? null : Arrays.asList(conditions));
722 }
723
724 public final synchronized Update conditions(
725 @Nullable final Iterable<? extends XPath> conditions) {
726 this.conditions = conditions == null ? null : ImmutableList.copyOf(conditions);
727 return this;
728 }
729
730 public final synchronized Update condition(final String condition,
731 final Object... arguments) throws ParseException {
732 this.conditions = add(this.conditions,
733 XPath.parse(this.namespaces, condition, arguments));
734 return this;
735 }
736
737 public final synchronized Update condition(final URI property,
738 final Object... allowedValues) {
739 this.conditions = add(this.conditions, conditionFor(property, allowedValues));
740 return this;
741 }
742
743 public final synchronized Update ids(@Nullable final URI... ids) {
744 return ids(ids == null ? null : Arrays.asList(ids));
745 }
746
747 public final synchronized Update ids(@Nullable final Iterable<? extends URI> ids) {
748 this.ids = ids == null ? null : ImmutableSet.copyOf(ids);
749 return this;
750 }
751
752 public final synchronized Update record(@Nullable final Record record) {
753 this.record = record;
754 return this;
755 }
756
757 public final synchronized Update criteria(@Nullable final Criteria... criteria) {
758 return criteria(criteria == null ? null : Arrays.asList(criteria));
759 }
760
761 public final synchronized Update criteria(
762 @Nullable final Iterable<? extends Criteria> criteria) {
763 this.criteria = criteria == null || Iterables.isEmpty(criteria) ? null : Criteria
764 .compose(Iterables.toArray(criteria, Criteria.class));
765 return this;
766 }
767
768 public final synchronized Update criteria(@Nullable final String criteria)
769 throws ParseException {
770 this.criteria = criteria == null ? null : Criteria.parse(criteria, this.namespaces);
771 return this;
772 }
773
774 public final synchronized Outcome exec() throws OperationException {
775 return doExec(this.timeout, this.type, merge(this.conditions), this.ids, this.record,
776 this.criteria, null);
777 }
778
779 public final synchronized Outcome exec(@Nullable final Handler<? super Outcome> handler)
780 throws OperationException {
781 final Record record = this.record != null ? this.record : Record.create();
782 return doExec(this.timeout, this.type, merge(this.conditions), this.ids, record,
783 this.criteria, handler);
784 }
785
786 public final synchronized Outcome exec(
787 @Nullable final Collection<? super Outcome> collection) throws OperationException {
788 return doExec(this.timeout, this.type, merge(this.conditions), this.ids, this.record,
789 this.criteria, handlerFor(collection));
790 }
791
792 protected abstract Outcome doExec(@Nullable Long timeout, URI type,
793 @Nullable XPath condition, @Nullable Set<URI> ids, @Nullable Record record,
794 @Nullable Criteria criteria, @Nullable Handler<? super Outcome> handler)
795 throws OperationException;
796
797 }
798
799 public abstract static class Delete extends Operation {
800
801 private final URI type;
802
803 @Nullable
804 private List<XPath> conditions;
805
806 @Nullable
807 private Set<URI> ids;
808
809 protected Delete(final Map<String, String> namespaces, final URI type) {
810 super(namespaces);
811 this.type = checkType(type);
812 this.conditions = null;
813 this.ids = null;
814 }
815
816 @Override
817 public Delete timeout(@Nullable final Long timeout) {
818 return (Delete) super.timeout(timeout);
819 }
820
821 @Override
822 public Delete namespaces(@Nullable final Map<String, String> namespaces) {
823 return (Delete) super.namespaces(namespaces);
824 }
825
826 public final synchronized Delete conditions(@Nullable final XPath... conditions) {
827 return conditions(conditions == null ? null : Arrays.asList(conditions));
828 }
829
830 public final synchronized Delete conditions(
831 @Nullable final Iterable<? extends XPath> conditions) {
832 this.conditions = conditions == null ? null : ImmutableList.copyOf(conditions);
833 return this;
834 }
835
836 public final synchronized Delete condition(final String condition,
837 final Object... arguments) throws ParseException {
838 this.conditions = add(this.conditions,
839 XPath.parse(this.namespaces, condition, arguments));
840 return this;
841 }
842
843 public final synchronized Delete condition(final URI property,
844 final Object... allowedValues) {
845 this.conditions = add(this.conditions, conditionFor(property, allowedValues));
846 return this;
847 }
848
849 public final synchronized Delete ids(@Nullable final URI... ids) {
850 return ids(ids == null ? null : Arrays.asList(ids));
851 }
852
853 public final synchronized Delete ids(@Nullable final Iterable<? extends URI> ids) {
854 this.ids = ids == null ? null : ImmutableSet.copyOf(ids);
855 return this;
856 }
857
858 public final synchronized Outcome exec() throws OperationException {
859 return doExec(this.timeout, this.type, merge(this.conditions), this.ids, null);
860 }
861
862 public final synchronized Outcome exec(@Nullable final Handler<? super Outcome> handler)
863 throws OperationException {
864 return doExec(this.timeout, this.type, merge(this.conditions), this.ids, handler);
865 }
866
867 public final synchronized Outcome exec(
868 @Nullable final Collection<? super Outcome> collection) throws OperationException {
869 return doExec(this.timeout, this.type, merge(this.conditions), this.ids,
870 handlerFor(collection));
871 }
872
873 protected abstract Outcome doExec(@Nullable Long timeout, URI type,
874 @Nullable XPath condition, @Nullable Set<URI> ids,
875 @Nullable Handler<? super Outcome> handler) throws OperationException;
876
877 }
878
879 public abstract static class Match extends Operation {
880
881 private static final Map<URI, URI> COMPONENT_NORMALIZATION_MAP =
882 ImmutableMap.<URI, URI>builder().put(KS.RESOURCE, KS.RESOURCE)
883 .put(KS.MATCHED_RESOURCE, KS.RESOURCE).put(KS.MENTION, KS.MENTION)
884 .put(KS.MATCHED_MENTION, KS.MENTION).put(KS.ENTITY, KS.ENTITY)
885 .put(KS.MATCHED_ENTITY, KS.ENTITY).build();
886
887 private final Map<URI, Set<XPath>> conditions;
888
889 private final Map<URI, Set<URI>> ids;
890
891 private final Map<URI, Set<URI>> properties;
892
893 protected Match(final Map<String, String> namespaces) {
894 super(namespaces);
895 this.conditions = Maps.newHashMap();
896 this.ids = Maps.newHashMap();
897 this.properties = Maps.newHashMap();
898 }
899
900 @Override
901 public Match timeout(@Nullable final Long timeout) {
902 return (Match) super.timeout(timeout);
903 }
904
905 @Override
906 public Match namespaces(@Nullable final Map<String, String> namespaces) {
907 return (Match) super.namespaces(namespaces);
908 }
909
910 public final synchronized Match conditions(@Nullable final URI component,
911 @Nullable final XPath... conditions) {
912 return conditions(component, conditions == null ? null : Arrays.asList(conditions));
913 }
914
915 public final synchronized Match conditions(@Nullable final URI component,
916 @Nullable final Iterable<? extends XPath> conditions) {
917 if (component == null) {
918 this.conditions.clear();
919 } else {
920 this.conditions.put(checkComponent(component), conditions == null ? null
921 : ImmutableSet.copyOf(conditions));
922 }
923 return this;
924 }
925
926 public final synchronized Match condition(final URI component, final String condition,
927 final Object... arguments) throws ParseException {
928 final URI comp = checkComponent(component);
929 final XPath cond = XPath.parse(this.namespaces, condition, arguments);
930 this.conditions.put(comp, add(this.conditions.get(comp), cond));
931 return this;
932 }
933
934 public final synchronized Match condition(final URI component, final URI property,
935 final Object... allowedValues) {
936 final URI comp = checkComponent(component);
937 final XPath cond = conditionFor(property, allowedValues);
938 this.conditions.put(comp, add(this.conditions.get(comp), cond));
939 return this;
940 }
941
942 public final synchronized Match ids(@Nullable final URI component,
943 @Nullable final URI... ids) {
944 return ids(component, ids == null ? null : Arrays.asList(ids));
945 }
946
947 public final synchronized Match ids(@Nullable final URI component,
948 @Nullable final Iterable<? extends URI> ids) {
949 if (component == null) {
950 this.ids.clear();
951 } else {
952 this.ids.put(checkComponent(component),
953 ids == null ? null : ImmutableSet.copyOf(ids));
954 }
955 return this;
956 }
957
958
959
960 public final synchronized Match properties(@Nullable final URI component,
961 @Nullable final URI... properties) {
962 return properties(component, properties == null ? null : Arrays.asList(properties));
963 }
964
965 public final synchronized Match properties(@Nullable final URI component,
966 @Nullable final Iterable<? extends URI> properties) {
967 if (component == null) {
968 this.properties.clear();
969 } else {
970 this.properties.put(checkComponent(component), properties == null ? null
971 : ImmutableSet.copyOf(properties));
972 }
973 return this;
974 }
975
976 public final synchronized Stream<Record> exec() throws OperationException {
977 final Map<URI, XPath> conditions = Maps.newHashMap();
978 for (final URI component : this.conditions.keySet()) {
979 conditions.put(component, merge(this.conditions.get(component)));
980 }
981 return doExec(this.timeout, conditions, this.ids, this.properties);
982 }
983
984 protected abstract Stream<Record> doExec(@Nullable final Long timeout,
985 final Map<URI, XPath> conditions, final Map<URI, Set<URI>> ids,
986 final Map<URI, Set<URI>> properties) throws OperationException;
987
988 private URI checkComponent(final URI component) {
989 final URI uri = COMPONENT_NORMALIZATION_MAP.get(component);
990 Preconditions.checkArgument(uri != null, "Invalid match component %s", component);
991 return uri;
992 }
993
994 }
995
996
997
998 public abstract static class SparqlUpdate extends Operation {
999
1000 @Nullable
1001 private Stream<? extends Statement> statements;
1002
1003 protected SparqlUpdate(final Map<String, String> namespaces) throws ParseException {
1004 super(namespaces);
1005 }
1006
1007 public final synchronized SparqlUpdate statements(@Nullable final Statement... statements) {
1008 this.statements = statements == null ? null : Stream.create(statements);
1009 return this;
1010 }
1011
1012 public final synchronized SparqlUpdate statements(
1013 @Nullable final Iterable<? extends Statement> statements) {
1014 this.statements = statements == null ? null : Stream.create(statements);
1015 return this;
1016 }
1017
1018 public final synchronized Outcome exec() throws OperationException {
1019 return doExec(this.timeout, this.statements);
1020 }
1021
1022 protected abstract Outcome doExec(@Nullable Long timeout,
1023 @Nullable final Stream<? extends Statement> statements) throws OperationException;
1024
1025 }
1026
1027
1028 public abstract static class SparqlDelete extends Operation {
1029
1030 @Nullable
1031 private Stream<? extends Statement> statements;
1032
1033 protected SparqlDelete(final Map<String, String> namespaces) throws ParseException {
1034 super(namespaces);
1035 }
1036
1037 public final synchronized SparqlDelete statements(@Nullable final Statement... statements) {
1038 this.statements = statements == null ? null : Stream.create(statements);
1039 return this;
1040 }
1041
1042 public final synchronized SparqlDelete statements(
1043 @Nullable final Iterable<? extends Statement> statements) {
1044 this.statements = statements == null ? null : Stream.create(statements);
1045 return this;
1046 }
1047
1048 public final synchronized Outcome exec() throws OperationException {
1049 return doExec(this.timeout, this.statements);
1050 }
1051
1052 protected abstract Outcome doExec(@Nullable Long timeout,
1053 @Nullable final Stream<? extends Statement> statements) throws OperationException;
1054
1055 }
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079 public abstract static class Sparql extends Operation {
1080
1081 private static final Pattern PLACEHOLDER_PATTERN = Pattern
1082 .compile("(?:(?<=\\A|[^\\\\]))([$][$])(?:(?=\\z|.))");
1083
1084 private final String expression;
1085
1086 @Nullable
1087 private Set<URI> defaultGraphs;
1088
1089 @Nullable
1090 private Set<URI> namedGraphs;
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106 protected Sparql(final Map<String, String> namespaces, final String expression,
1107 final Object... arguments) throws ParseException {
1108 super(namespaces);
1109 this.expression = expand(expression, arguments);
1110 this.defaultGraphs = null;
1111 this.namedGraphs = null;
1112 }
1113
1114 @Override
1115 public Sparql timeout(@Nullable final Long timeout) {
1116 return (Sparql) super.timeout(timeout);
1117 }
1118
1119 @Override
1120 public Sparql namespaces(@Nullable final Map<String, String> namespaces) {
1121 return (Sparql) super.namespaces(namespaces);
1122 }
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133 public final synchronized Sparql defaultGraphs(@Nullable final URI... defaultGraphs) {
1134 return defaultGraphs(defaultGraphs == null ? null : Arrays.asList(defaultGraphs));
1135 }
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146 public final synchronized Sparql defaultGraphs(
1147 @Nullable final Iterable<URI> defaultGraphs) {
1148 this.defaultGraphs = defaultGraphs == null ? null : ImmutableSet.copyOf(defaultGraphs);
1149 return this;
1150 }
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161 public final synchronized Sparql namedGraphs(@Nullable final URI... namedGraphs) {
1162 return namedGraphs(namedGraphs == null ? null : Arrays.asList(namedGraphs));
1163 }
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174 public final synchronized Sparql namedGraphs(@Nullable final Iterable<URI> namedGraphs) {
1175 this.namedGraphs = namedGraphs == null ? null : ImmutableSet.copyOf(namedGraphs);
1176 return this;
1177 }
1178
1179
1180
1181
1182
1183
1184
1185
1186 public final synchronized boolean execBoolean() throws OperationException {
1187 return doExec(this.timeout, Boolean.class, this.expression, this.defaultGraphs,
1188 this.namedGraphs).getUnique();
1189 }
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199 public final synchronized Stream<Statement> execTriples() throws OperationException {
1200 return doExec(this.timeout, Statement.class, this.expression, this.defaultGraphs,
1201 this.namedGraphs);
1202 }
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214 public final synchronized Stream<BindingSet> execTuples() throws OperationException {
1215 return doExec(this.timeout, BindingSet.class, this.expression, this.defaultGraphs,
1216 this.namedGraphs);
1217 }
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241 protected abstract <T> Stream<T> doExec(@Nullable final Long timeout, final Class<T> type,
1242 final String expression, @Nullable final Set<URI> defaultGraphs,
1243 @Nullable final Set<URI> namedGraphs) throws OperationException;
1244
1245 private String expand(final String expression, final Object... arguments)
1246 throws ParseException {
1247 int expansions = 0;
1248 String result = expression;
1249 final Matcher matcher = PLACEHOLDER_PATTERN.matcher(expression);
1250 try {
1251 if (matcher.find()) {
1252 final StringBuilder builder = new StringBuilder();
1253 int last = 0;
1254 do {
1255 Object arg = arguments[expansions++];
1256 builder.append(expression.substring(last, matcher.start(1)));
1257 builder.append(arg instanceof Number ? arg : Data.toString(arg, null,
1258 false));
1259 last = matcher.end(1);
1260 } while (matcher.find());
1261 builder.append(expression.substring(last, expression.length()));
1262 result = builder.toString();
1263 }
1264 } catch (final IndexOutOfBoundsException ex) {
1265 throw new ParseException(expression, "No argument supplied for placeholder #"
1266 + expansions);
1267 }
1268 if (expansions != arguments.length) {
1269 throw new ParseException(expression, "Expression string contains " + expansions
1270 + " placholders, but " + arguments.length + " arguments where supplied");
1271 }
1272 return result;
1273 }
1274
1275 }
1276
1277 }