package org.limewire.ui.swing.filter; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.SwingUtilities; import javax.swing.Timer; import org.limewire.core.api.Category; import ca.odell.glazedlists.event.ListEvent; import ca.odell.glazedlists.event.ListEventListener; /** * Support class to detect the category with the most search results. */ class CategoryDetector<E extends FilterableItem> { /** Number of results needed to trigger category detection. */ private static final int RESULTS_COUNT = 25; /** Delay in milliseconds needed to trigger category detection. */ private static final int TIMER_DELAY = 10000; private final FilterableSource<E> filterableSource; private final CategoryFilter categoryFilter; private CategoryDetectorListener detectorListener; private ListEventListener<E> resultsListener; private ActionListener timerListener; private Timer timer; /** * Constructs a CategoryDetector using the specified search results model * and category filter. */ public CategoryDetector(FilterableSource<E> filterableSource, CategoryFilter categoryFilter) { this.filterableSource = filterableSource; this.categoryFilter = categoryFilter; } /** * Starts category detection. When the default category is found, the * specified listener is notified. */ public void start(CategoryDetectorListener listener) { detectorListener = listener; // Create results listener to detect the category when enough results // are received. resultsListener = new ListEventListener<E>() { @Override public void listChanged(ListEvent listChanges) { int size = listChanges.getSourceList().size(); if (size >= RESULTS_COUNT) { fireDelayedCategoryFound(); } } }; filterableSource.getUnfilteredList().addListEventListener(resultsListener); // Create timer listener to detect the category after enough time has // passed. timerListener = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { fireDelayedCategoryFound(); } }; timer = new Timer(TIMER_DELAY, timerListener); timer.start(); } /** * Stops category detection. This method removes all listeners. */ public void stop() { // Remove listener from list. if (resultsListener != null) { filterableSource.getUnfilteredList().removeListEventListener(resultsListener); resultsListener = null; } // Stop timer and remove listener. if (timer != null) { timer.stop(); timer.removeActionListener(timerListener); timerListener = null; timer = null; } } /** * Notifies the detector listener with the default category, and stops * category detection. Delayed notification is performed by posting a new * event on the event queue. We do this because the method may be invoked * in response to an EventList change, and we want to delay category * notification since further processing of the EventList chain in the same * event can cause issues. */ private void fireDelayedCategoryFound() { // Perform delayed notification by posting Runnable on event queue. SwingUtilities.invokeLater(new Runnable() { @Override public void run() { if (detectorListener != null) { Category category = categoryFilter.getDefaultCategory(); detectorListener.categoryFound(category); detectorListener = null; } } }); // Stop category detection. stop(); } /** * Defines a listener for category found events. */ public static interface CategoryDetectorListener { /** * Invoked when the category is found. */ void categoryFound(Category category); } }