2
* @(#)MergingSearchEngine.java 1.8 06/10/30
4
* Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
5
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
7
* This code is free software; you can redistribute it and/or modify it
8
* under the terms of the GNU General Public License version 2 only, as
9
* published by the Free Software Foundation. Sun designates this
10
* particular file as subject to the "Classpath" exception as provided
11
* by Sun in the LICENSE file that accompanied this code.
13
* This code is distributed in the hope that it will be useful, but WITHOUT
14
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16
* version 2 for more details (a copy is included in the LICENSE file that
17
* accompanied this code).
19
* You should have received a copy of the GNU General Public License version
20
* 2 along with this work; if not, write to the Free Software Foundation,
21
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
23
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
24
* CA 95054 USA or visit www.sun.com if you need additional information or
28
package javax.help.search;
30
import java.util.Hashtable;
31
import java.util.Vector;
32
import java.util.Enumeration;
33
import java.util.Locale;
35
import java.lang.reflect.Constructor;
36
import java.lang.reflect.InvocationTargetException;
37
import javax.help.HelpSet;
38
import javax.help.HelpUtilities;
39
import javax.help.NavigatorView;
40
import javax.help.search.SearchListener;
41
import javax.help.search.SearchEvent;
42
import javax.help.search.SearchEngine;
43
import javax.help.search.SearchQuery;
46
* A class that provides a merging/removing layer for the search.
48
public class MergingSearchEngine extends SearchEngine {
50
private Vector engines;
51
private Hashtable enginePerView = new Hashtable();
52
private boolean stopQuery = false;
54
public MergingSearchEngine(NavigatorView view) {
56
throw new IllegalArgumentException("view must not be null");
58
engines = new Vector();
59
// HERE - the makeEngine() should be delayed until the actual query
60
SearchEngine engine = makeEngine(view);
61
engines.addElement(engine);
64
public MergingSearchEngine(SearchEngine engine) {
66
throw new IllegalArgumentException("engine must not be null");
68
engines = new Vector();
69
engines.addElement(engine);
73
* Creates the query for this helpset.
75
public SearchQuery createQuery() {
76
return new MergingSearchQuery(this);
80
* Adds/Removes a Search Engine to/from list.
82
* Possibly the makeEngine should be delayed until the actual query.
85
public void merge(NavigatorView view) {
87
throw new IllegalArgumentException("view must not be null");
89
SearchEngine engine = makeEngine(view);
91
throw new IllegalArgumentException("view is invalid");
93
engines.addElement(engine);
94
enginePerView.put(view, engine);
98
* Remove a Navigator View
99
* Throws an IllegalArgumentException if view is null or if there
100
* is no search engine for a view.
102
public void remove(NavigatorView view) {
104
throw new IllegalArgumentException("view is either null or invalid");
106
SearchEngine engine = (SearchEngine) enginePerView.get(view);
107
if (engine != null) {
108
engines.removeElement(engine);
109
enginePerView.remove(engine);
111
throw new IllegalArgumentException("view is either null or invalid");
116
public Enumeration getEngines() {
117
return engines.elements();
120
private SearchEngine makeEngine(NavigatorView view) {
121
Hashtable params = view.getParameters();
123
// if there were no parameters or there were parameters but
124
// no data then return a null SearchEngine
125
if (params == null ||
126
(params != null && !params.containsKey("data"))) {
129
String engineName = (String) params.get("engine");
130
HelpSet hs = view.getHelpSet();
131
URL base = hs.getHelpSetURL();
132
ClassLoader loader = hs.getLoader();
134
if (engineName == null) {
135
engineName = HelpUtilities.getDefaultQueryEngine();
136
params.put("engine", engineName);
139
SearchEngine back = null;
141
Constructor konstructor;
142
Class types[] = {URL.class, Hashtable.class};
143
Object args[] = {base, params};
147
debug(" base: "+base);
148
debug(" params: "+params);
151
if (loader == null) {
152
klass = Class.forName(engineName);
154
klass = loader.loadClass(engineName);
156
} catch (Throwable t) {
157
throw new Error("Could not load engine named "+engineName+" for view: "+view);
161
konstructor = klass.getConstructor(types);
162
} catch (Throwable t) {
163
throw new Error("Could not find constructor for "+engineName+". For view: "+view);
166
back = (SearchEngine) konstructor.newInstance(args);
167
} catch (InvocationTargetException e) {
168
System.err.println("Exception while creating engine named "+engineName+" for view: "+view);
170
} catch (Throwable t) {
171
throw new Error("Could not create engine named "+engineName+" for view: "+view);
176
private class MergingSearchQuery extends SearchQuery implements SearchListener {
178
private MergingSearchEngine mhs;
179
private Vector queries;
180
private String searchparams;
182
public MergingSearchQuery(SearchEngine hs) {
184
if (hs instanceof MergingSearchEngine) {
185
this.mhs = (MergingSearchEngine) hs;
189
// Start all the search engines
190
public synchronized void start(String searchparams, Locale l)
191
throws IllegalArgumentException, IllegalStateException
193
MergingSearchEngine.this.debug("startSearch()");
195
// if we're already alive you can't start again
197
throw new IllegalStateException();
202
// setup everthing to get started
203
super.start(searchparams, l);
204
queries = new Vector();
206
// Get a query for each engine
207
for (Enumeration e = mhs.getEngines();
208
e.hasMoreElements(); ) {
209
SearchEngine engine = (SearchEngine) e.nextElement();
210
if (engine != null) {
211
queries.addElement(engine.createQuery());
215
// Set the listener to this class and start the query
216
for (Enumeration e = queries.elements(); e.hasMoreElements(); ) {
217
SearchQuery query = (SearchQuery) e.nextElement();
218
query.addSearchListener(this);
219
query.start(searchparams, l);
223
// Stop all the search engines
224
// This is an override of the SearchQuery.stop
225
// Donnot call super.stop in this method as an
226
// extra fireSearchStopped will be genertated
227
public synchronized void stop() throws IllegalStateException {
228
// Can't stop what is already stopped silly
229
if (queries == null) {
235
// Loop through all the queries and and make sure they have
236
// all inActive. If any query is active wait a small period of
238
boolean queriesActive = true;
239
while (queriesActive) {
240
queriesActive = false;
242
// Throughout this process the queries will disappear so
243
// protect against a null pointer
244
if (queries == null) {
247
for (Enumeration e = queries.elements();
248
e.hasMoreElements(); ) {
249
SearchQuery query = (SearchQuery) e.nextElement();
250
if (query.isActive()) {
251
debug ("queries are active waiting to stop");
252
queriesActive = true;
258
} catch (InterruptedException ex) {
259
ex.printStackTrace();
267
public boolean isActive() {
269
// if there aren't any queries we aren't alive
270
if (queries == null) {
274
// Loop through all the queries and see if anyone is alive
275
for (Enumeration e = queries.elements();
276
e.hasMoreElements(); ) {
277
SearchQuery query = (SearchQuery) e.nextElement();
278
if (query.isActive()) {
283
// Didn't find anyone alive so we're not alive
287
public SearchEngine getSearchEngine() {
291
public synchronized void itemsFound(SearchEvent e) {
292
SearchQuery queryin = (SearchQuery) e.getSource();
298
// Loop through all the queries and match this one
299
if (queries != null) {
300
Enumeration enum1 = queries.elements();
301
while (enum1.hasMoreElements()) {
302
SearchQuery query = (SearchQuery) enum1.nextElement();
303
if (query == queryin) {
304
// Redirect any Events as if they were from me
311
public void searchStarted(SearchEvent e) {
312
// Ignore these events as this class already informed
313
// the listeners the search was started so we don't have
314
// to do anything else
317
public synchronized void searchFinished(SearchEvent e) {
318
SearchQuery queryin = (SearchQuery) e.getSource();
320
// Loop through all the queries and match this one
321
if (queries != null) {
322
Enumeration enum1 = queries.elements();
323
while (enum1.hasMoreElements()) {
324
SearchQuery query = (SearchQuery) enum1.nextElement();
325
if (query == queryin) {
326
queryin.removeSearchListener(this);
327
queries.removeElement(query);
330
// If all the queries are done then send a searchFinished
331
if (queries.isEmpty()) {
334
fireSearchFinished();
341
} // This needs to be public to deal with inner classes...
343
private static final boolean debug = false;
344
private static void debug(String msg) {
346
System.err.println("MergineSearchEngine: "+msg);