javax.help.search.MergingSearchEngine Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of javahelp Show documentation
Show all versions of javahelp Show documentation
The JavaHelp API provides a platform-independent help framework.
The newest version!
/*
* @(#)MergingSearchEngine.java 1.8 06/10/30
*
* Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package javax.help.search;
import java.util.Hashtable;
import java.util.Vector;
import java.util.Enumeration;
import java.util.Locale;
import java.net.URL;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import javax.help.HelpSet;
import javax.help.HelpUtilities;
import javax.help.NavigatorView;
import javax.help.search.SearchListener;
import javax.help.search.SearchEvent;
import javax.help.search.SearchEngine;
import javax.help.search.SearchQuery;
/*
* A class that provides a merging/removing layer for the search.
*/
public class MergingSearchEngine extends SearchEngine {
private Vector engines;
private Hashtable enginePerView = new Hashtable();
private boolean stopQuery = false;
public MergingSearchEngine(NavigatorView view) {
if (view == null) {
throw new IllegalArgumentException("view must not be null");
}
engines = new Vector();
// HERE - the makeEngine() should be delayed until the actual query
SearchEngine engine = makeEngine(view);
engines.addElement(engine);
}
public MergingSearchEngine(SearchEngine engine) {
if (engine == null) {
throw new IllegalArgumentException("engine must not be null");
}
engines = new Vector();
engines.addElement(engine);
}
/**
* Creates the query for this helpset.
*/
public SearchQuery createQuery() {
return new MergingSearchQuery(this);
}
/**
* Adds/Removes a Search Engine to/from list.
*
* Possibly the makeEngine should be delayed until the actual query.
*/
public void merge(NavigatorView view) {
if (view == null) {
throw new IllegalArgumentException("view must not be null");
}
SearchEngine engine = makeEngine(view);
if (engine == null) {
throw new IllegalArgumentException("view is invalid");
}
engines.addElement(engine);
enginePerView.put(view, engine);
}
/*
* Remove a Navigator View
* Throws an IllegalArgumentException if view is null or if there
* is no search engine for a view.
*/
public void remove(NavigatorView view) {
if (view == null) {
throw new IllegalArgumentException("view is either null or invalid");
}
SearchEngine engine = (SearchEngine) enginePerView.get(view);
if (engine != null) {
engines.removeElement(engine);
enginePerView.remove(engine);
} else {
throw new IllegalArgumentException("view is either null or invalid");
}
}
public Enumeration getEngines() {
return engines.elements();
}
private SearchEngine makeEngine(NavigatorView view) {
Hashtable params = view.getParameters();
// if there were no parameters or there were parameters but
// no data then return a null SearchEngine
if (params == null ||
(params != null && !params.containsKey("data"))) {
return null;
}
String engineName = (String) params.get("engine");
HelpSet hs = view.getHelpSet();
URL base = hs.getHelpSetURL();
ClassLoader loader = hs.getLoader();
if (engineName == null) {
engineName = HelpUtilities.getDefaultQueryEngine();
params.put("engine", engineName);
}
SearchEngine back = null;
Constructor konstructor;
Class types[] = {URL.class, Hashtable.class};
Object args[] = {base, params};
Class klass;
debug("makeEngine");
debug(" base: "+base);
debug(" params: "+params);
try {
if (loader == null) {
klass = Class.forName(engineName);
} else {
klass = loader.loadClass(engineName);
}
} catch (Throwable t) {
throw new Error("Could not load engine named "+engineName+" for view: "+view);
}
try {
konstructor = klass.getConstructor(types);
} catch (Throwable t) {
throw new Error("Could not find constructor for "+engineName+". For view: "+view);
}
try {
back = (SearchEngine) konstructor.newInstance(args);
} catch (InvocationTargetException e) {
System.err.println("Exception while creating engine named "+engineName+" for view: "+view);
e.printStackTrace();
} catch (Throwable t) {
throw new Error("Could not create engine named "+engineName+" for view: "+view);
}
return back;
}
private class MergingSearchQuery extends SearchQuery implements SearchListener {
private MergingSearchEngine mhs;
private Vector queries;
private String searchparams;
public MergingSearchQuery(SearchEngine hs) {
super(hs);
if (hs instanceof MergingSearchEngine) {
this.mhs = (MergingSearchEngine) hs;
}
}
// Start all the search engines
public synchronized void start(String searchparams, Locale l)
throws IllegalArgumentException, IllegalStateException
{
MergingSearchEngine.this.debug("startSearch()");
// if we're already alive you can't start again
if (isActive()) {
throw new IllegalStateException();
}
stopQuery = false;
// setup everthing to get started
super.start(searchparams, l);
queries = new Vector();
// Get a query for each engine
for (Enumeration e = mhs.getEngines();
e.hasMoreElements(); ) {
SearchEngine engine = (SearchEngine) e.nextElement();
if (engine != null) {
queries.addElement(engine.createQuery());
}
}
// Set the listener to this class and start the query
for (Enumeration e = queries.elements(); e.hasMoreElements(); ) {
SearchQuery query = (SearchQuery) e.nextElement();
query.addSearchListener(this);
query.start(searchparams, l);
}
}
// Stop all the search engines
// This is an override of the SearchQuery.stop
// Donnot call super.stop in this method as an
// extra fireSearchStopped will be genertated
public synchronized void stop() throws IllegalStateException {
// Can't stop what is already stopped silly
if (queries == null) {
return;
}
stopQuery = true;
// Loop through all the queries and and make sure they have
// all inActive. If any query is active wait a small period of
// time
boolean queriesActive = true;
while (queriesActive) {
queriesActive = false;
// Throughout this process the queries will disappear so
// protect against a null pointer
if (queries == null) {
continue;
}
for (Enumeration e = queries.elements();
e.hasMoreElements(); ) {
SearchQuery query = (SearchQuery) e.nextElement();
if (query.isActive()) {
debug ("queries are active waiting to stop");
queriesActive = true;
}
}
if (queriesActive) {
try {
wait(250);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
queries = null;
}
public boolean isActive() {
// if there aren't any queries we aren't alive
if (queries == null) {
return false;
}
// Loop through all the queries and see if anyone is alive
for (Enumeration e = queries.elements();
e.hasMoreElements(); ) {
SearchQuery query = (SearchQuery) e.nextElement();
if (query.isActive()) {
return true;
}
}
// Didn't find anyone alive so we're not alive
return false;
}
public SearchEngine getSearchEngine() {
return mhs;
}
public synchronized void itemsFound(SearchEvent e) {
SearchQuery queryin = (SearchQuery) e.getSource();
if (stopQuery) {
return;
}
// Loop through all the queries and match this one
if (queries != null) {
Enumeration enum1 = queries.elements();
while (enum1.hasMoreElements()) {
SearchQuery query = (SearchQuery) enum1.nextElement();
if (query == queryin) {
// Redirect any Events as if they were from me
fireItemsFound(e);
}
}
}
}
public void searchStarted(SearchEvent e) {
// Ignore these events as this class already informed
// the listeners the search was started so we don't have
// to do anything else
}
public synchronized void searchFinished(SearchEvent e) {
SearchQuery queryin = (SearchQuery) e.getSource();
// Loop through all the queries and match this one
if (queries != null) {
Enumeration enum1 = queries.elements();
while (enum1.hasMoreElements()) {
SearchQuery query = (SearchQuery) enum1.nextElement();
if (query == queryin) {
queryin.removeSearchListener(this);
queries.removeElement(query);
}
}
// If all the queries are done then send a searchFinished
if (queries.isEmpty()) {
queries = null;
if (!stopQuery) {
fireSearchFinished();
}
}
}
}
} // This needs to be public to deal with inner classes...
private static final boolean debug = false;
private static void debug(String msg) {
if (debug) {
System.err.println("MergineSearchEngine: "+msg);
}
}
}