summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Culkin <scorpress@gmail.com>2022-08-16 23:04:56 -0400
committerBen Culkin <scorpress@gmail.com>2022-08-16 23:04:56 -0400
commite43dc808d7304b90327c1def4452f6e3d9946983 (patch)
tree7797eef414fb53d519076a471f8f8c65c61c71c5
parent081083574d93d983c0189907e5e829cb8c853188 (diff)
Update iterator capabilities
-rw-r--r--src/main/java/bjc/data/MarkListIterator.java166
-rw-r--r--src/main/java/bjc/data/ResettableIterator.java14
-rw-r--r--src/main/java/bjc/esodata/Stack.java2
-rw-r--r--src/test/java/bjc/data/MarkListIteratorTest.java41
-rw-r--r--src/test/java/bjc/test/TestUtils.java8
5 files changed, 222 insertions, 9 deletions
diff --git a/src/main/java/bjc/data/MarkListIterator.java b/src/main/java/bjc/data/MarkListIterator.java
new file mode 100644
index 0000000..725b050
--- /dev/null
+++ b/src/main/java/bjc/data/MarkListIterator.java
@@ -0,0 +1,166 @@
+package bjc.data;
+
+import java.util.*;
+
+/**
+ * ListIterator which allows navigation/marking of an iterator.
+ *
+ * @author bjcul
+ *
+ * @param <E> The element type
+ */
+public class MarkListIterator<E> implements ListIterator<E> {
+ private Iterator<E> backing;
+
+ private List<E> cache;
+ private Deque<Integer> marks;
+
+ private int currIdx;
+ private int maxIdx;
+
+ /**
+ * Create a new marking list iterator.
+ *
+ * @param backing The iterator which backs us.
+ */
+ public MarkListIterator(Iterator<E> backing) {
+ this.backing = backing;
+
+ this.currIdx = 0;
+ this.maxIdx = 0;
+
+ this.cache = new ArrayList<>();
+ this.marks = new ArrayDeque<>();
+ }
+
+ /**
+ * Create a new marking list iterator.
+ *
+ * @param backing The iterable to get the backing iterator from.
+ */
+ public MarkListIterator(Iterable<E> 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();
+ }
+}
diff --git a/src/main/java/bjc/data/ResettableIterator.java b/src/main/java/bjc/data/ResettableIterator.java
index 8b7f07a..8c1c4aa 100644
--- a/src/main/java/bjc/data/ResettableIterator.java
+++ b/src/main/java/bjc/data/ResettableIterator.java
@@ -5,12 +5,12 @@ import java.util.*;
/*
* @TODO Oct 6, 2020 - Ben Culkin - :CleverCache
*
- * In the future, there are certain efficencies we could take with our cached
+ * In the future, there are certain efficiencies we could take with our cached
* elements; namely, the case where we repeat the same element multiple times,
* or the case where we have a mixture of identical (and probably sizable) elements
*
- * The general downside to these of course, is that these efficencies would cost
- * us something in terms of complexity, as well as not benefitting iterators which
+ * The general downside to these of course, is that these efficiencies would cost
+ * us something in terms of complexity, as well as not benefiting iterators which
* aren't large enough.
*
* Still an interesting thought as to the best way to implement such a thing though.
@@ -57,7 +57,7 @@ public class ResettableIterator<T> implements Iterator<T> {
@Override
public boolean hasNext() {
if (isRepeating) return cacheIterator.hasNext() ? true : backing.hasNext();
- else return backing.hasNext();
+ return backing.hasNext();
}
@Override
@@ -65,10 +65,10 @@ public class ResettableIterator<T> implements Iterator<T> {
if (isRepeating) {
if (cacheIterator.hasNext()) {
return cacheIterator.next();
- } else {
- cacheIterator = null;
- isRepeating = false;
}
+
+ cacheIterator = null;
+ isRepeating = false;
}
T itm = backing.next();
diff --git a/src/main/java/bjc/esodata/Stack.java b/src/main/java/bjc/esodata/Stack.java
index 360e57d..9dfee17 100644
--- a/src/main/java/bjc/esodata/Stack.java
+++ b/src/main/java/bjc/esodata/Stack.java
@@ -337,7 +337,7 @@ public abstract class Stack<T> {
}
/*
- * Dataflow Combinators
+ * Data-flow Combinators
*/
/**
diff --git a/src/test/java/bjc/data/MarkListIteratorTest.java b/src/test/java/bjc/data/MarkListIteratorTest.java
new file mode 100644
index 0000000..6eb4994
--- /dev/null
+++ b/src/test/java/bjc/data/MarkListIteratorTest.java
@@ -0,0 +1,41 @@
+package bjc.data;
+
+import static org.junit.Assert.*;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.Test;
+
+import bjc.test.TestUtils;
+
+/**
+ * Tests for {@link MarkListIterator}
+ * @author bjcul
+ *
+ */
+@SuppressWarnings("javadoc")
+public class MarkListIteratorTest {
+
+ @Test
+ public void test() {
+ List<String> list1 = Arrays.asList("a", "b", "c");
+
+ MarkListIterator<String> itr1 = new MarkListIterator<>(list1);
+ itr1.mark();
+
+ assertFalse(itr1.hasPrevious());
+ assertTrue(itr1.hasNext());
+ TestUtils.assertIteratorEquals(itr1, "a", "b", "c");
+
+ assertTrue(itr1.hasPrevious());
+ assertFalse(itr1.hasNext());
+
+ itr1.rollback(false);
+
+ assertFalse(itr1.hasPrevious());
+ assertTrue(itr1.hasNext());
+ TestUtils.assertIteratorEquals(itr1, "a", "b", "c");
+ }
+
+}
diff --git a/src/test/java/bjc/test/TestUtils.java b/src/test/java/bjc/test/TestUtils.java
index d79157d..04cdf70 100644
--- a/src/test/java/bjc/test/TestUtils.java
+++ b/src/test/java/bjc/test/TestUtils.java
@@ -21,7 +21,13 @@ public class TestUtils {
public static <T> void assertIteratorEquals(Iterator<T> src, T... vals) {
for (int i = 0; i < vals.length; i++) {
if (src.hasNext()) {
- assertEquals(vals[i], src.next());
+ T actNext = src.next();
+ T expNext = vals[i];
+
+ String fmt = "mismatch at index %d";
+ String msg = String.format(fmt, i);
+
+ assertEquals(msg, expNext, actNext);
} else {
String msg = String.format("not enough values: got %d, wanted %d", i,
vals.length);