[seek-dev] kepler quicksearch

Serguei Krivov Serguei.Krivov at uvm.edu
Mon Sep 19 10:55:59 PDT 2005


Sorry if it is out of context. But in case you are talking about prefix
search on graphs, where prefix should match a set of fields associated with
nodes AND you are not using database, then for sure you may like to consider
the prefuse's prefix keyword search. The claim is that it is fast and
depends *only* on the length of query  string. This sound outrageous, but
check it for yourself.  I am pasting code below. I believe that prefuse's
classes for nodes and edges could be easily replaced. Prefuse  Nodes have
text attributes retrievable by getAttribute().  Several attributes could be
looked aupon simmilataneously. The class Tire is the main data structure
that actually does search. KeywordSearchPanel provide a compact interface to
search.  If you like to use the algorithm I may help to extract prefuse
independent code.

Serguei

package edu.berkeley.guir.prefuse.util;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.NoSuchElementException;

import edu.berkeley.guir.prefuse.graph.DefaultEdge;
import edu.berkeley.guir.prefuse.graph.DefaultTree;
import edu.berkeley.guir.prefuse.graph.DefaultTreeNode;
import edu.berkeley.guir.prefuse.graph.Edge;
import edu.berkeley.guir.prefuse.graph.Entity;
import edu.berkeley.guir.prefuse.graph.Tree;
import edu.berkeley.guir.prefuse.graph.TreeNode;

/**
 * Represents a trie data structure (a play on the words "tree" and 
 * "retrieval"). This builds a tree structure representing a set of
 * words by indexing on word prefixes. It is useful for performing
 * prefix-based searches over large amounts of text in an
 * efficient manner.
 *
 * @version 1.0
 * @author <a href="http://jheer.org">Jeffrey Heer</a> prefuse(AT)jheer.org
 * @see PrefixSearchFocusSet
 */
public class Trie {

    public class TrieNode {
        boolean isLeaf;
        int leafCount = 0;
    } //
    public class TrieBranch extends TrieNode {
        char[] chars = new char[] {0};
        TrieNode[] children = new TrieNode[1];
    } //
    public class TrieLeaf extends TrieNode {
        public TrieLeaf(String word, Entity e) {
            this.word = word;
            entity = e;
            next = null;
            leafCount = 1;
        }
        String word;
        Entity entity;
        TrieLeaf next;
    } //
    public class TrieIterator implements Iterator {
        private LinkedList queue;
        private Entity next;
        public TrieIterator(TrieNode node) {
            queue = new LinkedList();
            queue.add(node);
        } //
        public boolean hasNext() {
            return !queue.isEmpty();
        } //
        public Object next() {
            if ( queue.isEmpty() )
                throw new NoSuchElementException();
            
            TrieNode n = (TrieNode)queue.removeFirst();
            Object o;
            if ( n instanceof TrieLeaf ) {
                TrieLeaf l = (TrieLeaf)n;
                o = l.entity;
                if ( l.next != null )
                    queue.addFirst(l.next);
                return o;
            } else {
                TrieBranch b = (TrieBranch)n;
                for ( int i = b.chars.length-1; i > 0; i-- ) {
                    queue.addFirst(b.children[i]);
                }
                if ( b.children[0] != null )
                    queue.addFirst(b.children[0]);
                return next();
            }
        } //
        public void remove() {
            throw new UnsupportedOperationException();
        } //
    } // end of inner clas TrieIterator
    
    private TrieBranch root = new TrieBranch();
    private boolean caseSensitive = false;
    
    public Trie(boolean caseSensitive) {
        this.caseSensitive = caseSensitive;
    } //
    
    public void addString(String word, Entity e) {
        TrieLeaf leaf = new TrieLeaf(word,e);
        addLeaf(root, leaf, 0);
    } //
    
    public void removeString(String word, Entity e) {
        removeLeaf(root, word, e, 0);
    } //
    
    private final int getIndex(char[] chars, char c) {
        for ( int i=0; i<chars.length; i++ )
            if ( chars[i] == c ) return i;
        return -1;
    } //
    
    private final char getChar(String s, int i) {
        char c = ( i < 0 || i >= s.length() ? 0 : s.charAt(i) );
        return ( caseSensitive ? c : Character.toLowerCase(c) );
    } //
    
    private boolean removeLeaf(TrieBranch b, String word, Entity ent, int
depth) {
        char c = getChar(word, depth);
        int i = getIndex(b.chars, c);
        
        if ( i == -1 ) {
            // couldn't find leaf
            return false;
        } else {
            TrieNode n = b.children[i];
            if ( n instanceof TrieBranch ) {
                TrieBranch tb = (TrieBranch)n;
                boolean rem = removeLeaf(tb, word, ent, depth+1);
                if ( rem ) {
                    b.leafCount--;
                    if ( tb.leafCount == 1 )
                        b.children[i] =
tb.children[tb.children[0]!=null?0:1];
                }
                return rem;
            } else {
                TrieLeaf nl = (TrieLeaf)n;
                if ( nl.entity == ent ) {
                    b.children[i] = nl.next;
                    if ( nl.next == null )
                        repairBranch(b,i);
                    b.leafCount--;
                    return true;
                } else {
                    TrieLeaf nnl = nl.next;
                    while ( nnl != null && nnl.entity != ent ) {
                        nl = nnl; nnl = nnl.next;
                    }
                    if ( nnl == null )
                        return false; // couldn't find leaf
                    
                    // update leaf counts
                    for ( TrieLeaf tl = (TrieLeaf)n; tl.entity != ent; tl =
tl.next )
                        tl.leafCount--;
                    
                    nl.next = nnl.next;
                    b.leafCount--;
                    return true;
                }     
            }
        }
    } //
    
    private void repairBranch(TrieBranch b, int i) {
        if ( i == 0 ) {
            b.children[0] = null;
        } else {
            int len = b.chars.length;
            char[] nchars = new char[len-1];
            TrieNode[] nkids = new TrieNode[len-1];
            System.arraycopy(b.chars,0,nchars,0,i);
            System.arraycopy(b.children,0,nkids,0,i);
            System.arraycopy(b.chars,i+1,nchars,i,len-i-1);
            System.arraycopy(b.children,i+1,nkids,i,len-i-1);
            b.chars = nchars;
            b.children = nkids;
        }
    } //
    
    private void addLeaf(TrieBranch b, TrieLeaf l, int depth) {
        b.leafCount += l.leafCount;
        
        char c = getChar(l.word, depth);
        int i = getIndex(b.chars, c);
        
        if ( i == -1 ) {
            addChild(b,l,c);
        } else {
            TrieNode n = b.children[i];
            if ( n == null ) {
                // we have completely spelled out the word
                b.children[i] = l;
            } else if ( n instanceof TrieBranch ) {
                // recurse down the tree
                addLeaf((TrieBranch)n,l,depth+1);
            } else {
                // node is a leaf, need to do a split?
                TrieLeaf nl = (TrieLeaf)n;
                if ( i==0 || (caseSensitive ? nl.word.equals(l.word) 
                                  : nl.word.equalsIgnoreCase(l.word)) )
                {
                    // same word, so chain the entries
                    for ( ; nl.next != null; nl = nl.next )
                        nl.leafCount++;
                    nl.leafCount++;
                    nl.next = l;
                } else {
                    // different words, need to do a split
                    TrieBranch nb = new TrieBranch();
                    b.children[i] = nb;
                    addLeaf(nb,nl,depth+1);
                    addLeaf(nb,l,depth+1);
                }
            }
        }
    } //
    
    private void addChild(TrieBranch b, TrieNode n, char c) {
        int len = b.chars.length;
        char[] nchars = new char[len+1];
        TrieNode[] nkids = new TrieNode[len+1];
        System.arraycopy(b.chars,0,nchars,0,len);
        System.arraycopy(b.children,0,nkids,0,len);
        nchars[len] = c;
        nkids[len] = n;
        b.chars = nchars;
        b.children = nkids;
    } //
    
    public TrieNode find(String word) {
        return (word.length() < 1 ? null : find(word, root, 0));
    } //
    
    private TrieNode find(String word, TrieBranch b, int depth) {
        char c = getChar(word, depth);
        int i = getIndex(b.chars, c);
        if ( i == -1 ) {
            return null; // not in trie
        } else if ( b.children[i] instanceof TrieLeaf ) {
            return b.children[i];
        } else if ( word.length()-1 == depth ) {
            return b.children[i]; // end of search
        } else {
            return find(word, (TrieBranch)b.children[i], depth+1); //
recurse
        }
    } //
    
    public Tree tree() {
        TreeNode r = new DefaultTreeNode();
        r.setAttribute("label", "root");
        tree(root, r);
        return new DefaultTree(r);
    } //
    
    private void tree(TrieBranch b, TreeNode n) {
        for ( int i=0; i<b.chars.length; i++ ) {
            if ( b.children[i] instanceof TrieLeaf ) {
                TrieLeaf l = (TrieLeaf)b.children[i];
                while ( l != null ) {
                    TreeNode c = new DefaultTreeNode();
                    c.setAttribute("label", l.word);
                    Edge e = new DefaultEdge(n,c);
                    n.addChild(e);
                    l = l.next;
                }
            } else {
                DefaultTreeNode c = new DefaultTreeNode();
                c.setAttribute("label", String.valueOf(b.chars[i]));
                Edge e = new DefaultEdge(n,c);
                n.addChild(e);
                tree((TrieBranch)b.children[i], c);
            }
        }
    } //
    
} // end of class Trie


package edu.berkeley.guir.prefuse.focus;

import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.StringTokenizer;

import edu.berkeley.guir.prefuse.event.FocusEvent;
import edu.berkeley.guir.prefuse.event.FocusEventMulticaster;
import edu.berkeley.guir.prefuse.event.FocusListener;
import edu.berkeley.guir.prefuse.graph.Entity;
import edu.berkeley.guir.prefuse.graph.Tree;
import edu.berkeley.guir.prefuse.util.Trie;

/**
 * <p>
 * A {@link FocusSet FocusSet} implementation that performs efficient
keyword
 * searches on graph data. The {@link #index(Iterator, String) index} method
 * should be used to register searchable graph data. Then the
 * {@link #search(String) search} method can be used to perform a search.
The
 * matching search results then become the members of this 
 * <code>FocusSet</code>. This class uses a {@link Trie Trie} data structure
 * to find search results in time proportional to only the length of the
 * query string, however, only prefix matches will be identified as valid
 * search matches.
 * </p>
 * 
 * <p>
 * <b>NOTE:</b> The {@link #add(Entity) add}, (@link #remove(Entity)
remove},
 * and {@link #set(Entity) set} methods are not supported by this 
 * implementation, and will generate exceptions if called. Instead, the
focus
 * membership is determined by the search matches found using the
 * {@link #search(String) search} method.
 * </p>
 *
 * @version 1.0
 * @author <a href="http://jheer.org">Jeffrey Heer</a> prefuse(AT)jheer.org
 */
public class PrefixSearchFocusSet implements FocusSet {

    private FocusListener m_listener = null;
    private LinkedHashSet m_set = new LinkedHashSet();
    private Trie m_trie;
    private Trie.TrieNode m_curNode;
    private String m_delim = ", ";
    private String m_query = null;
    
    /**
     * Creates a new KeywordSearchFocusSet that is not case sensitive.
     */
    public PrefixSearchFocusSet() {
        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 PrefixSearchFocusSet(boolean caseSensitive) {
        m_trie = new Trie(caseSensitive);
    } //
    
    public void reset(){
    	m_trie = new Trie(false);
    }
    
    /**
     * Adds a listener to monitor changes to this FocusSet.
     * @param fl the FocusListener to add
     */
    public void addFocusListener(FocusListener fl) {
        m_listener = FocusEventMulticaster.add(m_listener, fl);
    } //

    /**
     * Removes a listener currently monitoring this FocusSet.
     * @param fl the FocusListener to remove
     */
    public void removeFocusListener(FocusListener fl) {
        m_listener = FocusEventMulticaster.remove(m_listener, fl);
    } //

    /**
     * Returns the delimiter string used to divide attribute values
     * into separately indexed words. By default, this is just a
     * whitespace.
     * @return the delimiter string used by the indexer
     * @see java.util.StringTokenizer
     */
    public String getDelimiterString() {
        return m_delim;
    } //
    
    /**
     * Sets the delimiter string used to divide attribute values
     * into separately indexed words.
     * @param delim the new delimiter string used by the indexer
     * @see java.util.StringTokenizer
     */
    public void setDelimiterString(String delim) {
        m_delim = delim;
    } //
    
    /**
     * Returns the current search query, if any
     * @return the current;y active search query
     */
    public String getQuery() {
        return m_query;
    } //
    
    /**
     * Searches the indexed attributes of this FocusSet for matching
     * string prefixes, adding the Entity instances for each search match
     * to the FocusSet.
     * @param query the query string to search for. Indexed attributes
     *  with a matching prefix will be added to the FocusSet.
     */
    public void search(String query) {
        FocusEvent fe;
        synchronized ( this ) {
	        Entity[] rem = (Entity[])m_set.toArray(FocusEvent.EMPTY);
	        m_set.clear();
	        m_query = query;
	        m_curNode = m_trie.find(query);
	        if ( m_curNode != null ) {
	            Iterator iter = trieIterator();
	            while ( iter.hasNext() )
	                m_set.add(iter.next());
	        }
	        Entity[] add = (Entity[])m_set.toArray(FocusEvent.EMPTY);
	        fe = new FocusEvent(this, FocusEvent.FOCUS_SET, add, rem);
        }
        m_listener.focusChanged(fe);
    } //
    
    /**
     * Provides a Tree of the underlying Trie backing this focus set.
     * This is here solely for debugging purposes.
     * @param entities the entities to index
     * @param attrName the Entity attribute to index
     * @return a Tree of the Trie
     */
    public static Tree getTree(Iterator entities, String attrName) {
        PrefixSearchFocusSet set = new PrefixSearchFocusSet(false);
        set.index(entities, attrName);
        return set.m_trie.tree();
    } //
    
    /**
     * Indexes the attribute values for the given attribute name for
     * each Entity in the provided Iterator. These values are used
     * to construct an internal data structure allowing fast searches
     * over these attributes. To index multiple attributes, simply call
     * this method multiple times with the desired attribute names.
     * @param entities an Iterator over Entity instances to index
     * @param attrName the name of the attribute to index
     */
    public synchronized void index(Iterator entities, String attrName) {
        while ( entities.hasNext() ) {
            Entity e = (Entity)entities.next();
            index(e, attrName);
        }
    } //
    
    public synchronized void index(Entity e, String attrName) {
        String s;
        if ( (s=e.getAttribute(attrName)) == null ) return;
        StringTokenizer st = new StringTokenizer(s,m_delim);
        while ( st.hasMoreTokens() ) {
            String tok = st.nextToken();
            addString(tok, e);
        }
    } //
    
    private void addString(String s, Entity e) {
        m_trie.addString(s,e);
    } //
    
    public synchronized void remove(Entity e, String attrName) {
        String s;
        if ( (s=e.getAttribute(attrName)) == null ) return;
        StringTokenizer st = new StringTokenizer(s,m_delim);
        while ( st.hasMoreTokens() ) {
            String tok = st.nextToken();
            removeString(tok, e);
        }
    } //
    
    private void removeString(String s, Entity e) {
        m_trie.removeString(s,e);
    } //
    
    /**
     * Clears this focus set, invalidating any previous search.
     * @see edu.berkeley.guir.prefuse.focus.FocusSet#clear()
     */
    public void clear() {
        FocusEvent fe;
        synchronized ( this ) {
	        m_curNode = null;
	        m_query = null;
	        Entity[] rem = (Entity[])m_set.toArray(FocusEvent.EMPTY);
	        m_set.clear();
	        fe = new FocusEvent(this, FocusEvent.FOCUS_REMOVED, null,
rem);
    	}
        m_listener.focusChanged(fe);
    } //

    /**
     * Returns an Iterator over the Entity instances matching
     * the most recent search query.
     * @return an Iterator over the Entity instances matching
     * the most recent search query.
     */
    public synchronized Iterator iterator() {
        if ( m_curNode == null ) {
            return Collections.EMPTY_LIST.iterator();
        } else {
            return m_set.iterator();
        }
    } //
    
    private Iterator trieIterator() {
        return m_trie.new TrieIterator(m_curNode);
    } //

    /**
     * Returns the number of matches for the most recent search query.
     * @return the number of matches for the most recent search query.
     */
    public synchronized int size() {
        return (m_curNode==null ? 0 : m_set.size());
    } //

    /**
     * Indicates if a given Entity is contained within this FocusSet (i.e.
     * the Entity is currently a matching search result).
     * @param entity the Entity to check for containment
     * @return true if this Entity is in the FocusSet, false otherwise
     */
    public synchronized boolean contains(Entity entity) {
        return m_set.contains(entity);
    } //
    
    //
========================================================================
    // == UNSUPPORTED OPERATIONS
==============================================
    
    /**
     * This method is not supported by this implementation. Don't call it!
     * Instead, use the {@link #search(String) search} or
     * {@link #clear() clear} methods.
     */
    public void add(Entity focus) {
        throw new UnsupportedOperationException();
    } //
    /**
     * This method is not supported by this implementation. Don't call it!
     * Instead, use the {@link #search(String) search} or
     * {@link #clear() clear} methods.
     */
    public void add(Collection foci) {
        throw new UnsupportedOperationException();
    } //
    /**
     * This method is not supported by this implementation. Don't call it!
     * Instead, use the {@link #search(String) search} or
     * {@link #clear() clear} methods.
     */
    public void remove(Entity focus) {
        throw new UnsupportedOperationException();
    } //
    /**
     * This method is not supported by this implementation. Don't call it!
     * Instead, use the {@link #search(String) search} or
     * {@link #clear() clear} methods.
     */
    public void remove(Collection foci) {
        throw new UnsupportedOperationException();
    } //
    /**
     * This method is not supported by this implementation. Don't call it!
     * Instead, use the {@link #search(String) search} or
     * {@link #clear() clear} methods.
     */
    public void set(Entity focus) {
        throw new UnsupportedOperationException();
    } //
    /**
     * This method is not supported by this implementation. Don't call it!
     * Instead, use the {@link #search(String) search} or
     * {@link #clear() clear} methods.
     */
    public void set(Collection foci) {
        throw new UnsupportedOperationException();
    } //
    
}  // end of class KeywordSearchFocusSet


package org.ecoinformatics.seek.growl;

import java.awt.Color;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Polygon;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Iterator;

import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;

import org.ecoinformatics.seek.ograph.ONode;

import edu.berkeley.guir.prefuse.FocusManager;
import edu.berkeley.guir.prefuse.ItemRegistry;
import edu.berkeley.guir.prefuse.focus.FocusSet;
import edu.berkeley.guir.prefuse.focus.PrefixSearchFocusSet;
import edu.berkeley.guir.prefuse.graph.Entity;
import edu.berkeley.guir.prefuse.graph.Graph;
import edu.berkeley.guir.prefuse.graph.Node;

/**
 * Provides keyword search over the currently visualized data.
 * 
 * @version 1.0
 * @author <a href="http://jheer.org">Jeffrey Heer, Serguei Krivov</a>  
 */
public class PrefixSearchPanel extends JPanel implements DocumentListener,
//KeyListener,
		ActionListener {
	private ItemRegistry registry;

	private PrefixSearchFocusSet searcher;

	private FocusSet focus;

	private JTextField queryF = new JTextField(15);

	private JLabel resultL = new JLabel();

	private JLabel matchL = new JLabel();

	private JLabel searchL = new JLabel("search >> ");

	private IconButton upArrow = new IconButton(new
ArrowIcon(ArrowIcon.UP),
			new ArrowIcon(ArrowIcon.UP_DEPRESSED));

	private IconButton downArrow = new IconButton(
			new ArrowIcon(ArrowIcon.DOWN), new ArrowIcon(
					ArrowIcon.DOWN_DEPRESSED));

	private String[] searchAttr;

	private Entity m_results[];

	private int m_curResult;

	public PrefixSearchPanel(String[] attr, ItemRegistry registry) {
		searchAttr = attr;
		FocusManager fmanager = registry.getFocusManager();
		focus = fmanager.getDefaultFocusSet();
		FocusSet search = registry.getFocusManager().getFocusSet(
				FocusManager.SEARCH_KEY);
		if (search != null) {
			if (search instanceof PrefixSearchFocusSet) {
				searcher = (PrefixSearchFocusSet) search;
			} else {
				throw new IllegalStateException(
						"Search focus set not
instance of PrefixSearchFocusSet!");
			}
		} else {
			searcher = new PrefixSearchFocusSet();
			fmanager.putFocusSet(FocusManager.SEARCH_KEY,
searcher);
		}
		init(registry);
	} //

	public PrefixSearchPanel(String[] attr, ItemRegistry registry,
			PrefixSearchFocusSet searchSet, FocusSet focusSet) {
		searchAttr = attr;
		searcher = searchSet;
		focus = focusSet;
		init(registry);
	} //
	
	public void clear(){
		searcher.clear();
	}

	private void init(ItemRegistry registry) {
		this.registry = registry;

		// // add a listener to dynamically build search index
		// registry.addItemRegistryListener(new
ItemRegistryListener() {
		// public void registryItemAdded(VisualItem item) {
		// if
(!item.getItemClass().equals(ItemRegistry.DEFAULT_NODE_CLASS))
		// return;
		// for ( int i=0; i < searchAttr.length; i++ )
		// searcher.index(item.getEntity(), searchAttr[i]);
		// searchUpdate();
		// } //
		// public void registryItemRemoved(VisualItem item) {
		// if
(!item.getItemClass().equals(ItemRegistry.DEFAULT_NODE_CLASS))
		// return;
		// for ( int i=0; i < searchAttr.length; i++ )
		// searcher.remove(item.getEntity(), searchAttr[i]);
		// searchUpdate();
		// } //
		// });
		initGraph(registry.getGraph());

		//queryF.addKeyListener(this);
		queryF.getDocument().addDocumentListener(this);
		queryF.setMaximumSize(new Dimension(100, 20));

		upArrow.addActionListener(this);
		upArrow.setEnabled(false);
		downArrow.addActionListener(this);
		downArrow.setEnabled(false);

		matchL.addMouseListener(new MouseAdapter() {
			public void mouseEntered(MouseEvent e) {
				if (matchL.getText().length() > 0) {
					matchL.setCursor(new
Cursor(Cursor.HAND_CURSOR));
				}
			} //

			public void mouseExited(MouseEvent e) {
				if (matchL.getText().length() > 0) {
					matchL.setCursor(new
Cursor(Cursor.DEFAULT_CURSOR));
				}
			} //

			public void mouseClicked(MouseEvent e) {
				if (matchL.getText().length() > 0) {
					focus.set(m_results[m_curResult]);
				}
			} //
		});

		setBackground(Color.WHITE);
		initUI();
	} //
	
	public void initGraph(Graph g) {
		//searcher.clear();
		searcher.reset();
		for (Iterator it = g.getNodes(); it.hasNext();) {
			Node n = (Node) it.next();

			for (int i = 0; i < searchAttr.length; i++)
				searcher.index(n, searchAttr[i]);
			
		}
		//searchUpdate();
	}

	private void initUI() {
		this.setLayout(new BoxLayout(this, BoxLayout.X_AXIS));

		Box b = new Box(BoxLayout.X_AXIS);
		b.add(resultL);
		b.add(Box.createHorizontalStrut(5));
		b.add(Box.createHorizontalGlue());
		b.add(matchL);
		b.add(Box.createHorizontalStrut(5));
		b.add(downArrow);
		b.add(upArrow);
		b.add(Box.createHorizontalStrut(5));
		b.add(searchL);
		b.add(queryF);

		this.add(b);
	} //

	private void searchUpdate() {
		String query = queryF.getText();
		if (query.length() == 0) {
			searcher.clear();
			resultL.setText("");
			matchL.setText("");
			downArrow.setEnabled(false);
			upArrow.setEnabled(false);
			m_results = null;
		} else {
			searcher.search(query);
			int r = searcher.size();
			resultL.setText(r + " match" + (r == 1 ? "" :
"es"));
			m_results = new Entity[r];
			Iterator iter = searcher.iterator();
			for (int i = 0; iter.hasNext(); i++) {
				m_results[i] = (Entity) iter.next();
			}
			if (r > 0) {
				String label = "name";
				matchL.setText("1/" + r + ": "
						+
m_results[0].getAttribute(label));
				downArrow.setEnabled(true);
				upArrow.setEnabled(true);
			} else {
				matchL.setText("");
				downArrow.setEnabled(false);
				upArrow.setEnabled(false);
			}
			m_curResult = 0;
		}
		validate();
	} //

	public void setBackground(Color bg) {
		super.setBackground(bg);
		if (queryF != null)
			queryF.setBackground(bg);
		if (resultL != null)
			resultL.setBackground(bg);
		if (matchL != null)
			matchL.setBackground(bg);
		if (searchL != null)
			searchL.setBackground(bg);
		if (upArrow != null)
			upArrow.setBackground(bg);
		if (downArrow != null)
			downArrow.setBackground(bg);
	} //

	public void setForeground(Color fg) {
		super.setForeground(fg);
		if (queryF != null) {
			queryF.setForeground(fg);
			queryF.setCaretColor(fg);
		}
		if (resultL != null)
			resultL.setForeground(fg);
		if (matchL != null)
			matchL.setForeground(fg);
		if (searchL != null)
			searchL.setForeground(fg);
		if (upArrow != null)
			upArrow.setForeground(fg);
		if (downArrow != null)
			downArrow.setForeground(fg);
	} //

	public void setSearchPrompt(String prompt) {
		searchL.setText(prompt);
		searchL.validate();
	} //

	/**
	 * @see
javax.swing.event.DocumentListener#changedUpdate(javax.swing.event.DocumentE
vent)
	 */
	public void changedUpdate(DocumentEvent e) {
		searchUpdate();
	} //

	/**
	 * @see
javax.swing.event.DocumentListener#insertUpdate(javax.swing.event.DocumentEv
ent)
	 */
	public void insertUpdate(DocumentEvent e) {
		searchUpdate();
	} //

	/**
	 * @see
javax.swing.event.DocumentListener#removeUpdate(javax.swing.event.DocumentEv
ent)
	 */
	public void removeUpdate(DocumentEvent e) {
		searchUpdate();
	} //
	
	

	/**
	 * @see
java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
	 */
	public void actionPerformed(ActionEvent e) {
		if (matchL.getText().length() == 0)
			return;
		if (e.getSource() == downArrow) {
			m_curResult = (m_curResult + 1) % m_results.length;
		} else if (e.getSource() == upArrow) {
			m_curResult = (m_curResult - 1) % m_results.length;
			if (m_curResult < 0)
				m_curResult += m_results.length;
		}
		String label = "label";
		matchL.setText((m_curResult + 1) + "/" + m_results.length +
": "
				+
((ONode)m_results[m_curResult]).getName()); //getAttribute(label));
		validate();
		repaint();
	}//

	public class IconButton extends JButton {
		public IconButton(Icon icon1, Icon icon2) {
			super(icon1);
			if (icon1.getIconWidth() != icon2.getIconWidth()
					|| icon2.getIconHeight() !=
icon2.getIconHeight()) {
				throw new IllegalArgumentException("Icons
must have "
						+ "matching dimensions");
			}
			setPressedIcon(icon2);
			setDisabledIcon(new ArrowIcon(ArrowIcon.DISABLED));
			setBorderPainted(false);
			setFocusPainted(false);
			setBackground(getBackground());
			Insets in = getMargin();
			in.left = 0;
			in.right = 0;
			setMargin(in);
		} //
	} // end of class IconButton

	public class ArrowIcon implements Icon {
		public static final int UP = 0, UP_DEPRESSED = 1;

		public static final int DOWN = 2, DOWN_DEPRESSED = 3;

		public static final int DISABLED = 4;

		private int type;

		public ArrowIcon(int type) {
			this.type = type;
		} //

		public int getIconHeight() {
			return 11;
		} //

		public int getIconWidth() {
			return 11;
		} //

		public void paintIcon(Component c, Graphics g, int x, int y)
{
			if (type >= DISABLED)
				return;
			Polygon p = new Polygon();
			int w = getIconWidth();
			int h = getIconHeight();
			if (type < DOWN) {
				p.addPoint(x, y + h - 1);
				p.addPoint(x + w - 1, y + h - 1);
				p.addPoint(x + (w - 1) / 2, y);
				p.addPoint(x, y + h);
			} else {
				p.addPoint(x, y);
				p.addPoint(x + w - 1, y);
				p.addPoint(x + (w - 1) / 2, y + h - 1);
				p.addPoint(x, y);
			}
			g.setColor((type % 2 != 0 ? Color.LIGHT_GRAY :
getForeground()));
			g.fillPolygon(p);
			g.setColor(Color.BLACK);
			g.drawPolygon(p);
		} //
	} // end of inner class ArrowIcon
	
//	public void keyPressed(KeyEvent e) {
//		if(e.getKeyCode()==KeyEvent.VK_ENTER 
//				|| e.getKeyCode()==KeyEvent.VK_BACK_SPACE
//				|| e.getKeyCode()==KeyEvent.VK_DELETE)
//		searchUpdate();
//	} //
//
//	public void keyReleased(KeyEvent e) {
//		// TODO Auto-generated method stub
//		
//	}
//
//	public void keyTyped(KeyEvent e) {
//		// TODO Auto-generated method stub
//		
//	}

} // end of class KeywordSearchPanel



More information about the Seek-dev mailing list