source: main/trunk/greenstone2/runtime-src/src/java/org/nzdl/gsdl/Phind/Phind.java@ 21878

Last change on this file since 21878 was 21878, checked in by kjdon, 14 years ago

we need to keep the original version of the search word to display for the title of the result box for the case where there are no results. Otherwise you end up with URL encoded version being displayed, which is fine for english, but not for arabic.

  • Property svn:keywords set to Author Date Id Revision
File size: 33.6 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 // we need to keep the original version of the search word to
304 // display for the title of the result box for the case where
305 // there are no results. Otherwise you end up with URL encoded
306 // version being displayed.
307 String originalSearchWord = searchWord;
308
309 // Convert the String from UTF8 charaters into
310 // an encoding that is okay for a URL.
311 searchWord = URLUTF8Encoder.encode(searchWord);
312
313 // Look up the word
314 if (searchWord.length() > 1) {
315 setStatus("searching for \"" + searchWord + "\"");
316 firstDisplay.emptyContents();
317 ResultBox result = lookupPhraseOnServer(null, false, searchWord, originalSearchWord, 2);
318
319 // if there is an error, return
320 if (result == null) {
321 setStatus("No results for \"" + searchWord + "\"");
322 return;
323 }
324
325 // display the result
326 result.display = firstDisplay.display(result);
327 result.resize(result.display.size());
328 result.paintAll(result.getGraphics());
329 }
330
331 enablePreviousAndNext();
332 }
333 }
334
335
336 // Search for a phrase
337 //
338 // If querymode is 2, the user has clicked on a phrase.
339 // If querymode is 3, the user has requested more phrases.
340 // If querymode is 4, the user has requested more documents.
341 void searchForPhrase(ResultBox source, String key, String phrase, int queryMode) {
342
343 // System.out.println("searchForPhrase: " + key + " " + phrase + " " + queryMode);
344
345 if (mode == idleMode) {
346
347 // If we are going to replace the first ResultDisplay, then empty it
348 if (queryMode <= 2) {
349 if (source.display.next != null) source.display.next.emptyContents();
350 }
351
352 // look up the word
353 setStatus("Searching for \"" + phrase + "\"");
354 ResultBox result = lookupPhraseOnServer(source, true, key, phrase, queryMode);
355 if (result == null) {
356 setStatus("No result for \"" + phrase + "\"");
357 return;
358 }
359
360 // If this is not already displayed, display it in the last free spot
361 if (queryMode <= 2) {
362 result.display = lastDisplay.display(result);
363 result.resize(result.display.size());
364 result.paintAll(result.getGraphics());
365 }
366
367 enablePreviousAndNext();
368 }
369 }
370
371
372 // Look up a phrase (or symbol) on the server
373 //
374 // Arguments are the source of the query (a ResultBox, or null if the
375 // query comes from hitting the search button), the key to search for
376 // (the text of a phrase or a symbol number), the phrase as a string,
377 // and the query mode.
378 // Query modes are:
379 // 0 = obsolete
380 // 1 = obsolete
381 // 2 = get first N phrases and URLs,
382 // 3 = get another N phrases into same window
383 // 4 = get another N documents into same window
384 // 5 = get another N thesaurus links into same window
385
386 ResultBox lookupPhraseOnServer(ResultBox source,
387 boolean keyKnown, String key, String phrase,
388 int queryMode) {
389 disableSearchButton();
390 mode = searchMode;
391 ResultBox r = null;
392
393 if (queryMode <= 2) {
394 r = new ResultBox(this, collection, key, phrase, source);
395 } else if ((queryMode == 3) || (queryMode == 4) || (queryMode == 5)) {
396 r = source;
397 }
398
399 try {
400 queryServer(keyKnown, key, queryMode, r);
401 } catch (Exception e) {
402 System.out.println("Phind query error: " + e.toString());
403 setStatus("Query error: " + e.toString());
404 mode = idleMode;
405 enableSearchButton();
406 return null;
407 }
408
409 // The query is finished
410 setStatus(r.c.numberOfItems + " results for \"" + phrase + "\"");
411 mode = idleMode;
412 enableSearchButton();
413 lastQueryEndTime = new Date();
414
415 return r;
416 }
417
418
419 // Query the phindcgi program
420 //
421 // Send a query (a word or symbol number) to the server
422 // and pass the response to a ResultBox.
423
424 void queryServer(boolean keyKnown, String word, int queryMode, ResultBox area)
425 throws IOException {
426
427 // Build the query
428 String query = phindcgi_address + "pxml=1&c=" + collection + "&pc=" + classifier;
429
430 if (keyKnown) {
431 query = query + "&ppnum=" + word;
432 } else {
433 query = query + "&pptext=" + word;
434 }
435
436
437 // Specify the set of results to return
438 int first_e = 0;
439 int last_e = 0;
440 int first_d = 0;
441 int last_d = 0;
442 int first_l = 0;
443 int last_l = 0;
444
445 // the initial query
446 if (queryMode <= 2) {
447 last_e = phraseBlockSize;
448 last_d = phraseBlockSize;
449 last_l = phraseBlockSize;
450 }
451
452 // add phrases to an existing result set
453 else if (queryMode == 3) {
454 first_e = area.nextPhraseBlock * phraseBlockSize;
455 area.nextPhraseBlock++;
456 last_e = area.nextPhraseBlock * phraseBlockSize;
457 }
458
459 // add documents to existing result set
460 else if (queryMode == 4) {
461 first_d = area.nextDocumentBlock * phraseBlockSize;
462 area.nextDocumentBlock++;
463 last_d = area.nextDocumentBlock * phraseBlockSize;
464 }
465
466 // add thesaurus links to existing result set
467 else if (queryMode == 5) {
468 first_l = area.nextThesaurusLinkBlock * phraseBlockSize;
469 area.nextThesaurusLinkBlock++;
470 last_l = area.nextThesaurusLinkBlock * phraseBlockSize;
471 }
472
473 query = query + "&pfe=" + first_e + "&ple=" + last_e
474 + "&pfd=" + first_d + "&pld=" + last_d
475 + "&pfl=" + first_l + "&pll=" + last_l;
476
477 // Send the query to the phindcgi program
478 System.out.println("sending query: " + query);
479 /** Start timeout counter.
480 * DLConsulting 12-07-2004
481 */
482 int time = 0;
483 try {
484 URL phindcgi = new URL(query);
485 // DataInputStream in = new DataInputStream(phindcgi.openStream());
486
487 URLConnection conn = phindcgi.openConnection();
488 DataInputStream in = new DataInputStream(conn.getInputStream());
489
490 byte[] buffer;
491 int availableBytes = 0;
492
493
494 while (!area.finished) {
495 // Lovely old method, which would work just fine if Sun supported their own API and made
496 // in.available() work.
497
498 /*
499 availableBytes = in.available();
500 if (availableBytes == 0) {
501 // if i had threads i'd do a wait here for 1 second
502 /** Ah-ha but we do have threads here - at least one thread
503 * anyway! Note that we want to prevent interrupted
504 * exceptions flinging us out of the read loop.
505 * We also check that the time count hasn't reached out
506 * timeout threshold yet. If so, make use of the nifty
507 * execption handling.
508 * DLConsulting 12-07-2004
509 * Start...
510
511 System.err.println("No bytes available");
512 if(time >= timeout) {
513 throw new InterruptedIOException(
514 "No data recieved in " + time +
515 " seconds. Connection timed-out.");
516 }
517 try {
518 Thread.sleep(1000);
519 }
520 catch(InterruptedException exception) {
521 System.err.println("Unexpected InterruptedException. Non-fatal.");
522 exception.printStackTrace();
523 }
524 time++;
525 /** ...End
526 } else {
527 System.err.println("Preparing to read " + availableBytes + " bytes");
528 buffer = new byte[availableBytes];
529 in.read(buffer);
530 System.err.println("Done reading");
531 area.parseBytes(buffer);
532 }
533 */
534
535 // New - and hopefully safer - method.
536
537 // Create a new SafeReader, and then wait while it reads
538 // we can watch for timeouts as necessary
539 int timeout = 0;
540 SafeReader reader = new SafeReader(in);
541 reader.start();
542 while(!reader.isComplete()) {
543 // Wait timeout time.
544 try {
545 Thread.sleep(1000);
546 }
547 catch(InterruptedException exception) {
548 System.err.println("test.init(): Unexpected InterruptedException. Non-fatal.");
549 exception.printStackTrace();
550 }
551 // Then see if the readers read anything
552 if(reader.poll() == 0) {
553 timeout++;
554 // Have we reached timeout?
555 if(timeout > reader.getTimeout()) {
556 // Nothing been read. Kill the reader
557 reader.interrupt();
558 // And throw a hissy fit
559 throw new InterruptedIOException("No data recieved in " + reader.getTimeout() +
560 " seconds. Connection timed-out.");
561 }
562 // Otherwise, wait a little longer.
563 }
564 // Otherwise we can continue waiting and polling
565 }
566 // Process the buffer
567 System.err.println("Done reading");
568 area.parseBytes(reader.getBuffer());
569 }
570
571 in.close();
572 } catch (Exception e) {
573 System.err.println( "Error sending query to phindcgi: " + e);
574 }
575 area.repaint();
576 }
577
578
579 // Tidy up URLs
580 //
581 // Ensure a URL address (as string) has a protocol, host, and file.
582 //
583 // If the URL is a CGI script URL, it should be tidied up so that it is
584 // appropriate to tage attrib=value pairs on the end. This means it
585 // must either end with a "?" or (if it contains a question-mark
586 // internally) end with a "&".
587 String tidy_URL(String address, boolean isCGI) {
588
589 // System.err.println("tidy URL: " + address);
590
591 // make sure the URL has protocol, host, and file
592 if (address.startsWith("http")) {
593 // the address has all the necessary components
594 } else if (address.startsWith("/")) {
595 // there is no protocol and host
596 URL document = getDocumentBase();
597 String protocol = document.getProtocol();
598 String host = document.getHost();
599 String port = "";
600 if (document.getPort()!=-1) {
601 port = ":" + document.getPort();
602 }
603 address = protocol + "://" + host + port + address;
604 } else {
605 // this URL is relative to the directory the document is in
606 URL document = getDocumentBase();
607 String protocol = document.getProtocol();
608 String host = document.getHost();
609 String directory = document.getFile();
610 int end = directory.lastIndexOf('/');
611 String port = "";
612 if (document.getPort() != -1) {
613 port = ":" + document.getPort();
614 }
615 directory = directory.substring(0, end + 1);
616 address = protocol + "://" + host + port + directory + address;
617 }
618
619 // if the URL is a cgi script, make sure it has a "?" in it,
620 // and that it ends with a "?" or "&"
621 if (isCGI) {
622 if (address.indexOf((int) '?') == -1) {
623 address = address + "?";
624 } else if (!address.endsWith("?")) {
625 address = address + "&";
626 }
627 }
628
629 return address;
630 }
631
632
633
634 // Open an arbitrary web page
635 void displayWebPage(String address, String window) {
636 try {
637 URL url= new URL(address);
638 if (window.length() > 0) {
639 getAppletContext().showDocument(url, window);
640 } else {
641 getAppletContext().showDocument(url);
642 }
643 } catch (Exception e) {
644 System.out.println("Cannot open web page: " + e.toString());
645 }
646 }
647
648
649 // Get the applet parameters
650 void getParameters() {
651
652 // What is this collection called?
653 collection = parameterValue("collection");
654 System.out.println("Phind collection: " + collection);
655
656 // Which of the collection's classifiers are we using?
657 classifier = parameterValue("classifier", "1");
658 System.out.println("Phind classifier: " + classifier);
659
660 // Where is the phind CGI script
661 phindcgi_address = parameterValue("phindcgi");
662 phindcgi_address = tidy_URL(phindcgi_address, true);
663 System.out.println("Phind phindcgi: " + phindcgi_address);
664
665 // Where is the Greenstone library
666 library_address = parameterValue("library");
667 library_address = tidy_URL(library_address, true);
668 System.out.println("Phind library: " + library_address);
669
670 // Is there a default search term?
671 initialSearch = parameterValue("initial_search", "");
672
673 // Should we display the control panel
674 showControlPanel = true;
675 if (parameterValue("control_panel", "show").toLowerCase().equals("hide")) {
676 showControlPanel = false;
677 }
678
679 // Should we show a background image?
680 backdrop_address = parameterValue("backdrop", "");
681 if (backdrop_address.length() > 0) {
682 backdrop_address = tidy_URL(backdrop_address, false);
683 System.out.println("Phind backdrop URL: " + backdrop_address);
684
685 try {
686 URL backdrop_url = new URL(backdrop_address);
687 backgroundImage = getImage(backdrop_url);
688 showImage = true;
689 } catch (Exception e) {
690 System.out.println("Phind could not load " + backdrop_address);
691 showImage = false;
692 }
693 }
694
695 // Should we draw a border?
696 showBorder = parameterValue("border", "on").equals("off");
697
698 // Are the windows arranged vertically or horizontally
699 if (parameterValue("orientation", "vertical").toLowerCase().startsWith("hori")) {
700 vertical = false;
701 } else {
702 vertical = true;
703 }
704
705 // How many phind windows are there?
706 depth = parameterValue("depth", 3);
707
708 // Result sort order
709 // Standard is "LlEeDd", expansion-first is "EeLlDd"
710 String order = parameterValue("resultorder", "standard");
711 if (!order.equals("standard")) {
712 int next = 20;
713 ResultItem.sortMessage = next;
714 for (int x = 0; x < order.length(); x++) {
715 if (order.charAt(x) == ',') {
716 next--;
717 } else if (order.charAt(x) == 'L') {
718 ResultItem.sortLinkItem = next;
719 } else if (order.charAt(x) == 'l') {
720 ResultItem.sortMoreLinks = next;
721 } else if (order.charAt(x) == 'E') {
722 ResultItem.sortPhraseItem = next;
723 } else if (order.charAt(x) == 'e') {
724 ResultItem.sortMorePhrases = next;
725 } else if (order.charAt(x) == 'D') {
726 ResultItem.sortDocumentItem = next;
727 } else if (order.charAt(x) == 'd') {
728 ResultItem.sortMoreDocuments = next;
729 }
730 }
731 System.out.println("link: " + ResultItem.sortLinkItem);
732 System.out.println("exps: " + ResultItem.sortPhraseItem);
733 System.out.println("docs: " + ResultItem.sortDocumentItem);
734
735 }
736
737 // How many phrases should we fetch at any given time?
738 phraseBlockSize = parameterValue("blocksize", 10);
739
740 // What font should we use?
741 fontSize = parameterValue("fontsize", 10);
742 fontName = parameterValue("fontname", "Helvetica");
743
744 // Column dimensions
745 column_1_width = parameterValue("first_column_width", 6);
746 column_2_width = parameterValue("second_column_width", column_1_width);
747
748 // Where do we open new windows
749 searchWindowName = parameterValue("search_window", "phindsearch");
750 documentWindowName = parameterValue("document_window", "phinddoc");
751
752 // Colours
753 panel_fg = parameterValue("panel_fg", Color.black);
754 panel_bg = parameterValue("panel_bg", Color.white);
755
756 highlight_bg = parameterValue("highlight_bg", Color.yellow);
757
758 expansion_fg = parameterValue("expansion_fg", Color.black);
759 thesaurus_fg = parameterValue("thesaurus_fg", new Color(0, 100, 0));
760 document_fg = parameterValue("document_fg", Color.blue);
761
762 thesaurus_bar_fg = parameterValue("thesaurus_bar_fg", Color.black);
763 expansion_bar_fg = parameterValue("expansion_bar_fg", Color.black);
764 document_bar_fg = parameterValue("document_bar_fg", Color.black);
765
766 thesaurus_bar_bg = parameterValue("thesaurus_bar_bg", new Color(160, 160, 190));
767 expansion_bar_bg = parameterValue("expansion_bar_bg", new Color(255, 200, 200));
768 document_bar_bg = parameterValue("document_bar_bg", new Color(150, 193, 156));
769
770 column_1_fg = parameterValue("first_column_fg", Color.black);
771 column_1_bg = parameterValue("first_column_bg", new Color(235, 245, 235));
772 column_2_fg = parameterValue("second_column_fg", Color.black);
773 column_2_bg = parameterValue("second_column_bg", new Color(200, 220, 200));
774
775 message_fg = parameterValue("message_fg", Color.black);
776 message_bg = parameterValue("message_bg", Color.white);
777
778 // Colours I don't use, yet
779 // thesaurus_bg = parameterValue("thesaurus_bg", Color.white);
780 // expansion_bg = parameterValue("expansion_bg", Color.white);
781 // document_bg = parameterValue("document_bg", Color.white);
782
783 /** How long should the read loop wait before timing out?
784 * DLConsulting 12-07-2004
785 */
786 timeout = parameterValue("timeout", 60);
787 }
788
789 // Get the value of a parameter given its name.
790 // There are many types of parameters, hence the variety of functions.
791
792 // Get a REQUIRED string. Stop the applet if we cannot.
793 String parameterValue(String name) {
794 try {
795 return getParameter(name);
796 } catch (Exception e) {
797 System.err.println("Phind: you must give a parameter for \""
798 + name + "\". Stopping.");
799 stop();
800 }
801 return "";
802 }
803
804 // Get an optional parameter. Return a default if we cannot.
805 String parameterValue(String name, String defaultValue) {
806 String text = getParameter(name);
807 if (text == null) {
808 return defaultValue;
809 }
810 System.out.println("Phind " + name + ": " + text);
811 return text;
812 }
813
814 int parameterValue(String name, int defaultValue) {
815 int value;
816 try {
817 value = Integer.parseInt(getParameter(name));
818 } catch (Exception e) {
819 return defaultValue;
820 }
821 System.out.println("Phind " + name + ": " + value);
822 return value;
823 }
824
825 Color parameterValue(String name, Color defaultValue) {
826
827 String text = getParameter(name);
828 if (text == null) {
829 return defaultValue;
830 }
831 text = text.toLowerCase();
832
833 // a number of the form "#ddffee"
834 if (text.startsWith("#") && (text.length() == 7)) {
835 text = text.substring(1);
836 int r, g, b;
837 try {
838 r = Integer.parseInt(text.substring(0,2), 16);
839 g = Integer.parseInt(text.substring(2,4), 16);
840 b = Integer.parseInt(text.substring(4), 16);
841 return new Color(r, g, b);
842 } catch (Exception e) {
843 return defaultValue;
844 }
845 }
846
847 // a known Java colour string
848 else if (text.equals("black")) { return Color.black; }
849 else if (text.equals("blue")) { return Color.blue; }
850 else if (text.equals("cyan")) { return Color.cyan; }
851 else if (text.equals("darkgray")) { return Color.darkGray; }
852 else if (text.equals("gray")) { return Color.gray; }
853 else if (text.equals("green")) { return Color.green; }
854 else if (text.equals("lightgray")) { return Color.lightGray; }
855 else if (text.equals("magenta")) { return Color.magenta; }
856 else if (text.equals("orange")) { return Color.orange; }
857 else if (text.equals("pink")) { return Color.pink; }
858 else if (text.equals("red")) { return Color.red; }
859 else if (text.equals("white")) { return Color.white; }
860 else if (text.equals("yellow")) { return Color.yellow; }
861
862 return defaultValue;
863 }
864
865
866 // Control panel operations
867
868 // Initialise the control panel
869 void initialiseControlPanel() {
870
871 if (showControlPanel) {
872 Panel p1 = new Panel();
873 add("North", p1);
874 p1.setLayout(new FlowLayout(FlowLayout.CENTER, 0, 0));
875
876 searchButton = new Button("Search");
877 searchButton.setFont(boldFont);
878 p1.add(searchButton);
879
880 Label tempLabel = new Label(" for");
881 tempLabel.setFont(boldFont);
882 p1.add(tempLabel);
883
884 wordField = new TextField(12);
885 wordField.setFont(boldFont);
886 p1.add(wordField);
887
888 Label temp2 = new Label(" ");
889 p1.add(temp2);
890
891 prevButton = new Button("Previous");
892 prevButton.setFont(boldFont);
893 prevButton.disable();
894
895 p1.add(prevButton);
896
897 nextButton = new Button(" Next ");
898 nextButton.setFont(boldFont);
899 nextButton.disable();
900 p1.add(nextButton);
901
902 }
903 }
904
905 // Button and field functionality
906
907 // Enable and disable the word field
908 void enableSearchButton() {
909 if (showControlPanel) {
910 searchButton.enable();
911 }
912 }
913 void disableSearchButton() {
914 if (showControlPanel) {
915 searchButton.disable();
916 }
917 }
918
919 // Get and set the search text in the wordField
920 String getSearchTerm() {
921 if (showControlPanel) {
922 return wordField.getText();
923 } else {
924 return initialSearch;
925 }
926 }
927 void setSearchTerm(String word) {
928 if (showControlPanel) {
929 wordField.setText(word);
930 }
931 }
932
933 // Enable or disable the "Previous" and "Next" buttons
934 void enablePreviousAndNext() {
935 if (showControlPanel) {
936 Component c = firstDisplay.current;
937 if (c.getClass().getName().endsWith("ResultBox")) {
938 if (((ResultBox) c).prevBoxExists()) {
939 prevButton.enable();
940 } else {
941 prevButton.disable();
942 }
943 }
944
945 c = lastDisplay.current;
946 if (c.getClass().getName().endsWith("ResultBox")) {
947 if (((ResultBox) c).nextBoxExists()) {
948 nextButton.enable();
949 } else {
950 nextButton.disable();
951 }
952 }
953 }
954 }
955
956 // Shift to previous box
957 //
958 // If the user clicks "Previous" then scroll up.
959 void shiftPrevious() {
960
961 Component c = firstDisplay.current;
962 if (c.getClass().getName().endsWith("ResultBox")) {
963
964 ResultBox b = (ResultBox) c;
965 if (b.prevBoxExists()) {
966 b = b.prev;
967
968 // empty all the displays
969 firstDisplay.emptyContents();
970
971 // add as many result boxes as there are displays
972 for (int i = 1 ; ((i <= depth) && (b != null)); i++) {
973 lastDisplay.display(b);
974 b.resize(b.display.size());
975 b.paintAll(b.getGraphics());
976 b = b.next;
977 }
978 }
979 }
980 enablePreviousAndNext();
981 }
982
983 // Shift to next box
984 //
985 // If the user clicks "Next" then scroll down if possible
986 void shiftNext() {
987
988 Component c = lastDisplay.current;
989 if (c.getClass().getName().endsWith("ResultBox")) {
990
991 ResultBox b = (ResultBox) c;
992 if (b.nextBoxExists()) {
993
994 // find the new "first" displayed box
995 c = firstDisplay.current;
996 b = (ResultBox) c;
997 b = b.next;
998
999 // empty all the displays
1000 firstDisplay.emptyContents();
1001
1002 // add as many result boxes as there are displays
1003 for (int i = 1 ; ((i <= depth) && (b != null)); i++) {
1004 lastDisplay.display(b);
1005 b.resize(b.display.size());
1006 b.paintAll(b.getGraphics());
1007 b = b.next;
1008 }
1009 }
1010 }
1011 enablePreviousAndNext();
1012 }
1013
1014 public class SafeReader
1015 extends Thread {
1016 // Has the reader finished reading?
1017 private boolean complete;
1018 // The buffer in where the data read is stored
1019 private byte[] buffer;
1020 // The stream this reader reads from
1021 private DataInputStream in;
1022 // If the bytes_read hasn't changed in this time, then stop this thread
1023 // and timeout
1024 private int timeout;
1025 // The current buffer size
1026 private int buffer_current_size;
1027 // The maximum buffer size
1028 private int buffer_max_size;
1029 // The number of bytes read through this reader since the last poll
1030 private int bytes_read;
1031
1032 /** Default constructor
1033 * @param in the DataInputStream to read from
1034 */
1035 public SafeReader(DataInputStream in) {
1036 complete = false;
1037 buffer_current_size = 0;
1038 buffer_max_size = 16;
1039 buffer = new byte[buffer_max_size]; // Default size
1040 timeout = 60; // Sixty seconds
1041 bytes_read = 0;
1042
1043 this.in = in;
1044 }
1045
1046 /** Construct a new SafeReader
1047 * @param in the DataInputStream to read from
1048 * @param timeout how long this thread should wait when reading data
1049 */
1050 public SafeReader(DataInputStream in, int timeout) {
1051 this(in);
1052 this.timeout = timeout;
1053 }
1054
1055 /** Retrieve the current buffer
1056 * @return a byte[] buffer trimmed to the appropriate size
1057 */
1058 public byte[] getBuffer() {
1059 byte[] final_buffer = new byte[buffer_current_size];
1060 System.arraycopy(buffer, 0, final_buffer, 0, buffer_current_size);
1061 return final_buffer;
1062 }
1063
1064 /** Retrieve the current timeout
1065 * @return the timeout as an int
1066 */
1067 public int getTimeout() {
1068 return timeout;
1069 }
1070
1071 /** See whether the reader has finished reading
1072 * @return true if the read is complete (-1 bytes read!) or false otherwise
1073 */
1074 public synchronized boolean isComplete() {
1075 return complete;
1076 }
1077
1078 /** Poll the reader to determine if it has read any bytes recently
1079 * @return the number of bytes read
1080 */
1081 public long poll() {
1082 // Maybe should test if complete - but its only one second right?
1083 long result = bytes_read;
1084 bytes_read = 0;
1085 return result;
1086 }
1087
1088 /** Try to read bytes from the input stream. Note the caller is responsible for closing the datainputstream
1089 */
1090 public void run() {
1091 try {
1092 int i = 0;
1093 byte[] read_buffer = new byte[8];
1094 while ((i = in.read(read_buffer)) != -1) {
1095 // Check our storage buffer is big enough
1096 if(buffer_max_size - (buffer_current_size + i) <= 0) {
1097 // Double the size
1098 buffer_max_size = 2 * buffer_max_size;
1099 byte[] new_buffer = new byte[buffer_max_size];
1100 System.arraycopy(buffer, 0, new_buffer, 0, buffer_current_size);
1101 buffer = new_buffer;
1102 }
1103 // Then copy in the read buffer
1104 System.arraycopy(read_buffer, 0, buffer, buffer_current_size, i);
1105 buffer_current_size += i;
1106 bytes_read += i;
1107 }
1108 setComplete(true);
1109 }
1110 catch(IOException exception) {
1111 System.err.println("SafeReader.run(): Unexpected IOException. Fatal.");
1112 exception.printStackTrace();
1113 }
1114 }
1115
1116 /** Set the complete boolean to the desired state, synchronized to avoid race with isComplete()
1117 * @param value the desired state
1118 */
1119 private synchronized void setComplete(boolean value) {
1120 complete = value;
1121 }
1122 }
1123}
1124
Note: See TracBrowser for help on using the repository browser.