GH-1279: Improve the implementation of in-memory, general-purpose,non-transactional graphs.
Summary:
- Improved performance of GraphMem for:
- Graph#find
- Slightly by tuning the filter predicates
- Significantly when results are processed via Iterator#forEachRemaining
- Graph#stream
- Slightly by tuning the filter predicates
- Significantly for most stream operations
- java.util.stream.BaseStream#parallel is now fully supported.
(Before these changes, parallel execution was even slower than single-threaded execution)
- Graph#contains
- Only for non-concrete (fluent) patterns
- GraphMem used a variety of different Iterator implementations, helper classes, and wrappers:
-> None of them supported #forEachRemaining
-> It seemed appropriate to almost universally implement #forEachRemaining to avoid a wrapper or helper
from breaking the newly gained performance advantages of #forEachRemaining
Details:
Implemented Iterator#forEachRemaining in an optimized way for many iterators throughout the Jena project:
- Replaced hasNext();next(); calls by #forEachRemaining in some promising places (not all)
Optimized NiceIterator:
- NiceIterator#hasNext now avoids redundant calls to current.hasNext()
- NiceIterator#andThen has optimized code to handle expensive hasNext calls of wrapped iterators.
Tuned GraphMem:
- Removed unused classes in the 'mem' namespace.
- Iterators:
- Tuned Iterator implementations, mainly by adding forEachRemaining implementations
- Optimized code in BasicKeyIterator. The new iterator works in reverse order, so I had to adapt HashCommon#removeFrom
- Optimized code in HashedBunchMap#iterator
- TrackingTripleIterator#forEachRemaining now simply calls super.forEachRemaining() and sets current to null
-> This had a significant impact on performance
- Spliterators:
- Created specialized spliterators SparseArraySpliterator and SparseArraySubSpliterator (+unit tests)
- Replaced Spliterator implementation within HashCommon and HashedBunchMap with SparseArraySpliterator
- Implemented Spliterators to support fast stream operations, where the SparseSpliterator*
implementations were not suitable
- Replaced usage of org.apache.jena.graph.impl.GraphBase#containsByFind with optimized implementations:
- Introduced org.apache.jena.graph.impl.TripleStore#containsMatch
- Implemented org.apache.jena.mem.NodeToTriplesMapBase#containsMatch using the spliterator
- Filter operations:
- Introduced org.apache.jena.graph.Triple.Field#filterOnConcrete to avoid double-checking of Node#isConcrete (+unit tests)
- Created org.apache.jena.mem.FieldFilter to efficiently build filter predicates only when needed, and only with
the required conditions (+unit tests)
- Used org.apache.jena.mem.FieldFilter#filterOn in NodeToTriplesMap#iterator, NodeToTriplesMapBase#stream,
and NodeToTriplesMapBase#containsMatch to only filter when a filter is needed.
For example: For find(sub, ANY, ANY), there is no need for a filter in the underlying TripleBunch
diff --git a/jena-arq/src/main/java/org/apache/jena/query/ResultSet.java b/jena-arq/src/main/java/org/apache/jena/query/ResultSet.java
index 1ba28f9..44db088 100644
--- a/jena-arq/src/main/java/org/apache/jena/query/ResultSet.java
+++ b/jena-arq/src/main/java/org/apache/jena/query/ResultSet.java
@@ -20,6 +20,7 @@
import java.util.Iterator ;
import java.util.List ;
+import java.util.function.Consumer;
import org.apache.jena.rdf.model.Model ;
import org.apache.jena.sparql.engine.binding.Binding ;
@@ -53,6 +54,9 @@
@Override
public QuerySolution next() ;
+ @Override
+ public void forEachRemaining(Consumer<? super QuerySolution> action);
+
/** Moves onto the next result (legacy - use .next()). */
public QuerySolution nextSolution() ;
diff --git a/jena-arq/src/main/java/org/apache/jena/riot/protobuf/ProtobufRDF.java b/jena-arq/src/main/java/org/apache/jena/riot/protobuf/ProtobufRDF.java
index d9f8e4c..e4012ca 100644
--- a/jena-arq/src/main/java/org/apache/jena/riot/protobuf/ProtobufRDF.java
+++ b/jena-arq/src/main/java/org/apache/jena/riot/protobuf/ProtobufRDF.java
@@ -29,7 +29,6 @@
import org.apache.jena.riot.system.StreamRDF;
import org.apache.jena.riot.thrift.ThriftRDF;
import org.apache.jena.sparql.core.Var;
-import org.apache.jena.sparql.engine.binding.Binding;
import org.apache.jena.sparql.exec.RowSet;
import org.apache.jena.sparql.exec.RowSetStream;
@@ -155,10 +154,7 @@
try {
List<Var> vars = rowSet.getResultVars();
try ( Binding2Protobuf b2p = new Binding2Protobuf(out, vars, false) ) {
- for ( ; rowSet.hasNext() ; ) {
- Binding b = rowSet.next();
- b2p.output(b);
- }
+ rowSet.forEachRemaining(b2p::output);
}
} finally { IO.flush(out); }
}
diff --git a/jena-arq/src/main/java/org/apache/jena/riot/rowset/rw/RowSetReaderTSV.java b/jena-arq/src/main/java/org/apache/jena/riot/rowset/rw/RowSetReaderTSV.java
index 5722e6e..f25a5cf 100644
--- a/jena-arq/src/main/java/org/apache/jena/riot/rowset/rw/RowSetReaderTSV.java
+++ b/jena-arq/src/main/java/org/apache/jena/riot/rowset/rw/RowSetReaderTSV.java
@@ -24,6 +24,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
+import java.util.function.Consumer;
import java.util.regex.Pattern;
import org.apache.jena.atlas.io.IO;
@@ -199,6 +200,22 @@
return row;
}
+ @Override
+ public void forEachRemaining(Consumer<? super Binding> action) {
+ if ( finished )
+ return;
+ if ( null != currentBinding ) {
+ action.accept(currentBinding);
+ currentBinding = null;
+ }
+ Binding row;
+ while (null != (row = parseNextBinding())) {
+ action.accept(row);
+ }
+ IO.close(reader);
+ finished = true;
+ }
+
private Binding parseNextBinding() {
String line;
try {
diff --git a/jena-arq/src/main/java/org/apache/jena/riot/system/RiotLib.java b/jena-arq/src/main/java/org/apache/jena/riot/system/RiotLib.java
index 77f641c..f1ffae0 100644
--- a/jena-arq/src/main/java/org/apache/jena/riot/system/RiotLib.java
+++ b/jena-arq/src/main/java/org/apache/jena/riot/system/RiotLib.java
@@ -283,10 +283,7 @@
* Collect all the matching triples
*/
public static void accTriples(Collection<Triple> acc, Graph graph, Node s, Node p, Node o) {
- ExtendedIterator<Triple> iter = graph.find(s, p, o);
- while (iter.hasNext())
- acc.add(iter.next());
- iter.close();
+ graph.find(s, p, o).forEach(acc::add);
}
public static void writeBase(IndentedWriter out, String base, boolean newStyle) {
diff --git a/jena-arq/src/main/java/org/apache/jena/riot/system/StreamRDFOps.java b/jena-arq/src/main/java/org/apache/jena/riot/system/StreamRDFOps.java
index 76e5745..9019c1d 100644
--- a/jena-arq/src/main/java/org/apache/jena/riot/system/StreamRDFOps.java
+++ b/jena-arq/src/main/java/org/apache/jena/riot/system/StreamRDFOps.java
@@ -116,11 +116,7 @@
/** Set triples to a StreamRDF - does not call .start/.finish */
public static void sendTriplesToStream(Iterator<Triple> iter, StreamRDF dest)
{
- for ( ; iter.hasNext() ; )
- {
- Triple t = iter.next() ;
- dest.triple(t) ;
- }
+ iter.forEachRemaining(dest::triple);
}
/** Send quads of a dataset (including default graph as quads) to a StreamRDF, without prefixes */
@@ -132,10 +128,6 @@
/** Set quads to a StreamRDF - does not call .start/.finish */
public static void sendQuadsToStream(Iterator<Quad> iter, StreamRDF dest)
{
- for ( ; iter.hasNext() ; )
- {
- Quad q = iter.next() ;
- dest.quad(q) ;
- }
+ iter.forEachRemaining(dest::quad);
}
}
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/engine/JsonIterator.java b/jena-arq/src/main/java/org/apache/jena/sparql/engine/JsonIterator.java
index 898ae76..1b5ee3d 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/engine/JsonIterator.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/engine/JsonIterator.java
@@ -20,6 +20,7 @@
import java.util.Iterator;
import java.util.Map;
+import java.util.function.Consumer;
import org.apache.jena.atlas.json.JsonObject;
import org.apache.jena.graph.Node;
@@ -44,4 +45,9 @@
@Override
public JsonObject next() { return results.next(); }
+
+ @Override
+ public void forEachRemaining(Consumer<? super JsonObject> action) {
+ results.forEachRemaining(action);
+ }
}
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/engine/ResultSetCheckCondition.java b/jena-arq/src/main/java/org/apache/jena/sparql/engine/ResultSetCheckCondition.java
index aa7925c..a279492 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/engine/ResultSetCheckCondition.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/engine/ResultSetCheckCondition.java
@@ -19,6 +19,7 @@
package org.apache.jena.sparql.engine;
import java.util.List ;
+import java.util.function.Consumer;
import org.apache.jena.query.QueryExecution ;
import org.apache.jena.query.QuerySolution ;
@@ -79,6 +80,16 @@
other.close();
}
+ /**
+ * Attention: The check is only done once before the first consumer accept call.
+ * @param action The action to be performed for each element
+ */
+ @Override
+ public void forEachRemaining(Consumer<? super QuerySolution> action) {
+ check() ;
+ other.forEachRemaining(action);
+ }
+
@Override
public int getRowNumber() {
check() ;
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/engine/ResultSetStream.java b/jena-arq/src/main/java/org/apache/jena/sparql/engine/ResultSetStream.java
index f0e5e59..6099dfd 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/engine/ResultSetStream.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/engine/ResultSetStream.java
@@ -21,6 +21,7 @@
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
+import java.util.function.Consumer;
import org.apache.jena.atlas.iterator.Iter;
import org.apache.jena.query.QuerySolution;
@@ -125,6 +126,17 @@
@Override
public QuerySolution next() { return nextSolution(); }
+ @Override
+ public void forEachRemaining(Consumer<? super QuerySolution> action) {
+ if ( queryExecutionIter == null )
+ return;
+ queryExecutionIter.forEachRemaining(binding -> {
+ rowNumber++;
+ action.accept(new ResultBinding(model, binding));
+ });
+ close();
+ }
+
/** Return the "row number" - a count of the number of possibilities returned so far.
* Remains valid (as the total number of possibilities) after the iterator ends.
*/
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/engine/binding/BindingInputStream.java b/jena-arq/src/main/java/org/apache/jena/sparql/engine/binding/BindingInputStream.java
index 422ede0..f4beee1 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/engine/binding/BindingInputStream.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/engine/binding/BindingInputStream.java
@@ -27,6 +27,7 @@
import java.util.Collections ;
import java.util.Iterator ;
import java.util.List ;
+import java.util.function.Consumer;
import org.apache.jena.atlas.iterator.IteratorSlotted ;
import org.apache.jena.atlas.lib.Closeable ;
@@ -107,6 +108,11 @@
}
@Override
+ public void forEachRemaining(Consumer<? super Binding> action) {
+ iter.forEachRemaining(action);
+ }
+
+ @Override
public void remove()
{ iter.remove() ; }
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/exec/JsonResults.java b/jena-arq/src/main/java/org/apache/jena/sparql/exec/JsonResults.java
index 618ac46..3d5e379 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/exec/JsonResults.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/exec/JsonResults.java
@@ -21,6 +21,7 @@
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
+import java.util.function.Consumer;
import org.apache.jena.atlas.json.JsonArray;
import org.apache.jena.atlas.json.JsonObject;
@@ -83,6 +84,15 @@
return jsonObject;
}
+ @Override
+ public void forEachRemaining(Consumer<? super JsonObject> action) {
+ if ( queryIterator == null )
+ return;
+ queryIterator.forEachRemaining(binding
+ -> action.accept( JsonResults.generateJsonObject(binding, template) ));
+ close();
+ }
+
/** Close the query iterator */
private void close() {
queryIterator.close();
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/resultset/ResultSetMem.java b/jena-arq/src/main/java/org/apache/jena/sparql/resultset/ResultSetMem.java
index 9d917be..319adb2 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/resultset/ResultSetMem.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/resultset/ResultSetMem.java
@@ -20,6 +20,7 @@
import java.util.ArrayList ;
import java.util.List ;
+import java.util.function.Consumer;
import org.apache.jena.atlas.iterator.PeekIterator ;
import org.apache.jena.query.QuerySolution ;
@@ -147,6 +148,15 @@
public QuerySolution next() { return nextSolution() ; }
@Override
+ public void forEachRemaining(Consumer<? super QuerySolution> action) {
+ iterator.forEachRemaining(binding -> {
+ rowNumber++;
+ action.accept(new ResultBinding(model, binding));
+ });
+
+ }
+
+ @Override
public void close() {}
/** Reset this result set back to the beginning */
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/resultset/ResultSetPeeking.java b/jena-arq/src/main/java/org/apache/jena/sparql/resultset/ResultSetPeeking.java
index 8aa9327..219ed0c 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/resultset/ResultSetPeeking.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/resultset/ResultSetPeeking.java
@@ -20,6 +20,7 @@
import java.util.List;
import java.util.NoSuchElementException;
+import java.util.function.Consumer;
import org.apache.jena.atlas.lib.Closeable;
import org.apache.jena.query.QuerySolution ;
@@ -76,6 +77,13 @@
}
@Override
+ public void forEachRemaining(Consumer<? super QuerySolution> action) {
+ while (this.hasNext()) {
+ action.accept(this.next());
+ }
+ }
+
+ @Override
public QuerySolution nextSolution() {
return this.next();
}
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/resultset/ResultSetWrapper.java b/jena-arq/src/main/java/org/apache/jena/sparql/resultset/ResultSetWrapper.java
index 157036a..f39ebdb 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/resultset/ResultSetWrapper.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/resultset/ResultSetWrapper.java
@@ -19,6 +19,7 @@
package org.apache.jena.sparql.resultset;
import java.util.List ;
+import java.util.function.Consumer;
import org.apache.jena.query.QuerySolution ;
import org.apache.jena.query.ResultSet ;
@@ -46,6 +47,11 @@
}
@Override
+ public void forEachRemaining(Consumer<? super QuerySolution> action) {
+ get().forEachRemaining(action);
+ }
+
+ @Override
public QuerySolution nextSolution() {
return get().nextSolution();
}
diff --git a/jena-base/src/main/java/org/apache/jena/atlas/iterator/Iter.java b/jena-base/src/main/java/org/apache/jena/atlas/iterator/Iter.java
index b444355..c08868c 100644
--- a/jena-base/src/main/java/org/apache/jena/atlas/iterator/Iter.java
+++ b/jena-base/src/main/java/org/apache/jena/atlas/iterator/Iter.java
@@ -218,10 +218,7 @@
/** See {@link Stream#collect(Supplier, BiConsumer, BiConsumer)}, except without the {@code BiConsumer<R, R> combiner} */
public static <T,R> R collect(Iterator<T> iter, Supplier<R> supplier, BiConsumer<R, ? super T> accumulator) {
R result = supplier.get();
- while(iter.hasNext()) {
- T elt = iter.next();
- accumulator.accept(result, elt);
- }
+ iter.forEachRemaining(elt -> accumulator.accept(result, elt));
return result;
}
@@ -235,10 +232,7 @@
* @see #map(Iterator, Function)
*/
public static <T> void apply(Iterator<? extends T> stream, Consumer<T> action) {
- for (; stream.hasNext();) {
- T item = stream.next();
- action.accept(item);
- }
+ stream.forEachRemaining(action);
}
// ---- Filter
@@ -293,6 +287,22 @@
throw new NoSuchElementException("filter.next");
}
+ @Override
+ public void forEachRemaining(Consumer<? super T> action) {
+ if ( finished )
+ return;
+ if ( slotOccupied ) {
+ action.accept(slot);
+ }
+ T t;
+ while (stream.hasNext()) {
+ t = stream.next();
+ if ( filter.test(t) )
+ action.accept(t);
+ }
+ slotOccupied = false;
+ }
+
private void closeIterator() {
if ( finished )
return;
@@ -418,6 +428,11 @@
}
@Override
+ public void forEachRemaining(Consumer<? super R> action) {
+ stream.forEachRemaining(item->action.accept(converter.apply(item)));
+ }
+
+ @Override
public void close() {
Iter.close(stream);
}
@@ -461,6 +476,14 @@
}
@Override
+ public void forEachRemaining(Consumer<? super T> action) {
+ stream.forEachRemaining(item->{
+ this.action.accept(item);
+ action.accept(item);
+ });
+ }
+
+ @Override
public void close() {
Iter.close(stream);
}
@@ -651,17 +674,14 @@
/** Count the iterator (this is destructive on the iterator) */
public static <T> long count(Iterator<T> iterator) {
- long x = 0;
- while (iterator.hasNext()) {
- iterator.next();
- x++;
- }
- return x;
+ ActionCount<T> action = new ActionCount<>();
+ iterator.forEachRemaining(action);
+ return action.getCount();
}
/** Consume the iterator */
public static <T> void consume(Iterator<T> iterator) {
- count(iterator);
+ iterator.forEachRemaining(x->{}); // Do nothing.
}
/** Create a string from an iterator, using the separator. Note: this consumes the iterator. */
@@ -748,15 +768,12 @@
/** Print an iterator (destructive) */
public static <T> void print(PrintStream out, Iterator<T> stream) {
- apply(stream, out::println);
+ stream.forEachRemaining(out::println);
}
/** Send the elements of the iterator to a sink - consumes the iterator */
public static <T> void sendToSink(Iterator<T> iter, Sink<T> sink) {
- while ( iter.hasNext() ) {
- T thing = iter.next();
- sink.send(thing);
- }
+ iter.forEachRemaining(sink::send);
sink.close();
}
@@ -887,6 +904,11 @@
iterator.forEachRemaining(action);
}
+ @Override
+ public void forEachRemaining(Consumer<? super T> action) {
+ iterator.forEachRemaining(action);
+ }
+
/** Consume the {@code Iter} and produce a {@code Set} */
public Set<T> toSet() {
return toSet(iterator);
@@ -1009,7 +1031,7 @@
/** Apply an action to every element of an iterator */
public void apply(Consumer<T> action) {
- apply(iterator, action);
+ iterator.forEachRemaining(action);
}
/** Join on an {@code Iterator}..
@@ -1077,7 +1099,7 @@
/** Count the iterator (this is destructive on the iterator) */
public long count() {
ActionCount<T> action = new ActionCount<>();
- apply(action);
+ this.forEachRemaining(action);
return action.getCount();
}
diff --git a/jena-base/src/main/java/org/apache/jena/atlas/iterator/IteratorConcat.java b/jena-base/src/main/java/org/apache/jena/atlas/iterator/IteratorConcat.java
index 9f36ec3..8d01eb1 100644
--- a/jena-base/src/main/java/org/apache/jena/atlas/iterator/IteratorConcat.java
+++ b/jena-base/src/main/java/org/apache/jena/atlas/iterator/IteratorConcat.java
@@ -22,6 +22,7 @@
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
+import java.util.function.Consumer;
/**
* Iterator of Iterators IteratorConcat is better when there are lots of iterators to
@@ -91,6 +92,24 @@
}
@Override
+ public void forEachRemaining(Consumer<? super T> action) {
+ if( finished )
+ return;
+ if( current != null ) {
+ current.forEachRemaining(action);
+ Iter.close(current);
+ }
+ idx++;
+ for ( ; idx < iterators.size() ; idx++ ) {
+ current = iterators.get(idx);
+ current.forEachRemaining(action);
+ Iter.close(current);
+ }
+ current = null;
+ finished = true;
+ }
+
+ @Override
public void close() {
//iterators.forEach(Iter::close);
// Earlier iterators already closed
diff --git a/jena-base/src/main/java/org/apache/jena/atlas/iterator/IteratorCons.java b/jena-base/src/main/java/org/apache/jena/atlas/iterator/IteratorCons.java
index ca83a9c..e4d3122 100644
--- a/jena-base/src/main/java/org/apache/jena/atlas/iterator/IteratorCons.java
+++ b/jena-base/src/main/java/org/apache/jena/atlas/iterator/IteratorCons.java
@@ -20,6 +20,7 @@
import java.util.Iterator;
import java.util.NoSuchElementException;
+import java.util.function.Consumer;
import org.apache.jena.atlas.lib.Lib;
@@ -99,6 +100,18 @@
}
@Override
+ public void forEachRemaining(Consumer<? super T> action) {
+ if ( iter1 != null ) {
+ iter1.forEachRemaining(action);
+ iter1 = null;
+ }
+ if ( iter2 != null ) {
+ iter2.forEachRemaining(action);
+ iter2 = null;
+ }
+ }
+
+ @Override
public void remove() {
if ( null == removeFrom )
throw new IllegalStateException("no calls to next() since last call to remove()");
diff --git a/jena-base/src/main/java/org/apache/jena/atlas/iterator/IteratorDelayedInitialization.java b/jena-base/src/main/java/org/apache/jena/atlas/iterator/IteratorDelayedInitialization.java
index 2d7f8e5..63f7feb 100644
--- a/jena-base/src/main/java/org/apache/jena/atlas/iterator/IteratorDelayedInitialization.java
+++ b/jena-base/src/main/java/org/apache/jena/atlas/iterator/IteratorDelayedInitialization.java
@@ -20,6 +20,7 @@
import java.util.Iterator ;
import java.util.NoSuchElementException ;
+import java.util.function.Consumer;
/** Class to delay the initialization of an iterator until first call of an Iterator operation. */
@@ -61,6 +62,13 @@
}
@Override
+ public void forEachRemaining(Consumer<? super T> action) {
+ init() ;
+ iterator.forEachRemaining(action);
+ close();
+ }
+
+ @Override
public void remove()
{
init() ;
diff --git a/jena-base/src/main/java/org/apache/jena/atlas/iterator/IteratorFlatMap.java b/jena-base/src/main/java/org/apache/jena/atlas/iterator/IteratorFlatMap.java
index 84c8291..9d5b9dd 100644
--- a/jena-base/src/main/java/org/apache/jena/atlas/iterator/IteratorFlatMap.java
+++ b/jena-base/src/main/java/org/apache/jena/atlas/iterator/IteratorFlatMap.java
@@ -20,6 +20,7 @@
import java.util.Iterator;
import java.util.NoSuchElementException;
+import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Stream;
@@ -78,6 +79,25 @@
}
@Override
+ public void forEachRemaining(Consumer<? super OUT> action) {
+ if ( finished )
+ return;
+ if ( current != null ) {
+ current.forEachRemaining(action);
+ Iter.close(current);
+ current = null;
+ }
+ input.forEachRemaining(x->{
+ current = mapper.apply(x);
+ if ( current == null )
+ return;
+ current.forEachRemaining(action);
+ Iter.close(current);
+ });
+ current = null;
+ }
+
+ @Override
public void close() {
if ( current != null )
Iter.close(current);
diff --git a/jena-base/src/main/java/org/apache/jena/atlas/iterator/IteratorInteger.java b/jena-base/src/main/java/org/apache/jena/atlas/iterator/IteratorInteger.java
index 896c6f7..921f822 100644
--- a/jena-base/src/main/java/org/apache/jena/atlas/iterator/IteratorInteger.java
+++ b/jena-base/src/main/java/org/apache/jena/atlas/iterator/IteratorInteger.java
@@ -19,6 +19,7 @@
package org.apache.jena.atlas.iterator;
import java.util.Iterator ;
+import java.util.function.Consumer;
public class IteratorInteger implements Iterator<Long>
{
@@ -49,8 +50,14 @@
@Override
public Long next()
{
- Long v = current;
- current++ ;
- return v ;
+ return current++;
+ }
+
+ @Override
+ public void forEachRemaining(Consumer<? super Long> action) {
+ while (current < finish) {
+ action.accept(current);
+ current++;
+ }
}
}
diff --git a/jena-base/src/main/java/org/apache/jena/atlas/iterator/IteratorOnClose.java b/jena-base/src/main/java/org/apache/jena/atlas/iterator/IteratorOnClose.java
index d89b433..668d1ba 100644
--- a/jena-base/src/main/java/org/apache/jena/atlas/iterator/IteratorOnClose.java
+++ b/jena-base/src/main/java/org/apache/jena/atlas/iterator/IteratorOnClose.java
@@ -20,6 +20,7 @@
import java.util.Iterator;
import java.util.NoSuchElementException;
+import java.util.function.Consumer;
/**
* Add an "onClose" action to an Iterator.
@@ -58,6 +59,12 @@
}
@Override
+ public void forEachRemaining(Consumer<? super T> action) {
+ super.forEachRemaining(action);
+ close();
+ }
+
+ @Override
public void close() {
if ( ! hasClosed ) {
try {
diff --git a/jena-base/src/main/java/org/apache/jena/atlas/iterator/IteratorWrapper.java b/jena-base/src/main/java/org/apache/jena/atlas/iterator/IteratorWrapper.java
index e26962d..c3a45e5 100644
--- a/jena-base/src/main/java/org/apache/jena/atlas/iterator/IteratorWrapper.java
+++ b/jena-base/src/main/java/org/apache/jena/atlas/iterator/IteratorWrapper.java
@@ -19,6 +19,7 @@
package org.apache.jena.atlas.iterator;
import java.util.Iterator;
+import java.util.function.Consumer;
public class IteratorWrapper<T> implements IteratorCloseable<T> {
protected final Iterator<T> iterator;
@@ -47,6 +48,11 @@
}
@Override
+ public void forEachRemaining(Consumer<? super T> action) {
+ get().forEachRemaining(action);
+ }
+
+ @Override
public void close() {
Iter.close(iterator);
}
diff --git a/jena-base/src/main/java/org/apache/jena/atlas/iterator/PushbackIterator.java b/jena-base/src/main/java/org/apache/jena/atlas/iterator/PushbackIterator.java
index 3fa0f9b..2bbd6a2 100644
--- a/jena-base/src/main/java/org/apache/jena/atlas/iterator/PushbackIterator.java
+++ b/jena-base/src/main/java/org/apache/jena/atlas/iterator/PushbackIterator.java
@@ -21,6 +21,7 @@
import java.util.ArrayDeque ;
import java.util.Deque ;
import java.util.Iterator ;
+import java.util.function.Consumer;
/**
* An iterator where you can push items back into the iterator, to be yielded (LIFO) next time.
@@ -53,4 +54,12 @@
return items.pop() ;
return iter.next() ;
}
+
+ @Override
+ public void forEachRemaining(Consumer<? super T> action) {
+ while(!items.isEmpty()) {
+ action.accept(items.pop());
+ }
+ iter.forEachRemaining(action);
+ }
}
diff --git a/jena-base/src/test/java/org/apache/jena/atlas/iterator/TestIter.java b/jena-base/src/test/java/org/apache/jena/atlas/iterator/TestIter.java
index f5c4ee9..fe4c3b4 100644
--- a/jena-base/src/test/java/org/apache/jena/atlas/iterator/TestIter.java
+++ b/jena-base/src/test/java/org/apache/jena/atlas/iterator/TestIter.java
@@ -128,6 +128,15 @@
assertFalse(iter.hasNext());
}
+ private static void testWithForeachRemaining(Iterator<? > iter, Object...items) {
+ Integer i[] = {0};
+ iter.forEachRemaining(x -> {
+ assertTrue(i[0] < items.length);
+ assertEquals(items[i[0]], x);
+ i[0]++;
+ });
+ }
+
static Iter.Folder<String, String> f1 = (acc, arg)->acc + arg ;
@Test
@@ -159,12 +168,64 @@
}
@Test
+ public void operate_01() {
+ var elements = new ArrayList<>(Arrays.asList("x", "y", "z"));
+ Iterator<String> it = Iter.operate(data2.iterator(), item -> elements.remove(item));
+ test(it, elements.toArray());
+ assertEquals(0, elements.size());
+ }
+
+ @Test
+ public void operate_02() {
+ var elements = new ArrayList<>(Arrays.asList("x", "y", "z"));
+ Iterator<String> it = Iter.operate(data2.iterator(), item -> elements.remove(item));
+ testWithForeachRemaining(it, elements.toArray());
+ assertEquals(0, elements.size());
+ }
+
+ @Test
+ public void limit_01() {
+ Iterator<String> it = Iter.limit(data2.iterator(), 0);
+ assertFalse(it.hasNext());
+ }
+
+ @Test
+ public void limit_02() {
+ Iterator<String> it = Iter.limit(data2.iterator(), 1);
+ test(it, "x");
+ }
+
+ @Test
+ public void limit_03() {
+ Iterator<String> it = Iter.limit(data2.iterator(), 2);
+ test(it, "x", "y");
+ }
+
+ @Test
+ public void limit_04() {
+ Iterator<String> it = Iter.limit(data2.iterator(), 3);
+ test(it, "x", "y", "z");
+ }
+
+ @Test
+ public void limit_05() {
+ Iterator<String> it = Iter.limit(data2.iterator(), 4);
+ test(it, "x", "y", "z");
+ }
+
+ @Test
public void map_01() {
Iterator<String> it = Iter.map(data2.iterator(), item -> item + item);
test(it, "xx", "yy", "zz");
}
@Test
+ public void map_02() {
+ Iterator<String> it = Iter.map(data2.iterator(), item -> item + item);
+ testWithForeachRemaining(it, "xx", "yy", "zz");
+ }
+
+ @Test
public void flatmap_01() {
Iterator<String> it = Iter.flatMap(data2.iterator(), item -> Arrays.asList(item+item, item).iterator());
test(it, "xx", "x", "yy", "y", "zz", "z");
@@ -179,7 +240,6 @@
});
test(it, 1, 9);
}
-
@Test
public void flatmap_03() {
List<Integer> data = Arrays.asList(1,2,3);
@@ -195,6 +255,37 @@
Iter<String> it = Iter.iter(data.iterator()).flatMap(mapper);
test(it, "two");
}
+ @Test
+ public void flatmap_04() {
+ Iterator<String> it = Iter.flatMap(data2.iterator(), item -> Arrays.asList(item+item, item).iterator());
+ testWithForeachRemaining(it, "xx", "x", "yy", "y", "zz", "z");
+ }
+
+ @Test
+ public void flatmap_05() {
+ List<Integer> data = Arrays.asList(1,2,3);
+ Iterator<Integer> it = Iter.flatMap(data.iterator(), x -> {
+ if ( x == 2 ) return Iter.nullIterator();
+ return Arrays.asList(x*x).iterator();
+ });
+ testWithForeachRemaining(it, 1, 9);
+ }
+
+ @Test
+ public void flatmap_06() {
+ List<Integer> data = Arrays.asList(1,2,3);
+ Function<Integer, Iterator<String>> mapper = x -> {
+ switch(x) {
+ case 1: return Iter.nullIterator();
+ case 2: return Arrays.asList("two").iterator();
+ case 3: return Iter.nullIterator();
+ default: throw new IllegalArgumentException();
+ }
+ };
+
+ Iter<String> it = Iter.iter(data.iterator()).flatMap(mapper);
+ testWithForeachRemaining(it, "two");
+ }
private Predicate<String> filter = item -> item.length() == 1;
@@ -515,6 +606,18 @@
}
@Test
+ public void filter_04() {
+ Iterator<String> it = Iter.filter(data3.iterator(), item -> "x".equals(item) || "z".equals(item));
+ testWithForeachRemaining(it, "x", "z");
+ }
+
+ @Test
+ public void filter_05() {
+ Iterator<String> it = Iter.filter(data3.iterator(), item -> null == item || "x".equals(item));
+ testWithForeachRemaining(it, null, "x", null, null, null, null);
+ }
+
+ @Test
public void distinct_01() {
List<String> x = Arrays.asList("a", "b", "a");
Iterator<String> iter = Iter.distinct(x.iterator());
diff --git a/jena-core/src/main/java/org/apache/jena/graph/Triple.java b/jena-core/src/main/java/org/apache/jena/graph/Triple.java
index 3f9a1d5..93d7f5d 100644
--- a/jena-core/src/main/java/org/apache/jena/graph/Triple.java
+++ b/jena-core/src/main/java/org/apache/jena/graph/Triple.java
@@ -197,6 +197,8 @@
public abstract Predicate<Triple> filterOn( Node n );
+ public abstract Predicate<Triple> filterOnConcrete( Node n );
+
public final Predicate<Triple> filterOn( Triple t )
{ return filterOn( getField( t ) ); }
@@ -214,8 +216,11 @@
: anyTriple
;
}
+ @Override public Predicate<Triple> filterOnConcrete( final Node n )
+ { return x -> n.equals( x.subj ); }
};
+
public static final Field fieldObject = new Field()
{
@Override public Node getField( Triple t )
@@ -226,6 +231,9 @@
? x -> n.sameValueAs( x.obj )
: anyTriple;
}
+
+ @Override public Predicate<Triple> filterOnConcrete( final Node n )
+ { return x -> n.sameValueAs( x.obj ); }
};
public static final Field fieldPredicate = new Field()
@@ -238,6 +246,9 @@
? x -> n.equals( x.pred )
: anyTriple;
}
+
+ @Override public Predicate<Triple> filterOnConcrete( final Node n )
+ { return x -> n.equals( x.pred ); }
};
}
}
diff --git a/jena-core/src/main/java/org/apache/jena/graph/impl/GraphBase.java b/jena-core/src/main/java/org/apache/jena/graph/impl/GraphBase.java
index dbbd1ec..ae21977 100644
--- a/jena-core/src/main/java/org/apache/jena/graph/impl/GraphBase.java
+++ b/jena-core/src/main/java/org/apache/jena/graph/impl/GraphBase.java
@@ -18,6 +18,7 @@
package org.apache.jena.graph.impl;
+import org.apache.jena.atlas.iterator.Iter;
import org.apache.jena.graph.* ;
import org.apache.jena.shared.AddDeniedException ;
import org.apache.jena.shared.ClosedException ;
@@ -307,9 +308,7 @@
ExtendedIterator<Triple> it = GraphUtil.findAll( this );
try
{
- int tripleCount = 0;
- while (it.hasNext()) { it.next(); tripleCount += 1; }
- return tripleCount;
+ return (int) Iter.count(it);
}
finally
{ it.close(); }
diff --git a/jena-core/src/main/java/org/apache/jena/graph/impl/TripleStore.java b/jena-core/src/main/java/org/apache/jena/graph/impl/TripleStore.java
index e9b044d..cfda319 100644
--- a/jena-core/src/main/java/org/apache/jena/graph/impl/TripleStore.java
+++ b/jena-core/src/main/java/org/apache/jena/graph/impl/TripleStore.java
@@ -21,6 +21,8 @@
import org.apache.jena.graph.* ;
import org.apache.jena.util.iterator.ExtendedIterator ;
+import java.util.stream.Stream;
+
/**
TripleStore - interface for bulk storage of triples used in composed graphs.
*/
@@ -57,6 +59,11 @@
public abstract boolean contains( Triple t );
/**
+ Answer true iff this triple store contains the triple match <code>t</code>.
+ */
+ public abstract boolean containsMatch( Triple t );
+
+ /**
Answer an setwise iterator over all the subjects of triples in this store.
*/
public ExtendedIterator<Node> listSubjects();
@@ -78,6 +85,12 @@
public abstract ExtendedIterator<Triple> find( Triple t );
/**
+ Answer an ExtendedIterator returning all the triples from this store that
+ match the pattern <code>m = (S, P, O)</code>.
+ */
+ public abstract Stream<Triple> stream(Node sm, Node pm, Node om);
+
+ /**
Clear this store, ie remove all triples from it.
*/
public abstract void clear();
diff --git a/jena-core/src/main/java/org/apache/jena/mem/ArrayBunch.java b/jena-core/src/main/java/org/apache/jena/mem/ArrayBunch.java
index 8baf555..6349cf7 100644
--- a/jena-core/src/main/java/org/apache/jena/mem/ArrayBunch.java
+++ b/jena-core/src/main/java/org/apache/jena/mem/ArrayBunch.java
@@ -19,6 +19,8 @@
package org.apache.jena.mem;
import java.util.ConcurrentModificationException ;
+import java.util.Spliterator;
+import java.util.function.Consumer;
import org.apache.jena.graph.Triple ;
import org.apache.jena.util.iterator.ExtendedIterator ;
@@ -65,7 +67,7 @@
{
if (size == elements.length) grow();
elements[size++] = t;
- changes += 1;
+ changes++;
}
/**
@@ -84,7 +86,7 @@
@Override
public void remove( Triple t )
{
- changes += 1;
+ changes++;
for (int i = 0; i < size; i += 1)
{
if (t.equals( elements[i] ))
@@ -108,29 +110,84 @@
protected final int initialChanges = changes;
protected int i = size;
- protected final Triple [] e = elements;
-
+
@Override public boolean hasNext()
{
- if (changes > initialChanges) throw new ConcurrentModificationException();
- return i > 0;
+ return 0 < i;
}
@Override public Triple next()
{
- if (changes > initialChanges) throw new ConcurrentModificationException();
+ if (changes != initialChanges) throw new ConcurrentModificationException();
if (i == 0) noElements( "no elements left in ArrayBunch iteration" );
- return e[--i];
+ return elements[--i];
}
-
+
+ @Override
+ public void forEachRemaining(Consumer<? super Triple> action)
+ {
+ while(0 < i--) action.accept(elements[i]);
+ if (changes != initialChanges) throw new ConcurrentModificationException();
+ }
+
@Override public void remove()
{
- if (changes > initialChanges) throw new ConcurrentModificationException();
+ if (changes != initialChanges) throw new ConcurrentModificationException();
int last = --size;
- e[i] = e[last];
- e[last] = null;
+ elements[i] = elements[last];
+ elements[last] = null;
if (size == 0) container.emptied();
}
};
}
+
+ @Override
+ public Spliterator<Triple> spliterator() {
+
+ return new Spliterator<Triple>() {
+
+ protected final int initialChanges = changes;
+
+ int i = size;
+
+ @Override
+ public boolean tryAdvance(Consumer<? super Triple> action)
+ {
+ if(0 < i)
+ {
+ action.accept(elements[--i]);
+ if (changes != initialChanges) throw new ConcurrentModificationException();
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void forEachRemaining(Consumer<? super Triple> action) {
+ while(0 < i--) action.accept(elements[i]);
+ if (changes != initialChanges) throw new ConcurrentModificationException();
+ }
+
+ @Override
+ public Spliterator<Triple> trySplit() {
+ /* the number of elements here should always be small, so splitting is not wise */
+ return null;
+ }
+
+ @Override
+ public long estimateSize() {
+ return i;
+ }
+
+ @Override
+ public long getExactSizeIfKnown() {
+ return i;
+ }
+
+ @Override
+ public int characteristics() {
+ return DISTINCT | SIZED | NONNULL | IMMUTABLE;
+ }
+ };
+ }
}
diff --git a/jena-core/src/main/java/org/apache/jena/mem/BunchMap.java b/jena-core/src/main/java/org/apache/jena/mem/BunchMap.java
index 4ca9058..79f9708 100644
--- a/jena-core/src/main/java/org/apache/jena/mem/BunchMap.java
+++ b/jena-core/src/main/java/org/apache/jena/mem/BunchMap.java
@@ -18,6 +18,8 @@
package org.apache.jena.mem;
+import java.util.Iterator;
+import java.util.Spliterator;
import java.util.function.Function ;
import org.apache.jena.util.iterator.ExtendedIterator ;
@@ -68,4 +70,8 @@
Answer an iterator over all the keys in this map.
*/
public ExtendedIterator<Object> keyIterator();
+
+ public Iterator<TripleBunch> iterator();
+
+ public Spliterator<TripleBunch> spliterator();
}
diff --git a/jena-core/src/main/java/org/apache/jena/mem/FieldFilter.java b/jena-core/src/main/java/org/apache/jena/mem/FieldFilter.java
new file mode 100644
index 0000000..c9d2ce7
--- /dev/null
+++ b/jena-core/src/main/java/org/apache/jena/mem/FieldFilter.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jena.mem;
+
+import org.apache.jena.graph.Node;
+import org.apache.jena.graph.Triple;
+
+import java.util.function.Predicate;
+
+/**
+ * A class that encapsulates a filter on fields on a triple.
+ * <p>
+ * The filter is a predicate that takes a triple and returns true if it passes
+ * the filter and false otherwise.
+ * </p>
+ */
+public class FieldFilter {
+
+ public static final FieldFilter EMPTY = new FieldFilter();
+
+ private final Predicate<Triple> filter;
+
+ private final boolean hasFilter;
+
+ private FieldFilter(Predicate<Triple> filter) {
+ this.filter = filter;
+ this.hasFilter = true;
+ }
+
+ private FieldFilter() {
+ this.filter = null;
+ this.hasFilter = false;
+ }
+
+ public boolean hasFilter() {
+ return hasFilter;
+ }
+
+ public Predicate<Triple> getFilter() {
+ return filter;
+ }
+
+ public static FieldFilter filterOn(Triple.Field f1, Node n1, Triple.Field f2, Node n2) {
+ if(n1.isConcrete()) {
+ if(n2.isConcrete()) {
+ return new FieldFilter(f1.filterOnConcrete(n1).and(f2.filterOnConcrete(n2)));
+ }
+ return new FieldFilter(f1.filterOnConcrete(n1));
+ } else if (n2.isConcrete()) {
+ return new FieldFilter(f2.filterOnConcrete(n2));
+ }
+ return FieldFilter.EMPTY;
+ }
+}
diff --git a/jena-core/src/main/java/org/apache/jena/mem/GraphMem.java b/jena-core/src/main/java/org/apache/jena/mem/GraphMem.java
index 3b4ddf2..78fc000 100644
--- a/jena-core/src/main/java/org/apache/jena/mem/GraphMem.java
+++ b/jena-core/src/main/java/org/apache/jena/mem/GraphMem.java
@@ -22,6 +22,8 @@
import org.apache.jena.graph.impl.TripleStore ;
import org.apache.jena.util.iterator.ExtendedIterator ;
+import java.util.stream.Stream;
+
/** @deprecated This implementation of GraphMem will be replaced by a new implementation at Jena 4.6.0.
* Application should be using {@link Factory#createDefaultGraph()} for a general purpose graph or {@link Factory#createGraphMem()}
* to specific this style of implementation.
@@ -60,7 +62,7 @@
Otherwise we use the default implementation.
*/
@Override public boolean graphBaseContains( Triple t )
- { return t.isConcrete() ? store.contains( t ) : super.graphBaseContains( t ); }
+ { return t.isConcrete() ? store.contains( t ) : store.containsMatch( t ); }
/**
Clear this GraphMem, ie remove all its triples (delegated to the store).
@@ -71,6 +73,11 @@
getEventManager().notifyEvent(this, GraphEvents.removeAll ) ;
}
+ @Override
+ public Stream<Triple> stream(Node s, Node p, Node o) {
+ return store.stream(s, p, o);
+ }
+
/**
Clear this GraphMem, ie remove all its triples (delegated to the store).
*/
diff --git a/jena-core/src/main/java/org/apache/jena/mem/GraphMemBase.java b/jena-core/src/main/java/org/apache/jena/mem/GraphMemBase.java
index a096d57..39ffd1d 100644
--- a/jena-core/src/main/java/org/apache/jena/mem/GraphMemBase.java
+++ b/jena-core/src/main/java/org/apache/jena/mem/GraphMemBase.java
@@ -61,7 +61,7 @@
*/
public GraphMemBase openAgain()
{
- count += 1;
+ count++;
return this;
}
diff --git a/jena-core/src/main/java/org/apache/jena/mem/GraphTripleStore.java b/jena-core/src/main/java/org/apache/jena/mem/GraphTripleStore.java
deleted file mode 100644
index 5a915a6..0000000
--- a/jena-core/src/main/java/org/apache/jena/mem/GraphTripleStore.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.jena.mem;
-
-import org.apache.jena.graph.* ;
-import org.apache.jena.graph.Triple.* ;
-import org.apache.jena.graph.impl.TripleStore ;
-
-/**
- GraphTripleStore - the underlying triple-indexed triple store for GraphMem et al,
- ripped out from the heart of GraphMem as part of simplifying the reification code.
- A GraphTripleStore is a searchable repository for triples.
-
-*/
-
-public class GraphTripleStore extends GraphTripleStoreBase implements TripleStore
- {
- public GraphTripleStore( Graph parent )
- {
- super( parent,
- new NodeToTriplesMap( Field.fieldSubject, Field.fieldPredicate, Field.fieldObject ),
- new NodeToTriplesMap( Field.fieldPredicate, Field.fieldObject, Field.fieldSubject ),
- new NodeToTriplesMap( Field.fieldObject, Field.fieldSubject, Field.fieldPredicate )
- );
- }
- }
diff --git a/jena-core/src/main/java/org/apache/jena/mem/GraphTripleStoreBase.java b/jena-core/src/main/java/org/apache/jena/mem/GraphTripleStoreBase.java
index 80b46a8..d7570a8 100644
--- a/jena-core/src/main/java/org/apache/jena/mem/GraphTripleStoreBase.java
+++ b/jena-core/src/main/java/org/apache/jena/mem/GraphTripleStoreBase.java
@@ -19,6 +19,7 @@
package org.apache.jena.mem;
import java.util.Iterator;
+import java.util.stream.Stream;
import org.apache.jena.graph.Graph ;
import org.apache.jena.graph.Node ;
@@ -129,10 +130,23 @@
@Override
public boolean contains( Triple t )
{ return subjects.containsBySameValueAs( t ); }
-
- public boolean containsByEquality( Triple t )
- { return subjects.contains( t ); }
-
+
+ @Override
+ public boolean containsMatch(Triple t)
+ {
+ Node pm = t.getPredicate();
+ Node om = t.getObject();
+ Node sm = t.getSubject();
+ if (sm.isConcrete())
+ return subjects.containsMatch( sm, pm, om );
+ else if (om.isConcrete())
+ return objects.containsMatch( om, sm, pm );
+ else if (pm.isConcrete())
+ return predicates.containsMatch( pm, om, sm );
+ else
+ return !this.isEmpty();
+ }
+
/**
Answer an ExtendedIterator returning all the triples from this store that
match the pattern <code>m = (S, P, O)</code>.
@@ -166,4 +180,40 @@
else
return new StoreTripleIterator( parent, subjects.iterateAll(), subjects, predicates, objects );
}
+
+ /**
+ Answer a Stream returning all the triples from this store that
+ match the pattern <code>m = (S, P, O)</code>.
+
+ <p>Because the node-to-triples maps index on each of subject, predicate,
+ and (non-literal) object, concrete S/P/O patterns can immediately select
+ an appropriate map. Because the match for literals must be by sameValueAs,
+ not equality, the optimisation is not applied for literals. [This is probably a
+ Bad Thing for strings.]
+
+ <p>Practice suggests doing the predicate test <i>last</i>, because there are
+ "usually" many more statements than predicates, so the predicate doesn't
+ cut down the search space very much. By "practice suggests" I mean that
+ when the order went, accidentally, from S/O/P to S/P/O, performance on
+ (ANY, P, O) searches on largish models with few predicates declined
+ dramatically - specifically on the not-galen.owl ontology.
+ */
+ @Override
+ public Stream<Triple> stream(Node sm, Node pm, Node om)
+ {
+ if (null == sm) sm = Node.ANY;
+ if (null == pm) pm = Node.ANY;
+ if (null == om) om = Node.ANY;
+
+ if (sm.isConcrete())
+ return subjects.stream( sm, pm, om );
+ else if (om.isConcrete())
+ return objects.stream( om, sm, pm );
+ else if (pm.isConcrete())
+ return predicates.stream( pm, om, sm );
+ else
+ return subjects.streamAll();
+ }
}
+
+
diff --git a/jena-core/src/main/java/org/apache/jena/mem/HashCommon.java b/jena-core/src/main/java/org/apache/jena/mem/HashCommon.java
index 2c818f0..6983c23 100644
--- a/jena-core/src/main/java/org/apache/jena/mem/HashCommon.java
+++ b/jena-core/src/main/java/org/apache/jena/mem/HashCommon.java
@@ -19,6 +19,7 @@
package org.apache.jena.mem;
import java.util.*;
+import java.util.function.Consumer;
import org.apache.jena.shared.BrokenException ;
import org.apache.jena.shared.JenaException ;
@@ -178,7 +179,7 @@
public void remove( Key key )
{ primitiveRemove( key ); }
- private void primitiveRemove( Key key )
+ protected void primitiveRemove( Key key )
{
int slot = findSlot( key );
if (slot < 0) removeFrom( ~slot );
@@ -235,7 +236,7 @@
about the overhead of the linear probing.
<p>
Iterators running over the keys may miss elements that are moved from the
- top of the table to the bottom because of Iterator::remove. removeFrom
+ bottom of the table to the top because of Iterator::remove. removeFrom
returns such a moved key as its result, and null otherwise.
*/
protected Key removeFrom( int here )
@@ -258,9 +259,8 @@
{ /* Nothing. We'd have preferred an `unless` statement. */}
else
{
- if (here <= original && scan > original) {
- wrappedAround = keys[scan];
- }
+ if (here >= original && scan < original)
+ { wrappedAround = keys[scan]; }
keys[here] = keys[scan];
moveAssociatedValues( here, scan );
here = scan;
@@ -318,15 +318,20 @@
@Override public boolean hasNext()
{
- if (changes > initialChanges) throw new ConcurrentModificationException( "changes " + changes + " > initialChanges " + initialChanges );
- return index < movedKeys.size();
+ return index < movedKeys.size();
}
@Override public Key next()
{
+ if (changes > initialChanges) throw new ConcurrentModificationException( "changes " + changes + " > initialChanges " + initialChanges );
+ if (index < movedKeys.size()) return movedKeys.get( index++ );
+ return noElements( "" );
+ }
+
+ @Override public void forEachRemaining(Consumer<? super Key> action)
+ {
+ while(index < movedKeys.size()) action.accept( movedKeys.get( index++ ) );
if (changes > initialChanges) throw new ConcurrentModificationException();
- if (hasNext() == false) noElements( "" );
- return movedKeys.get( index++ );
}
@Override public void remove()
@@ -347,7 +352,7 @@
{
protected final List<Key> movedKeys;
- int index = 0;
+ int pos = capacity-1;
final int initialChanges;
final NotifyEmpty container;
@@ -360,16 +365,30 @@
@Override public boolean hasNext()
{
- if (changes > initialChanges) throw new ConcurrentModificationException();
- while (index < capacity && keys[index] == null) index += 1;
- return index < capacity;
+ while(-1 < pos)
+ {
+ if(null != keys[pos])
+ return true;
+ pos--;
+ }
+ return false;
}
@Override public Key next()
{
if (changes > initialChanges) throw new ConcurrentModificationException();
- if (hasNext() == false) noElements( "HashCommon keys" );
- return keys[index++];
+ if (-1 < pos && null != keys[pos]) return keys[pos--];
+ throw new NoSuchElementException("HashCommon keys");
+ }
+
+ @Override public void forEachRemaining(Consumer<? super Key> action)
+ {
+ while(-1 < pos)
+ {
+ if(null != keys[pos]) action.accept(keys[pos]);
+ pos--;
+ }
+ if (changes > initialChanges) throw new ConcurrentModificationException();
}
@Override public void remove()
@@ -377,11 +396,21 @@
if (changes > initialChanges) throw new ConcurrentModificationException();
// System.err.println( ">> keyIterator::remove, size := " + size +
// ", removing " + keys[index + 1] );
- Key moved = removeFrom( index - 1 );
+ Key moved = removeFrom( pos + 1 );
if (moved != null) movedKeys.add( moved );
if (size == 0) container.emptied();
if (size < 0) throw new BrokenException( "BROKEN" );
showkeys();
}
}
+
+ public Spliterator<Key> keySpliterator()
+ {
+ final var initialChanges = changes;
+ final Runnable checkForConcurrentModification = () ->
+ {
+ if (changes != initialChanges) throw new ConcurrentModificationException();
+ };
+ return new SparseArraySpliterator<>(keys, size, checkForConcurrentModification);
+ }
}
diff --git a/jena-core/src/main/java/org/apache/jena/mem/HashedBunchMap.java b/jena-core/src/main/java/org/apache/jena/mem/HashedBunchMap.java
index 02fb56e..2e34b1c 100644
--- a/jena-core/src/main/java/org/apache/jena/mem/HashedBunchMap.java
+++ b/jena-core/src/main/java/org/apache/jena/mem/HashedBunchMap.java
@@ -18,9 +18,13 @@
package org.apache.jena.mem;
+import java.util.*;
+import java.util.function.Consumer;
import java.util.function.Function ;
import org.apache.jena.shared.BrokenException ;
+import org.apache.jena.util.iterator.ExtendedIterator;
+import org.apache.jena.util.iterator.NiceIterator;
/**
An implementation of BunchMap that does open-addressed hashing.
@@ -28,7 +32,7 @@
public class HashedBunchMap extends HashCommon<Object> implements BunchMap
{
protected TripleBunch [] values;
-
+
public HashedBunchMap()
{
super( 10 );
@@ -37,22 +41,22 @@
@Override protected Object[] newKeyArray( int size )
{ return new Object[size]; }
-
+
/**
- Clear this map: all entries are removed. The keys <i>and value</i> array
+ Clear this map: all entries are removed. The keys <i>and value</i> array
elements are set to null (so the values may be garbage-collected).
*/
@Override
public void clear()
{
size = 0;
- for (int i = 0; i < capacity; i += 1) keys[i] = values[i] = null;
- }
-
+ for (int i = 0; i < capacity; i += 1) keys[i] = values[i] = null;
+ }
+
@Override
public long size()
{ return size; }
-
+
@Override
public TripleBunch get( Object key )
{
@@ -81,7 +85,7 @@
put$(slot, key, value) ;
return value ;
}
-
+
private void put$(int slot, Object key, TripleBunch value) {
keys[slot] = key;
values[slot] = value;
@@ -89,7 +93,7 @@
if ( size == threshold )
grow();
}
-
+
protected void grow()
{
Object [] oldContents = keys;
@@ -101,10 +105,10 @@
for (int i = 0; i < oldCapacity; i += 1)
{
Object key = oldContents[i];
- if (key != null)
+ if (key != null)
{
int j = findSlot( key );
- if (j < 0)
+ if (j < 0)
{
throw new BrokenException( "oh dear, already have a slot for " + key + ", viz " + ~j );
}
@@ -120,11 +124,133 @@
*/
@Override protected void removeAssociatedValues( int here )
{ values[here] = null; }
-
+
/**
Called by HashCommon when a key is moved: move the
associated element of the <code>values</code> array.
*/
@Override protected void moveAssociatedValues( int here, int scan )
{ values[here] = values[scan]; }
+
+ @Override public Iterator<TripleBunch> iterator()
+ {
+ final List<Object> movedKeys = new ArrayList<>();
+ ExtendedIterator<TripleBunch> basic = new BasicValueIterator( changes, movedKeys );
+ ExtendedIterator<TripleBunch> leftovers = new MovedValuesIterator( changes, movedKeys );
+ return basic.andThen( leftovers );
+ }
+
+ /**
+ The MovedKeysIterator iterates over the elements of the <code>keys</code>
+ list. It's not sufficient to just use List::iterator, because the .remove
+ method must remove elements from the hash table itself.
+ <p>
+ Note that the list supplied on construction will be empty: it is filled before
+ the first call to <code>hasNext()</code>.
+ */
+ protected final class MovedValuesIterator extends NiceIterator<TripleBunch>
+ {
+ private final List<Object> movedKeys;
+
+ protected int index = 0;
+ final int initialChanges;
+
+ protected MovedValuesIterator(int initialChanges, List<Object> movedKeys)
+ {
+ this.movedKeys = movedKeys;
+ this.initialChanges = initialChanges;
+ }
+
+ @Override public boolean hasNext()
+ {
+ return index < movedKeys.size();
+ }
+
+ @Override public TripleBunch next()
+ {
+ if (changes > initialChanges) throw new ConcurrentModificationException( "changes " + changes + " > initialChanges " + initialChanges );
+ if (index < movedKeys.size()) return get(movedKeys.get( index++ ));
+ return noElements( "" );
+ }
+
+ @Override public void forEachRemaining(Consumer<? super TripleBunch> action)
+ {
+ while(index < movedKeys.size()) action.accept( get(movedKeys.get( index++ )) );
+ if (changes > initialChanges) throw new ConcurrentModificationException();
+ }
+
+ @Override public void remove()
+ {
+ if (changes > initialChanges) throw new ConcurrentModificationException();
+ primitiveRemove( movedKeys.get( index - 1 ) );
+ }
+ }
+
+ /**
+ The BasicKeyIterator iterates over the <code>keys</code> array.
+ If a .remove call moves an unprocessed key underneath the iterator's
+ index, that key value is added to the <code>movedKeys</code>
+ list supplied to the constructor.
+ */
+ protected final class BasicValueIterator extends NiceIterator<TripleBunch>
+ {
+ protected final List<Object> movedKeys;
+
+ int pos = capacity-1;
+ final int initialChanges;
+
+ protected BasicValueIterator(int initialChanges, List<Object> movedKeys)
+ {
+ this.movedKeys = movedKeys;
+ this.initialChanges = initialChanges;
+ }
+
+ @Override public boolean hasNext()
+ {
+ while(-1 < pos)
+ {
+ if(null != values[pos])
+ return true;
+ pos--;
+ }
+ return false;
+ }
+
+ @Override public TripleBunch next()
+ {
+ if (changes > initialChanges) throw new ConcurrentModificationException();
+ if (-1 < pos && null != values[pos]) return values[pos--];
+ throw new NoSuchElementException("HashCommon keys");
+ }
+
+ @Override public void forEachRemaining(Consumer<? super TripleBunch> action)
+ {
+ while(-1 < pos)
+ {
+ if(null != values[pos]) action.accept(values[pos]);
+ pos--;
+ }
+ if (changes > initialChanges) throw new ConcurrentModificationException();
+ }
+
+ @Override public void remove()
+ {
+ if (changes > initialChanges) throw new ConcurrentModificationException();
+ // System.err.println( ">> keyIterator::remove, size := " + size +
+ // ", removing " + keys[index + 1] );
+ Object moved = removeFrom( pos + 1 );
+ if (moved != null) movedKeys.add( moved );
+ if (size < 0) throw new BrokenException( "BROKEN" );
+ }
+ }
+
+ @Override public Spliterator<TripleBunch> spliterator() {
+ final var initialChanges = changes;
+ final Runnable checkForConcurrentModification = () ->
+ {
+ if (changes != initialChanges) throw new ConcurrentModificationException();
+ };
+
+ return new SparseArraySpliterator<>(values, size, checkForConcurrentModification);
+ }
}
diff --git a/jena-core/src/main/java/org/apache/jena/mem/HashedTripleBunch.java b/jena-core/src/main/java/org/apache/jena/mem/HashedTripleBunch.java
index b534e63..5369e89 100644
--- a/jena-core/src/main/java/org/apache/jena/mem/HashedTripleBunch.java
+++ b/jena-core/src/main/java/org/apache/jena/mem/HashedTripleBunch.java
@@ -18,7 +18,7 @@
package org.apache.jena.mem;
-import java.util.Iterator ;
+import java.util.Spliterator;
import org.apache.jena.graph.Triple ;
import org.apache.jena.util.iterator.ExtendedIterator ;
@@ -28,7 +28,7 @@
public HashedTripleBunch( TripleBunch b )
{
super( nextSize( (int) (b.size() / loadFactor) ) );
- for (Iterator<Triple> it = b.iterator(); it.hasNext();) add( it.next() );
+ b.spliterator().forEachRemaining(this::add);
changes = 0;
}
@@ -74,7 +74,7 @@
public void add( Triple t )
{
keys[findSlot( t )] = t;
- changes += 1;
+ changes++;
if (++size > threshold) grow();
}
@@ -94,7 +94,7 @@
@Override public void remove( Triple t )
{
super.remove( t );
- changes += 1;
+ changes++;
}
@Override
@@ -104,4 +104,7 @@
@Override
public ExtendedIterator<Triple> iterator( final NotifyEmpty container )
{ return keyIterator( container ); }
-}
+
+ @Override public Spliterator<Triple> spliterator()
+ { return super.keySpliterator(); }
+ }
diff --git a/jena-core/src/main/java/org/apache/jena/mem/NodeToTriplesMap.java b/jena-core/src/main/java/org/apache/jena/mem/NodeToTriplesMap.java
deleted file mode 100644
index a41f748..0000000
--- a/jena-core/src/main/java/org/apache/jena/mem/NodeToTriplesMap.java
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.jena.mem;
-
-import static org.apache.jena.util.iterator.WrappedIterator.create;
-
-import java.util.*;
-import java.util.function.Predicate;
-
-import org.apache.jena.graph.* ;
-import org.apache.jena.graph.Triple.* ;
-import org.apache.jena.util.iterator.* ;
-
-/**
- NodeToTriplesMap: a map from nodes to sets of triples.
- Subclasses must override at least one of useXXXInFilter methods.
-*/
-public class NodeToTriplesMap extends NodeToTriplesMapBase
- {
- public NodeToTriplesMap( Field indexField, Field f2, Field f3 )
- { super( indexField, f2, f3 ); }
-
- @Override public boolean add( Triple t )
- {
- Object o = getIndexField( t );
-
- // Feb 2016 : no measurable difference.
- //OpenSetBunch s = (OpenSetBunch) bunchMap.getOrSet(o, (k)->createSetBunch()) ;
-
- OpenSetBunch s = (OpenSetBunch) bunchMap.get( o );
- if (s == null) bunchMap.put( o, s = createSetBunch() );
- if (s.baseSet().add( t )) { size += 1; return true; } else return false;
- }
-
- private static class OpenSetBunch extends SetBunch
- {
- private static final TripleBunch empty = new ArrayBunch();
-
- public OpenSetBunch()
- { super( empty ); }
-
- public Set<Triple> baseSet()
- { return elements; }
- }
-
- private OpenSetBunch createSetBunch()
- { return new OpenSetBunch(); }
-
- @Override public boolean remove( Triple t )
- {
- Object o = getIndexField( t );
- OpenSetBunch s = (OpenSetBunch) bunchMap.get( o );
- if (s == null)
- return false;
- else
- {
- Set<Triple> base = s.baseSet();
- boolean result = base.remove( t );
- if (result) size -= 1;
- if (base.isEmpty()) bunchMap.remove( o );
- return result;
- }
- }
-
- @Override public ExtendedIterator<Triple> iterator( Object o, HashCommon.NotifyEmpty container )
- {
- TripleBunch b = bunchMap.get( o );
- return b == null ? NullIterator.<Triple>instance() : b.iterator();
- }
-
- @Override public boolean contains( Triple t )
- {
- TripleBunch s = bunchMap.get( getIndexField( t ) );
- return s == null ? false : s.contains( t );
- }
-
- protected static boolean equalsObjectOK( Triple t )
- {
- Node o = t.getObject();
- return o.isLiteral() ? o.getLiteralDatatype() == null : true;
- }
-
- @Override
- public boolean containsBySameValueAs( Triple t )
- { return equalsObjectOK( t ) ? contains( t ) : slowContains( t ); }
-
- protected boolean slowContains( Triple t )
- {
- TripleBunch s = bunchMap.get( getIndexField( t ) );
- if (s == null)
- return false;
- else
- {
- Iterator<Triple> it = s.iterator();
- while (it.hasNext()) if (t.matches( it.next() )) return true;
- return false;
- }
- }
-
- public ExtendedIterator<Triple> iterateAll(Triple pattern) {
- Predicate<Triple> filter = indexField.filterOn(pattern)
- .and(f2.filterOn(pattern)).and(f3.filterOn(pattern));
- return create(iterateAll()).filterKeep(filter);
- }
-
- @Override public ExtendedIterator<Triple> iterator( Node index, Node n2, Node n3 )
- {
- TripleBunch s = bunchMap.get( index.getIndexingValue() );
- if (s == null) return NullIterator.<Triple>instance();
- final Predicate<Triple> filter = f2.filterOn( n2 ).and( f3.filterOn( n3 ) );
- return create(s.iterator()).filterKeep(filter);
- }
-
- /**
- Answer an iterator over all the triples that are indexed by the item <code>y</code>.
- Note that <code>y</code> need not be a Node (because of indexing values).
- */
- @Override public Iterator<Triple> iteratorForIndexed( Object y )
- { return get( y ).iterator(); }
-
- /**
- @see org.apache.jena.mem.Temp#get(java.lang.Object)
- */
- private TripleBunch get( Object y )
- { return bunchMap.get( y ); }
- }
diff --git a/jena-core/src/main/java/org/apache/jena/mem/NodeToTriplesMapBase.java b/jena-core/src/main/java/org/apache/jena/mem/NodeToTriplesMapBase.java
index 74ec968..5861945 100644
--- a/jena-core/src/main/java/org/apache/jena/mem/NodeToTriplesMapBase.java
+++ b/jena-core/src/main/java/org/apache/jena/mem/NodeToTriplesMapBase.java
@@ -19,6 +19,9 @@
package org.apache.jena.mem;
import java.util.*;
+import java.util.function.Consumer;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
import org.apache.jena.graph.* ;
import org.apache.jena.graph.Triple.Field ;
@@ -60,7 +63,7 @@
*/
public abstract boolean remove( Triple t );
- public abstract Iterator<Triple> iterator( Object o, HashCommon.NotifyEmpty container );
+ public abstract ExtendedIterator<Triple> iterator( Object o, HashCommon.NotifyEmpty container );
/**
Answer true iff this NTM contains the concrete triple <code>t</code>.
@@ -100,22 +103,22 @@
Answer an iterator over all the triples that are indexed by the item <code>y</code>.
Note that <code>y</code> need not be a Node (because of indexing values).
*/
- public abstract Iterator<Triple> iteratorForIndexed( Object y );
+ public abstract ExtendedIterator<Triple> iteratorForIndexed( Object y );
/**
Answer an iterator over all the triples in this NTM.
*/
public ExtendedIterator<Triple> iterateAll()
{
- final Iterator<Object> nodes = domain();
return new NiceIterator<Triple>()
{
+ private final Iterator<TripleBunch> bunchIterator = bunchMap.iterator();
private Iterator<Triple> current = NullIterator.instance();
private NotifyMe emptier = new NotifyMe();
@Override public Triple next()
{
- if (hasNext() == false) noElements( "NodeToTriples iterator" );
+ if (!hasNext()) noElements( "NodeToTriples iterator" );
return current.next();
}
@@ -123,7 +126,7 @@
{
@Override
public void emptied()
- { nodes.remove(); }
+ { bunchIterator.remove(); }
}
@Override public boolean hasNext()
@@ -131,14 +134,56 @@
while (true)
{
if (current.hasNext()) return true;
- if (nodes.hasNext() == false) return false;
- Object next = nodes.next();
- current = NodeToTriplesMapBase.this.iterator( next, emptier );
+ if (!bunchIterator.hasNext()) return false;
+ current = bunchIterator.next().iterator( emptier );
}
}
- @Override public void remove()
+ @Override public void forEachRemaining(Consumer<? super Triple> action)
+ {
+ if (current != null) current.forEachRemaining(action);
+ bunchIterator.forEachRemaining(next ->
+ {
+ current = next.iterator();
+ current.forEachRemaining(action);
+ });
+ }
+
+ @Override public void remove()
{ current.remove(); }
};
}
+
+
+ public Stream<Triple> streamAll()
+ {
+ return StreamSupport.stream(bunchMap.spliterator(), false)
+ .flatMap(bunch -> StreamSupport.stream(bunch.spliterator(), false));
+ }
+
+ public Stream<Triple> stream( Node index, Node n2, Node n3 )
+ {
+ Object indexValue = index.getIndexingValue();
+ TripleBunch s = bunchMap.get( indexValue );
+ if (s == null) return Stream.empty();
+ var filter = FieldFilter.filterOn(f2, n2, f3, n3);
+ return filter.hasFilter()
+ ? StreamSupport.stream(s.spliterator(), false).filter(filter.getFilter())
+ : StreamSupport.stream(s.spliterator(), false);
+ }
+
+ public boolean containsMatch( Node index, Node n2, Node n3 )
+ {
+ TripleBunch s = bunchMap.get( index.getIndexingValue() );
+ if (s == null)
+ return false;
+ var filter = FieldFilter.filterOn(f2, n2, f3, n3);
+ if (!filter.hasFilter())
+ return true;
+ var spliterator = s.spliterator();
+ final boolean[] found = {false};
+ Consumer<Triple> tester = triple -> found[0] = filter.getFilter().test(triple);
+ while (!found[0] && spliterator.tryAdvance(tester));
+ return found[0];
+ }
}
diff --git a/jena-core/src/main/java/org/apache/jena/mem/NodeToTriplesMapMem.java b/jena-core/src/main/java/org/apache/jena/mem/NodeToTriplesMapMem.java
index f95bab6..2385ae2 100644
--- a/jena-core/src/main/java/org/apache/jena/mem/NodeToTriplesMapMem.java
+++ b/jena-core/src/main/java/org/apache/jena/mem/NodeToTriplesMapMem.java
@@ -18,9 +18,6 @@
package org.apache.jena.mem;
-import static org.apache.jena.util.iterator.WrappedIterator.create;
-
-import java.util.Iterator ;
import java.util.function.Predicate;
import org.apache.jena.graph.Node ;
@@ -84,11 +81,18 @@
Answer an iterator over all the triples in this NTM which have index node
<code>o</code>.
*/
- @Override public Iterator<Triple> iterator( Object o, HashCommon.NotifyEmpty container )
+ @Override public ExtendedIterator<Triple> iterator( Object o, HashCommon.NotifyEmpty container )
{
TripleBunch s = bunchMap.get( o );
return s == null ? NullIterator.<Triple>instance() : s.iterator( container );
}
+
+ public ExtendedIterator<Triple> iterateAll(Triple pattern)
+ {
+ Predicate<Triple> filter = indexField.filterOn(pattern)
+ .and(f2.filterOn(pattern)).and(f3.filterOn(pattern));
+ return iterateAll().filterKeep(filter);
+ }
public class NotifyMe implements HashCommon.NotifyEmpty
{
@@ -129,17 +133,19 @@
TripleBunch s = bunchMap.get( indexValue );
// System.err.println( ">> ntmf::iterator: " + (s == null ? (Object) "None" : s.getClass()) );
if (s == null) return NullIterator.<Triple>instance();
- final Predicate<Triple> filter = f2.filterOn( n2 ).and( f3.filterOn( n3 ) );
- return create(s.iterator( new NotifyMe( indexValue ))).filterKeep(filter);
- }
+ var filter = FieldFilter.filterOn(f2, n2, f3, n3);
+ return filter.hasFilter()
+ ? s.iterator( new NotifyMe( indexValue ) ).filterKeep( filter.getFilter() )
+ : s.iterator( new NotifyMe( indexValue ) );
+ }
- protected TripleBunch get( Object index )
+ protected TripleBunch get( Object index )
{ return bunchMap.get( index ); }
/**
Answer an iterator over all the triples that are indexed by the item <code>y</code>.
Note that <code>y</code> need not be a Node (because of indexing values).
*/
- @Override public Iterator<Triple> iteratorForIndexed( Object y )
+ @Override public ExtendedIterator<Triple> iteratorForIndexed( Object y )
{ return get( y ).iterator(); }
}
diff --git a/jena-core/src/main/java/org/apache/jena/mem/ObjectIterator.java b/jena-core/src/main/java/org/apache/jena/mem/ObjectIterator.java
index f495cb6..1ba1a55 100644
--- a/jena-core/src/main/java/org/apache/jena/mem/ObjectIterator.java
+++ b/jena-core/src/main/java/org/apache/jena/mem/ObjectIterator.java
@@ -19,6 +19,7 @@
package org.apache.jena.mem;
import java.util.*;
+import java.util.function.Consumer;
import org.apache.jena.graph.* ;
import org.apache.jena.util.CollectionFactory ;
@@ -57,7 +58,25 @@
( "ObjectIterator.next()" );
return pending.remove( pending.size() - 1 );
}
-
+
+ @Override public void forEachRemaining(Consumer<? super Node> action)
+ {
+ pending.forEach(action);
+ domain.forEachRemaining(y ->
+ {
+ if (y instanceof Node)
+ action.accept( (Node) y );
+ else
+ {
+ iteratorFor( y ).forEachRemaining(triple ->
+ {
+ if (seen.add( triple.getObject() )) action.accept( triple.getObject() );
+ });
+ }
+ }
+ );
+ }
+
protected void refillPending()
{
Object y = domain.next();
@@ -65,12 +84,9 @@
pending.add( (Node) y );
else
{
- Iterator<Triple> z = iteratorFor( y );
- while (z.hasNext())
- {
- Node object = z.next().getObject();
- if (seen.add( object )) pending.add( object );
- }
+ iteratorFor( y ).forEachRemaining(triple -> {
+ if (seen.add( triple.getObject() )) pending.add( triple.getObject() );
+ });
}
}
diff --git a/jena-core/src/main/java/org/apache/jena/mem/SetBunch.java b/jena-core/src/main/java/org/apache/jena/mem/SetBunch.java
deleted file mode 100644
index 28dac82..0000000
--- a/jena-core/src/main/java/org/apache/jena/mem/SetBunch.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.jena.mem;
-
-import java.util.HashSet ;
-import java.util.Iterator ;
-import java.util.Set ;
-
-import org.apache.jena.graph.Node ;
-import org.apache.jena.graph.Triple ;
-import org.apache.jena.util.iterator.ExtendedIterator ;
-import org.apache.jena.util.iterator.WrappedIterator ;
-
-public class SetBunch implements TripleBunch
- {
- protected Set<Triple> elements = new HashSet<>(20);
-
- public SetBunch( TripleBunch b )
- {
- for (Iterator<Triple> it = b.iterator(); it.hasNext();)
- elements.add( it.next() );
- }
-
- protected static boolean equalsObjectOK( Triple t )
- {
- Node o = t.getObject();
- return o.isLiteral() ? o.getLiteralDatatype() == null : true;
- }
-
- @Override
- public boolean contains( Triple t )
- { return elements.contains( t ); }
-
- @Override
- public boolean containsBySameValueAs( Triple t )
- { return equalsObjectOK( t ) ? elements.contains( t ) : slowContains( t ); }
-
- protected boolean slowContains( Triple t )
- {
- for ( Triple element : elements )
- {
- if ( t.matches( element ) )
- {
- return true;
- }
- }
- return false;
- }
-
- @Override
- public int size()
- { return elements.size(); }
-
- @Override
- public void add( Triple t )
- { elements.add( t ); }
-
- @Override
- public void remove( Triple t )
- { elements.remove( t ); }
-
- @Override
- public ExtendedIterator<Triple> iterator( HashCommon.NotifyEmpty container )
- {
- return iterator();
- }
-
- @Override
- public ExtendedIterator<Triple> iterator()
- { return WrappedIterator.create( elements.iterator() ); }
-
- }
diff --git a/jena-core/src/main/java/org/apache/jena/mem/SparseArraySpliterator.java b/jena-core/src/main/java/org/apache/jena/mem/SparseArraySpliterator.java
new file mode 100644
index 0000000..8c8fb98
--- /dev/null
+++ b/jena-core/src/main/java/org/apache/jena/mem/SparseArraySpliterator.java
@@ -0,0 +1,203 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.mem;
+
+import java.util.Spliterator;
+import java.util.function.Consumer;
+
+/**
+ * A spliterator for sparse arrays. This spliterator will iterate over the array
+ * skipping null entries.
+ *
+ * This spliterator supports splitting into sub-spliterators.
+ *
+ * The spliterator will check for concurrent modifications by invoking a {@link Runnable}
+ * before each action.
+ *
+ * @param <E> the type of the array elements
+ */
+public class SparseArraySpliterator<E> implements Spliterator<E> {
+
+ private final E[] entries;
+ private int pos;
+ private final float fillRatio;
+ private final Runnable checkForConcurrentModification;
+
+ /**
+ * Create a spliterator for the given array, with the given size.
+ * @param entries the array
+ * @param estimatedElementsCount the estimated size
+ */
+ public SparseArraySpliterator(final E[] entries, final int estimatedElementsCount, final Runnable checkForConcurrentModification) {
+ this.entries = entries;
+ this.pos = entries.length;
+ this.fillRatio = (float) estimatedElementsCount / (float)entries.length;
+ this.checkForConcurrentModification = checkForConcurrentModification;
+ }
+
+
+ /**
+ * If a remaining element exists, performs the given action on it,
+ * returning {@code true}; else returns {@code false}. If this
+ * Spliterator is {@link #ORDERED} the action is performed on the
+ * next element in encounter order. Exceptions thrown by the
+ * action are relayed to the caller.
+ *
+ * @param action The action
+ * @return {@code false} if no remaining elements existed
+ * upon entry to this method, else {@code true}.
+ * @throws NullPointerException if the specified action is null
+ */
+ @Override
+ public boolean tryAdvance(Consumer<? super E> action) {
+ this.checkForConcurrentModification.run();
+ while(-1 < --pos) {
+ if(null != entries[pos]) {
+ action.accept(entries[pos]);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Performs the given action for each remaining element, sequentially in
+ * the current thread, until all elements have been processed or the action
+ * throws an exception. If this Spliterator is {@link #ORDERED}, actions
+ * are performed in encounter order. Exceptions thrown by the action
+ * are relayed to the caller.
+ *
+ * @param action The action
+ * @throws NullPointerException if the specified action is null
+ * @implSpec The default implementation repeatedly invokes {@link #tryAdvance} until
+ * it returns {@code false}. It should be overridden whenever possible.
+ */
+ @Override
+ public void forEachRemaining(Consumer<? super E> action) {
+ pos--;
+ while (-1 < pos) {
+ if(null != entries[pos]) {
+ action.accept(entries[pos]);
+ }
+ pos--;
+ }
+ this.checkForConcurrentModification.run();
+ }
+
+ /**
+ * If this spliterator can be partitioned, returns a Spliterator
+ * covering elements, that will, upon return from this method, not
+ * be covered by this Spliterator.
+ *
+ * <p>If this Spliterator is {@link #ORDERED}, the returned Spliterator
+ * must cover a strict prefix of the elements.
+ *
+ * <p>Unless this Spliterator covers an infinite number of elements,
+ * repeated calls to {@code trySplit()} must eventually return {@code null}.
+ * Upon non-null return:
+ * <ul>
+ * <li>the value reported for {@code estimateSize()} before splitting,
+ * must, after splitting, be greater than or equal to {@code estimateSize()}
+ * for this and the returned Spliterator; and</li>
+ * <li>if this Spliterator is {@code SUBSIZED}, then {@code estimateSize()}
+ * for this spliterator before splitting must be equal to the sum of
+ * {@code estimateSize()} for this and the returned Spliterator after
+ * splitting.</li>
+ * </ul>
+ *
+ * <p>This method may return {@code null} for any reason,
+ * including emptiness, inability to split after traversal has
+ * commenced, data structure constraints, and efficiency
+ * considerations.
+ *
+ * @return a {@code Spliterator} covering some portion of the
+ * elements, or {@code null} if this spliterator cannot be split
+ * @apiNote An ideal {@code trySplit} method efficiently (without
+ * traversal) divides its elements exactly in half, allowing
+ * balanced parallel computation. Many departures from this ideal
+ * remain highly effective; for example, only approximately
+ * splitting an approximately balanced tree, or for a tree in
+ * which leaf nodes may contain either one or two elements,
+ * failing to further split these nodes. However, large
+ * deviations in balance and/or overly inefficient {@code
+ * trySplit} mechanics typically result in poor parallel
+ * performance.
+ */
+ @Override
+ public Spliterator<E> trySplit() {
+ if (pos < 2) {
+ return null;
+ }
+ if (this.estimateSize() < 2L) {
+ return null;
+ }
+ final int toIndexOfSubIterator = this.pos;
+ this.pos = pos >>> 1;
+ return new SparseArraySubSpliterator<E>(entries, this.pos, toIndexOfSubIterator, fillRatio, checkForConcurrentModification);
+ }
+
+ /**
+ * Returns an estimate of the number of elements that would be
+ * encountered by a {@link #forEachRemaining} traversal, or returns {@link
+ * Long#MAX_VALUE} if infinite, unknown, or too expensive to compute.
+ *
+ * <p>If this Spliterator is {@link #SIZED} and has not yet been partially
+ * traversed or split, or this Spliterator is {@link #SUBSIZED} and has
+ * not yet been partially traversed, this estimate must be an accurate
+ * count of elements that would be encountered by a complete traversal.
+ * Otherwise, this estimate may be arbitrarily inaccurate, but must decrease
+ * as specified across invocations of {@link #trySplit}.
+ *
+ * @return the estimated size, or {@code Long.MAX_VALUE} if infinite,
+ * unknown, or too expensive to compute.
+ * @apiNote Even an inexact estimate is often useful and inexpensive to compute.
+ * For example, a sub-spliterator of an approximately balanced binary tree
+ * may return a value that estimates the number of elements to be half of
+ * that of its parent; if the root Spliterator does not maintain an
+ * accurate count, it could estimate size to be the power of two
+ * corresponding to its maximum depth.
+ */
+ @Override
+ public long estimateSize() { return ((long) (this.fillRatio * pos)) + 1L; }
+
+ /**
+ * Returns a set of characteristics of this Spliterator and its
+ * elements. The result is represented as ORed values from {@link
+ * #ORDERED}, {@link #DISTINCT}, {@link #SORTED}, {@link #SIZED},
+ * {@link #NONNULL}, {@link #IMMUTABLE}, {@link #CONCURRENT},
+ * {@link #SUBSIZED}. Repeated calls to {@code characteristics()} on
+ * a given spliterator, prior to or in-between calls to {@code trySplit},
+ * should always return the same result.
+ *
+ * <p>If a Spliterator reports an inconsistent set of
+ * characteristics (either those returned from a single invocation
+ * or across multiple invocations), no guarantees can be made
+ * about any computation using this Spliterator.
+ *
+ * @return a representation of characteristics
+ * @apiNote The characteristics of a given spliterator before splitting
+ * may differ from the characteristics after splitting. For specific
+ * examples see the characteristic values {@link #SIZED}, {@link #SUBSIZED}
+ * and {@link #CONCURRENT}.
+ */
+ @Override
+ public int characteristics() {
+ return DISTINCT | NONNULL | IMMUTABLE;
+ }
+}
diff --git a/jena-core/src/main/java/org/apache/jena/mem/SparseArraySubSpliterator.java b/jena-core/src/main/java/org/apache/jena/mem/SparseArraySubSpliterator.java
new file mode 100644
index 0000000..f17e5d1
--- /dev/null
+++ b/jena-core/src/main/java/org/apache/jena/mem/SparseArraySubSpliterator.java
@@ -0,0 +1,220 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.mem;
+
+import java.util.Spliterator;
+import java.util.function.Consumer;
+
+/**
+ * A spliterator for sparse arrays. This spliterator will iterate over the array
+ * skipping null entries.
+ *
+ * This spliterator supports splitting into sub-spliterators.
+ *
+ * The spliterator will check for concurrent modifications by invoking a {@link Runnable}
+ * before each action.
+ *
+ * @param <E>
+ */
+public class SparseArraySubSpliterator<E> implements Spliterator<E> {
+
+ private final E[] entries;
+ private final int fromIndex;
+ private int pos;
+ private final float fillRatio;
+ private final Runnable checkForConcurrentModification;
+
+ /**
+ * Create a spliterator for the given array, with the given size.
+ *
+ * @param entries the array
+ * @param fromIndex the index of the first element, inclusive
+ * @param toIndex the index of the last element, exclusive
+ * @param fillRatio the ratio of elements containing null
+ * @param checkForConcurrentModification
+ */
+ public SparseArraySubSpliterator(final E[] entries, final int fromIndex, final int toIndex, final float fillRatio, final Runnable checkForConcurrentModification) {
+ this.entries = entries;
+ this.fromIndex = fromIndex;
+ this.pos = toIndex;
+ this.fillRatio = fillRatio;
+ this.checkForConcurrentModification = checkForConcurrentModification;
+ }
+
+ /**
+ * Create a spliterator for the given array, with the given size.
+ * @param entries the array
+ * @param estimatedElementsCount the estimated size
+ */
+ public SparseArraySubSpliterator(final E[] entries, final int estimatedElementsCount, final Runnable checkForConcurrentModification) {
+ this(entries, 0, entries.length, ((float)estimatedElementsCount / (float)entries.length), checkForConcurrentModification);
+ }
+
+
+ /**
+ * If a remaining element exists, performs the given action on it,
+ * returning {@code true}; else returns {@code false}. If this
+ * Spliterator is {@link #ORDERED} the action is performed on the
+ * next element in encounter order. Exceptions thrown by the
+ * action are relayed to the caller.
+ *
+ * @param action The action
+ * @return {@code false} if no remaining elements existed
+ * upon entry to this method, else {@code true}.
+ * @throws NullPointerException if the specified action is null
+ */
+ @Override
+ public boolean tryAdvance(Consumer<? super E> action) {
+ this.checkForConcurrentModification.run();
+ while(fromIndex <= --pos) {
+ if(null != entries[pos]) {
+ action.accept(entries[pos]);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Performs the given action for each remaining element, sequentially in
+ * the current thread, until all elements have been processed or the action
+ * throws an exception. If this Spliterator is {@link #ORDERED}, actions
+ * are performed in encounter order. Exceptions thrown by the action
+ * are relayed to the caller.
+ *
+ * @param action The action
+ * @throws NullPointerException if the specified action is null
+ * @implSpec The default implementation repeatedly invokes {@link #tryAdvance} until
+ * it returns {@code false}. It should be overridden whenever possible.
+ */
+ @Override
+ public void forEachRemaining(Consumer<? super E> action) {
+ pos--;
+ while (fromIndex <= pos) {
+ if(null != entries[pos]) {
+ action.accept(entries[pos]);
+ }
+ pos--;
+ }
+ this.checkForConcurrentModification.run();
+ }
+
+ /**
+ * If this spliterator can be partitioned, returns a Spliterator
+ * covering elements, that will, upon return from this method, not
+ * be covered by this Spliterator.
+ *
+ * <p>If this Spliterator is {@link #ORDERED}, the returned Spliterator
+ * must cover a strict prefix of the elements.
+ *
+ * <p>Unless this Spliterator covers an infinite number of elements,
+ * repeated calls to {@code trySplit()} must eventually return {@code null}.
+ * Upon non-null return:
+ * <ul>
+ * <li>the value reported for {@code estimateSize()} before splitting,
+ * must, after splitting, be greater than or equal to {@code estimateSize()}
+ * for this and the returned Spliterator; and</li>
+ * <li>if this Spliterator is {@code SUBSIZED}, then {@code estimateSize()}
+ * for this spliterator before splitting must be equal to the sum of
+ * {@code estimateSize()} for this and the returned Spliterator after
+ * splitting.</li>
+ * </ul>
+ *
+ * <p>This method may return {@code null} for any reason,
+ * including emptiness, inability to split after traversal has
+ * commenced, data structure constraints, and efficiency
+ * considerations.
+ *
+ * @return a {@code Spliterator} covering some portion of the
+ * elements, or {@code null} if this spliterator cannot be split
+ * @apiNote An ideal {@code trySplit} method efficiently (without
+ * traversal) divides its elements exactly in half, allowing
+ * balanced parallel computation. Many departures from this ideal
+ * remain highly effective; for example, only approximately
+ * splitting an approximately balanced tree, or for a tree in
+ * which leaf nodes may contain either one or two elements,
+ * failing to further split these nodes. However, large
+ * deviations in balance and/or overly inefficient {@code
+ * trySplit} mechanics typically result in poor parallel
+ * performance.
+ */
+ @Override
+ public Spliterator<E> trySplit() {
+ final int entriesCount = pos - fromIndex;
+ if (entriesCount < 2) {
+ return null;
+ }
+ if (this.estimateSize() < 2L) {
+ return null;
+ }
+ final int toIndexOfSubIterator = this.pos;
+ this.pos = fromIndex + (entriesCount >>> 1);
+ return new SparseArraySubSpliterator<>(entries, this.pos, toIndexOfSubIterator, fillRatio, checkForConcurrentModification);
+ }
+
+ /**
+ * Returns an estimate of the number of elements that would be
+ * encountered by a {@link #forEachRemaining} traversal, or returns {@link
+ * Long#MAX_VALUE} if infinite, unknown, or too expensive to compute.
+ *
+ * <p>If this Spliterator is {@link #SIZED} and has not yet been partially
+ * traversed or split, or this Spliterator is {@link #SUBSIZED} and has
+ * not yet been partially traversed, this estimate must be an accurate
+ * count of elements that would be encountered by a complete traversal.
+ * Otherwise, this estimate may be arbitrarily inaccurate, but must decrease
+ * as specified across invocations of {@link #trySplit}.
+ *
+ * @return the estimated size, or {@code Long.MAX_VALUE} if infinite,
+ * unknown, or too expensive to compute.
+ * @apiNote Even an inexact estimate is often useful and inexpensive to compute.
+ * For example, a sub-spliterator of an approximately balanced binary tree
+ * may return a value that estimates the number of elements to be half of
+ * that of its parent; if the root Spliterator does not maintain an
+ * accurate count, it could estimate size to be the power of two
+ * corresponding to its maximum depth.
+ */
+ @Override
+ public long estimateSize() { return ((long) (fillRatio * (pos - fromIndex))) + 1L; }
+
+
+ /**
+ * Returns a set of characteristics of this Spliterator and its
+ * elements. The result is represented as ORed values from {@link
+ * #ORDERED}, {@link #DISTINCT}, {@link #SORTED}, {@link #SIZED},
+ * {@link #NONNULL}, {@link #IMMUTABLE}, {@link #CONCURRENT},
+ * {@link #SUBSIZED}. Repeated calls to {@code characteristics()} on
+ * a given spliterator, prior to or in-between calls to {@code trySplit},
+ * should always return the same result.
+ *
+ * <p>If a Spliterator reports an inconsistent set of
+ * characteristics (either those returned from a single invocation
+ * or across multiple invocations), no guarantees can be made
+ * about any computation using this Spliterator.
+ *
+ * @return a representation of characteristics
+ * @apiNote The characteristics of a given spliterator before splitting
+ * may differ from the characteristics after splitting. For specific
+ * examples see the characteristic values {@link #SIZED}, {@link #SUBSIZED}
+ * and {@link #CONCURRENT}.
+ */
+ @Override
+ public int characteristics() {
+ return DISTINCT | NONNULL | IMMUTABLE;
+ }
+}
diff --git a/jena-core/src/main/java/org/apache/jena/mem/TrackingTripleIterator.java b/jena-core/src/main/java/org/apache/jena/mem/TrackingTripleIterator.java
index bf0c844..4972be5 100644
--- a/jena-core/src/main/java/org/apache/jena/mem/TrackingTripleIterator.java
+++ b/jena-core/src/main/java/org/apache/jena/mem/TrackingTripleIterator.java
@@ -19,6 +19,7 @@
package org.apache.jena.mem;
import java.util.Iterator;
+import java.util.function.Consumer;
import org.apache.jena.graph.* ;
import org.apache.jena.util.iterator.WrappedIterator ;
@@ -27,6 +28,8 @@
A WrappedIterator which remembers the last object next'ed in a
protected instance variable, so that subclasses have access to it
during .remove.
+ After a call to {@link TrackingTripleIterator#forEachRemaining} current is null. So calling #remove after
+ #forEachRemaining is not supported.
*/
public class TrackingTripleIterator extends WrappedIterator<Triple>
{
@@ -44,5 +47,14 @@
*/
@Override
public Triple next()
- { return current = super.next(); }
+ { return current = super.next(); }
+
+ @Override
+ public void forEachRemaining(Consumer<? super Triple> action)
+ {
+ /** The behavior of {@link java.util.Iterator#remove}is undefined after a call to forEachRemaining.
+ So it should be okay, to not waste performance here. */
+ this.current = null;
+ super.forEachRemaining(action);
+ }
}
diff --git a/jena-core/src/main/java/org/apache/jena/mem/TripleBunch.java b/jena-core/src/main/java/org/apache/jena/mem/TripleBunch.java
index c1e2ba2..2dfb353 100644
--- a/jena-core/src/main/java/org/apache/jena/mem/TripleBunch.java
+++ b/jena-core/src/main/java/org/apache/jena/mem/TripleBunch.java
@@ -21,6 +21,8 @@
import org.apache.jena.graph.Triple ;
import org.apache.jena.util.iterator.ExtendedIterator ;
+import java.util.Spliterator;
+
/**
A bunch of triples - a stripped-down set with specialized methods. A
bunch is expected to store triples that share some useful property
@@ -72,5 +74,10 @@
<code>container</code> is invoked.
*/
public abstract ExtendedIterator<Triple> iterator( HashCommon.NotifyEmpty container );
+
+ /**
+ Answer a spliterator over all the triples in this bunch.
+ */
+ public abstract Spliterator<Triple> spliterator();
}
diff --git a/jena-core/src/main/java/org/apache/jena/mem/WrappedHashMap.java b/jena-core/src/main/java/org/apache/jena/mem/WrappedHashMap.java
deleted file mode 100644
index b5c929e..0000000
--- a/jena-core/src/main/java/org/apache/jena/mem/WrappedHashMap.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.jena.mem;
-
-import java.util.Map;
-import java.util.function.Function ;
-
-import org.apache.jena.util.CollectionFactory ;
-import org.apache.jena.util.iterator.* ;
-
-/**
- An implementation of BunchMap that delegates to a [Hashed]Map.
-*/
-public class WrappedHashMap implements BunchMap
- {
- protected final Map<Object, TripleBunch> map = CollectionFactory.createHashedMap();
-
- public WrappedHashMap() {}
-
- @Override
- public void clear()
- { map.clear(); }
-
- @Override
- public long size()
- { return map.size(); }
-
- @Override
- public TripleBunch get( Object key )
- { return map.get( key ); }
-
- @Override
- public void put( Object key, TripleBunch value )
- { map.put( key, value ); }
-
- @Override
- public TripleBunch getOrSet( Object key, Function<Object, TripleBunch> setter) {
- return map.computeIfAbsent(key, setter);
- }
-
- @Override
- public void remove( Object key )
- { map.remove( key ); }
-
- @Override
- public ExtendedIterator<Object> keyIterator()
- { return WrappedIterator.create( map.keySet().iterator() ); }
- }
diff --git a/jena-core/src/main/java/org/apache/jena/util/IteratorCollection.java b/jena-core/src/main/java/org/apache/jena/util/IteratorCollection.java
index d049225..a6dd8ee 100644
--- a/jena-core/src/main/java/org/apache/jena/util/IteratorCollection.java
+++ b/jena-core/src/main/java/org/apache/jena/util/IteratorCollection.java
@@ -45,7 +45,7 @@
public static <T> Set<T> iteratorToSet( Iterator<? extends T> i )
{
Set<T> result = CollectionFactory.createHashedSet();
- try { while (i.hasNext()) result.add( i.next() ); }
+ try { i.forEachRemaining(result::add); }
finally { NiceIterator.close( i ); }
return result;
}
@@ -54,13 +54,13 @@
Answer the elements of the given iterator as a list, in the order that they
arrived from the iterator. The iterator is consumed by this operation:
even if an exception is thrown, the iterator will be closed.
- @param it the iterator to convert
- @return a list of the elements of <code>it</code>, in order
+ @param it the iterator to convert
+ @return a list of the elements of <code>it</code>, in order
*/
public static <T> List<T> iteratorToList( Iterator<? extends T> it )
{
List<T> result = new ArrayList<>();
- try { while (it.hasNext()) result.add( it.next() ); }
+ try { it.forEachRemaining(result::add); }
finally { NiceIterator.close( it ); }
return result;
}
diff --git a/jena-core/src/main/java/org/apache/jena/util/iterator/FilterIterator.java b/jena-core/src/main/java/org/apache/jena/util/iterator/FilterIterator.java
index c5ddb83..26a0a99 100644
--- a/jena-core/src/main/java/org/apache/jena/util/iterator/FilterIterator.java
+++ b/jena-core/src/main/java/org/apache/jena/util/iterator/FilterIterator.java
@@ -20,6 +20,7 @@
import java.util.Iterator;
import java.util.NoSuchElementException;
+import java.util.function.Consumer;
import java.util.function.Predicate;
/**
@@ -85,4 +86,18 @@
}
throw new NoSuchElementException();
}
+
+ @Override
+ public void forEachRemaining(Consumer<? super T> action)
+ {
+ if (hasCurrent) {
+ action.accept(current);
+ hasCurrent = false;
+ }
+ super.forEachRemaining(e -> {
+ if (f.test(e)) {
+ action.accept(e);
+ }
+ });
+ }
}
diff --git a/jena-core/src/main/java/org/apache/jena/util/iterator/LazyIterator.java b/jena-core/src/main/java/org/apache/jena/util/iterator/LazyIterator.java
index 7ed6fd3..fb154bd 100644
--- a/jena-core/src/main/java/org/apache/jena/util/iterator/LazyIterator.java
+++ b/jena-core/src/main/java/org/apache/jena/util/iterator/LazyIterator.java
@@ -18,6 +18,8 @@
package org.apache.jena.util.iterator;
+import java.util.function.Consumer;
+
/** An ExtendedIterator that is created lazily.
* This is useful when constructing an iterator is expensive and
* you'd prefer to delay doing it until certain it's actually needed.
@@ -53,6 +55,12 @@
}
@Override
+ public void forEachRemaining(Consumer<? super T> action) {
+ lazy();
+ it.forEachRemaining(action);
+ }
+
+ @Override
public void remove() {
lazy();
it.remove();
diff --git a/jena-core/src/main/java/org/apache/jena/util/iterator/Map1Iterator.java b/jena-core/src/main/java/org/apache/jena/util/iterator/Map1Iterator.java
index 9df725b..4f4ba7c 100644
--- a/jena-core/src/main/java/org/apache/jena/util/iterator/Map1Iterator.java
+++ b/jena-core/src/main/java/org/apache/jena/util/iterator/Map1Iterator.java
@@ -19,6 +19,7 @@
package org.apache.jena.util.iterator;
import java.util.Iterator;
+import java.util.function.Consumer;
import java.util.function.Function;
/**
@@ -48,6 +49,11 @@
public @Override boolean hasNext()
{ return base.hasNext(); }
+
+ public @Override void forEachRemaining(Consumer<? super To> action)
+ { this.base.forEachRemaining(
+ x -> action.accept( map.apply( x ) ) ); }
+
public @Override void remove()
{ base.remove(); }
diff --git a/jena-core/src/main/java/org/apache/jena/util/iterator/MapFilterIterator.java b/jena-core/src/main/java/org/apache/jena/util/iterator/MapFilterIterator.java
index ec49ad7..3668622 100644
--- a/jena-core/src/main/java/org/apache/jena/util/iterator/MapFilterIterator.java
+++ b/jena-core/src/main/java/org/apache/jena/util/iterator/MapFilterIterator.java
@@ -19,6 +19,7 @@
package org.apache.jena.util.iterator;
import java.util.*;
+import java.util.function.Consumer;
/**
A MapFilterIterator takes a MapFilter and an [Extended]Iterator and returns a new
@@ -91,4 +92,20 @@
}
throw new NoSuchElementException();
}
+
+ @Override
+ synchronized public void forEachRemaining(Consumer<? super X> action) {
+ if(dead)
+ return;
+ if(current != null) {
+ action.accept(current);
+ current = null;
+ }
+ underlying.forEachRemaining( x -> {
+ X y = f.accept(x);
+ if(y != null)
+ action.accept(y);
+ });
+ dead = true;
+ }
}
diff --git a/jena-core/src/main/java/org/apache/jena/util/iterator/NiceIterator.java b/jena-core/src/main/java/org/apache/jena/util/iterator/NiceIterator.java
index cf86331..1696791 100644
--- a/jena-core/src/main/java/org/apache/jena/util/iterator/NiceIterator.java
+++ b/jena-core/src/main/java/org/apache/jena/util/iterator/NiceIterator.java
@@ -19,6 +19,7 @@
package org.apache.jena.util.iterator;
import java.util.*;
+import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
@@ -102,10 +103,18 @@
private Iterator<? extends T> current = a;
private Iterator<? extends T> removeFrom = null;
+ boolean hasNext = false;
+
@Override public boolean hasNext()
{
- while (current.hasNext() == false && index < pending.size()) current = advance();
- return current.hasNext();
+ if (hasNext) return true;
+ if (current.hasNext()) return hasNext = true;
+ while (index < pending.size())
+ {
+ current = advance();
+ if(current.hasNext()) return hasNext = true;
+ }
+ return false;
}
private Iterator< ? extends T> advance()
@@ -120,9 +129,20 @@
{
if (!hasNext()) noElements( "concatenation" );
removeFrom = current;
+ hasNext = false;
return current.next();
}
+ @Override public void forEachRemaining(Consumer<? super T> action)
+ {
+ current.forEachRemaining(action);
+ while(index < pending.size())
+ {
+ current = advance();
+ current.forEachRemaining(action);
+ }
+ }
+
@Override public void close()
{
close( current );
@@ -206,7 +226,7 @@
public static <T> Set<T> asSet( ExtendedIterator<T> it )
{
Set<T> result = new HashSet<>();
- while (it.hasNext()) result.add( it.next() );
+ it.forEachRemaining(result::add);
return result;
}
@@ -217,7 +237,7 @@
public static <T> List<T> asList( ExtendedIterator<T> it )
{
List<T> result = new ArrayList<>();
- while (it.hasNext()) result.add( it.next() );
+ it.forEachRemaining(result::add);
return result;
}
}
diff --git a/jena-core/src/main/java/org/apache/jena/util/iterator/SingletonIterator.java b/jena-core/src/main/java/org/apache/jena/util/iterator/SingletonIterator.java
index 1d36a66..59c0f64 100644
--- a/jena-core/src/main/java/org/apache/jena/util/iterator/SingletonIterator.java
+++ b/jena-core/src/main/java/org/apache/jena/util/iterator/SingletonIterator.java
@@ -18,6 +18,8 @@
package org.apache.jena.util.iterator;
+import java.util.function.Consumer;
+
/**
* A ClosableIterator that contains only one element
*/
@@ -58,4 +60,11 @@
}
}
+ @Override
+ public void forEachRemaining(Consumer<? super T> action) {
+ if(!delivered) {
+ action.accept(item);
+ delivered = true;
+ }
+ }
}
diff --git a/jena-core/src/main/java/org/apache/jena/util/iterator/WrappedIterator.java b/jena-core/src/main/java/org/apache/jena/util/iterator/WrappedIterator.java
index f45a154..cc1d385 100644
--- a/jena-core/src/main/java/org/apache/jena/util/iterator/WrappedIterator.java
+++ b/jena-core/src/main/java/org/apache/jena/util/iterator/WrappedIterator.java
@@ -19,6 +19,7 @@
package org.apache.jena.util.iterator;
import java.util.Iterator;
+import java.util.function.Consumer;
import java.util.stream.Stream;
/**
@@ -106,10 +107,11 @@
@Override public T next()
{ return base.next(); }
- /**
- if .remove() is allowed, delegate to the base iterator's .remove;
- otherwise, throw an UnsupportedOperationException.
- */
+ /** forEachRemaining: defer to the base iterator */
+ @Override
+ public void forEachRemaining(Consumer<? super T> action)
+ { base.forEachRemaining(action); }
+
@Override public void remove()
{
if (removeDenied) throw new UnsupportedOperationException();
diff --git a/jena-core/src/test/java/org/apache/jena/graph/test/TestNodeToTriplesMap.java b/jena-core/src/test/java/org/apache/jena/graph/test/TestNodeToTriplesMapMem.java
similarity index 74%
rename from jena-core/src/test/java/org/apache/jena/graph/test/TestNodeToTriplesMap.java
rename to jena-core/src/test/java/org/apache/jena/graph/test/TestNodeToTriplesMapMem.java
index 1adcdd4..0efbb8b 100644
--- a/jena-core/src/test/java/org/apache/jena/graph/test/TestNodeToTriplesMap.java
+++ b/jena-core/src/test/java/org/apache/jena/graph/test/TestNodeToTriplesMapMem.java
@@ -23,26 +23,26 @@
import junit.framework.TestSuite;
import org.apache.jena.graph.* ;
import org.apache.jena.graph.Triple.* ;
-import org.apache.jena.mem.NodeToTriplesMap ;
+import org.apache.jena.mem.NodeToTriplesMapMem ;
/**
TestNodeToTriplesMap: added, post-hoc, by kers once NTM got
rather complicated. So these tests may be (are, at the moment)
incomplete.
*/
-public class TestNodeToTriplesMap extends GraphTestBase
+public class TestNodeToTriplesMapMem extends GraphTestBase
{
- public TestNodeToTriplesMap( String name )
+ public TestNodeToTriplesMapMem(String name )
{ super( name ); }
public static TestSuite suite()
- { return new TestSuite( TestNodeToTriplesMap.class ); }
+ { return new TestSuite( TestNodeToTriplesMapMem.class ); }
- protected NodeToTriplesMap ntS = new NodeToTriplesMap( Field.fieldSubject, Field.fieldPredicate, Field.fieldObject );
+ protected NodeToTriplesMapMem ntS = new NodeToTriplesMapMem( Field.fieldSubject, Field.fieldPredicate, Field.fieldObject );
- protected NodeToTriplesMap ntP = new NodeToTriplesMap( Field.fieldPredicate, Field.fieldObject, Field.fieldSubject );
+ protected NodeToTriplesMapMem ntP = new NodeToTriplesMapMem( Field.fieldPredicate, Field.fieldObject, Field.fieldSubject );
- protected NodeToTriplesMap ntO = new NodeToTriplesMap( Field.fieldObject, Field.fieldPredicate, Field.fieldSubject );
+ protected NodeToTriplesMapMem ntO = new NodeToTriplesMapMem( Field.fieldObject, Field.fieldPredicate, Field.fieldSubject );
protected static final Node x = node( "x" );
@@ -53,7 +53,7 @@
testZeroSize( "fresh NTM", ntS );
}
- protected void testZeroSize( String title, NodeToTriplesMap nt )
+ protected void testZeroSize( String title, NodeToTriplesMapMem nt )
{
assertEquals( title + " should have size 0", 0, nt.size() );
assertEquals( title + " should be isEmpty()", true, nt.isEmpty() );
@@ -72,7 +72,7 @@
testJustOne( x, ntS );
}
- protected void testJustOne( Node x, NodeToTriplesMap nt )
+ protected void testJustOne( Node x, NodeToTriplesMapMem nt )
{
assertEquals( 1, nt.size() );
assertEquals( false, nt.isEmpty() );
@@ -136,7 +136,49 @@
}
assertEquals( tripleSet( "x nice a; x nice c; y nice d; y nice f" ), ntS.iterateAll().toSet() );
}
-
+
+ public void testRemoveByIteratorTriggerMove()
+ {
+ /*need hash collisions to be able to test moves caused by iterator#remove*/
+ var nodeA = new Node_URI("A") {
+ @Override
+ public int hashCode() {
+ return 1;
+ }
+ };
+ var nodeB = new Node_URI("B") {
+ @Override
+ public int hashCode() {
+ return 1;
+ }
+ };
+ var nodeC = new Node_URI("C") {
+ @Override
+ public int hashCode() {
+ return 1;
+ }
+ };
+ ntS.add(Triple.create(nodeA, NodeFactory.createURI("loves"), nodeB));
+ ntS.add(Triple.create(nodeB, NodeFactory.createURI("loves"), nodeC));
+ ntS.add(Triple.create(nodeC, NodeFactory.createURI("loves"), nodeA));
+
+ var triplesToFind = ntS.iterateAll().toSet();
+
+ Iterator<Triple> it = ntS.iterateAll();
+ while (it.hasNext())
+ {
+ Triple t = it.next();
+ triplesToFind.remove(t);
+ if (t.getSubject().equals( nodeA )) it.remove();
+ }
+ assertTrue(triplesToFind.isEmpty());
+
+ var expectedRemainingTripples = new HashSet<Triple>();
+ expectedRemainingTripples.add(Triple.create(nodeB, NodeFactory.createURI("loves"), nodeC));
+ expectedRemainingTripples.add(Triple.create(nodeC, NodeFactory.createURI("loves"), nodeA));
+ assertEquals( expectedRemainingTripples, ntS.iterateAll().toSet() );
+ }
+
public void testIteratorWIthPatternOnEmpty()
{
assertEquals( tripleSet( "" ), ntS.iterateAll( triple( "a P b" ) ).toSet() );
@@ -207,7 +249,7 @@
// TODO more here
- protected void addTriples( NodeToTriplesMap nt, String facts )
+ protected void addTriples( NodeToTriplesMapMem nt, String facts )
{
Triple [] t = tripleArray( facts );
for ( Triple aT : t )
diff --git a/jena-core/src/test/java/org/apache/jena/graph/test/TestPackage_graph.java b/jena-core/src/test/java/org/apache/jena/graph/test/TestPackage_graph.java
index b52dcd9..2a8c2b9 100644
--- a/jena-core/src/test/java/org/apache/jena/graph/test/TestPackage_graph.java
+++ b/jena-core/src/test/java/org/apache/jena/graph/test/TestPackage_graph.java
@@ -41,7 +41,7 @@
addTest( TestNode.suite() );
addTest( TestTriple.suite() );
addTest( TestTripleField.suite() );
- addTest( TestNodeToTriplesMap.suite() );
+ addTest( TestNodeToTriplesMapMem.suite() );
addTest( TestReifier.suite() );
addTest( TestTypedLiterals.suite() );
addTest( TestDateTime.suite() );
diff --git a/jena-core/src/test/java/org/apache/jena/graph/test/TestTripleField.java b/jena-core/src/test/java/org/apache/jena/graph/test/TestTripleField.java
index 46d3b6d..d63e0df 100644
--- a/jena-core/src/test/java/org/apache/jena/graph/test/TestTripleField.java
+++ b/jena-core/src/test/java/org/apache/jena/graph/test/TestTripleField.java
@@ -69,7 +69,25 @@
assertTrue( Field.fieldPredicate.filterOn( node( "P" ) ).test( triple( "a P b" ) ) );
assertFalse( Field.fieldPredicate.filterOn( node( "Q" ) ).test( triple( "a P b" ) ) );
}
-
+
+ public void testFilterOnConcreteSubject()
+ {
+ assertTrue( Field.fieldSubject.filterOnConcrete( node( "a" ) ).test( triple( "a P b" ) ) );
+ assertFalse( Field.fieldSubject.filterOnConcrete( node( "x" ) ).test( triple( "a P b" ) ) );
+ }
+
+ public void testFilterOnConcreteObject()
+ {
+ assertTrue( Field.fieldObject.filterOnConcrete( node( "b" ) ).test( triple( "a P b" ) ) );
+ assertFalse( Field.fieldObject.filterOnConcrete( node( "c" ) ).test( triple( "a P b" ) ) );
+ }
+
+ public void testFilterOnConcretePredicate()
+ {
+ assertTrue( Field.fieldPredicate.filterOnConcrete( node( "P" ) ).test( triple( "a P b" ) ) );
+ assertFalse( Field.fieldPredicate.filterOnConcrete( node( "Q" ) ).test( triple( "a P b" ) ) );
+ }
+
public void testFilterByTriple()
{
assertTrue( Field.fieldSubject.filterOn( triple( "s P o" ) ).test( triple( "s Q p" ) ) );
diff --git a/jena-core/src/test/java/org/apache/jena/mem/FieldFilterTest.java b/jena-core/src/test/java/org/apache/jena/mem/FieldFilterTest.java
new file mode 100644
index 0000000..0b4ded2
--- /dev/null
+++ b/jena-core/src/test/java/org/apache/jena/mem/FieldFilterTest.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jena.mem;
+
+import org.apache.jena.graph.Node;
+import org.apache.jena.graph.Triple;
+import org.junit.Test;
+
+import static org.apache.jena.graph.test.GraphTestBase.node;
+import static org.apache.jena.graph.test.GraphTestBase.triple;
+import static org.junit.Assert.*;
+
+public class FieldFilterTest {
+
+ @Test
+ public void filterOnTwoNodes() {
+ var sut = FieldFilter.filterOn(Triple.Field.fieldSubject, node("a"), Triple.Field.fieldPredicate, node("P"));
+ assertTrue(sut.hasFilter());
+ var filter = sut.getFilter();
+ assertTrue(filter.test(triple( "a P b" )));
+ assertFalse(filter.test(triple( "c P b" )));
+ assertFalse(filter.test(triple( "a Q b" )));
+ }
+
+ @Test
+ public void filterOnFirstNode() {
+ var sut = FieldFilter.filterOn(Triple.Field.fieldSubject, node("a"), Triple.Field.fieldPredicate, Node.ANY);
+ assertTrue(sut.hasFilter());
+ var filter = sut.getFilter();
+ assertTrue(filter.test(triple( "a P b" )));
+ assertFalse(filter.test(triple( "c P b" )));
+ assertTrue(filter.test(triple( "a Q b" )));
+ }
+
+ @Test
+ public void filterOnSecondNode() {
+ var sut = FieldFilter.filterOn(Triple.Field.fieldSubject, Node.ANY, Triple.Field.fieldPredicate, node("P"));
+ assertTrue(sut.hasFilter());
+ var filter = sut.getFilter();
+ assertTrue(filter.test(triple( "a P b" )));
+ assertTrue(filter.test(triple( "c P b" )));
+ assertFalse(filter.test(triple( "a Q b" )));
+ }
+
+ @Test
+ public void filterOnAny() {
+ var sut = FieldFilter.filterOn(Triple.Field.fieldSubject, Node.ANY, Triple.Field.fieldPredicate, Node.ANY);
+ assertFalse(sut.hasFilter());
+ assertNull(sut.getFilter());
+ }
+}
\ No newline at end of file
diff --git a/jena-core/src/test/java/org/apache/jena/mem/GraphTripleStoreMem_CS.java b/jena-core/src/test/java/org/apache/jena/mem/GraphTripleStoreMem_CS.java
index 2768dd3..cda2f88 100644
--- a/jena-core/src/test/java/org/apache/jena/mem/GraphTripleStoreMem_CS.java
+++ b/jena-core/src/test/java/org/apache/jena/mem/GraphTripleStoreMem_CS.java
@@ -27,7 +27,7 @@
import org.xenei.junit.contract.IProducer;
@RunWith(ContractSuite.class)
-@ContractImpl(GraphTripleStore.class)
+@ContractImpl(GraphTripleStoreMem.class)
public class GraphTripleStoreMem_CS {
private IProducer<GraphTripleStoreMem> producer = new IProducer<GraphTripleStoreMem>() {
diff --git a/jena-core/src/test/java/org/apache/jena/mem/GraphTripleStore_CS.java b/jena-core/src/test/java/org/apache/jena/mem/GraphTripleStore_CS.java
deleted file mode 100644
index 96cad99..0000000
--- a/jena-core/src/test/java/org/apache/jena/mem/GraphTripleStore_CS.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.jena.mem;
-
-import org.junit.runner.RunWith;
-import org.xenei.junit.contract.Contract;
-import org.xenei.junit.contract.ContractImpl;
-import org.xenei.junit.contract.ContractSuite;
-
-import org.apache.jena.graph.Graph;
-import org.xenei.junit.contract.IProducer;
-
-@RunWith(ContractSuite.class)
-@ContractImpl(GraphTripleStore.class)
-public class GraphTripleStore_CS {
-
- private IProducer<GraphTripleStore> producer = new IProducer<GraphTripleStore>() {
-
- @Override
- public GraphTripleStore newInstance() {
- return new GraphTripleStore(Graph.emptyGraph);
- }
-
- @Override
- public void cleanUp() {
- }
-
- };
-
- @Contract.Inject
- public IProducer<GraphTripleStore> getTripleStore() {
- return producer;
- }
-}
diff --git a/jena-core/src/test/java/org/apache/jena/mem/HashedBunchMap_CS.java b/jena-core/src/test/java/org/apache/jena/mem/HashedBunchMap_CS.java
index cb64b57..20843e8 100644
--- a/jena-core/src/test/java/org/apache/jena/mem/HashedBunchMap_CS.java
+++ b/jena-core/src/test/java/org/apache/jena/mem/HashedBunchMap_CS.java
@@ -24,7 +24,7 @@
import org.xenei.junit.contract.IProducer;
@RunWith(ContractSuite.class)
-@ContractImpl(WrappedHashMap.class)
+@ContractImpl(HashedBunchMap.class)
public class HashedBunchMap_CS {
protected IProducer<HashedBunchMap> mapProducer = new IProducer<HashedBunchMap>() {
diff --git a/jena-core/src/test/java/org/apache/jena/mem/SetBunch_CS.java b/jena-core/src/test/java/org/apache/jena/mem/SetBunch_CS.java
deleted file mode 100644
index b6b1f69..0000000
--- a/jena-core/src/test/java/org/apache/jena/mem/SetBunch_CS.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- Licensed to the Apache Software Foundation (ASF) under one
- or more contributor license agreements. See the NOTICE file
- distributed with this work for additional information
- regarding copyright ownership. The ASF licenses this file
- to you under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance
- with the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
-package org.apache.jena.mem;
-
-import org.junit.runner.RunWith;
-import org.xenei.junit.contract.Contract.Inject;
-import org.xenei.junit.contract.ContractImpl;
-import org.xenei.junit.contract.ContractSuite;
-import org.xenei.junit.contract.IProducer;
-
-@RunWith(ContractSuite.class)
-@ContractImpl(SetBunch.class)
-public class SetBunch_CS {
-
- protected IProducer<SetBunch> mapProducer = new IProducer<SetBunch>() {
-
- @Override
- public SetBunch newInstance() {
- return new SetBunch( new ArrayBunch() );
- }
-
- @Override
- public void cleanUp() {
- // nothing to do
- }
-
- };
-
- @Inject
- public IProducer<SetBunch> getGraphProducer() {
- return mapProducer;
- }
-
-
-}
diff --git a/jena-core/src/test/java/org/apache/jena/mem/SparseArraySpliteratorTest.java b/jena-core/src/test/java/org/apache/jena/mem/SparseArraySpliteratorTest.java
new file mode 100644
index 0000000..8d62e25
--- /dev/null
+++ b/jena-core/src/test/java/org/apache/jena/mem/SparseArraySpliteratorTest.java
@@ -0,0 +1,527 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jena.mem;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Spliterator;
+
+import static org.junit.Assert.*;
+
+public class SparseArraySpliteratorTest {
+
+ @Test
+ public void tryAdvanceEmpty() {
+ {
+ Integer[] array = new Integer[0];
+ Spliterator<Integer> spliterator = new SparseArraySpliterator<>(array, 0, () -> {
+ });
+ assertFalse(spliterator.tryAdvance((i) -> {
+ fail("Should not have advanced");
+ }));
+ }
+ {
+ Integer[] array = new Integer[1];
+ Spliterator<Integer> spliterator = new SparseArraySpliterator<>(array, 0, () -> {
+ });
+ assertFalse(spliterator.tryAdvance((i) -> {
+ fail("Should not have advanced");
+ }));
+ }
+ }
+
+ @Test
+ public void tryAdvanceOne() {
+ {
+ Integer[] array = new Integer[] { 1 };
+ Spliterator<Integer> spliterator = new SparseArraySpliterator<>(array, 1, () -> {
+ });
+ var itemsFound = new ArrayList<>();
+ while (spliterator.tryAdvance((i) -> {
+ itemsFound.add(1);
+ })) ;
+ assertEquals(1, itemsFound.size());
+ itemsFound.contains(1);
+ }
+ {
+ Integer[] array = new Integer[]{ 1 , null };
+ Spliterator<Integer> spliterator = new SparseArraySpliterator<>(array, 1, () -> {
+ });
+ var itemsFound = new ArrayList<>();
+ while (spliterator.tryAdvance((i) -> {
+ itemsFound.add(1);
+ })) ;
+ assertEquals(1, itemsFound.size());
+ itemsFound.contains(1);
+ }
+ {
+ Integer[] array = new Integer[]{ null , 1 };
+ Spliterator<Integer> spliterator = new SparseArraySpliterator<>(array, 1, () -> {
+ });
+ var itemsFound = new ArrayList<>();
+ while (spliterator.tryAdvance((i) -> {
+ itemsFound.add(1);
+ })) ;
+ assertEquals(1, itemsFound.size());
+ itemsFound.contains(1);
+ }
+ }
+
+ @Test
+ public void tryAdvanceTwo() {
+ {
+ Integer[] array = new Integer[]{ 1 , 2 };
+ Spliterator<Integer> spliterator = new SparseArraySpliterator<>(array, 2, () -> {
+ });
+ var itemsFound = new ArrayList<>();
+ while (spliterator.tryAdvance((i) -> {
+ itemsFound.add(i);
+ })) ;
+ assertEquals(2, itemsFound.size());
+ itemsFound.contains(1);
+ itemsFound.contains(2);
+ }
+ {
+ Integer[] array = new Integer[]{ 1 , null , 2 };
+ Spliterator<Integer> spliterator = new SparseArraySpliterator<>(array, 2, () -> {
+ });
+ var itemsFound = new ArrayList<>();
+ while (spliterator.tryAdvance((i) -> {
+ itemsFound.add(i);
+ })) ;
+ assertEquals(2, itemsFound.size());
+ itemsFound.contains(1);
+ itemsFound.contains(2);
+ }
+ {
+ Integer[] array = new Integer[]{ 1 , null , null , 2 };
+ Spliterator<Integer> spliterator = new SparseArraySpliterator<>(array, 2, () -> {
+ });
+ var itemsFound = new ArrayList<>();
+ while (spliterator.tryAdvance((i) -> {
+ itemsFound.add(i);
+ })) ;
+ assertEquals(2, itemsFound.size());
+ itemsFound.contains(1);
+ itemsFound.contains(2);
+ }
+ {
+ Integer[] array = new Integer[]{ null , 1 , null , 2 };
+ Spliterator<Integer> spliterator = new SparseArraySpliterator<>(array, 2, () -> {
+ });
+ var itemsFound = new ArrayList<>();
+ while (spliterator.tryAdvance((i) -> {
+ itemsFound.add(i);
+ })) ;
+ assertEquals(2, itemsFound.size());
+ itemsFound.contains(1);
+ itemsFound.contains(2);
+ }
+ {
+ Integer[] array = new Integer[]{ null , 1 , null , null , 2 };
+ Spliterator<Integer> spliterator = new SparseArraySpliterator<>(array, 2, () -> {
+ });
+ var itemsFound = new ArrayList<>();
+ while (spliterator.tryAdvance((i) -> {
+ itemsFound.add(i);
+ })) ;
+ assertEquals(2, itemsFound.size());
+ itemsFound.contains(1);
+ itemsFound.contains(2);
+ }
+ }
+
+ @Test
+ public void tryAdvanceThree() {
+ {
+ Integer[] array = new Integer[]{ 1 , 2 , 3 };
+ Spliterator<Integer> spliterator = new SparseArraySpliterator<>(array, 3, () -> {
+ });
+ var itemsFound = new ArrayList<>();
+ while (spliterator.tryAdvance((i) -> {
+ itemsFound.add(i);
+ })) ;
+ assertEquals(3, itemsFound.size());
+ itemsFound.contains(1);
+ itemsFound.contains(2);
+ itemsFound.contains(3);
+ }
+ {
+ Integer[] array = new Integer[]{ 1 , null , 2 , 3 };
+ Spliterator<Integer> spliterator = new SparseArraySpliterator<>(array, 3, () -> {
+ });
+ var itemsFound = new ArrayList<>();
+ while (spliterator.tryAdvance((i) -> {
+ itemsFound.add(i);
+ })) ;
+ assertEquals(3, itemsFound.size());
+ itemsFound.contains(1);
+ itemsFound.contains(2);
+ itemsFound.contains(3);
+ }
+ {
+ Integer[] array = new Integer[]{ 1 , null , null , 2 , 3 };
+ Spliterator<Integer> spliterator = new SparseArraySpliterator<>(array, 3, () -> {
+ });
+ var itemsFound = new ArrayList<>();
+ while (spliterator.tryAdvance((i) -> {
+ itemsFound.add(i);
+ })) ;
+ assertEquals(3, itemsFound.size());
+ itemsFound.contains(1);
+ itemsFound.contains(2);
+ itemsFound.contains(3);
+ }
+ {
+ Integer[] array = new Integer[]{ null , 1 , null , 2 , null , 3 };
+ Spliterator<Integer> spliterator = new SparseArraySpliterator<>(array, 3, () -> {
+ });
+ var itemsFound = new ArrayList<>();
+ while (spliterator.tryAdvance((i) -> {
+ itemsFound.add(i);
+ })) ;
+ assertEquals(3, itemsFound.size());
+ itemsFound.contains(1);
+ itemsFound.contains(2);
+ itemsFound.contains(3);
+ }
+ {
+ Integer[] array = new Integer[]{null, 1, null, null, 2, null, 3};
+ Spliterator<Integer> spliterator = new SparseArraySpliterator<>(array, 3, () -> {
+ });
+ var itemsFound = new ArrayList<>();
+ while (spliterator.tryAdvance((i) -> {
+ itemsFound.add(i);
+ })) ;
+ assertEquals(3, itemsFound.size());
+ itemsFound.contains(1);
+ itemsFound.contains(2);
+ itemsFound.contains(3);
+ }
+ }
+
+ @Test
+ public void forEachRemainingEmpty() {
+ {
+ Integer[] array = new Integer[]{};
+ Spliterator<Integer> spliterator = new SparseArraySpliterator<>(array, 1, () -> {
+ });
+ var itemsFound = new ArrayList<>();
+ spliterator.forEachRemaining((i) -> {
+ itemsFound.add(i);
+ });
+ assertEquals(0, itemsFound.size());
+ }
+ {
+ Integer[] array = new Integer[]{ null };
+ Spliterator<Integer> spliterator = new SparseArraySpliterator<>(array, 1, () -> {
+ });
+ var itemsFound = new ArrayList<>();
+ spliterator.forEachRemaining((i) -> {
+ itemsFound.add(i);
+ });
+ assertEquals(0, itemsFound.size());
+ }
+ }
+
+ @Test
+ public void forEachRemainingOne() {
+ {
+ Integer[] array = new Integer[]{ 1 };
+ Spliterator<Integer> spliterator = new SparseArraySpliterator<>(array, 1, () -> {
+ });
+ var itemsFound = new ArrayList<>();
+ spliterator.forEachRemaining((i) -> {
+ itemsFound.add(i);
+ });
+ assertEquals(1, itemsFound.size());
+ itemsFound.contains(1);
+ }
+ {
+ Integer[] array = new Integer[]{ null , 1 };
+ Spliterator<Integer> spliterator = new SparseArraySpliterator<>(array, 1, () -> {
+ });
+ var itemsFound = new ArrayList<>();
+ spliterator.forEachRemaining((i) -> {
+ itemsFound.add(i);
+ });
+ assertEquals(1, itemsFound.size());
+ itemsFound.contains(1);
+ }
+ {
+ Integer[] array = new Integer[]{ 1 , null };
+ Spliterator<Integer> spliterator = new SparseArraySpliterator<>(array, 1, () -> {
+ });
+ var itemsFound = new ArrayList<>();
+ spliterator.forEachRemaining((i) -> {
+ itemsFound.add(i);
+ });
+ assertEquals(1, itemsFound.size());
+ itemsFound.contains(1);
+ }
+ }
+
+ @Test
+ public void forEachRemainingTwo() {
+ {
+ Integer[] array = new Integer[]{ 1 , 2 };
+ Spliterator<Integer> spliterator = new SparseArraySpliterator<>(array, 2, () -> {
+ });
+ var itemsFound = new ArrayList<>();
+ spliterator.forEachRemaining((i) -> {
+ itemsFound.add(i);
+ });
+ assertEquals(2, itemsFound.size());
+ itemsFound.contains(1);
+ itemsFound.contains(2);
+ }
+ {
+ Integer[] array = new Integer[]{ 1 , null , 2 };
+ Spliterator<Integer> spliterator = new SparseArraySpliterator<>(array, 2, () -> {
+ });
+ var itemsFound = new ArrayList<>();
+ spliterator.forEachRemaining((i) -> {
+ itemsFound.add(i);
+ });
+ assertEquals(2, itemsFound.size());
+ itemsFound.contains(1);
+ itemsFound.contains(2);
+ }
+ {
+ Integer[] array = new Integer[]{ 1 , null , null , 2 };
+ Spliterator<Integer> spliterator = new SparseArraySpliterator<>(array, 2, () -> {
+ });
+ var itemsFound = new ArrayList<>();
+ spliterator.forEachRemaining((i) -> {
+ itemsFound.add(i);
+ });
+ assertEquals(2, itemsFound.size());
+ itemsFound.contains(1);
+ itemsFound.contains(2);
+ }
+ {
+ Integer[] array = new Integer[]{ null , 1 , null , 2 };
+ Spliterator<Integer> spliterator = new SparseArraySpliterator<>(array, 2, () -> {
+ });
+ var itemsFound = new ArrayList<>();
+ spliterator.forEachRemaining((i) -> {
+ itemsFound.add(i);
+ });
+ assertEquals(2, itemsFound.size());
+ itemsFound.contains(1);
+ itemsFound.contains(2);
+ }
+ {
+ Integer[] array = new Integer[]{ null , 1 , null , null , 2 };
+ Spliterator<Integer> spliterator = new SparseArraySpliterator<>(array, 2, () -> {
+ });
+ var itemsFound = new ArrayList<>();
+ spliterator.forEachRemaining((i) -> {
+ itemsFound.add(i);
+ });
+ assertEquals(2, itemsFound.size());
+ itemsFound.contains(1);
+ itemsFound.contains(2);
+ }
+ }
+
+ @Test
+ public void forEachRemainingThree(){
+ {
+ Integer[] array = new Integer[]{ 1 , 2 , 3 };
+ Spliterator<Integer> spliterator = new SparseArraySpliterator<>(array, 3, () -> {
+ });
+ var itemsFound = new ArrayList<>();
+ spliterator.forEachRemaining((i) -> {
+ itemsFound.add(i);
+ });
+ assertEquals(3, itemsFound.size());
+ itemsFound.contains(1);
+ itemsFound.contains(2);
+ itemsFound.contains(3);
+ }
+ {
+ Integer[] array = new Integer[]{ 1 , null , 2 , 3 };
+ Spliterator<Integer> spliterator = new SparseArraySpliterator<>(array, 3, () -> {
+ });
+ var itemsFound = new ArrayList<>();
+ spliterator.forEachRemaining((i) -> {
+ itemsFound.add(i);
+ });
+ assertEquals(3, itemsFound.size());
+ itemsFound.contains(1);
+ itemsFound.contains(2);
+ itemsFound.contains(3);
+ }
+ {
+ Integer[] array = new Integer[]{ 1 , null , null , 2 , 3 };
+ Spliterator<Integer> spliterator = new SparseArraySpliterator<>(array, 3, () -> {
+ });
+ var itemsFound = new ArrayList<>();
+ spliterator.forEachRemaining((i) -> {
+ itemsFound.add(i);
+ });
+ assertEquals(3, itemsFound.size());
+ itemsFound.contains(1);
+ itemsFound.contains(2);
+ itemsFound.contains(3);
+ }
+ {
+ Integer[] array = new Integer[]{ null , 1 , null , 2 , 3 };
+ Spliterator<Integer> spliterator = new SparseArraySpliterator<>(array, 3, () -> {
+ });
+ var itemsFound = new ArrayList<>();
+ spliterator.forEachRemaining((i) -> {
+ itemsFound.add(i);
+ });
+ assertEquals(3, itemsFound.size());
+ itemsFound.contains(1);
+ itemsFound.contains(2);
+ itemsFound.contains(3);
+ }
+ {
+ Integer[] array = new Integer[]{null, 1, null, null, 2, 3};
+ Spliterator<Integer> spliterator = new SparseArraySpliterator<>(array, 3, () -> {
+ });
+ var itemsFound = new ArrayList<>();
+ spliterator.forEachRemaining((i) -> {
+ itemsFound.add(i);
+ });
+ assertEquals(3, itemsFound.size());
+ }
+ }
+
+ @Test
+ public void trySplitEmpty() {
+ Integer[] array = new Integer[]{};
+ Spliterator<Integer> spliterator = new SparseArraySpliterator<>(array, 1, () -> {
+ });
+ assertNull(spliterator.trySplit());
+ }
+
+ @Test
+ public void trySplitOne() {
+ Integer[] array = new Integer[]{ 1 };
+ Spliterator<Integer> spliterator = new SparseArraySpliterator<>(array, 1, () -> {
+ });
+ assertNull(spliterator.trySplit());
+ }
+
+ @Test
+ public void trySplitTwo() {
+ Integer[] array = new Integer[]{ 1 , 2 };
+ Spliterator<Integer> spliterator = new SparseArraySpliterator<>(array, 2, () -> {
+ });
+ // Estimated size is not exact
+ assertBetween(2, 3, spliterator.estimateSize());
+ Spliterator<Integer> split = spliterator.trySplit();
+ assertBetween(1, 2, spliterator.estimateSize());
+ assertBetween(1, 3, split.estimateSize());
+ }
+
+ @Test
+ public void trySplitThree() {
+ Integer[] array = new Integer[]{ 1 , 2 , 3 };
+ Spliterator<Integer> spliterator = new SparseArraySpliterator<>(array, 3, () -> {
+ });
+ // Estimated size is not exact
+ assertBetween(3, 4, spliterator.estimateSize());
+ Spliterator<Integer> split = spliterator.trySplit();
+ assertBetween(1, 2, spliterator.estimateSize());
+ assertBetween(2, 3, split.estimateSize());
+ }
+
+ @Test
+ public void trySplitFour() {
+ Integer[] array = new Integer[]{ 1 , 2 , 3 , 4 };
+ Spliterator<Integer> spliterator = new SparseArraySpliterator<>(array, 4, () -> {
+ });
+ // Estimated size is not exact
+ assertBetween(4, 5, spliterator.estimateSize());
+ Spliterator<Integer> split = spliterator.trySplit();
+ assertBetween(2, 3, spliterator.estimateSize());
+ assertBetween(2, 4, split.estimateSize());
+ }
+
+ @Test
+ public void trySplitFive() {
+ Integer[] array = new Integer[]{ 1 , 2 , 3 , 4 , 5 };
+ Spliterator<Integer> spliterator = new SparseArraySpliterator<>(array, 5, () -> {
+ });
+ // Estimated size is not exact
+ assertBetween(5, 6, spliterator.estimateSize());
+ Spliterator<Integer> split = spliterator.trySplit();
+ assertBetween(2, 3, spliterator.estimateSize());
+ assertBetween(3, 4, split.estimateSize());
+ }
+
+ @Test
+ public void trySplitOneHundred() {
+ Integer[] array = new Integer[200];
+ for (int i = 0; i < array.length; i++) {
+ if(i % 2 == 0) {
+ array[i] = i;
+ }
+ }
+ Spliterator<Integer> spliterator = new SparseArraySpliterator<>(array, 100, () -> {
+ });
+ // Estimated size is not exact
+ assertBetween(100, 101, spliterator.estimateSize());
+ Spliterator<Integer> split = spliterator.trySplit();
+ assertBetween(50, 51, spliterator.estimateSize());
+ assertBetween(50, 51, split.estimateSize());
+ }
+
+ private void assertBetween(long min, long max, long estimateSize) {
+ assertTrue("estimateSize=" + estimateSize + " min=" + min + " max=" + max, estimateSize >= min);
+ assertTrue("estimateSize=" + estimateSize + " min=" + min + " max=" + max, estimateSize <= max);
+ }
+
+ @Test
+ public void estimateSizeZero() {
+ Integer[] array = new Integer[]{};
+ Spliterator<Integer> spliterator = new SparseArraySpliterator<>(array, 0, () -> {
+ });
+ assertBetween(0, 1, spliterator.estimateSize());
+ }
+
+ @Test
+ public void estimateSizeOne() {
+ Integer[] array = new Integer[]{ 1 };
+ Spliterator<Integer> spliterator = new SparseArraySpliterator<>(array, 1, () -> {
+ });
+ assertBetween(1, 2, spliterator.estimateSize());
+ }
+
+ @Test
+ public void estimateSizeTwo() {
+ Integer[] array = new Integer[]{ 1 , 2 };
+ Spliterator<Integer> spliterator = new SparseArraySpliterator<>(array, 2, () -> {
+ });
+ assertBetween(2, 3, spliterator.estimateSize());
+ }
+
+ @Test
+ public void estimateSizeFive() {
+ Integer[] array = new Integer[]{ 1 , 2 , 3 , 4 , 5 };
+ Spliterator<Integer> spliterator = new SparseArraySpliterator<>(array, 5, () -> {
+ });
+ assertBetween(5, 6, spliterator.estimateSize());
+ }
+}
\ No newline at end of file
diff --git a/jena-core/src/test/java/org/apache/jena/mem/SparseArraySubSpliteratorTest.java b/jena-core/src/test/java/org/apache/jena/mem/SparseArraySubSpliteratorTest.java
new file mode 100644
index 0000000..4a48e84
--- /dev/null
+++ b/jena-core/src/test/java/org/apache/jena/mem/SparseArraySubSpliteratorTest.java
@@ -0,0 +1,527 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jena.mem;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Spliterator;
+
+import static org.junit.Assert.*;
+
+public class SparseArraySubSpliteratorTest {
+
+ @Test
+ public void tryAdvanceEmpty() {
+ {
+ Integer[] array = new Integer[0];
+ Spliterator<Integer> spliterator = new SparseArraySubSpliterator<>(array, 0, () -> {
+ });
+ assertFalse(spliterator.tryAdvance((i) -> {
+ fail("Should not have advanced");
+ }));
+ }
+ {
+ Integer[] array = new Integer[1];
+ Spliterator<Integer> spliterator = new SparseArraySubSpliterator<>(array, 0, () -> {
+ });
+ assertFalse(spliterator.tryAdvance((i) -> {
+ fail("Should not have advanced");
+ }));
+ }
+ }
+
+ @Test
+ public void tryAdvanceOne() {
+ {
+ Integer[] array = new Integer[] { 1 };
+ Spliterator<Integer> spliterator = new SparseArraySubSpliterator<>(array, 1, () -> {
+ });
+ var itemsFound = new ArrayList<>();
+ while (spliterator.tryAdvance((i) -> {
+ itemsFound.add(1);
+ })) ;
+ assertEquals(1, itemsFound.size());
+ itemsFound.contains(1);
+ }
+ {
+ Integer[] array = new Integer[]{ 1 , null };
+ Spliterator<Integer> spliterator = new SparseArraySubSpliterator<>(array, 1, () -> {
+ });
+ var itemsFound = new ArrayList<>();
+ while (spliterator.tryAdvance((i) -> {
+ itemsFound.add(1);
+ })) ;
+ assertEquals(1, itemsFound.size());
+ itemsFound.contains(1);
+ }
+ {
+ Integer[] array = new Integer[]{ null , 1 };
+ Spliterator<Integer> spliterator = new SparseArraySubSpliterator<>(array, 1, () -> {
+ });
+ var itemsFound = new ArrayList<>();
+ while (spliterator.tryAdvance((i) -> {
+ itemsFound.add(1);
+ })) ;
+ assertEquals(1, itemsFound.size());
+ itemsFound.contains(1);
+ }
+ }
+
+ @Test
+ public void tryAdvanceTwo() {
+ {
+ Integer[] array = new Integer[]{ 1 , 2 };
+ Spliterator<Integer> spliterator = new SparseArraySubSpliterator<>(array, 2, () -> {
+ });
+ var itemsFound = new ArrayList<>();
+ while (spliterator.tryAdvance((i) -> {
+ itemsFound.add(i);
+ })) ;
+ assertEquals(2, itemsFound.size());
+ itemsFound.contains(1);
+ itemsFound.contains(2);
+ }
+ {
+ Integer[] array = new Integer[]{ 1 , null , 2 };
+ Spliterator<Integer> spliterator = new SparseArraySubSpliterator<>(array, 2, () -> {
+ });
+ var itemsFound = new ArrayList<>();
+ while (spliterator.tryAdvance((i) -> {
+ itemsFound.add(i);
+ })) ;
+ assertEquals(2, itemsFound.size());
+ itemsFound.contains(1);
+ itemsFound.contains(2);
+ }
+ {
+ Integer[] array = new Integer[]{ 1 , null , null , 2 };
+ Spliterator<Integer> spliterator = new SparseArraySubSpliterator<>(array, 2, () -> {
+ });
+ var itemsFound = new ArrayList<>();
+ while (spliterator.tryAdvance((i) -> {
+ itemsFound.add(i);
+ })) ;
+ assertEquals(2, itemsFound.size());
+ itemsFound.contains(1);
+ itemsFound.contains(2);
+ }
+ {
+ Integer[] array = new Integer[]{ null , 1 , null , 2 };
+ Spliterator<Integer> spliterator = new SparseArraySubSpliterator<>(array, 2, () -> {
+ });
+ var itemsFound = new ArrayList<>();
+ while (spliterator.tryAdvance((i) -> {
+ itemsFound.add(i);
+ })) ;
+ assertEquals(2, itemsFound.size());
+ itemsFound.contains(1);
+ itemsFound.contains(2);
+ }
+ {
+ Integer[] array = new Integer[]{ null , 1 , null , null , 2 };
+ Spliterator<Integer> spliterator = new SparseArraySubSpliterator<>(array, 2, () -> {
+ });
+ var itemsFound = new ArrayList<>();
+ while (spliterator.tryAdvance((i) -> {
+ itemsFound.add(i);
+ })) ;
+ assertEquals(2, itemsFound.size());
+ itemsFound.contains(1);
+ itemsFound.contains(2);
+ }
+ }
+
+ @Test
+ public void tryAdvanceThree() {
+ {
+ Integer[] array = new Integer[]{ 1 , 2 , 3 };
+ Spliterator<Integer> spliterator = new SparseArraySubSpliterator<>(array, 3, () -> {
+ });
+ var itemsFound = new ArrayList<>();
+ while (spliterator.tryAdvance((i) -> {
+ itemsFound.add(i);
+ })) ;
+ assertEquals(3, itemsFound.size());
+ itemsFound.contains(1);
+ itemsFound.contains(2);
+ itemsFound.contains(3);
+ }
+ {
+ Integer[] array = new Integer[]{ 1 , null , 2 , 3 };
+ Spliterator<Integer> spliterator = new SparseArraySubSpliterator<>(array, 3, () -> {
+ });
+ var itemsFound = new ArrayList<>();
+ while (spliterator.tryAdvance((i) -> {
+ itemsFound.add(i);
+ })) ;
+ assertEquals(3, itemsFound.size());
+ itemsFound.contains(1);
+ itemsFound.contains(2);
+ itemsFound.contains(3);
+ }
+ {
+ Integer[] array = new Integer[]{ 1 , null , null , 2 , 3 };
+ Spliterator<Integer> spliterator = new SparseArraySubSpliterator<>(array, 3, () -> {
+ });
+ var itemsFound = new ArrayList<>();
+ while (spliterator.tryAdvance((i) -> {
+ itemsFound.add(i);
+ })) ;
+ assertEquals(3, itemsFound.size());
+ itemsFound.contains(1);
+ itemsFound.contains(2);
+ itemsFound.contains(3);
+ }
+ {
+ Integer[] array = new Integer[]{ null , 1 , null , 2 , null , 3 };
+ Spliterator<Integer> spliterator = new SparseArraySubSpliterator<>(array, 3, () -> {
+ });
+ var itemsFound = new ArrayList<>();
+ while (spliterator.tryAdvance((i) -> {
+ itemsFound.add(i);
+ })) ;
+ assertEquals(3, itemsFound.size());
+ itemsFound.contains(1);
+ itemsFound.contains(2);
+ itemsFound.contains(3);
+ }
+ {
+ Integer[] array = new Integer[]{null, 1, null, null, 2, null, 3};
+ Spliterator<Integer> spliterator = new SparseArraySubSpliterator<>(array, 3, () -> {
+ });
+ var itemsFound = new ArrayList<>();
+ while (spliterator.tryAdvance((i) -> {
+ itemsFound.add(i);
+ })) ;
+ assertEquals(3, itemsFound.size());
+ itemsFound.contains(1);
+ itemsFound.contains(2);
+ itemsFound.contains(3);
+ }
+ }
+
+ @Test
+ public void forEachRemainingEmpty() {
+ {
+ Integer[] array = new Integer[]{};
+ Spliterator<Integer> spliterator = new SparseArraySubSpliterator<>(array, 1, () -> {
+ });
+ var itemsFound = new ArrayList<>();
+ spliterator.forEachRemaining((i) -> {
+ itemsFound.add(i);
+ });
+ assertEquals(0, itemsFound.size());
+ }
+ {
+ Integer[] array = new Integer[]{ null };
+ Spliterator<Integer> spliterator = new SparseArraySubSpliterator<>(array, 1, () -> {
+ });
+ var itemsFound = new ArrayList<>();
+ spliterator.forEachRemaining((i) -> {
+ itemsFound.add(i);
+ });
+ assertEquals(0, itemsFound.size());
+ }
+ }
+
+ @Test
+ public void forEachRemainingOne() {
+ {
+ Integer[] array = new Integer[]{ 1 };
+ Spliterator<Integer> spliterator = new SparseArraySubSpliterator<>(array, 1, () -> {
+ });
+ var itemsFound = new ArrayList<>();
+ spliterator.forEachRemaining((i) -> {
+ itemsFound.add(i);
+ });
+ assertEquals(1, itemsFound.size());
+ itemsFound.contains(1);
+ }
+ {
+ Integer[] array = new Integer[]{ null , 1 };
+ Spliterator<Integer> spliterator = new SparseArraySubSpliterator<>(array, 1, () -> {
+ });
+ var itemsFound = new ArrayList<>();
+ spliterator.forEachRemaining((i) -> {
+ itemsFound.add(i);
+ });
+ assertEquals(1, itemsFound.size());
+ itemsFound.contains(1);
+ }
+ {
+ Integer[] array = new Integer[]{ 1 , null };
+ Spliterator<Integer> spliterator = new SparseArraySubSpliterator<>(array, 1, () -> {
+ });
+ var itemsFound = new ArrayList<>();
+ spliterator.forEachRemaining((i) -> {
+ itemsFound.add(i);
+ });
+ assertEquals(1, itemsFound.size());
+ itemsFound.contains(1);
+ }
+ }
+
+ @Test
+ public void forEachRemainingTwo() {
+ {
+ Integer[] array = new Integer[]{ 1 , 2 };
+ Spliterator<Integer> spliterator = new SparseArraySubSpliterator<>(array, 2, () -> {
+ });
+ var itemsFound = new ArrayList<>();
+ spliterator.forEachRemaining((i) -> {
+ itemsFound.add(i);
+ });
+ assertEquals(2, itemsFound.size());
+ itemsFound.contains(1);
+ itemsFound.contains(2);
+ }
+ {
+ Integer[] array = new Integer[]{ 1 , null , 2 };
+ Spliterator<Integer> spliterator = new SparseArraySubSpliterator<>(array, 2, () -> {
+ });
+ var itemsFound = new ArrayList<>();
+ spliterator.forEachRemaining((i) -> {
+ itemsFound.add(i);
+ });
+ assertEquals(2, itemsFound.size());
+ itemsFound.contains(1);
+ itemsFound.contains(2);
+ }
+ {
+ Integer[] array = new Integer[]{ 1 , null , null , 2 };
+ Spliterator<Integer> spliterator = new SparseArraySubSpliterator<>(array, 2, () -> {
+ });
+ var itemsFound = new ArrayList<>();
+ spliterator.forEachRemaining((i) -> {
+ itemsFound.add(i);
+ });
+ assertEquals(2, itemsFound.size());
+ itemsFound.contains(1);
+ itemsFound.contains(2);
+ }
+ {
+ Integer[] array = new Integer[]{ null , 1 , null , 2 };
+ Spliterator<Integer> spliterator = new SparseArraySubSpliterator<>(array, 2, () -> {
+ });
+ var itemsFound = new ArrayList<>();
+ spliterator.forEachRemaining((i) -> {
+ itemsFound.add(i);
+ });
+ assertEquals(2, itemsFound.size());
+ itemsFound.contains(1);
+ itemsFound.contains(2);
+ }
+ {
+ Integer[] array = new Integer[]{ null , 1 , null , null , 2 };
+ Spliterator<Integer> spliterator = new SparseArraySubSpliterator<>(array, 2, () -> {
+ });
+ var itemsFound = new ArrayList<>();
+ spliterator.forEachRemaining((i) -> {
+ itemsFound.add(i);
+ });
+ assertEquals(2, itemsFound.size());
+ itemsFound.contains(1);
+ itemsFound.contains(2);
+ }
+ }
+
+ @Test
+ public void forEachRemainingThree(){
+ {
+ Integer[] array = new Integer[]{ 1 , 2 , 3 };
+ Spliterator<Integer> spliterator = new SparseArraySubSpliterator<>(array, 3, () -> {
+ });
+ var itemsFound = new ArrayList<>();
+ spliterator.forEachRemaining((i) -> {
+ itemsFound.add(i);
+ });
+ assertEquals(3, itemsFound.size());
+ itemsFound.contains(1);
+ itemsFound.contains(2);
+ itemsFound.contains(3);
+ }
+ {
+ Integer[] array = new Integer[]{ 1 , null , 2 , 3 };
+ Spliterator<Integer> spliterator = new SparseArraySubSpliterator<>(array, 3, () -> {
+ });
+ var itemsFound = new ArrayList<>();
+ spliterator.forEachRemaining((i) -> {
+ itemsFound.add(i);
+ });
+ assertEquals(3, itemsFound.size());
+ itemsFound.contains(1);
+ itemsFound.contains(2);
+ itemsFound.contains(3);
+ }
+ {
+ Integer[] array = new Integer[]{ 1 , null , null , 2 , 3 };
+ Spliterator<Integer> spliterator = new SparseArraySubSpliterator<>(array, 3, () -> {
+ });
+ var itemsFound = new ArrayList<>();
+ spliterator.forEachRemaining((i) -> {
+ itemsFound.add(i);
+ });
+ assertEquals(3, itemsFound.size());
+ itemsFound.contains(1);
+ itemsFound.contains(2);
+ itemsFound.contains(3);
+ }
+ {
+ Integer[] array = new Integer[]{ null , 1 , null , 2 , 3 };
+ Spliterator<Integer> spliterator = new SparseArraySubSpliterator<>(array, 3, () -> {
+ });
+ var itemsFound = new ArrayList<>();
+ spliterator.forEachRemaining((i) -> {
+ itemsFound.add(i);
+ });
+ assertEquals(3, itemsFound.size());
+ itemsFound.contains(1);
+ itemsFound.contains(2);
+ itemsFound.contains(3);
+ }
+ {
+ Integer[] array = new Integer[]{null, 1, null, null, 2, 3};
+ Spliterator<Integer> spliterator = new SparseArraySubSpliterator<>(array, 3, () -> {
+ });
+ var itemsFound = new ArrayList<>();
+ spliterator.forEachRemaining((i) -> {
+ itemsFound.add(i);
+ });
+ assertEquals(3, itemsFound.size());
+ }
+ }
+
+ @Test
+ public void trySplitEmpty() {
+ Integer[] array = new Integer[]{};
+ Spliterator<Integer> spliterator = new SparseArraySubSpliterator<>(array, 1, () -> {
+ });
+ assertNull(spliterator.trySplit());
+ }
+
+ @Test
+ public void trySplitOne() {
+ Integer[] array = new Integer[]{ 1 };
+ Spliterator<Integer> spliterator = new SparseArraySubSpliterator<>(array, 1, () -> {
+ });
+ assertNull(spliterator.trySplit());
+ }
+
+ @Test
+ public void trySplitTwo() {
+ Integer[] array = new Integer[]{ 1 , 2 };
+ Spliterator<Integer> spliterator = new SparseArraySubSpliterator<>(array, 2, () -> {
+ });
+ // Estimated size is not exact
+ assertBetween(2, 3, spliterator.estimateSize());
+ Spliterator<Integer> split = spliterator.trySplit();
+ assertBetween(1, 2, spliterator.estimateSize());
+ assertBetween(1, 3, split.estimateSize());
+ }
+
+ @Test
+ public void trySplitThree() {
+ Integer[] array = new Integer[]{ 1 , 2 , 3 };
+ Spliterator<Integer> spliterator = new SparseArraySubSpliterator<>(array, 3, () -> {
+ });
+ // Estimated size is not exact
+ assertBetween(3, 4, spliterator.estimateSize());
+ Spliterator<Integer> split = spliterator.trySplit();
+ assertBetween(1, 2, spliterator.estimateSize());
+ assertBetween(2, 3, split.estimateSize());
+ }
+
+ @Test
+ public void trySplitFour() {
+ Integer[] array = new Integer[]{ 1 , 2 , 3 , 4 };
+ Spliterator<Integer> spliterator = new SparseArraySubSpliterator<>(array, 4, () -> {
+ });
+ // Estimated size is not exact
+ assertBetween(4, 5, spliterator.estimateSize());
+ Spliterator<Integer> split = spliterator.trySplit();
+ assertBetween(2, 3, spliterator.estimateSize());
+ assertBetween(2, 4, split.estimateSize());
+ }
+
+ @Test
+ public void trySplitFive() {
+ Integer[] array = new Integer[]{ 1 , 2 , 3 , 4 , 5 };
+ Spliterator<Integer> spliterator = new SparseArraySubSpliterator<>(array, 5, () -> {
+ });
+ // Estimated size is not exact
+ assertBetween(5, 6, spliterator.estimateSize());
+ Spliterator<Integer> split = spliterator.trySplit();
+ assertBetween(2, 3, spliterator.estimateSize());
+ assertBetween(3, 4, split.estimateSize());
+ }
+
+ @Test
+ public void trySplitOneHundred() {
+ Integer[] array = new Integer[200];
+ for (int i = 0; i < array.length; i++) {
+ if(i % 2 == 0) {
+ array[i] = i;
+ }
+ }
+ Spliterator<Integer> spliterator = new SparseArraySubSpliterator<>(array, 100, () -> {
+ });
+ // Estimated size is not exact
+ assertBetween(100, 101, spliterator.estimateSize());
+ Spliterator<Integer> split = spliterator.trySplit();
+ assertBetween(50, 51, spliterator.estimateSize());
+ assertBetween(50, 51, split.estimateSize());
+ }
+
+ private void assertBetween(long min, long max, long estimateSize) {
+ assertTrue("estimateSize=" + estimateSize + " min=" + min + " max=" + max, estimateSize >= min);
+ assertTrue("estimateSize=" + estimateSize + " min=" + min + " max=" + max, estimateSize <= max);
+ }
+
+ @Test
+ public void estimateSizeZero() {
+ Integer[] array = new Integer[]{};
+ Spliterator<Integer> spliterator = new SparseArraySubSpliterator<>(array, 0, () -> {
+ });
+ assertBetween(0, 1, spliterator.estimateSize());
+ }
+
+ @Test
+ public void estimateSizeOne() {
+ Integer[] array = new Integer[]{ 1 };
+ Spliterator<Integer> spliterator = new SparseArraySubSpliterator<>(array, 1, () -> {
+ });
+ assertBetween(1, 2, spliterator.estimateSize());
+ }
+
+ @Test
+ public void estimateSizeTwo() {
+ Integer[] array = new Integer[]{ 1 , 2 };
+ Spliterator<Integer> spliterator = new SparseArraySubSpliterator<>(array, 2, () -> {
+ });
+ assertBetween(2, 3, spliterator.estimateSize());
+ }
+
+ @Test
+ public void estimateSizeFive() {
+ Integer[] array = new Integer[]{ 1 , 2 , 3 , 4 , 5 };
+ Spliterator<Integer> spliterator = new SparseArraySubSpliterator<>(array, 5, () -> {
+ });
+ assertBetween(5, 6, spliterator.estimateSize());
+ }
+}
\ No newline at end of file
diff --git a/jena-core/src/test/java/org/apache/jena/mem/WrappedHashMap_CS.java b/jena-core/src/test/java/org/apache/jena/mem/WrappedHashMap_CS.java
deleted file mode 100644
index 25c2bb6..0000000
--- a/jena-core/src/test/java/org/apache/jena/mem/WrappedHashMap_CS.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- Licensed to the Apache Software Foundation (ASF) under one
- or more contributor license agreements. See the NOTICE file
- distributed with this work for additional information
- regarding copyright ownership. The ASF licenses this file
- to you under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance
- with the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
-package org.apache.jena.mem;
-
-import org.junit.runner.RunWith;
-import org.xenei.junit.contract.Contract.Inject;
-import org.xenei.junit.contract.ContractImpl;
-import org.xenei.junit.contract.ContractSuite;
-import org.xenei.junit.contract.IProducer;
-
-@RunWith(ContractSuite.class)
-@ContractImpl(WrappedHashMap.class)
-public class WrappedHashMap_CS {
-
- protected IProducer<WrappedHashMap> mapProducer = new IProducer<WrappedHashMap>() {
-
- @Override
- public WrappedHashMap newInstance() {
- return new WrappedHashMap();
- }
-
- @Override
- public void cleanUp() {
- // nothing to do
- }
-
-
-
- };
-
- @Inject
- public IProducer<WrappedHashMap> getGraphProducer() {
- return mapProducer;
- }
-
-
-}
diff --git a/jena-core/src/test/java/org/apache/jena/mem/test/TestConcurrentModificationException.java b/jena-core/src/test/java/org/apache/jena/mem/test/TestConcurrentModificationException.java
index fa5696f..cd9fe06 100644
--- a/jena-core/src/test/java/org/apache/jena/mem/test/TestConcurrentModificationException.java
+++ b/jena-core/src/test/java/org/apache/jena/mem/test/TestConcurrentModificationException.java
@@ -37,8 +37,7 @@
public static TestSuite suite()
{
TestSuite result = new TestSuite();
- result.addTestSuite( TestArrayBunchCME.class );
- result.addTestSuite( TestSetBunchCME.class );
+ result.addTestSuite( TestArrayBunchCME.class );
result.addTestSuite( TestHashedBunchCME.class );
return result;
}
@@ -51,16 +50,7 @@
@Override public TripleBunch getBunch()
{ return new ArrayBunch(); }
}
-
- public static class TestSetBunchCME extends TestConcurrentModificationException
- {
- public TestSetBunchCME(String name)
- { super( name ); }
- @Override public TripleBunch getBunch()
- { return new SetBunch( new ArrayBunch() ); }
- }
-
public static class TestHashedBunchCME extends TestConcurrentModificationException
{
public TestHashedBunchCME(String name)
@@ -88,6 +78,7 @@
TripleBunch b = getBunch();
b.add( NodeCreateUtils.createTriple( "a P b" ) );
b.add( NodeCreateUtils.createTriple( "c Q d" ) );
+ b.add( NodeCreateUtils.createTriple( "e R f" ) );
ExtendedIterator<Triple> it = b.iterator();
it.next();
b.remove( NodeCreateUtils.createTriple( "a P b" ) );
diff --git a/jena-core/src/test/java/org/apache/jena/mem/test/TestGraphTripleStore.java b/jena-core/src/test/java/org/apache/jena/mem/test/TestGraphTripleStoreMem.java
similarity index 79%
rename from jena-core/src/test/java/org/apache/jena/mem/test/TestGraphTripleStore.java
rename to jena-core/src/test/java/org/apache/jena/mem/test/TestGraphTripleStoreMem.java
index bf793cc..0ac4855 100644
--- a/jena-core/src/test/java/org/apache/jena/mem/test/TestGraphTripleStore.java
+++ b/jena-core/src/test/java/org/apache/jena/mem/test/TestGraphTripleStoreMem.java
@@ -22,17 +22,17 @@
import org.apache.jena.graph.Graph ;
import org.apache.jena.graph.impl.TripleStore ;
import org.apache.jena.graph.test.AbstractTestTripleStore ;
-import org.apache.jena.mem.GraphTripleStore ;
+import org.apache.jena.mem.GraphTripleStoreMem;
-public class TestGraphTripleStore extends AbstractTestTripleStore
+public class TestGraphTripleStoreMem extends AbstractTestTripleStore
{
- public TestGraphTripleStore( String name )
+ public TestGraphTripleStoreMem(String name )
{ super( name ); }
public static TestSuite suite()
- { return new TestSuite( TestGraphTripleStore.class ); }
+ { return new TestSuite( TestGraphTripleStoreMem.class ); }
@Override
public TripleStore getTripleStore()
- { return new GraphTripleStore( Graph.emptyGraph ); }
+ { return new GraphTripleStoreMem( Graph.emptyGraph ); }
}
diff --git a/jena-core/src/test/java/org/apache/jena/mem/test/TestHashCommon.java b/jena-core/src/test/java/org/apache/jena/mem/test/TestHashCommon.java
index 948e5da..83dd7d0 100644
--- a/jena-core/src/test/java/org/apache/jena/mem/test/TestHashCommon.java
+++ b/jena-core/src/test/java/org/apache/jena/mem/test/TestHashCommon.java
@@ -105,17 +105,18 @@
public void testRemoveSimpleMove()
{
- ProbeHashCommon h = probeWith( "0:2:X 1:1:Y 2:2:Z" );
- assertSame( null, h.removeFrom( 1 ) );
- assertAlike( probeWith( "1:2:X 2:2:Z"), h );
+ ProbeHashCommon h = probeWith( "0:0:X 1:2:Y -1:2:Z" );
+ Item moved = (Item) h.removeFrom( 1 );
+ assertSame( null, moved );
+ assertAlike( probeWith( "0:0:X 1:2:Z" ), h );
}
public void testRemoveCircularMove()
{
- ProbeHashCommon h = probeWith( "0:0:X 1:2:Y -1:2:Z" );
+ ProbeHashCommon h = probeWith( "0:2:X 1:1:Y 2:2:Z" );
Item moved = (Item) h.removeFrom( 1 );
- assertAlike( probeWith( "0:0:X 1:2:Z" ), h );
- assertEquals( new Item( 2, "Z" ), moved );
+ assertEquals( new Item( 2, "X" ), moved );
+ assertAlike( probeWith( "1:2:X 2:2:Z"), h );
}
public void testKeyIterator()
diff --git a/jena-core/src/test/java/org/apache/jena/mem/test/TestMemPackage.java b/jena-core/src/test/java/org/apache/jena/mem/test/TestMemPackage.java
index 065552b..3cab3ca 100644
--- a/jena-core/src/test/java/org/apache/jena/mem/test/TestMemPackage.java
+++ b/jena-core/src/test/java/org/apache/jena/mem/test/TestMemPackage.java
@@ -32,9 +32,8 @@
public static TestSuite suite()
{
TestSuite result = new TestSuite();
- result.addTest( TestGraphTripleStore.suite() );
+ result.addTest( TestGraphTripleStoreMem.suite() );
result.addTest( new TestSuite( TestArrayTripleBunch.class ) );
- result.addTest( new TestSuite( TestWrappedSetTripleBunch.class ) );
result.addTest( new TestSuite( TestHashedTripleBunch.class ) );
result.addTestSuite( TestHashedBunchMap.class );
result.addTestSuite( TestHashCommon.class );
diff --git a/jena-core/src/test/java/org/apache/jena/mem/test/TestWrappedSetTripleBunch.java b/jena-core/src/test/java/org/apache/jena/mem/test/TestWrappedSetTripleBunch.java
deleted file mode 100644
index b6a9fec..0000000
--- a/jena-core/src/test/java/org/apache/jena/mem/test/TestWrappedSetTripleBunch.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.jena.mem.test;
-
-import org.apache.jena.mem.* ;
-
-public class TestWrappedSetTripleBunch extends TestTripleBunch
- {
- public TestWrappedSetTripleBunch( String name )
- { super( name ); }
-
- @Override
- public TripleBunch getBunch()
- { return new SetBunch( emptyBunch ); }
- }
diff --git a/jena-jdbc/jena-jdbc-core/src/test/java/org/apache/jena/jdbc/results/AbstractResultSetTests.java b/jena-jdbc/jena-jdbc-core/src/test/java/org/apache/jena/jdbc/results/AbstractResultSetTests.java
index e12104a..c994845 100644
--- a/jena-jdbc/jena-jdbc-core/src/test/java/org/apache/jena/jdbc/results/AbstractResultSetTests.java
+++ b/jena-jdbc/jena-jdbc-core/src/test/java/org/apache/jena/jdbc/results/AbstractResultSetTests.java
@@ -237,7 +237,12 @@
*/
@Test
public void results_select_objects_02() throws SQLException {
- ResultSet rset = this.createResults(ds, "SELECT ?o { ?s ?p ?o . FILTER(ISNUMERIC(?o)) }");
+ /* Ordering the numbers is important to ensure we get the right type as first result.
+ * When org.apache.jena.jdbc.JdbcCompatibility.HIGH is used, the first result is used
+ * to determine the type of the column.
+ */
+
+ ResultSet rset = this.createResults(ds, "SELECT ?o { ?s ?p ?o . FILTER(ISNUMERIC(?o)) } ORDER BY DESC(?o)");
Assert.assertNotNull(rset);
Assert.assertFalse(rset.isClosed());
Assert.assertTrue(rset.isBeforeFirst());
diff --git a/jena-shacl/src/main/java/org/apache/jena/shacl/parser/Constraints.java b/jena-shacl/src/main/java/org/apache/jena/shacl/parser/Constraints.java
index ed78d53..2eeb959 100644
--- a/jena-shacl/src/main/java/org/apache/jena/shacl/parser/Constraints.java
+++ b/jena-shacl/src/main/java/org/apache/jena/shacl/parser/Constraints.java
@@ -147,20 +147,19 @@
/*package*/ static List<Constraint> parseConstraints(Graph shapesGraph, Node shape, Map<Node, Shape> parsed, Set<Node> traversed) {
List<Constraint> constraints = new ArrayList<>();
Iterator<Triple> iter = G.find(shapesGraph, shape, null, null);
- while(iter.hasNext()) {
- Triple t = iter.next();
+ iter.forEachRemaining(t -> {
Node p = t.getPredicate();
// The parser handles sh:property specially as a PropertyShape.
if ( SHACL.property.equals(p) )
- continue;
+ return;
if ( SHACL.path.equals(p) )
- continue;
+ return;
Node s = t.getSubject();
Node o = t.getObject();
Constraint c = parseConstraint(shapesGraph, s, p, o, parsed, traversed);
if ( c != null )
constraints.add(c);
- }
+ });
return constraints;
}
diff --git a/jena-tdb1/src/main/java/org/apache/jena/tdb/sys/DatasetControlMRSW.java b/jena-tdb1/src/main/java/org/apache/jena/tdb/sys/DatasetControlMRSW.java
index 460bd32..ba47f12 100644
--- a/jena-tdb1/src/main/java/org/apache/jena/tdb/sys/DatasetControlMRSW.java
+++ b/jena-tdb1/src/main/java/org/apache/jena/tdb/sys/DatasetControlMRSW.java
@@ -24,6 +24,7 @@
import java.util.Iterator ;
import java.util.NoSuchElementException ;
import java.util.concurrent.atomic.AtomicLong ;
+import java.util.function.Consumer;
import org.apache.jena.atlas.iterator.Iter ;
import org.apache.jena.atlas.lib.Closeable ;
@@ -127,6 +128,13 @@
}
@Override
+ public void forEachRemaining(Consumer<? super T> action) {
+ iter.forEachRemaining(action);
+ checkIterConcurrentModification();
+ close();
+ }
+
+ @Override
public void remove() {
checkIterConcurrentModification();
iter.remove();
diff --git a/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/IteratorCheckNotConcurrent.java b/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/IteratorCheckNotConcurrent.java
index 70917bd..75598fb 100644
--- a/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/IteratorCheckNotConcurrent.java
+++ b/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/IteratorCheckNotConcurrent.java
@@ -24,6 +24,7 @@
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Consumer;
import org.apache.jena.atlas.iterator.Iter;
import org.apache.jena.atlas.lib.Closeable;
@@ -78,6 +79,13 @@
}
@Override
+ public void forEachRemaining(Consumer<? super T> action) {
+ iter.forEachRemaining(action);
+ close();
+ checkConcurrentModification();
+ }
+
+ @Override
public void remove() {
checkConcurrentModification();
iter.remove();