package trees; import static trees.BoundedMatchIterator.State.LEFT; import static trees.BoundedMatchIterator.State.MIDDLE; import static trees.BoundedMatchIterator.State.RIGHT; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; import parser.Match; import util.PeekIterator; public class BoundedMatchIterator implements Iterator<Match>, Iterable<Match> { /****************************************************************************/ private final MatchIterator iterator; /****************************************************************************/ private final PeekIterator<Match> leftIter; /****************************************************************************/ private final PeekIterator<Match> rightIter; /****************************************************************************/ private final boolean inclusive; /****************************************************************************/ private final boolean rightOpen; /****************************************************************************/ enum State { LEFT, MIDDLE, RIGHT } /****************************************************************************/ private State state = LEFT; /****************************************************************************/ private Match next; /****************************************************************************/ private Match prev; /****************************************************************************/ BoundedMatchIterator(Match match, List<Match> left, List<Match> right, boolean leftToRight, boolean inclusive) { this.inclusive = inclusive; this.leftIter = new PeekIterator<>(leftToRight ? left : right); this.rightIter = new PeekIterator<>(leftToRight ? right : left); this.iterator = new MatchIterator(match, leftToRight); this.rightOpen = !rightIter.hasNext(); forward(); } /****************************************************************************/ private void forward() { switch(state) { case LEFT : leftItem (); break; case MIDDLE : middleItem(); break; case RIGHT : rightItem (); break; } } /****************************************************************************/ private void leftItem() { while (leftIter.hasNext()) { assert iterator.hasNext(); Match match = iterator.next(); if (match == leftIter.peek()) { leftIter.next(); if (match == rightIter.peek()) { rightIter.next(); } } else if (!inclusive) { next = match; break; } } if (!leftIter.hasNext()) { state = MIDDLE; iterator.skipChilds(); middleItem(); } } /****************************************************************************/ private void middleItem() { while (rightIter.hasNext()) { assert iterator.hasNext(); Match match = iterator.next(); if (match == rightIter.peek()) { rightIter.next(); continue; } else if (inclusive) { next = match; break; } } if (!rightIter.hasNext()) { state = RIGHT; iterator.skipChilds(); rightItem(); } } /****************************************************************************/ private void rightItem() { next = inclusive == rightOpen && iterator.hasNext() ? iterator.next() : null; } /***************************************************************************** * Ensures that the descendant of the last item returned by next() (if any) * will be skipped in the iteration. Behaves as if next() was called for all * the descendants. */ public void skipChilds() { if (prev == null || prev.children().isEmpty()) { return; } int maxChildIndex = prev.children().size() - 1; for (int i = 0 ; i < maxChildIndex ; ++i) { iterator.skipChilds(); iterator.next(); } iterator.skipChilds(); forward(); } /****************************************************************************/ @Override public boolean hasNext() { return next != null; } /****************************************************************************/ @Override public Match next() { if (next == null) { throw new NoSuchElementException(); } prev = next; forward(); return prev; } /****************************************************************************/ @Override public Iterator<Match> iterator() { return this; } /****************************************************************************/ @Override public void remove() { throw new UnsupportedOperationException(); } }