/* * Copyright 2015, 2016 Tagir Valeev * * Licensed 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 one.util.streamex; import java.util.Spliterator; import java.util.function.BiFunction; import java.util.function.BiPredicate; import java.util.function.BinaryOperator; import java.util.function.Consumer; import java.util.function.Function; import static one.util.streamex.StreamExInternals.*; /* package */final class CollapseSpliterator<T, R> extends Box<T> implements Spliterator<R> { private final Spliterator<T> source; private final CollapseSpliterator<T, R> root; // used as lock private R acc; volatile Connector<T, R> left; volatile Connector<T, R> right; private final Function<T, R> mapper; private final BiFunction<R, T, R> accumulator; private final BinaryOperator<R> combiner; private final BiPredicate<? super T, ? super T> mergeable; private static final class Connector<T, R> { CollapseSpliterator<T, R> lhs, rhs; T left = none(), right = none(); R acc; Connector(CollapseSpliterator<T, R> lhs, R acc, CollapseSpliterator<T, R> rhs) { this.lhs = lhs; this.rhs = rhs; this.acc = acc; } R drain() { if (lhs != null) lhs.right = null; if (rhs != null) rhs.left = null; return acc; } R drainLeft() { return left == NONE ? drain() : none(); } R drainRight() { return right == NONE ? drain() : none(); } } CollapseSpliterator(BiPredicate<? super T, ? super T> mergeable, Function<T, R> mapper, BiFunction<R, T, R> accumulator, BinaryOperator<R> combiner, Spliterator<T> source) { super(none()); this.source = source; this.mergeable = mergeable; this.mapper = mapper; this.accumulator = accumulator; this.combiner = combiner; this.root = this; } private CollapseSpliterator(CollapseSpliterator<T, R> root, Spliterator<T> source, Connector<T, R> left, Connector<T, R> right) { super(none()); this.source = source; this.root = root; this.mergeable = root.mergeable; this.mapper = root.mapper; this.accumulator = root.accumulator; this.combiner = root.combiner; this.left = left; this.right = right; if (left != null) left.rhs = this; right.lhs = this; } @Override public boolean tryAdvance(Consumer<? super R> action) { if (left != null) { if (accept(handleLeft(), action)) { return true; } } if (a == NONE) {// start if (!source.tryAdvance(this)) { return accept(pushRight(none(), none()), action); } } T first = a; R acc = mapper.apply(a); T last = first; while (source.tryAdvance(this)) { if (!this.mergeable.test(last, a)) { action.accept(acc); return true; } last = a; acc = this.accumulator.apply(acc, last); } return accept(pushRight(acc, last), action); } @Override public void forEachRemaining(Consumer<? super R> action) { while (left != null) { accept(handleLeft(), action); } if (a != NONE) { acc = mapper.apply(a); } source.forEachRemaining(next -> { if (a == NONE) { acc = mapper.apply(next); } else if (!this.mergeable.test(a, next)) { action.accept(acc); acc = mapper.apply(next); } else { acc = accumulator.apply(acc, next); } a = next; }); if (a == NONE) { accept(pushRight(none(), none()), action); } else if (accept(pushRight(acc, a), action)) { if (right != null) { action.accept(right.acc); right = null; } } } private boolean accept(R acc, Consumer<? super R> action) { if (acc != NONE) { action.accept(acc); return true; } return false; } private R handleLeft() { synchronized (root) { Connector<T, R> l = left; if (l == null) { return none(); } if (l.left == NONE && l.right == NONE && l.acc != NONE) { return l.drain(); } } if (source.tryAdvance(this)) { T first = this.a; T last = first; R acc = this.mapper.apply(first); while (source.tryAdvance(this)) { if (!this.mergeable.test(last, a)) return pushLeft(first, acc); last = a; acc = this.accumulator.apply(acc, last); } a = none(); return connectOne(first, acc, last); } return connectEmpty(); } // l + <first|acc|?> private R pushLeft(T first, R acc) { synchronized (root) { Connector<T, R> l = left; if (l == null) return acc; left = null; l.rhs = null; T laright = l.right; l.right = none(); if (l.acc == NONE) { l.acc = acc; l.left = first; return none(); } if (this.mergeable.test(laright, first)) { l.acc = this.combiner.apply(l.acc, acc); return l.drainLeft(); } if (l.left == NONE) { left = new Connector<>(null, acc, this); return l.drain(); } } return acc; } // <?|acc|last> + r private R pushRight(R acc, T last) { a = none(); if (right == null) return acc; synchronized (root) { Connector<T, R> r = right; if (r == null) return acc; right = null; r.lhs = null; T raleft = r.left; r.left = none(); if (r.acc == NONE) { if (acc == NONE) { r.drain(); } else { r.acc = acc; r.right = last; } return none(); } if (acc == NONE) { return r.drainRight(); } if (mergeable.test(last, raleft)) { r.acc = combiner.apply(acc, r.acc); return r.drainRight(); } if (r.right == NONE) right = new Connector<>(this, r.drain(), null); return acc; } } // l + <first|acc|last> + r private R connectOne(T first, R acc, T last) { synchronized (root) { Connector<T, R> l = left; if (l == null) { return pushRight(acc, last); } if (l.acc == NONE) { l.acc = acc; l.left = first; l.right = last; return connectEmpty(); } T laright = l.right; if (mergeable.test(laright, first)) { l.acc = combiner.apply(l.acc, acc); l.right = last; return connectEmpty(); } left = null; l.rhs = null; l.right = none(); if (l.left != NONE) { return pushRight(acc, last); } acc = pushRight(acc, last); if (acc != NONE) left = new Connector<>(null, acc, this); return l.drain(); } } // l + r private R connectEmpty() { synchronized (root) { Connector<T, R> l = left, r = right; if (l == null) { return pushRight(none(), none()); } left = right = null; l.rhs = null; T laright = l.right; l.right = none(); if (l.acc == NONE) { if (r == null) l.drain(); else { if (l.lhs != null) { l.lhs.right = r; r.lhs = l.lhs; } } return none(); } if (r == null) { return l.drainLeft(); } r.lhs = null; if (r.acc == NONE) { if (r.rhs != null) { r.rhs.left = l; l.rhs = r.rhs; l.right = laright; } return none(); } T raleft = r.left; r.left = none(); if (mergeable.test(laright, raleft)) { R acc = combiner.apply(l.acc, r.acc); if (l.left == NONE && r.right == NONE) { l.drain(); r.drain(); return acc; } l.acc = acc; l.right = r.right; if (r.rhs != null) { r.rhs.left = l; l.rhs = r.rhs; } return none(); } if (l.left == NONE) { if (r.right == NONE) right = new Connector<>(this, r.drain(), null); return l.drain(); } return r.drainRight(); } } @Override public Spliterator<R> trySplit() { Spliterator<T> prefix = source.trySplit(); if (prefix == null) return null; Connector<T, R> newBox = new Connector<>(null, none(), this); synchronized (root) { CollapseSpliterator<T, R> result = new CollapseSpliterator<>(root, prefix, left, newBox); this.left = newBox; return result; } } @Override public long estimateSize() { return source.estimateSize(); } @Override public int characteristics() { return source.characteristics() & (CONCURRENT | IMMUTABLE | ORDERED); } }