/* * Copyright 2010-2015 Institut Pasteur. * * This file is part of Icy. * * Icy is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Icy is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Icy. If not, see <http://www.gnu.org/licenses/>. */ package icy.search; import icy.system.IcyExceptionHandler; import icy.system.thread.SingleProcessor; import icy.system.thread.ThreadUtil; import icy.util.StringUtil; import java.util.ArrayList; import java.util.List; /** * The SearchResultProducer create {@link SearchResult} objects from given search keywords.<br> * These {@link SearchResult} are then consumed by a {@link SearchResultConsumer}. * * @author Thomas Provoost & Stephane Dallongeville */ public abstract class SearchResultProducer implements Comparable<SearchResultProducer> { private class SearchRunner implements Runnable { private final String[] words; private final SearchResultConsumer consumer; public SearchRunner(String[] words, SearchResultConsumer consumer) { super(); this.words = words; this.consumer = consumer; } @Override public void run() { // perform search if we have at least one not empty keyword if ((words.length > 1) || !StringUtil.isEmpty(words[0])) { try { doSearch(words, consumer); } catch (Throwable t) { // just display the exception and continue IcyExceptionHandler.showErrorMessage(t, true, true); } } else { final boolean notEmpty; synchronized (results) { // clear the list if necessary notEmpty = !results.isEmpty(); if (notEmpty) results.clear(); } // avoid death lock by sending event after synchronization if (notEmpty) consumer.resultsChanged(SearchResultProducer.this); } // search completed (do it after searching set to false) consumer.searchCompleted(SearchResultProducer.this); } } /** Result list */ protected List<SearchResult> results; /** Internals */ protected final SingleProcessor processor; public SearchResultProducer() { super(); results = new ArrayList<SearchResult>(); processor = new SingleProcessor(true, this.getClass().getSimpleName()); } /** Returns the result producer order */ public int getOrder() { // default return 10; } /** Returns the result producer name */ public abstract String getName(); /** * Returns the tooltip displayed on the menu (in small under the label). */ public String getTooltipText() { return "Click to run"; } /** Returns the result list */ public List<SearchResult> getResults() { return results; } /** * Performs the search request (asynchronous), mostly build the search result list.<br> * Only one search request should be processed at one time so take care of waiting for previous * search request completion.<br> * * @param words * Search keywords * @param consumer * Search result consumer for this search request.<br> * The consumer should be notified of new results by using the * {@link SearchResultConsumer#resultsChanged(SearchResultProducer)} method. */ public void search(String[] words, SearchResultConsumer consumer) { processor.submit(new SearchRunner(words, consumer)); } /** * Performs the search request (internal).<br> * The method is responsible for filling the <code>results</code> list :<br> * - If no result correspond to the requested search then <code>results</code> should be * cleared.<br> * - Else it should contains the founds results.<br> * <code>results</code> variable access should be synchronized as it can be externally accessed.<br> * The method could return earlier if {@link #hasWaitingSearch()} returns true. * * @param words * Search keywords * @param consumer * Search result consumer for this search request.<br> * The consumer should be notified of new results by using the * {@link SearchResultConsumer#resultsChanged(SearchResultProducer)} method. * @see #hasWaitingSearch() */ public abstract void doSearch(String[] words, SearchResultConsumer consumer); /** * Wait for the search request to complete. */ public void waitSearchComplete() { while (isSearching()) ThreadUtil.sleep(1); } /** * Returns true if the search result producer is currently processing a search request. */ public boolean isSearching() { return processor.isProcessing(); } /** * Returns true if there is a waiting search pending.<br> * This method should be called during search process to cancel it if another search is waiting. */ public boolean hasWaitingSearch() { return processor.hasWaitingTasks(); } /** * Add the SearchResult to the result list. * * @param result * Result to add to the result list. * @param consumer * If not null then consumer is notified about result change */ public void addResult(SearchResult result, SearchResultConsumer consumer) { if (result != null) { synchronized (results) { results.add(result); } // notify change to consumer if (consumer != null) consumer.resultsChanged(this); } } @Override public int compareTo(SearchResultProducer o) { // sort on order return getOrder() - o.getOrder(); } }