source: main/trunk/greenstone3/src/java/org/greenstone/applet/phind/JPhind.java@ 38937

Last change on this file since 38937 was 38937, checked in by anupama, 7 weeks ago

Cleaning up the code somewhat.

File size: 37.6 KB
Line 
1/**********************************************************************
2 * 2024 - rewriting as JApplet to work with webswing (which won't work with AWT Applet)
3 * http://fizyka.umk.pl/~jacek/docs/javatutorial/uiswing/converting/how.html
4 *
5 * Phind.java -- the Phind java applet - modified to work with gsdl3 kjdon
6 *
7 * Copyright 1997-2000 Gordon W. Paynter
8 * Copyright 2000 The New Zealand Digital Library Project
9 *
10 * A component of the Greenstone digital library software
11 * from the New Zealand Digital Library Project at the
12 * University of Waikato, New Zealand.
13 *
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
18 *
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to the Free Software
26 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27 *
28 *********************************************************************/
29
30
31/*********************************************************************
32
33JPhind is a JApplet swing port of the awt Phind Applet and can be run
34as an application too due to the inclusion of the main() method.
35
36To run as an applet, ensure PhindPhraseBrowse.java refers to JPhind.class
37(not the old Phind.class Applet), then run in commandline:
38
39appletviewer "http://localhost:8383/greenstone3/gs2-library?a=a&rt=d&s=PhindApplet&c=tudor"
40
41To run this applet as an application, for which purpose this class has a main() method, run as:
42
43java -cp ./web/WEB-INF/lib/gsdl3.jar:./web/WEB-INF/lib/gutil.jar:./web/applet/phind.jar:./web/applet/xercesImpl.jar:./web/applet/xml-apis.jar:./web/WEB-INF/lib/log4j-1.2.8.jar:./web/WEB-INF/classes org.greenstone.applet.phind.JPhind "http://localhost:8383/greenstone3/library?a=a&rt=d&s=PhindApplet&c=tudor" --collection tudor --classifier 1 --phindcgi "?a=a&rt=r&s=PhindApplet&o=xml&ro=1" --library "library" --backdrop "interfaces/default/images/phindbg1.jpg" --xtraParams "orientation=vertical&depth=2&resultorder=L,l,E,e,D,d&fontsize=10&blocksize=10"
44
45Basic version:
46java -cp ./web/WEB-INF/lib/gsdl3.jar:./web/WEB-INF/lib/gutil.jar:./web/applet/phind.jar:./web/applet/xercesImpl.jar:./web/applet/xml-apis.jar:./web/WEB-INF/lib/log4j-1.2.8.jar:./web/WEB-INF/classes org.greenstone.applet.phind.JPhind "http://localhost:8383/greenstone3/gs2-library" --collection tudor --classifier 1 --phindcgi "?a=a&rt=r&s=PhindApplet&o=xml&ro=1" --library "gs2-library" --backdrop "interfaces/default/images/phindbg1.jpg"
47
48To print very basic usage, run this as an application so:
49java -cp ./web/WEB-INF/lib/gsdl3.jar:./web/WEB-INF/lib/gutil.jar:./web/applet/phind.jar:./web/WEB-INF/lib/log4j-1.2.8.jar:./web/WEB-INF/classes org.greenstone.applet.phind.JPhind
50
51
52To use the applet, you'll need to embed it in a web page like this:
53
54<APPLET CODE="Phind.class" WIDTH=500 HEIGHT=500>
55
56 <PARAM NAME=collection VALUE="fao.org">
57 <PARAM NAME=classifier VALUE="1">
58 <PARAM NAME=phindcgi VALUE="http://kowhai/cgi-bin/phindcgi">
59 <PARAM NAME=library VALUE="http://kowhai/cgi-bin/library">
60 <PARAM NAME=backdrop VALUE="http://kowhai/~paynter/transfer/phindtest/green1.jpg">
61 The Phind java applet.
62</APPLET>
63
64There are a bunch of other parameters; these are described in the
65getParameters method below. It is all done for you in Greenstone
66in the document.dm macro file (the _phindapplet_ macro).
67
68You may have problems with Java applet security. Java applet's can only
69open socket connections (including the HTTP connections the applet uses
70to get data) to the same server the applet was loaded from. This means
71that your phindcgi, library, and (optional) backdrop URLs must be on the
72same machine that your web page was loaded from.
73
74**********************************************************************
75
76The applet comprises several classes:
77
781. Phind (this file) is the applet class, loaded by the browser.
79 It also handles network connections.
802. ResultDisplay is a Panel that sits in the applet and displays
81 things; displays are connected in a doubly-linked list.
823. ResultBox holds the results of a query. Result boxes are shown
83 to the user through ResultDisplays. Another doubly-linked list.
844. ResultTitle acts as a caption to a ResultBox describing its contents.
855. ResultCanvas is what the ResultBox data is drawn on. It draws the
86 words on the screen, and handles user input from the mouse.
876. ResultItem represents a single result object (a phrase or document).
887. PhindTitle is for drawing backdrops in ResultDisplays.
89
90**********************************************************************/
91
92package org.greenstone.applet.phind;
93
94import org.webswing.toolkit.api.WebswingUtil;
95
96import javax.swing.JApplet;
97import javax.swing.JComponent;
98import javax.swing.JFrame;
99import javax.swing.JLabel;
100import javax.swing.JPanel;
101import javax.swing.JTextField;
102import javax.swing.JButton;
103
104//import java.awt.Choice;
105import java.awt.Color;
106import java.awt.Dimension;
107import java.awt.Font;
108import java.awt.event.ActionEvent;
109import java.awt.event.ActionListener;
110
111import java.awt.BorderLayout;
112import java.awt.FlowLayout;
113import java.awt.GridLayout;
114import java.awt.Image;
115
116import java.net.URL;
117import java.net.MalformedURLException;
118import java.io.DataInputStream;
119
120import java.net.Socket;
121import java.net.InetAddress;
122import java.net.UnknownHostException;
123import java.io.IOException;
124
125import java.util.Vector;
126import java.util.Date;
127
128import org.w3c.dom.Element;
129import org.w3c.dom.Document;
130//import javax.xml.parsers.*;
131import org.xml.sax.InputSource;
132import org.apache.xerces.parsers.DOMParser;
133
134import java.util.HashMap;
135import java.util.Map;
136
137public class JPhind extends JApplet
138 implements ActionListener {
139
140 // if run as webswing vs either commandline application or as applet through appletviewer
141 boolean isWebswing = false;
142 // if run as applet vs application
143 boolean isRunAsApplet = true;
144
145 // set only if JPhind object is run as an application
146 URL docBaseURL = null;
147 JLabel statusBar = null;
148 Map<String,String> appParams;
149
150 // What is the collection called?
151 public String collection;
152
153 // Which phind classifier are we using? (There may be more than one.)
154 public String classifier;
155
156 // Internet address of phind resources
157 public String library_address, phindcgi_address;
158
159 // Initial search term
160 public String initialSearch;
161
162 // Number of phrases to retrieve at any one time
163 public int phraseBlockSize;
164
165 // Appearance parameters
166 public boolean vertical;
167 public int depth;
168
169 // Font
170 public int fontSize;
171 public String fontName;
172 public Font plainFont, boldFont;
173
174 // Do we want a background image in the applet?
175 public boolean showImage;
176 public String backdrop_address;
177 public Image backgroundImage;
178 public boolean showBorder;
179
180 // Colours
181 public Color panel_fg, panel_bg,
182 column_1_fg, column_1_bg,
183 column_2_fg, column_2_bg,
184 highlight_fg, highlight_bg,
185 thesaurus_fg, thesaurus_bg, thesaurus_bar_fg, thesaurus_bar_bg,
186 expansion_fg, expansion_bg, expansion_bar_fg, expansion_bar_bg,
187 document_fg, document_bg, document_bar_fg, document_bar_bg,
188 message_fg, message_bg;
189
190 // Column dimensions
191 int column_1_width, column_2_width;
192
193 // Where do we open new windows
194 String searchWindowName, documentWindowName;
195
196 // the mode of operation
197 int mode;
198 final int initMode = 0;
199 final int idleMode = 1;
200 final int searchMode = 2;
201
202 // Elements of the control panel
203 boolean showControlPanel;
204 JLabel titleLabel;
205 JTextField wordField;
206 JButton searchButton, prevButton, nextButton;
207
208 // Holders for the downloaded data
209 JPanel resultPanel;
210 JResultDisplay firstDisplay, lastDisplay;
211
212 // The time at which the last query finished
213 Date lastQueryEndTime;
214
215 // lastQueryEndTime is stored to ensure a 1 second gap between a query
216 // returning and a new one beginning. It is needed because the FAO
217 // folks in Rome have a huge lag, and frquently click several times
218 // while they wait; these clicks are turned into new queries, which
219 // they await again. It is no elegant solution, but it seems like the
220 // easiest, given that I don't know threads.
221 // 1. The search button is easy to disable, and is disabled when a
222 // socket connection is in progress.
223 // 2. ResutCanvas widgets can't be similarly disabled because the
224 // browser hides or wipes them, which looks very bad.
225 // 3. I cannot just ignore clicks on canvasses because the browser
226 // caches the clicks while the socket connection is going on, and then
227 // sends them through afterwards, when the canvas is accepting clicks
228 // again.
229 // 4. Current sequence of events is to record the time the last query
230 // ends, then whenever a click happens make sure a second has past. if
231 // you double-click the the first query is sent, returns, end-tie is
232 // set, and the second (and any others made during query time) is
233 // *immediately* processed, but since 1 second hasn't past it is
234 // ignored.
235
236
237 public JPhind() { super(); /*super.init();*/ }
238
239 public JPhind(String[] args) {
240
241 this.isRunAsApplet = false;
242 try {
243 this.docBaseURL = new URL(args[0]);
244 } catch(MalformedURLException mue) {
245 mue.printStackTrace();
246 System.err.println("*** Unable to instantiate URL from parameter: " + args[0]);
247 System.exit(-1);
248 }
249
250 appParams = new HashMap<String,String>((args.length+1)/2);
251
252 String key = null;
253 String value = null;
254 for(int i = 1; i < args.length; i++) { // after arg0, have key-value pairs (params)
255 if(i%2==1) {
256 key = args[i].substring(2); // remove -- prefix of paramname
257 //System.err.println("got key: " + key);
258 } else {
259 value = args[i];
260 appParams.put(key, value);
261 //System.err.println("got value: " + value);
262
263
264 // HttpUtils.parseQueryString() deprecated, so hacking decode xtra key-value pairs
265 // https://stackoverflow.com/questions/13592236/parse-a-uri-string-into-name-value-collection?page=1&tab=scoredesc#tab-top
266 // String.split() is preferred over Tokenizer but 2nd parameter behaves differently
267 // than I expected.
268 // https://docs.oracle.com/javase/6/docs/api/java/lang/String.html#split%28java.lang.String,%20int%29
269 if(key.equals("xtraParams")) {
270 value = value.replace("&amp;", "&"); // just in case we have html entities
271 String[] param_list = value.split("&", 0); // 0 for all occurrences
272 for(String key_val : param_list) {
273 String[] paramPair = key_val.split("=", 2); // get first 2 strings, key and val
274 //System.err.println("key_val: " + key_val);
275 if(paramPair.length == 2) {
276 String xtraParamsKey = paramPair[0];
277 String xtraParamsVal = paramPair[1];
278 //System.err.println("key - val: " + xtraParamsKey + " - " + xtraParamsVal);
279 appParams.put(xtraParamsKey, xtraParamsVal);
280 }
281 }
282 }
283
284 key = value = null;
285 }
286 }
287
288 isWebswing = appParams.getOrDefault("webswing", "0").equals("1") ? true : false;
289
290 // manually calling (J)Applet method init()
291 init();
292 }
293
294 /**
295 * Overriding (J)Applet method getParameter to first check appParams map
296 * if Phind was run run as an application.
297 * https://stackoverflow.com/questions/15905127/overridden-methods-in-javadoc
298 */
299 @Override
300 public String getParameter(String name) {
301 if(!isRunAsApplet) {
302 return appParams.get(name);
303 }
304 else {
305 return super.getParameter(name);
306 }
307 }
308
309 @Override
310 public void stop() {
311 if(!isRunAsApplet) {
312 System.exit(-1);
313 }
314 else {
315 super.stop(); // or put this applet's stop() method's contents here if there was one
316 }
317 }
318
319 public String getAppletInfo() {
320 return "Phind by Gordon Paynter ([email protected]). Copyright 1997-2000.";
321 }
322
323
324 public void init() {
325
326 mode = initMode;
327
328 // Read applet parameters
329 getParameters();
330
331 // Initialise the user interface
332 setBackground(panel_bg);
333 lastQueryEndTime = new Date();
334
335 // fonts used to output text
336 plainFont = new Font(fontName, Font.PLAIN, fontSize);
337 boldFont = new Font(fontName, Font.BOLD, fontSize);
338
339 // The phind applet layout manager
340 setLayout(new BorderLayout());
341
342 // Panel containing the displays is in the center of the display
343 resultPanel = new JPanel();
344 if (vertical) {
345 resultPanel.setLayout(new GridLayout(depth,1,0,2));
346 } else {
347 System.out.println("horizontal");
348 resultPanel.setLayout(new GridLayout(1,depth,2,0));
349 }
350 add(resultPanel, BorderLayout.CENTER);
351
352 // Manual status bar to mimic applet's default one, in case we
353 // want one if we run Collage as application And in fact, when
354 // webswing runs our applet, we never get a status bar. So we
355 // can create a custom status bar now even if we're running as
356 // an applet. When this applet is run through the
357 // appletviewer we might end up with 2 status bars.
358
359 //if(!isRunAsApplet) {
360 statusBar = new JLabel();
361 this.add(statusBar, BorderLayout.SOUTH);
362 Dimension d = statusBar.getSize();
363 d.height = fontSize + 10;
364 statusBar.setPreferredSize(d);
365 //}
366
367 // Create ResultDisplays and place into the interface
368 JResultDisplay d1, d2 = null;
369 firstDisplay = new JResultDisplay(this, null);
370 resultPanel.add(firstDisplay);
371
372 if (depth == 1) {
373 lastDisplay = firstDisplay;
374 } else {
375 d1 = firstDisplay;
376 for (int i = 2; i <= depth; i++) {
377 d2 = new JResultDisplay(this, d1);
378 resultPanel.add(d2);
379 d1 = d2;
380 }
381 lastDisplay = d2;
382 }
383
384 // The control panel
385 initialiseControlPanel();
386
387 // lets get started then
388 setStatus("Welcome to Phind.");
389 mode = idleMode;
390
391 // Perform initial search, if requested
392 if (initialSearch.length() > 0) {
393 searchForWord(initialSearch);
394 }
395
396 }
397
398
399 // Display a message in the status bar
400 void setStatus(String status) {
401 showStatus(status);
402 }
403
404 // The user performs an action in the interface
405 /* public boolean action(Event evt, Object arg) {
406
407 if (evt.getSource() == searchButton) {
408 System.out.println("evt source ==searchButton");
409 searchForWord(getSearchTerm());
410 } else if (evt.getSource() == wordField) {
411 System.out.println("evt source ==wordField");
412 searchForWord(getSearchTerm());
413 } else if (evt.getSource() == prevButton) {
414 shiftPrevious();
415 } else if (evt.getSource() == nextButton) {
416 shiftNext();
417 } else {
418 System.out.println("unknown action: " + evt.toString()
419 + ", object: " + arg.toString());
420 }
421 return true;
422 }
423
424 */
425
426 public void actionPerformed(ActionEvent evt) {
427
428 JComponent target = (JComponent)evt.getSource();
429 if (target==searchButton) {
430 System.out.println("search button pressed");
431 searchForWord(getSearchTerm());
432 } else if (target == wordField) {
433 System.out.println("word field entered");
434 searchForWord(getSearchTerm());
435 } else if (target == prevButton) {
436 System.out.println("prev button pressed");
437 shiftPrevious();
438 }else if (target == nextButton) {
439 System.out.println("prev button pressed");
440 shiftNext();
441 } else {
442 System.out.println("unknown action: " + evt.toString() );
443
444 }
445 }
446
447 // Search for a word
448 //
449 // Called on two occasions:
450 // when the "Search" Button is pressed, or
451 // to perform an "initial search"
452 void searchForWord(String searchWord) {
453
454 System.err.println("in searchforword!!");
455 if (mode == idleMode) {
456
457 setSearchTerm(searchWord);
458
459 // Convert the String from UTF8 charaters into
460 // an encoding that is okay for a URL.
461 searchWord = URLUTF8Encoder.encode(searchWord);
462
463 // Look up the word
464 if (searchWord.length() > 1) {
465 setStatus("searching for \"" + searchWord + "\"");
466 firstDisplay.emptyContents();
467 JResultBox result = lookupPhraseOnServer(null, false, searchWord, searchWord, 2);
468
469 // if there is an error, return
470 if (result == null) {
471 setStatus("No results for \"" + searchWord + "\"");
472 return;
473 }
474
475 // display the result
476 result.display = firstDisplay.display(result);
477 result.setSize(result.display.getSize());
478 result.paintAll(result.getGraphics());
479 }
480
481 enablePreviousAndNext();
482 }
483 }
484
485
486 // Search for a phrase
487 //
488 // If querymode is 2, the user has clicked on a phrase.
489 // If querymode is 3, the user has requested more phrases.
490 // If querymode is 4, the user has requested more documents.
491 void searchForPhrase(JResultBox source, String key, String phrase, int queryMode) {
492
493 // System.out.println("searchForPhrase: " + key + " " + phrase + " " + queryMode);
494
495 if (mode == idleMode) {
496
497 // If we are going to replace the first ResultDisplay, then empty it
498 if (queryMode <= 2) {
499 if (source.display.next != null) source.display.next.emptyContents();
500 }
501
502 // look up the word
503 setStatus("Searching for \"" + phrase + "\"");
504 JResultBox result = lookupPhraseOnServer(source, true, key, phrase, queryMode);
505 if (result == null) {
506 setStatus("No result for \"" + phrase + "\"");
507 return;
508 }
509
510 // If this is not already displayed, display it in the last free spot
511 if (queryMode <= 2) {
512 result.display = lastDisplay.display(result);
513 result.setSize(result.display.getSize());
514 result.paintAll(result.getGraphics());
515 }
516
517 enablePreviousAndNext();
518 }
519 }
520
521
522 // Look up a phrase (or symbol) on the server
523 //
524 // Arguments are the source of the query (a ResultBox, or null if the
525 // query comes from hitting the search button), the key to search for
526 // (the text of a phrase or a symbol number), the phrase as a string,
527 // and the query mode.
528 // Query modes are:
529 // 0 = obsolete
530 // 1 = obsolete
531 // 2 = get first N phrases and URLs,
532 // 3 = get another N phrases into same window
533 // 4 = get another N documents into same window
534 // 5 = get another N thesaurus links into same window
535
536 JResultBox lookupPhraseOnServer(JResultBox source,
537 boolean keyKnown, String key, String phrase,
538 int queryMode) {
539 disableSearchButton();
540 mode = searchMode;
541 JResultBox r = null;
542
543 if (queryMode <= 2) {
544 r = new JResultBox(this, collection, key, phrase, source);
545 } else if ((queryMode == 3) || (queryMode == 4) || (queryMode == 5)) {
546 r = source;
547 }
548
549 try {
550 queryServer(keyKnown, key, queryMode, r);
551 } catch (Exception e) {
552 System.out.println("Phind query error: " + e.toString());
553 setStatus("Query error: " + e.toString());
554 mode = idleMode;
555 enableSearchButton();
556 return null;
557 }
558
559 // The query is finished
560 setStatus(r.c.numberOfItems + " results for \"" + phrase + "\"");
561 mode = idleMode;
562 enableSearchButton();
563 lastQueryEndTime = new Date();
564
565 return r;
566 }
567
568
569 // Query the phindcgi program
570 //
571 // Send a query (a word or symbol number) to the server
572 // and pass the response to a ResultBox.
573
574 void queryServer(boolean keyKnown, String word, int queryMode, JResultBox area)
575 throws IOException {
576
577 // Build the query
578 String query = phindcgi_address + "c=" + collection + "&pc=" + classifier;
579
580 if (keyKnown) {
581 query = query + "&ppnum=" + word;
582 } else {
583 query = query + "&ppnum=0" + "&pptext=" + word;
584 }
585
586
587 // Specify the set of results to return
588 int first_e = 0;
589 int last_e = 0;
590 int first_d = 0;
591 int last_d = 0;
592 int first_l = 0;
593 int last_l = 0;
594
595 // the initial query
596 if (queryMode <= 2) {
597 last_e = phraseBlockSize;
598 last_d = phraseBlockSize;
599 last_l = phraseBlockSize;
600 }
601
602 // add phrases to an existing result set
603 else if (queryMode == 3) {
604 first_e = area.nextPhraseBlock * phraseBlockSize;
605 area.nextPhraseBlock++;
606 last_e = area.nextPhraseBlock * phraseBlockSize;
607 }
608
609 // add documents to existing result set
610 else if (queryMode == 4) {
611 first_d = area.nextDocumentBlock * phraseBlockSize;
612 area.nextDocumentBlock++;
613 last_d = area.nextDocumentBlock * phraseBlockSize;
614 }
615
616 // add thesaurus links to existing result set
617 else if (queryMode == 5) {
618 first_l = area.nextThesaurusLinkBlock * phraseBlockSize;
619 area.nextThesaurusLinkBlock++;
620 last_l = area.nextThesaurusLinkBlock * phraseBlockSize;
621 }
622
623 query = query + "&pfe=" + first_e + "&ple=" + last_e
624 + "&pfd=" + first_d + "&pld=" + last_d
625 + "&pfl=" + first_l + "&pll=" + last_l;
626
627 // Send the query to the phindcgi program
628 System.out.println("1:sending query: " + query);
629 try {
630 URL phindcgi = new URL(query);
631 DataInputStream in = new DataInputStream(phindcgi.openStream());
632 DOMParser parser = new DOMParser();
633 parser.parse(new InputSource(in));
634 Document data_doc = parser.getDocument();
635 Element data_elem = data_doc.getDocumentElement();
636 area.parseXML(data_elem);
637 in.close();
638 } catch (Exception e) {
639 System.err.println( "Error sending query to phindcgi: " + e);
640 e.printStackTrace();
641 }
642 area.repaint();
643 }
644
645 @Override
646 public void showStatus(String msg) {
647 // Either firefox doesn't provide a status bar window for applets any more or webswing
648 // doesn't provide a status window, so we don't see Applet.showStatus() output appear
649 // in webswing-phind and in fact don't even see any Applet statusBar in webswing.
650 // However, since we print useful and interesting information to the status bar,
651 // we'll now always show and print to our manually added statusBar now
652 // not only if(!isRunAsApplet) when we needed to manually create a statusBar.
653 this.statusBar.setText(msg);
654 if(isRunAsApplet) {
655 super.showStatus(msg);
656 }
657 }
658
659 @Override
660 public URL getDocumentBase() {
661 if(!isRunAsApplet) { // launched as application
662 //System.err.println("*** docBaseURL: " + docBaseURL);
663 return this.docBaseURL;
664 } else {
665 return super.getDocumentBase();
666 }
667 }
668
669 // Tidy up URLs
670 //
671 // Ensure a URL address (as string) has a protocol, host, and file.
672 //
673 // If the URL is a CGI script URL, it should be tidied up so that it is
674 // appropriate to tage attrib=value pairs on the end. This means it
675 // must either end with a "?" or (if it contains a question-mark
676 // internally) end with a "&".
677 String tidy_URL(String address, boolean isCGI) {
678
679 System.err.println("tidy URL: " + address);
680
681 // make sure the URL has protocol, host, and file
682 if (address.startsWith("http") || address.startsWith("https")) {
683 // the address has all the necessary components
684 } else if (address.startsWith("/")) {
685 // there is not protocol and host
686 URL document = getDocumentBase();
687 //if(document == null) {
688 // document = getDocumentBase(address);
689 //}
690 String port = "";
691 if (document.getPort()!=-1) {
692 port = ":" + document.getPort();
693 }
694 address = "http://" + document.getHost() + port + address;
695 } else {
696 // this URL is relative to the directory the document is in
697 URL document = getDocumentBase();
698 //if(document == null) {
699 // document = getDocumentBase(address);
700 //}
701 String directory = document.getFile();
702 int end = directory.lastIndexOf('/');
703 String port = "";
704 if (document.getPort()!=-1) {
705 port = ":" + document.getPort();
706 }
707 directory = directory.substring(0,end + 1);
708 address = "http://" + document.getHost() + port + directory + address;
709
710 }
711
712 // if the URL is a cgi script, make sure it has a "?" in ti,
713 // and that it ends with a "?" or "&"
714 if (isCGI) {
715 if (address.indexOf((int) '?') == -1) {
716 address = address + "?";
717 } else if (!address.endsWith("?")) {
718 address = address + "&";
719 }
720 }
721
722 return address;
723 }
724
725
726
727 // Open an arbitrary web page
728 void displayWebPage(String address, String window) {
729 try {
730 URL url= new URL(address);
731 if(isRunAsApplet) {
732 if (window.length() > 0) {
733 getAppletContext().showDocument(url, window);
734 } else {
735 getAppletContext().showDocument(url);
736 }
737 } else if(isWebswing) { // webswing and not applet but application
738 if (window.length() > 0) {
739 WebswingUtil.getWebswingApi().sendActionEvent("openURL",
740 url.toString() + " - " +window,
741 null);
742 } else {
743 WebswingUtil.getWebswingApi().sendActionEvent("openURL", url.toString(), null);
744 }
745 } else { // else application, but not webswing, TODO: open browser at the URL
746 System.err.println("Phind.displayWebPage() for non-applet and non-webswing application is not yet implemented.");
747 }
748 } catch (Exception e) {
749 System.out.println("Cannot open web page: " + e.toString());
750 }
751 }
752
753
754 // Get the applet parameters
755 void getParameters() {
756 String webswing = parameterValue("webswing", "0");
757 isWebswing = webswing.equals("1") ? true : false;
758
759 // What is this collection called?
760 collection = parameterValue("collection");
761
762 String docBase = getDocumentBase().toString();
763 int index = -1;
764 // Fallback for webswing, as customising applet param "collection" didn't work
765 // and the ideally-customisable collection and library applet params remain null
766 // This assumes webswing running the applet has a docBase URL of the form:
767 //docBase = "http://localhost:8383/greenstone3/library/collection/tudor/page/phind";
768 if(collection == null) {
769 //System.err.println("*** docBase: " + docBase);
770 index = docBase.indexOf("collection/");
771 if(index != -1) {
772 index += "collection/".length();
773 int endIndex = docBase.indexOf("/", index);
774 collection = docBase.substring(index, endIndex);
775 //System.err.println("*** collection: " + docBase.substring(index, endIndex) );
776 }
777 }
778
779 System.out.println("Phind collection: " + collection);
780
781 // Which of the collection's classifiers are we using?
782 classifier = parameterValue("classifier", "1");
783 System.out.println("Phind classifier: " + classifier);
784
785 // Where is the Greenstone library
786 library_address = parameterValue("library");
787
788 // Fallback for webswing, as customising applet param "library" didn't work
789 if(library_address == null) {
790 index = docBase.indexOf("/collection");
791 if(index != -1) {
792 String docBasePrefix = docBase.substring(0, index);
793 index = docBasePrefix.lastIndexOf("/");
794 if(index != -1) {
795
796 library_address = docBasePrefix.substring(index+1);
797 //System.err.println("*** library: " + docBasePrefix.substring(index+1) );
798 }
799 }
800 }
801 library_address = tidy_URL(library_address, true);
802 System.out.println("Phind library: " + library_address);
803
804 // Where is the phind CGI script
805 // we assume this is relative to the greenstone library
806 phindcgi_address = parameterValue("library")+parameterValue("phindcgi");
807 phindcgi_address = phindcgi_address.replace("&amp;", "&");
808 phindcgi_address = tidy_URL(phindcgi_address, true);
809 System.out.println("Phind phindcgi: " + phindcgi_address);
810
811
812 // Is there a default search term?
813 initialSearch = parameterValue("initial_search", "");
814
815 // Should we display the control panel
816 showControlPanel = true;
817 if (parameterValue("control_panel", "show").toLowerCase().equals("hide")) {
818 showControlPanel = false;
819 }
820
821 // Should we show a background image?
822 backdrop_address = parameterValue("backdrop", "");
823 if (backdrop_address.length() > 0) {
824 backdrop_address = tidy_URL(backdrop_address, false);
825 System.out.println("Phind backdrop URL: " + backdrop_address);
826
827 try {
828 URL backdrop_url = new URL(backdrop_address);
829 backgroundImage = getImage(backdrop_url);
830 showImage = true;
831 } catch (Exception e) {
832 System.out.println("Phind could not load " + backdrop_address);
833 showImage = false;
834 }
835 }
836
837 // Should we draw a border?
838 showBorder = parameterValue("border", "on").equals("off");
839
840 // Are the windows arranged vertically or horizontally
841 if (parameterValue("orientation", "vertical").toLowerCase().startsWith("hori")) {
842 vertical = false;
843 } else {
844 vertical = true;
845 }
846
847 // How many phind windows are there?
848 depth = parameterValue("depth", 3);
849
850 // Result sort order
851 // Standard is "LlEeDd", expansion-first is "EeLlDd"
852 String order = parameterValue("resultorder", "standard");
853 if (!order.equals("standard")) {
854 int next = 20;
855 ResultItem.sortMessage = next;
856 for (int x = 0; x < order.length(); x++) {
857 if (order.charAt(x) == ',') {
858 next--;
859 } else if (order.charAt(x) == 'L') {
860 ResultItem.sortLinkItem = next;
861 } else if (order.charAt(x) == 'l') {
862 ResultItem.sortMoreLinks = next;
863 } else if (order.charAt(x) == 'E') {
864 ResultItem.sortPhraseItem = next;
865 } else if (order.charAt(x) == 'e') {
866 ResultItem.sortMorePhrases = next;
867 } else if (order.charAt(x) == 'D') {
868 ResultItem.sortDocumentItem = next;
869 } else if (order.charAt(x) == 'd') {
870 ResultItem.sortMoreDocuments = next;
871 }
872 }
873 System.out.println("link: " + ResultItem.sortLinkItem);
874 System.out.println("exps: " + ResultItem.sortPhraseItem);
875 System.out.println("docs: " + ResultItem.sortDocumentItem);
876
877 }
878
879 // How many phrases should we fetch at any given time?
880 phraseBlockSize = parameterValue("blocksize", 10);
881
882 // What font should we use?
883 fontSize = parameterValue("fontsize", 10);
884 fontName = parameterValue("fontname", "Helvetica");
885
886 // Column dimensions
887 column_1_width = parameterValue("first_column_width", 6);
888 column_2_width = parameterValue("second_column_width", column_1_width);
889
890 // Where do we open new windows
891 searchWindowName = parameterValue("search_window", "phindsearch");
892 documentWindowName = parameterValue("document_window", "phinddoc");
893
894 // Colours
895 panel_fg = parameterValue("panel_fg", Color.black);
896 panel_bg = parameterValue("panel_bg", Color.white);
897
898 highlight_bg = parameterValue("highlight_bg", Color.yellow);
899
900 expansion_fg = parameterValue("expansion_fg", Color.black);
901 thesaurus_fg = parameterValue("thesaurus_fg", new Color(0, 100, 0));
902 document_fg = parameterValue("document_fg", Color.blue);
903
904 thesaurus_bar_fg = parameterValue("thesaurus_bar_fg", Color.black);
905 expansion_bar_fg = parameterValue("expansion_bar_fg", Color.black);
906 document_bar_fg = parameterValue("document_bar_fg", Color.black);
907
908 thesaurus_bar_bg = parameterValue("thesaurus_bar_bg", new Color(160, 160, 190));
909 expansion_bar_bg = parameterValue("expansion_bar_bg", new Color(255, 200, 200));
910 document_bar_bg = parameterValue("document_bar_bg", new Color(150, 193, 156));
911
912 column_1_fg = parameterValue("first_column_fg", Color.black);
913 column_1_bg = parameterValue("first_column_bg", new Color(235, 245, 235));
914 column_2_fg = parameterValue("second_column_fg", Color.black);
915 column_2_bg = parameterValue("second_column_bg", new Color(200, 220, 200));
916
917 message_fg = parameterValue("message_fg", Color.black);
918 message_bg = parameterValue("message_bg", Color.white);
919
920 // Colours I don't use, yet
921 // thesaurus_bg = parameterValue("thesaurus_bg", Color.white);
922 // expansion_bg = parameterValue("expansion_bg", Color.white);
923 // document_bg = parameterValue("document_bg", Color.white);
924 }
925
926 // Get the value of a parameter given its name.
927 // There are many types of parameters, hence the variety of functions.
928
929 // Get a REQUIRED string. Stop the applet if we cannot.
930 String parameterValue(String name) {
931 try {
932 return getParameter(name);
933 } catch (Exception e) {
934 System.err.println("Phind: you must give a parameter for \""
935 + name + "\". Stopping.");
936 stop();
937 }
938 return "";
939 }
940
941 // Get an optional parameter. Return a default if we cannot.
942 String parameterValue(String name, String defaultValue) {
943 String text = getParameter(name);
944 if (text == null) {
945 return defaultValue;
946 }
947 System.out.println("Phind " + name + ": " + text);
948 return text;
949 }
950
951 int parameterValue(String name, int defaultValue) {
952 int value;
953 try {
954 value = Integer.parseInt(getParameter(name));
955 } catch (Exception e) {
956 return defaultValue;
957 }
958 System.out.println("Phind " + name + ": " + value);
959 return value;
960 }
961
962 Color parameterValue(String name, Color defaultValue) {
963
964 String text = getParameter(name);
965 if (text == null) {
966 return defaultValue;
967 }
968 text = text.toLowerCase();
969
970 // a number of the form "#ddffee"
971 if (text.startsWith("#") && (text.length() == 7)) {
972 text = text.substring(1);
973 int r, g, b;
974 try {
975 r = Integer.parseInt(text.substring(0,2), 16);
976 g = Integer.parseInt(text.substring(2,4), 16);
977 b = Integer.parseInt(text.substring(4), 16);
978 return new Color(r, g, b);
979 } catch (Exception e) {
980 return defaultValue;
981 }
982 }
983
984 // a known Java colour string
985 else if (text.equals("black")) { return Color.black; }
986 else if (text.equals("blue")) { return Color.blue; }
987 else if (text.equals("cyan")) { return Color.cyan; }
988 else if (text.equals("darkgray")) { return Color.darkGray; }
989 else if (text.equals("gray")) { return Color.gray; }
990 else if (text.equals("green")) { return Color.green; }
991 else if (text.equals("lightgray")) { return Color.lightGray; }
992 else if (text.equals("magenta")) { return Color.magenta; }
993 else if (text.equals("orange")) { return Color.orange; }
994 else if (text.equals("pink")) { return Color.pink; }
995 else if (text.equals("red")) { return Color.red; }
996 else if (text.equals("white")) { return Color.white; }
997 else if (text.equals("yellow")) { return Color.yellow; }
998
999 return defaultValue;
1000 }
1001
1002
1003 // Control panel operations
1004
1005 // Initialise the control panel
1006 void initialiseControlPanel() {
1007
1008 if (showControlPanel) {
1009 JPanel p1 = new JPanel();
1010 this.add(p1, BorderLayout.NORTH);
1011 p1.setLayout(new FlowLayout(FlowLayout.CENTER, 0, 0));
1012
1013 searchButton = new JButton("Search");
1014 searchButton.setFont(boldFont);
1015 //searchButton.setEnabled(true);
1016 searchButton.addActionListener(this);
1017 p1.add(searchButton);
1018
1019 JLabel tempLabel = new JLabel(" for");
1020 tempLabel.setFont(boldFont);
1021 p1.add(tempLabel);
1022
1023 wordField = new JTextField(12);
1024 wordField.setFont(boldFont);
1025 wordField.addActionListener(this);
1026 p1.add(wordField);
1027
1028 JLabel temp2 = new JLabel(" ");
1029 p1.add(temp2);
1030
1031 prevButton = new JButton("Previous");
1032 prevButton.setFont(boldFont);
1033 prevButton.addActionListener(this);
1034 prevButton.setEnabled(false);
1035
1036 p1.add(prevButton);
1037
1038 nextButton = new JButton(" Next ");
1039 nextButton.setFont(boldFont);
1040 nextButton.addActionListener(this);
1041 nextButton.setEnabled(false);
1042 p1.add(nextButton);
1043
1044 }
1045 }
1046
1047 // Button and field functionality
1048
1049 // Enable and disable the word field
1050 void enableSearchButton() {
1051 if (showControlPanel) {
1052 searchButton.setEnabled(true);
1053 }
1054 }
1055 void disableSearchButton() {
1056 if (showControlPanel) {
1057 searchButton.setEnabled(false);
1058 }
1059 }
1060
1061 // Get and set the search text in the wordField
1062 String getSearchTerm() {
1063 if (showControlPanel) {
1064 return wordField.getText();
1065 } else {
1066 return initialSearch;
1067 }
1068 }
1069 void setSearchTerm(String word) {
1070 if (showControlPanel) {
1071 wordField.setText(word);
1072 }
1073 }
1074
1075 // Enable or disable the "Previous" and "Next" buttons
1076 void enablePreviousAndNext() {
1077 if (showControlPanel) {
1078 JComponent c = firstDisplay.current;
1079 if (c.getClass().getName().endsWith("JResultBox")) {
1080 if (((JResultBox) c).prevBoxExists()) {
1081 prevButton.setEnabled(true);
1082 } else {
1083 prevButton.setEnabled(false);
1084 }
1085 }
1086
1087 c = lastDisplay.current;
1088 if (c.getClass().getName().endsWith("JResultBox")) {
1089 if (((JResultBox) c).nextBoxExists()) {
1090 nextButton.setEnabled(true);
1091 } else {
1092 nextButton.setEnabled(false);
1093 }
1094 }
1095 }
1096 }
1097
1098 // Shift to previous box
1099 //
1100 // If the user clicks "Previous" then scroll up.
1101 void shiftPrevious() {
1102
1103 JComponent c = firstDisplay.current;
1104 if (c.getClass().getName().endsWith("JResultBox")) {
1105
1106 JResultBox b = (JResultBox) c;
1107 if (b.prevBoxExists()) {
1108 b = b.prev;
1109
1110 // empty all the displays
1111 firstDisplay.emptyContents();
1112
1113 // add as many result boxes as there are displays
1114 for (int i = 1 ; ((i <= depth) && (b != null)); i++) {
1115 lastDisplay.display(b);
1116 b.setSize(b.display.getSize());
1117 b.paintAll(b.getGraphics());
1118 b = b.next;
1119 }
1120 }
1121 }
1122 enablePreviousAndNext();
1123 }
1124
1125 // Shift to next box
1126 //
1127 // If the user clicks "Next" then scroll down if possible
1128 void shiftNext() {
1129
1130 JComponent c = lastDisplay.current;
1131 if (c.getClass().getName().endsWith("JResultBox")) {
1132
1133 JResultBox b = (JResultBox) c;
1134 if (b.nextBoxExists()) {
1135
1136 // find the new "first" displayed box
1137 c = firstDisplay.current;
1138 b = (JResultBox) c;
1139 b = b.next;
1140
1141 // empty all the displays
1142 firstDisplay.emptyContents();
1143
1144 // add as many result boxes as there are displays
1145 for (int i = 1 ; ((i <= depth) && (b != null)); i++) {
1146 lastDisplay.display(b);
1147 b.setSize(b.display.getSize());
1148 b.paintAll(b.getGraphics());
1149 b = b.next;
1150 }
1151 }
1152 }
1153 enablePreviousAndNext();
1154 }
1155
1156 // adding a main method to allow this applet to function as an application also
1157 public static void main(String args[]) {
1158 if(args.length < 9) {
1159 System.err.println("Need minimum --params: <baseURL> --collection <collection> --classifier <classifier> --phindcgi <URL> --library <libURL> [--webswing <1/0>] [--backdrop <ImgURL>] [--xtraParams <key1=value1&key2=value2&...]");
1160 }
1161 else { // collection fao.org, classifier 1, phindcgi url, library url, backdrop imgurl
1162 JPhind phind = new JPhind(args);
1163 JFrame frame = new JFrame("Phind Applet as Application");
1164 frame.getContentPane().add(phind, BorderLayout.CENTER);
1165 frame.setSize(500,500);
1166 // https://stackoverflow.com/questions/19433358/difference-between-dispose-and-exit-on-close-in-java
1167 // default: https://docs.oracle.com/javase/8/docs/api/javax/swing/JFrame.html#EXIT_ON_CLOSE
1168 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // don't do EXIT_ON_CLOSE in Applets!
1169 frame.setVisible(true);
1170
1171 }
1172 }
1173
1174
1175}
Note: See TracBrowser for help on using the repository browser.