package org.androiddaisyreader.model;
import java.util.ListIterator;
import java.util.Stack;
/**
* Navigates through the structure of a book.
*
* This doesn't process the contents, which is handled at the Section level.
*
* @author jharty
*/
public class Navigator {
private Book book;
private Stack<ListIterator<? extends Navigable>> stack = new Stack<ListIterator<? extends Navigable>>();
/**
* Creates a new navigator, which is initialised at the start of the book.
*
* TODO 20120301 (jharty): add a mechanism to navigate to a chosen location.
*
* @param book the book to read.
*/
public Navigator(Book book) {
this.book = book;
gotoStartOfContent();
}
/**
* Reset navigation to the start of the content; generally the beginning of
* the book.
*/
public void gotoStartOfContent() {
stack.clear();
stack.push(book.getChildren().listIterator());
}
// think about goto(navigation_point);
/**
* Is there a next section?
*
* @return true if there is, else return false.
*/
public boolean hasNext() {
while ((stack.size() > 1) && !stack.peek().hasNext()) {
stack.pop();
}
return stack.peek().hasNext();
}
/**
* Is there a previous section?
*
* @return true if there is, else return false.
*/
public boolean hasPrevious() {
if ((stack.size() > 1) || stack.peek().hasPrevious()) {
return true;
}
return false;
}
/**
* Navigate to the next section.
*
* @return the next item.
*/
public Navigable next() {
Navigable item;
if (!hasNext()) {
return null;
}
item = stack.peek().next();
if (item.getChildren().size() > 0) {
stack.push(item.getChildren().listIterator());
}
return item;
}
/**
* Navigate to the previous section.
*
* @return the previous item.
*/
public Navigable previous() {
// TODO 20120127 (jharty): I'm sure this logic is overly complicated.
// Simplify and make more elegant.
Navigable item;
if (!hasPrevious()) {
return null;
}
if (stack.peek().hasPrevious()) {
boolean pushedItem = false;
item = stack.peek().previous();
// Now try to get to the bottom right root
while (item.getChildren().size() > 0) {
ListIterator<? extends Navigable> nextLevel = item.getChildren().listIterator();
if (!pushedItem) {
// We need to reset the cursor to before the current item as
// we will not use it yet.
// It will be used when the stack is popped again.
stack.peek().next();
}
while (nextLevel.hasNext()) {
item = nextLevel.next();
}
stack.push(nextLevel);
pushedItem = true;
}
if (pushedItem) {
// Reset the cursor to be before the item we're about to return.
stack.peek().previous();
}
} else {
// We have finished at this level, go up one level
stack.pop();
// How do we get the current item on the stack
item = stack.peek().previous();
}
return item;
}
}