/* GNU GENERAL LICENSE Copyright (C) 2006 The Lobo Project. Copyright (C) 2014 - 2017 Lobo Evolution This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either verion 3 of the License, or (at your option) any later version. This program 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 General License for more details. You should have received a copy of the GNU General Public along with this program. If not, see <http://www.gnu.org/licenses/>. Contact info: lobochief@users.sourceforge.net; ivan.difrancesco@yahoo.it */ /* * Created on Jun 6, 2005 */ package org.lobobrowser.http; import java.io.IOException; import java.io.ObjectInputStream; 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; /** * The Class History. * * @author J. H. S. */ public class History implements java.io.Serializable { /** The Constant serialVersionUID. */ private static final long serialVersionUID = 2257845000800300100L; /** The history sequence. */ private transient ArrayList<String> historySequence; /** The history sorted set. */ private final SortedSet<String> historySortedSet = new TreeSet<String>(); /** The history map. */ private final Map<String, TimedEntry> historyMap = new HashMap<String, TimedEntry>(); /** The history timed set. */ private final SortedSet<TimedEntry> historyTimedSet = new TreeSet<TimedEntry>(); /** The sequence capacity. */ private int sequenceCapacity; /** The common entries capacity. */ private int commonEntriesCapacity; /** The sequence index. */ private transient int sequenceIndex; /** * Instantiates a new history. * * @param sequenceCapacity * the sequence capacity * @param commonEntriesCapacity * the common entries capacity */ public History(int sequenceCapacity, int commonEntriesCapacity) { super(); this.historySequence = new ArrayList<String>(); this.sequenceIndex = -1; this.sequenceCapacity = sequenceCapacity; this.commonEntriesCapacity = commonEntriesCapacity; } /** * Read object. * * @param in * the in * @throws ClassNotFoundException * the class not found exception * @throws IOException * Signals that an I/O exception has occurred. */ private void readObject(ObjectInputStream in) throws ClassNotFoundException, IOException { this.historySequence = new ArrayList<String>(); this.sequenceIndex = -1; in.defaultReadObject(); } /** Gets the common entries capacity. * * @return the common entries capacity */ public int getCommonEntriesCapacity() { return commonEntriesCapacity; } /** Sets the common entries capacity. * * @param commonEntriesCapacity * the new common entries capacity */ public void setCommonEntriesCapacity(int commonEntriesCapacity) { this.commonEntriesCapacity = commonEntriesCapacity; } /** Gets the sequence capacity. * * @return the sequence capacity */ public int getSequenceCapacity() { return sequenceCapacity; } /** Sets the sequence capacity. * * @param sequenceCapacity * the new sequence capacity */ public void setSequenceCapacity(int sequenceCapacity) { this.sequenceCapacity = sequenceCapacity; } /** Gets the current item. * * @return the current item */ public String getCurrentItem() { if (this.sequenceIndex >= 0) { return this.historySequence.get(this.sequenceIndex); } else { return null; } } /** * Back. * * @return the string */ public String back() { if (this.sequenceIndex > 0) { this.sequenceIndex--; return this.getCurrentItem(); } else { return null; } } /** * Forward. * * @return the string */ public String forward() { if ((this.sequenceIndex + 1) < this.historySequence.size()) { this.sequenceIndex++; return this.getCurrentItem(); } else { return null; } } /** * Gets the recent items. * * @param maxNumItems * the max num items * @return the recent items */ public Collection<String> getRecentItems(int maxNumItems) { Collection<String> items = new LinkedList<String>(); Iterator<TimedEntry> i = this.historyTimedSet.iterator(); int count = 0; while (i.hasNext() && (count++ < maxNumItems)) { TimedEntry entry = i.next(); items.add(entry.value); } return items; } /** * Gets the head match items. * * @param item * the item * @param maxNumItems * the max num items * @return the head match items */ public Collection<String> getHeadMatchItems(String item, int maxNumItems) { Object[] array = this.historySortedSet.toArray(); int idx = Arrays.binarySearch(array, item); int startIdx = idx >= 0 ? idx : (-idx - 1); int count = 0; Collection<String> items = new LinkedList<String>(); for (int i = startIdx; (i < array.length) && (count++ < maxNumItems); i++) { String potentialItem = (String) array[i]; if (potentialItem.startsWith(item)) { items.add(potentialItem); } else { break; } } return items; } /** * Adds the as recent only. * * @param item * the item */ public void addAsRecentOnly(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 TimedEntry entryToRemove = this.historyTimedSet.last(); this.historyMap.remove(entryToRemove.value); this.historySortedSet.remove(entryToRemove.value); this.historyTimedSet.remove(entryToRemove); } } } /** * Adds the item. * * @param item * the item * @param updateAsRecent * the update as recent */ public void addItem(String item, boolean updateAsRecent) { int newIndex = this.sequenceIndex + 1; while (newIndex >= this.historySequence.size()) { this.historySequence.add(null); } this.historySequence.set(newIndex, item); this.sequenceIndex = newIndex; 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); } } }