package com.codepoetics.octarine.joins; import com.codepoetics.octarine.functional.tuples.T2; import java.util.*; import java.util.function.Consumer; class KeyMatchingSpliterator<K, L, R> implements Spliterator<T2<L, R>> { public static <K, L, R> KeyMatchingSpliterator<K, L, R> over( Comparator<? super K> comparator, Spliterator<Map.Entry<K, L>> self, Spliterator<Map.Entry<K, R>> other, L emptyLeft, R emptyRight) { return new KeyMatchingSpliterator<>(comparator, self, other, emptyLeft, emptyRight); } private final Comparator<? super K> comparator; private final Spliterator<Map.Entry<K, L>> leftIter; private final Spliterator<Map.Entry<K, R>> rightIter; private final L emptyLeft; private final R emptyRight; private boolean initialised = false; private Map.Entry<K, L> leftBuffer = null; private Map.Entry<K, R> rightBuffer = null; private boolean hasLeft = true; private boolean hasRight = true; KeyMatchingSpliterator(Comparator<? super K> comparator, Spliterator<Map.Entry<K, L>> leftIter, Spliterator<Map.Entry<K, R>> rightIter, L emptyLeft, R emptyRight) { this.comparator = comparator; this.leftIter = leftIter; this.rightIter = rightIter; this.emptyLeft = emptyLeft; this.emptyRight = emptyRight; } private void setLeftBuffer(Map.Entry<K, L> leftBuffer) { this.leftBuffer = leftBuffer; } private void setRightBuffer(Map.Entry<K, R> rightBuffer) { this.rightBuffer = rightBuffer; } @Override public boolean tryAdvance(Consumer<? super T2<L, R>> action) { if (!initialised) { advanceBoth(); initialised = true; } if (!hasLeft && !hasRight) { return false; } if (hasLeft && hasRight) { sendGreatest(action); } else if (hasLeft) { sendLeft(action); } else { sendRight(action); } return true; } private void sendGreatest(Consumer<? super T2<L, R>> action) { int cmp = comparator.compare(leftBuffer.getKey(), rightBuffer.getKey()); if (cmp < 0) { sendLeft(action); } if (cmp > 0) { sendRight(action); } if (cmp == 0) { sendBoth(action); } } private void sendLeft(Consumer<? super T2<L, R>> action) { action.accept(T2.of(leftBuffer.getValue(), emptyRight)); advanceLeft(); } private void sendRight(Consumer<? super T2<L, R>> action) { action.accept(T2.of(emptyLeft, rightBuffer.getValue())); advanceRight(); } private void sendBoth(Consumer<? super T2<L, R>> action) { action.accept(T2.of(leftBuffer.getValue(), rightBuffer.getValue())); advanceBoth(); } private void advanceLeft() { hasLeft = leftIter.tryAdvance(this::setLeftBuffer); } private void advanceRight() { hasRight = rightIter.tryAdvance(this::setRightBuffer); } private void advanceBoth() { advanceLeft(); advanceRight(); } @Override public Spliterator<T2<L, R>> trySplit() { return null; } @Override public long estimateSize() { return Long.MAX_VALUE; } @Override public int characteristics() { return Spliterator.ORDERED | Spliterator.NONNULL | Spliterator.IMMUTABLE; } }