1 /*
   2  * Copyright (c) 1995, 2008, Oracle and/or its affiliates. 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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.applet;
  27 
  28 import java.util.*;
  29 import java.io.*;
  30 import java.awt.*;
  31 import java.awt.event.*;
  32 import java.awt.print.*;
  33 import javax.print.attribute.*;
  34 import java.applet.*;
  35 import java.net.URL;
  36 import java.net.MalformedURLException;
  37 import java.net.SocketPermission;
  38 import sun.misc.Ref;
  39 import java.security.AccessController;
  40 import java.security.PrivilegedAction;
  41 import java.lang.reflect.InvocationTargetException;
  42 import java.lang.reflect.Method;
  43 import sun.awt.SunToolkit;
  44 import sun.awt.AppContext;
  45 import java.lang.ref.WeakReference;
  46 
  47 /**
  48  * A frame to show the applet tag in.
  49  */
  50 class 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 
  96 class 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  */
 119 public 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                        Integer.toString(d.width - (in.left + in.right)));
 591         panel.atts.put("height",
 592                        Integer.toString(d.height - (in.top + in.bottom)));
 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 (FileOutputStream fos = new FileOutputStream(file);
 672                      BufferedOutputStream bos = new BufferedOutputStream(fos);
 673                      ObjectOutputStream os = new ObjectOutputStream(bos)) {
 674 
 675                     showStatus(amh.getMessage("appletsave.err1", 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 }