/* * 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 com.nvarghese.beowulf.common.cobra.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. */ public class History /* implements java.io.Serializable */{ // private static final long serialVersionUID = 2257845000800300100L; private transient ArrayList historySequence; private final SortedSet historySortedSet = new TreeSet(); private final Map historyMap = new HashMap(); private final SortedSet 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 (String) 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 getRecentItems(final int maxNumItems) { Collection items = new LinkedList(); Iterator i = this.historyTimedSet.iterator(); int count = 0; while (i.hasNext() && count++ < maxNumItems) { TimedEntry entry = (TimedEntry) i.next(); items.add(entry.value); } return items; } public Collection getHeadMatchItems(final String item, final int maxNumItems) { Object[] array = this.historySortedSet.toArray(); int idx = Arrays.binarySearch(array, item); int startIdx = idx >= 0 ? idx : (-idx - 1); int count = 0; Collection items = new LinkedList(); 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; } public void addAsRecentOnly(final String item) { TimedEntry entry = (TimedEntry) 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 = (TimedEntry) 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) { 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); } } private class TimedEntry implements Comparable/* , 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(); } public boolean equals(final Object obj) { if (obj != null) { TimedEntry other = (TimedEntry) obj; return other.value.equals(this.value); } return false; } @Override public int hashCode() { return super.hashCode(); } /* * (non-Javadoc) * * @see java.lang.Comparable#compareTo(java.lang.Object) */ public int compareTo(final Object arg0) { if (this.equals(arg0)) { return 0; } TimedEntry other = (TimedEntry) arg0; long time1 = this.timestamp; long time2 = other.timestamp; if (time1 > time2) { // More recent goes first return -1; } else if (time2 > time1) { return +1; } else { int diff = System.identityHashCode(this) - System.identityHashCode(other); if (diff == 0) { return +1; } return diff; } } } }