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