package nl.siegmann.epublib.browsersupport;
import java.util.ArrayList;
import java.util.List;
import nl.siegmann.epublib.domain.Book;
import nl.siegmann.epublib.domain.Resource;
/**
* A history of the user's locations with the epub.
*
* @author paul.siegmann
*
*/
public class NavigationHistory implements NavigationEventListener {
public static final int DEFAULT_MAX_HISTORY_SIZE = 1000;
private static final long DEFAULT_HISTORY_WAIT_TIME = 1000;
private static class Location {
private String href;
public Location(String href) {
super();
this.href = href;
}
@SuppressWarnings("unused")
public void setHref(String href) {
this.href = href;
}
public String getHref() {
return href;
}
}
private long lastUpdateTime = 0;
private List<Location> locations = new ArrayList<>();
private Navigator navigator;
private int currentPos = -1;
private int currentSize = 0;
private int maxHistorySize = DEFAULT_MAX_HISTORY_SIZE;
private long historyWaitTime = DEFAULT_HISTORY_WAIT_TIME;
public NavigationHistory(Navigator navigator) {
this.navigator = navigator;
navigator.addNavigationEventListener(this);
initBook(navigator.getBook());
}
public int getCurrentPos() {
return currentPos;
}
public int getCurrentSize() {
return currentSize;
}
public void initBook(Book book) {
if (book == null) {
return;
}
locations = new ArrayList<>();
currentPos = -1;
currentSize = 0;
if (navigator.getCurrentResource() != null) {
addLocation(navigator.getCurrentResource().getHref());
}
}
/**
* If the time between a navigation event is less than the historyWaitTime then the new location is not added to the history.
* When a user is rapidly viewing many pages using the slider we do not want all of them to be added to the history.
*
* @return
*/
public long getHistoryWaitTime() {
return historyWaitTime;
}
public void setHistoryWaitTime(long historyWaitTime) {
this.historyWaitTime = historyWaitTime;
}
public void addLocation(Resource resource) {
if (resource == null) {
return;
}
addLocation(resource.getHref());
}
/**
* Adds the location after the current position.
* If the currentposition is not the end of the list then the elements between the current element and the end of the list will be discarded.
* Does nothing if the new location matches the current location.
* <br/>
* If this nr of locations becomes larger then the historySize then the first item(s) will be removed.
*
* @param location
* @return
*/
public void addLocation(Location location) {
// do nothing if the new location matches the current location
if ( !(locations.isEmpty()) &&
location.getHref().equals(locations.get(currentPos).getHref())) {
return;
}
currentPos++;
if (currentPos != currentSize) {
locations.set(currentPos, location);
} else {
locations.add(location);
checkHistorySize();
}
currentSize = currentPos + 1;
}
/**
* Removes all elements that are too much for the maxHistorySize out of the history.
*
*/
private void checkHistorySize() {
while(locations.size() > maxHistorySize) {
locations.remove(0);
currentSize--;
currentPos--;
}
}
public void addLocation(String href) {
addLocation(new Location(href));
}
private String getLocationHref(int pos) {
if (pos < 0 || pos >= locations.size()) {
return null;
}
return locations.get(currentPos).getHref();
}
/**
* Moves the current positions delta positions.
*
* move(-1) to go one position back in history.<br/>
* move(1) to go one position forward.<br/>
*
* @param delta
*
* @return Whether we actually moved. If the requested value is illegal it will return false, true otherwise.
*/
public boolean move(int delta) {
if (((currentPos + delta) < 0)
|| ((currentPos + delta) >= currentSize)) {
return false;
}
currentPos += delta;
navigator.gotoResource(getLocationHref(currentPos), this);
return true;
}
/**
* If this is not the source of the navigationEvent then the addLocation will be called with the href of the currentResource in the navigationEvent.
*/
@Override
public void navigationPerformed(NavigationEvent navigationEvent) {
if (this == navigationEvent.getSource()) {
return;
}
if (navigationEvent.getCurrentResource() == null) {
return;
}
if ((System.currentTimeMillis() - this.lastUpdateTime) > historyWaitTime) {
// if the user scrolled rapidly through the pages then the last page will not be added to the history. We fix that here:
addLocation(navigationEvent.getOldResource());
addLocation(navigationEvent.getCurrentResource().getHref());
}
lastUpdateTime = System.currentTimeMillis();
}
public String getCurrentHref() {
if (currentPos < 0 || currentPos >= locations.size()) {
return null;
}
return locations.get(currentPos).getHref();
}
public void setMaxHistorySize(int maxHistorySize) {
this.maxHistorySize = maxHistorySize;
}
public int getMaxHistorySize() {
return maxHistorySize;
}
}