package prefuse.data.search; import java.util.Iterator; import java.util.StringTokenizer; import prefuse.data.Tuple; /** * <p> * SearchTupleSet implementation supporting word prefix searches over indexed * Tuple data fields. This class uses a {@link Trie Trie} data structure * to find search results quickly; however, only prefix matches will be * identified as valid search matches. Multi-term search queries will result * in the union of the results for the individual query terms. That is, Tuples * that match any one of the terms will be included in the results. * </p> * * <p> * For more advanced search capabilities, see * {@link KeywordSearchTupleSet} or {@link RegexSearchTupleSet}. * </p> * * @version 1.0 * @author <a href="http://jheer.org">jeffrey heer</a> * @see prefuse.data.query.SearchQueryBinding */ public class PrefixSearchTupleSet extends SearchTupleSet { private Trie m_trie; private Trie.TrieNode m_curNode; private String m_delim = " \t\n\r"; private String m_query = ""; /** * Creates a new KeywordSearchFocusSet that is not case sensitive. */ public PrefixSearchTupleSet() { this(false); } /** * Creates a new KeywordSearchFocusSet with the indicated case sensitivity. * @param caseSensitive true if the search routines should be case * sensitive, false otherwise. */ public PrefixSearchTupleSet(boolean caseSensitive) { m_trie = new Trie(caseSensitive); } /** * Returns the delimiter string used to divide data values and * queries into separate words. By default, the value consists * of just whitespace characters. * @return the delimiter string used. This is passed as an argument to a * {@link java.util.StringTokenizer} instance that will tokenize the text. * @see java.util.StringTokenizer */ public String getDelimiterString() { return m_delim; } /** * Sets the delimiter string used to divide data values and * queries into separate words. By default, the delimiter consists * of just whitespace characters. * @param delim the delimiter string to use. This is passed as an argument * to a {@link java.util.StringTokenizer} instance that will tokenize the * text. * @see java.util.StringTokenizer */ public void setDelimiterString(String delim) { m_delim = delim; } /** * @see prefuse.data.search.SearchTupleSet#getQuery() */ public String getQuery() { return m_query; } /** * Searches the indexed Tuple fields for matching string prefixes, * adding the Tuple instances for each search match to this TupleSet. * The query string is first broken up into separate terms, as determined * by the current delimiter string. A search for each term is conducted, * and all matching Tuples are included in the results. * @param query the query string to search for. * @see #setDelimiterString(String) */ public void search(String query) { if ( query == null ) query = ""; if ( query.equals(m_query) ) return; Tuple[] rem = clearInternal(); m_query = query; StringTokenizer st = new StringTokenizer(m_query, m_delim); if ( !st.hasMoreTokens() ) m_query = ""; while ( st.hasMoreTokens() ) prefixSearch(st.nextToken()); Tuple[] add = getTupleCount() > 0 ? toArray() : null; fireTupleEvent(add, rem); } /** * Issues a prefix search and collects the results */ private void prefixSearch(String query) { m_curNode = m_trie.find(query); if ( m_curNode != null ) { Iterator iter = trieIterator(); while ( iter.hasNext() ) addInternal((Tuple)iter.next()); } } /** * Indexes the given field of the provided Tuple instance. * @see prefuse.data.search.SearchTupleSet#index(prefuse.data.Tuple, java.lang.String) */ public void index(Tuple t, String field) { String s; if ( (s=t.getString(field)) == null ) return; StringTokenizer st = new StringTokenizer(s,m_delim); while ( st.hasMoreTokens() ) { String tok = st.nextToken(); addString(tok, t); } } private void addString(String s, Tuple t) { m_trie.addString(s,t); } /** * Returns true, as unidexing is supported by this class. * @see prefuse.data.search.SearchTupleSet#isUnindexSupported() */ public boolean isUnindexSupported() { return true; } /** * @see prefuse.data.search.SearchTupleSet#unindex(prefuse.data.Tuple, java.lang.String) */ public void unindex(Tuple t, String field) { String s; if ( (s=t.getString(field)) == null ) return; StringTokenizer st = new StringTokenizer(s,m_delim); while ( st.hasMoreTokens() ) { String tok = st.nextToken(); removeString(tok, t); } } /** * Removes all search hits and clears out the index. * @see prefuse.data.tuple.TupleSet#clear() */ public void clear() { m_trie = new Trie(m_trie.isCaseSensitive()); super.clear(); } private void removeString(String s, Tuple t) { m_trie.removeString(s,t); } private Iterator trieIterator() { return m_trie.new TrieIterator(m_curNode); } } // end of class PrefixSearchTupleSet