View Javadoc
1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements. See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership. The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.commons.rdf.api;
19  
20  import static org.junit.Assert.assertEquals;
21  import static org.junit.Assert.assertFalse;
22  import static org.junit.Assert.assertNotNull;
23  import static org.junit.Assert.assertTrue;
24  import static org.junit.Assert.fail;
25  
26  import java.util.ArrayList;
27  import java.util.HashSet;
28  import java.util.Iterator;
29  import java.util.List;
30  import java.util.Locale;
31  import java.util.Map;
32  import java.util.Optional;
33  import java.util.concurrent.ConcurrentHashMap;
34  import java.util.stream.Stream;
35  
36  import org.junit.Assume;
37  import org.junit.Before;
38  import org.junit.Test;
39  
40  /**
41   * Test Graph implementation
42   * <p>
43   * To add to your implementation's tests, create a subclass with a name ending
44   * in <code>Test</code> and provide {@link #createFactory()} which minimally
45   * must support {@link RDF#createGraph()} and {@link RDF#createIRI(String)}, but
46   * ideally support all operations.
47   * <p>
48   * This test uses try-with-resources blocks for calls to {@link Graph#stream()}
49   * and {@link Graph#iterate()}.
50   *
51   * @see Graph
52   * @see RDF
53   */
54  public abstract class AbstractGraphTest {
55  
56      protected RDF factory;
57      protected Graph graph;
58      protected IRI alice;
59      protected IRI bob;
60      protected IRI name;
61      protected IRI knows;
62      protected IRI member;
63      protected BlankNode bnode1;
64      protected BlankNode bnode2;
65      protected Literal aliceName;
66      protected Literal bobName;
67      protected Literal secretClubName;
68      protected Literal companyName;
69      protected Triple bobNameTriple;
70  
71      /**
72       *
73       * This method must be overridden by the implementing test to provide a
74       * factory for the test to create {@link Graph}, {@link IRI} etc.
75       *
76       * @return {@link RDF} instance to be tested.
77       */
78      protected abstract RDF createFactory();
79  
80      @Before
81      public void createGraphAndAdd() {
82          factory = createFactory();
83          graph = factory.createGraph();
84          assertEquals(0, graph.size());
85  
86          alice = factory.createIRI("http://example.com/alice");
87          bob = factory.createIRI("http://example.com/bob");
88          name = factory.createIRI("http://xmlns.com/foaf/0.1/name");
89          knows = factory.createIRI("http://xmlns.com/foaf/0.1/knows");
90          member = factory.createIRI("http://xmlns.com/foaf/0.1/member");
91          try {
92              bnode1 = factory.createBlankNode("org1");
93              bnode2 = factory.createBlankNode("org2");
94          } catch (final UnsupportedOperationException ex) {
95              // leave as null
96          }
97  
98          try {
99              secretClubName = factory.createLiteral("The Secret Club");
100             companyName = factory.createLiteral("A company");
101             aliceName = factory.createLiteral("Alice");
102             bobName = factory.createLiteral("Bob", "en-US");
103         } catch (final UnsupportedOperationException ex) {
104             // leave as null
105         }
106 
107         if (aliceName != null) {
108             graph.add(alice, name, aliceName);
109         }
110         graph.add(alice, knows, bob);
111 
112         if (bnode1 != null) {
113             graph.add(alice, member, bnode1);
114         }
115 
116         if (bobName != null) {
117             try {
118                 bobNameTriple = factory.createTriple(bob, name, bobName);
119             } catch (final UnsupportedOperationException ex) {
120                 // leave as null
121             }
122             if (bobNameTriple != null) {
123                 graph.add(bobNameTriple);
124             }
125         }
126         if (bnode1 != null) {
127             graph.add(factory.createTriple(bob, member, bnode1));
128             graph.add(factory.createTriple(bob, member, bnode2));
129             if (secretClubName != null) {
130                 graph.add(bnode1, name, secretClubName);
131                 graph.add(bnode2, name, companyName);
132             }
133         }
134     }
135 
136     @Test
137     public void size() throws Exception {
138         assertTrue(graph.size() > 0);
139         Assume.assumeNotNull(bnode1, bnode2, aliceName, bobName, secretClubName, companyName, bobNameTriple);
140         // Can only reliably predict size if we could create all triples
141         assertEquals(8, graph.size());
142     }
143 
144     @Test
145     public void iterate() throws Exception {
146 
147         Assume.assumeTrue(graph.size() > 0);
148 
149         final List<Triple> triples = new ArrayList<>();
150         for (final Triple t : graph.iterate()) {
151             triples.add(t);
152         }
153         assertEquals(graph.size(), triples.size());
154         if (bobNameTriple != null) {
155             assertTrue(triples.contains(bobNameTriple));
156         }
157 
158         // aborted iteration
159         final Iterable<Triple> iterate = graph.iterate();
160         final Iterator<Triple> it = iterate.iterator();
161 
162         assertTrue(it.hasNext());
163         it.next();
164         closeIterable(iterate);
165 
166         // second iteration - should start from fresh and
167         // get the same count
168         long count = 0;
169         final Iterable<Triple> iterable = graph.iterate();
170         for (@SuppressWarnings("unused") final
171         Triple t : iterable) {
172             count++;
173         }
174         assertEquals(graph.size(), count);
175     }
176 
177     /**
178      * Special triple closing for RDF4J.
179      */
180     private void closeIterable(final Iterable<Triple> iterate) throws Exception {
181         if (iterate instanceof AutoCloseable) {
182             ((AutoCloseable) iterate).close();
183         }
184     }
185 
186     @Test
187     public void iterateFilter() throws Exception {
188         final List<RDFTerm> friends = new ArrayList<>();
189         final IRI alice = factory.createIRI("http://example.com/alice");
190         final IRI knows = factory.createIRI("http://xmlns.com/foaf/0.1/knows");
191         for (final Triple t : graph.iterate(alice, knows, null)) {
192             friends.add(t.getObject());
193         }
194         assertEquals(1, friends.size());
195         assertEquals(bob, friends.get(0));
196 
197         // .. can we iterate over zero hits?
198         final Iterable<Triple> iterate = graph.iterate(bob, knows, alice);
199         for (final Triple unexpected : iterate) {
200             fail("Unexpected triple " + unexpected);
201         }
202         // closeIterable(iterate);
203     }
204 
205     @Test
206     public void contains() throws Exception {
207         assertFalse(graph.contains(bob, knows, alice)); // or so he claims..
208 
209         assertTrue(graph.contains(alice, knows, bob));
210 
211         try (Stream<? extends Triple> stream = graph.stream()) {
212             final Optional<? extends Triple> first = stream.skip(4).findFirst();
213             Assume.assumeTrue(first.isPresent());
214             final Triple existingTriple = first.get();
215             assertTrue(graph.contains(existingTriple));
216         }
217 
218         final Triple nonExistingTriple = factory.createTriple(bob, knows, alice);
219         assertFalse(graph.contains(nonExistingTriple));
220 
221         Triple triple = null;
222         try {
223             triple = factory.createTriple(alice, knows, bob);
224         } catch (final UnsupportedOperationException ex) {
225         }
226         if (triple != null) {
227             // FIXME: Should not this always be true?
228             // assertTrue(graph.contains(triple));
229         }
230     }
231 
232     @Test
233     public void remove() throws Exception {
234         final long fullSize = graph.size();
235         graph.remove(alice, knows, bob);
236         final long shrunkSize = graph.size();
237         assertEquals(1, fullSize - shrunkSize);
238 
239         graph.remove(alice, knows, bob);
240         assertEquals(shrunkSize, graph.size()); // unchanged
241 
242         graph.add(alice, knows, bob);
243         graph.add(alice, knows, bob);
244         graph.add(alice, knows, bob);
245         // Undetermined size at this point -- but at least it
246         // should be bigger
247         assertTrue(graph.size() > shrunkSize);
248 
249         // and after a single remove they should all be gone
250         graph.remove(alice, knows, bob);
251         assertEquals(shrunkSize, graph.size());
252 
253         Triple otherTriple;
254         try (Stream<? extends Triple> stream = graph.stream()) {
255             final Optional<? extends Triple> anyTriple = stream.findAny();
256             Assume.assumeTrue(anyTriple.isPresent());
257             otherTriple = anyTriple.get();
258         }
259 
260         graph.remove(otherTriple);
261         assertEquals(shrunkSize - 1, graph.size());
262         graph.remove(otherTriple);
263         assertEquals(shrunkSize - 1, graph.size()); // no change
264 
265         // for some reason in rdf4j this causes duplicates!
266         graph.add(otherTriple);
267         // graph.stream().forEach(System.out::println);
268         // should have increased
269         assertTrue(graph.size() >= shrunkSize);
270     }
271 
272     @Test
273     public void clear() throws Exception {
274         graph.clear();
275         assertFalse(graph.contains(alice, knows, bob));
276         assertEquals(0, graph.size());
277         graph.clear(); // no-op
278         assertEquals(0, graph.size());
279     }
280 
281     @Test
282     public void getTriples() throws Exception {
283         long tripleCount;
284         try (Stream<? extends Triple> stream = graph.stream()) {
285             tripleCount = stream.count();
286         }
287         assertTrue(tripleCount > 0);
288 
289         try (Stream<? extends Triple> stream = graph.stream()) {
290             assertTrue(stream.allMatch(t -> graph.contains(t)));
291         }
292 
293         // Check exact count
294         Assume.assumeNotNull(bnode1, bnode2, aliceName, bobName, secretClubName, companyName, bobNameTriple);
295         assertEquals(8, tripleCount);
296     }
297 
298     @Test
299     public void getTriplesQuery() throws Exception {
300 
301         try (Stream<? extends Triple> stream = graph.stream(alice, null, null)) {
302             final long aliceCount = stream.count();
303             assertTrue(aliceCount > 0);
304             Assume.assumeNotNull(aliceName);
305             assertEquals(3, aliceCount);
306         }
307 
308         Assume.assumeNotNull(bnode1, bnode2, bobName, companyName, secretClubName);
309         try (Stream<? extends Triple> stream = graph.stream(null, name, null)) {
310             assertEquals(4, stream.count());
311         }
312         Assume.assumeNotNull(bnode1);
313         try (Stream<? extends Triple> stream = graph.stream(null, member, null)) {
314             assertEquals(3, stream.count());
315         }
316     }
317 
318     @Test
319     public void addBlankNodesFromMultipleGraphs() throws Exception {
320 
321         // Create two separate Graph instances
322         // and add them to a new Graph g3
323         try (final Graph g1 = createGraph1(); final Graph g2 = createGraph2(); final Graph g3 = factory.createGraph()) {
324             addAllTriples(g1, g3);
325             addAllTriples(g2, g3);
326 
327             // Let's make a map to find all those blank nodes after insertion
328             // (The Graph implementation is not currently required to
329             // keep supporting those BlankNodes with contains() - see
330             // COMMONSRDF-15)
331 
332             final Map<String, BlankNodeOrIRI> whoIsWho = new ConcurrentHashMap<>();
333             // ConcurrentHashMap as we will try parallel forEach below,
334             // which should not give inconsistent results (it does with a
335             // HashMap!)
336 
337             // look up BlankNodes by name
338             final IRI name = factory.createIRI("http://xmlns.com/foaf/0.1/name");
339             try (Stream<? extends Triple> stream = g3.stream(null, name, null)) {
340                 stream.parallel().forEach(t -> whoIsWho.put(t.getObject().ntriplesString(), t.getSubject()));
341             }
342 
343             assertEquals(4, whoIsWho.size());
344             // and contains 4 unique values
345             assertEquals(4, new HashSet<>(whoIsWho.values()).size());
346 
347             final BlankNodeOrIRI b1Alice = whoIsWho.get("\"Alice\"");
348             assertNotNull(b1Alice);
349             final BlankNodeOrIRI b2Bob = whoIsWho.get("\"Bob\"");
350             assertNotNull(b2Bob);
351             final BlankNodeOrIRI b1Charlie = whoIsWho.get("\"Charlie\"");
352             assertNotNull(b1Charlie);
353             final BlankNodeOrIRI b2Dave = whoIsWho.get("\"Dave\"");
354             assertNotNull(b2Dave);
355 
356             // All blank nodes should differ
357             notEquals(b1Alice, b2Bob);
358             notEquals(b1Alice, b1Charlie);
359             notEquals(b1Alice, b2Dave);
360             notEquals(b2Bob, b1Charlie);
361             notEquals(b2Bob, b2Dave);
362             notEquals(b1Charlie, b2Dave);
363 
364             // And we should be able to query with them again
365             // as we got them back from g3
366             final IRI hasChild = factory.createIRI("http://example.com/hasChild");
367             assertTrue(g3.contains(b1Alice, hasChild, b2Bob));
368             assertTrue(g3.contains(b2Dave, hasChild, b1Charlie));
369             // But not
370             assertFalse(g3.contains(b1Alice, hasChild, b1Alice));
371             assertFalse(g3.contains(b1Alice, hasChild, b1Charlie));
372             assertFalse(g3.contains(b1Alice, hasChild, b2Dave));
373             // nor
374             assertFalse(g3.contains(b2Dave, hasChild, b1Alice));
375             assertFalse(g3.contains(b2Dave, hasChild, b1Alice));
376 
377             // and these don't have any children (as far as we know)
378             assertFalse(g3.contains(b2Bob, hasChild, null));
379             assertFalse(g3.contains(b1Charlie, hasChild, null));
380         } catch (final UnsupportedOperationException ex) {
381             Assume.assumeNoException(ex);
382         }
383     }
384 
385     @Test
386     public void containsLanguageTagsCaseInsensitive() throws Exception {
387         // COMMONSRDF-51: Ensure we can add/contains/remove with any casing
388         // of literal language tag
389         final Literal lower = factory.createLiteral("Hello", "en-gb");
390         final Literal upper = factory.createLiteral("Hello", "EN-GB");
391         final Literal mixed = factory.createLiteral("Hello", "en-GB");
392 
393         final IRI example1 = factory.createIRI("http://example.com/s1");
394         final IRI greeting = factory.createIRI("http://example.com/greeting");
395 
396         try (final Graph graph = factory.createGraph()) {
397             graph.add(example1, greeting, upper);
398 
399             // any kind of Triple should match
400             assertTrue(graph.contains(factory.createTriple(example1, greeting, upper)));
401             assertTrue(graph.contains(factory.createTriple(example1, greeting, lower)));
402             assertTrue(graph.contains(factory.createTriple(example1, greeting, mixed)));
403 
404             // or as patterns
405             assertTrue(graph.contains(null, null, upper));
406             assertTrue(graph.contains(null, null, lower));
407             assertTrue(graph.contains(null, null, mixed));
408         }
409     }
410 
411     @Test
412     public void containsLanguageTagsCaseInsensitiveTurkish() throws Exception {
413         // COMMONSRDF-51: Special test for Turkish issue where
414         // "i".toLowerCase() != "i"
415         // See also:
416         // https://garygregory.wordpress.com/2015/11/03/java-lowercase-conversion-turkey/
417 
418         // This is similar to the test in AbstractRDFTest, but on a graph
419         final Locale defaultLocale = Locale.getDefault();
420         try (final Graph g = factory.createGraph()) {
421             Locale.setDefault(Locale.ROOT);
422             final Literal lowerROOT = factory.createLiteral("moi", "fi");
423             final Literal upperROOT = factory.createLiteral("moi", "FI");
424             final Literal mixedROOT = factory.createLiteral("moi", "fI");
425             final IRI exampleROOT = factory.createIRI("http://example.com/s1");
426             final IRI greeting = factory.createIRI("http://example.com/greeting");
427             g.add(exampleROOT, greeting, mixedROOT);
428 
429             final Locale turkish = Locale.forLanguageTag("TR");
430             Locale.setDefault(turkish);
431             // If the below assertion fails, then the Turkish
432             // locale no longer have this peculiarity that
433             // we want to test.
434             Assume.assumeFalse("FI".toLowerCase().equals("fi"));
435 
436             // Below is pretty much the same as in
437             // containsLanguageTagsCaseInsensitive()
438             final Literal lower = factory.createLiteral("moi", "fi");
439             final Literal upper = factory.createLiteral("moi", "FI");
440             final Literal mixed = factory.createLiteral("moi", "fI");
441 
442             final IRI exampleTR = factory.createIRI("http://example.com/s2");
443             g.add(exampleTR, greeting, upper);
444             assertTrue(g.contains(factory.createTriple(exampleTR, greeting, upper)));
445             assertTrue(g.contains(factory.createTriple(exampleTR, greeting, upperROOT)));
446             assertTrue(g.contains(factory.createTriple(exampleTR, greeting, lower)));
447             assertTrue(g.contains(factory.createTriple(exampleTR, greeting, lowerROOT)));
448             assertTrue(g.contains(factory.createTriple(exampleTR, greeting, mixed)));
449             assertTrue(g.contains(factory.createTriple(exampleTR, greeting, mixedROOT)));
450             assertTrue(g.contains(exampleTR, null, upper));
451             assertTrue(g.contains(exampleTR, null, upperROOT));
452             assertTrue(g.contains(exampleTR, null, lower));
453             assertTrue(g.contains(exampleTR, null, lowerROOT));
454             assertTrue(g.contains(exampleTR, null, mixed));
455             assertTrue(g.contains(exampleTR, null, mixedROOT));
456 
457             // What about the triple we added while in ROOT locale?
458             assertTrue(g.contains(factory.createTriple(exampleROOT, greeting, upper)));
459             assertTrue(g.contains(factory.createTriple(exampleROOT, greeting, lower)));
460             assertTrue(g.contains(factory.createTriple(exampleROOT, greeting, mixed)));
461             assertTrue(g.contains(exampleROOT, null, upper));
462             assertTrue(g.contains(exampleROOT, null, lower));
463             assertTrue(g.contains(exampleROOT, null, mixed));
464         } finally {
465             Locale.setDefault(defaultLocale);
466         }
467     }
468 
469 
470     @Test
471     public void removeLanguageTagsCaseInsensitive() throws Exception {
472         // COMMONSRDF-51: Ensure we can remove with any casing
473         // of literal language tag
474         final Literal lower = factory.createLiteral("Hello", "en-gb");
475         final Literal upper = factory.createLiteral("Hello", "EN-GB");
476         final Literal mixed = factory.createLiteral("Hello", "en-GB");
477 
478         final IRI example1 = factory.createIRI("http://example.com/s1");
479         final IRI greeting = factory.createIRI("http://example.com/greeting");
480 
481         try (final Graph graph = factory.createGraph()) {
482             graph.add(example1, greeting, upper);
483 
484             // Remove should also honour any case
485             graph.remove(example1, null, mixed);
486             assertFalse(graph.contains(null, greeting, null));
487 
488             graph.add(example1, greeting, lower);
489             graph.remove(example1, null, upper);
490 
491             // Check with Triple
492             graph.add(factory.createTriple(example1, greeting, mixed));
493             graph.remove(factory.createTriple(example1, greeting, upper));
494             assertFalse(graph.contains(null, greeting, null));
495         }
496     }
497 
498     private static Optional<? extends Triple> closableFindAny(final Stream<? extends Triple> stream) {
499         try (Stream<? extends Triple> s = stream) {
500             return s.findAny();
501         }
502     }
503 
504     @Test
505     public void streamLanguageTagsCaseInsensitive() throws Exception {
506         // COMMONSRDF-51: Ensure we can add/contains/remove with any casing
507         // of literal language tag
508         final Literal lower = factory.createLiteral("Hello", "en-gb");
509         final Literal upper = factory.createLiteral("Hello", "EN-GB");
510         final Literal mixed = factory.createLiteral("Hello", "en-GB");
511 
512         final IRI example1 = factory.createIRI("http://example.com/s1");
513         final IRI greeting = factory.createIRI("http://example.com/greeting");
514 
515         try (final Graph graph = factory.createGraph()) {
516             graph.add(example1, greeting, upper);
517 
518             // or as patterns
519             assertTrue(closableFindAny(graph.stream(null, null, upper)).isPresent());
520             assertTrue(closableFindAny(graph.stream(null, null, lower)).isPresent());
521             assertTrue(closableFindAny(graph.stream(null, null, mixed)).isPresent());
522 
523             // Check the triples returned equal a new triple
524             final Triple t = closableFindAny(graph.stream(null, null, lower)).get();
525             assertEquals(t, factory.createTriple(example1, greeting, mixed));
526         }
527     }
528 
529     private void notEquals(final BlankNodeOrIRI node1, final BlankNodeOrIRI node2) {
530         assertFalse(node1.equals(node2));
531         // in which case we should be able to assume
532         // (as they are in the same graph)
533         assertFalse(node1.ntriplesString().equals(node2.ntriplesString()));
534     }
535 
536     /**
537      * Add all triples from the source to the target.
538      * <p>
539      * The triples may be copied in any order. No special conversion or
540      * adaptation of {@link BlankNode}s are performed.
541      *
542      * @param source
543      *            Source Graph to copy triples from
544      * @param target
545      *            Target Graph where triples will be added
546      */
547     private void addAllTriples(final Graph source, final Graph target) {
548 
549         // unordered() as we don't need to preserve triple order
550         // sequential() as we don't (currently) require target Graph to be
551         // thread-safe
552 
553         try (Stream<? extends Triple> stream = source.stream()) {
554             stream.unordered().sequential().forEach(t -> target.add(t));
555         }
556     }
557 
558     /**
559      * Make a new graph with two BlankNodes - each with a different
560      * uniqueReference
561      */
562     private Graph createGraph1() {
563         final RDF factory1 = createFactory();
564 
565         final IRI name = factory1.createIRI("http://xmlns.com/foaf/0.1/name");
566         final Graph g1 = factory1.createGraph();
567         final BlankNode b1 = createOwnBlankNode("b1", "0240eaaa-d33e-4fc0-a4f1-169d6ced3680");
568         g1.add(b1, name, factory1.createLiteral("Alice"));
569 
570         final BlankNode b2 = createOwnBlankNode("b2", "9de7db45-0ce7-4b0f-a1ce-c9680ffcfd9f");
571         g1.add(b2, name, factory1.createLiteral("Bob"));
572 
573         final IRI hasChild = factory1.createIRI("http://example.com/hasChild");
574         g1.add(b1, hasChild, b2);
575 
576         return g1;
577     }
578 
579     /**
580      * Create a different implementation of BlankNode to be tested with
581      * graph.add(a,b,c); (the implementation may or may not then choose to
582      * translate such to its own instances)
583      *
584      * @param name
585      * @return
586      */
587     private BlankNode createOwnBlankNode(final String name, final String uuid) {
588         return new BlankNode() {
589             @Override
590             public String ntriplesString() {
591                 return "_: " + name;
592             }
593 
594             @Override
595             public String uniqueReference() {
596                 return uuid;
597             }
598 
599             @Override
600             public int hashCode() {
601                 return uuid.hashCode();
602             }
603 
604             @Override
605             public boolean equals(final Object obj) {
606                 if (!(obj instanceof BlankNode)) {
607                     return false;
608                 }
609                 final BlankNode other = (BlankNode) obj;
610                 return uuid.equals(other.uniqueReference());
611             }
612         };
613     }
614 
615     private Graph createGraph2() {
616         final RDF factory2 = createFactory();
617         final IRI name = factory2.createIRI("http://xmlns.com/foaf/0.1/name");
618 
619         final Graph g2 = factory2.createGraph();
620 
621         final BlankNode b1 = createOwnBlankNode("b1", "bc8d3e45-a08f-421d-85b3-c25b373abf87");
622         g2.add(b1, name, factory2.createLiteral("Charlie"));
623 
624         final BlankNode b2 = createOwnBlankNode("b2", "2209097a-5078-4b03-801a-6a2d2f50d739");
625         g2.add(b2, name, factory2.createLiteral("Dave"));
626 
627         final IRI hasChild = factory2.createIRI("http://example.com/hasChild");
628         // NOTE: Opposite direction of loadGraph1
629         g2.add(b2, hasChild, b1);
630         return g2;
631     }
632 
633     /**
634      * An attempt to use the Java 8 streams to look up a more complicated query.
635      * <p>
636      * FYI, the equivalent SPARQL version (untested):
637      *
638      * <pre>
639      *     SELECT ?orgName WHERE {
640      *             ?org foaf:name ?orgName .
641      *             ?alice foaf:member ?org .
642      *             ?bob foaf:member ?org .
643      *             ?alice foaf:knows ?bob .
644      *           FILTER NOT EXIST { ?bob foaf:knows ?alice }
645      *    }
646      * </pre>
647      *
648      * @throws Exception If test fails
649      */
650     @Test
651     public void whyJavaStreamsMightNotTakeOverFromSparql() throws Exception {
652         Assume.assumeNotNull(bnode1, bnode2, secretClubName);
653         // Find a secret organizations
654         try (Stream<? extends Triple> stream = graph.stream(null, knows, null)) {
655             assertEquals("\"The Secret Club\"",
656                     // Find One-way "knows"
657                     stream.filter(t -> !graph.contains((BlankNodeOrIRI) t.getObject(), knows, t.getSubject()))
658                             .map(knowsTriple -> {
659                                 try (Stream<? extends Triple> memberOf = graph
660                                         // and those they know, what are they
661                                         // member of?
662                                         .stream((BlankNodeOrIRI) knowsTriple.getObject(), member, null)) {
663                                     return memberOf
664                                             // keep those which first-guy is a
665                                             // member of
666                                             .filter(memberTriple -> graph.contains(knowsTriple.getSubject(), member,
667                                                     // First hit is good enough
668                                                     memberTriple.getObject()))
669                                             .findFirst().get().getObject();
670                                 }
671                             })
672                             // then look up the name of that org
673                             .map(org -> {
674                                 try (Stream<? extends Triple> orgName = graph.stream((BlankNodeOrIRI) org, name,
675                                         null)) {
676                                     return orgName.findFirst().get().getObject().ntriplesString();
677                                 }
678                             }).findFirst().get());
679         }
680     }
681 }