001    /*
002     * Created on Jul 10, 2008
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
005     * in compliance with the License. You may obtain a copy of the License at
006     *
007     * http://www.apache.org/licenses/LICENSE-2.0
008     *
009     * Unless required by applicable law or agreed to in writing, software distributed under the License
010     * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
011     * or implied. See the License for the specific language governing permissions and limitations under
012     * the License.
013     *
014     * Copyright @2008-2010 the original author or authors.
015     */
016    package org.fest.swing.applet;
017    
018    import static java.awt.BorderLayout.CENTER;
019    import static java.awt.BorderLayout.SOUTH;
020    import static javax.swing.BorderFactory.*;
021    import static javax.swing.SwingUtilities.invokeLater;
022    import static javax.swing.SwingUtilities.isEventDispatchThread;
023    import static javax.swing.border.BevelBorder.LOWERED;
024    import static org.fest.swing.edt.GuiActionRunner.execute;
025    import static org.fest.util.Strings.concat;
026    
027    import java.applet.Applet;
028    import java.applet.AppletStub;
029    import java.awt.BorderLayout;
030    import java.awt.Dimension;
031    import java.util.Map;
032    
033    import javax.swing.JFrame;
034    import javax.swing.JLabel;
035    
036    import org.fest.swing.annotation.RunsInCurrentThread;
037    import org.fest.swing.annotation.RunsInEDT;
038    import org.fest.swing.edt.GuiQuery;
039    
040    /**
041     * Understands a window that displays an <code>{@link Applet}</code>.
042     * <p>
043     * Typical usage:
044     * <pre>
045     * AppletViewer viewer = AppletViewer.newViewer(new MyApplet());
046     *
047     * // test the applet, viewer can be wrapped with a FrameFixture.
048     * FrameFixture viewerFixture = new FrameFixture(viewer);
049     *
050     * viewer.unloadApplet() // stops and destroys the applet
051     * viewerFixture.cleanUp();
052     * </pre>
053     * <p>
054     * <b>Note:</b> In version 1.2, due to bug <a href="http://jira.codehaus.org/browse/FEST-219" target="_blank">FEST-219</a>
055     * constructors in this class have been replaced with the static factory methods <code>newViewer</code>. It was not
056     * possible to just deprecate them. To ensure correct behavior of the applet viewer, they had to be made unaccessible to
057     * client code.
058     * </p>
059     * </p>
060     *
061     * @author Alex Ruiz
062     * @author Yvonne Wang
063     */
064    public class AppletViewer extends JFrame implements StatusDisplay {
065    
066      private static final long serialVersionUID = 1L;
067    
068      private static final String APPLET_VIEWER_TITLE = "Applet Viewer: ";
069      private static final String APPLET_LOADED_MESSAGE = "Applet loaded";
070      private static final Dimension DEFAULT_SIZE = new Dimension(100, 100);
071    
072      private final JLabel statusLabel = new JLabel();
073    
074      private final Applet applet;
075      private transient AppletStub stub;
076      private boolean loaded;
077    
078      /**
079       * Creates a new </code>{@link AppletViewer}</code>. This factory method creates new instances of
080       * <code>{@link BasicAppletStub}</code> and <code>{@link BasicAppletContext}</code>.
081       * <p>
082       * <b>Note:</b> This method is executed in the event dispatch thread (EDT.)
083       * </p>
084       * @param applet the applet to view.
085       * @return the created <code>AppletViewer</code>.
086       * @throws NullPointerException if <code>applet</code> is <code>null</code>.
087       * @since 1.2
088       */
089      @RunsInEDT
090      public static AppletViewer newViewer(Applet applet) {
091        AppletViewer viewer = createInEDT(applet);
092        viewer.appletStub(new BasicAppletStub(viewer, new BasicAppletContext(viewer)));
093        return viewer;
094      }
095    
096      /**
097       * Creates a new </code>{@link AppletViewer}</code>. This factory method creates new instances of
098       * <code>{@link BasicAppletStub}</code> and <code>{@link BasicAppletContext}</code>.
099       * <p>
100       * <b>Note:</b> This method is executed in the event dispatch thread (EDT.)
101       * </p>
102       * @param applet the applet to view.
103       * @param parameters the parameters included in an applet HTML tag.
104       * @return the created <code>AppletViewer</code>.
105       * @throws NullPointerException if <code>applet</code> is <code>null</code>.
106       * @throws NullPointerException if <code>parameters</code> is <code>null</code>.
107       * @since 1.2
108       */
109      @RunsInEDT
110      public static AppletViewer newViewer(Applet applet, Map<String, String> parameters) {
111        AppletViewer viewer = createInEDT(applet);
112        viewer.appletStub(new BasicAppletStub(viewer, new BasicAppletContext(viewer), parameters));
113        return viewer;
114      }
115    
116    
117      /**
118       * Creates a new </code>{@link AppletViewer}</code>.
119       * <p>
120       * <b>Note:</b> This method is executed in the event dispatch thread (EDT.)
121       * </p>
122       * @param applet the applet to view.
123       * @param stub the applet's stub.
124       * @return the created <code>AppletViewer</code>.
125       * @throws NullPointerException if <code>applet</code> is <code>null</code>.
126       * @throws NullPointerException if <code>stub</code> is <code>null</code>.
127       * @since 1.2
128       */
129      @RunsInEDT
130      public static AppletViewer newViewer(Applet applet, AppletStub stub) {
131        AppletViewer viewer = createInEDT(applet);
132        viewer.appletStub(stub);
133        return viewer;
134      }
135    
136      @RunsInEDT
137      private static AppletViewer createInEDT(final Applet applet) {
138        return execute(new GuiQuery<AppletViewer>() {
139          protected AppletViewer executeInEDT() {
140            return new AppletViewer(applet);
141          }
142        });
143      }
144    
145      private AppletViewer(Applet applet) {
146        if (applet == null) throw new NullPointerException("The applet to load should not be null");
147        this.applet = applet;
148        setUpFrame();
149        addContent();
150      }
151    
152      private void setUpFrame() {
153        setTitle(concat(APPLET_VIEWER_TITLE, applet.getClass().getName()));
154        setSize(DEFAULT_SIZE);
155        setLayout(new BorderLayout());
156      }
157    
158      private void addContent() {
159        add(applet, CENTER);
160        statusLabel.setBorder(createCompoundBorder(createBevelBorder(LOWERED), createEmptyBorder(2, 5, 2, 5)));
161        statusLabel.setName("status");
162        add(statusLabel, SOUTH);
163      }
164    
165      private void appletStub(AppletStub newAppletStub) {
166        if (newAppletStub == null) throw new NullPointerException("The AppletStub should not be null");
167        stub = newAppletStub;
168        applet.setStub(stub);
169        setUpApplet();
170      }
171    
172      private void setUpApplet() {
173        loadApplet();
174        showStatus(APPLET_LOADED_MESSAGE);
175      }
176    
177      /**
178       * Initializes and starts the applet in this viewer.
179       */
180      public void reloadApplet() {
181        if (loaded) unloadApplet();
182        loadApplet();
183      }
184    
185      private void loadApplet() {
186        applet.init();
187        applet.start();
188        loaded = true;
189      }
190    
191      /**
192       * Stops and destroys the applet loaded in this viewer. This method should be called before closing or disposing this
193       * viewer.
194       */
195      public void unloadApplet() {
196        applet.stop();
197        applet.destroy();
198        loaded = false;
199      }
200    
201      /**
202       * Indicates whether the applet in this viewer is loaded or not.
203       * @return <code>true</code> if this applet is loaded, <code>false</code> otherwise.
204       */
205      public boolean appletLoaded() { return loaded; }
206    
207      /**
208       * Displays the given status message. This method is executed in the event dispatch thread (EDT.)
209       * @param status the status to display.
210       */
211      @RunsInEDT
212      public void showStatus(final String status) {
213        if (isEventDispatchThread()) {
214          setStatus(status);
215          return;
216        }
217        invokeLater(new Runnable() {
218          public void run() {
219            setStatus(status);
220          }
221        });
222      }
223    
224      @RunsInCurrentThread
225      private void setStatus(String status) {
226        statusLabel.setText(status);
227      }
228    
229      /**
230       * Returns the applet displayed in this viewer.
231       * @return the applet displayed in this viewer.
232       */
233      public Applet applet() {
234        return applet;
235      }
236    
237      /**
238       * Returns the <code>{@link AppletStub}</code> in this viewer.
239       * @return the <code>AppletStub</code> in this viewer.
240       */
241      public AppletStub stub() {
242        return stub;
243      }
244    }