/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.impl.gui.main.contactlist;
import java.util.*;
import java.util.regex.*;
import net.java.sip.communicator.impl.gui.*;
import net.java.sip.communicator.impl.gui.main.contactlist.contactsource.*;
import net.java.sip.communicator.service.contactsource.*;
/**
* The SearchFilter is a ContactListFilter that filters the
* contact list content by a filter string.
*
* @author Yana Stamcheva
*/
public class SearchFilter
implements ContactListFilter
{
/**
* The default contact source search type.
*/
public static final int DEFAULT_SOURCE = 0;
/**
* The history contact source search type.
*/
public static final int HISTORY_SOURCE = 1;
/**
* The string, which we're searching.
*/
private String filterString;
/**
* The pattern to filter.
*/
private Pattern filterPattern;
/**
* The MetaContactListSource to search in.
*/
private final MetaContactListSource mclSource;
/**
* The list of external contact sources to search in.
*/
private Collection contactSources;
/**
* The type of the search source. One of the above defined DEFAUT_SOURCE or
* HISTORY_SOURCE.
*/
private int searchSourceType = DEFAULT_SOURCE;
/**
* Creates an instance of SearchFilter.
*/
public SearchFilter(MetaContactListSource contactListSource)
{
this.mclSource = contactListSource;
}
/**
* Applies this filter to the default contact source.
* @param filterQuery the query that tracks this filter.
*/
public void applyFilter(FilterQuery filterQuery)
{
// If the filter has a default contact source, we apply it first.
if (searchSourceType == DEFAULT_SOURCE)
{
MetaContactQuery defaultQuery
= mclSource.queryMetaContactSource(filterPattern);
defaultQuery.addContactQueryListener(GuiActivator.getContactList());
// First add the MetaContactListSource
filterQuery.addContactQuery(defaultQuery);
}
// If we have stopped filtering in the mean time we return here.
if (filterQuery.isCanceled())
return;
Iterator filterSources
= getContactSources().iterator();
// Then we apply the filter on all its contact sources.
while (filterSources.hasNext())
{
final ExternalContactSource filterSource = filterSources.next();
// If we have stopped filtering in the mean time we return here.
if (filterQuery.isCanceled())
return;
filterQuery.addContactQuery(
applyFilter(filterSource));
}
// Closes this filter to indicate that we finished adding queries to it.
filterQuery.close();
}
/**
* Applies this filter to the given contactSource.
*
* @param contactSource the ExternalContactSource to apply the
* filter to
* @return the ContactQuery that tracks this filter
*/
public ContactQuery applyFilter(ExternalContactSource contactSource)
{
ContactSourceService sourceService
= contactSource.getContactSourceService();
ContactQuery contactQuery;
if (sourceService instanceof ExtendedContactSourceService)
contactQuery
= ((ExtendedContactSourceService) sourceService)
.queryContactSource(filterPattern);
else
contactQuery = sourceService.queryContactSource(filterString);
// Add first available results.
this.addMatching(contactQuery.getQueryResults());
contactQuery.addContactQueryListener(GuiActivator.getContactList());
return contactQuery;
}
/**
* Indicates if the given uiGroup matches this filter.
* @param uiContact the UIGroup to check
* @return true if the given uiGroup matches the current
* filter, false - otherwise
*/
public boolean isMatching(UIContact uiContact)
{
Iterator searchStrings = uiContact.getSearchStrings();
if (searchStrings != null)
{
while (searchStrings.hasNext())
{
if (isMatching(searchStrings.next()))
return true;
}
}
return false;
}
/**
* For all groups we return false. If some of the child contacts of this
* group matches this filter the group would be automatically added when
* the contact is added in the list.
* @param uiGroup the UIGroup to check
* @return false
*/
public boolean isMatching(UIGroup uiGroup)
{
return false;
}
/**
* Creates the SearchFilter by specifying the string used for
* filtering.
* @param filter the String used for filtering
*/
public void setFilterString(String filter)
{
// First escape all special characters from the given filter string.
this.filterString = filter;
// Then create the pattern.
// By default, case-insensitive matching assumes that only characters
// in the US-ASCII charset are being matched, that's why we use
// the UNICODE_CASE flag to enable unicode case-insensitive matching.
// Sun Bug ID: 6486934 "RegEx case_insensitive match is broken"
this.filterPattern
= Pattern.compile(
Pattern.quote(filterString),
Pattern.MULTILINE
| Pattern.CASE_INSENSITIVE
| Pattern.UNICODE_CASE);
}
/**
* Checks if the given contact is matching the current filter.
* A SourceContact would be matching the filter if its display
* name is matching the search string.
* @param contact the ContactListContactDescriptor to check
* @return true to indicate that the given contact is
* matching the current filter, otherwise returns false
*/
private boolean isMatching(SourceContact contact)
{
return isMatching(contact.getDisplayName());
}
/**
* Indicates if the given string matches this filter.
* @param text the text to check
* @return true to indicate that the given text matches
* this filter, false - otherwise
*/
private boolean isMatching(String text)
{
return filterPattern.matcher(text).find();
}
/**
* Adds the list of sourceContacts to the contact list.
* @param sourceContacts the list of SourceContacts to add
*/
private void addMatching(List sourceContacts)
{
Iterator contactsIter = sourceContacts.iterator();
while (contactsIter.hasNext())
addSourceContact(contactsIter.next());
}
/**
* Adds the given sourceContact to the contact list.
* @param sourceContact the SourceContact to add
*/
private void addSourceContact(SourceContact sourceContact)
{
ContactSourceService contactSource
= sourceContact.getContactSource();
ExternalContactSource sourceUI
= TreeContactList.getContactSource(contactSource);
if (sourceUI != null
// ExtendedContactSourceService has already matched the
// SourceContact over the pattern
&& (contactSource instanceof ExtendedContactSourceService)
|| isMatching(sourceContact))
{
GuiActivator.getContactList().addContact(
sourceUI.createUIContact(sourceContact),
sourceUI.getUIGroup(),
false,
true);
}
else
ExternalContactSource.removeUIContact(sourceContact);
}
/**
* Sets the search source type: DEFAULT_SOURCE or HISTORY_SOURCE.
* @param searchSourceType the type of the search source to set
*/
public void setSearchSourceType(int searchSourceType)
{
this.searchSourceType = searchSourceType;
switch(searchSourceType)
{
case DEFAULT_SOURCE:
contactSources = TreeContactList.getContactSources();
break;
case HISTORY_SOURCE:
{
Collection historySources
= new LinkedList();
ExternalContactSource historySource
= TreeContactList.getContactSource(
ContactSourceService.CALL_HISTORY);
historySources.add(historySource);
contactSources = historySources;
break;
}
}
}
/**
* Returns the list of ExternalContactSource this filter searches
* in.
* @return the list of ExternalContactSource this filter searches
* in
*/
public Collection getContactSources()
{
if (contactSources == null)
contactSources = TreeContactList.getContactSources();
return contactSources;
}
/**
* Indicates if this filter contains a default source.
* @return true if this filter contains a default source,
* false otherwise
*/
public boolean hasDefaultSource()
{
return (searchSourceType == DEFAULT_SOURCE);
}
}