Skip to content

Commit c24319e

Browse files
committed
GH-4819 ordered dual union iteration support
1 parent 8580bfe commit c24319e

7 files changed

Lines changed: 324 additions & 84 deletions

File tree

core/common/iterator/src/main/java/org/eclipse/rdf4j/common/iteration/DualUnionIteration.java

Lines changed: 84 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,17 @@
1515
import java.util.NoSuchElementException;
1616

1717
import org.eclipse.rdf4j.common.annotation.Experimental;
18-
import org.eclipse.rdf4j.common.order.StatementOrder;
19-
import org.eclipse.rdf4j.model.Value;
2018

2119
/**
2220
* Provides a bag union of the two provided iterations.
2321
*/
2422
public class DualUnionIteration<E> implements CloseableIteration<E> {
2523

26-
private final StatementOrder statementOrder;
27-
private final Comparator<Value> cmp;
24+
private final Comparator<E> cmp;
2825
private CloseableIteration<? extends E> iteration1;
2926
private CloseableIteration<? extends E> iteration2;
27+
private E nextElementIteration1;
28+
private E nextElementIteration2;
3029
private E nextElement;
3130
/**
3231
* Flag indicating whether this iteration has been closed.
@@ -37,20 +36,15 @@ private DualUnionIteration(CloseableIteration<? extends E> iteration1,
3736
CloseableIteration<? extends E> iteration2) {
3837
this.iteration1 = iteration1;
3938
this.iteration2 = iteration2;
40-
this.statementOrder = null;
4139
this.cmp = null;
4240
}
4341

4442
@Experimental
45-
public DualUnionIteration(StatementOrder statementOrder, Comparator<Value> cmp,
43+
public DualUnionIteration(Comparator<E> cmp,
4644
CloseableIteration<? extends E> iteration1, CloseableIteration<? extends E> iteration2) {
4745
this.iteration1 = iteration1;
4846
this.iteration2 = iteration2;
49-
this.statementOrder = statementOrder;
5047
this.cmp = cmp;
51-
52-
// TODO
53-
throw new UnsupportedOperationException("Not implemented yet");
5448
}
5549

5650
public static <E> CloseableIteration<? extends E> getWildcardInstance(
@@ -66,25 +60,16 @@ public static <E> CloseableIteration<? extends E> getWildcardInstance(
6660
}
6761

6862
@Experimental
69-
public static <E> CloseableIteration<? extends E> getWildcardInstance(StatementOrder order,
70-
Comparator<Value> cmp,
63+
public static <E> CloseableIteration<? extends E> getWildcardInstance(Comparator<E> cmp,
7164
CloseableIteration<? extends E> leftIteration, CloseableIteration<? extends E> rightIteration) {
7265

73-
if (rightIteration instanceof EmptyIteration) {
74-
return leftIteration;
75-
} else if (leftIteration instanceof EmptyIteration) {
76-
return rightIteration;
77-
} else {
78-
if (!rightIteration.hasNext()) {
79-
rightIteration.close();
80-
return leftIteration;
81-
}
82-
if (!leftIteration.hasNext()) {
83-
leftIteration.close();
84-
return rightIteration;
85-
}
86-
return new DualUnionIteration<>(order, cmp, leftIteration, rightIteration);
87-
}
66+
// if (rightIteration instanceof EmptyIteration) {
67+
// return leftIteration;
68+
// } else if (leftIteration instanceof EmptyIteration) {
69+
// return rightIteration;
70+
// } else {
71+
return new DualUnionIteration<>(cmp, leftIteration, rightIteration);
72+
// }
8873
}
8974

9075
public static <E> CloseableIteration<E> getInstance(CloseableIteration<E> leftIteration,
@@ -99,32 +84,6 @@ public static <E> CloseableIteration<E> getInstance(CloseableIteration<E> leftIt
9984
}
10085
}
10186

102-
public E getNextElement() {
103-
if (iteration1 == null && iteration2 != null) {
104-
if (iteration2.hasNext()) {
105-
return iteration2.next();
106-
} else {
107-
iteration2.close();
108-
iteration2 = null;
109-
}
110-
} else if (iteration1 != null) {
111-
if (iteration1.hasNext()) {
112-
return iteration1.next();
113-
} else if (iteration2.hasNext()) {
114-
iteration1.close();
115-
iteration1 = null;
116-
return iteration2.next();
117-
} else {
118-
iteration1.close();
119-
iteration1 = null;
120-
iteration2.close();
121-
iteration2 = null;
122-
}
123-
}
124-
125-
return null;
126-
}
127-
12887
@Override
12988
public final boolean hasNext() {
13089
if (closed) {
@@ -156,7 +115,11 @@ public final E next() {
156115
*/
157116
private E lookAhead() {
158117
if (nextElement == null) {
159-
nextElement = getNextElement();
118+
if (cmp == null) {
119+
lookaheadWithoutOrder();
120+
} else {
121+
lookaheadWithOrder();
122+
}
160123

161124
if (nextElement == null) {
162125
close();
@@ -165,6 +128,73 @@ private E lookAhead() {
165128
return nextElement;
166129
}
167130

131+
private void lookaheadWithOrder() {
132+
assert cmp != null;
133+
if (nextElementIteration1 == null && iteration1 != null) {
134+
if (iteration1.hasNext()) {
135+
nextElementIteration1 = iteration1.next();
136+
} else {
137+
iteration1.close();
138+
iteration1 = null;
139+
}
140+
}
141+
142+
if (nextElementIteration2 == null && iteration2 != null) {
143+
if (iteration2.hasNext()) {
144+
nextElementIteration2 = iteration2.next();
145+
} else {
146+
iteration2.close();
147+
iteration2 = null;
148+
}
149+
}
150+
151+
if (nextElementIteration1 != null && nextElementIteration2 != null) {
152+
int compare = cmp.compare(nextElementIteration1, nextElementIteration2);
153+
154+
if (compare <= 0) {
155+
nextElement = nextElementIteration1;
156+
nextElementIteration1 = null;
157+
} else {
158+
nextElement = nextElementIteration2;
159+
nextElementIteration2 = null;
160+
}
161+
} else {
162+
if (nextElementIteration1 != null) {
163+
nextElement = nextElementIteration1;
164+
nextElementIteration1 = null;
165+
} else if (nextElementIteration2 != null) {
166+
nextElement = nextElementIteration2;
167+
nextElementIteration2 = null;
168+
}
169+
}
170+
}
171+
172+
private void lookaheadWithoutOrder() {
173+
assert cmp == null;
174+
175+
if (iteration1 == null && iteration2 != null) {
176+
if (iteration2.hasNext()) {
177+
nextElement = iteration2.next();
178+
} else {
179+
iteration2.close();
180+
iteration2 = null;
181+
}
182+
} else if (iteration1 != null) {
183+
if (iteration1.hasNext()) {
184+
nextElement = iteration1.next();
185+
} else if (iteration2.hasNext()) {
186+
iteration1.close();
187+
iteration1 = null;
188+
nextElement = iteration2.next();
189+
} else {
190+
iteration1.close();
191+
iteration1 = null;
192+
iteration2.close();
193+
iteration2 = null;
194+
}
195+
}
196+
}
197+
168198
/**
169199
* Throws an {@link UnsupportedOperationException}.
170200
*/

core/common/order/src/main/java/org/eclipse/rdf4j/common/order/StatementOrder.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,5 @@ public Comparator<Statement> getComparator(Comparator<Value> comparator) {
4040

4141
throw new IllegalStateException("Unknown StatementOrder: " + this);
4242
}
43+
4344
}

core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/iterator/PeekMarkIterator.java

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ private void calculateNext() {
5151

5252
if (bufferIterator.hasNext()) {
5353
next = bufferIterator.next();
54-
assert resetPossible == 1;
5554
} else {
5655
if (!mark && resetPossible > -1) {
5756
resetPossible--;
@@ -62,7 +61,7 @@ private void calculateNext() {
6261
}
6362

6463
if (mark && next != null) {
65-
assert resetPossible == 1;
64+
assert resetPossible > 0;
6665
buffer.add(next);
6766
}
6867

@@ -107,6 +106,10 @@ public E peek() {
107106
return next;
108107
}
109108

109+
/**
110+
* Mark the current position so that the iterator can be reset to the current state. This will cause elements to be
111+
* stored in memory until one of {@link #reset()}, {@link #unmark()} or {@link #mark()} is called
112+
*/
110113
public void mark() {
111114
if (closed) {
112115
throw new IllegalStateException("The iteration has been closed.");
@@ -120,38 +123,51 @@ public void mark() {
120123

121124
}
122125

126+
/**
127+
* Reset the iterator to the marked position. Resetting an iterator multiple times will always reset to the same
128+
* position. If the iterator was not marked, this will throw an exception.
129+
*/
123130
public void reset() {
124131
if (closed) {
125132
throw new IllegalStateException("The iteration has been closed.");
126133
}
127134
if (buffer == null) {
128135
throw new IllegalStateException("Mark never set");
129136
}
137+
if (resetPossible < 0) {
138+
throw new IllegalStateException("Reset not possible");
139+
}
140+
130141
if (mark && bufferIterator.hasNext()) {
131142
while (bufferIterator.hasNext()) {
132143
buffer.add(bufferIterator.next());
133144
}
134145
}
135146

136-
mark = false;
137-
if (resetPossible < 0) {
138-
throw new IllegalStateException("Reset not possible");
139-
} else if (resetPossible == 0) {
147+
if (resetPossible == 0) {
148+
assert !mark;
140149
buffer.add(next);
141150
next = null;
142151
bufferIterator = buffer.iterator();
143-
} else if (resetPossible == 1) {
152+
} else if (resetPossible > 0) {
144153
next = null;
145154
bufferIterator = buffer.iterator();
146155
}
147156

157+
mark = false;
148158
resetPossible = 1;
149159
}
150160

161+
/**
162+
* @return true if the iterator is marked
163+
*/
151164
boolean isMarked() {
152165
return !closed && mark;
153166
}
154167

168+
/**
169+
* @return true if {@link #reset()} can be called on this iterator
170+
*/
155171
boolean isResettable() {
156172
return !closed && (mark || resetPossible >= 0);
157173
}
@@ -160,11 +176,16 @@ boolean isResettable() {
160176
public void close() {
161177
this.closed = true;
162178
iterator.close();
179+
buffer = null;
163180
}
164181

165-
// What will happen if we are iterating over the buffer at this point, then unmark is called followed by mark?
182+
/**
183+
* Unmark the iterator. This will cause the iterator to stop buffering elements. If the iterator was recently reset
184+
* and there are still elements in the buffer, then these elements will still be returned by next().
185+
*/
166186
public void unmark() {
167187
mark = false;
168188
resetPossible = -1;
189+
buffer = null;
169190
}
170191
}

0 commit comments

Comments
 (0)