package com.limegroup.gnutella.gui.search;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.SortedMap;
import java.util.TreeMap;
import com.limegroup.gnutella.Assert;
import com.limegroup.gnutella.util.ApproximateMatcher;
import com.limegroup.gnutella.util.Comparators;
/**
* Used by TableLineModel to quickly find similar SearchResults. This takes
* advantage of the fact that two SearchResults' are similar only if their file
* sizes are similar. A typical TableLineGrouper is a set of SearchResults',
* {a1,..., an}.
*/
final public class TableLineGrouper {
/** Maps sizes to lists of index/line pairs. This list is needed in case
* there are multiple files with same size. (Performance is severely degraded
* in this case.) */
private /* Integer -> List<SearchResult> */ SortedMap map=
new TreeMap(Comparators.integerComparator());
/** Used to compare all filenames in this. Ignores case, whitespace. */
final private ApproximateMatcher matcher;
public TableLineGrouper() {
this.matcher=new ApproximateMatcher(120);
this.matcher.setIgnoreCase(true);
this.matcher.setIgnoreWhitespace(true);
this.matcher.setCompareBackwards(true);
}
/** Returns true if empty, i.e., cleared. */
public boolean isEmpty() {
return map.isEmpty();
}
public void clear() {
map.clear();
}
/** Returns an j s.t. list[j].match(line),
* or -1 if no such i but the caller should keep searching
* (because no line in list is close in size)
* or -2 if no such i but the caller shouldn't keep searching
* (because no line in list is close in size)
* @requires elements of list are GrouperPair */
private int matchHelper(List /* of SearchResult */ list,
SearchResult line) {
Assert.that(list!=null, "Trying to match null list");
Assert.that(line!=null, "Trying to match null line");
for (int j=0; j<list.size(); j++) {
SearchResult line2=(SearchResult)list.get(j);
int match=line2.match(line, matcher);
if (match==0) //matches
return j;
else if (match==2) //non-similar sizes
return -2;
}
return -1;
}
/** Returns a line G in this s.t. G.similar(line), or null of no such line
* exists. */
public SearchResult match(SearchResult line) {
//OK. This line has no hash...use old algorithm...
Integer key=new Integer(line.getSize());
//1. Search forward for results with similar (but possibly larger) sizes
Iterator iter=map.tailMap(key).values().iterator();
while (iter.hasNext()) {
List lines=(List)iter.next();
int ret=matchHelper(lines, line);
if (ret>=0)
return (SearchResult)lines.get(ret);
else if (ret==-2)
break;
}
//2. Search backwards for results with similar (but possibly smaller)
// sizes. Note that we do this in a different way from (1) because
// sorted maps provide no way of iterating through values backwards.
SortedMap map=this.map.headMap(key);
while (true) {
Integer key2;
try {
key2=(Integer)map.lastKey();
} catch (NoSuchElementException e) {
break;
}
List lines=(List)map.get(key2);
int ret=matchHelper(lines, line);
if (ret>=0)
return (SearchResult)lines.get(ret);
else if (ret==-2)
break;
map=map.headMap(key2);
}
//3. No luck?
return null;
}
/**
* Adds line to this. Generally there should be no lines similar to line
* in this, i.e., this.match(line)==null, but this isn't strictly required.
* @requires line not in this
* @modifies this
*/
public void add(SearchResult line) {
Assert.that(line!=null,"Attempting to add null line");
Integer key=new Integer(line.getSize());
List lines=(List)map.get(key);
if (lines==null) {
lines=new LinkedList();
map.put(key, lines);
}
lines.add(line);
}
/*
public static void main(String[] args) {
System.out.println("Starting.");
//1. Basic tests
TableLineGrouper grouper=new TableLineGrouper();
TableLine line1=new TableLine("file.mp3", new Integer(100),
new Integer(0), new Integer(0), "host", new Integer(6346),
new byte[16], new Integer(0));
TableLine line2=new TableLine("file.mp3", new Integer(99),
new Integer(0), new Integer(0), "host", new Integer(6346),
new byte[16], new Integer(0));
TableLine line3=new TableLine("file.mp3", new Integer(101),
new Integer(0), new Integer(0), "host", new Integer(6346),
new byte[16], new Integer(0));
TableLine line4=new TableLine("different.mp3", new Integer(100),
new Integer(0), new Integer(0), "host", new Integer(6346),
new byte[16], new Integer(0));
Assert.that(grouper.match(line1)==null);
grouper.add(line1);
Assert.that(grouper.match(line1)==line1);
Assert.that(grouper.match(line2)==line1);
Assert.that(grouper.match(line3)==line1);
Assert.that(grouper.match(line4)==null);
TableLine line5=new TableLine("file.mp3", new Integer(1000000),
new Integer(0), new Integer(0), "host", new Integer(6346),
new byte[16], new Integer(0));
TableLine line6=new TableLine("file.mp3", new Integer(1001000),
new Integer(0), new Integer(0), "host", new Integer(6346),
new byte[16], new Integer(0));
TableLine line7=new TableLine("file.mp3", new Integer(999000),
new Integer(0), new Integer(0), "host", new Integer(6346),
new byte[16], new Integer(0));
TableLine line8=new TableLine("file.mp3", new Integer(10000000),
new Integer(0), new Integer(0), "host", new Integer(6346),
new byte[16], new Integer(0));
Assert.that(grouper.match(line5)==null);
grouper.add(line5);
grouper.add(line8);
Assert.that(grouper.match(line6)==line5);
Assert.that(grouper.match(line7)==line5);
System.out.println("Done.");
}
*/
/*
//Old unit test not adapted to new interface.
//2. Tests whether match searches forward and backward correctly
grouper=new TableLineGrouper();
TableLine line0=new TableLine("zero.mp3", new Integer(98),
new Integer(0), new Integer(0), "host", new Integer(6346),
new byte[16], new Integer(0));
line1=new TableLine("first.mp3", new Integer(99),
new Integer(0), new Integer(0), "host", new Integer(6346),
new byte[16], new Integer(0));
line2=new TableLine("second.mp3", new Integer(100),
new Integer(0), new Integer(0), "host", new Integer(6346),
new byte[16], new Integer(0));
line3=new TableLine("third.mp3", new Integer(101),
new Integer(0), new Integer(0), "host", new Integer(6346),
new byte[16], new Integer(0));
line4=new TableLine("fourth.mp3", new Integer(102),
new Integer(0), new Integer(0), "host", new Integer(6346),
new byte[16], new Integer(0));
Assert.that(grouper.add(line0)==0);
Assert.that(grouper.add(line1)==1);
Assert.that(grouper.add(line2)==2);
Assert.that(grouper.add(line3)==3);
Assert.that(grouper.add(line4)==4);
TableLine line0c=new TableLine("zero.mp3", new Integer(100),
new Integer(0), new Integer(0), "host", new Integer(6346),
new byte[16], new Integer(0));
TableLine line1c=new TableLine("first.mp3", new Integer(100),
new Integer(0), new Integer(0), "host", new Integer(6346),
new byte[16], new Integer(0));
TableLine line3c=new TableLine("third.mp3", new Integer(100),
new Integer(0), new Integer(0), "host", new Integer(6346),
new byte[16], new Integer(0));
TableLine line4c=new TableLine("fourth.mp3", new Integer(100),
new Integer(0), new Integer(0), "host", new Integer(6346),
new byte[16], new Integer(0));
Assert.that(grouper.match(line1c)==1);
Assert.that(grouper.match(line3c)==3);
Assert.that(grouper.match(line0c)==0);
Assert.that(grouper.match(line4c)==4);
}
*/
}