/*
* esodata - data structures and other things, of varying utility
* Copyright 2022, Ben Culkin
*
* This program 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.
*
* This program 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 this program. If not, see .
*/
package bjc.data;
import java.util.*;
/**
* ListIterator which allows navigation/marking of an iterator.
*
* @author bjcul
*
* @param The element type
*/
public class MarkListIterator implements ListIterator {
private Iterator backing;
private List cache;
private Deque marks;
private int currIdx;
private int maxIdx;
/**
* Create a new marking list iterator.
*
* @param backing The iterator which backs us.
*/
public MarkListIterator(Iterator backing) {
this.backing = backing;
this.currIdx = 0;
this.maxIdx = 0;
this.cache = new ArrayList<>();
this.marks = new ArrayDeque<>();
}
/**
* Get the current element of the iterator.
*
* @return The current iterator of the element
*/
public E current() {
return cache.get(currIdx);
}
/**
* Create a new marking list iterator.
*
* @param backing The iterable to get the backing iterator from.
*/
public MarkListIterator(Iterable backing) {
this(backing.iterator());
}
@Override
public boolean hasNext() {
if (currIdx < maxIdx)
return true;
return backing.hasNext();
}
@Override
public E next() {
if (currIdx < maxIdx) {
return cache.get(currIdx++);
}
currIdx++;
maxIdx++;
E next = backing.next();
cache.add(next);
return next;
}
@Override
public boolean hasPrevious() {
return maxIdx > 0 && currIdx > 0;
}
@Override
public E previous() {
currIdx--;
return cache.get(currIdx);
}
@Override
public int nextIndex() {
return currIdx + 1;
}
@Override
public int previousIndex() {
return currIdx - 1;
}
/**
* Mark the current position in the iterator.
*/
public void mark() {
marks.push(currIdx);
}
/**
* Reset the iterator to the last position that was marked, leaving the mark in
* place.
*
* @return Whether or not a rollback actually happened
*/
public boolean rollback() {
return rollback(false);
}
/**
* Reset the iterator to the last position that was marked.
*
* @param clearMark Whether to clear the mark being rolled back to.
*
* @return Whether or not a rollback actually happened
*/
public boolean rollback(boolean clearMark) {
if (marks.isEmpty()) return false;
if (clearMark) {
currIdx = marks.pop();
} else {
currIdx = marks.peek();
}
return true;
}
/**
* Remove the last position that was marked.
*
* @return The marked position
*/
public int commit() {
return marks.pop();
}
/**
* Resets the cache state of this iterator.
*
* Once this has been called, all of the previous elements and marks are
* discarded.
*/
public void reset() {
this.cache = new ArrayList<>();
this.marks = new ArrayDeque<>();
this.currIdx = 0;
this.maxIdx = 0;
}
@Override
public void remove() {
throw new UnsupportedOperationException("Can't remove items");
}
@Override
public void set(E e) {
throw new UnsupportedOperationException("Can't set items");
}
@Override
public void add(E e) {
throw new UnsupportedOperationException("Can't add items");
}
/**
* Check if this iterator has at least one active mark.
*
* @return Whether this iterator has any active marks.
*/
public boolean hasMark() {
return !marks.isEmpty();
}
}