source: main/trunk/greenstone3/src/java/org/greenstone/applet/GsdlCollageApplet/GsdlCollageApplet.java@ 38939

Last change on this file since 38939 was 38939, checked in by anupama, 2 months ago

Comments on how to run the appletviewer, before I forget that.

File size: 39.3 KB
Line 
1package org.greenstone.applet.GsdlCollageApplet;
2
3import org.webswing.toolkit.api.WebswingUtil;
4import org.webswing.toolkit.api.action.WebActionEvent;
5import org.webswing.toolkit.api.action.WebActionListener;
6
7import org.webswing.toolkit.api.lifecycle.WebswingShutdownListener;
8import org.webswing.toolkit.api.lifecycle.OnBeforeShutdownEvent;
9
10import org.webswing.toolkit.api.url.*;
11
12//import java.applet.Applet;
13import javax.swing.JApplet;
14import java.awt.event.ComponentListener;
15import java.awt.event.ComponentEvent;
16import java.awt.event.MouseAdapter;
17import java.awt.event.MouseEvent;
18import java.awt.event.MouseListener;
19import java.awt.event.WindowAdapter;
20import java.awt.event.WindowEvent;
21import java.awt.*;
22import java.net.*;
23
24
25import javax.swing.JFrame;
26import javax.swing.JLabel;
27import java.util.Map;
28import java.util.HashMap;
29
30
31/**
32 *
33 * @author Katrina Edgar
34 * @author David Bainbridge
35 * adjusted for GS3 in 2024 (Anu)
36 *
37 * Main class for the GsdlCollageApplet<br>
38 * Processes the parameters passed through the Applet<br>
39 * Builds an appropriate starting url from these parameters<br>
40 * Creates a storage class for downloaded images<br>
41 * Starts thread to download images and their associated url<br>
42 * Starts thread to display downloaded images on the applet screen<br>
43 */
44
45public class GsdlCollageApplet extends JApplet implements ComponentListener
46{
47 static final int EXTRA_HEIGHT = 30; // for status bar or any extra elements
48 private int X_DIM = 600;
49 private int Y_DIM = 300;
50
51 boolean isWebswing = false; // if run as webswing
52 // To run this GsdlCollageApplet as Application instead of as Applet
53 boolean isRunAsApplet = true;
54 // set only if JPhind object is run as an application
55 URL docBaseURL = null;
56 JLabel statusBar = null;
57 Map<String,String> appParams;
58
59 // package access, used mainly for GS3
60 int gsdlversion = 2;
61 String baseURL = null;
62
63 /** When asked to stop running, this variable will be set to true */
64 private boolean stop_running = false;
65
66 /** Amount of error checking output produced <br>
67 * Ranges from 0 - no output to 3 - maximum output */
68 protected int verbosity_ = 0;
69 /** Indicates whether java2 functionality should be used <br>
70 * If true will allow advanced image processing techniques to occur,
71 * such as fading and colouring using pixel manipulation <br>
72 * If false, images will maintain an alpha value of 1 (appear solid),
73 * newer images will simply be pasted on top of existing images <br> */
74 protected boolean isJava2_ = true;
75 /** Number of nested links to follow When used with greenstone,
76 * controls to which level of the document links will be followed For
77 * example a maximum depth of 1 will mean only the top page of a
78 * document will be examined, while a depth of 2 will include sections
79 * within this document, 3 includes sections within sections, and so
80 * on */
81 protected int maxDepth_ = 3;
82
83 /** Maximum number of images to display on the canvas */
84 protected int maxDisplay_ = 25;
85
86 /** Maximum number of downloaded images to store <br> Prevents applet
87 * from using excessive amounts of bandwidth by downloading too many
88 * images simulataneously*/
89 protected int maxDownloads_ = Integer.MAX_VALUE;
90
91 /** Types of images permitted in the collage, for example gif, jpg,
92 * png... */
93 protected String imageType_ = ".jpg%.png";
94
95 /** Caption of the collage */
96 protected String caption_ = "";
97
98 /** Background color of applet screen */
99 protected Color bgcolor_ = new Color(150, 193, 155);
100
101 /** Time lapse between repainting of the applet */
102 protected int refreshDelay_ = 2000;
103
104 /** Stores an image and url pair and provides associated methods */
105 protected DownloadImages download_images_ = null;
106 /** Downloads images from a starting url, recursively follows nested
107 * links */
108 protected DownloadUrls download_thread_ = null;
109 /** Image processing and placement on applet screen */
110 protected DisplayImages display_thread_ = null;
111
112
113 /** Gets the set, calculated or default width (x dimension) of the applet/application */
114 public int X_DIM() {
115 return X_DIM;
116 }
117 /** Gets the set, calculated or default height (y dimension) of the applet/application */
118 public int Y_DIM() {
119 return Y_DIM;
120 }
121
122 /** Gets verbosity */
123 public int verbosity() { return verbosity_; }
124 /** Gets maximum depth for nested links */
125 public int maxDepth() { return maxDepth_; }
126
127 /** Gets maximum number of images to display */
128 public int maxDisplay() { return maxDisplay_;}
129
130 /** Gets maximum number of images to store during downloading */
131 public int maxDownloads() { return maxDownloads_; }
132 /** Gets the refresh delay used between repaint */
133 public int refreshDelay() { return refreshDelay_; }
134
135 /** Gets parameters from the applet code and stores locally.
136 * Forms a starting url for image retrieval to begin from.
137 * The starting url is formed either by:
138 * * Using the image_url parameter as provided, which is assumed to
139 * be a complete url
140 * * If this parameter does not exist, the assumption is that the
141 * collage is being incorporated with the Greenstone Digital Library
142 * Software. The starting url is formed by concatenating
143 * the gwcgi, collection and classifier parameters as provided.
144 * Then starts downloading and displaying images */
145
146 Thread paint ;
147
148 public GsdlCollageApplet() {
149
150 super();
151
152 // status bar if run as application or as webswing applet,
153 // mimicing running as regular applet which gives us a status bar by default
154 // Useful for Phind, not for Collage where there are layout issues even if hidden
155 //setLayout(new BorderLayout());
156 //if(!isRunAsApplet) {
157 statusBar = null; //new JLabel();
158
159 //this.add(statusBar, BorderLayout.SOUTH);
160
161 ///Window win = SwingUtilities.getWindowAncestor(this);
162 ///if(win instanceof JFrame) {
163 /// JFrame topFrame = (JFrame)win;
164 /// topFrame.add(statusBar, BorderLayout.SOUTH);
165 ///} else { //AppletViewer or browser
166 /// this.add(statusBar, BorderLayout.SOUTH);
167 ///}
168
169 //Dimension d = statusBar.getSize();
170 //d.height = EXTRA_HEIGHT;
171 //statusBar.setPreferredSize(d);
172 //}
173
174 this.addMouseListener(new CollageMouseAdapter());
175 }
176
177 /**
178 * This method is called (by main()) only when this class is run
179 * as an application, not as applet. It prepares the variables and
180 * commandline parameters to be set up, so the rest of the
181 * initialisation can be done in the usual way (with the
182 * Applet.getParameters() method) in the init() method called
183 * hereafter for both application and applet mode.
184 */
185 public void applicationPreInit(String[] args) {
186 // I copied the contents of this contructor for my code for the JPhind.java constructor
187 // This constructor will be used when this JApplet is run as an application
188 // instead of as applet
189 this.isRunAsApplet = false;
190
191 appParams = new HashMap<String,String>((args.length+1)/2);
192 // For GS2, gwcgi = param gwcgi
193 // For GS3, gwcgi = param gwcgi + param library
194 // docBaseURL = starting_url/image_url = param gwcgi
195
196 String key = null;
197 String value = null;
198 for(int i = 0; i < args.length; i++) {
199 if(i%2==0) {
200 key = args[i].substring(2); // remove -- prefix of paramname
201 //System.err.println("got key: " + key);
202 } else {
203 value = args[i];
204 appParams.put(key, value);
205 //System.err.println("got value: " + value);
206
207
208 // HttpUtils.parseQueryString() deprecated, so hacking decode xtra key-value pairs
209 // https://stackoverflow.com/questions/13592236/parse-a-uri-string-into-name-value-collection?page=1&tab=scoredesc#tab-top
210
211 if(key.equals("xtraParams")) {
212 value = value.replace("&amp;", "&"); // just in case we have html entities
213 parseXtraParams(value, "=", "&", appParams);
214 }
215
216 key = value = null;
217 }
218 }
219
220 // Need these parameters set before init(), so that the display works
221 int w = this.getWidth();
222 if(appParams.get("width") != null) {
223 w = Integer.parseInt(appParams.get("width"));
224 }
225 int h = this.getHeight();
226 if(appParams.get("height") != null) {
227 h = Integer.parseInt(appParams.get("height"));
228 }
229 // For applet, w and h may now be set to a positive value. But if GsdlCollageApplet
230 // is run as an application, it won't yet be visible and not have a non-zero dimension.
231 // So use defaults here:
232 this.X_DIM = (w <= 0) ? X_DIM : w;
233 this.Y_DIM = (h <= 0) ? Y_DIM : h;
234
235 // Attempting hack to circumvent division by zero error in MyAffineTransform line 39
236 //DisplayImages.app_x_dim_ = X_DIM;
237 //DisplayImages.app_y_dim_ = Y_DIM;
238
239 try {
240 this.docBaseURL = new URL(appParams.get("baseurl"));
241 } catch(MalformedURLException mue) {
242 mue.printStackTrace();
243 System.err.println("*** Unable to instantiate URL from parameter baseurl: " + appParams.get("baseurl"));
244 System.exit(-1);
245 }
246
247 }
248
249 /**
250 * Overriding (J)Applet method getParameter to first check appParams map
251 * if Phind was run run as an application.
252 * https://stackoverflow.com/questions/15905127/overridden-methods-in-javadoc
253 */
254 @Override
255 public String getParameter(String name) {
256 if(!isRunAsApplet) {
257 return appParams.get(name);
258 }
259 else {
260 if(appParams != null) {
261 String value = appParams.get(name);
262 if(value != null) {
263 return value;
264 }
265 }
266 return super.getParameter(name);
267 }
268 }
269
270 @Override
271 public URL getDocumentBase() {
272 if(!isRunAsApplet) { // launched as application
273 //System.err.println("*** docBaseURL: " + docBaseURL);
274 return this.docBaseURL;
275 } else {
276 return super.getDocumentBase();
277 }
278 }
279
280 @Override
281 public void showStatus(String msg) {
282 // Either firefox doesn't provide a status bar window for applets any more or webswing
283 // doesn't provide a status window, so we don't see Applet.showStatus() output appear
284 // in webswing-phind and in fact don't even see any Applet statusBar in webswing.
285 // However, since we print useful and interesting information to the status bar,
286 // we'll now always show and print to our manually added statusBar now
287 // not only if(!isRunAsApplet) when we needed to manually create a statusBar.
288 if(this.statusBar != null) {
289 this.statusBar.setText(msg);
290 }
291 if(isRunAsApplet) {
292 super.showStatus(msg);
293 }
294 }
295
296 // Given a string xtraParams of key-value pairs separatod by pairSeparators,
297 // parses out each (key, value) and puts them into the given map
298 Map parseXtraParams(String xtraParams, String kvSeparator, String kvPairSeparator, Map map) {
299
300 // String.split() is preferred over Tokenizer but 2nd parameter behaves differently
301 // than I expected.
302 // https://docs.oracle.com/javase/6/docs/api/java/lang/String.html#split%28java.lang.String,%20int%29
303 String[] param_list = xtraParams.split(kvPairSeparator, 0);// 0 means for all occurrences
304
305 if(map == null) {
306 map = new HashMap<String,String>(param_list.length);
307 }
308
309 for(String key_val : param_list) {
310 String[] paramPair = key_val.split(kvSeparator, 2); // get first 2 strings, key and val
311 //System.err.println("key_val: " + key_val);
312 if(paramPair.length == 2) {
313 String xtraParamsKey = paramPair[0];
314 String xtraParamsVal = paramPair[1];
315 // Let's remove any bookending quotes from value, this is necessary for some
316 // values when run as a webswing applet
317 if(xtraParamsVal.startsWith("\"") && xtraParamsVal.endsWith("\"")) {
318 xtraParamsVal = xtraParamsVal.substring(1, xtraParamsVal.length()-1);
319 }
320
321 if (verbosity_ >= 4) {
322 System.err.println("*** xtraParams key - val: " + xtraParamsKey + " - " + xtraParamsVal);
323 }
324 map.put(xtraParamsKey, xtraParamsVal);
325 }
326 }
327
328 return map;
329 }
330
331 public void init()
332 {
333 if (verbosity_ >= 4) {
334 System.err.println("Attempting to retrieve parameters...");
335 }
336
337 // This section is to help *webswing* *applet* use dynamic params in the config file.
338 // Extract anything that's in xtraParams (key=value&k2=v2&... string) into
339 // appParams, so all is ready to call getParameter and get expected params out by name
340 // thereafter.
341 // Basically, this section allows us to support any dynamic parameters getting added
342 // by Javascript to the existing applet parameters defined in webswing.config.in
343 // Here we unpack the xtraParams sent and set them up as regular applet parameters, so
344 // that the rest of the applet init() function can read them in as regular applet params.
345 String xtraParams = getParameter("xtraParams");
346 if(xtraParams != null) {
347 // will optionally create appParams and add the key,value pairs in xtraParams into it
348 // after splitting key::value;;k2::v2 etc
349 appParams = parseXtraParams(xtraParams, "::", ";;", appParams);
350 }
351
352
353 // gets the parameters specified
354 String showStatusBar = getParameter("statusbar");
355 if(statusBar == null) {
356 System.err.println("### Status bar code turned off. Ignoring statusbar settings.");
357 } else {
358 if(showStatusBar == null || showStatusBar.equals("0")) {
359 System.err.println("### Hiding status bar (default)");
360 statusBar.setVisible(false);
361 }
362 }
363 String verbosity_param = getParameter("verbosity");
364 String is_java2_param = getParameter("isJava2");
365 String max_depth_param = getParameter("maxDepth");
366 String max_downloads_param = getParameter("maxDownloads");
367 String max_display_param = getParameter("maxDisplay");
368 String refresh_delay_param = getParameter("refreshDelay");
369 String image_type_param = getParameter("imageType");
370 String bgcolor_param = getParameter("bgcolor");
371 String caption_param = getParameter("caption");
372 String document_root = getParameter("documentroot");
373 String gwcgi = getParameter("gwcgi"); // for GS2
374 String baseurl = getParameter("baseurl"); // for GS3, URL before /library
375
376 String webswing = getParameter("webswing");
377 isWebswing = (webswing == null) ? false : webswing.equals("1");
378
379 String gsdlVersionStr = getParameter("gsdlversion");
380 if(gsdlVersionStr != null) {
381 System.err.println("*** gsdl version: " + gsdlVersionStr);
382 this.gsdlversion = Integer.parseInt(gsdlVersionStr);
383 }
384
385 // Check it isn't null
386 if ((verbosity_param!=null) && (!verbosity_param.startsWith("_"))) {
387 verbosity_ = Integer.parseInt(verbosity_param);
388 }
389 else {
390 verbosity_ = 1;
391 }
392
393
394 if (verbosity_ >= 4) {
395 System.err.println("Got parameters.");
396 }
397
398 if (caption_param != null && !caption_param.startsWith("_")) {
399 caption_ = caption_param;
400 }
401 else {
402 if (verbosity_ >= 4) {
403 System.err.println("No Caption: setting to a space.");
404 }
405 caption_ = " ";
406 }
407
408 if ((bgcolor_param != null) && (!bgcolor_param.startsWith("_"))) {
409 if (bgcolor_param.startsWith("#")) {
410 bgcolor_ = Color.decode(bgcolor_param);
411 }
412 else {
413 String [] c = bgcolor_param.split(",");
414 if (c.length == 3)
415 bgcolor_ = new Color(Integer.parseInt(c[0]), Integer.parseInt(c[1]), Integer.parseInt(c[2]));
416 }
417 if (verbosity_ >= 4){
418 System.err.println("Set BG to be " + bgcolor_.toString());
419 }
420 }
421 else {
422 if (verbosity_ >= 4) {
423 System.err.println("No BG: setting to NZDL green.");
424 }
425 bgcolor_ = new Color(150, 193, 155);
426 }
427
428 if ((image_type_param != null) && (!image_type_param.startsWith("_"))) {
429 imageType_ = image_type_param;
430 }
431
432 if ((is_java2_param == null) || (is_java2_param.equals("auto")) || (is_java2_param.startsWith("_"))) {
433 String version = System.getProperty("java.version");
434 version = version.substring(0, version.lastIndexOf("."));
435 System.err.println("VERSION: " + version);
436 float ver = (new Float(version)).floatValue();
437 isJava2_ = (ver >= 1.2) ? true : false;
438 }
439 else {
440 isJava2_ = (is_java2_param.equals("true")) ? true : false;
441 }
442
443 if ((max_depth_param != null) && (!max_depth_param.startsWith("_"))) {
444 // System.err.println("maxDepth = " + max_depth_param);
445 maxDepth_ = Integer.parseInt(max_depth_param);
446 }
447
448 if ((max_downloads_param!=null) && (!max_downloads_param.startsWith("_"))) {
449 maxDownloads_ = Integer.parseInt(max_downloads_param);
450 System.out.println(" maxDownloads " + maxDownloads_ );
451 maxDownloads_=50;
452 }
453
454 if ((max_display_param!=null) && (!max_display_param.startsWith("_"))) {
455 maxDisplay_ = Integer.parseInt(max_display_param);
456
457 }
458
459 if ((refresh_delay_param!=null) && (!refresh_delay_param.startsWith("_"))) {
460 refreshDelay_ = Integer.parseInt(refresh_delay_param);
461 }
462
463
464 if (document_root !=null){ // e.g. "/greenstone/web/images" for GS2
465 if(document_root.indexOf("/") == 0) {
466 document_root = document_root.substring(1); // takes off first slash
467 }
468 if (document_root.indexOf("/") > 0 ){ // e.g we end up with "greenstone" for GS2, "greenstone3" for GS3
469 document_root = document_root.substring(0, document_root.indexOf("/"));
470 }
471 }
472
473 String image_url = getParameter("imageURL");
474 String image_ignore = getParameter("imageIgnorePrefix");
475 String href_musthave = getParameter("hrefMustHave");
476 String image_mustnothave = getParameter("imageMustNotHave");
477
478 // builds starting url when incorporated with Greenstone
479 if (image_url==null)
480 {
481 String collection_param = getParameter("collection");
482 String classifier_param = getParameter("classifier");
483
484 if(gsdlversion == 2) {
485 // GS2 way
486 String gwcgi_param = gwcgi;
487 gwcgi_param = tidy_URL(gwcgi_param, true); //true to append ? to URL
488 image_url = gwcgi_param + "a=d";
489 image_url += "&c=" + collection_param;
490 image_url += "&cl=" + classifier_param;
491 } else {
492 // Try GS3 way
493 String library = getParameter("library");
494 //String site = getParameter("sitename");
495
496 // starting URL (image_url) might not be base_url. We need to store base_url separately
497 baseURL = tidy_URL(baseurl, false);
498 if(!baseURL.endsWith("/")) {
499 baseURL += "/";
500 }
501
502 // building up to baseURL/LIBNAME/collection/COLNAME/browse/
503 image_url = baseURL + library + "/collection/" + collection_param + "/browse/";
504
505 int index = classifier_param.indexOf(".");
506 if(index == -1) {
507 image_url += classifier_param;
508 } else {
509
510 //"CL2#" + classifier_param;
511 //String superClassifier = classifier_param.substring(0, index);
512 //image_url = image_url + superClassifier + "#" + classifier_param;
513
514 // I'm guessing we want something like
515 //http://localhost:8383/greenstone3/library/collection/smallbea/browse/CL2/3, when classifier_param is CL2.3
516 // to get the classifier page containing the images that are to be turned into a collage?
517 String classifierSuffix = classifier_param.replace(".", "/");
518 image_url = image_url + classifierSuffix;
519 }
520 }
521 }
522
523 // This block was a nice idea had it worked, but it doesn't
524 // work. It's here as sample syntax. Handle getting called by
525 // webswing JavaScript (but only if run in webswing mode):
526 // when the user navigates away from the webswing app's web
527 // page, do cleanup
528 // https://www.webswing.org/docs/23.2/integrate/jslink.html#invoking-java-from-javascript
529 // https://vaadin.com/directory/component/webswing-vaadin1
530 if(isWebswing) {
531
532 //System.err.println("#### isWebswing, about to add listeners");
533
534 WebswingUtil.getWebswingApi().addBrowserActionListener(new WebActionListener() {
535 //@Override
536 public void actionPerformed(WebActionEvent actionEvent) {
537
538 System.err.println("***JavaScript to Java comms: actionPerformed called");
539 //System.err.println("actionEvent = ");
540
541 switch (actionEvent.getActionName()) {
542 case "navigatingAway":
543 // do cleanup
544 System.err.println("@@@@ In Java collage addBrowserActionListener - on navigatingAway called");
545 GsdlCollageApplet.this.stopRunning();
546 break;
547 case "testJavaCall":
548 System.err.println("@@@@ In GsdlCollageApplet's addBrowserActionListener - testJavaCall called");
549 WebswingUtil.getWebswingApi().sendActionEvent("javaToWebswingJSConsoleLog", "GsdlCollageApplet got TEST call !!", null);
550 //WebswingUtil.getWebswingApi().sendActionEvent("javaToWebswingJSConsoleLog", "GsdlCollageApplet - testjavaCall handler got your message", null);
551 }
552 }
553 });
554 }
555
556 if(isWebswing && isRunAsApplet) {
557
558 // When webswing runs Collage in Applet mode, it's not
559 // shutdown the way we'd like on the javascript kill()
560 // command when the user navigates away from the collage
561 // page, as that only generates a Java WindowClosingEvent
562 // by default if no shutdown handlers registered. And a
563 // WindowClosingEvent only has any effect on applications,
564 // not applets. But we can write our own custom handler to
565 // mimic the applet shutdown sequence of stop() then
566 // destroy() see
567 // https://www.webswing.org/docs/20.2/integrate/api.html
568 // This code also written with the help of webswing's
569 // source code to know the methods in interface
570 // WebswingShutdownListener and the import statements
571 // required for these methods.
572 // After stop() and destroy(), we call System.exit(0) on
573 // top of usual applet behaviour to make sure we get rid
574 // of the applet "application" that webswing launched for
575 // us (it took the applet code and ran it, presumably like
576 // a java program, so a System.exit call may be warranted,
577 // whereas System.exit() isn't called on a regular applet).
578 WebswingUtil.getWebswingApi().addShutdownListener(new WebswingShutdownListener() {
579 // These are official webswing API comments for info
580 /**
581 * Invoked before Webswing requests application to exit.
582 * Do not execute long operations in this listener - listener execution will be interrupted if blocking for &gt; 3 seconds and the application will exit without delay.
583 * Connection to server is still open when this callback is triggered.
584 * This method can delay the shutdown. Calling {@link WebswingApi#resetInactivityTimeout()} within the delay period will cancel the shutdown sequence.
585 *
586 * This method is not called on the event dispatch thread.
587 *
588 * @param event Event contains the reason of this shutdown - either triggered from Admin console's rest interface or by inactivity
589 * @return number of seconds to delay the shutdown, returning 0 will cause shutdown without delay (even if {@link WebswingApi#resetInactivityTimeout()} has been called)
590 */
591 public int onBeforeShutdown(OnBeforeShutdownEvent event) {
592 return 0; // seconds to delay before starting shutdown procedure
593 }
594
595 /**
596 * Invoked when Webswing requests swing application to exit.
597 * This method should cause this process to exit (not necessarily in the same thread).
598 * When this method is called, connection to server is already closed
599 *
600 * This method is not called on the event dispatch thread.
601 */
602 public void onShutdown() {
603 // https://docs.oracle.com/javase%2F7%2Fdocs%2Fapi%2F%2F/java/applet/Applet.html#destroy()
604 // destroy(): "Called by the browser or
605 // applet viewer to inform this applet that
606 // it is being reclaimed and that it should
607 // destroy any resources that it has
608 // allocated. The stop method will always be
609 // called before destroy."
610 // We're like the browser now and so we should
611 // honour the above contract.
612 if (verbosity_ >= 3) {
613 System.err.println("------- GsdlCollageApplet WebswingShutdownListener.onShutdown() - our custom behaviour");
614
615 // This message is sent to JavaScript too late: webpage has already
616 // finished unloading and the JS webswingInstance can't receive msgs.
617 // if(isWebswing && isRunAsApplet) {
618 // WebswingUtil.getWebswingApi().sendActionEvent("javaToWebswingJSConsoleLog", "GsdlCollageApplet running as webswing applet - quitting now", null);
619 // }
620 }
621 stop();
622 destroy();
623 // Even though we're an applet and applets
624 // shouldn't system.exit(0) we're running our
625 // applet version of the code through
626 // webswing. And this handler/webswing stands
627 // in place of a browser and controls what
628 // *ought* to happen if a browser doesn't take
629 // care of the applet life cycle and we ought
630 // to do it (here in the webswing handler).
631 System.exit(0);
632 }
633 });
634 }
635
636
637 MediaTracker trk = new MediaTracker(this);
638
639 // creates a class to store the image and its associated url
640 download_images_ = new DownloadImages( this, verbosity_,isJava2_ );
641 // starts the download image thread with the starting url
642
643 download_thread_ = new DownloadUrls(this, download_images_,
644 image_url, href_musthave, image_mustnothave,
645 image_ignore, imageType_,document_root,verbosity_,trk);
646 // If GsdlCollageApplet is launched as a *webswing applet*, it
647 // doesn't have dimensions, isn't showing nor is it even
648 // displayable at this point in the code. Only when webswing
649 // resizes the GsdlCollageApplet Component, does it have
650 // dimensions and do we know for sure it is showing.
651 if(isShowing() && isDisplayable()) { // case of: if(!(isRunAsApplet && isWebswing)) {
652 if (verbosity_ >= 4) {
653 System.err.println("**** Creating display thread");
654 }
655 // starts the display image thread with the currently downloaded images
656 display_thread_ = new DisplayImages(this, download_images_,isJava2_, bgcolor_);
657 } else {
658 if (verbosity_ >= 4) {
659 System.err.println("**** GsdlCollageApplet.init() - display_thread_ not yet showing.");
660 System.err.println("**** Will create and start display_thread_ later, in ComponentListener.resize()");
661 }
662 }
663 // The display_thread_ needs the component showing before it can be started up.
664 // It was showing already by this point for the applet in non-webswing mode (when
665 // using the appletviewer),
666 // and I got it showing by this point when run as a cmdline or webswing application.
667 // But when the applet is run in webswing mode, it's not yet showing
668 // at this point! A componentListener allows us to handle componentShown events on
669 // the JComponent of this JApplet. However, the applet in webswing mode doesn't trigger
670 // that, it only triggers componentResized. So maybe webswing starts it off as 0 size?
671 this.addComponentListener(this);
672
673
674 }
675
676 public void componentHidden(ComponentEvent e) {
677 if (verbosity_ >= 4) {
678 System.err.println(e.getComponent().getClass().getName() + " --- Hidden");
679 }
680 }
681
682 public void componentMoved(ComponentEvent e) {
683 if (verbosity_ >= 4) {
684 System.err.println(e.getComponent().getClass().getName() + " --- Moved");
685 }
686 }
687
688 public void componentResized(ComponentEvent e) {
689 if(display_thread_ == null) {
690 if (verbosity_ >= 4) {
691 System.err.println(e.getComponent().getClass().getName() + " --- Resized");
692 System.err.println("Ready to instantiate display thread");
693 }
694 display_thread_ = new DisplayImages(this, download_images_,isJava2_, bgcolor_);
695 display_thread_.start();
696 }
697 }
698
699 public void componentShown(ComponentEvent e) {
700 if(display_thread_ == null) {
701 if (verbosity_ >= 4) {
702 System.err.println("@@@ " + e.getComponent().getClass().getName() + " --- Shown");
703 System.err.println("GsdlCollageApplet ready to instantiate display thread");
704 }
705 display_thread_ = new DisplayImages(this, download_images_,isJava2_, bgcolor_);
706 display_thread_.start();
707 }
708 }
709
710 public JLabel getStatusBar() {
711 return this.statusBar;
712 }
713
714 // TODO: Do I need to follow
715 // https://stackoverflow.com/questions/5861894/how-to-synchronize-or-lock-upon-variables-in-java
716 // I just followed the Java API's recommendations about what to do
717 // as replacement around Thread.stop() and Thread.destroy() being
718 // deprecated (note: not Applet.stop() and
719 // Applet.destroy(). https://docs.oracle.com/javase/8/docs/api/java/lang/Thread.html#stop--
720 public void stopRunning() {
721 if(verbosity_ >= 3) {
722 System.err.println("**** GsdlCollageApplet.stopRunning() called");
723 }
724
725 stop_running = true;
726 if(download_thread_ != null) {
727 download_thread_.stopRunning();
728 }
729 if(display_thread_ != null) {
730 display_thread_.stopRunning();
731 }
732
733 // The Display Thread?
734 //if(!Thread.currentThread().isInterrupted()) {
735 // Thread.currentThread().interrupt();
736 //}
737 }
738
739 public boolean isStopping() {
740 return stop_running;
741 }
742
743 // Applet's mouseDown() is deprecated, so we implement mousePressed
744 protected class CollageMouseAdapter extends MouseAdapter {
745
746 /** Goes to the url associated with the image that is clicked on screen<br>
747 * Displays the url containing the image in a new window */
748 //public boolean mouseDown(Event event, int x, int y)
749 public void mousePressed(MouseEvent e)
750 {
751 int x = e.getX();
752 int y = e.getY();
753
754 // determines which image was clicked on
755 CollageImage cimage = GsdlCollageApplet.this.display_thread_.clickedOnImage(x,y);
756 // if they were clicking on an image (as opposed to background)
757 if (cimage != null)
758 {
759 System.err.println("Click on image: from url = " + cimage.from_url_);
760 try {
761 // displays the associated url in a new window
762 URL from_url = null;
763
764 java.util.regex.Pattern p = java.util.regex.Pattern.compile("cl=CL\\d(\\.\\d)*\\s");
765 java.util.regex.Matcher m = p.matcher(cimage.from_url_.trim()+" ");
766
767 if (m.find() ){
768 from_url = cimage.url_;
769 }
770 else{
771 from_url = new URL(cimage.from_url_ + "#" + cimage.name_);
772 }
773
774 if(isRunAsApplet) {
775 GsdlCollageApplet.this.getAppletContext().showDocument(from_url,"gsdlDoc");
776 } else if(isWebswing) {
777 WebswingUtil.getWebswingApi().sendActionEvent("openURL",
778 from_url.toString() + " - " +"gsdlDoc",
779 null); // window name is gsdlDoc
780 } else {
781 System.err.println("@@@GsdlCollageApplet.CollageMouseAdapter.mousePressed()"
782 + "\n\topening url " + from_url + "\n\t"
783 + "for non-applets and non-webswing applications is not yet implemented.");
784 }
785 }
786 catch (MalformedURLException ex) {
787 ex.printStackTrace();
788 }
789
790 }
791 }
792 }
793
794 /** Start threads for downloading and displaying images */
795 public void start()
796 {
797 download_thread_.start();
798
799 if(display_thread_ != null) {
800 //if(!(isRunAsApplet && isWebswing)) { // then display_thread_ is instantiated at this point
801 // start it
802 display_thread_.start();
803 }
804
805 paint = new Thread(new Runnable(){
806 public void run() {
807 try {
808
809 Thread curr_thread = Thread.currentThread();
810
811 while (curr_thread == paint) {
812
813 repaint();
814
815 Thread.sleep(2000);
816 curr_thread = Thread.currentThread();
817 }
818
819
820 } catch (Exception e) {
821
822 }
823 }
824
825
826 });
827
828 paint.start();
829
830 }
831
832 /** Stops threads for downloading and displaying images */
833 public void stop()
834 {
835 if(verbosity_ >= 3) {
836 System.err.println("\n\n@@@ GsdlCollageApplet.stop() called: stopping threads...");
837 }
838 //download_thread_.stop();
839 //display_thread_.stop();
840 stopRunning();
841 // TODO: maybe we want to unset the interrupt variable on the threads to restart
842 // on start() getting called? No: because we system.exit() in the webswing
843 // shutdown handler we implemented.
844 }
845
846 /** Destroys threads for downloading and displaying images Java
847 * API method specification for destroy(): "Called by the browser
848 * or applet viewer to inform this applet that it is being
849 * reclaimed and that it should destroy any resources that it has
850 * allocated. The stop method will always be called before
851 * destroy."
852 */
853 public void destroyed()
854 {
855 if(verbosity_ >= 3) {
856 System.err.println("@@@@@@@@@ GsdlCollageApplet.destroy() called");
857 }
858 download_thread_ = null;
859
860 display_thread_ = null;
861
862 }
863
864
865 /** Repaints the applet */
866 public void update(Graphics g)
867 {
868 // System.err.println("Update called");
869 paint(g);
870 }
871
872 /** Repaints the applet using the paint method of the thread that is
873 * currently displaying images */
874 public void paint(Graphics g) {
875
876 if (display_thread_!=null)
877 {
878
879 //display_thread_.display_collage();
880 display_thread_.paint(g);
881 }
882 else
883 {
884 System.err.println("Applet still trying to paint!!");
885 }
886
887 }
888
889
890 /** Ensures a URL address (as string) has a protocol, host, and file.
891 *
892 * If the URL is a CGI script URL, it should be tidied up so that it is
893 * appropriate to tag attrib=value pairs on the end. This means it
894 * must either end with a "?" or (if it contains a question-mark
895 * internally) end with a "&". */
896 String tidy_URL(String address, boolean isCGI) {
897
898 // System.err.println("tidy URL: " + address);
899
900 // make sure the URL has protocol, host, and file
901 if (address.startsWith("http")) {
902 // the address has all the necessary components
903 } else if (address.startsWith("/")) {
904 // there is not protocol and host
905 URL document = getDocumentBase();
906 String port = "";
907 if (document.getPort()!=-1) {
908 port = ":" + document.getPort();
909 }
910 address = "http://" + document.getHost() + port + address;
911 } else {
912 // this URL is relative to the directory the document is in
913 URL document = getDocumentBase();
914 String directory = document.getFile();
915 int end = directory.lastIndexOf('/');
916 String port = "";
917 if (document.getPort()!=-1) {
918 port = ":" + document.getPort();
919 }
920 directory = directory.substring(0,end + 1);
921 address = "http://" + document.getHost() + port + directory + address;
922
923 }
924
925 // if the URL is a cgi script, make sure it has a "?" in ti,
926 // and that it ends with a "?" or "&"
927 if (isCGI) {
928 if (address.indexOf((int) '?') == -1) {
929 address = address + "?";
930 } else if (!address.endsWith("?")) {
931 address = address + "&";
932 }
933 }
934
935 return address;
936 }
937
938
939 public static void printUsage() {
940 System.err.println("Params needed include: --gsdlversion <3/2> [--statusbar 0/1] [--baseurl <GS3 DL baseURL>/--gwcgi <library.cgi URL>] --library l --collection c --classifier cl --imageType \".jpg%.png\" --imageMustNotHave \"interfaces/\" [--hrefMustHave \"LIBRARYNAME/sites/SITENAME/collect/COLNAME\"] -documentroot greenstone3 [--webswing <1/0>] [--verbosity <v>] --maxDepth 500 --maxDisplay 25 --refreshDelay 1500 --isJava2 auto --bgcolor \"#96c29a\" [--xtraParams <key1=value1&key2=value2&...]");
941 System.err.println("To run as webswing application, additionally pass in --webswing 1");
942 }
943
944 // To also be able to run this applet as an application, need a main method
945 /**
946 * After building the GS3 Multimedia collection, try running this Application as:
947 *
948 java -cp ./web/applet/GsdlCollageApplet.jar:./web/WEB-INF/lib/log4j-1.2.8.jar:./web/WEB-INF/classes:./web/ext/webswing/api/webswing-api.jar org.greenstone.applet.GsdlCollageApplet.GsdlCollageApplet --statusbar 0 --baseurl "http://localhost:8383/greenstone3/" --library library --collection smallbea --gsdlversion 3 --hrefMustHave "library/collection/smallbea/browse/CL3" --documentroot greenstone3 --verbosity 3 --imageType ".jpg%.png" --imageMustNotHave "interfaces/" --classifier "CL3.1" --maxDepth 500 --maxDisplay 25 --refreshDelay 1500 --isJava2 auto --bgcolor "#96c29a" --width 645 --height 780
949 *
950 * If wanting to run this with the appletviewer, need to ensure a copy of webswing-api.jar exists in web/applet
951 * as specified in classifier.xsl's applet element's tag (because of the webswing import statements I think).
952 * Then ensure the GS3 server is running and you've built your collection with the collage classifier
953 * and run the appletviewer with the collage classifier's URL embedded in quotes, e.g:
954 * appletviewer "http://localhost:8383/greenstone3/library/collection/smallbea/browse/CL3"
955 * Note that appletviewer is being deprecated in Java and may not be available in future Java downloads.
956 * Sadly, the appletviewer runs in a sort of sandbox that doesn't allow downloading from URLs, so the applet window
957 * appears and shows an infinite message it's trying to download, but fails to do so because of security exceptions.
958 */
959 public static void main(String[] args) {
960 if(args.length == 0) {
961 printUsage();
962 } else if(args[0].equals("--help") || args[0].equals("-h")) {
963 printUsage();
964 } else {
965 JFrame frame = new JFrame("Collage Applet as Application");
966 GsdlCollageApplet collageApp = new GsdlCollageApplet();
967 frame.getContentPane().add(collageApp, BorderLayout.CENTER);
968 //frame.setSize(X_DIM, Y_DIM); //Y_DIM+EXTRA_HEIGHT);
969 frame.setVisible(true);
970 // https://stackoverflow.com/questions/19433358/difference-between-dispose-and-exit-on-close-in-java
971 // default: https://docs.oracle.com/javase/8/docs/api/javax/swing/JFrame.html#EXIT_ON_CLOSE
972 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // Running as an application. But don't do EXIT_ON_CLOSE in Applet mode!
973
974 // prepare to run the collage applet as an application
975 collageApp.applicationPreInit(args);
976 // It's now figured out the dimensions based on anything specified, so set frame size
977 frame.setSize(collageApp.X_DIM(), collageApp.Y_DIM()); //Y_DIM+EXTRA_HEIGHT);
978
979 // status bar code. Not ideal, but had to add it here to get relative dimensions right
980 JLabel statusBar = collageApp.getStatusBar();
981 if(statusBar != null) {
982 collageApp.setLayout(new BorderLayout());
983
984 frame.add(statusBar, BorderLayout.SOUTH);
985 Dimension d = statusBar.getSize();
986 d.height = EXTRA_HEIGHT;
987 d.width = collageApp.getWidth();
988 statusBar.setPreferredSize(d);
989 //statusBar.setPreferredSize(new Dimension(collageApp.getWidth(), EXTRA_HEIGHT));
990 }
991
992 // Run it at last: manually calling (J)Applet methods init() and start()
993 collageApp.init();
994
995 collageApp.showStatus("Collage application running");
996 collageApp.start();
997
998 // When we terminate the application, need to manually call the applet method stop()
999 //https://docs.oracle.com/javase/tutorial/uiswing/events/windowlistener.html#windowfocuslistener
1000 frame.addWindowListener(new WindowAdapter() {
1001 public void windowClosing(WindowEvent e) {
1002 if(collageApp.isWebswing) {
1003 WebswingUtil.getWebswingApi().sendActionEvent("javaToWebswingJSConsoleLog", "GsdlCollageApplet - quitting now", null);
1004 //org.webswing.toolkit.api.WebswingUtil.getWebswingApi().sendActionEvent("javaToWebswingJSConsoleLog", "GsdlCollageApplet about to QUIT !!", null);
1005 }
1006 collageApp.showStatus("Stopping threads");
1007 System.err.println("\n\n*** Closing collage Application: stopping threads...");
1008 collageApp.stopRunning();
1009
1010 //collageApp.stop();
1011 //collageApp.destroy();
1012 }
1013 });
1014
1015 }
1016 }
1017}
Note: See TracBrowser for help on using the repository browser.