1
2
3
4
5
6
7
8
9
10
11
12
13
14 package eu.fbk.rdfpro.util;
15
16 import java.io.Serializable;
17 import java.util.AbstractCollection;
18 import java.util.AbstractSet;
19 import java.util.Arrays;
20 import java.util.Collection;
21 import java.util.Collections;
22 import java.util.HashSet;
23 import java.util.Iterator;
24 import java.util.LinkedHashSet;
25 import java.util.NoSuchElementException;
26 import java.util.Objects;
27 import java.util.Set;
28 import java.util.function.Function;
29
30 import javax.annotation.Nullable;
31
32 import com.google.common.collect.Iterables;
33 import com.google.common.collect.Lists;
34
35 import org.openrdf.model.Graph;
36 import org.openrdf.model.Literal;
37 import org.openrdf.model.Model;
38 import org.openrdf.model.Namespace;
39 import org.openrdf.model.Resource;
40 import org.openrdf.model.Statement;
41 import org.openrdf.model.URI;
42 import org.openrdf.model.Value;
43 import org.openrdf.model.ValueFactory;
44 import org.openrdf.model.impl.NamespaceImpl;
45 import org.openrdf.model.impl.URIImpl;
46 import org.openrdf.model.impl.ValueFactoryImpl;
47 import org.openrdf.model.util.ModelException;
48 import org.openrdf.query.BindingSet;
49 import org.openrdf.query.Dataset;
50 import org.openrdf.query.QueryEvaluationException;
51 import org.openrdf.query.algebra.StatementPattern;
52 import org.openrdf.query.algebra.TupleExpr;
53 import org.openrdf.query.algebra.Var;
54 import org.openrdf.query.algebra.evaluation.TripleSource;
55 import org.openrdf.query.algebra.evaluation.impl.EvaluationStatistics;
56 import org.openrdf.query.algebra.evaluation.impl.EvaluationStrategyImpl;
57
58 import info.aduna.iteration.CloseableIteration;
59
60 public abstract class QuadModel extends AbstractCollection<Statement> implements Graph,
61 Serializable {
62
63 protected final static Resource[] CTX_ANY = new Resource[0];
64
65 protected final static Resource[] CTX_DEFAULT = new Resource[] { null };
66
67 private static final long serialVersionUID = 1L;
68
69 public static QuadModel create() {
70 return new QuadModelImpl();
71 }
72
73 public static QuadModel create(final Iterable<Statement> statements) {
74 final QuadModel model = create();
75 Iterables.addAll(model, statements);
76 return model;
77 }
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94 public static QuadModel wrap(final org.openrdf.sail.SailConnection connection,
95 final boolean trackChanges) {
96 return new QuadModelSailAdapter(connection, trackChanges);
97 }
98
99 public static QuadModel wrap(final org.openrdf.repository.RepositoryConnection connection,
100 final boolean trackChanges) {
101 return new QuadModelRepositoryAdapter(connection, trackChanges);
102 }
103
104 public static QuadModel wrap(final Model model) {
105 return new QuadModelModelAdapter(model);
106 }
107
108 protected abstract Set<Namespace> doGetNamespaces();
109
110 @Nullable
111 protected abstract Namespace doGetNamespace(String prefix);
112
113 @Nullable
114 protected abstract Namespace doSetNamespace(String prefix, String name);
115
116 protected abstract int doSize(@Nullable Resource subj, @Nullable URI pred,
117 @Nullable Value obj, Resource[] ctxs);
118
119 protected int doSizeEstimate(@Nullable final Resource subj, @Nullable final URI pred,
120 @Nullable final Value obj, @Nullable final Resource ctx) {
121 return -1;
122 }
123
124 protected abstract Iterator<Statement> doIterator(@Nullable final Resource subj,
125 @Nullable final URI pred, @Nullable final Value obj, final Resource[] ctxs);
126
127 protected abstract boolean doAdd(@Nullable Resource subj, @Nullable URI pred,
128 @Nullable Value obj, Resource[] ctxs);
129
130 protected abstract boolean doRemove(@Nullable Resource subj, @Nullable URI pred,
131 @Nullable Value obj, Resource[] ctxs);
132
133 protected Iterator<BindingSet> doEvaluate(final TupleExpr expr,
134 @Nullable final Dataset dataset, @Nullable final BindingSet bindings) {
135
136 return Algebra.evaluateTupleExpr(expr, dataset, bindings, new EvaluationStrategyImpl(
137 getTripleSource(), dataset, Algebra.getFederatedServiceResolver()),
138 getEvaluationStatistics(), getValueNormalizer());
139 }
140
141 protected Value doNormalize(@Nullable final Value value) {
142 return value;
143 }
144
145 public final QuadModel unmodifiable() {
146 return this instanceof UnmodifiableModel ? this : new UnmodifiableModel(this);
147 }
148
149
150
151
152
153
154 public final Set<Namespace> getNamespaces() {
155 return doGetNamespaces();
156 }
157
158
159
160
161
162
163
164
165 @Nullable
166 public final Namespace getNamespace(final String prefix) {
167 return doGetNamespace(prefix);
168 }
169
170
171
172
173
174
175
176
177
178
179 public final Namespace setNamespace(final String prefix, final String name) {
180 doSetNamespace(Objects.requireNonNull(prefix), Objects.requireNonNull(name));
181 return new NamespaceImpl(prefix, name);
182 }
183
184
185
186
187
188
189
190 public final void setNamespace(final Namespace namespace) {
191 doSetNamespace(namespace.getPrefix(), namespace.getName());
192 }
193
194
195
196
197
198
199
200
201 @Nullable
202 public final Namespace removeNamespace(final String prefix) {
203 return doSetNamespace(Objects.requireNonNull(prefix), null);
204 }
205
206 @Override
207 public final boolean isEmpty() {
208 final Iterator<Statement> iterator = doIterator(null, null, null, CTX_ANY);
209 try {
210 return !iterator.hasNext();
211 } finally {
212 IO.closeQuietly(iterator);
213 }
214 }
215
216 @Override
217 public final int size() {
218 return doSize(null, null, null, CTX_ANY);
219 }
220
221 public final int size(@Nullable final Resource subj, @Nullable final URI pred,
222 @Nullable final Value obj) {
223 return doSize(subj, pred, obj, CTX_ANY);
224 }
225
226 public final int size(@Nullable final Resource subj, @Nullable final URI pred,
227 @Nullable final Value obj, final Resource... contexts) {
228 return doSize(subj, pred, obj, contexts);
229 }
230
231 public final int sizeEstimate(@Nullable final Resource subj, @Nullable final URI pred,
232 @Nullable final Value obj, final Resource... contexts) {
233 if (contexts.length == 0) {
234 return doSizeEstimate(subj, pred, obj, null);
235 } else {
236 int estimate = 0;
237 for (final Resource ctx : contexts) {
238 final int delta = doSizeEstimate(subj, pred, obj, ctx);
239 if (ctx == null) {
240 return delta;
241 }
242 estimate += delta;
243 }
244 return estimate;
245 }
246 }
247
248 @Override
249 public final Iterator<Statement> iterator() {
250 return doIterator(null, null, null, CTX_ANY);
251 }
252
253 public final Iterator<Statement> iterator(@Nullable final Resource subj,
254 @Nullable final URI pred, @Nullable final Value obj) {
255 return doIterator(subj, pred, obj, CTX_ANY);
256 }
257
258 public final Iterator<Statement> iterator(@Nullable final Resource subj,
259 @Nullable final URI pred, @Nullable final Value obj, final Resource... contexts) {
260 return doIterator(subj, pred, obj, contexts);
261 }
262
263 public final Iterator<BindingSet> evaluate(final TupleExpr expr,
264 @Nullable final Dataset dataset, @Nullable final BindingSet bindings) {
265 return doEvaluate(Objects.requireNonNull(expr), dataset, bindings);
266 }
267
268 @Override
269 public final boolean contains(@Nullable final Object object) {
270 if (object instanceof Statement) {
271 final Statement stmt = (Statement) object;
272 return contains(stmt.getSubject(), stmt.getPredicate(), stmt.getObject(),
273 new Resource[] { stmt.getContext() });
274 }
275 return false;
276 }
277
278 public final boolean contains(@Nullable final Resource subj, @Nullable final URI pred,
279 @Nullable final Value obj) {
280 return contains(subj, pred, obj, CTX_ANY);
281 }
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302 public final boolean contains(@Nullable final Resource subj, @Nullable final URI pred,
303 @Nullable final Value obj, final Resource... ctxs) {
304 final Iterator<Statement> iterator = doIterator(subj, pred, obj, ctxs);
305 try {
306 return iterator.hasNext();
307 } finally {
308 IO.closeQuietly(iterator);
309 }
310 }
311
312 @Override
313 public final boolean add(final Statement stmt) {
314 return doAdd(stmt.getSubject(), stmt.getPredicate(), stmt.getObject(),
315 new Resource[] { stmt.getContext() });
316 }
317
318 public final boolean add(@Nullable final Resource subj, @Nullable final URI pred,
319 @Nullable final Value obj) {
320 return doAdd(subj, pred, obj, CTX_ANY);
321 }
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345 @Override
346 public final boolean add(@Nullable final Resource subj, @Nullable final URI pred,
347 @Nullable final Value obj, final Resource... ctxs) {
348 return doAdd(subj, pred, obj, ctxs);
349 }
350
351 @Override
352 public final void clear() {
353 doRemove(null, null, null, CTX_ANY);
354 }
355
356
357
358
359
360
361
362
363 public final boolean clear(final Resource... contexts) {
364 return doRemove(null, null, null, contexts);
365 }
366
367 @Override
368 public final boolean remove(@Nullable final Object object) {
369 if (object instanceof Statement) {
370 final Statement stmt = (Statement) object;
371 return doRemove(stmt.getSubject(), stmt.getPredicate(), stmt.getObject(),
372 new Resource[] { stmt.getContext() });
373 }
374 return false;
375 }
376
377 public final boolean remove(@Nullable final Resource subj, @Nullable final URI pred,
378 @Nullable final Value obj) {
379 return doRemove(subj, pred, obj, CTX_ANY);
380 }
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401 public final boolean remove(@Nullable final Resource subj, @Nullable final URI pred,
402 @Nullable final Value obj, final Resource... ctxs) {
403 return doRemove(subj, pred, obj, ctxs);
404 }
405
406 @Override
407 @SuppressWarnings({ "unchecked", "rawtypes" })
408 public boolean removeAll(final Collection<?> c) {
409 Collection<?> toRemove = c;
410 if ((c instanceof Set<?> || c instanceof Model || c instanceof QuadModel)
411 && c.size() > size()) {
412 toRemove = Lists.newArrayList();
413 for (final Statement stmt : this) {
414 if (c.contains(stmt)) {
415 ((Collection) toRemove).add(stmt);
416 }
417 }
418 }
419 boolean modified = false;
420 for (final Object element : toRemove) {
421 final boolean removed = remove(element);
422 modified |= removed;
423 }
424 return modified;
425 }
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452 public QuadModel filter(@Nullable final Resource subj, @Nullable final URI pred,
453 @Nullable final Value obj, final Resource... ctxs) {
454 return new FilteredModel(this, subj, pred, obj, ctxs);
455 }
456
457
458
459
460
461
462
463
464
465
466
467 public QuadModel filter(final Collection<Statement> statements) {
468 return new QuadModelSubModel(this, statements);
469 }
470
471
472
473
474
475
476
477
478
479
480
481
482 public final Set<Resource> subjects() {
483 return new ValueSet<Resource>() {
484
485 @Override
486 public boolean contains(final Object object) {
487 if (object instanceof Resource) {
488 return QuadModel.this.contains((Resource) object, null, null);
489 }
490 return false;
491 }
492
493 @Override
494 public boolean remove(final Object object) {
495 if (object instanceof Resource) {
496 return doRemove((Resource) object, null, null, null);
497 }
498 return false;
499 }
500
501 @Override
502 public boolean add(final Resource subj) {
503 return doAdd(subj, null, null, null);
504 }
505
506 @Override
507 Resource term(final Statement stmt) {
508 return stmt.getSubject();
509 }
510
511 };
512 }
513
514
515
516
517
518
519
520
521
522
523
524
525 public final Set<URI> predicates() {
526 return new ValueSet<URI>() {
527
528 @Override
529 public boolean contains(final Object object) {
530 if (object instanceof URI) {
531 return QuadModel.this.contains(null, (URI) object, null);
532 }
533 return false;
534 }
535
536 @Override
537 public boolean remove(final Object object) {
538 if (object instanceof URI) {
539 return doRemove(null, (URI) object, null, null);
540 }
541 return false;
542 }
543
544 @Override
545 public boolean add(final URI pred) {
546 return doAdd(null, pred, null, null);
547 }
548
549 @Override
550 URI term(final Statement stmt) {
551 return stmt.getPredicate();
552 }
553
554 };
555 }
556
557
558
559
560
561
562
563
564
565
566
567
568 public final Set<Value> objects() {
569 return new ValueSet<Value>() {
570
571 @Override
572 public boolean contains(final Object object) {
573 if (object instanceof Value) {
574 return QuadModel.this.contains(null, null, (Value) object);
575 }
576 return false;
577 }
578
579 @Override
580 public boolean remove(final Object object) {
581 if (object instanceof Value) {
582 return doRemove(null, null, (Value) object, null);
583 }
584 return false;
585 }
586
587 @Override
588 public boolean add(final Value obj) {
589 return doAdd(null, null, obj, null);
590 }
591
592 @Override
593 Value term(final Statement stmt) {
594 return stmt.getObject();
595 }
596
597 };
598 }
599
600
601
602
603
604
605
606
607
608
609
610
611 public final Set<Resource> contexts() {
612 return new ValueSet<Resource>() {
613
614 @Override
615 public boolean contains(final Object object) {
616 if (object instanceof Resource || object == null) {
617 return QuadModel.this.contains(null, null, null, (Resource) object);
618 }
619 return false;
620 }
621
622 @Override
623 public boolean remove(final Object object) {
624 if (object instanceof Resource || object == null) {
625 return doRemove(null, null, null, new Resource[] { (Resource) object });
626 }
627 return false;
628 }
629
630 @Override
631 public boolean add(final Resource context) {
632 return doAdd(null, null, null, new Resource[] { context });
633 }
634
635 @Override
636 Resource term(final Statement stmt) {
637 return stmt.getContext();
638 }
639
640 };
641 }
642
643
644
645
646
647
648
649
650
651
652 @Nullable
653 public final Value objectValue() throws ModelException {
654 final Iterator<Value> iter = objects().iterator();
655 if (iter.hasNext()) {
656 final Value obj = iter.next();
657 if (iter.hasNext()) {
658 throw new ModelException(obj, iter.next());
659 }
660 return obj;
661 }
662 return null;
663 }
664
665
666
667
668
669
670
671
672
673 @Nullable
674 public final Literal objectLiteral() throws ModelException {
675 final Value obj = objectValue();
676 if (obj == null) {
677 return null;
678 }
679 if (obj instanceof Literal) {
680 return (Literal) obj;
681 }
682 throw new ModelException(obj);
683 }
684
685
686
687
688
689
690
691
692
693 @Nullable
694 public final Resource objectResource() throws ModelException {
695 final Value obj = objectValue();
696 if (obj == null) {
697 return null;
698 }
699 if (obj instanceof Resource) {
700 return (Resource) obj;
701 }
702 throw new ModelException(obj);
703 }
704
705
706
707
708
709
710
711
712
713 @Nullable
714 public final URI objectURI() throws ModelException {
715 final Value obj = objectValue();
716 if (obj == null) {
717 return null;
718 }
719 if (obj instanceof URI) {
720 return (URI) obj;
721 }
722 throw new ModelException(obj);
723 }
724
725
726
727
728
729
730
731
732
733 @Nullable
734 public final String objectString() throws ModelException {
735 final Value obj = objectValue();
736 if (obj == null) {
737 return null;
738 }
739 return obj.stringValue();
740 }
741
742 @Deprecated
743 @Override
744 public final Iterator<Statement> match(@Nullable final Resource subj,
745 @Nullable final URI pred, @Nullable final Value obj, final Resource... contexts) {
746 return filter(subj, pred, obj, contexts).iterator();
747 }
748
749 @Deprecated
750 @Override
751 public final ValueFactory getValueFactory() {
752 return ValueFactoryImpl.getInstance();
753 }
754
755 public final TripleSource getTripleSource() {
756 return new TripleSource() {
757
758 @Override
759 public ValueFactory getValueFactory() {
760 return Statements.VALUE_FACTORY;
761 }
762
763 @Override
764 public CloseableIteration<? extends Statement, QueryEvaluationException> getStatements(
765 final Resource subj, final URI pred, final Value obj,
766 final Resource... contexts) throws QueryEvaluationException {
767 return Iterators.toIteration(QuadModel.this.doIterator(subj, pred, obj, contexts));
768 }
769
770 };
771 }
772
773 public final EvaluationStatistics getEvaluationStatistics() {
774
775 return Algebra.getEvaluationStatistics((final StatementPattern pattern) -> {
776
777 final Var sv = pattern.getSubjectVar();
778 final Var pv = pattern.getPredicateVar();
779 final Var ov = pattern.getObjectVar();
780 final Var cv = pattern.getContextVar();
781
782 final Resource s = sv == null || !(sv.getValue() instanceof Resource) ? null
783 : (Resource) sv.getValue();
784 final URI p = pv == null || !(pv.getValue() instanceof URI) ? null : (URI) pv
785 .getValue();
786 final Value o = ov == null ? null : ov.getValue();
787 final Resource c = cv == null || !(cv.getValue() instanceof Resource) ? null
788 : (Resource) cv.getValue();
789
790 return doSizeEstimate(s, p, o, c);
791
792 });
793 }
794
795 public final Function<Value, Value> getValueNormalizer() {
796 return new Function<Value, Value>() {
797
798 @Override
799 public Value apply(final Value value) {
800 return doNormalize(value);
801 }
802
803 };
804 }
805
806 @SuppressWarnings("unchecked")
807 public <T extends Value> T normalize(final T value) {
808 return (T) doNormalize(value);
809 }
810
811 private abstract class ValueSet<V extends Value> extends AbstractSet<V> {
812
813 @Override
814 public boolean isEmpty() {
815 return QuadModel.this.isEmpty();
816 }
817
818 @Override
819 public int size() {
820 final Iterator<Statement> iter = QuadModel.this.iterator();
821 final Set<V> set = new HashSet<>();
822 while (iter.hasNext()) {
823 set.add(term(iter.next()));
824 }
825 return set.size();
826 }
827
828 @Override
829 public Iterator<V> iterator() {
830 return new ValueSetIterator(QuadModel.this.iterator());
831 }
832
833 @Override
834 public void clear() {
835 QuadModel.this.clear();
836 }
837
838 @Override
839 public abstract boolean add(V term);
840
841 abstract V term(Statement st);
842
843 private final class ValueSetIterator implements Iterator<V> {
844
845 private final Iterator<Statement> iter;
846
847 private final Set<V> set = new LinkedHashSet<V>();
848
849 private Statement current;
850
851 private Statement next;
852
853 private ValueSetIterator(final Iterator<Statement> iter) {
854 this.iter = iter;
855 }
856
857 @Override
858 public boolean hasNext() {
859 if (this.next == null) {
860 this.next = findNext();
861 }
862 return this.next != null;
863 }
864
865 @Override
866 public V next() {
867 if (this.next == null) {
868 this.next = findNext();
869 if (this.next == null) {
870 throw new NoSuchElementException();
871 }
872 }
873 this.current = this.next;
874 this.next = null;
875 final V value = term(this.current);
876 this.set.add(value);
877 return value;
878 }
879
880 @Override
881 public void remove() {
882 if (this.current == null) {
883 throw new IllegalStateException();
884 }
885 ValueSet.this.remove(this.current);
886 this.current = null;
887 }
888
889 private Statement findNext() {
890 while (this.iter.hasNext()) {
891 final Statement st = this.iter.next();
892 if (accept(st)) {
893 return st;
894 }
895 }
896 return null;
897 }
898
899 private boolean accept(final Statement st) {
900 return !this.set.contains(term(st));
901 }
902 }
903
904 }
905
906 private static final class UnmodifiableModel extends QuadModel {
907
908 private static final long serialVersionUID = 1L;
909
910 private final QuadModel model;
911
912 UnmodifiableModel(final QuadModel model) {
913 this.model = model;
914 }
915
916 @Override
917 protected Set<Namespace> doGetNamespaces() {
918 return this.model.doGetNamespaces();
919 }
920
921 @Override
922 protected Namespace doGetNamespace(final String prefix) {
923 return this.model.doGetNamespace(prefix);
924 }
925
926 @Override
927 protected Namespace doSetNamespace(final String prefix, final String name) {
928 throw new UnsupportedOperationException();
929 }
930
931 @Override
932 protected int doSize(@Nullable final Resource subj, @Nullable final URI pred,
933 @Nullable final Value obj, @Nullable final Resource[] ctxs) {
934 return this.model.doSize(subj, pred, obj, ctxs);
935 }
936
937 @Override
938 protected int doSizeEstimate(@Nullable final Resource subj, @Nullable final URI pred,
939 @Nullable final Value obj, @Nullable final Resource ctx) {
940 return this.model.doSizeEstimate(subj, pred, obj, ctx);
941 }
942
943 @Override
944 protected Iterator<Statement> doIterator(@Nullable final Resource subj,
945 @Nullable final URI pred, @Nullable final Value obj, final Resource[] ctxs) {
946 return Iterators.unmodifiable(doIterator(subj, pred, obj, ctxs));
947 }
948
949 @Override
950 protected boolean doAdd(@Nullable final Resource subj, @Nullable final URI pred,
951 @Nullable final Value obj, final Resource[] ctxs) {
952 throw new UnsupportedOperationException();
953 }
954
955 @Override
956 protected boolean doRemove(@Nullable final Resource subj, @Nullable final URI pred,
957 @Nullable final Value obj, final Resource[] ctxs) {
958 throw new UnsupportedOperationException();
959 }
960
961 @Override
962 protected Iterator<BindingSet> doEvaluate(final TupleExpr expr,
963 @Nullable final Dataset dataset, @Nullable final BindingSet bindings) {
964 return this.model.doEvaluate(expr, dataset, bindings);
965 }
966
967 }
968
969 private static final class EmptyModel extends QuadModel {
970
971 private static final long serialVersionUID = 1L;
972
973 private final QuadModel model;
974
975 EmptyModel(final QuadModel model) {
976 this.model = model;
977 }
978
979 @Override
980 protected Set<Namespace> doGetNamespaces() {
981 return this.model.doGetNamespaces();
982 }
983
984 @Override
985 protected Namespace doGetNamespace(final String prefix) {
986 return this.model.doGetNamespace(prefix);
987 }
988
989 @Override
990 protected Namespace doSetNamespace(final String prefix, final String name) {
991 return this.model.doSetNamespace(prefix, name);
992 }
993
994 @Override
995 protected int doSize(@Nullable final Resource subj, @Nullable final URI pred,
996 @Nullable final Value obj, final Resource[] ctxs) {
997 return 0;
998 }
999
1000 @Override
1001 protected int doSizeEstimate(@Nullable final Resource subj, @Nullable final URI pred,
1002 @Nullable final Value obj, @Nullable final Resource ctx) {
1003 return 0;
1004 }
1005
1006 @Override
1007 protected Iterator<Statement> doIterator(@Nullable final Resource subj,
1008 @Nullable final URI pred, @Nullable final Value obj, final Resource[] ctxs) {
1009 return Collections.emptyIterator();
1010 }
1011
1012 @Override
1013 protected boolean doAdd(@Nullable final Resource subj, @Nullable final URI pred,
1014 @Nullable final Value obj, final Resource[] ctxs) {
1015 throw new UnsupportedOperationException("All statements are filtered out of view");
1016 }
1017
1018 @Override
1019 protected boolean doRemove(@Nullable final Resource subj, @Nullable final URI pred,
1020 @Nullable final Value obj, final Resource[] ctxs) {
1021 return false;
1022 }
1023
1024 @Override
1025 protected Iterator<BindingSet> doEvaluate(final TupleExpr expr,
1026 @Nullable final Dataset dataset, @Nullable final BindingSet bindings) {
1027 return Collections.emptyIterator();
1028 }
1029
1030 @Override
1031 public QuadModel filter(@Nullable final Resource subj, @Nullable final URI pred,
1032 @Nullable final Value obj, final Resource... ctxs) {
1033 return this;
1034 }
1035
1036 }
1037
1038 private final static class FilteredModel extends QuadModel {
1039
1040 private static final URI EMPTY = new URIImpl("sesame:empty");
1041
1042 private static final long serialVersionUID = 1L;
1043
1044 private final QuadModel model;
1045
1046 @Nullable
1047 private final Resource subj;
1048
1049 @Nullable
1050 private final URI pred;
1051
1052 @Nullable
1053 private final Value obj;
1054
1055 private final Resource[] contexts;
1056
1057 FilteredModel(final QuadModel model, @Nullable final Resource subj,
1058 @Nullable final URI pred, @Nullable final Value obj, final Resource[] contexts) {
1059 this.model = model;
1060 this.subj = subj;
1061 this.pred = pred;
1062 this.obj = obj;
1063 this.contexts = contexts;
1064 }
1065
1066 @Override
1067 protected Set<Namespace> doGetNamespaces() {
1068 return this.model.doGetNamespaces();
1069 }
1070
1071 @Override
1072 protected Namespace doGetNamespace(final String prefix) {
1073 return this.model.doGetNamespace(prefix);
1074 }
1075
1076 @Override
1077 protected Namespace doSetNamespace(final String prefix, final String name) {
1078 return this.model.doSetNamespace(prefix, name);
1079 }
1080
1081 @Override
1082 protected int doSize(@Nullable Resource subj, @Nullable URI pred, @Nullable Value obj,
1083 @Nullable Resource[] ctxs) {
1084
1085 subj = (Resource) filter(subj, this.subj);
1086 pred = (URI) filter(pred, this.pred);
1087 obj = filter(obj, this.obj);
1088 ctxs = filter(ctxs, this.contexts);
1089
1090 return subj == EMPTY || pred == EMPTY || obj == EMPTY || ctxs == null ? 0
1091 : this.model.doSize(subj, pred, obj, ctxs);
1092 }
1093
1094 @Override
1095 protected int doSizeEstimate(@Nullable Resource subj, @Nullable URI pred,
1096 @Nullable Value obj, @Nullable final Resource ctx) {
1097
1098 subj = (Resource) filter(subj, this.subj);
1099 pred = (URI) filter(pred, this.pred);
1100 obj = filter(obj, this.obj);
1101 final Resource[] ctxs = filter(ctx == null ? CTX_ANY : new Resource[] { ctx },
1102 this.contexts);
1103
1104 if (subj == EMPTY || pred == EMPTY || obj == EMPTY || ctxs == null) {
1105 return 0;
1106 } else if (ctxs.length == 0) {
1107 return this.model.doSizeEstimate(subj, pred, obj, null);
1108 } else if (ctxs.length == 1) {
1109 return this.model.doSizeEstimate(subj, pred, obj, ctxs[0]);
1110 } else {
1111 int size = 0;
1112 for (final Resource c : ctxs) {
1113 size += this.model.doSizeEstimate(subj, pred, obj, c);
1114 }
1115 return size;
1116 }
1117 }
1118
1119 @Override
1120 protected Iterator<Statement> doIterator(@Nullable Resource subj, @Nullable URI pred,
1121 @Nullable Value obj, Resource[] ctxs) {
1122
1123 subj = (Resource) filter(subj, this.subj);
1124 pred = (URI) filter(pred, this.pred);
1125 obj = filter(obj, this.obj);
1126 ctxs = filter(ctxs, this.contexts);
1127
1128 return subj == EMPTY || pred == EMPTY || obj == EMPTY || ctxs == null ? Collections
1129 .emptyIterator() : this.model.doIterator(subj, pred, obj, ctxs);
1130 }
1131
1132 @Override
1133 protected boolean doAdd(@Nullable Resource subj, @Nullable URI pred, @Nullable Value obj,
1134 Resource[] ctxs) {
1135
1136 subj = (Resource) filter(subj, this.subj);
1137 pred = (URI) filter(pred, this.pred);
1138 obj = filter(obj, this.obj);
1139 ctxs = filter(ctxs, this.contexts);
1140
1141 if (subj == EMPTY || pred == EMPTY || obj == EMPTY || ctxs == null) {
1142 throw new IllegalArgumentException("Statement is filtered out of view");
1143 }
1144
1145 return this.model.doAdd(subj, pred, obj, ctxs);
1146 }
1147
1148 @Override
1149 protected boolean doRemove(@Nullable Resource subj, @Nullable URI pred,
1150 @Nullable Value obj, Resource[] ctxs) {
1151
1152 subj = (Resource) filter(subj, this.subj);
1153 pred = (URI) filter(pred, this.pred);
1154 obj = filter(obj, this.obj);
1155 ctxs = filter(ctxs, this.contexts);
1156
1157 return subj == EMPTY || pred == EMPTY || obj == EMPTY || ctxs == null ? false
1158 : this.model.doRemove(subj, pred, obj, ctxs);
1159 }
1160
1161 @Override
1162 public QuadModel filter(@Nullable Resource subj, @Nullable URI pred, @Nullable Value obj,
1163 Resource... ctxs) {
1164
1165 subj = (Resource) filter(subj, this.subj);
1166 pred = (URI) filter(pred, this.pred);
1167 obj = filter(obj, this.obj);
1168 ctxs = filter(ctxs, this.contexts);
1169
1170 return subj == EMPTY || pred == EMPTY || obj == EMPTY || ctxs == null ? new EmptyModel(
1171 this.model) : this.model.filter(subj, pred, obj, ctxs);
1172 }
1173
1174 private static Value filter(@Nullable final Value inputValue,
1175 @Nullable final Value thisValue) {
1176 if (inputValue == null) {
1177 return thisValue;
1178 } else if (thisValue == null || inputValue.equals(thisValue)) {
1179 return inputValue;
1180 } else {
1181 return EMPTY;
1182 }
1183 }
1184
1185 @Nullable
1186 private static Resource[] filter(final Resource[] inputCtxs, final Resource[] thisCtxs) {
1187 if (inputCtxs.length == 0) {
1188 return thisCtxs;
1189 } else if (thisCtxs.length == 0) {
1190 return inputCtxs;
1191 } else {
1192 int inputIdx = 0;
1193 outer: for (; inputIdx < inputCtxs.length; ++inputIdx) {
1194 for (final Resource thisCtx : thisCtxs) {
1195 if (Objects.equals(inputCtxs[inputIdx], thisCtx)) {
1196 continue outer;
1197 }
1198 }
1199 if (inputCtxs.length == 1) {
1200 return null;
1201 }
1202 final Resource[] ctxs = new Resource[inputCtxs.length - 1];
1203 if (inputIdx > 0) {
1204 System.arraycopy(inputCtxs, 0, ctxs, 0, inputIdx);
1205 }
1206 int index = inputIdx;
1207 for (++inputIdx; inputIdx < inputCtxs.length; ++inputIdx) {
1208 for (final Resource thisCtx : thisCtxs) {
1209 if (Objects.equals(inputCtxs[inputIdx], thisCtx)) {
1210 ctxs[index++] = inputCtxs[inputIdx];
1211 break;
1212 }
1213 }
1214 }
1215 return index == 0 ? null : index == ctxs.length ? ctxs
1216 : Arrays.copyOfRange(ctxs, 0, index);
1217 }
1218 return inputCtxs;
1219 }
1220 }
1221
1222 }
1223
1224 }