blob: 5170b0258a1181104b0ac9c3094773bd79a02f3d [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1995-2004 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package sun.applet;
27
28import java.util.*;
29import java.io.*;
30import java.awt.*;
31import java.awt.event.*;
32import java.awt.print.*;
33import javax.print.attribute.*;
34import java.applet.*;
35import java.net.URL;
36import java.net.MalformedURLException;
37import java.net.SocketPermission;
38import sun.misc.Ref;
39import java.security.AccessController;
40import java.security.PrivilegedAction;
41import java.lang.reflect.InvocationTargetException;
42import java.lang.reflect.Method;
43import sun.awt.SunToolkit;
44import sun.awt.AppContext;
45import java.lang.ref.WeakReference;
46
47/**
48 * A frame to show the applet tag in.
49 */
50class TextFrame extends Frame {
51
52 /**
53 * Create the tag frame.
54 */
55 TextFrame(int x, int y, String title, String text) {
56 setTitle(title);
57 TextArea txt = new TextArea(20, 60);
58 txt.setText(text);
59 txt.setEditable(false);
60
61 add("Center", txt);
62
63 Panel p = new Panel();
64 add("South", p);
65 Button b = new Button(amh.getMessage("button.dismiss", "Dismiss"));
66 p.add(b);
67
68 class ActionEventListener implements ActionListener {
69 public void actionPerformed(ActionEvent evt) {
70 dispose();
71 }
72 }
73 b.addActionListener(new ActionEventListener());
74
75 pack();
76 move(x, y);
77 setVisible(true);
78
79 WindowListener windowEventListener = new WindowAdapter() {
80
81 public void windowClosing(WindowEvent evt) {
82 dispose();
83 }
84 };
85
86 addWindowListener(windowEventListener);
87 }
88 private static AppletMessageHandler amh = new AppletMessageHandler("textframe");
89
90}
91
92/**
93 * Lets us construct one using unix-style one shot behaviors
94 */
95
96class StdAppletViewerFactory implements AppletViewerFactory
97{
98 public AppletViewer createAppletViewer(int x, int y,
99 URL doc, Hashtable atts) {
100 return new AppletViewer(x, y, doc, atts, System.out, this);
101 }
102
103 public MenuBar getBaseMenuBar() {
104 return new MenuBar();
105 }
106
107 public boolean isStandalone() {
108 return true;
109 }
110}
111
112/**
113 * The applet viewer makes it possible to run a Java applet without using a browser.
114 * For details on the syntax that <B>appletviewer</B> supports, see
115 * <a href="../../../docs/tooldocs/appletviewertags.html">AppletViewer Tags</a>.
116 * (The document named appletviewertags.html in the JDK's docs/tooldocs directory,
117 * once the JDK docs have been installed.)
118 */
119public class AppletViewer extends Frame implements AppletContext,
120 Printable {
121
122 /**
123 * Some constants...
124 */
125 private static String defaultSaveFile = "Applet.ser";
126
127 /**
128 * The panel in which the applet is being displayed.
129 */
130 AppletViewerPanel panel;
131
132 /**
133 * The status line.
134 */
135 Label label;
136
137 /**
138 * output status messages to this stream
139 */
140
141 PrintStream statusMsgStream;
142
143 /**
144 * For cloning
145 */
146 AppletViewerFactory factory;
147
148
149 private final class UserActionListener implements ActionListener {
150 public void actionPerformed(ActionEvent evt) {
151 processUserAction(evt);
152 }
153 }
154
155 /**
156 * Create the applet viewer
157 */
158 public AppletViewer(int x, int y, URL doc, Hashtable atts,
159 PrintStream statusMsgStream, AppletViewerFactory factory) {
160 this.factory = factory;
161 this.statusMsgStream = statusMsgStream;
162 setTitle(amh.getMessage("tool.title", atts.get("code")));
163
164 MenuBar mb = factory.getBaseMenuBar();
165
166 Menu m = new Menu(amh.getMessage("menu.applet"));
167
168 addMenuItem(m, "menuitem.restart");
169 addMenuItem(m, "menuitem.reload");
170 addMenuItem(m, "menuitem.stop");
171 addMenuItem(m, "menuitem.save");
172 addMenuItem(m, "menuitem.start");
173 addMenuItem(m, "menuitem.clone");
174 m.add(new MenuItem("-"));
175 addMenuItem(m, "menuitem.tag");
176 addMenuItem(m, "menuitem.info");
177 addMenuItem(m, "menuitem.edit").disable();
178 addMenuItem(m, "menuitem.encoding");
179 m.add(new MenuItem("-"));
180 addMenuItem(m, "menuitem.print");
181 m.add(new MenuItem("-"));
182 addMenuItem(m, "menuitem.props");
183 m.add(new MenuItem("-"));
184 addMenuItem(m, "menuitem.close");
185 if (factory.isStandalone()) {
186 addMenuItem(m, "menuitem.quit");
187 }
188
189 mb.add(m);
190
191 setMenuBar(mb);
192
193 add("Center", panel = new AppletViewerPanel(doc, atts));
194 add("South", label = new Label(amh.getMessage("label.hello")));
195 panel.init();
196 appletPanels.addElement(panel);
197
198 pack();
199 move(x, y);
200 setVisible(true);
201
202 WindowListener windowEventListener = new WindowAdapter() {
203
204 public void windowClosing(WindowEvent evt) {
205 appletClose();
206 }
207
208 public void windowIconified(WindowEvent evt) {
209 appletStop();
210 }
211
212 public void windowDeiconified(WindowEvent evt) {
213 appletStart();
214 }
215 };
216
217 class AppletEventListener implements AppletListener
218 {
219 final Frame frame;
220
221 public AppletEventListener(Frame frame)
222 {
223 this.frame = frame;
224 }
225
226 public void appletStateChanged(AppletEvent evt)
227 {
228 AppletPanel src = (AppletPanel)evt.getSource();
229
230 switch (evt.getID()) {
231 case AppletPanel.APPLET_RESIZE: {
232 if(src != null) {
233 resize(preferredSize());
234 validate();
235 }
236 break;
237 }
238 case AppletPanel.APPLET_LOADING_COMPLETED: {
239 Applet a = src.getApplet(); // sun.applet.AppletPanel
240
241 // Fixed #4754451: Applet can have methods running on main
242 // thread event queue.
243 //
244 // The cause of this bug is that the frame of the applet
245 // is created in main thread group. Thus, when certain
246 // AWT/Swing events are generated, the events will be
247 // dispatched through the wrong event dispatch thread.
248 //
249 // To fix this, we rearrange the AppContext with the frame,
250 // so the proper event queue will be looked up.
251 //
252 // Swing also maintains a Frame list for the AppContext,
253 // so we will have to rearrange it as well.
254 //
255 if (a != null)
256 AppletPanel.changeFrameAppContext(frame, SunToolkit.targetToAppContext(a));
257 else
258 AppletPanel.changeFrameAppContext(frame, AppContext.getAppContext());
259
260 break;
261 }
262 }
263 }
264 };
265
266 addWindowListener(windowEventListener);
267 panel.addAppletListener(new AppletEventListener(this));
268
269 // Start the applet
270 showStatus(amh.getMessage("status.start"));
271 initEventQueue();
272 }
273
274 // XXX 99/9/10 probably should be "private"
275 public MenuItem addMenuItem(Menu m, String s) {
276 MenuItem mItem = new MenuItem(amh.getMessage(s));
277 mItem.addActionListener(new UserActionListener());
278 return m.add(mItem);
279 }
280
281 /**
282 * Send the initial set of events to the appletviewer event queue.
283 * On start-up the current behaviour is to load the applet and call
284 * Applet.init() and Applet.start().
285 */
286 private void initEventQueue() {
287 // appletviewer.send.event is an undocumented and unsupported system
288 // property which is used exclusively for testing purposes.
289 String eventList = System.getProperty("appletviewer.send.event");
290
291 if (eventList == null) {
292 // Add the standard events onto the event queue.
293 panel.sendEvent(AppletPanel.APPLET_LOAD);
294 panel.sendEvent(AppletPanel.APPLET_INIT);
295 panel.sendEvent(AppletPanel.APPLET_START);
296 } else {
297 // We're testing AppletViewer. Force the specified set of events
298 // onto the event queue, wait for the events to be processed, and
299 // exit.
300
301 // The list of events that will be executed is provided as a
302 // ","-separated list. No error-checking will be done on the list.
303 String [] events = splitSeparator(",", eventList);
304
305 for (int i = 0; i < events.length; i++) {
306 System.out.println("Adding event to queue: " + events[i]);
307 if (events[i].equals("dispose"))
308 panel.sendEvent(AppletPanel.APPLET_DISPOSE);
309 else if (events[i].equals("load"))
310 panel.sendEvent(AppletPanel.APPLET_LOAD);
311 else if (events[i].equals("init"))
312 panel.sendEvent(AppletPanel.APPLET_INIT);
313 else if (events[i].equals("start"))
314 panel.sendEvent(AppletPanel.APPLET_START);
315 else if (events[i].equals("stop"))
316 panel.sendEvent(AppletPanel.APPLET_STOP);
317 else if (events[i].equals("destroy"))
318 panel.sendEvent(AppletPanel.APPLET_DESTROY);
319 else if (events[i].equals("quit"))
320 panel.sendEvent(AppletPanel.APPLET_QUIT);
321 else if (events[i].equals("error"))
322 panel.sendEvent(AppletPanel.APPLET_ERROR);
323 else
324 // non-fatal error if we get an unrecognized event
325 System.out.println("Unrecognized event name: " + events[i]);
326 }
327
328 while (!panel.emptyEventQueue()) ;
329 appletSystemExit();
330 }
331 }
332
333 /**
334 * Split a string based on the presence of a specified separator. Returns
335 * an array of arbitrary length. The end of each element in the array is
336 * indicated by the separator of the end of the string. If there is a
337 * separator immediately before the end of the string, the final element
338 * will be empty. None of the strings will contain the separator. Useful
339 * when separating strings such as "foo/bar/bas" using separator "/".
340 *
341 * @param sep The separator.
342 * @param s The string to split.
343 * @return An array of strings. Each string in the array is determined
344 * by the location of the provided sep in the original string,
345 * s. Whitespace not stripped.
346 */
347 private String [] splitSeparator(String sep, String s) {
348 Vector v = new Vector();
349 int tokenStart = 0;
350 int tokenEnd = 0;
351
352 while ((tokenEnd = s.indexOf(sep, tokenStart)) != -1) {
353 v.addElement(s.substring(tokenStart, tokenEnd));
354 tokenStart = tokenEnd+1;
355 }
356 // Add the final element.
357 v.addElement(s.substring(tokenStart));
358
359 String [] retVal = new String[v.size()];
360 v.copyInto(retVal);
361 return retVal;
362 }
363
364 /*
365 * Methods for java.applet.AppletContext
366 */
367
368 private static Map audioClips = new HashMap();
369
370 /**
371 * Get an audio clip.
372 */
373 public AudioClip getAudioClip(URL url) {
374 checkConnect(url);
375 synchronized (audioClips) {
376 AudioClip clip = (AudioClip)audioClips.get(url);
377 if (clip == null) {
378 audioClips.put(url, clip = new AppletAudioClip(url));
379 }
380 return clip;
381 }
382 }
383
384 private static Map imageRefs = new HashMap();
385
386 /**
387 * Get an image.
388 */
389 public Image getImage(URL url) {
390 return getCachedImage(url);
391 }
392
393 static Image getCachedImage(URL url) {
394 // System.getSecurityManager().checkConnection(url.getHost(), url.getPort());
395 return (Image)getCachedImageRef(url).get();
396 }
397
398 /**
399 * Get an image ref.
400 */
401 static Ref getCachedImageRef(URL url) {
402 synchronized (imageRefs) {
403 AppletImageRef ref = (AppletImageRef)imageRefs.get(url);
404 if (ref == null) {
405 ref = new AppletImageRef(url);
406 imageRefs.put(url, ref);
407 }
408 return ref;
409 }
410 }
411
412 /**
413 * Flush the image cache.
414 */
415 static void flushImageCache() {
416 imageRefs.clear();
417 }
418
419 static Vector appletPanels = new Vector();
420
421 /**
422 * Get an applet by name.
423 */
424 public Applet getApplet(String name) {
425 AppletSecurity security = (AppletSecurity)System.getSecurityManager();
426 name = name.toLowerCase();
427 SocketPermission panelSp =
428 new SocketPermission(panel.getCodeBase().getHost(), "connect");
429 for (Enumeration e = appletPanels.elements() ; e.hasMoreElements() ;) {
430 AppletPanel p = (AppletPanel)e.nextElement();
431 String param = p.getParameter("name");
432 if (param != null) {
433 param = param.toLowerCase();
434 }
435 if (name.equals(param) &&
436 p.getDocumentBase().equals(panel.getDocumentBase())) {
437
438 SocketPermission sp =
439 new SocketPermission(p.getCodeBase().getHost(), "connect");
440
441 if (panelSp.implies(sp)) {
442 return p.applet;
443 }
444 }
445 }
446 return null;
447 }
448
449 /**
450 * Return an enumeration of all the accessible
451 * applets on this page.
452 */
453 public Enumeration getApplets() {
454 AppletSecurity security = (AppletSecurity)System.getSecurityManager();
455 Vector v = new Vector();
456 SocketPermission panelSp =
457 new SocketPermission(panel.getCodeBase().getHost(), "connect");
458
459 for (Enumeration e = appletPanels.elements() ; e.hasMoreElements() ;) {
460 AppletPanel p = (AppletPanel)e.nextElement();
461 if (p.getDocumentBase().equals(panel.getDocumentBase())) {
462
463 SocketPermission sp =
464 new SocketPermission(p.getCodeBase().getHost(), "connect");
465 if (panelSp.implies(sp)) {
466 v.addElement(p.applet);
467 }
468 }
469 }
470 return v.elements();
471 }
472
473 /**
474 * Ignore.
475 */
476 public void showDocument(URL url) {
477 }
478
479 /**
480 * Ignore.
481 */
482 public void showDocument(URL url, String target) {
483 }
484
485 /**
486 * Show status.
487 */
488 public void showStatus(String status) {
489 label.setText(status);
490 }
491
492 public void setStream(String key, InputStream stream)throws IOException{
493 // We do nothing.
494 }
495
496 public InputStream getStream(String key){
497 // We do nothing.
498 return null;
499 }
500
501 public Iterator getStreamKeys(){
502 // We do nothing.
503 return null;
504 }
505
506 /**
507 * System parameters.
508 */
509 static Hashtable systemParam = new Hashtable();
510
511 static {
512 systemParam.put("codebase", "codebase");
513 systemParam.put("code", "code");
514 systemParam.put("alt", "alt");
515 systemParam.put("width", "width");
516 systemParam.put("height", "height");
517 systemParam.put("align", "align");
518 systemParam.put("vspace", "vspace");
519 systemParam.put("hspace", "hspace");
520 }
521
522 /**
523 * Print the HTML tag.
524 */
525 public static void printTag(PrintStream out, Hashtable atts) {
526 out.print("<applet");
527
528 String v = (String)atts.get("codebase");
529 if (v != null) {
530 out.print(" codebase=\"" + v + "\"");
531 }
532
533 v = (String)atts.get("code");
534 if (v == null) {
535 v = "applet.class";
536 }
537 out.print(" code=\"" + v + "\"");
538 v = (String)atts.get("width");
539 if (v == null) {
540 v = "150";
541 }
542 out.print(" width=" + v);
543
544 v = (String)atts.get("height");
545 if (v == null) {
546 v = "100";
547 }
548 out.print(" height=" + v);
549
550 v = (String)atts.get("name");
551 if (v != null) {
552 out.print(" name=\"" + v + "\"");
553 }
554 out.println(">");
555
556 // A very slow sorting algorithm
557 int len = atts.size();
558 String params[] = new String[len];
559 len = 0;
560 for (Enumeration e = atts.keys() ; e.hasMoreElements() ;) {
561 String param = (String)e.nextElement();
562 int i = 0;
563 for (; i < len ; i++) {
564 if (params[i].compareTo(param) >= 0) {
565 break;
566 }
567 }
568 System.arraycopy(params, i, params, i + 1, len - i);
569 params[i] = param;
570 len++;
571 }
572
573 for (int i = 0 ; i < len ; i++) {
574 String param = params[i];
575 if (systemParam.get(param) == null) {
576 out.println("<param name=" + param +
577 " value=\"" + atts.get(param) + "\">");
578 }
579 }
580 out.println("</applet>");
581 }
582
583 /**
584 * Make sure the atrributes are uptodate.
585 */
586 public void updateAtts() {
587 Dimension d = panel.size();
588 Insets in = panel.insets();
589 panel.atts.put("width",
590 new Integer(d.width - (in.left + in.right)).toString());
591 panel.atts.put("height",
592 new Integer(d.height - (in.top + in.bottom)).toString());
593 }
594
595 /**
596 * Restart the applet.
597 */
598 void appletRestart() {
599 panel.sendEvent(AppletPanel.APPLET_STOP);
600 panel.sendEvent(AppletPanel.APPLET_DESTROY);
601 panel.sendEvent(AppletPanel.APPLET_INIT);
602 panel.sendEvent(AppletPanel.APPLET_START);
603 }
604
605 /**
606 * Reload the applet.
607 */
608 void appletReload() {
609 panel.sendEvent(AppletPanel.APPLET_STOP);
610 panel.sendEvent(AppletPanel.APPLET_DESTROY);
611 panel.sendEvent(AppletPanel.APPLET_DISPOSE);
612
613 /**
614 * Fixed #4501142: Classlaoder sharing policy doesn't
615 * take "archive" into account. This will be overridden
616 * by Java Plug-in. [stanleyh]
617 */
618 AppletPanel.flushClassLoader(panel.getClassLoaderCacheKey());
619
620 /*
621 * Make sure we don't have two threads running through the event queue
622 * at the same time.
623 */
624 try {
625 panel.joinAppletThread();
626 panel.release();
627 } catch (InterruptedException e) {
628 return; // abort the reload
629 }
630
631 panel.createAppletThread();
632 panel.sendEvent(AppletPanel.APPLET_LOAD);
633 panel.sendEvent(AppletPanel.APPLET_INIT);
634 panel.sendEvent(AppletPanel.APPLET_START);
635 }
636
637 /**
638 * Save the applet to a well known file (for now) as a serialized object
639 */
640 void appletSave() {
641 AccessController.doPrivileged(new PrivilegedAction() {
642
643 public Object run() {
644 // XXX: this privileged block should be made smaller
645 // by initializing a private static variable with "user.dir"
646
647 // Applet needs to be stopped for serialization to succeed.
648 // Since panel.sendEvent only queues the event, there is a
649 // chance that the event will not be processed before
650 // serialization begins. However, by sending the event before
651 // FileDialog is created, enough time is given such that this
652 // situation is unlikely to ever occur.
653
654 panel.sendEvent(AppletPanel.APPLET_STOP);
655 FileDialog fd = new FileDialog(AppletViewer.this,
656 amh.getMessage("appletsave.filedialogtitle"),
657 FileDialog.SAVE);
658 // needed for a bug under Solaris...
659 fd.setDirectory(System.getProperty("user.dir"));
660 fd.setFile(defaultSaveFile);
661 fd.show();
662 String fname = fd.getFile();
663 if (fname == null) {
664 // Restart applet if Save is cancelled.
665 panel.sendEvent(AppletPanel.APPLET_START);
666 return null; // cancelled
667 }
668 String dname = fd.getDirectory();
669 File file = new File(dname, fname);
670
671 try {
672 BufferedOutputStream s = new BufferedOutputStream(new FileOutputStream(file));
673 ObjectOutputStream os = new ObjectOutputStream(s);
674 showStatus(amh.getMessage("appletsave.err1",
675 panel.applet.toString(), file.toString()));
676 os.writeObject(panel.applet);
677 } catch (IOException ex) {
678 System.err.println(amh.getMessage("appletsave.err2", ex));
679 } finally {
680 panel.sendEvent(AppletPanel.APPLET_START);
681 }
682 return null;
683 }
684 });
685 }
686
687 /**
688 * Clone the viewer and the applet.
689 */
690 void appletClone() {
691 Point p = location();
692 updateAtts();
693 factory.createAppletViewer(p.x + XDELTA, p.y + YDELTA,
694 panel.documentURL, (Hashtable)panel.atts.clone());
695 }
696
697 /**
698 * Show the applet tag.
699 */
700 void appletTag() {
701 ByteArrayOutputStream out = new ByteArrayOutputStream();
702 updateAtts();
703 printTag(new PrintStream(out), panel.atts);
704 showStatus(amh.getMessage("applettag"));
705
706 Point p = location();
707 new TextFrame(p.x + XDELTA, p.y + YDELTA, amh.getMessage("applettag.textframe"), out.toString());
708 }
709
710 /**
711 * Show the applet info.
712 */
713 void appletInfo() {
714 String str = panel.applet.getAppletInfo();
715 if (str == null) {
716 str = amh.getMessage("appletinfo.applet");
717 }
718 str += "\n\n";
719
720 String atts[][] = panel.applet.getParameterInfo();
721 if (atts != null) {
722 for (int i = 0 ; i < atts.length ; i++) {
723 str += atts[i][0] + " -- " + atts[i][1] + " -- " + atts[i][2] + "\n";
724 }
725 } else {
726 str += amh.getMessage("appletinfo.param");
727 }
728
729 Point p = location();
730 new TextFrame(p.x + XDELTA, p.y + YDELTA, amh.getMessage("appletinfo.textframe"), str);
731
732 }
733
734 /**
735 * Show character encoding type
736 */
737 void appletCharacterEncoding() {
738 showStatus(amh.getMessage("appletencoding", encoding));
739 }
740
741 /**
742 * Edit the applet.
743 */
744 void appletEdit() {
745 }
746
747 /**
748 * Print the applet.
749 */
750 void appletPrint() {
751 PrinterJob pj = PrinterJob.getPrinterJob();
752
753 if (pj != null) {
754 PrintRequestAttributeSet aset = new HashPrintRequestAttributeSet();
755 if (pj.printDialog(aset)) {
756 pj.setPrintable(this);
757 try {
758 pj.print(aset);
759 statusMsgStream.println(amh.getMessage("appletprint.finish"));
760 } catch (PrinterException e) {
761 statusMsgStream.println(amh.getMessage("appletprint.fail"));
762 }
763 } else {
764 statusMsgStream.println(amh.getMessage("appletprint.cancel"));
765 }
766 } else {
767 statusMsgStream.println(amh.getMessage("appletprint.fail"));
768 }
769 }
770
771 public int print(Graphics graphics, PageFormat pf, int pageIndex) {
772 if (pageIndex > 0) {
773 return Printable.NO_SUCH_PAGE;
774 } else {
775 Graphics2D g2d = (Graphics2D)graphics;
776 g2d.translate(pf.getImageableX(), pf.getImageableY());
777 panel.applet.printAll(graphics);
778 return Printable.PAGE_EXISTS;
779 }
780 }
781
782 /**
783 * Properties.
784 */
785 static AppletProps props;
786 public static synchronized void networkProperties() {
787 if (props == null) {
788 props = new AppletProps();
789 }
790 props.addNotify();
791 props.setVisible(true);
792 }
793
794 /**
795 * Start the applet.
796 */
797 void appletStart() {
798 panel.sendEvent(AppletPanel.APPLET_START);
799 }
800
801 /**
802 * Stop the applet.
803 */
804 void appletStop() {
805 panel.sendEvent(AppletPanel.APPLET_STOP);
806 }
807
808 /**
809 * Shutdown a viewer.
810 * Stop, Destroy, Dispose and Quit a viewer
811 */
812 private void appletShutdown(AppletPanel p) {
813 p.sendEvent(AppletPanel.APPLET_STOP);
814 p.sendEvent(AppletPanel.APPLET_DESTROY);
815 p.sendEvent(AppletPanel.APPLET_DISPOSE);
816 p.sendEvent(AppletPanel.APPLET_QUIT);
817 }
818
819 /**
820 * Close this viewer.
821 * Stop, Destroy, Dispose and Quit an AppletView, then
822 * reclaim resources and exit the program if this is
823 * the last applet.
824 */
825 void appletClose() {
826
827 // The caller thread is event dispatch thread, so
828 // spawn a new thread to avoid blocking the event queue
829 // when calling appletShutdown.
830 //
831 final AppletPanel p = panel;
832
833 new Thread(new Runnable()
834 {
835 public void run()
836 {
837 appletShutdown(p);
838 appletPanels.removeElement(p);
839 dispose();
840
841 if (countApplets() == 0) {
842 appletSystemExit();
843 }
844 }
845 }).start();
846 }
847
848 /**
849 * Exit the program.
850 * Exit from the program (if not stand alone) - do no clean-up
851 */
852 private void appletSystemExit() {
853 if (factory.isStandalone())
854 System.exit(0);
855 }
856
857 /**
858 * Quit all viewers.
859 * Shutdown all viewers properly then
860 * exit from the program (if not stand alone)
861 */
862 protected void appletQuit()
863 {
864 // The caller thread is event dispatch thread, so
865 // spawn a new thread to avoid blocking the event queue
866 // when calling appletShutdown.
867 //
868 new Thread(new Runnable()
869 {
870 public void run()
871 {
872 for (Enumeration e = appletPanels.elements() ; e.hasMoreElements() ;) {
873 AppletPanel p = (AppletPanel)e.nextElement();
874 appletShutdown(p);
875 }
876 appletSystemExit();
877 }
878 }).start();
879 }
880
881 /**
882 * Handle events.
883 */
884 public void processUserAction(ActionEvent evt) {
885
886 String label = ((MenuItem)evt.getSource()).getLabel();
887
888 if (amh.getMessage("menuitem.restart").equals(label)) {
889 appletRestart();
890 return;
891 }
892
893 if (amh.getMessage("menuitem.reload").equals(label)) {
894 appletReload();
895 return;
896 }
897
898 if (amh.getMessage("menuitem.clone").equals(label)) {
899 appletClone();
900 return;
901 }
902
903 if (amh.getMessage("menuitem.stop").equals(label)) {
904 appletStop();
905 return;
906 }
907
908 if (amh.getMessage("menuitem.save").equals(label)) {
909 appletSave();
910 return;
911 }
912
913 if (amh.getMessage("menuitem.start").equals(label)) {
914 appletStart();
915 return;
916 }
917
918 if (amh.getMessage("menuitem.tag").equals(label)) {
919 appletTag();
920 return;
921 }
922
923 if (amh.getMessage("menuitem.info").equals(label)) {
924 appletInfo();
925 return;
926 }
927
928 if (amh.getMessage("menuitem.encoding").equals(label)) {
929 appletCharacterEncoding();
930 return;
931 }
932
933 if (amh.getMessage("menuitem.edit").equals(label)) {
934 appletEdit();
935 return;
936 }
937
938 if (amh.getMessage("menuitem.print").equals(label)) {
939 appletPrint();
940 return;
941 }
942
943 if (amh.getMessage("menuitem.props").equals(label)) {
944 networkProperties();
945 return;
946 }
947
948 if (amh.getMessage("menuitem.close").equals(label)) {
949 appletClose();
950 return;
951 }
952
953 if (factory.isStandalone() && amh.getMessage("menuitem.quit").equals(label)) {
954 appletQuit();
955 return;
956 }
957 //statusMsgStream.println("evt = " + evt);
958 }
959
960 /**
961 * How many applets are running?
962 */
963
964 public static int countApplets() {
965 return appletPanels.size();
966 }
967
968
969 /**
970 * The current character.
971 */
972 static int c;
973
974 /**
975 * Scan spaces.
976 */
977 public static void skipSpace(Reader in) throws IOException {
978 while ((c >= 0) &&
979 ((c == ' ') || (c == '\t') || (c == '\n') || (c == '\r'))) {
980 c = in.read();
981 }
982 }
983
984 /**
985 * Scan identifier
986 */
987 public static String scanIdentifier(Reader in) throws IOException {
988 StringBuffer buf = new StringBuffer();
989 while (true) {
990 if (((c >= 'a') && (c <= 'z')) ||
991 ((c >= 'A') && (c <= 'Z')) ||
992 ((c >= '0') && (c <= '9')) || (c == '_')) {
993 buf.append((char)c);
994 c = in.read();
995 } else {
996 return buf.toString();
997 }
998 }
999 }
1000
1001 /**
1002 * Scan tag
1003 */
1004 public static Hashtable scanTag(Reader in) throws IOException {
1005 Hashtable atts = new Hashtable();
1006 skipSpace(in);
1007 while (c >= 0 && c != '>') {
1008 String att = scanIdentifier(in);
1009 String val = "";
1010 skipSpace(in);
1011 if (c == '=') {
1012 int quote = -1;
1013 c = in.read();
1014 skipSpace(in);
1015 if ((c == '\'') || (c == '\"')) {
1016 quote = c;
1017 c = in.read();
1018 }
1019 StringBuffer buf = new StringBuffer();
1020 while ((c > 0) &&
1021 (((quote < 0) && (c != ' ') && (c != '\t') &&
1022 (c != '\n') && (c != '\r') && (c != '>'))
1023 || ((quote >= 0) && (c != quote)))) {
1024 buf.append((char)c);
1025 c = in.read();
1026 }
1027 if (c == quote) {
1028 c = in.read();
1029 }
1030 skipSpace(in);
1031 val = buf.toString();
1032 }
1033 //statusMsgStream.println("PUT " + att + " = '" + val + "'");
1034 if (! val.equals("")) {
1035 atts.put(att.toLowerCase(java.util.Locale.ENGLISH), val);
1036 }
1037 while (true) {
1038 if ((c == '>') || (c < 0) ||
1039 ((c >= 'a') && (c <= 'z')) ||
1040 ((c >= 'A') && (c <= 'Z')) ||
1041 ((c >= '0') && (c <= '9')) || (c == '_'))
1042 break;
1043 c = in.read();
1044 }
1045 //skipSpace(in);
1046 }
1047 return atts;
1048 }
1049
1050 /* values used for placement of AppletViewer's frames */
1051 private static int x = 0;
1052 private static int y = 0;
1053 private static final int XDELTA = 30;
1054 private static final int YDELTA = XDELTA;
1055
1056 static String encoding = null;
1057
1058 static private Reader makeReader(InputStream is) {
1059 if (encoding != null) {
1060 try {
1061 return new BufferedReader(new InputStreamReader(is, encoding));
1062 } catch (IOException x) { }
1063 }
1064 InputStreamReader r = new InputStreamReader(is);
1065 encoding = r.getEncoding();
1066 return new BufferedReader(r);
1067 }
1068
1069 /**
1070 * Scan an html file for <applet> tags
1071 */
1072 public static void parse(URL url, String enc) throws IOException {
1073 encoding = enc;
1074 parse(url, System.out, new StdAppletViewerFactory());
1075 }
1076
1077 public static void parse(URL url) throws IOException {
1078 parse(url, System.out, new StdAppletViewerFactory());
1079 }
1080
1081 public static void parse(URL url, PrintStream statusMsgStream,
1082 AppletViewerFactory factory) throws IOException {
1083 // <OBJECT> <EMBED> tag flags
1084 boolean isAppletTag = false;
1085 boolean isObjectTag = false;
1086 boolean isEmbedTag = false;
1087
1088 // warning messages
1089 String requiresNameWarning = amh.getMessage("parse.warning.requiresname");
1090 String paramOutsideWarning = amh.getMessage("parse.warning.paramoutside");
1091 String appletRequiresCodeWarning = amh.getMessage("parse.warning.applet.requirescode");
1092 String appletRequiresHeightWarning = amh.getMessage("parse.warning.applet.requiresheight");
1093 String appletRequiresWidthWarning = amh.getMessage("parse.warning.applet.requireswidth");
1094 String objectRequiresCodeWarning = amh.getMessage("parse.warning.object.requirescode");
1095 String objectRequiresHeightWarning = amh.getMessage("parse.warning.object.requiresheight");
1096 String objectRequiresWidthWarning = amh.getMessage("parse.warning.object.requireswidth");
1097 String embedRequiresCodeWarning = amh.getMessage("parse.warning.embed.requirescode");
1098 String embedRequiresHeightWarning = amh.getMessage("parse.warning.embed.requiresheight");
1099 String embedRequiresWidthWarning = amh.getMessage("parse.warning.embed.requireswidth");
1100 String appNotLongerSupportedWarning = amh.getMessage("parse.warning.appnotLongersupported");
1101
1102 java.net.URLConnection conn = url.openConnection();
1103 Reader in = makeReader(conn.getInputStream());
1104 /* The original URL may have been redirected - this
1105 * sets it to whatever URL/codebase we ended up getting
1106 */
1107 url = conn.getURL();
1108
1109 int ydisp = 1;
1110 Hashtable atts = null;
1111
1112 while(true) {
1113 c = in.read();
1114 if (c == -1)
1115 break;
1116
1117 if (c == '<') {
1118 c = in.read();
1119 if (c == '/') {
1120 c = in.read();
1121 String nm = scanIdentifier(in);
1122 if (nm.equalsIgnoreCase("applet") ||
1123 nm.equalsIgnoreCase("object") ||
1124 nm.equalsIgnoreCase("embed")) {
1125
1126 // We can't test for a code tag until </OBJECT>
1127 // because it is a parameter, not an attribute.
1128 if(isObjectTag) {
1129 if (atts.get("code") == null && atts.get("object") == null) {
1130 statusMsgStream.println(objectRequiresCodeWarning);
1131 atts = null;
1132 }
1133 }
1134
1135 if (atts != null) {
1136 // XXX 5/18 In general this code just simply
1137 // shouldn't be part of parsing. It's presence
1138 // causes things to be a little too much of a
1139 // hack.
1140 factory.createAppletViewer(x, y, url, atts);
1141 x += XDELTA;
1142 y += YDELTA;
1143 // make sure we don't go too far!
1144 Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
1145 if ((x > d.width - 300) || (y > d.height - 300)) {
1146 x = 0;
1147 y = 2 * ydisp * YDELTA;
1148 ydisp++;
1149 }
1150 }
1151 atts = null;
1152 isAppletTag = false;
1153 isObjectTag = false;
1154 isEmbedTag = false;
1155 }
1156 }
1157 else {
1158 String nm = scanIdentifier(in);
1159 if (nm.equalsIgnoreCase("param")) {
1160 Hashtable t = scanTag(in);
1161 String att = (String)t.get("name");
1162 if (att == null) {
1163 statusMsgStream.println(requiresNameWarning);
1164 } else {
1165 String val = (String)t.get("value");
1166 if (val == null) {
1167 statusMsgStream.println(requiresNameWarning);
1168 } else if (atts != null) {
1169 atts.put(att.toLowerCase(), val);
1170 } else {
1171 statusMsgStream.println(paramOutsideWarning);
1172 }
1173 }
1174 }
1175 else if (nm.equalsIgnoreCase("applet")) {
1176 isAppletTag = true;
1177 atts = scanTag(in);
1178 if (atts.get("code") == null && atts.get("object") == null) {
1179 statusMsgStream.println(appletRequiresCodeWarning);
1180 atts = null;
1181 } else if (atts.get("width") == null) {
1182 statusMsgStream.println(appletRequiresWidthWarning);
1183 atts = null;
1184 } else if (atts.get("height") == null) {
1185 statusMsgStream.println(appletRequiresHeightWarning);
1186 atts = null;
1187 }
1188 }
1189 else if (nm.equalsIgnoreCase("object")) {
1190 isObjectTag = true;
1191 atts = scanTag(in);
1192 // The <OBJECT> attribute codebase isn't what
1193 // we want. If its defined, remove it.
1194 if(atts.get("codebase") != null) {
1195 atts.remove("codebase");
1196 }
1197
1198 if (atts.get("width") == null) {
1199 statusMsgStream.println(objectRequiresWidthWarning);
1200 atts = null;
1201 } else if (atts.get("height") == null) {
1202 statusMsgStream.println(objectRequiresHeightWarning);
1203 atts = null;
1204 }
1205 }
1206 else if (nm.equalsIgnoreCase("embed")) {
1207 isEmbedTag = true;
1208 atts = scanTag(in);
1209
1210 if (atts.get("code") == null && atts.get("object") == null) {
1211 statusMsgStream.println(embedRequiresCodeWarning);
1212 atts = null;
1213 } else if (atts.get("width") == null) {
1214 statusMsgStream.println(embedRequiresWidthWarning);
1215 atts = null;
1216 } else if (atts.get("height") == null) {
1217 statusMsgStream.println(embedRequiresHeightWarning);
1218 atts = null;
1219 }
1220 }
1221 else if (nm.equalsIgnoreCase("app")) {
1222 statusMsgStream.println(appNotLongerSupportedWarning);
1223 Hashtable atts2 = scanTag(in);
1224 nm = (String)atts2.get("class");
1225 if (nm != null) {
1226 atts2.remove("class");
1227 atts2.put("code", nm + ".class");
1228 }
1229 nm = (String)atts2.get("src");
1230 if (nm != null) {
1231 atts2.remove("src");
1232 atts2.put("codebase", nm);
1233 }
1234 if (atts2.get("width") == null) {
1235 atts2.put("width", "100");
1236 }
1237 if (atts2.get("height") == null) {
1238 atts2.put("height", "100");
1239 }
1240 printTag(statusMsgStream, atts2);
1241 statusMsgStream.println();
1242 }
1243 }
1244 }
1245 }
1246 in.close();
1247 }
1248
1249 /**
1250 * Old main entry point.
1251 *
1252 * @deprecated
1253 */
1254 @Deprecated
1255 public static void main(String argv[]) {
1256 // re-route everything to the new main entry point
1257 Main.main(argv);
1258 }
1259
1260 private static AppletMessageHandler amh = new AppletMessageHandler("appletviewer");
1261
1262 private static void checkConnect(URL url)
1263 {
1264 SecurityManager security = System.getSecurityManager();
1265 if (security != null) {
1266 try {
1267 java.security.Permission perm =
1268 url.openConnection().getPermission();
1269 if (perm != null)
1270 security.checkPermission(perm);
1271 else
1272 security.checkConnect(url.getHost(), url.getPort());
1273 } catch (java.io.IOException ioe) {
1274 security.checkConnect(url.getHost(), url.getPort());
1275 }
1276 }
1277 }
1278}