• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

amaembo / streamex / #671

04 Nov 2023 02:03PM UTC coverage: 99.638% (-0.05%) from 99.69%
#671

push

amaembo
Remove broken Javadoc patch execution

5784 of 5805 relevant lines covered (99.64%)

1.0 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.6
/src/main/java/one/util/streamex/CollapseSpliterator.java
1
/*
2
 * Copyright 2015, 2023 StreamEx contributors
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *     http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
package one.util.streamex;
17

18
import java.util.Spliterator;
19
import java.util.function.BiFunction;
20
import java.util.function.BiPredicate;
21
import java.util.function.BinaryOperator;
22
import java.util.function.Consumer;
23
import java.util.function.Function;
24

25
import static one.util.streamex.Internals.Box;
26
import static one.util.streamex.Internals.NONE;
27
import static one.util.streamex.Internals.none;
28

29
/* package */final class CollapseSpliterator<T, R> extends Box<T> implements Spliterator<R> {
30
    private final Spliterator<T> source;
31
    private final CollapseSpliterator<T, R> root; // used as lock
32
    private R acc;
33
    volatile Connector<T, R> left;
34
    volatile Connector<T, R> right;
35
    private final Function<T, R> mapper;
36
    private final BiFunction<R, T, R> accumulator;
37
    private final BinaryOperator<R> combiner;
38
    private final BiPredicate<? super T, ? super T> mergeable;
39

40
    private static final class Connector<T, R> {
41
        CollapseSpliterator<T, R> lhs, rhs;
42
        T left = none(), right = none();
1✔
43
        R acc;
44

45
        Connector(CollapseSpliterator<T, R> lhs, R acc, CollapseSpliterator<T, R> rhs) {
1✔
46
            this.lhs = lhs;
1✔
47
            this.rhs = rhs;
1✔
48
            this.acc = acc;
1✔
49
        }
1✔
50

51
        R drain() {
52
            if (lhs != null)
1✔
53
                lhs.right = null;
1✔
54
            if (rhs != null)
1✔
55
                rhs.left = null;
1✔
56
            return acc;
1✔
57
        }
58

59
        R drainLeft() {
60
            return left == NONE ? drain() : none();
1✔
61
        }
62

63
        R drainRight() {
64
            return right == NONE ? drain() : none();
1✔
65
        }
66
    }
67

68
    CollapseSpliterator(BiPredicate<? super T, ? super T> mergeable, Function<T, R> mapper,
69
            BiFunction<R, T, R> accumulator, BinaryOperator<R> combiner, Spliterator<T> source) {
70
        super(none());
1✔
71
        this.source = source;
1✔
72
        this.mergeable = mergeable;
1✔
73
        this.mapper = mapper;
1✔
74
        this.accumulator = accumulator;
1✔
75
        this.combiner = combiner;
1✔
76
        this.root = this;
1✔
77
    }
1✔
78

79
    private CollapseSpliterator(CollapseSpliterator<T, R> root, Spliterator<T> source, Connector<T, R> left,
80
            Connector<T, R> right) {
81
        super(none());
1✔
82
        this.source = source;
1✔
83
        this.root = root;
1✔
84
        this.mergeable = root.mergeable;
1✔
85
        this.mapper = root.mapper;
1✔
86
        this.accumulator = root.accumulator;
1✔
87
        this.combiner = root.combiner;
1✔
88
        this.left = left;
1✔
89
        this.right = right;
1✔
90
        if (left != null)
1✔
91
            left.rhs = this;
1✔
92
        right.lhs = this;
1✔
93
    }
1✔
94

95
    @Override
96
    public boolean tryAdvance(Consumer<? super R> action) {
97
        if (left != null) {
1✔
98
            if (accept(handleLeft(), action)) {
1✔
99
                return true;
1✔
100
            }
101
        }
102
        if (a == NONE) { // start
1✔
103
            if (!source.tryAdvance(this)) {
1✔
104
                return accept(pushRight(none(), none()), action);
1✔
105
            }
106
        }
107
        T first = a;
1✔
108
        R acc = mapper.apply(a);
1✔
109
        T last = first;
1✔
110
        while (source.tryAdvance(this)) {
1✔
111
            if (!this.mergeable.test(last, a)) {
1✔
112
                action.accept(acc);
1✔
113
                return true;
1✔
114
            }
115
            last = a;
1✔
116
            acc = this.accumulator.apply(acc, last);
1✔
117
        }
118
        return accept(pushRight(acc, last), action);
1✔
119
    }
120

121
    @Override
122
    public void forEachRemaining(Consumer<? super R> action) {
123
        while (left != null) {
1✔
124
            accept(handleLeft(), action);
1✔
125
        }
126
        if (a != NONE) {
1✔
127
            acc = mapper.apply(a);
1✔
128
        }
129
        source.forEachRemaining(next -> {
1✔
130
            if (a == NONE) {
1✔
131
                acc = mapper.apply(next);
1✔
132
            } else if (!this.mergeable.test(a, next)) {
1✔
133
                action.accept(acc);
1✔
134
                acc = mapper.apply(next);
1✔
135
            } else {
136
                acc = accumulator.apply(acc, next);
1✔
137
            }
138
            a = next;
1✔
139
        });
1✔
140
        if (a == NONE) {
1✔
141
            accept(pushRight(none(), none()), action);
1✔
142
        } else if (accept(pushRight(acc, a), action)) {
1✔
143
            if (right != null) {
1✔
144
                action.accept(right.acc);
1✔
145
                right = null;
1✔
146
            }
147
        }
148
    }
1✔
149

150
    private boolean accept(R acc, Consumer<? super R> action) {
151
        if (acc != NONE) {
1✔
152
            action.accept(acc);
1✔
153
            return true;
1✔
154
        }
155
        return false;
1✔
156
    }
157

158
    private R handleLeft() {
159
        synchronized (root) {
1✔
160
            Connector<T, R> l = left;
1✔
161
            if (l == null) {
1✔
162
                return none();
×
163
            }
164
            if (l.left == NONE && l.right == NONE && l.acc != NONE) {
1✔
165
                return l.drain();
1✔
166
            }
167
        }
1✔
168
        if (source.tryAdvance(this)) {
1✔
169
            T first = this.a;
1✔
170
            T last = first;
1✔
171
            R acc = this.mapper.apply(first);
1✔
172
            while (source.tryAdvance(this)) {
1✔
173
                if (!this.mergeable.test(last, a))
1✔
174
                    return pushLeft(first, acc);
1✔
175
                last = a;
1✔
176
                acc = this.accumulator.apply(acc, last);
1✔
177
            }
178
            a = none();
1✔
179
            return connectOne(first, acc, last);
1✔
180
        }
181
        return connectEmpty();
1✔
182
    }
183

184
    // l + <first|acc|?>
185
    private R pushLeft(T first, R acc) {
186
        synchronized (root) {
1✔
187
            Connector<T, R> l = left;
1✔
188
            if (l == null)
1✔
189
                return acc;
1✔
190
            left = null;
1✔
191
            l.rhs = null;
1✔
192
            T laright = l.right;
1✔
193
            l.right = none();
1✔
194
            if (l.acc == NONE) {
1✔
195
                l.acc = acc;
1✔
196
                l.left = first;
1✔
197
                return none();
1✔
198
            }
199
            if (this.mergeable.test(laright, first)) {
1✔
200
                l.acc = this.combiner.apply(l.acc, acc);
1✔
201
                return l.drainLeft();
1✔
202
            }
203
            if (l.left == NONE) {
1✔
204
                left = new Connector<>(null, acc, this);
1✔
205
                return l.drain();
1✔
206
            }
207
        }
1✔
208
        return acc;
1✔
209
    }
210

211
    // <?|acc|last> + r
212
    private R pushRight(R acc, T last) {
213
        a = none();
1✔
214
        if (right == null)
1✔
215
            return acc;
1✔
216
        synchronized (root) {
1✔
217
            Connector<T, R> r = right;
1✔
218
            if (r == null)
1✔
219
                return acc;
×
220
            right = null;
1✔
221
            r.lhs = null;
1✔
222
            T raleft = r.left;
1✔
223
            r.left = none();
1✔
224
            if (r.acc == NONE) {
1✔
225
                if (acc == NONE) {
1✔
226
                    r.drain();
1✔
227
                } else {
228
                    r.acc = acc;
1✔
229
                    r.right = last;
1✔
230
                }
231
                return none();
1✔
232
            }
233
            if (acc == NONE) {
1✔
234
                return r.drainRight();
1✔
235
            }
236
            if (mergeable.test(last, raleft)) {
1✔
237
                r.acc = combiner.apply(acc, r.acc);
1✔
238
                return r.drainRight();
1✔
239
            }
240
            if (r.right == NONE)
1✔
241
                right = new Connector<>(this, r.drain(), null);
1✔
242
            return acc;
1✔
243
        }
244
    }
245

246
    // l + <first|acc|last> + r
247
    private R connectOne(T first, R acc, T last) {
248
        synchronized (root) {
1✔
249
            Connector<T, R> l = left;
1✔
250
            if (l == null) {
1✔
251
                return pushRight(acc, last);
1✔
252
            }
253
            if (l.acc == NONE) {
1✔
254
                l.acc = acc;
1✔
255
                l.left = first;
1✔
256
                l.right = last;
1✔
257
                return connectEmpty();
1✔
258
            }
259
            T laright = l.right;
1✔
260
            if (mergeable.test(laright, first)) {
1✔
261
                l.acc = combiner.apply(l.acc, acc);
1✔
262
                l.right = last;
1✔
263
                return connectEmpty();
1✔
264
            }
265
            left = null;
1✔
266
            l.rhs = null;
1✔
267
            l.right = none();
1✔
268
            if (l.left != NONE) {
1✔
269
                return pushRight(acc, last);
1✔
270
            }
271
            acc = pushRight(acc, last);
1✔
272
            if (acc != NONE)
1✔
273
                left = new Connector<>(null, acc, this);
1✔
274
            return l.drain();
1✔
275
        }
276
    }
277

278
    // l + r
279
    private R connectEmpty() {
280
        synchronized (root) {
1✔
281
            Connector<T, R> l = left, r = right;
1✔
282
            if (l == null) {
1✔
283
                return pushRight(none(), none());
×
284
            }
285
            left = right = null;
1✔
286
            l.rhs = null;
1✔
287
            T laright = l.right;
1✔
288
            l.right = none();
1✔
289
            if (l.acc == NONE) {
1✔
290
                if (r == null)
1✔
291
                    l.drain();
1✔
292
                else {
293
                    if (l.lhs != null) {
1✔
294
                        l.lhs.right = r;
1✔
295
                        r.lhs = l.lhs;
1✔
296
                    }
297
                }
298
                return none();
1✔
299
            }
300
            if (r == null) {
1✔
301
                return l.drainLeft();
1✔
302
            }
303
            r.lhs = null;
1✔
304
            if (r.acc == NONE) {
1✔
305
                if (r.rhs != null) {
1✔
306
                    r.rhs.left = l;
1✔
307
                    l.rhs = r.rhs;
1✔
308
                    l.right = laright;
1✔
309
                }
310
                return none();
1✔
311
            }
312
            T raleft = r.left;
1✔
313
            r.left = none();
1✔
314
            if (mergeable.test(laright, raleft)) {
1✔
315
                R acc = combiner.apply(l.acc, r.acc);
1✔
316
                if (l.left == NONE && r.right == NONE) {
1✔
317
                    l.drain();
1✔
318
                    r.drain();
1✔
319
                    return acc;
1✔
320
                }
321
                l.acc = acc;
1✔
322
                l.right = r.right;
1✔
323
                if (r.rhs != null) {
1✔
324
                    r.rhs.left = l;
1✔
325
                    l.rhs = r.rhs;
1✔
326
                }
327
                return none();
1✔
328
            }
329
            if (l.left == NONE) {
1✔
330
                if (r.right == NONE)
1✔
331
                    right = new Connector<>(this, r.drain(), null);
1✔
332
                return l.drain();
1✔
333
            }
334
            return r.drainRight();
1✔
335
        }
336
    }
337

338
    @Override
339
    public Spliterator<R> trySplit() {
340
        Spliterator<T> prefix = source.trySplit();
1✔
341
        if (prefix == null)
1✔
342
            return null;
1✔
343
        Connector<T, R> newBox = new Connector<>(null, none(), this);
1✔
344
        synchronized (root) {
1✔
345
            CollapseSpliterator<T, R> result = new CollapseSpliterator<>(root, prefix, left, newBox);
1✔
346
            this.left = newBox;
1✔
347
            return result;
1✔
348
        }
349
    }
350

351
    @Override
352
    public long estimateSize() {
353
        return source.estimateSize();
1✔
354
    }
355

356
    @Override
357
    public int characteristics() {
358
        return source.characteristics() & (CONCURRENT | IMMUTABLE | ORDERED);
1✔
359
    }
360
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc