/*
GNU LESSER GENERAL PUBLIC LICENSE
Copyright (C) 2006 The Lobo Project
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Contact info: lobochief@users.sourceforge.net
*/
/*
* Created on Jun 6, 2005
*/
package org.lobobrowser.util;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;
/**
* @author J. H. S.
*/
// TODO: Looks like it is not used
public class History implements java.io.Serializable {
private static final long serialVersionUID = 2257845000800300100L;
private transient ArrayList<String> historySequence;
private final SortedSet<String> historySortedSet = new TreeSet<>();
private final Map<String, TimedEntry> historyMap = new HashMap<>();
private final SortedSet<TimedEntry> historyTimedSet = new TreeSet<>();
private int sequenceCapacity;
private int commonEntriesCapacity;
private transient int sequenceIndex;
/**
* @param sequenceCapacity
* @param commonEntriesCapacity
*/
public History(final int sequenceCapacity, final int commonEntriesCapacity) {
super();
this.historySequence = new ArrayList<>();
this.sequenceIndex = -1;
this.sequenceCapacity = sequenceCapacity;
this.commonEntriesCapacity = commonEntriesCapacity;
}
private void readObject(final java.io.ObjectInputStream in) throws ClassNotFoundException, java.io.IOException {
this.historySequence = new ArrayList<>();
this.sequenceIndex = -1;
in.defaultReadObject();
}
/**
* @return Returns the commonEntriesCapacity.
*/
public int getCommonEntriesCapacity() {
return commonEntriesCapacity;
}
/**
* @param commonEntriesCapacity
* The commonEntriesCapacity to set.
*/
public void setCommonEntriesCapacity(final int commonEntriesCapacity) {
this.commonEntriesCapacity = commonEntriesCapacity;
}
/**
* @return Returns the sequenceCapacity.
*/
public int getSequenceCapacity() {
return sequenceCapacity;
}
/**
* @param sequenceCapacity
* The sequenceCapacity to set.
*/
public void setSequenceCapacity(final int sequenceCapacity) {
this.sequenceCapacity = sequenceCapacity;
}
public String getCurrentItem() {
if (this.sequenceIndex >= 0) {
return this.historySequence.get(this.sequenceIndex);
} else {
return null;
}
}
public String back() {
if (this.sequenceIndex > 0) {
this.sequenceIndex--;
return this.getCurrentItem();
} else {
return null;
}
}
public String forward() {
if ((this.sequenceIndex + 1) < this.historySequence.size()) {
this.sequenceIndex++;
return this.getCurrentItem();
} else {
return null;
}
}
public Collection<String> getRecentItems(final int maxNumItems) {
final Collection<String> items = new LinkedList<>();
final Iterator<TimedEntry> i = this.historyTimedSet.iterator();
int count = 0;
while (i.hasNext() && (count++ < maxNumItems)) {
final TimedEntry entry = i.next();
items.add(entry.value);
}
return items;
}
public Collection<String> getHeadMatchItems(final String item, final int maxNumItems) {
final String[] array = ArrayUtilities.copy(this.historySortedSet, String.class);
final int idx = Arrays.binarySearch(array, item);
final int startIdx = idx >= 0 ? idx : (-idx - 1);
int count = 0;
final Collection<String> items = new LinkedList<>();
for (int i = startIdx; (i < array.length) && (count++ < maxNumItems); i++) {
final String potentialItem = array[i];
if (potentialItem.startsWith(item)) {
items.add(potentialItem);
} else {
break;
}
}
return items;
}
public void addAsRecentOnly(final String item) {
TimedEntry entry = this.historyMap.get(item);
if (entry != null) {
this.historyTimedSet.remove(entry);
entry.touch();
this.historyTimedSet.add(entry);
} else {
entry = new TimedEntry(item);
this.historyTimedSet.add(entry);
this.historyMap.put(item, entry);
this.historySortedSet.add(item);
if (this.historyTimedSet.size() > this.commonEntriesCapacity) {
// Most outdated goes last
final TimedEntry entryToRemove = this.historyTimedSet.last();
this.historyMap.remove(entryToRemove.value);
this.historySortedSet.remove(entryToRemove.value);
this.historyTimedSet.remove(entryToRemove);
}
}
}
public void addItem(final String item, final boolean updateAsRecent) {
final int newIndex = this.sequenceIndex + 1;
while (newIndex >= this.historySequence.size()) {
this.historySequence.add(null);
}
this.historySequence.set(newIndex, item);
this.sequenceIndex = newIndex;
final int expectedSize = newIndex + 1;
while (this.historySequence.size() > expectedSize) {
this.historySequence.remove(expectedSize);
}
while (this.historySequence.size() > this.sequenceCapacity) {
this.historySequence.remove(0);
this.sequenceIndex--;
}
if (updateAsRecent) {
this.addAsRecentOnly(item);
}
}
private class TimedEntry implements Comparable<TimedEntry>, java.io.Serializable {
private static final long serialVersionUID = 2257845000000000200L;
private long timestamp = System.currentTimeMillis();
private final String value;
/**
* @param value
*/
public TimedEntry(final String value) {
this.value = value;
}
public void touch() {
this.timestamp = System.currentTimeMillis();
}
@Override
public boolean equals(final Object obj) {
final TimedEntry other = (TimedEntry) obj;
return other.value.equals(this.value);
}
/*
* (non-Javadoc)
*
* @see java.lang.Comparable#compareTo(java.lang.Object)
*/
public int compareTo(final TimedEntry other) {
if (this.equals(other)) {
return 0;
}
final long time1 = this.timestamp;
final long time2 = other.timestamp;
if (time1 > time2) {
// More recent goes first
return -1;
} else if (time2 > time1) {
return +1;
} else {
final int diff = System.identityHashCode(this) - System.identityHashCode(other);
if (diff == 0) {
return +1;
}
return diff;
}
}
}
}