package com.googlecode.totallylazy.collections; import com.googlecode.totallylazy.functions.Function1; import com.googlecode.totallylazy.functions.Functions; import com.googlecode.totallylazy.Option; import com.googlecode.totallylazy.annotations.tailrec; import static com.googlecode.totallylazy.Option.none; import static com.googlecode.totallylazy.collections.PersistentList.functions; public class ListZipper<T> implements Zipper<T> { public final PersistentList<T> focus; public final PersistentList<T> breadcrumbs; private ListZipper(PersistentList<T> focus, PersistentList<T> breadcrumbs) { this.focus = focus; this.breadcrumbs = breadcrumbs; } public static <T> ListZipper<T> zipper(PersistentList<T> focus) { return zipper(focus, PersistentList.constructors.<T>empty()); } private static <T> ListZipper<T> zipper(PersistentList<T> focus, PersistentList<T> breadcrumbs) { return new ListZipper<T>(focus, breadcrumbs); } public Option<ListZipper<T>> nextOption() { if(isLast()) return none(); return focus.headOption().map(t -> zipper(focus.tail(), breadcrumbs.cons(t))); } public Option<ListZipper<T>> previousOption() { return breadcrumbs.headOption().map(t -> zipper(focus.cons(t), breadcrumbs.tail())); } @Override public ListZipper<T> next() { return nextOption().get(); } @Override public ListZipper<T> previous() { return previousOption().get(); } @Override public ListZipper<T> first() { return top(); } @Override public ListZipper<T> last() { return bottom(); } @Override public boolean isFirst() { return isTop(); } @Override public boolean isLast() { return isBottom(); } @tailrec public ListZipper<T> top() { if (isTop()) return this; return previous().top(); } @tailrec public ListZipper<T> bottom() { if (isBottom()) return this; return next().bottom(); } @Override public T value() { return focus.head(); } @Override public int index() { return breadcrumbs.size(); } @Override public ListZipper<T> index(int index) { int position = index(); if (position == index) return this; if (position < index) return next().index(index); return previous().index(index); } @Override public int hashCode() { return focus.hashCode() * breadcrumbs.hashCode(); } @Override public boolean equals(Object other) { return other instanceof ListZipper && (((ListZipper) other).focus.equals(focus) && ((ListZipper) other).breadcrumbs.equals(breadcrumbs)); } @Override public String toString() { return String.format("focus(%s), breadcrumbs(%s)", focus, breadcrumbs); } public ListZipper<T> modify(Function1<? super PersistentList<T>, ? extends PersistentList<T>> callable) { return zipper(Functions.call(callable, focus), breadcrumbs); } public PersistentList<T> toList() { return top().focus; } public ListZipper<T> insert(T instance) { return modify(functions.cons(instance)); } public ListZipper<T> remove() { return delete(); } public ListZipper<T> delete() { return modify(functions.<T>tail()); } public T current() { return focus.head(); } public boolean isBottom() { return focus.tail().isEmpty(); } public boolean isTop() { return breadcrumbs.isEmpty(); } }