package openmods.utils; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import java.util.Collection; import java.util.Iterator; import java.util.List; public class Stack<E> implements Iterable<E> { private final List<E> data; private final int bottomElement; public Stack() { this.data = Lists.newArrayList(); this.bottomElement = 0; } public Stack(int initialCapacity) { this.data = Lists.newArrayListWithCapacity(initialCapacity); this.bottomElement = 0; } private Stack(List<E> data, int bottomElement) { this.data = data; this.bottomElement = bottomElement; } public void push(E value) { data.add(value); } public void pushAll(Collection<E> values) { data.addAll(values); } public void checkIsNonEmpty() { if (isEmpty()) throw new StackUnderflowException(); } private void checkIndex(int index) { if (index < bottomElement) throw new StackUnderflowException(); } public E pop() { checkIsNonEmpty(); try { return data.remove(data.size() - 1); } catch (IndexOutOfBoundsException e) { throw new StackUnderflowException(); } } public E popAndExpectEmptyStack() { if (size() != 1) throw new StackUnderflowException("Expected exactly one element, got %d, contents: %s", size(), printContents()); return pop(); } private int indexFromTop(int index) { return data.size() - 1 - index; } public E peek(int index) { final int peekIndex = indexFromTop(index); checkIndex(peekIndex); return data.get(peekIndex); } public void dup() { checkIsNonEmpty(); E last = data.get(data.size() - 1); data.add(last); } public E drop(int index) { final int dropIndex = indexFromTop(index); checkIndex(dropIndex); return data.remove(dropIndex); } public int size() { return data.size() - bottomElement; } public boolean isEmpty() { return size() == 0; } public static <T> Stack<T> create() { return new Stack<T>(); } @Override public Iterator<E> iterator() { return data.listIterator(bottomElement); } public void clear() { if (bottomElement == 0) data.clear(); final int size = data.size(); if (size - bottomElement > 0) data.subList(bottomElement, size).clear(); } public Stack<E> substack(int depth) { final int newBottom = data.size() - depth; if (newBottom < bottomElement) throw new StackUnderflowException(String.format("Not enough elements to create substack: required %s, size %d", depth, size())); return newBottom == 0? this : new Stack<E>(data, newBottom); } public Stack<E> checkIsEmpty() { if (!isEmpty()) throw new StackValidationException("Expected empty stack, but actually contains: %s", printContents()); return this; } public Stack<E> checkSizeIsExactly(int expectedSize) { if (size() != expectedSize) throw new StackUnderflowException("Expected stack size %d, got %d, contents: %s", expectedSize, size(), printContents()); return this; } public Stack<E> checkSizeIsAtLeast(int expectedSize) { if (size() < expectedSize) throw new StackUnderflowException("Expected stack size >= %d, got %d, contents: %s", expectedSize, size(), printContents()); return this; } public String printContents() { return Iterables.toString(this); } @Override public String toString() { return printContents(); } }