package org.limewire.core.impl.search;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
import org.limewire.core.api.URN;
import org.limewire.core.api.endpoint.RemoteHost;
import org.limewire.core.api.search.GroupedSearchResult;
import org.limewire.core.api.search.SearchResult;
import org.limewire.friend.api.Friend;
import org.limewire.util.Objects;
/**
* An implementation of GroupedSearchResult for grouping search results. The
* instance values in GroupedSearchResultImpl are updated in a background
* thread as search results are received.
*/
class GroupedSearchResultImpl implements GroupedSearchResult {
private static final Comparator<Friend> FRIEND_COMPARATOR = new FriendComparator();
private static final Comparator<RemoteHost> REMOTE_HOST_COMPARATOR = new RemoteHostComparator();
private final Set<RemoteHost> remoteHosts;
private volatile List<SearchResult> coreResults;
private volatile Set<Friend> friends;
private volatile boolean anonymous;
private volatile float relevance = 0;
/**
* Constructs a GroupedSearchResult containing the specified search result.
*/
public GroupedSearchResultImpl(SearchResult searchResult, String query) {
this.remoteHosts = new ConcurrentSkipListSet<RemoteHost>(REMOTE_HOST_COMPARATOR);
addNewSource(searchResult, query);
}
/**
* Adds the specified search result to the grouping. The specified query
* text is used to adjust the relevance score. This method is only called
* by CoreSearchResultList after a write lock is obtained on the parent
* list.
*/
void addNewSource(SearchResult result, String query) {
// Optimize for only having a single result.
if (coreResults == null) {
coreResults = Collections.singletonList(result);
} else {
if (coreResults.size() == 1) {
coreResults = new CopyOnWriteArrayList<SearchResult>(coreResults);
}
coreResults.add(result);
}
// Accumulate relevance score.
relevance += result.getRelevance(query);
// Build collection of non-anonymous friends for filtering.
RemoteHost host = result.getSource();
remoteHosts.add(host);
Friend friend = host.getFriendPresence().getFriend();
if (friend.isAnonymous()) {
anonymous = true;
} else {
if (friends == null) {
// optimize for a single friend having it
friends = Collections.singleton(friend);
} else {
// convert to CopyOnWriteArraySet if we need to.
if (!(friends instanceof CopyOnWriteArraySet)) {
Set<Friend> newFriends = new CopyOnWriteArraySet<Friend>();
newFriends.addAll(friends);
friends = newFriends;
}
friends.add(friend);
}
}
}
@Override
public boolean isAnonymous() {
return anonymous;
}
@Override
public String getFileName() {
return coreResults.get(0).getFileName();
}
@Override
public Collection<Friend> getFriends() {
if (friends == null) {
return Collections.<Friend>emptySet();
} else {
// Create sorted set of friends.
Set<Friend> newFriends = new TreeSet<Friend>(FRIEND_COMPARATOR);
newFriends.addAll(friends);
return newFriends;
}
}
@Override
public float getRelevance() {
return relevance;
}
@Override
public List<SearchResult> getSearchResults() {
return coreResults;
}
@Override
public Collection<RemoteHost> getSources() {
return remoteHosts;
}
@Override
public URN getUrn() {
return coreResults.get(0).getUrn();
}
/**
* Comparator to order Friend objects.
*/
private static class FriendComparator implements Comparator<Friend> {
@Override
public int compare(Friend o1, Friend o2) {
String id1 = o1.getId();
String id2 = o2.getId();
return Objects.compareToNullIgnoreCase(id1, id2, false);
}
}
/**
* Comparator to order RemoteHost objects.
*/
private static class RemoteHostComparator implements Comparator<RemoteHost> {
@Override
public int compare(RemoteHost o1, RemoteHost o2) {
int compare = 0;
boolean anonymous1 = o1.getFriendPresence().getFriend().isAnonymous();
boolean anonymous2 = o2.getFriendPresence().getFriend().isAnonymous();
if (anonymous1 == anonymous2) {
String name1 = o1.getFriendPresence().getFriend().getRenderName();
String name2 = o2.getFriendPresence().getFriend().getRenderName();
compare = name1.compareToIgnoreCase(name2);
} else if (anonymous1) {
compare = 1;
} else if (anonymous2) {
compare = -1;
}
return compare;
}
}
}