/** * */ package net.varkhan.base.containers; /** * <b>A bidirectional iterator over an Indexed content container.</b> * <p/> * IndexIterators enumerate valid {@code long} indexes for the contents * of an {@link Indexed} container, allowing both forward and backward * traversal. * <p/> * * @author varkhan * @date Mar 11, 2009 * @time 12:55:58 AM */ public interface Index { /** * Returns the current identifier this iterators points to. * * @return the current identifier */ public long current(); /** * Returns true if the iteration has identifiers after this one. * * @return true if the iteration has identifiers after this one */ public boolean hasNext(); /** * Returns the next identifier in the iteration. * * @return the next identifier in the iteration */ public long next(); /** * Returns true if the iteration has identifiers before this one. * * @return true if the iteration has identifiers before this one */ public boolean hasPrevious(); /** * Returns the previous identifier in the iteration. * * @return the previous identifier in the iteration */ public long previous(); /********************************************************************************** ** Static predefined iterators **/ /** * An empty index iterator (whose {@code hasNext()} and {@code hasPrevious()} * methods always return {@literal false}, and {@code current()}, {@code next()} * and {@code previous()} methods throw an {@link InvalidIndexException}). */ public static final Index EMPTY=new Empty(); /** * An empty index iterator (whose {@code hasNext()} and {@code hasPrevious()} * methods always return {@literal false}, and {@code current()}, {@code next()} * and {@code previous()} methods throw an {@link InvalidIndexException}). */ public static final class Empty implements Index { public long current() { throw new InvalidIndexException(); } public boolean hasNext() { return false; } public long next() { throw new InvalidIndexException(); } public boolean hasPrevious() { return false; } public long previous() { throw new InvalidIndexException(); } } /** * A singleton iterator, that returns a single element. */ public static final class Singleton implements Index { private final long element; private volatile boolean available=true; /** * Create a singleton index iterator. * * @param e the single element this iterator returns */ public Singleton(long e) { element=e; } public long current() { return element; } public boolean hasNext() { return available; } public long next() { if(!available) throw new InvalidIndexException(); available=false; return element; } public boolean hasPrevious() { return false; } public long previous() { throw new InvalidIndexException(); } } /** * A range iterator, that returns all indexes in a range. */ public static final class Range implements Index { private final long min; private final long max; private volatile long pos; /** * Create a range index iterator. * * @param min the lowest index to return * @param max the highest index to return */ public Range(long min, long max) { this.min = min; this.max = max; this.pos = min-1; } public long current() { return pos; } public boolean hasNext() { return pos<max; } public long next() { if(pos>=max) throw new InvalidIndexException(); return ++pos; } public boolean hasPrevious() { return pos>min; } public long previous() { if(pos<=min) throw new InvalidIndexException(); return --pos; } } /** * An enumeration iterator, that returns all indexes in an array, in order. */ public static final class Enumerate implements Index { private final long[] elements; private volatile int pos = -1; /** * Create a range index iterator. * * @param e the array of all the indexes to return */ public Enumerate(long... e) { elements = e; } public long current() { return elements[pos]; } public boolean hasNext() { return pos+1<elements.length; } public long next() { if(pos+1>=elements.length) throw new Sequence.InvalidIndexException(); return elements[++pos]; } public boolean hasPrevious() { return pos>0; } public long previous() { if(pos<=0) throw new Sequence.InvalidIndexException(); return elements[--pos]; } } /** * A sequence iterator, that returns all the indexes of a series of Indexes, in order. */ public static final class Sequence implements Index { private final Index[] segments; private volatile int pos = 0; /** * Create a sequence iterator. * * @param s the array of all the subsequence iterators */ public Sequence(Index... s) { segments = s; } public long current() { return segments[pos].current(); } public boolean hasNext() { int pos = this.pos; if(pos<0) pos = 0; while(pos<segments.length && !segments[pos].hasNext()) pos ++; return pos<segments.length; } public long next() { if(pos<0) pos = 0; while(pos<segments.length && !segments[pos].hasNext()) pos ++; if(pos>=segments.length) throw new InvalidIndexException(); return segments[pos].next(); } public boolean hasPrevious() { int pos = this.pos; if(pos>=segments.length) pos = segments.length-1; while(pos>=0 && !segments[pos].hasPrevious()) pos --; return pos>=0; } public long previous() { if(pos>=segments.length) pos = segments.length-1; while(pos>=0 && !segments[pos].hasPrevious()) pos --; if(pos<0) throw new InvalidIndexException(); return segments[pos].next(); } } /********************************************************************************** ** Exceptions **/ /** * <b>An exception thrown upon lookup of a non-existing index.</b> * <p/> * An {@link Index} or an {@link Indexed} content container may * throw this exception when asked to retrieve an object at an invalid * index, or an index that does not have associated content. */ public static class InvalidIndexException extends RuntimeException { private static final long serialVersionUID=1L; /** * The offending index */ private final long index; /** * Constructs a new runtime exception with {@literal null} as its * detail message. The cause is not initialized, and may subsequently be * initialized by a call to {@link #initCause}. */ public InvalidIndexException() { this.index=-1L; } /** * Constructs a new runtime exception with the specified detail message. * The cause is not initialized, and may subsequently be initialized by a * call to {@link #initCause}. * * @param message the detail message. The detail message is saved for * later retrieval by the {@link #getMessage()} method. */ public InvalidIndexException(String message) { super(message); this.index=-1L; } /** * Constructs a new runtime exception with the specified detail message and * cause. * * @param message the detail message (which is saved for later retrieval * by the {@link #getMessage()} method). * @param cause the cause (which is saved for later retrieval by the * {@link #getCause()} method). (A {@literal null} value is * permitted, and indicates that the cause is nonexistent or * unknown.) */ public InvalidIndexException(String message, Throwable cause) { super(message, cause); this.index=-1L; } /** * Constructs a new runtime exception with {@literal null} as its * detail message. * * @param index the invalid index */ public InvalidIndexException(long index) { this.index=index; } /** * Constructs a new runtime exception with the specified detail message. * The cause is not initialized, and may subsequently be initialized by a * call to {@link #initCause}. * * @param message the detail message. The detail message is saved for * later retrieval by the {@link #getMessage()} method. * @param index the invalid index */ public InvalidIndexException(String message, long index) { super(message); this.index=index; } /** * Gets the value of the index that caused this exception to be raised * * @return the invalid index */ public long getIndex() { return index; } } }