source: trunk/gsdl/src/java/org/nzdl/gsdl/Phind/Phind.java@ 8928

Last change on this file since 8928 was 8928, checked in by sjboddie, 19 years ago

Altered phind so it works correctly over an https connection
(DataInputStream.available() seems always to return 0 for https connections
under Sun java, though it works ok with Microsoft's VM).

  • Property svn:keywords set to Author Date Id Revision
File size: 33.3 KB
Line 
1/**********************************************************************
2 *
3 * Phind.java -- the Phind java applet
4 *
5 * Copyright 1997-2000 Gordon W. Paynter
6 * Copyright 2000 The New Zealand Digital Library Project
7 *
8 * A component of the Greenstone digital library software
9 * from the New Zealand Digital Library Project at the
10 * University of Waikato, New Zealand.
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 *
26 *********************************************************************/
27
28
29/*********************************************************************
30
31To use the applet, you'll need to embed it in a web page like this:
32
33<APPLET CODE="Phind.class" WIDTH=500 HEIGHT=500>
34
35 <PARAM NAME=collection VALUE="fao.org">
36 <PARAM NAME=classifier VALUE="1">
37 <PARAM NAME=phindcgi VALUE="http://kowhai/cgi-bin/phindcgi">
38 <PARAM NAME=library VALUE="http://kowhai/cgi-bin/library">
39 <PARAM NAME=backdrop VALUE="http://kowhai/~paynter/transfer/phindtest/green1.jpg">
40 The Phind java applet.
41</APPLET>
42
43There are a bunch of other parameters; these are described in the
44getParameters method below. It is all done for you in Greenstone
45in the document.dm macro file (the _phindapplet_ macro).
46
47You may have problems with Java applet security. Java applet's can only
48open socket connections (including the HTTP connections the applet uses
49to get data) to the same server the applet was loaded from. This means
50that your phindcgi, library, and (optional) backdrop URLs must be on the
51same machine that your web page was loaded from.
52
53**********************************************************************
54
55The applet comprises several classes:
56
571. Phind (this file) is the applet class, loaded by the browser.
58 It also handles network connections.
592. ResultDisplay is a Panel that sits in the applet and displays
60 things; displays are connected in a doubly-linked list.
613. ResultBox holds the results of a query. Result boxes are shown
62 to the user through ResultDisplays. Another doubly-linked list.
634. ResultTitle acts as a caption to a ResultBox describing its contents.
645. ResultCanvas is what the ResultBox data is drawn on. It draws the
65 words on the screen, and handles user input from the mouse.
666. ResultItem represents a single result object (a phrase or document).
677. PhindTitle is for drawing backdrops in ResultDisplays.
68
69**********************************************************************/
70
71package org.nzdl.gsdl.Phind;
72
73import java.awt.BorderLayout;
74import java.awt.Button;
75import java.awt.Choice;
76import java.awt.Color;
77import java.awt.Component;
78import java.awt.Dimension;
79import java.awt.Event;
80import java.awt.FlowLayout;
81import java.awt.Font;
82import java.awt.GridLayout;
83import java.awt.Image;
84import java.awt.Label;
85import java.awt.Panel;
86import java.awt.TextField;
87
88import java.net.URL;
89import java.net.URLConnection;
90import java.io.DataInputStream;
91import java.io.InputStream;
92
93import java.net.Socket;
94//import java.net.SocketTimeoutException;
95import java.net.InetAddress;
96import java.net.UnknownHostException;
97import java.io.IOException;
98import java.io.InterruptedIOException;
99
100import java.util.Vector;
101import java.util.Date;
102
103import org.nzdl.gsdl.Phind.URLUTF8Encoder;
104
105public class Phind extends java.applet.Applet {
106
107 // What is the collection called?
108 public String collection;
109
110 // Which phind classifier are we using? (There may be more than one.)
111 public String classifier;
112
113 // Internet address of phind resources
114 public String library_address, phindcgi_address;
115
116 // Initial serach term
117 public String initialSearch;
118
119 // Number of phrases to retrieve at any one time
120 public int phraseBlockSize;
121
122 // Appearance parameters
123 public boolean vertical;
124 public int depth;
125
126 // Font
127 public int fontSize;
128 public String fontName;
129 public Font plainFont, boldFont;
130
131 // Do we want a background image in the applet?
132 public boolean showImage;
133 public String backdrop_address;
134 public Image backgroundImage;
135 public boolean showBorder;
136
137 // Colours
138 public Color panel_fg, panel_bg,
139 column_1_fg, column_1_bg,
140 column_2_fg, column_2_bg,
141 highlight_fg, highlight_bg,
142 thesaurus_fg, thesaurus_bg, thesaurus_bar_fg, thesaurus_bar_bg,
143 expansion_fg, expansion_bg, expansion_bar_fg, expansion_bar_bg,
144 document_fg, document_bg, document_bar_fg, document_bar_bg,
145 message_fg, message_bg;
146
147 // Column dimensions
148 int column_1_width, column_2_width;
149
150 // Where do we open new windows
151 String searchWindowName, documentWindowName;
152
153 // the mode of operation
154 int mode;
155 final int initMode = 0;
156 final int idleMode = 1;
157 final int searchMode = 2;
158
159 // Elements of the control panel
160 boolean showControlPanel;
161 Label titleLabel;
162 TextField wordField;
163 Button searchButton, prevButton, nextButton;
164
165 // Holders for the downloaded data
166 Panel resultPanel;
167 ResultDisplay firstDisplay, lastDisplay;
168
169 // The time at which the last query finished
170 Date lastQueryEndTime;
171
172 // lastQueryEndTime is stored to ensure a 1 second gap between a query
173 // returning and a new one beginning. It is needed because the FAO
174 // folks in Rome have a huge lag, and frquently click several times
175 // while they wait; these clicks are turned into new queries, which
176 // they await again. It is no elegant solution, but it seems like the
177 // easiest, given that I don't know threads.
178 // 1. The search button is easy to disable, and is disabled when a
179 // socket connection is in progress.
180 // 2. ResutCanvas widgets can'r be similarly disabled because the
181 // browser hides or wipes them, which looks very bad.
182 // 3. I cannot just ignore clicks on canvasses because the browser
183 // caches the clicks while the socket connection is going on, and then
184 // sends them through afterwards, when the canvas is accepting clicks
185 // again.
186 // 4. Current sequence of events is to record the time the last query
187 // ends, then whenever a click happens make sure a second has past. if
188 // you double-click the the first query is sent, returns, end-tie is
189 // set, and the second (and any others made during query time) is
190 // *immediately* processed, but since 1 second hasn't past it is
191 // ignored.
192
193 /** We're adding a timeout to the loop used to read the result from the
194 * server - partially because of the 'fails to return anything' death
195 * spiral - but also because its a good idea anyway. Why DataInputStream
196 * doesn't have a TimeOutException, given its most often used to get data
197 * from a URL, I don't know. Lets default it to 60 seconds.
198 * DLConsulting 12-07-2004
199 */
200 private int timeout = 60;
201
202 public String getAppletInfo() {
203 return "Phind by Gordon Paynter ([email protected]). Copyright 1997-2000.";
204 }
205
206
207 public void init() {
208
209 mode = initMode;
210
211 System.out.println("Java vendor: " + System.getProperty("java.vendor"));
212 System.out.println("Java version: " + System.getProperty("java.version"));
213
214 // Read applet parameters
215 getParameters();
216
217 // Initialise the user interface
218 setBackground(panel_bg);
219 lastQueryEndTime = new Date();
220
221 // fonts used to output text
222 plainFont = new Font(fontName, Font.PLAIN, fontSize);
223 boldFont = new Font(fontName, Font.BOLD, fontSize);
224
225 // The phind applet layout manager
226 setLayout(new BorderLayout());
227
228 // Panel containing the displays is in the center of the display
229 resultPanel = new Panel();
230 if (vertical) {
231 resultPanel.setLayout(new GridLayout(depth,1,0,2));
232 } else {
233 System.out.println("horizontal");
234 resultPanel.setLayout(new GridLayout(1,depth,2,0));
235 }
236 add("Center", resultPanel);
237
238 // Create ResultDisplays and place into the interface
239 ResultDisplay d1, d2 = null;
240 firstDisplay = new ResultDisplay(this, null);
241 resultPanel.add(firstDisplay);
242
243 if (depth == 1) {
244 lastDisplay = firstDisplay;
245 } else {
246 d1 = firstDisplay;
247 for (int i = 2; i <= depth; i++) {
248 d2 = new ResultDisplay(this, d1);
249 resultPanel.add(d2);
250 d1 = d2;
251 }
252 lastDisplay = d2;
253 }
254
255 // The control panel
256 initialiseControlPanel();
257
258 // lets get started then
259 setStatus("Welcome to Phind.");
260 mode = idleMode;
261
262 // Perform initial search, if requested
263 if (initialSearch.length() > 0) {
264 searchForWord(initialSearch);
265 }
266 }
267
268
269 // Display a message in the status bar
270 void setStatus(String status) {
271 showStatus(status);
272 }
273
274 // The user performs an action in the interface
275 public boolean action(Event evt, Object arg) {
276
277 if (evt.target == searchButton) {
278 searchForWord(getSearchTerm());
279 } else if (evt.target == wordField) {
280 searchForWord(getSearchTerm());
281 } else if (evt.target == prevButton) {
282 shiftPrevious();
283 } else if (evt.target == nextButton) {
284 shiftNext();
285 } else {
286 System.out.println("unknown action: " + evt.toString()
287 + ", object: " + arg.toString());
288 }
289 return true;
290 }
291
292
293 // Search for a word
294 //
295 // Called on two occasions:
296 // when the "Search" Button is pressed, or
297 // to perform an "initial search"
298 void searchForWord(String searchWord) {
299
300 if (mode == idleMode) {
301
302 setSearchTerm(searchWord);
303
304 // Convert the String from UTF8 charaters into
305 // an encoding that is okay for a URL.
306 searchWord = URLUTF8Encoder.encode(searchWord);
307
308 // Look up the word
309 if (searchWord.length() > 1) {
310 setStatus("searching for \"" + searchWord + "\"");
311 firstDisplay.emptyContents();
312 ResultBox result = lookupPhraseOnServer(null, false, searchWord, searchWord, 2);
313
314 // if there is an error, return
315 if (result == null) {
316 setStatus("No results for \"" + searchWord + "\"");
317 return;
318 }
319
320 // display the result
321 result.display = firstDisplay.display(result);
322 result.resize(result.display.size());
323 result.paintAll(result.getGraphics());
324 }
325
326 enablePreviousAndNext();
327 }
328 }
329
330
331 // Search for a phrase
332 //
333 // If querymode is 2, the user has clicked on a phrase.
334 // If querymode is 3, the user has requested more phrases.
335 // If querymode is 4, the user has requested more documents.
336 void searchForPhrase(ResultBox source, String key, String phrase, int queryMode) {
337
338 // System.out.println("searchForPhrase: " + key + " " + phrase + " " + queryMode);
339
340 if (mode == idleMode) {
341
342 // If we are going to replace the first ResultDisplay, then empty it
343 if (queryMode <= 2) {
344 if (source.display.next != null) source.display.next.emptyContents();
345 }
346
347 // look up the word
348 setStatus("Searching for \"" + phrase + "\"");
349 ResultBox result = lookupPhraseOnServer(source, true, key, phrase, queryMode);
350 if (result == null) {
351 setStatus("No result for \"" + phrase + "\"");
352 return;
353 }
354
355 // If this is not already displayed, display it in the last free spot
356 if (queryMode <= 2) {
357 result.display = lastDisplay.display(result);
358 result.resize(result.display.size());
359 result.paintAll(result.getGraphics());
360 }
361
362 enablePreviousAndNext();
363 }
364 }
365
366
367 // Look up a phrase (or symbol) on the server
368 //
369 // Arguments are the source of the query (a ResultBox, or null if the
370 // query comes from hitting the search button), the key to search for
371 // (the text of a phrase or a symbol number), the phrase as a string,
372 // and the query mode.
373 // Query modes are:
374 // 0 = obsolete
375 // 1 = obsolete
376 // 2 = get first N phrases and URLs,
377 // 3 = get another N phrases into same window
378 // 4 = get another N documents into same window
379 // 5 = get another N thesaurus links into same window
380
381 ResultBox lookupPhraseOnServer(ResultBox source,
382 boolean keyKnown, String key, String phrase,
383 int queryMode) {
384 disableSearchButton();
385 mode = searchMode;
386 ResultBox r = null;
387
388 if (queryMode <= 2) {
389 r = new ResultBox(this, collection, key, phrase, source);
390 } else if ((queryMode == 3) || (queryMode == 4) || (queryMode == 5)) {
391 r = source;
392 }
393
394 try {
395 queryServer(keyKnown, key, queryMode, r);
396 } catch (Exception e) {
397 System.out.println("Phind query error: " + e.toString());
398 setStatus("Query error: " + e.toString());
399 mode = idleMode;
400 enableSearchButton();
401 return null;
402 }
403
404 // The query is finished
405 setStatus(r.c.numberOfItems + " results for \"" + phrase + "\"");
406 mode = idleMode;
407 enableSearchButton();
408 lastQueryEndTime = new Date();
409
410 return r;
411 }
412
413
414 // Query the phindcgi program
415 //
416 // Send a query (a word or symbol number) to the server
417 // and pass the response to a ResultBox.
418
419 void queryServer(boolean keyKnown, String word, int queryMode, ResultBox area)
420 throws IOException {
421
422 // Build the query
423 String query = phindcgi_address + "pxml=1&c=" + collection + "&pc=" + classifier;
424
425 if (keyKnown) {
426 query = query + "&ppnum=" + word;
427 } else {
428 query = query + "&pptext=" + word;
429 }
430
431
432 // Specify the set of results to return
433 int first_e = 0;
434 int last_e = 0;
435 int first_d = 0;
436 int last_d = 0;
437 int first_l = 0;
438 int last_l = 0;
439
440 // the initial query
441 if (queryMode <= 2) {
442 last_e = phraseBlockSize;
443 last_d = phraseBlockSize;
444 last_l = phraseBlockSize;
445 }
446
447 // add phrases to an existing result set
448 else if (queryMode == 3) {
449 first_e = area.nextPhraseBlock * phraseBlockSize;
450 area.nextPhraseBlock++;
451 last_e = area.nextPhraseBlock * phraseBlockSize;
452 }
453
454 // add documents to existing result set
455 else if (queryMode == 4) {
456 first_d = area.nextDocumentBlock * phraseBlockSize;
457 area.nextDocumentBlock++;
458 last_d = area.nextDocumentBlock * phraseBlockSize;
459 }
460
461 // add thesaurus links to existing result set
462 else if (queryMode == 5) {
463 first_l = area.nextThesaurusLinkBlock * phraseBlockSize;
464 area.nextThesaurusLinkBlock++;
465 last_l = area.nextThesaurusLinkBlock * phraseBlockSize;
466 }
467
468 query = query + "&pfe=" + first_e + "&ple=" + last_e
469 + "&pfd=" + first_d + "&pld=" + last_d
470 + "&pfl=" + first_l + "&pll=" + last_l;
471
472 // Send the query to the phindcgi program
473 System.out.println("sending query: " + query);
474 /** Start timeout counter.
475 * DLConsulting 12-07-2004
476 */
477 int time = 0;
478 try {
479 URL phindcgi = new URL(query);
480 // DataInputStream in = new DataInputStream(phindcgi.openStream());
481
482 URLConnection conn = phindcgi.openConnection();
483 DataInputStream in = new DataInputStream(conn.getInputStream());
484
485 byte[] buffer;
486 int availableBytes = 0;
487
488
489 while (!area.finished) {
490 // Lovely old method, which would work just fine if Sun supported their own API and made
491 // in.available() work.
492
493 /*
494 availableBytes = in.available();
495 if (availableBytes == 0) {
496 // if i had threads i'd do a wait here for 1 second
497 /** Ah-ha but we do have threads here - at least one thread
498 * anyway! Note that we want to prevent interrupted
499 * exceptions flinging us out of the read loop.
500 * We also check that the time count hasn't reached out
501 * timeout threshold yet. If so, make use of the nifty
502 * execption handling.
503 * DLConsulting 12-07-2004
504 * Start...
505
506 System.err.println("No bytes available");
507 if(time >= timeout) {
508 throw new InterruptedIOException(
509 "No data recieved in " + time +
510 " seconds. Connection timed-out.");
511 }
512 try {
513 Thread.sleep(1000);
514 }
515 catch(InterruptedException exception) {
516 System.err.println("Unexpected InterruptedException. Non-fatal.");
517 exception.printStackTrace();
518 }
519 time++;
520 /** ...End
521 } else {
522 System.err.println("Preparing to read " + availableBytes + " bytes");
523 buffer = new byte[availableBytes];
524 in.read(buffer);
525 System.err.println("Done reading");
526 area.parseBytes(buffer);
527 }
528 */
529
530 // New - and hopefully safer - method.
531
532 // Create a new SafeReader, and then wait while it reads
533 // we can watch for timeouts as necessary
534 int timeout = 0;
535 SafeReader reader = new SafeReader(in);
536 reader.start();
537 while(!reader.isComplete()) {
538 // Wait timeout time.
539 try {
540 Thread.sleep(1000);
541 }
542 catch(InterruptedException exception) {
543 System.err.println("test.init(): Unexpected InterruptedException. Non-fatal.");
544 exception.printStackTrace();
545 }
546 // Then see if the readers read anything
547 if(reader.poll() == 0) {
548 timeout++;
549 // Have we reached timeout?
550 if(timeout > reader.getTimeout()) {
551 // Nothing been read. Kill the reader
552 reader.interrupt();
553 // And throw a hissy fit
554 throw new InterruptedIOException("No data recieved in " + reader.getTimeout() +
555 " seconds. Connection timed-out.");
556 }
557 // Otherwise, wait a little longer.
558 }
559 // Otherwise we can continue waiting and polling
560 }
561 // Process the buffer
562 System.err.println("Done reading");
563 area.parseBytes(reader.getBuffer());
564 }
565
566 in.close();
567 } catch (Exception e) {
568 System.err.println( "Error sending query to phindcgi: " + e);
569 }
570 area.repaint();
571 }
572
573
574 // Tidy up URLs
575 //
576 // Ensure a URL address (as string) has a protocol, host, and file.
577 //
578 // If the URL is a CGI script URL, it should be tidied up so that it is
579 // appropriate to tage attrib=value pairs on the end. This means it
580 // must either end with a "?" or (if it contains a question-mark
581 // internally) end with a "&".
582 String tidy_URL(String address, boolean isCGI) {
583
584 // System.err.println("tidy URL: " + address);
585
586 // make sure the URL has protocol, host, and file
587 if (address.startsWith("http")) {
588 // the address has all the necessary components
589 } else if (address.startsWith("/")) {
590 // there is no protocol and host
591 URL document = getDocumentBase();
592 String protocol = document.getProtocol();
593 String host = document.getHost();
594 String port = "";
595 if (document.getPort()!=-1) {
596 port = ":" + document.getPort();
597 }
598 address = protocol + "://" + host + port + address;
599 } else {
600 // this URL is relative to the directory the document is in
601 URL document = getDocumentBase();
602 String protocol = document.getProtocol();
603 String host = document.getHost();
604 String directory = document.getFile();
605 int end = directory.lastIndexOf('/');
606 String port = "";
607 if (document.getPort() != -1) {
608 port = ":" + document.getPort();
609 }
610 directory = directory.substring(0, end + 1);
611 address = protocol + "://" + host + port + directory + address;
612 }
613
614 // if the URL is a cgi script, make sure it has a "?" in it,
615 // and that it ends with a "?" or "&"
616 if (isCGI) {
617 if (address.indexOf((int) '?') == -1) {
618 address = address + "?";
619 } else if (!address.endsWith("?")) {
620 address = address + "&";
621 }
622 }
623
624 return address;
625 }
626
627
628
629 // Open an arbitrary web page
630 void displayWebPage(String address, String window) {
631 try {
632 URL url= new URL(address);
633 if (window.length() > 0) {
634 getAppletContext().showDocument(url, window);
635 } else {
636 getAppletContext().showDocument(url);
637 }
638 } catch (Exception e) {
639 System.out.println("Cannot open web page: " + e.toString());
640 }
641 }
642
643
644 // Get the applet parameters
645 void getParameters() {
646
647 // What is this collection called?
648 collection = parameterValue("collection");
649 System.out.println("Phind collection: " + collection);
650
651 // Which of the collection's classifiers are we using?
652 classifier = parameterValue("classifier", "1");
653 System.out.println("Phind classifier: " + classifier);
654
655 // Where is the phind CGI script
656 phindcgi_address = parameterValue("phindcgi");
657 phindcgi_address = tidy_URL(phindcgi_address, true);
658 System.out.println("Phind phindcgi: " + phindcgi_address);
659
660 // Where is the Greenstone library
661 library_address = parameterValue("library");
662 library_address = tidy_URL(library_address, true);
663 System.out.println("Phind library: " + library_address);
664
665 // Is there a default search term?
666 initialSearch = parameterValue("initial_search", "");
667
668 // Should we display the control panel
669 showControlPanel = true;
670 if (parameterValue("control_panel", "show").toLowerCase().equals("hide")) {
671 showControlPanel = false;
672 }
673
674 // Should we show a background image?
675 backdrop_address = parameterValue("backdrop", "");
676 if (backdrop_address.length() > 0) {
677 backdrop_address = tidy_URL(backdrop_address, false);
678 System.out.println("Phind backdrop URL: " + backdrop_address);
679
680 try {
681 URL backdrop_url = new URL(backdrop_address);
682 backgroundImage = getImage(backdrop_url);
683 showImage = true;
684 } catch (Exception e) {
685 System.out.println("Phind could not load " + backdrop_address);
686 showImage = false;
687 }
688 }
689
690 // Should we draw a border?
691 showBorder = parameterValue("border", "on").equals("off");
692
693 // Are the windows arranged vertically or horizontally
694 if (parameterValue("orientation", "vertical").toLowerCase().startsWith("hori")) {
695 vertical = false;
696 } else {
697 vertical = true;
698 }
699
700 // How many phind windows are there?
701 depth = parameterValue("depth", 3);
702
703 // Result sort order
704 // Standard is "LlEeDd", expansion-first is "EeLlDd"
705 String order = parameterValue("resultorder", "standard");
706 if (!order.equals("standard")) {
707 int next = 20;
708 ResultItem.sortMessage = next;
709 for (int x = 0; x < order.length(); x++) {
710 if (order.charAt(x) == ',') {
711 next--;
712 } else if (order.charAt(x) == 'L') {
713 ResultItem.sortLinkItem = next;
714 } else if (order.charAt(x) == 'l') {
715 ResultItem.sortMoreLinks = next;
716 } else if (order.charAt(x) == 'E') {
717 ResultItem.sortPhraseItem = next;
718 } else if (order.charAt(x) == 'e') {
719 ResultItem.sortMorePhrases = next;
720 } else if (order.charAt(x) == 'D') {
721 ResultItem.sortDocumentItem = next;
722 } else if (order.charAt(x) == 'd') {
723 ResultItem.sortMoreDocuments = next;
724 }
725 }
726 System.out.println("link: " + ResultItem.sortLinkItem);
727 System.out.println("exps: " + ResultItem.sortPhraseItem);
728 System.out.println("docs: " + ResultItem.sortDocumentItem);
729
730 }
731
732 // How many phrases should we fetch at any given time?
733 phraseBlockSize = parameterValue("blocksize", 10);
734
735 // What font should we use?
736 fontSize = parameterValue("fontsize", 10);
737 fontName = parameterValue("fontname", "Helvetica");
738
739 // Column dimensions
740 column_1_width = parameterValue("first_column_width", 6);
741 column_2_width = parameterValue("second_column_width", column_1_width);
742
743 // Where do we open new windows
744 searchWindowName = parameterValue("search_window", "phindsearch");
745 documentWindowName = parameterValue("document_window", "phinddoc");
746
747 // Colours
748 panel_fg = parameterValue("panel_fg", Color.black);
749 panel_bg = parameterValue("panel_bg", Color.white);
750
751 highlight_bg = parameterValue("highlight_bg", Color.yellow);
752
753 expansion_fg = parameterValue("expansion_fg", Color.black);
754 thesaurus_fg = parameterValue("thesaurus_fg", new Color(0, 100, 0));
755 document_fg = parameterValue("document_fg", Color.blue);
756
757 thesaurus_bar_fg = parameterValue("thesaurus_bar_fg", Color.black);
758 expansion_bar_fg = parameterValue("expansion_bar_fg", Color.black);
759 document_bar_fg = parameterValue("document_bar_fg", Color.black);
760
761 thesaurus_bar_bg = parameterValue("thesaurus_bar_bg", new Color(160, 160, 190));
762 expansion_bar_bg = parameterValue("expansion_bar_bg", new Color(255, 200, 200));
763 document_bar_bg = parameterValue("document_bar_bg", new Color(150, 193, 156));
764
765 column_1_fg = parameterValue("first_column_fg", Color.black);
766 column_1_bg = parameterValue("first_column_bg", new Color(235, 245, 235));
767 column_2_fg = parameterValue("second_column_fg", Color.black);
768 column_2_bg = parameterValue("second_column_bg", new Color(200, 220, 200));
769
770 message_fg = parameterValue("message_fg", Color.black);
771 message_bg = parameterValue("message_bg", Color.white);
772
773 // Colours I don't use, yet
774 // thesaurus_bg = parameterValue("thesaurus_bg", Color.white);
775 // expansion_bg = parameterValue("expansion_bg", Color.white);
776 // document_bg = parameterValue("document_bg", Color.white);
777
778 /** How long should the read loop wait before timing out?
779 * DLConsulting 12-07-2004
780 */
781 timeout = parameterValue("timeout", 60);
782 }
783
784 // Get the value of a parameter given its name.
785 // There are many types of parameters, hence the variety of functions.
786
787 // Get a REQUIRED string. Stop the applet if we cannot.
788 String parameterValue(String name) {
789 try {
790 return getParameter(name);
791 } catch (Exception e) {
792 System.err.println("Phind: you must give a parameter for \""
793 + name + "\". Stopping.");
794 stop();
795 }
796 return "";
797 }
798
799 // Get an optional parameter. Return a default if we cannot.
800 String parameterValue(String name, String defaultValue) {
801 String text = getParameter(name);
802 if (text == null) {
803 return defaultValue;
804 }
805 System.out.println("Phind " + name + ": " + text);
806 return text;
807 }
808
809 int parameterValue(String name, int defaultValue) {
810 int value;
811 try {
812 value = Integer.parseInt(getParameter(name));
813 } catch (Exception e) {
814 return defaultValue;
815 }
816 System.out.println("Phind " + name + ": " + value);
817 return value;
818 }
819
820 Color parameterValue(String name, Color defaultValue) {
821
822 String text = getParameter(name);
823 if (text == null) {
824 return defaultValue;
825 }
826 text = text.toLowerCase();
827
828 // a number of the form "#ddffee"
829 if (text.startsWith("#") && (text.length() == 7)) {
830 text = text.substring(1);
831 int r, g, b;
832 try {
833 r = Integer.parseInt(text.substring(0,2), 16);
834 g = Integer.parseInt(text.substring(2,4), 16);
835 b = Integer.parseInt(text.substring(4), 16);
836 return new Color(r, g, b);
837 } catch (Exception e) {
838 return defaultValue;
839 }
840 }
841
842 // a known Java colour string
843 else if (text.equals("black")) { return Color.black; }
844 else if (text.equals("blue")) { return Color.blue; }
845 else if (text.equals("cyan")) { return Color.cyan; }
846 else if (text.equals("darkgray")) { return Color.darkGray; }
847 else if (text.equals("gray")) { return Color.gray; }
848 else if (text.equals("green")) { return Color.green; }
849 else if (text.equals("lightgray")) { return Color.lightGray; }
850 else if (text.equals("magenta")) { return Color.magenta; }
851 else if (text.equals("orange")) { return Color.orange; }
852 else if (text.equals("pink")) { return Color.pink; }
853 else if (text.equals("red")) { return Color.red; }
854 else if (text.equals("white")) { return Color.white; }
855 else if (text.equals("yellow")) { return Color.yellow; }
856
857 return defaultValue;
858 }
859
860
861 // Control panel operations
862
863 // Initialise the control panel
864 void initialiseControlPanel() {
865
866 if (showControlPanel) {
867 Panel p1 = new Panel();
868 add("North", p1);
869 p1.setLayout(new FlowLayout(FlowLayout.CENTER, 0, 0));
870
871 searchButton = new Button("Search");
872 searchButton.setFont(boldFont);
873 p1.add(searchButton);
874
875 Label tempLabel = new Label(" for");
876 tempLabel.setFont(boldFont);
877 p1.add(tempLabel);
878
879 wordField = new TextField(12);
880 wordField.setFont(boldFont);
881 p1.add(wordField);
882
883 Label temp2 = new Label(" ");
884 p1.add(temp2);
885
886 prevButton = new Button("Previous");
887 prevButton.setFont(boldFont);
888 prevButton.disable();
889
890 p1.add(prevButton);
891
892 nextButton = new Button(" Next ");
893 nextButton.setFont(boldFont);
894 nextButton.disable();
895 p1.add(nextButton);
896
897 }
898 }
899
900 // Button and field functionality
901
902 // Enable and disable the word field
903 void enableSearchButton() {
904 if (showControlPanel) {
905 searchButton.enable();
906 }
907 }
908 void disableSearchButton() {
909 if (showControlPanel) {
910 searchButton.disable();
911 }
912 }
913
914 // Get and set the search text in the wordField
915 String getSearchTerm() {
916 if (showControlPanel) {
917 return wordField.getText();
918 } else {
919 return initialSearch;
920 }
921 }
922 void setSearchTerm(String word) {
923 if (showControlPanel) {
924 wordField.setText(word);
925 }
926 }
927
928 // Enable or disable the "Previous" and "Next" buttons
929 void enablePreviousAndNext() {
930 if (showControlPanel) {
931 Component c = firstDisplay.current;
932 if (c.getClass().getName().endsWith("ResultBox")) {
933 if (((ResultBox) c).prevBoxExists()) {
934 prevButton.enable();
935 } else {
936 prevButton.disable();
937 }
938 }
939
940 c = lastDisplay.current;
941 if (c.getClass().getName().endsWith("ResultBox")) {
942 if (((ResultBox) c).nextBoxExists()) {
943 nextButton.enable();
944 } else {
945 nextButton.disable();
946 }
947 }
948 }
949 }
950
951 // Shift to previous box
952 //
953 // If the user clicks "Previous" then scroll up.
954 void shiftPrevious() {
955
956 Component c = firstDisplay.current;
957 if (c.getClass().getName().endsWith("ResultBox")) {
958
959 ResultBox b = (ResultBox) c;
960 if (b.prevBoxExists()) {
961 b = b.prev;
962
963 // empty all the displays
964 firstDisplay.emptyContents();
965
966 // add as many result boxes as there are displays
967 for (int i = 1 ; ((i <= depth) && (b != null)); i++) {
968 lastDisplay.display(b);
969 b.resize(b.display.size());
970 b.paintAll(b.getGraphics());
971 b = b.next;
972 }
973 }
974 }
975 enablePreviousAndNext();
976 }
977
978 // Shift to next box
979 //
980 // If the user clicks "Next" then scroll down if possible
981 void shiftNext() {
982
983 Component c = lastDisplay.current;
984 if (c.getClass().getName().endsWith("ResultBox")) {
985
986 ResultBox b = (ResultBox) c;
987 if (b.nextBoxExists()) {
988
989 // find the new "first" displayed box
990 c = firstDisplay.current;
991 b = (ResultBox) c;
992 b = b.next;
993
994 // empty all the displays
995 firstDisplay.emptyContents();
996
997 // add as many result boxes as there are displays
998 for (int i = 1 ; ((i <= depth) && (b != null)); i++) {
999 lastDisplay.display(b);
1000 b.resize(b.display.size());
1001 b.paintAll(b.getGraphics());
1002 b = b.next;
1003 }
1004 }
1005 }
1006 enablePreviousAndNext();
1007 }
1008
1009 public class SafeReader
1010 extends Thread {
1011 // Has the reader finished reading?
1012 private boolean complete;
1013 // The buffer in where the data read is stored
1014 private byte[] buffer;
1015 // The stream this reader reads from
1016 private DataInputStream in;
1017 // If the bytes_read hasn't changed in this time, then stop this thread
1018 // and timeout
1019 private int timeout;
1020 // The current buffer size
1021 private int buffer_current_size;
1022 // The maximum buffer size
1023 private int buffer_max_size;
1024 // The number of bytes read through this reader since the last poll
1025 private int bytes_read;
1026
1027 /** Default constructor
1028 * @param in the DataInputStream to read from
1029 */
1030 public SafeReader(DataInputStream in) {
1031 complete = false;
1032 buffer_current_size = 0;
1033 buffer_max_size = 16;
1034 buffer = new byte[buffer_max_size]; // Default size
1035 timeout = 60; // Sixty seconds
1036 bytes_read = 0;
1037
1038 this.in = in;
1039 }
1040
1041 /** Construct a new SafeReader
1042 * @param in the DataInputStream to read from
1043 * @param timeout how long this thread should wait when reading data
1044 */
1045 public SafeReader(DataInputStream in, int timeout) {
1046 this(in);
1047 this.timeout = timeout;
1048 }
1049
1050 /** Retrieve the current buffer
1051 * @return a byte[] buffer trimmed to the appropriate size
1052 */
1053 public byte[] getBuffer() {
1054 byte[] final_buffer = new byte[buffer_current_size];
1055 System.arraycopy(buffer, 0, final_buffer, 0, buffer_current_size);
1056 return final_buffer;
1057 }
1058
1059 /** Retrieve the current timeout
1060 * @return the timeout as an int
1061 */
1062 public int getTimeout() {
1063 return timeout;
1064 }
1065
1066 /** See whether the reader has finished reading
1067 * @return true if the read is complete (-1 bytes read!) or false otherwise
1068 */
1069 public synchronized boolean isComplete() {
1070 return complete;
1071 }
1072
1073 /** Poll the reader to determine if it has read any bytes recently
1074 * @return the number of bytes read
1075 */
1076 public long poll() {
1077 // Maybe should test if complete - but its only one second right?
1078 long result = bytes_read;
1079 bytes_read = 0;
1080 return result;
1081 }
1082
1083 /** Try to read bytes from the input stream. Note the caller is responsible for closing the datainputstream
1084 */
1085 public void run() {
1086 try {
1087 int i = 0;
1088 byte[] read_buffer = new byte[8];
1089 while ((i = in.read(read_buffer)) != -1) {
1090 // Check our storage buffer is big enough
1091 if(buffer_max_size - (buffer_current_size + i) <= 0) {
1092 // Double the size
1093 buffer_max_size = 2 * buffer_max_size;
1094 byte[] new_buffer = new byte[buffer_max_size];
1095 System.arraycopy(buffer, 0, new_buffer, 0, buffer_current_size);
1096 buffer = new_buffer;
1097 }
1098 // Then copy in the read buffer
1099 System.arraycopy(read_buffer, 0, buffer, buffer_current_size, i);
1100 buffer_current_size += i;
1101 bytes_read += i;
1102 }
1103 setComplete(true);
1104 }
1105 catch(IOException exception) {
1106 System.err.println("SafeReader.run(): Unexpected IOException. Fatal.");
1107 exception.printStackTrace();
1108 }
1109 }
1110
1111 /** Set the complete boolean to the desired state, synchronized to avoid race with isComplete()
1112 * @param value the desired state
1113 */
1114 private synchronized void setComplete(boolean value) {
1115 complete = value;
1116 }
1117 }
1118}
1119
Note: See TracBrowser for help on using the repository browser.