package com.limegroup.gnutella.gui.search; import java.util.Collections; import java.util.Set; import com.limegroup.gnutella.RemoteFileDesc; import com.limegroup.gnutella.search.HostData; import com.limegroup.gnutella.util.ApproximateMatcher; import com.limegroup.gnutella.util.I18NConvert; import com.limegroup.gnutella.settings.UISettings; /** * A single SearchResult. * * (A collection of RemoteFileDesc, HostData, and Set of alternate locations.) */ final class SearchResult { private final RemoteFileDesc RFD; private final HostData DATA; private Set _alts; /** The processed version of the filename used for approximate matching. * Not allocated until a match must be done. The assumption here is that * all matches will use the same ApproximateMatcher. TODO3: when we move * to Java 1.3, this should be a weak reference so the memory is reclaimed * after GC. */ private String processedFilename=null; /** * Constructs a new SearchResult with the given data. */ SearchResult(RemoteFileDesc rfd, HostData data, Set alts) { RFD = rfd; DATA = data; if(UISettings.UI_ADD_REPLY_ALT_LOCS.getValue()) _alts = alts; else _alts = Collections.EMPTY_SET; } /** Gets the RemoteFileDesc */ RemoteFileDesc getRemoteFileDesc() { return RFD; } /** Gets the HostData */ HostData getHostData() { return DATA; } /** Gets the Alternate Locations */ Set getAlts() { return _alts; } /** * Clears the alternate locations for this SearchResult. */ void clearAlts() { _alts = null; } /** * Sets the alternate locations for this SearchResult. */ void setAlts(Set alts) { _alts = alts; } /** * Gets the size of this SearchResult. */ int getSize() { return RFD.getSize(); } /** * Gets the filename without the extension. */ String getFilenameNoExtension() { String fullname = RFD.getFileName(); int i = fullname.lastIndexOf("."); if(i<0) return fullname; return I18NConvert.instance().compose(fullname.substring(0,i)); } /** * Returns the extension of this result. */ String getExtension() { String fullname = RFD.getFileName(); int i = fullname.lastIndexOf("."); if(i<0) return ""; return fullname.substring(i+1); } /** * Gets the processed filename. */ private String getProcessedFilename(ApproximateMatcher matcher) { if(processedFilename!=null) return processedFilename; processedFilename = matcher.process(getFilenameNoExtension()); return processedFilename; } /** * Compares this against o approximately: * <ul> * <li> Returns 0 if o is similar to this. * <li> Returns 1 if they have non-similar extensions. * <li> Returns 2 if they have non-similar sizes. * <li> Returns 3 if they have non-similar names. * <ul> * * Design note: this takes an ApproximateMatcher as an argument so that many * comparisons may be done with the same matcher, greatly reducing the * number of allocations.<b> * * <b>This method is not thread-safe.</b> */ int match(SearchResult o, final ApproximateMatcher matcher) { //Same file type? if (! getExtension().equals(o.getExtension())) return 1; long thisSize = getSize(); long thatSize = o.getSize(); // Sizes same? if(thisSize != thatSize) return 2; //Preprocess the processed fileNames getProcessedFilename(matcher); o.getProcessedFilename(matcher); //Filenames close? This is the most expensive test, so it should go //last. Allow 5% edit difference in filenames or 4 characters, //whichever is smaller. int allowedDifferences=Math.round(Math.min( 0.10f*((float)getFilenameNoExtension().length()), 0.10f*((float)o.getFilenameNoExtension().length()))); allowedDifferences=Math.min(allowedDifferences, 4); if (! matcher.matches(getProcessedFilename(matcher), o.getProcessedFilename(matcher), allowedDifferences)) return 3; return 0; } }