package org.testory.common; import static java.util.Objects.requireNonNull; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.NoSuchElementException; import java.util.Objects; public class Chain<E> implements Iterable<E> { private final int size; private final E element; private final Chain<E> tail; private Chain(int size, E element, Chain<E> tail) { this.tail = tail; this.size = size; this.element = element; } public static <E> Chain<E> chain() { return new Chain<>(0, null, null); } public int size() { return size; } public E get() { checkHasElement(); return element; } public Chain<E> add(E newElement) { return new Chain<>(size + 1, requireNonNull(newElement), this); } public Chain<E> remove() { checkHasElement(); return tail; } public Chain<E> reverse() { Chain<E> reversed = chain(); for (E e : this) { reversed = reversed.add(e); } return reversed; } public Iterator<E> iterator() { return new Iterator<E>() { Chain<E> chain = Chain.this; public boolean hasNext() { return chain.hasElement(); } public E next() { E nextElement = chain.get(); chain = chain.tail; return nextElement; } public void remove() { throw new UnsupportedOperationException(); } }; } public boolean equals(Object obj) { return obj instanceof Chain && equals((Chain<?>) obj); } private boolean equals(Chain<?> chain) { if (size != chain.size) { return false; } Chain<E> first = this; Chain<?> second = chain; while (first.size > 0) { if (first == second) { return true; } if (!Objects.equals(first.element, second.element)) { return false; } first = first.tail; second = second.tail; } return true; } public int hashCode() { int hash = 0xFFFF; Chain<E> chain = this; while (chain.size > 0) { hash += element.hashCode(); hash *= 0xFFFF; chain = chain.tail; } return hash; } public String toString() { List<E> elements = new LinkedList<>(); Chain<E> chain = this; while (chain.size > 0) { elements.add(chain.element); chain = chain.tail; } return elements.toString(); } private void checkHasElement() { if (!hasElement()) { throw new NoSuchElementException(); } } private boolean hasElement() { return size != 0; } }