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