/* * ConcatenationRopeIteratorImpl.java * Copyright (C) 2007 Amin Ahmad. * * This file is part of Java Ropes. * * Java Ropes is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Java Ropes is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Java Ropes. If not, see . * * Amin Ahmad can be contacted at amin.ahmad@gmail.com or on the web at * www.ahmadsoft.org. */ package org.ahmadsoft.ropes.impl; import java.util.ArrayDeque; import java.util.Iterator; import org.ahmadsoft.ropes.Rope; /** * A fast iterator for concatenated ropes. Iterating over a complex * rope structure is guaranteed to be O(n) so long as it is reasonably * well-balanced. Compare this to O(nlogn) for iteration using * charAt. * * @author aahmad */ public class ConcatenationRopeIteratorImpl implements Iterator { private final ArrayDeque toTraverse; private Rope currentRope; private int currentRopePos; private int skip; private int currentAbsolutePos; public ConcatenationRopeIteratorImpl(final Rope rope) { this(rope, 0); } public ConcatenationRopeIteratorImpl(final Rope rope, final int start) { this.toTraverse = new ArrayDeque(); this.toTraverse.push(rope); this.currentRope = null; this.initialize(); if (start < 0 || start > rope.length()) { throw new IllegalArgumentException("Rope index out of range: " + start); } this.moveForward(start); } public boolean canMoveBackwards(final int amount) { return (-1 <= (this.currentRopePos - amount)); } public int getPos() { return this.currentAbsolutePos; } @Override public boolean hasNext() { return this.currentRopePos < this.currentRope.length() - 1 || !this.toTraverse.isEmpty(); } /** * Initialize the currentRope and currentRopePos fields. */ private void initialize() { while (!this.toTraverse.isEmpty()) { this.currentRope = this.toTraverse.pop(); if (this.currentRope instanceof ConcatenationRope) { this.toTraverse.push(((ConcatenationRope) this.currentRope).getRight()); this.toTraverse.push(((ConcatenationRope) this.currentRope).getLeft()); } else { break; } } if (this.currentRope == null) throw new IllegalArgumentException("No terminal ropes present."); this.currentRopePos = -1; this.currentAbsolutePos = -1; } public void moveBackwards(final int amount) { if (!this.canMoveBackwards(amount)) throw new IllegalArgumentException("Unable to move backwards " + amount + "."); this.currentRopePos -= amount; this.currentAbsolutePos -= amount; } public void moveForward(final int amount) { this.currentAbsolutePos += amount; int remainingAmt = amount; while (remainingAmt != 0) { final int available = this.currentRope.length() - this.currentRopePos - 1; if (remainingAmt <= available) { this.currentRopePos += remainingAmt; return; } remainingAmt -= available; if (this.toTraverse.isEmpty()) { this.currentAbsolutePos -= remainingAmt; throw new IllegalArgumentException("Unable to move forward " + amount + ". Reached end of rope."); } while (!this.toTraverse.isEmpty()) { this.currentRope = this.toTraverse.pop(); if (this.currentRope instanceof ConcatenationRope) { this.toTraverse.push(((ConcatenationRope) this.currentRope).getRight()); this.toTraverse.push(((ConcatenationRope) this.currentRope).getLeft()); } else { this.currentRopePos = -1; break; } } } } @Override public Character next() { this.moveForward(1 + this.skip); this.skip = 0; return this.currentRope.charAt(this.currentRopePos); } @Override public void remove() { throw new UnsupportedOperationException("Rope iterator is read-only."); } /* (non-Javadoc) * @see org.ahmadsoft.ropes.impl.RopeIterators#skip(int) */ public void skip(final int skip) { this.skip = skip; } }