Clover coverage report - clover
Coverage timestamp: Sat Oct 8 2005 22:54:17 EDT
file stats: LOC: 3,283   Methods: 264
NCLOC: 2,724   Classes: 54
 
 Source file Conditionals Statements Methods TOTAL
ScriptEditor.java 16.4% 30.5% 36.7% 27.6%
coverage coverage
 1   
 package abbot.editor;
 2   
 
 3   
 import java.awt.*;
 4   
 import java.awt.event.*;
 5   
 import java.io.*;
 6   
 import java.lang.reflect.*;
 7   
 import java.net.URL;
 8   
 import java.util.*;
 9   
 import java.util.List;
 10   
 
 11   
 import javax.swing.*;
 12   
 import javax.swing.filechooser.FileFilter;
 13   
 import javax.swing.event.*;
 14   
 
 15   
 import junit.extensions.abbot.*;
 16   
 import junit.runner.TestCollector;
 17   
 import abbot.BugReport;
 18   
 import abbot.Log;
 19   
 import abbot.ExitException;
 20   
 import abbot.NoExitSecurityManager;
 21   
 import abbot.AssertionFailedError;
 22   
 import abbot.Platform;
 23   
 import abbot.editor.actions.*;
 24   
 import abbot.editor.editors.*;
 25   
 import abbot.editor.recorder.*;
 26   
 import abbot.editor.widgets.*;
 27   
 import abbot.editor.widgets.TextField;
 28   
 import abbot.finder.*;
 29   
 import abbot.i18n.Strings;
 30   
 import abbot.script.*;
 31   
 import abbot.script.Action;
 32   
 import abbot.script.Resolver;
 33   
 import abbot.tester.*;
 34   
 import abbot.tester.Robot;
 35   
 import abbot.util.*;
 36   
 import abbot.util.Properties;
 37   
 import abbot.util.ThreadTerminatingSecurityManager.ThreadTerminatedException;
 38   
 
 39   
 import com.apple.mrj.*;
 40   
 
 41   
 /**
 42   
  * This is the 'model' behind the script editor UI.<p>
 43   
  *
 44   
  * Acts as a resolver, using the currently in-context script as the component
 45   
  * resolver. <p>
 46   
  */
 47   
 
 48   
 /* To add new actions, add the action to the list in initActions(),
 49   
  * and optionally add it to the menu layout in initMenus.  Define a name for
 50   
  * it in EditorConstants, and an inner Action class for it which uses that
 51   
  * name. 
 52   
  */
 53   
 // Apologies for the extreme cruftiness and lack of proper factoring.  This
 54   
 // was written at the same time as the underlying framework, and refactored
 55   
 // (sort of) into model/view at the same time, so it's hardly a shining
 56   
 // example of clean design.  Don't know if it would have been any better
 57   
 // written TDD, though.
 58   
 public class ScriptEditor 
 59   
     implements ActionListener, Resolver, EditorConstants {
 60   
 
 61   
     private static int selectKey;
 62   
     private static int captureKey;
 63   
     private static int captureImageKey;
 64   
 
 65   
     static {
 66  2
         try {
 67  2
             new EventExceptionHandler().install();
 68   
         }
 69   
         catch(Exception e) {
 70   
             // Ignore for now
 71   
         }
 72  2
         String key = System.getProperty("abbot.editor.select_key", "F1");
 73  2
         selectKey = KeyStroke.getKeyStroke(key).getKeyCode();
 74  2
         key = System.getProperty("abbot.editor.capture_key", "F2");
 75  2
         captureKey = KeyStroke.getKeyStroke(key).getKeyCode();
 76  2
         key = System.getProperty("abbot.editor.capture_image_key", "F3");
 77  2
         captureImageKey = KeyStroke.getKeyStroke(key).getKeyCode();
 78   
     }
 79   
 
 80   
     // if set, log all events, even those going to filtered components
 81   
     private static final boolean LOG_ALL_EVENTS =
 82   
         Boolean.getBoolean("abbot.editor.log_all_events");
 83   
     private static final long FIXTURE_EVENT_MASK =
 84   
         Long.getLong("abbot.fixture.event_mask", 
 85   
                     EventRecorder.RECORDING_EVENT_MASK).longValue();
 86   
 
 87   
     /** Key to use to invert an assertion/wait. */
 88   
     public static final int KC_INVERT = KeyEvent.VK_SHIFT;
 89   
     /** Key to use to insert a wait instead of an assertion.   Use option key
 90   
         on mac, control key anywhere else. */
 91   
     public static final int KC_WAIT =
 92  2
         Platform.isMacintosh() ? KeyEvent.VK_ALT : KeyEvent.VK_CONTROL;
 93   
     /** Flag for informational status. */
 94   
     private static final int INFO = 0;
 95   
     /** Flag to indicate a warning. */
 96   
     private static final int WARN = 1;
 97   
     /** Flag to indicate an error. */
 98   
     private static final int ERROR = 2;
 99   
     /** Flag to indicate a script failure. */
 100   
     private static final int FAILURE = 3;
 101   
     /** Prefixes for different types of status messages. */
 102   
     private static final String[] statusFormat =
 103   
         { "Normal", "Warning", "Error", "Failure" };
 104   
     private static final Color[] statusColor = {
 105   
         Color.black,
 106   
         Color.orange.darker(),
 107   
         Color.red,
 108   
         Color.red,
 109   
     };
 110   
 
 111   
     private ArrayList insertActions = new ArrayList();
 112   
     private ArrayList assertActions = new ArrayList();
 113   
     private ArrayList waitActions = new ArrayList();
 114   
     private ArrayList captureActions = new ArrayList();
 115   
 
 116   
     /** Adapter for representing the script itself, providing access to
 117   
      * individual script steps.
 118   
      */
 119   
     private ScriptModel scriptModel;
 120   
     private ScriptTable scriptTable;
 121   
     private EditorSecurityManager securityManager;
 122   
     private SecurityManager oldSecurityManager;
 123   
 
 124   
     private int nonce;
 125   
     /** Keep all application under test threads in the same group to make them
 126   
      * easier to track. */
 127   
     private ThreadGroup appGroup;
 128   
     private TestHierarchy hierarchy;
 129   
     private Hierarchy oldHierarchy;
 130   
     private Recorder[] recorders;
 131   
     private java.util.List savedStateWhileRecording;
 132   
     /** Allow exits from anywhere until the editor is fully initialized. */
 133   
     private boolean rootIsExiting = true;
 134   
     private boolean exiting;
 135   
     private boolean hiding;
 136   
     private boolean ignoreStepEvents;
 137   
     /** Whether to ignore incoming AWT events. */
 138   
     private boolean ignoreEvents;
 139   
     /** Is there a script or launch step currently running? */
 140   
     private boolean isScriptRunning;
 141   
     /** When was some portion of the app exercised? */
 142   
     private long lastLaunchTime;
 143   
     /** Are we trying to capture an image? */
 144   
     private boolean capturingImage;
 145   
     /** What component is currently "selected" for capture? */
 146   
     private Component captureComponent;
 147   
     private Component innermostCaptureComponent;
 148   
     private Highlighter highlighter;
 149   
     /** Is this the first editor launched, or one under test? */
 150   
     private boolean isRootEditor = true;
 151   
     /** AWT input state. */
 152   
     private static InputState state = Robot.getState();
 153   
     /** Generic filter to select a test script. */
 154   
     private ScriptFilter filter = new ScriptFilter();
 155   
 
 156   
     /** Current test case class (should derive from AWTTestCase). */
 157   
     private Class testClass;
 158   
     /** Current test suite.  */
 159   
     private ScriptTestSuite testSuite;
 160   
     /** Current test script. */
 161   
     private Script testScript;
 162   
     /** Is the current script a temporary placeholder? */
 163   
     private File tempFile;
 164   
     /** Runner used to execute the script. */
 165   
     private StepRunner runner;
 166   
     /** Current set of scripts, based on the test suite (if any). */
 167   
     private List testScriptList;
 168   
     /** Currently selected component.  Note that this may be a dummy
 169   
      * component.
 170   
      */
 171   
     private Component selectedComponent;
 172   
     /** Currently selected reference, if any. */
 173   
     private ComponentReference selectedReference;
 174   
 
 175   
     /** Are we currently recording events? */
 176   
     private boolean recording;
 177   
     /** The current recorder to pass events for capture. */
 178   
     private Recorder recorder;
 179   
     /** Since recorder starts with a key release, and stops with a key press,
 180   
         make sure we don't start immediately after stopping.
 181   
     */
 182   
     private boolean justStoppedRecording;
 183   
     /** Need to be able to set the combo box selection w/o reacting to the
 184   
      * resulting posted action.
 185   
      */
 186   
     private boolean ignoreComboBox;
 187   
     /** Where to stop. */
 188   
     private Step stopStep;
 189   
 
 190   
     // GUI components
 191   
     private JFileChooser chooser;
 192   
     private ScriptEditorFrame view;
 193   
     private ComboBoxModel model;
 194   
 
 195   
     private boolean invertAssertions;
 196   
     private boolean waitAssertions;
 197   
     private ActionMap actionMap;
 198   
     private String name;
 199   
 
 200   
     /**
 201   
      * Constructs a ScriptEditor which handles script editing logic.
 202   
      * ScriptEditorFrame provides the view/controller.
 203   
      * @see ScriptEditorFrame
 204   
      */
 205  2
     public ScriptEditor() {
 206   
 
 207  2
         if (Boolean.getBoolean("abbot.framework.launched")) {
 208  0
             isRootEditor = false;
 209  0
             name = "Script Editor (under test)";
 210   
         }
 211   
         else {
 212  2
             System.setProperty("abbot.framework.launched", "true");
 213  2
             name = "Script Editor (root)";
 214   
         }
 215   
 
 216   
         // TODO: clean this up
 217  2
         actionMap = initActions();
 218  2
         hierarchy = initContext(isRootEditor);
 219  2
         recorders = initRecorders();
 220  2
         view = initFrame(isRootEditor);
 221  2
         hierarchy.setFiltered(view, true);
 222  2
         updateDynamicActions(view);
 223   
 
 224  2
         view.setComponentBrowser(createComponentBrowser());
 225  2
         addEventHandlers(view);
 226   
 
 227   
         // Clear the status only if there were no errors
 228  2
         if (view.getStatus().equals(Strings.get("Initializing"))) {
 229  2
             setStatus(Strings.get("Ready"));
 230   
         }
 231  2
         rootIsExiting = false;
 232   
     }
 233   
 
 234   
     /** Provides a convenient menu setup definition.
 235   
         Use a defined action name to indicate that action's place within the
 236   
         menu.  Null values indicate menu separators.
 237   
      */
 238  2
     private String[][] initMenus() {
 239  2
         ArrayList fileMenu = new ArrayList();
 240  2
         ArrayList helpMenu = new ArrayList();
 241  2
         fileMenu.addAll(Arrays.asList(new String[] {
 242   
             MENU_FILE,
 243   
             ACTION_SCRIPT_NEW,
 244   
             ACTION_SCRIPT_DUPLICATE,
 245   
             ACTION_SCRIPT_OPEN,
 246   
             null,
 247   
             ACTION_SCRIPT_SAVE,
 248   
             ACTION_SCRIPT_SAVE_AS,
 249   
             ACTION_SCRIPT_RENAME,
 250   
             ACTION_SCRIPT_CLOSE,
 251   
             null,
 252   
             ACTION_SCRIPT_DELETE,
 253   
         }));
 254   
 
 255  2
         helpMenu.add(MENU_HELP);
 256  2
         if (!Platform.isOSX()) {
 257  2
             fileMenu.add(null);
 258  2
             fileMenu.add(ACTION_EDITOR_QUIT);
 259  2
             helpMenu.add(ACTION_EDITOR_ABOUT);
 260   
         }
 261  2
         helpMenu.addAll(Arrays.asList(new String[] {
 262   
             ACTION_EDITOR_USERGUIDE,
 263   
             ACTION_EDITOR_WEBSITE,
 264   
             ACTION_EDITOR_EMAIL,
 265   
             ACTION_EDITOR_BUGREPORT,
 266   
         }));
 267   
 
 268  2
         return new String[][] {
 269   
             (String[])fileMenu.toArray(new String[fileMenu.size()]),
 270   
             {
 271   
                 MENU_EDIT,
 272   
                 ACTION_STEP_CUT,
 273   
                 null,
 274   
                 ACTION_STEP_MOVE_UP,
 275   
                 ACTION_STEP_MOVE_DOWN,
 276   
                 ACTION_STEP_GROUP,
 277   
                 null,
 278   
                 ACTION_SELECT_COMPONENT,
 279   
                 null,
 280   
                 ACTION_SCRIPT_CLEAR,
 281   
             },
 282   
             {
 283   
                 MENU_TEST,
 284   
                 ACTION_RUN,
 285   
                 ACTION_RUN_TO,
 286   
                 ACTION_RUN_SELECTED,
 287   
                 null,
 288   
                 ACTION_EXPORT_HIERARCHY,
 289   
                 null,
 290   
                 ACTION_RUN_LAUNCH,
 291   
                 ACTION_RUN_TERMINATE,
 292   
                 null,
 293   
                 ACTION_TOGGLE_STOP_ON_FAILURE,
 294   
                 ACTION_TOGGLE_STOP_ON_ERROR,
 295   
                 ACTION_TOGGLE_FORKED,
 296   
                 ACTION_GET_VMARGS,
 297   
                 ACTION_TOGGLE_SLOW_PLAYBACK,
 298   
                 ACTION_TOGGLE_AWT_MODE,
 299   
             },
 300   
             {
 301   
                 MENU_INSERT,
 302   
                 ACTION_INSERT_ANNOTATION,
 303   
                 ACTION_INSERT_APPLET,
 304   
                 ACTION_INSERT_CALL,
 305   
                 ACTION_INSERT_COMMENT,
 306   
                 ACTION_INSERT_EXPRESSION,
 307   
                 ACTION_INSERT_LAUNCH,
 308   
                 ACTION_INSERT_FIXTURE,
 309   
                 ACTION_INSERT_SAMPLE,
 310   
                 ACTION_INSERT_SCRIPT,
 311   
                 ACTION_INSERT_SEQUENCE,
 312   
                 ACTION_INSERT_TERMINATE,
 313   
             },
 314   
             {
 315   
                 MENU_CAPTURE,
 316   
             },
 317   
             (String[])helpMenu.toArray(new String[helpMenu.size()]),
 318   
         };
 319   
     }
 320   
 
 321   
     // All editor actions should be defined here
 322  2
     private ActionMap initActions() {
 323  2
         javax.swing.Action[] actions = {
 324   
             new EditorAboutAction(),
 325   
             new EditorEmailAction(),
 326   
             new EditorBugReportAction(),
 327   
             new EditorWebsiteAction(),
 328   
             new EditorUserGuideAction(),
 329   
             new EditorQuitAction(),
 330   
             new ScriptOpenAction(),
 331   
             new ScriptNewAction(),
 332   
             new ScriptDuplicateAction(),
 333   
             new ScriptSaveAction(),
 334   
             new ScriptSaveAsAction(),
 335   
             new ScriptRenameAction(),
 336   
             new ScriptCloseAction(),
 337   
             new ScriptDeleteAction(),
 338   
             new ScriptClearAction(),
 339   
             new StepCutAction(),
 340   
             new StepMoveUpAction(),
 341   
             new StepMoveDownAction(),
 342   
             new StepGroupAction(),
 343   
             new RunAction(),
 344   
             new RunToAction(),
 345   
             new RunSelectedAction(),
 346   
             new RunLaunchAction(),
 347   
             new RunTerminateAction(),
 348   
             new GetVMArgsAction(),
 349   
             new SelectTestSuiteAction(),
 350   
             new ExportHierarchyAction(),
 351   
             new ToggleForkedAction(),
 352   
             new InsertLaunchAction(),
 353   
             new InsertFixtureAction(),
 354   
             new InsertAppletAction(),
 355   
             new InsertTerminateAction(),
 356   
             new InsertCallAction(),
 357   
             new InsertSampleAction(),
 358   
             new InsertSequenceAction(),
 359   
             new InsertScriptAction(),
 360   
             new InsertCommentAction(),
 361   
             new InsertExpressionAction(),
 362   
             new InsertAnnotationAction(),
 363   
             new ToggleStopOnFailureAction(),
 364   
             new ToggleStopOnErrorAction(),
 365   
             new ToggleSlowPlaybackAction(),
 366   
             new ToggleAWTModeAction(),
 367   
             new CaptureImageAction(),
 368   
             new CaptureComponentAction(),
 369   
             new SelectComponentAction(),
 370   
         };
 371  2
         ActionMap map = new ActionMap();
 372  2
         for (int i=0;i < actions.length;i++) {
 373  92
             Object key = actions[i].getValue(EditorAction.ACTION_KEY);
 374  92
             map.put(key, actions[i]);
 375   
         }
 376  2
         return map;
 377   
     }
 378   
 
 379   
     /**
 380   
      *  Add event handlers to their respective components
 381   
      */
 382  2
     private void addEventHandlers(final ScriptEditorFrame view) {
 383  2
         scriptTable.getSelectionModel().
 384   
             addListSelectionListener(new ScriptTableSelectionHandler());
 385  2
         scriptTable.addMouseListener(new MouseAdapter() {
 386  0
             public void mouseClicked(MouseEvent me) {
 387  0
                 if ((me.getModifiers() & MouseEvent.BUTTON1_MASK) != 0) {
 388  0
                     if (view.getEditor() == null)
 389  0
                         setStepEditor();
 390   
                 }
 391   
             }
 392   
         });
 393  2
         MouseListener ml = new MouseAdapter() {
 394  0
             public void mouseClicked(MouseEvent me) {                          
 395  0
                 if ((me.getModifiers() & MouseEvent.BUTTON1_MASK) != 0) {
 396  0
                     int size = scriptTable.getRowCount();            
 397  0
                     scriptTable.clearSelection();                    
 398  0
                     scriptTable.setCursorLocation(size); 
 399   
                 }                                                              
 400   
             }                                                                  
 401   
         };
 402  2
         view.addMouseListener(ml);
 403  2
         view.getTestScriptSelector().addItemListener(new ScriptSelectorItemHandler());
 404  2
         view.addWindowListener(new WindowAdapter() {
 405  0
             public void windowClosing(WindowEvent e) {
 406  0
                 quitApplication();
 407   
             }
 408   
         });
 409  2
         if (Platform.isOSX()) {
 410   
             // Mac has it's own dedicated Quit/About menu items
 411  0
             MRJApplicationUtils.registerQuitHandler(new MRJQuitHandler() {
 412  0
                 public void handleQuit() {
 413  0
                     quitApplication();
 414   
                 }
 415   
             });
 416  0
             MRJApplicationUtils.registerAboutHandler(new MRJAboutHandler() {
 417  0
                 public void handleAbout() {
 418  0
                     view.showAboutBox();
 419   
                 }
 420   
             });
 421   
         }
 422   
     }
 423   
 
 424   
     /**
 425   
      * Determines if the editor is testing itself and initializes the 
 426   
      * security manager accordingly.  
 427   
      */
 428  2
     private TestHierarchy initContext(boolean isRoot) {
 429  2
         TestHierarchy hierarchy = new TestHierarchy() {
 430   
             private String desc = "Test hierarchy for " + name;
 431  0
             public String toString() {
 432  0
                 return desc;
 433   
             }
 434   
         };
 435   
         // eventually this should go away; all hierarchy usage should be
 436   
         // explicit. 
 437  2
         oldHierarchy = AWTHierarchy.getDefault();
 438  2
         if (isRoot) {
 439  2
             AWTHierarchy.setDefault(hierarchy);
 440  2
             initSecurityManager();
 441  2
             try {
 442  2
                 new EventExceptionHandler().install();
 443   
             }
 444   
             catch(Exception e) {
 445  0
                 Log.warn(e);
 446   
             }
 447   
         }
 448  2
         return hierarchy;
 449   
     }
 450   
 
 451  2
     private Recorder[] initRecorders() {
 452   
         // Use the editor as the resolver, since the actual resolver will
 453   
         // be the currently scoped script.
 454  2
         Recorder[] recorders = new Recorder[] {
 455   
             new EventRecorder(this, false),
 456   
             new EventRecorder(this, true),
 457   
         };
 458   
 
 459  2
         ActionListener recorderListener = new ActionListener() {
 460  0
             public void actionPerformed(final ActionEvent event) {
 461  0
                 setStatus(event.getActionCommand());
 462   
             }
 463   
         };
 464  2
         for (int i=0;i < recorders.length;i++) {
 465  4
             recorders[i].addActionListener(recorderListener);
 466   
         }
 467  2
         return recorders;
 468   
     }
 469   
 
 470   
     /** Initialize the primary editor frame. */
 471  2
     private ScriptEditorFrame initFrame(final boolean isRoot) {
 472   
 
 473  2
         scriptModel = new ScriptModel() {
 474  0
             public boolean isCellEditable(int row, int col) {
 475  0
                 return false;
 476   
             }
 477   
         };
 478  2
         runner = new EditorStepRunner();
 479  2
         runner.setTerminateOnError(false);
 480  2
         runner.addStepListener(new StepListener() {
 481  0
             public void stateChanged(StepEvent ev) {
 482  0
                 if (ignoreStepEvents)
 483  0
                     return;
 484  0
                 reflectScriptExecutionState(ev);
 485   
             }
 486   
         });
 487   
         // Customize the ScriptTable so we can vary the color of any given
 488   
         // step based on our last run status (of which the table itself should
 489   
         // be ignorant).
 490  2
         scriptTable = new ScriptTable(scriptModel) {
 491  0
             public Color getStepColor(Step step, boolean selected) {
 492  0
                 Color color = super.getStepColor(step, selected);
 493   
                 // Make the stop step appear a different color
 494  0
                 if (step == stopStep) {
 495  0
                     Color stopColor = getSelectionBackground().darker();
 496  0
                     color = stopColor;
 497   
                 }
 498  0
                 Throwable thr = runner.getError(step);
 499  0
                 if (thr != null) {
 500  0
                     Color tint = (thr instanceof AssertionFailedError)
 501   
                         ? statusColor[ScriptEditor.FAILURE]
 502   
                         : statusColor[ScriptEditor.ERROR];
 503  0
                     if (step instanceof Sequence) {
 504  0
                         color = color.brighter();
 505   
                     }
 506  0
                     if (selected) {
 507  0
                         color = mixColors(color, tint);
 508   
                     }
 509   
                     else {
 510  0
                         color = tint;
 511   
                     }
 512   
                 }
 513  0
                 return color;
 514   
             }
 515   
         };
 516   
 
 517   
         // Override default "cut" action in table
 518  2
         ActionMap amap = scriptTable.getActionMap();
 519  2
         amap.put("cut", actionMap.get(ACTION_STEP_CUT));
 520  2
         InputMap imap = scriptTable.getInputMap();
 521  2
         imap.put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0), "toggle");
 522   
 
 523   
         // Only allow the script editor to dispose of the frame
 524  2
         String prefFile = Preferences.PROPS_FILENAME;
 525  2
         if (!isRoot) {
 526  0
             prefFile += ".tmp";
 527   
         }
 528  2
         Preferences prefs = new Preferences(prefFile);
 529  2
         String title = Strings.get("ScriptEditor.title",
 530   
                                    new Object[] {""});
 531  2
         ScriptEditorFrame f = new ScriptEditorFrame(initMenus(),
 532   
                                                     actionMap, this,
 533   
                                                     title,
 534   
                                                     scriptTable, prefs) {
 535   
             /** Don't allow code under test to hide the editor. */
 536  6
             public void hide() {
 537  6
                 if (hiding || isDisposeFromRootEditor()) {
 538  2
                     hiding = false;
 539  2
                     super.hide();
 540   
                 }
 541   
             }
 542  4
             public void dispose() {
 543   
                 // Need to prevent arbitrary disposal by the code under test.
 544   
                 // Only allow the dispose if one of the following is true:
 545   
                 // a) we triggered it (exiting == true)
 546   
                 // b) the root script editor is disposing us
 547  4
                 if (exiting || isDisposeFromRootEditor()) {
 548  4
                     super.dispose();
 549   
                 }
 550   
             }
 551  546
             public String getName() {
 552  546
                 String name = super.getName();
 553  546
                 if (isRoot)
 554  491
                     name += " (root)";
 555  546
                 return name;
 556   
             }
 557   
         };
 558  2
         return f;
 559   
     }
 560   
 
 561   
     /** Provide a color that is a mix of the two given colors. */
 562  0
     private Color mixColors(Color c1, Color c2) {
 563  0
         return new Color((c1.getRed() + c2.getRed())/2,
 564   
                          (c1.getGreen() + c2.getGreen())/2,
 565   
                          (c1.getBlue() + c2.getBlue())/2);
 566   
     }
 567   
 
 568   
     /** Return whether the root editor disposed of this instance. */
 569  4
     private boolean isDisposeFromRootEditor() {
 570   
         // FIXME cf how applets prevent disposal of embedded frame
 571   
         // AWTHierarchy surrounds disposal calls with the property
 572   
         // abbot.finder.disposal set to "true"
 573  4
         return !isRootEditor && Boolean.getBoolean("abbot.finder.disposal");
 574   
     }
 575   
 
 576  4
     private void createAsserts(ArrayList list, ComponentTester tester,
 577   
                                boolean wait) {
 578  4
         list.clear();
 579  4
         Method[] methods = tester.getAssertMethods();
 580  4
         for (int i = 0; i < methods.length; i++) {
 581  8
             list.add(new TesterMethodAction(tester, methods[i], wait));
 582   
         }
 583  4
         methods = tester.getComponentAssertMethods();
 584  4
         if (list.size() != 0 && methods.length != 0)
 585  4
             list.add(null);
 586  4
         for (int i = 0; i < methods.length; i++) {
 587  4
             list.add(new TesterMethodAction(tester, methods[i], wait));
 588   
         }
 589  4
         methods = tester.getPropertyMethods();
 590  4
         if (list.size() != 0 && methods.length != 0)
 591  0
             list.add(null);
 592  4
         for (int i = 0; i < methods.length; i++) {
 593  0
             String name = methods[i].getName();
 594  0
             if (name.startsWith("is"))
 595  0
                 name = name.substring(2);
 596  0
             else if (name.startsWith("get") || name.startsWith("has"))
 597  0
                 name = name.substring(3);
 598  0
             list.add(new TesterMethodAction(tester, methods[i], wait));
 599   
         }
 600   
     }
 601   
 
 602   
     /** Return a script step encapsulating an image comparison.  
 603   
      * Assumes the script context is not null.
 604   
      */
 605  0
     private Step captureComponentImage(Component comp) {
 606  0
         Step step = null;
 607  0
         ComponentTester tester = ComponentTester.getTester(comp);
 608  0
         java.awt.image.BufferedImage img =
 609   
             tester.capture(comp, !(comp instanceof Window));
 610  0
         try {
 611   
             // Save the image file relative to the current context
 612  0
             ComponentReference ref = addComponent(comp);
 613  0
             File scriptFile = ((Script)getResolverContext()).getFile();
 614  0
             File newFile = new File(getResolverContext().getDirectory(),
 615   
                                     scriptFile.getName()
 616   
                                     + "-" + ref.getID()
 617   
                                     + ".jpg");
 618  0
             int index = 1;
 619  0
             while (newFile.exists()) {
 620  0
                 newFile = new File(getResolverContext().getDirectory(),
 621   
                                    scriptFile.getName()
 622   
                                    + "-" + ref.getID()
 623   
                                    + "-" + index++ + ".jpg");
 624   
             }
 625  0
             ImageComparator.writeJPEG(newFile, img);
 626   
             // Note that the pathname is saved relative to the script
 627   
             // context. 
 628  0
             step = new Assert(getResolverContext(), null,
 629   
                               ComponentTester.class.getName(),
 630   
                               "assertImage", 
 631   
                               new String[] {
 632   
                                   ref.getID(), newFile.getName(), "true"
 633   
                               },
 634   
                               "true", false);
 635   
         }
 636   
         catch(IOException io) {
 637  0
             Log.warn(io);
 638   
         }
 639  0
         return step;
 640   
     }
 641   
 
 642   
     /** Start recording, launching the code under test if necessary. */
 643  0
     private void startRecording(Recorder rec) {
 644  0
         Log.debug("Starting recorder");
 645  0
         boolean noWindows = countShowingWindows(null) == 0;
 646  0
         boolean canLaunch = testScript != null
 647   
             && testScript.hasLaunch() && !isAppLaunched();
 648  0
         if (noWindows && !canLaunch) {
 649   
             // Can't launch, and there are no windows to
 650   
             // record on, so show a warning
 651  0
             view.showError(Strings.get("NoWindows.title"),
 652   
                            Strings.get("NoWindows"));
 653   
         }
 654   
         else {
 655  0
             if (recorder != null)
 656  0
                 stopRecording(true);
 657  0
             Log.debug("Now recording with " + rec);
 658  0
             recording = true;
 659  0
             recorder = rec;
 660   
             
 661  0
             setStatus("Please wait...");
 662   
             // Disable the UI while recording; we re-enable the frame itself
 663   
             // to avoid being unable to click on it later (w32, 1.4.1)
 664  0
             savedStateWhileRecording = AWT.disableHierarchy(view);
 665  0
             view.setEnabled(true);
 666   
             // get us out of the way
 667   
             // FIXME this puts us WAY back on linux; this is only a
 668   
             // problem in that the status bar is often hidden
 669   
             // Maybe make a floating status while the recorder is
 670   
             // running. 
 671   
             // Only go back if the app is already up, otherwise the
 672   
             // about-to-be-launched app is hidden.
 673  0
             if (!noWindows)
 674  0
                 view.toBack();
 675  0
             recorder.start();
 676   
 
 677  0
             if (noWindows) {
 678  0
                 launch(false);
 679   
             }
 680   
         }
 681   
     }
 682   
 
 683   
     /** Stop recording and update the recorder actions' state. */
 684  0
     private void stopRecording(boolean discardRecording) {
 685  0
         Log.debug("Stopping recorder");
 686  0
         recording = false;
 687  0
         int type = INFO;
 688  0
         String extended = null;
 689  0
         String status = Strings.get(discardRecording
 690   
                                     ? "RecorderCanceled"
 691   
                                     : "RecorderFinished");
 692  0
         try {
 693  0
             recorder.terminate();
 694   
         }
 695   
         catch(RecordingFailedException e) {
 696  0
             String msg = Strings.get("editor.recording.stop_failure");
 697  0
             Throwable error = e.getReason() instanceof BugReport
 698   
                 ? e.getReason() : new BugReport(msg, e.getReason());
 699  0
             Log.log("Recording stop failure: " + error.toString());
 700  0
             view.showWarning(msg);
 701  0
             status = error.getMessage();
 702  0
             extended = error.toString();
 703  0
             type = ERROR;
 704   
         }
 705  0
         try {
 706  0
             if (!discardRecording) {
 707  0
                 Step step = recorder.getStep();
 708   
                 // Ignore empty results
 709  0
                 if (!(step instanceof Sequence 
 710   
                       && ((Sequence)step).size() == 0)) {
 711  0
                     addStep(step);
 712   
                 }
 713   
             }
 714   
         }
 715   
         finally {
 716  0
             recorder = null;
 717  0
             AWT.reenableHierarchy(savedStateWhileRecording);
 718   
         }
 719  0
         view.toFront();
 720  0
         setStatus(status, extended, type);
 721   
     }
 722   
 
 723   
     private RecordAllAction recordAllAction;
 724   
     private RecordAllAction recordAllMotionAction;
 725   
 
 726  2
     private void updateDynamicActions(ScriptEditorFrame view) {
 727  2
         Class cls = selectedComponent == null
 728   
             ? Component.class : selectedComponent.getClass();
 729  2
         ComponentTester tester = ComponentTester.getTester(cls);
 730   
         // assert submenu
 731  2
         createAsserts(assertActions, tester, false);
 732   
         // wait submenu
 733  2
         createAsserts(waitActions, tester, true);
 734   
         // insert submenu (only include one instance of each uniquely-named
 735   
         // method. 
 736  2
         insertActions.clear();
 737  2
         Map map = new HashMap();
 738  2
         Method[] methods = tester.getActions();
 739  2
         for (int i=0;i < methods.length;i++) {
 740  14
             TesterMethodAction action =
 741   
                 new TesterMethodAction(tester, methods[i], true);
 742  14
             map.put(action.getName(), action);
 743   
         }
 744  2
         methods = tester.getComponentActions();
 745  2
         for (int i = 0; i < methods.length; i++) {
 746  32
             TesterMethodAction action =
 747   
                 new TesterMethodAction(tester, methods[i], true);
 748  32
             map.put(action.getName(), action);
 749   
         }
 750  2
         insertActions.addAll(map.values());
 751   
         // capture actions
 752   
 
 753  2
         captureActions.clear();
 754  2
         recordAllAction = new RecordAllAction(ACTION_CAPTURE,
 755   
                                               recorders[0], false);
 756  2
         captureActions.add(recordAllAction);
 757  2
         recordAllMotionAction = new RecordAllAction(ACTION_CAPTURE_ALL,
 758   
                                                     recorders[1], true);
 759  2
         captureActions.add(recordAllMotionAction);
 760  2
         captureActions.add(null);
 761  2
         captureActions.add(new CaptureImageAction());
 762  2
         captureActions.add(new CaptureComponentAction());
 763   
 
 764  2
         view.populateInsertMenu(insertActions);
 765  2
         view.populateAssertMenu(assertActions);
 766  2
         view.populateWaitMenu(waitActions);
 767  2
         view.populateCaptureMenu(captureActions);
 768   
     }
 769   
 
 770  125
     private void setSelected(String which, boolean select) {
 771  125
         javax.swing.Action action = actionMap.get(which);
 772  125
         if (action != null) {
 773  125
             ((EditorToggleAction)action).setSelected(select);
 774   
         }
 775   
         else {
 776  0
             Log.warn("Toggle action " + which + " is missing");
 777   
         }
 778   
     }
 779   
 
 780  925
     private void setEnabled(String which, boolean enable) {
 781  925
         javax.swing.Action action;
 782  925
         if (which == ACTION_DYNAMIC) {
 783  25
             ArrayList[] lists = new ArrayList[] {
 784   
                 captureActions, waitActions, assertActions, insertActions
 785   
             };
 786  25
             for (int i=0;i < lists.length;i++) {
 787  100
                 Iterator iter = lists[i].iterator();
 788  100
                 while (iter.hasNext()) {
 789  800
                     action = (javax.swing.Action)iter.next();
 790  800
                     if (action != null)
 791  725
                         action.setEnabled(enable);
 792   
                 }
 793   
             }
 794   
         }
 795   
         else {
 796  900
             action = actionMap.get(which);
 797  900
             if (action != null) {
 798  900
                 action.setEnabled(enable);
 799   
             }
 800   
             else {
 801  0
                 Log.warn("Action " + which + " is missing");
 802   
             }
 803   
         }
 804   
     }
 805   
 
 806   
    /**
 807   
      * Initalize the componentBrowser and listeners to the scriptTable
 808   
      */
 809  2
     private ComponentBrowser createComponentBrowser() {
 810  2
         ComponentBrowser cb = new ComponentBrowser(this, hierarchy);
 811  2
         cb.setEnabled(false);
 812  2
         cb.addSelectionListener(new ComponentBrowserListener() {
 813  0
             public void selectionChanged(ComponentBrowser src,
 814   
                                          Component comp,
 815   
                                          ComponentReference ref) {
 816  0
                 setSelectedComponent(comp, ref);
 817   
             }
 818  0
             public void propertyAction(ComponentBrowser src,
 819   
                                        Method m, Object value,
 820   
                                        boolean sample) {
 821  0
                 if (selectedComponent == null)
 822  0
                     return;
 823  0
                 addPropertyMethodCall(m, value, sample);
 824   
             }
 825   
         });
 826  2
         return cb;
 827   
     }
 828   
 
 829   
     /**
 830   
      * Install a new security manager to prevent launched applications
 831   
      * from exiting the JVM.  This is only a partial solution; ideally
 832   
      * we'd like to be able to kill all the launched app's threads, or force
 833   
      * an unload of the class and reload it.
 834   
      * Should only be installed once, in the root editor context.
 835   
      */
 836  2
     private void initSecurityManager() {
 837  2
         if (Boolean.getBoolean("abbot.no_security_manager"))
 838  0
             return;
 839   
 
 840  2
         securityManager = new EditorSecurityManager();
 841  2
         try {
 842  2
             oldSecurityManager = System.getSecurityManager();
 843  2
             System.setSecurityManager(securityManager);
 844   
         }
 845   
         catch(Exception e) {
 846  0
             oldSecurityManager = securityManager = null;
 847  0
             Log.warn(e);
 848   
         }
 849   
     }
 850   
 
 851   
     /** Respond to various components. */
 852  3
     public void actionPerformed(ActionEvent ev) {
 853  3
         if (ev.getSource() == view.getTestScriptSelector()
 854   
             && !ignoreComboBox) {
 855  0
             Script script = (Script)
 856   
                 view.getTestScriptSelector().getSelectedItem();
 857  0
             if (script != testScript)
 858  0
                 setScript(script);
 859   
         }
 860  3
         else if (ev.getSource() == view.getTestScriptDescription()) {
 861  3
             if (testScript != null) {
 862  3
                 JTextField tf = view.getTestScriptDescription();
 863  3
                 String desc = tf.getText();
 864  3
                 if ("".equals(desc)) {
 865  1
                     String cmd = ev.getActionCommand();
 866  1
                     if (!TextField.isDocumentAction(cmd)) {
 867  0
                         tf.setText(testScript.getDefaultDescription());
 868  0
                         testScript.setDescription(null);
 869   
                     }
 870   
                 }
 871  2
                 else if (!desc.equals(testScript.getDefaultDescription())) {
 872  0
                     testScript.setDescription(desc);
 873   
                 }
 874   
             }
 875   
         }
 876   
         else {
 877  0
             Log.warn("Unrecognized event: " + ev.getActionCommand()
 878   
                      + "(" + ev.getID() + ")");
 879   
         }
 880   
     }
 881   
 
 882   
     /** Remove the selected step. */
 883  0
     private void cutSelection() {
 884  0
         int row = scriptTable.getSelectedRow();
 885  0
         if (row == -1) {
 886  0
             Log.warn("Unexpected cut state");
 887  0
             return;
 888   
         }
 889  0
         scriptModel.removeSteps(scriptTable.getSelectedSteps());
 890  0
         int count = scriptTable.getRowCount();
 891  0
         if (count > 0) {
 892  0
             if (row >= count)
 893  0
                 row = count - 1;
 894  0
             scriptTable.setRowSelectionInterval(row, row);
 895  0
             scriptTable.setCursorLocation(row + 1);
 896   
         }
 897  0
         setActionsEnabledState();
 898  0
         setStatus("");
 899   
     }
 900   
 
 901  0
     private void moveSelectionUp() {
 902  0
         scriptTable.moveUp();
 903  0
         setActionsEnabledState();
 904   
     }
 905   
 
 906   
     /** Move the selected step down. */
 907  0
     private void moveSelectionDown() {
 908  0
         scriptTable.moveDown();
 909  0
         setActionsEnabledState();
 910   
     }
 911   
 
 912   
     /** Put the current selection into a sequence. */
 913  0
     private void groupSelection() {
 914  0
         int row = scriptTable.getSelectedRow();
 915  0
         Sequence seq = new Sequence(getResolverContext(), (String)null);
 916  0
         List list = scriptTable.getSelectedSteps();
 917  0
         Step first = (Step)list.get(0);
 918  0
         Sequence parent = scriptModel.getParent(first);
 919  0
         int index = parent.indexOf(first);
 920  0
         scriptModel.removeSteps(list);
 921  0
         Iterator iter = list.iterator();
 922  0
         Step last = parent;
 923  0
         while (iter.hasNext()) {
 924  0
             last = (Step)iter.next();
 925  0
             seq.addStep(last);
 926   
         }
 927  0
         scriptModel.insertStep(parent, seq, index);
 928  0
         scriptModel.toggle(row);
 929  0
         scriptTable.setRowSelectionInterval(row, scriptModel.getRowOf(last));
 930  0
         setActionsEnabledState();
 931   
     }
 932   
 
 933   
     /** Insert a launch step. */
 934  2
     void insertLaunch() {
 935  2
         Step step = new Launch(getResolverContext(),
 936   
                                LaunchEditor.HELP_DESC,
 937   
                                "abbot.editor.ScriptEditor",
 938   
                                "main",
 939   
                                new String[] { "[]" },
 940   
                                ".", false);
 941  2
         addStep(step);
 942   
     }
 943   
 
 944   
     /** Insert an applet step. */
 945  0
     void insertApplet() {
 946  0
         Step step = new Appletviewer(getResolverContext(),
 947   
                                      AppletviewerEditor.HELP_DESC,
 948   
                                      "your.applet.class.here",
 949   
                                      new HashMap(),
 950   
                                      null, null, null);
 951  0
         addStep(step);
 952   
     }
 953   
 
 954   
     /** Insert a terminate step. */
 955  2
     void insertTerminate() {
 956  2
         Step step = new Terminate(getResolverContext(),
 957   
                                   (String) null);
 958  2
         scriptTable.setCursorLocation(scriptTable.getRowCount());
 959  2
         addStep(step);
 960   
     }
 961   
 
 962  0
     private void insertCall(boolean sample) {
 963  0
         if (sample) {
 964  0
             addStep(new Sample(getResolverContext(),
 965   
                                (String)null, Strings.get("YourClassName"),
 966   
                                Strings.get("YourMethodName"), null,
 967   
                                Strings.get("YourPropertyName")));
 968   
         }
 969   
         else {
 970  0
             addStep(new Call(getResolverContext(),
 971   
                              (String)null, Strings.get("YourClassName"),
 972   
                              Strings.get("YourMethodName"), null));
 973   
         }
 974   
     }
 975   
 
 976   
     /** Insert a new, empty sequence. */
 977  0
     private void insertSequence() {
 978  0
         addStep(new Sequence(getResolverContext(),
 979   
                              (String) null, null));
 980   
     }
 981   
 
 982  0
     private void insertComment() {
 983  0
         addStep(new Comment(getResolverContext(), ""));
 984   
     }
 985   
 
 986  0
     private void insertExpression() {
 987  0
         addStep(new Expression(getResolverContext(), ""));
 988   
     }
 989   
 
 990  0
     private void insertAnnotation() {
 991  0
         addStep(new Annotation(getResolverContext(), ""));
 992   
     }
 993   
 
 994   
     /** Insert another script as a step in this one. */
 995  0
     private void insertScript(boolean fixture) {
 996  0
         JFileChooser chooser = getChooser(filter);
 997  0
         chooser.setCurrentDirectory(getWorkingDirectory());
 998  0
         if (chooser.showOpenDialog(view) == JFileChooser.APPROVE_OPTION) {
 999  0
             File file = chooser.getSelectedFile();
 1000  0
             if (!file.exists()) {
 1001  0
                 try {
 1002  0
                     file.createNewFile();
 1003   
                 }
 1004   
                 catch(IOException e) {
 1005  0
                     view.showError(e.toString());
 1006   
                 }
 1007   
             }
 1008  0
             Script script = fixture
 1009   
                 ? new Fixture(file.getAbsolutePath(), hierarchy)
 1010   
                 : new Script(file.getAbsolutePath(), hierarchy);
 1011  0
             try {
 1012  0
                 script.load();
 1013  0
                 addStep(script);
 1014   
             }
 1015   
             catch (Exception exc) {
 1016  0
                 view.showError(exc.toString());
 1017   
             }
 1018   
         }
 1019   
     }
 1020   
 
 1021   
     /** Returns the current test suite's directory, if available, the
 1022   
         directory of the current script, if available, or the current working 
 1023   
         directory.  If the current script has not yet been saved, 
 1024   
         uses the current working directory.
 1025   
     */
 1026  0
     private File getWorkingDirectory() {
 1027  0
         return testSuite != null
 1028   
             ? testSuite.getDirectory()
 1029  0
             : (testScript != null && !editingTempFile()
 1030   
                ? testScript.getDirectory() 
 1031   
                : new File(System.getProperty("user.dir")));
 1032   
     }
 1033   
 
 1034   
     /** Return a file chooser that filters for test scripts. */
 1035   
     // FIXME some open, close operations should be sticky w/r/t
 1036   
     // last directory used
 1037  0
     private JFileChooser getChooser(FileFilter f) {
 1038  0
         if (chooser == null) {
 1039  0
             chooser = new JFileChooser();
 1040  0
             chooser.setCurrentDirectory(getWorkingDirectory());
 1041   
         }
 1042  0
         chooser.setFileFilter(f);
 1043  0
         return chooser;
 1044   
     }
 1045   
 
 1046   
     /** Set the test case to the one corresponding to the given index. */
 1047  0
     private void setScript(int index) {
 1048  0
         if (getScripts().size() == 0) {
 1049  0
             setScript((Script) null);
 1050  0
             if (testSuite != null)
 1051  0
                 setStatus(Strings.get("NoScripts"), null, WARN);
 1052   
 
 1053   
         }
 1054   
         else {
 1055  0
             if (index >= getScripts().size())
 1056  0
                 index = getScripts().size() - 1;
 1057  0
             setScript(getScriptAt(index));
 1058   
         }
 1059   
     }
 1060   
 
 1061  2
     private void setScript(Script script) {
 1062  2
         if (script == testScript && script != null)
 1063  0
             return;
 1064   
 
 1065  2
         Log.debug("Setting script to '" + script + "'");
 1066  2
         if (script != null) {
 1067  2
             try {
 1068  2
                 script.load();
 1069   
             }
 1070   
             catch(InvalidScriptException ise) {
 1071  0
                 Log.warn(ise);
 1072  0
                 setScript((String)null);
 1073  0
                 view.showError("Invalid Script", ise.toString());
 1074  0
                 return;
 1075   
             }
 1076   
             catch(Exception e) {
 1077  0
                 setScript((String)null);
 1078  0
                 Log.warn(e);
 1079  0
                 return;
 1080   
             }
 1081   
         }
 1082   
 
 1083  2
         if (testScript != null) {
 1084  1
             UIContext context = testScript.getUIContext();
 1085  1
             if (context != null && !context.equivalent(runner.getCurrentContext()))
 1086  1
                 runner.terminate();
 1087   
         }
 1088  2
         testScript = script;
 1089  2
         scriptTable.clearSelection();
 1090  2
         scriptModel.setScript(script);
 1091  2
         if (script == null) {
 1092  0
             scriptTable.setCursorLocation(0);
 1093  0
             scriptTable.setEnabled(false);
 1094  0
             view.getTestScriptDescription().setText("");
 1095  0
             view.getTestScriptDescription().setEnabled(false);
 1096  0
             setStatus(Strings.get("NoScript"));
 1097   
         }
 1098   
         else {
 1099  2
             scriptTable.setEnabled(true);
 1100  2
             scriptTable.setCursorLocation(script.hasLaunch() ? 1 : 0);
 1101  2
             if (chooser != null) {
 1102  0
                 chooser.setCurrentDirectory(script.getDirectory());
 1103   
             }
 1104  2
             view.getTestScriptDescription().setText(script.getDescription());
 1105  2
             view.getTestScriptDescription().setEnabled(true);
 1106  2
             setStatus(Strings.get("editor.editing_script", new Object[] {
 1107   
                 testScript.getName()
 1108   
             }));
 1109   
         }
 1110  2
         setActionsEnabledState();
 1111   
 
 1112  2
         ignoreComboBox = true;
 1113  2
         view.getTestScriptSelector().setSelectedItem(testScript);
 1114  2
         ignoreComboBox = false;
 1115  2
         updateTitle();
 1116   
     }
 1117   
 
 1118   
     /** Update the state of all actions.  This method should be invoked after
 1119   
      * any GUI state change.
 1120   
      */
 1121  50
     private void setActionsEnabledState() {
 1122  50
         if (!SwingUtilities.isEventDispatchThread()) {
 1123  25
             SwingUtilities.invokeLater(new Runnable() {
 1124  25
                 public void run() {
 1125  25
                     setActionsEnabledState();
 1126   
                 }
 1127   
             });
 1128  25
             return;
 1129   
         }
 1130   
 
 1131  25
         boolean haveScript = testScript != null;
 1132  25
         boolean haveSelection = scriptTable.getSelectedRow() != -1;
 1133   
 
 1134  25
         setEnabled(ACTION_SCRIPT_OPEN, true);
 1135  25
         setEnabled(ACTION_TOGGLE_STOP_ON_FAILURE, haveScript);
 1136  25
         setSelected(ACTION_TOGGLE_STOP_ON_FAILURE,
 1137   
                     haveScript && runner.getStopOnFailure());
 1138  25
         setEnabled(ACTION_TOGGLE_STOP_ON_ERROR, haveScript);
 1139  25
         setSelected(ACTION_TOGGLE_STOP_ON_ERROR,
 1140   
                     haveScript && runner.getStopOnError());
 1141  25
         setEnabled(ACTION_TOGGLE_FORKED, haveScript);
 1142  25
         setSelected(ACTION_TOGGLE_FORKED, haveScript && testScript.isForked());
 1143  25
         setEnabled(ACTION_GET_VMARGS, haveScript && testScript.isForked());
 1144  25
         setEnabled(ACTION_TOGGLE_SLOW_PLAYBACK, haveScript);
 1145  25
         setSelected(ACTION_TOGGLE_SLOW_PLAYBACK,
 1146   
                     haveScript && testScript.isSlowPlayback());
 1147  25
         setEnabled(ACTION_TOGGLE_AWT_MODE, haveScript);
 1148  25
         setSelected(ACTION_TOGGLE_AWT_MODE,
 1149   
                     haveScript && testScript.isAWTMode());
 1150   
 
 1151  25
         setEnabled(ACTION_RUN, haveScript);
 1152  25
         setEnabled(ACTION_RUN_TO, haveScript && haveSelection);
 1153  25
         setEnabled(ACTION_RUN_SELECTED,
 1154   
                    haveScript && haveSelection && isAppLaunched());
 1155  25
         setEnabled(ACTION_EXPORT_HIERARCHY,
 1156   
                    haveScript && isAppLaunched());
 1157  25
         setEnabled(ACTION_RUN_LAUNCH, haveScript
 1158   
                    && testScript.hasLaunch()
 1159   
                    && !isAppLaunched());
 1160  25
         setEnabled(ACTION_RUN_TERMINATE, isAppLaunched());
 1161  25
         setEnabled(ACTION_SCRIPT_NEW, true);
 1162  25
         setEnabled(ACTION_SCRIPT_DUPLICATE, haveScript);
 1163  25
         setEnabled(ACTION_SCRIPT_SAVE, haveScript);
 1164  25
         setEnabled(ACTION_SCRIPT_SAVE_AS, haveScript);
 1165  25
         setEnabled(ACTION_SCRIPT_RENAME, haveScript);
 1166  25
         setEnabled(ACTION_SCRIPT_DELETE, haveScript);
 1167  25
         setEnabled(ACTION_SCRIPT_CLOSE, haveScript);
 1168   
 
 1169  25
         setEnabled(ACTION_STEP_CUT, haveScript && haveSelection);
 1170  25
         setEnabled(ACTION_STEP_MOVE_UP, 
 1171   
                    haveScript && haveSelection && scriptTable.canMoveUp());
 1172  25
         setEnabled(ACTION_STEP_MOVE_DOWN, 
 1173   
                    haveScript && haveSelection && scriptTable.canMoveDown());
 1174  25
         setEnabled(ACTION_STEP_GROUP, haveScript && haveSelection);
 1175  25
         setEnabled(ACTION_SCRIPT_CLEAR, haveScript);
 1176   
 
 1177  25
         setEnabled(ACTION_INSERT_LAUNCH, 
 1178   
                    haveScript && !testScript.hasLaunch());
 1179  25
         setEnabled(ACTION_INSERT_FIXTURE, 
 1180   
                    haveScript && !testScript.hasLaunch());
 1181  25
         setEnabled(ACTION_INSERT_APPLET, 
 1182   
                    haveScript && !testScript.hasLaunch());
 1183  25
         setEnabled(ACTION_INSERT_TERMINATE, 
 1184   
                    haveScript && !testScript.hasTerminate());
 1185  25
         setEnabled(ACTION_INSERT_SCRIPT, haveScript);
 1186  25
         setEnabled(ACTION_INSERT_CALL, haveScript);
 1187  25
         setEnabled(ACTION_INSERT_SAMPLE, haveScript);
 1188  25
         setEnabled(ACTION_INSERT_SEQUENCE, haveScript);
 1189  25
         setEnabled(ACTION_INSERT_COMMENT, haveScript);
 1190  25
         setEnabled(ACTION_INSERT_EXPRESSION, haveScript);
 1191  25
         setEnabled(ACTION_INSERT_ANNOTATION, haveScript);
 1192  25
         setEnabled(ACTION_DYNAMIC, haveScript);
 1193   
         
 1194  25
         view.getComponentBrowser().setEnabled(!isScriptRunning);
 1195   
     }
 1196   
 
 1197   
     /** Set the current test script.  */
 1198  2
     void setScript(String filename) {
 1199  2
         Script script = filename != null
 1200   
             ? new Script(filename, hierarchy) : null;
 1201  2
         setScript(script);
 1202   
     }
 1203   
 
 1204   
     /** Indicate the component and/or reference currently in use. */
 1205  0
     private void setSelectedComponent(Component c,
 1206   
                                       ComponentReference ref) {
 1207   
 
 1208  0
         if (c == selectedComponent && ref == selectedReference)
 1209  0
             return;
 1210   
 
 1211  0
         boolean updateActions = c != selectedComponent;
 1212  0
         selectedComponent = c;
 1213  0
         selectedReference = ref;
 1214  0
         String status;
 1215  0
         if (ref != null) {
 1216  0
             status = Strings.get(c == null ? "ComponentReferenceX"
 1217   
                                  : "ComponentReference",
 1218   
                                  new Object[] { ref.getID() });
 1219   
         }
 1220  0
         else if (c != null) {
 1221  0
             status = hierarchy.contains(c)
 1222   
                 ? Strings.get("UnreferencedComponent")
 1223   
                 : Strings.get("editor.component_filtered");
 1224   
         }
 1225   
         else {
 1226  0
             status = Strings.get("NoComponent");
 1227   
         }
 1228  0
         setStatus(status);
 1229  0
         if (updateActions) {
 1230  0
             updateDynamicActions(view);
 1231   
         }
 1232   
     }
 1233   
 
 1234  0
     private void setTestSuite(String suiteClassname) {
 1235  0
         setTestSuite(suiteClassname, null);
 1236   
     }
 1237   
 
 1238   
     /** Sets the currently selected test suite, updating all gui components
 1239   
         appropriately.  */
 1240  0
     private void setTestSuite(String suiteClassname, ClassLoader cl) {
 1241  0
         if (cl == null)
 1242  0
             cl = getClass().getClassLoader();
 1243  0
         Log.debug("Setting test suite to " + suiteClassname);
 1244  0
         testSuite = null;
 1245  0
         testScriptList = null;
 1246  0
         if (suiteClassname != null) {
 1247  0
             try {
 1248   
                 // FIXME use a dynamic class loader so we can reload after
 1249   
                 // changes to the suite/fixture class. 
 1250  0
                 Class cls = Class.forName(suiteClassname, true, cl);
 1251  0
                 if (!ScriptFixture.class.isAssignableFrom(cls)
 1252   
                     && !ScriptTestSuite.class.isAssignableFrom(cls)) {
 1253  0
                     view.showWarning(Strings.get("editor.wrong_class",
 1254   
                                                  new Object[] { cls.getName() }));
 1255   
                 }
 1256   
                 else {
 1257  0
                     testClass = cls;
 1258  0
                     Method suiteMethod = null;
 1259  0
                     testSuite = null;
 1260  0
                     try {
 1261  0
                         suiteMethod = testClass.getMethod("suite", new Class[0]);
 1262  0
                         testSuite =
 1263   
                             (ScriptTestSuite) suiteMethod.invoke(null, new Class[0]);
 1264   
                     }
 1265   
                     catch (NoSuchMethodException nsm) {
 1266  0
                         view.showError(nsm.toString());
 1267  0
                         testSuite = null;
 1268   
                     }
 1269   
                     catch (InvocationTargetException ite) {
 1270  0
                         view.showError(ite.toString());
 1271   
                     }
 1272   
                     catch (IllegalAccessException iae) {
 1273  0
                         view.showError(iae.toString());
 1274   
                     }
 1275   
                 }
 1276   
             }
 1277   
             catch (ClassNotFoundException e) {
 1278  0
                 view.showWarning(Strings.get("editor.suite_not_found",
 1279   
                                              new Object[] { suiteClassname }));
 1280   
             }
 1281   
         }
 1282  0
         if (testSuite == null) {
 1283  0
             view.getCurrentTestSuiteLabel().setText(Strings.get("NoSuite"));
 1284  0
             model = new DefaultComboBoxModel();
 1285  0
             view.getTestScriptSelector().setModel(model);
 1286  0
             view.getTestScriptSelector().setEnabled(false);
 1287  0
             model.setSelectedItem(null);
 1288   
         }
 1289   
         else {
 1290  0
             view.getTestScriptSelector().setEnabled(true);
 1291  0
             view.getCurrentTestSuiteLabel().setText(testSuite.toString());
 1292  0
             Object oldSelection = view.getTestScriptSelector().getSelectedItem();
 1293  0
             ignoreComboBox = true;
 1294  0
             view.getTestScriptSelector().setEnabled(true);
 1295   
             // Workaround for indexing bug on OSX
 1296  0
             view.getTestScriptSelector().setSelectedItem(null);
 1297  0
             List list = getScripts();
 1298  0
             Object[] data = list.toArray(new Object[list.size()]);
 1299  0
             model = new DefaultComboBoxModel(data);
 1300  0
             view.getTestScriptSelector().setModel(model);
 1301   
             // If the test suite didn't actually change, then keep the old
 1302   
             // selection. 
 1303  0
             if (getScripts().contains(oldSelection))
 1304  0
                 model.setSelectedItem(oldSelection);
 1305  0
             ignoreComboBox = false;
 1306   
         }
 1307   
     }
 1308   
 
 1309   
     /** Set the frame title to the default. */
 1310  2
     private void updateTitle() {
 1311  2
         String title = Strings.get("ScriptEditor.title", new Object[] {
 1312  2
             testScript != null
 1313   
             ? (" (" + testScript.getName() + ")")
 1314   
             : ""
 1315   
         });
 1316  2
         view.setTitle(title);
 1317   
     }
 1318   
 
 1319   
     /** Pull up a dialog with all available test suites.  */
 1320  0
     private void browseTests() {
 1321  0
         ClassLoader cl = getContextClassLoader();
 1322  0
         String path = cl instanceof PathClassLoader 
 1323   
             ? ((PathClassLoader)cl).getClassPath() : null;
 1324  0
         if (path == null)
 1325  0
             path = System.getProperty("java.class.path");
 1326   
 
 1327  0
         TestSelector selector = new TestSelector(view, path);
 1328  0
         selector.setVisible(true);
 1329  0
         String className = selector.getSelectedItem();
 1330  0
         if (className != null && checkSaveBeforeClose()) {
 1331  0
             terminate();
 1332  0
             boolean none = className.equals(TestSelector.TEST_NONE);
 1333  0
             setTestSuite(none ? null : className, cl);
 1334  0
             setScript(0);
 1335  0
             if (none) {
 1336  0
                 setStatus(Strings.get("editor.no_suite"));
 1337   
             }
 1338   
         }
 1339   
     }
 1340   
 
 1341   
     /** @return true if it's ok to exit. */
 1342  0
     protected boolean checkSaveBeforeClose() {
 1343   
         // query save/cancel/exit
 1344  0
         if (testScript != null && (testScript.isDirty())) {
 1345  0
             int opt = view.showConfirmation(Strings.get("ScriptModified"),
 1346   
                                             JOptionPane.YES_NO_CANCEL_OPTION);
 1347  0
             if (opt == JOptionPane.CANCEL_OPTION
 1348   
                 || opt == JOptionPane.CLOSED_OPTION) {
 1349  0
                 return false;
 1350   
             }
 1351  0
             else if (opt == JOptionPane.YES_OPTION) {
 1352  0
                 saveScript();
 1353   
             }
 1354   
         }
 1355  0
         return true;
 1356   
     }
 1357   
 
 1358  0
     private void closeScript() {
 1359  0
         if (!checkSaveBeforeClose())
 1360  0
             return;
 1361  0
         setScript((Script)null);
 1362   
     }
 1363   
 
 1364   
     /** Quit the application. */
 1365  0
     void quitApplication() {
 1366  0
         if (!checkSaveBeforeClose())
 1367  0
             return;
 1368   
 
 1369  0
         Log.debug("editor quit" + (isRootEditor ? " (root)" : ""));
 1370  0
         dispose();
 1371  0
         if (isRootEditor) {
 1372  0
             rootIsExiting = true;
 1373   
         }
 1374   
         else {
 1375  0
             AWTHierarchy.setDefault(oldHierarchy);
 1376   
         }
 1377  0
         System.exit(0);
 1378   
     }
 1379   
 
 1380   
     /** Set the contents of the status message. */
 1381  10
     public void setStatus(String msg) {
 1382  10
         setStatus(msg, null, INFO);
 1383   
     }
 1384   
 
 1385   
     /** Set the contents of the status message to the given exception. */
 1386  0
     private String getStackTrace(Throwable thr) {
 1387  0
         StringWriter writer = new StringWriter();
 1388  0
         thr.printStackTrace(new PrintWriter(writer));
 1389  0
         return writer.toString();
 1390   
     }
 1391   
 
 1392   
     /** Set the contents of the status message. */
 1393  10
     public void setStatus(String msg, String extended, int type) {
 1394  10
         String text = Strings.get(statusFormat[type], new Object[] { msg });
 1395  10
         view.setStatus(text, extended, statusColor[type]);
 1396   
         // Save all messages to the log
 1397  10
         Log.log(text);
 1398   
     }
 1399   
 
 1400  8
     private void setStatusForStep(Step step) {
 1401  8
         if (step == null) {
 1402  4
             setStatus("");
 1403  4
             return;
 1404   
         }
 1405   
 
 1406  4
         Throwable error = runner.getError(step);
 1407  4
         boolean fromScript = step == testScript;
 1408  4
         String msg = error != null ? error.toString() : null;
 1409  4
         String inStep = Strings.get("InStep", new Object[] { step });
 1410  4
         String where = Strings.get("StepAt", new Object[] {
 1411   
             Script.getFile(step), 
 1412   
             new Integer(Script.getLine(step))
 1413   
         }) + "\n";
 1414   
         // Don't need location info for these
 1415  4
         if (error instanceof AssertionFailedError
 1416   
             && ((AssertionFailedError)error).getLine() != 0) {
 1417  0
             where = "";
 1418   
         }
 1419   
 
 1420  4
         String extended = null;
 1421  4
         int type = INFO;
 1422  4
         if (error != null) {
 1423  0
             Log.log(error);
 1424  0
             boolean isFailure = (error instanceof AssertionFailedError);
 1425  0
             type = isFailure ? FAILURE : ERROR;
 1426  0
             extended = getStackTrace(error);
 1427   
             // If we're not stopping on failure, don't mention it
 1428   
             // specifically
 1429  0
             if (fromScript) {
 1430  0
                 if ((isFailure && !runner.getStopOnFailure())
 1431   
                     || (!isFailure && !runner.getStopOnError())) {
 1432  0
                     msg = Strings.get(isFailure
 1433   
                                       ? "ScriptFailure"
 1434   
                                       : "ScriptError");
 1435   
                 }
 1436   
                 else {
 1437   
                     // Otherwise ignore messages from the script itself
 1438  0
                     return;
 1439   
                 }
 1440   
             }
 1441   
             else {
 1442  0
                 extended = inStep + "\n" + where + extended;
 1443   
             }
 1444   
         }                          
 1445  4
         else if (fromScript) {
 1446  0
             msg = Strings.get("ScriptSuccess");
 1447   
         }
 1448   
         // If nothing interesting happened, leave the status alone
 1449  4
         if (msg != null)
 1450  0
             setStatus(msg, extended, type);
 1451   
     }
 1452   
 
 1453   
     /** Return a ComponentReference corresponding to the currently selected
 1454   
         Component or ComponentReference, creating one if necessary.
 1455   
     */
 1456  0
     private String getSelectedComponentID() {
 1457  0
         String id = null;
 1458  0
         if (selectedReference != null) {
 1459  0
             id = selectedReference.getID();
 1460   
         }
 1461  0
         else if (selectedComponent != null) {
 1462  0
             ComponentReference ref = addComponent(selectedComponent);
 1463  0
             id = ref.getID();
 1464  0
             setStatus(Strings.get("ComponentReference", new Object[] {id}));
 1465   
         }
 1466  0
         return id;
 1467   
     }
 1468   
 
 1469   
     /** Add either an Action or an Assert method provided by the given
 1470   
         Tester. */
 1471  0
     private void addTesterCall(Method method, ComponentTester tester,
 1472   
                                boolean wait, String docs) {
 1473  0
         boolean invert = invertAssertions;
 1474  0
         Class[] params = method.getParameterTypes();
 1475  0
         String id = getSelectedComponentID();
 1476  0
         Class componentClass = selectedComponent != null ? 
 1477   
             selectedComponent.getClass() : null;
 1478  0
         boolean componentArg0 = params.length > 0 
 1479   
             && Component.class.isAssignableFrom(params[0]);
 1480   
         
 1481  0
         String argString =
 1482   
             view.showInputDialog(Strings.get("IdentifyArguments"), docs, 
 1483  0
                                  componentArg0 ? id : null);
 1484  0
         if (argString == null)
 1485  0
             return;
 1486   
 
 1487  0
         String[] args = ArgumentParser.parseArgumentList(argString);
 1488  0
         try {
 1489  0
             insertTesterCall(tester, method, componentClass,
 1490   
                              id, args, wait, invert);
 1491   
         }
 1492   
         catch (IllegalArgumentException iae) {
 1493  0
             Log.warn(iae);
 1494   
         }
 1495   
         catch (NoSuchReferenceException nsr) {
 1496  0
             Log.warn(nsr);
 1497   
         }
 1498   
     }
 1499   
 
 1500   
     /** Invoked after the user has selected a component and a property for
 1501   
      * it.
 1502   
      */
 1503  0
     private void addPropertyMethodCall(Method method,
 1504   
                                        Object value, boolean sample) {
 1505  0
         String id = getSelectedComponentID();
 1506  0
         String methodName = method.getName();
 1507  0
         String[] args = ComponentTester.class.
 1508   
             isAssignableFrom(method.getDeclaringClass())
 1509   
             ? new String[] { id } : null;
 1510  0
         String targetClassName = method.getDeclaringClass().getName();
 1511  0
         if (sample) {
 1512  0
             String varName = Strings.get("YourPropertyName");
 1513  0
             Sample step = args == null
 1514   
                 ? new Sample(getResolverContext(), null, methodName, id, varName)
 1515   
                 : new Sample(getResolverContext(), null, targetClassName, methodName,
 1516   
                              args, varName);
 1517  0
             addStep(step);
 1518   
         }
 1519   
         else {
 1520  0
             String expectedValue = ArgumentParser.toString(value);
 1521  0
             Assert step = args == null
 1522   
                 ? new Assert(getResolverContext(), null, methodName, id,
 1523   
                              expectedValue, invertAssertions)
 1524   
                 : new Assert(getResolverContext(), null, targetClassName, methodName, 
 1525   
                              args, expectedValue, invertAssertions);
 1526  0
             step.setWait(waitAssertions);
 1527  0
             addStep(step);
 1528   
         }
 1529   
     }
 1530   
 
 1531   
     /** Returns null if not found. */
 1532  0
     public String getComponentID(Component comp) {
 1533  0
         ComponentReference ref = getComponentReference(comp);
 1534  0
         return ref != null ? ref.getID() : null;
 1535   
     }
 1536   
 
 1537   
     /** Insert a new step at the current cursor location.  */
 1538  4
     void addStep(Step step) {
 1539  4
         Sequence parent = scriptTable.getCursorParent();
 1540  4
         int index = scriptTable.getCursorParentIndex();
 1541  4
         scriptModel.insertStep(parent, step, index);
 1542  4
         int row = scriptModel.getRowOf(step);
 1543  4
         scriptTable.setRowSelectionInterval(row, row);
 1544  4
         scriptTable.setCursorLocation(row + 1);
 1545  4
         setActionsEnabledState();
 1546   
     }
 1547   
 
 1548   
     /** Create a new script. */
 1549  0
     private void newScript(boolean copyFixture) {
 1550  0
         if (!checkSaveBeforeClose())
 1551  0
             return;
 1552  0
         File file;
 1553  0
         try {
 1554  0
             file = File.createTempFile(Script.UNTITLED_FILE, ".xml");
 1555  0
             tempFile = file;
 1556   
         }
 1557   
         catch(IOException io) {
 1558  0
             JFileChooser chooser = getChooser(filter);
 1559  0
             File dir = getWorkingDirectory();
 1560  0
             chooser.setCurrentDirectory(dir);
 1561  0
             chooser.setSelectedFile(new File(dir, Script.UNTITLED_FILE + ".xml"));
 1562  0
             if (chooser.showSaveDialog(view) != JFileChooser.APPROVE_OPTION) {
 1563  0
                 return;
 1564   
             }
 1565  0
             file = chooser.getSelectedFile();
 1566   
         }
 1567  0
         if (!file.exists() || file.isFile()) {
 1568  0
             newScript(file, copyFixture);
 1569   
         }
 1570   
         else {
 1571   
             // FIXME display an error
 1572   
         }
 1573   
     }
 1574   
 
 1575   
     /** Create a new script at the given filename, or open it if it already
 1576   
      * exists.  Optionally copies the fixture from the current script.
 1577   
      */
 1578  2
     void newScript(File file, boolean copyFixture) {
 1579  2
         if (!checkSaveBeforeClose())
 1580  0
             return;
 1581   
 
 1582  2
         try {
 1583  2
             boolean insert = file.createNewFile() || file.length() == 0;
 1584  2
             Script srcFixture = testScript;
 1585  2
             setScript(file.getAbsolutePath());
 1586  2
             if (insert) {
 1587  2
                 if (!copyFixture || srcFixture == null) {
 1588  2
                     insertTerminate();
 1589  2
                     insertLaunch();
 1590   
                 }
 1591   
                 else {
 1592  0
                     copyFixture(srcFixture);
 1593   
                 }
 1594   
             }
 1595  2
             setStatus(Strings.get(copyFixture ? "FixtureDuplicated"
 1596   
                                   : "NewScriptCreated",
 1597   
                                   new Object[] { file.getName() }));
 1598   
         }
 1599   
         catch(IOException io) {
 1600  0
             view.showError("File Error", io.toString());
 1601   
         }
 1602   
     }
 1603   
 
 1604   
     /** Copy the fixture from the given script into the current one. */
 1605  0
     public void copyFixture(Script src) {
 1606  0
         Step first = src.getStep(0);
 1607  0
         Step last = src.getStep(src.size() - 1);
 1608  0
         if (first instanceof UIContext) {
 1609  0
             setStatus(Strings.get("editor.adding_launch"));
 1610  0
             addStep(first);
 1611   
         }
 1612  0
         if (src.size() > 1 && (src.getStep(1) instanceof Assert)) {
 1613  0
             Assert wait = (Assert)src.getStep(1);
 1614  0
             if (wait.isWait()
 1615   
                 && wait.getMethodName().equals("assertFrameShowing")) {
 1616  0
                 setStatus(Strings.get("editor.adding_wait"));
 1617  0
                 addStep(wait);
 1618   
             }
 1619   
         }
 1620  0
         if (last instanceof Terminate) {
 1621  0
             setStatus(Strings.get("editor.adding_terminate"));
 1622  0
             addStep(last);
 1623   
         }
 1624   
     }
 1625   
 
 1626   
     /** Launch the GUI under test. */
 1627  0
     private void launch(boolean terminateRecorder) {
 1628  0
         if (testScript == null) {
 1629  0
             Log.warn("null testScript");
 1630  0
             return;
 1631   
         }
 1632   
 
 1633  0
         ignoreStepEvents = true;
 1634  0
         invertAssertions = false;
 1635  0
         waitAssertions = false;
 1636  0
         view.setAssertOptions(waitAssertions, invertAssertions);
 1637   
 
 1638   
         // Clean up any extant windows
 1639  0
         if (terminateRecorder && recorder != null) {
 1640   
             // Assume we want to keep the results
 1641  0
             stopRecording(false);
 1642   
         }
 1643   
 
 1644  0
         setStatus(Strings.get("Launching"));
 1645   
         // FIXME time out and flag an error if the launch never returns
 1646   
         // (even if threaded, it should return at some point)
 1647  0
         runSteps(testScript, true, 
 1648   
                  Strings.get("LaunchingDone"),
 1649   
                  new Runnable() {
 1650  0
             public void run() {
 1651  0
                 ignoreStepEvents = false;
 1652   
             }
 1653   
         });
 1654   
     }
 1655   
 
 1656   
     /** Do everything we can to dispose of the application under test. */
 1657  2
     private void terminate() {
 1658  2
         if (recorder != null) {
 1659   
             // Assume we want to keep the results
 1660  0
             stopRecording(false);
 1661   
         }
 1662  2
         scriptTable.clearSelection();
 1663  2
         runner.terminate();
 1664   
     }
 1665   
 
 1666  0
     private void openScript() {
 1667  0
         if (!checkSaveBeforeClose())
 1668  0
             return;
 1669  0
         JFileChooser chooser = getChooser(filter);
 1670  0
         chooser.setCurrentDirectory(getWorkingDirectory());
 1671  0
         if (testScript != null) {
 1672  0
             chooser.setSelectedFile(testScript.getFile());
 1673   
         }
 1674  0
         if (chooser.showOpenDialog(view) == JFileChooser.APPROVE_OPTION) {
 1675  0
             File file = chooser.getSelectedFile();
 1676  0
             setScript(file.getAbsolutePath());
 1677   
         }
 1678  0
         if (!getScripts().contains(testScript)
 1679   
             && model != null) {
 1680  0
             model.setSelectedItem(null);
 1681   
         }
 1682   
     }
 1683   
 
 1684  0
     private void stopOnFailureToggle() {
 1685  0
         if (testScript != null) {
 1686  0
             runner.setStopOnFailure(!runner.getStopOnFailure());
 1687   
         }
 1688   
     }
 1689   
 
 1690  0
     private void stopOnErrorToggle() {
 1691  0
         if (testScript != null) {
 1692  0
             runner.setStopOnError(!runner.getStopOnError());
 1693   
         }
 1694   
     }
 1695   
 
 1696  0
     private void forkedToggle() {
 1697  0
         if (testScript != null) {
 1698  0
             testScript.setForked(!testScript.isForked());
 1699   
         }
 1700  0
         setActionsEnabledState();
 1701   
     }
 1702   
 
 1703  0
     private void getVMArgs() {
 1704  0
         String args = view.showInputDialog(Strings.get("GetVMArgs.title"),
 1705   
                                            Strings.get("GetVMArgs.msg"),
 1706   
                                            testScript.getVMArgs());
 1707  0
         if (args != null) {
 1708  0
             testScript.setVMArgs(args);
 1709   
         }
 1710   
     }
 1711   
 
 1712  0
     private void slowPlaybackToggle() {
 1713  0
         if (testScript != null) {
 1714  0
             testScript.setSlowPlayback(!testScript.isSlowPlayback());
 1715   
         }
 1716   
     }
 1717   
 
 1718  0
     private void awtModeToggle() {
 1719  0
         if (testScript != null) {
 1720  0
             testScript.setAWTMode(!testScript.isAWTMode());
 1721   
         }
 1722   
     }
 1723   
 
 1724  0
     private void runScript(Step stopAt) {
 1725  0
         if (testScript == null) {
 1726  0
             Log.warn("null testScript");
 1727  0
             return;
 1728   
         }
 1729  0
         Log.debug("Running test case " + testScript);
 1730   
         
 1731  0
         stopStep = stopAt;
 1732  0
         setStatus(Strings.get("actions.run.start"));
 1733  0
         Runnable completion = new Runnable() {
 1734  0
             public void run() {
 1735   
                 // When the script finishes, bring the main
 1736   
                 // app forward.
 1737  0
                 view.toFront();
 1738   
             }
 1739   
         };
 1740  0
         runSteps(testScript, false, 
 1741   
                  Strings.get("actions.run.finish"),
 1742   
                  completion);
 1743   
     }
 1744   
 
 1745  0
     private void exportHierarchy() {
 1746  0
         JFileChooser chooser = getChooser(null);
 1747  0
         if (chooser.showSaveDialog(view) == JFileChooser.APPROVE_OPTION) {
 1748  0
             setStatus(Strings.get("actions.export-hierarchy.start"));
 1749  0
             final File file = chooser.getSelectedFile();
 1750  0
             new Thread("Hierarchy Export") {
 1751  0
                 public void run() {
 1752  0
                     HierarchyWriter hw = new HierarchyWriter(hierarchy);
 1753  0
                     try {
 1754  0
                         FileWriter writer = new FileWriter(file);
 1755  0
                         hw.writeHierarchy(writer);
 1756   
                     }
 1757   
                     catch(IOException io) {
 1758  0
                         view.showError(Strings.get("SaveFailed.title"),
 1759   
                                        io.toString());
 1760   
                     }
 1761  0
                     setStatus(Strings.get("actions.export-hierarchy.finish"));
 1762   
                 }
 1763   
             }.start();
 1764   
         }
 1765   
     }
 1766   
 
 1767  0
     private void runSelectedSteps() {
 1768  0
         final List stepList = scriptTable.getSelectedSteps();
 1769  0
         if (testScript == null
 1770   
             || stepList == null || stepList.size() == 0
 1771   
             || !isAppLaunched()) {
 1772  0
             Log.warn("inconsistent program state");
 1773  0
             return;
 1774   
         }
 1775   
 
 1776  0
         Sequence steps = new Sequence(getResolverContext(), null, stepList);
 1777  0
         final int row0 = scriptModel.getRowOf((Step)stepList.get(0));
 1778  0
         final int row1 = scriptModel.getRowOf((Step)stepList.get(steps.size()-1));
 1779  0
         setStatus(Strings.get("actions.run-selected.start"));
 1780  0
         hideView();
 1781  0
         runSteps(steps, false, 
 1782   
                  Strings.get("actions.run-selected.finish"),
 1783   
                  new Runnable() {
 1784  0
             public void run() {
 1785  0
                 scriptTable.setRowSelectionInterval(row0, row1);
 1786  0
                 view.show();
 1787   
             }
 1788   
         });
 1789   
     }
 1790   
 
 1791   
     /** Invoke the given test script.  All test execution is done on a dedicated
 1792   
      * thread/thread group to avoid interfering with the event dispatch thread.
 1793   
      */
 1794  0
     private void runSteps(final Step which, final boolean launch,
 1795   
                           final String completionMessage,
 1796   
                           final Runnable onCompletion) {
 1797  0
         Log.debug("running " + which);
 1798  0
         setActionsEnabledState();
 1799  0
         lastLaunchTime = System.currentTimeMillis();
 1800  0
         final List savedState = AWT.disableHierarchy(view);
 1801  0
         Runnable action = new LaunchAction(which, savedState, onCompletion, completionMessage, launch);
 1802  0
         String groupName = "AUT Thread Group for " + this + ":" + nonce++;
 1803  0
         if (appGroup == null) {
 1804  0
             appGroup = new ThreadGroup(groupName) {
 1805  0
                 public void uncaughtException(Thread t, Throwable thrown) {
 1806  0
                     if (!(thrown instanceof ExitException)
 1807   
                                     && !(thrown instanceof ThreadDeath)) {
 1808  0
                         Log.warn("Application thread exception not caught: " + t);
 1809  0
                         Log.warn(thrown);
 1810   
                     }
 1811   
                 }
 1812   
             };
 1813   
         }
 1814  0
         Thread launcher = new Thread(appGroup, action,
 1815   
                                      "Script runner:" + nonce);
 1816  0
         launcher.setDaemon(true);
 1817  0
         view.getComponentBrowser().setEnabled(false);
 1818  0
         view.setEditor(null);
 1819  0
         isScriptRunning = true;
 1820  0
         launcher.start();
 1821   
     }
 1822   
 
 1823   
     /** Remove all contents from the current script. */
 1824  0
     private void clearScript() {
 1825  0
         if (testScript == null) {
 1826  0
             Log.warn("null testScript");
 1827  0
             return;
 1828   
         }
 1829  0
         if (view.showConfirmation(Strings.get("editor.confirm.clear_script"))
 1830   
             == JOptionPane.YES_OPTION) {
 1831  0
             scriptTable.clearSelection();
 1832  0
             scriptTable.setCursorLocation(0);
 1833  0
             testScript.clear();
 1834  0
             scriptModel.setScript(testScript);
 1835   
         }
 1836   
     }
 1837   
 
 1838   
     /** Delete the currently selected script/test case. */
 1839  0
     private void deleteScript() {
 1840  0
         if (testScript == null) {
 1841  0
             Log.warn("null testScript");
 1842  0
             return;
 1843   
         }
 1844  0
         if (view.showConfirmation(Strings.get("editor.confirm.delete_script"))
 1845   
             == JOptionPane.YES_OPTION) {
 1846  0
             File file = testScript.getFile();
 1847  0
             int index = view.getTestScriptSelector().getSelectedIndex();
 1848  0
             file.delete();
 1849  0
             setTestSuite(testClass.getName(), testClass.getClassLoader());
 1850  0
             setScript(index);
 1851   
         }
 1852   
     }
 1853   
 
 1854   
     /** Change the file backing the current script/test case, renaming
 1855   
      * its file if <code>rename</code> is true, or saving to a new destination
 1856   
      * if not. 
 1857   
      */
 1858  0
     private void saveAsScript(boolean rename) {
 1859  0
         if (testScript == null) {
 1860  0
             Log.warn("null testScript");
 1861  0
             return;
 1862   
         }
 1863  0
         File oldFile = testScript.getFile();
 1864  0
         JFileChooser chooser = getChooser(filter);
 1865  0
         chooser.setCurrentDirectory(getWorkingDirectory());
 1866   
         // FIXME make sure it falls within the test suite's script set?
 1867   
         //chooser.setFileFilter(testScript.getFileFilter());
 1868  0
         Log.debug("Showing save dialog");
 1869  0
         if (chooser.showSaveDialog(view) == JFileChooser.APPROVE_OPTION) {
 1870  0
             Log.debug("Accepted");
 1871  0
             File newFile = chooser.getSelectedFile();
 1872  0
             if (rename) {
 1873  0
                 if (!oldFile.renameTo(newFile)) {
 1874  0
                     view.showError(Strings.get("editor.save.rename_failed",
 1875   
                                                new Object[] { 
 1876   
                                                    oldFile, newFile
 1877   
                                                }));
 1878  0
                     return;
 1879   
                 }
 1880   
             }
 1881  0
             testScript.setFile(newFile);
 1882  0
             saveScript();
 1883  0
             updateTitle();
 1884   
 
 1885  0
             if (testSuite != null && testSuite.accept(newFile)) {
 1886  0
                 setTestSuite(testClass.getName(),
 1887   
                              testSuite.getClass().getClassLoader());
 1888   
             }
 1889   
             else{
 1890  0
                 setTestSuite(null);
 1891   
             }
 1892   
         
 1893  0
             if (rename) {
 1894  0
                 setStatus(Strings.get("ScriptRename", new Object[]{
 1895   
                     newFile.getName()
 1896   
                 }));
 1897   
             }
 1898   
             else {
 1899  0
                 setStatus(Strings.get("ScriptSaved", new Object[]{
 1900   
                     newFile.getName()
 1901   
                 }));
 1902   
             }
 1903   
             // Combo box doesn't know that the script has changed its
 1904   
             // toString 
 1905  0
             view.getTestScriptSelector().repaint();
 1906   
 
 1907   
             // Default script description reflects the path
 1908  0
             String text = testScript != null
 1909   
                 ? testScript.getDescription() : "";
 1910  0
             view.getTestScriptDescription().setText(text);
 1911   
         }
 1912   
     }
 1913   
 
 1914  0
     private boolean editingTempFile() {
 1915  0
         return testScript != null
 1916   
             && testScript.getFile().equals(tempFile);
 1917   
     }
 1918   
     
 1919   
     /** Save the current script/test case state to disk. */
 1920  0
     private void saveScript() {
 1921  0
         if (testScript == null) {
 1922  0
             Log.warn("null testScript");
 1923  0
             return;
 1924   
         }
 1925   
         // If the file is a temporary file, prompt for its real location
 1926  0
         if (editingTempFile()) {
 1927   
             // This will recurse back into saveScript, so we're done
 1928  0
             Log.debug("Directory is temporary directory, need to rename");
 1929  0
             saveAsScript(false);
 1930  0
             return;
 1931   
         }
 1932  0
         File file = testScript.getFile();
 1933  0
         File parent = file.getParentFile();
 1934  0
         boolean canWrite = (!file.exists() && parent.canWrite())
 1935   
             || (file.exists() && file.canWrite());
 1936  0
         if (!canWrite) {
 1937  0
             String msg = Strings.get("NoFilePermission",
 1938   
                                      new Object[] { file.toString() });
 1939  0
             view.showError(Strings.get("SaveFailed.title"), msg);
 1940   
         }
 1941   
         else {
 1942  0
             try {
 1943  0
                 setStatus(Strings.get("Saving", new Object[] { file }));
 1944  0
                 saveNestedScripts(testScript);
 1945  0
                 setStatus(Strings.get("Saved", new Object[] { file }));
 1946   
             }
 1947   
             catch (IOException exc) {
 1948  0
                 view.showError(Strings.get("SaveFailed.title"),
 1949   
                                exc.toString());
 1950   
             }
 1951   
         }
 1952   
     }
 1953   
 
 1954  4
     void saveNestedScripts(Sequence seq) throws IOException {
 1955  4
         if (seq instanceof Script)
 1956  4
             ((Script)seq).save();
 1957   
 
 1958  4
         Iterator iter = seq.steps().iterator();
 1959  4
         while (iter.hasNext()) {
 1960  3
             Step step = (Step)iter.next();
 1961  3
             if (step instanceof Sequence) {
 1962  3
                 saveNestedScripts((Sequence)step);
 1963   
             }
 1964   
         }
 1965   
     }
 1966   
 
 1967   
     private EventNormalizer normalizer = new EventNormalizer();
 1968   
 
 1969   
     /** Start listening to GUI events. */
 1970  0
     private void startListening() {
 1971  0
         normalizer.startListening(new SingleThreadedEventListener() {
 1972  0
             protected void processEvent(AWTEvent event) {
 1973  0
                 ScriptEditor.this.processEvent(event);
 1974   
             }
 1975   
         }, FIXTURE_EVENT_MASK);
 1976  0
         view.getComponentBrowser().setEnabled(true);
 1977   
     }
 1978   
 
 1979   
     /** Return the number of windows that are showing. */
 1980  0
     private int countShowingWindows(Window root) {
 1981  0
         int count = root != null && root.isShowing() ? 1 : 0;
 1982  0
         Iterator iter = root == null
 1983   
             ? hierarchy.getRoots().iterator()
 1984   
             : hierarchy.getComponents(root).iterator();
 1985  0
         while (iter.hasNext()) {
 1986  0
             Component c = (Component)iter.next();
 1987  0
             if (c instanceof Window) {
 1988  0
                 count += countShowingWindows((Window)c);
 1989   
             }
 1990   
         }
 1991  0
         return count;
 1992   
     }
 1993   
 
 1994   
     private int DONT_CARE = -1;
 1995  0
     private boolean isKeyPress(AWTEvent event, int code, int modifiers) {
 1996  0
         return event.getID() == KeyEvent.KEY_PRESSED
 1997   
             && ((KeyEvent)event).getKeyCode() == code
 1998   
             && (((KeyEvent)event).getModifiers() == modifiers
 1999   
                 || modifiers == DONT_CARE);
 2000   
     }
 2001  0
     private boolean isKeyRelease(AWTEvent event, int code, int modifiers) {
 2002  0
         return event.getID() == KeyEvent.KEY_RELEASED
 2003   
             && ((KeyEvent)event).getKeyCode() == code
 2004   
             && (((KeyEvent)event).getModifiers() == modifiers
 2005   
                 || modifiers == DONT_CARE);
 2006   
     }
 2007   
 
 2008   
     /** The editor does many things with the event stream, including logging
 2009   
      * events, passing them off to the recorder, and updating its internal
 2010   
      * state.
 2011   
      */
 2012  0
     private void processEvent(AWTEvent event) {
 2013  0
         Object src = event.getSource();
 2014  0
         boolean isComponent = src instanceof Component;
 2015  0
         boolean isFiltered =
 2016   
             isComponent && hierarchy.isFiltered((Component)src);
 2017   
         // Keep a log of all events we see on non-filtered components
 2018  0
         if (isRootEditor) {
 2019  0
             if ((LOG_ALL_EVENTS || (!isFiltered && !ignoreEvents))
 2020   
                 && Boolean.getBoolean("abbot.fixture.log_events")) {
 2021  0
                 Log.log("ED: " + Robot.toString(event)
 2022   
                         + " (" + Thread.currentThread() + ")");
 2023   
             }
 2024   
         }
 2025   
         // Allow only component events and AWT menu actions
 2026  0
         if (!isComponent && !(src instanceof MenuComponent)) {
 2027  0
             Log.warn("Source not a Component or MenuComponent: " + event);
 2028  0
             return;
 2029   
         }
 2030   
         // If the script is running (or even being launched), the code
 2031   
         // under test may do things that should really be done on the event
 2032   
         // dispatch thread (initial component show and such).  
 2033   
         // If this is the case, defer mucking about with AWT until the code
 2034   
         // under test has stabilized.
 2035  0
         if (isScriptRunning) {
 2036  0
             return;
 2037   
         }
 2038   
 
 2039  0
         if (!handleEditorTransient(event)
 2040   
             && !handleRecordingControl(event)
 2041   
             && !handleImageCaptureControl(event)
 2042   
             && !handleComponentSelection(event)
 2043   
             && recorder != null && recording && !isFiltered) {
 2044  0
             Log.debug("recorder process event");
 2045  0
             try {
 2046  0
                 recorder.record(event);
 2047   
             }
 2048   
             catch(RecordingFailedException e) {
 2049   
                 // Stop recording, but keep what we've got so far
 2050  0
                 stopRecording(false);
 2051  0
                 String msg = Strings.get("editor.recording.failure");
 2052  0
                 Throwable error = e.getReason() instanceof BugReport
 2053   
                     ? e.getReason() : new BugReport(msg, e.getReason());
 2054  0
                 Log.log("Recording failure: " + error.toString());
 2055  0
                 setStatus(error.getMessage(), error.toString(), ERROR);
 2056  0
                 view.showWarning(msg);
 2057   
             }
 2058   
         }
 2059   
 
 2060  0
         updateComponents(event);
 2061   
     }
 2062   
 
 2063  0
     private boolean handleComponentSelection(AWTEvent event) {
 2064  0
         Component ultimateComponent = state.getUltimateMouseComponent();
 2065  0
         if (ultimateComponent != null
 2066   
             && !hierarchy.isFiltered(ultimateComponent)) {
 2067  0
             boolean keySelect =
 2068   
                 isKeyPress(event, selectKey, KeyEvent.SHIFT_MASK);
 2069  0
             boolean mouseSelect = 
 2070   
                 event.getID() == MouseEvent.MOUSE_PRESSED
 2071   
                 && AWT.isTertiaryButton(((MouseEvent)event).getModifiers());
 2072  0
             boolean makeReference =
 2073   
                 isKeyPress(event, selectKey,
 2074   
                            KeyEvent.SHIFT_MASK | KeyEvent.ALT_MASK);
 2075   
             
 2076  0
             if (keySelect || mouseSelect || makeReference) {
 2077  0
                 Component selected = event instanceof MouseEvent
 2078   
                 ? InputState.getComponentAt((Component)event.getSource(),
 2079   
                                             ((MouseEvent)event).getPoint())
 2080   
                                             : ultimateComponent;
 2081  0
                 selectComponent(selected, makeReference);
 2082  0
                 return true;
 2083   
             }
 2084   
         }
 2085  0
         return false;
 2086   
     }
 2087   
     /** Update the state of components whose appearance depends on keyboard 
 2088   
      * state.
 2089   
      * @param event
 2090   
      */
 2091  0
     private void updateComponents(AWTEvent event) {
 2092   
         // Adjust the state of the assert/sample options
 2093  0
         if (isKeyPress(event, KC_INVERT, DONT_CARE)) {
 2094  0
             invertAssertions = true;
 2095  0
             view.setAssertOptions(waitAssertions, invertAssertions);
 2096   
         }
 2097  0
         else if (isKeyRelease(event, KC_INVERT, DONT_CARE)) {
 2098  0
             invertAssertions = false;
 2099  0
             view.setAssertOptions(waitAssertions, invertAssertions);
 2100   
         }
 2101  0
         else if (isKeyPress(event, KC_WAIT, DONT_CARE)) {
 2102  0
             waitAssertions = true;
 2103  0
             view.setAssertOptions(waitAssertions, invertAssertions);
 2104   
         }
 2105  0
         else if (isKeyRelease(event, KC_WAIT, DONT_CARE)) {
 2106  0
             waitAssertions = false;
 2107  0
             view.setAssertOptions(waitAssertions, invertAssertions);
 2108   
         }
 2109   
     }
 2110   
 
 2111  0
     private boolean handleEditorTransient(AWTEvent event) {
 2112   
         // Make sure we filter any transient windows generated by the
 2113   
         // script editor's frame.  This avoids some of the hierarchy event
 2114   
         // NPEs present on pre-1.4 VMs.
 2115  0
         if (event.getID() == WindowEvent.WINDOW_OPENED
 2116   
             && ((WindowEvent)event).getWindow().getParent() == view) {
 2117  0
             hierarchy.setFiltered(((WindowEvent)event).getWindow(), true);
 2118  0
             view.getComponentBrowser().refresh();
 2119  0
             return true;
 2120   
         }
 2121  0
         return false;
 2122   
     }
 2123  0
     private boolean handleImageCaptureControl(AWTEvent event) {
 2124  0
         Object src = event.getSource();
 2125  0
         boolean isComponent = event.getSource() instanceof Component;
 2126  0
         boolean isFiltered =
 2127   
             isComponent && hierarchy.isFiltered((Component)event.getSource());
 2128  0
         Component ultimateComponent = state.getUltimateMouseComponent();
 2129  0
         if (capturingImage) {
 2130   
             // Cancel an image capture on ESC
 2131  0
             if (isKeyRelease(event, KeyEvent.VK_ESCAPE, 0)) {
 2132  0
                 imageCaptureCancel();
 2133   
             }
 2134  0
             else if (captureComponent != null
 2135   
                      && isKeyPress(event, KeyEvent.VK_UP, 0)
 2136   
                      && !(src instanceof Window)) {
 2137  0
                 imageCaptureSelect(true);
 2138   
             }
 2139  0
             else if (captureComponent != null
 2140   
                      && isKeyPress(event, KeyEvent.VK_DOWN, 0)
 2141   
                      && !(src instanceof Window)) {
 2142  0
                 imageCaptureSelect(false);
 2143   
             }
 2144  0
             else if (isKeyRelease(event, captureImageKey, KeyEvent.SHIFT_MASK)
 2145   
                      && !isFiltered && testScript != null
 2146   
                      && ultimateComponent != null) {
 2147  0
                 imageCapture();
 2148   
             }
 2149  0
             return true;
 2150   
         }
 2151  0
         else if (isKeyRelease(event, captureImageKey, KeyEvent.SHIFT_MASK)
 2152   
                  && !isFiltered && testScript != null
 2153   
                  && ultimateComponent != null) {
 2154  0
             imageCaptureStart(ultimateComponent);
 2155  0
             return true;
 2156   
         }
 2157  0
         return false;
 2158   
     }
 2159   
 
 2160  0
     private boolean handleRecordingControl(AWTEvent e) {
 2161  0
         boolean editorActivated = e.getID() == WindowEvent.WINDOW_ACTIVATED
 2162   
             && e.getSource() == view;
 2163  0
         boolean appGone = isAppLaunched()
 2164   
             && countShowingWindows(null) == 0
 2165   
             && (appGroup != null && appGroup.activeCount() == 0)
 2166   
             && System.currentTimeMillis() - lastLaunchTime > 7500;
 2167  0
         if (appGone) 
 2168  0
             Log.debug("Code under test no longer running");
 2169  0
         boolean isComponent = e.getSource() instanceof Component;
 2170  0
         boolean isFiltered =
 2171   
             isComponent && hierarchy.isFiltered((Component)e.getSource());
 2172   
 
 2173  0
         if (isKeyPress(e, captureKey, KeyEvent.SHIFT_MASK)
 2174   
             || editorActivated || appGone) {
 2175  0
             Log.debug("stop recording trigger");
 2176  0
             if (recording) {
 2177  0
                 stopRecording(false);
 2178  0
                 justStoppedRecording = true;
 2179  0
                 if (capturingImage)
 2180  0
                     imageCaptureCancel();
 2181   
             }
 2182   
             else {
 2183  0
                 justStoppedRecording = false;
 2184   
             }
 2185  0
             return true;
 2186   
         }
 2187   
         // Start recording all events on alt+shift+F2
 2188  0
         else if (isKeyRelease(e, captureKey,
 2189   
                               KeyEvent.SHIFT_MASK | KeyEvent.ALT_MASK)) {
 2190  0
             Log.debug("start recording trigger");
 2191  0
             if (!isFiltered && !recording && !justStoppedRecording) {
 2192  0
                 Log.debug("Start recording events (+motion)");
 2193  0
                 startRecording(recorders[1]);
 2194  0
                 return true;
 2195   
             }
 2196   
         }
 2197   
         // Start recording on shift+F2
 2198  0
         else if (isKeyRelease(e, captureKey, KeyEvent.SHIFT_MASK)) {
 2199  0
             Log.debug("start recording trigger");
 2200  0
             if (!isFiltered && !recording && !justStoppedRecording) {
 2201  0
                 Log.debug("Start recording events");
 2202  0
                 startRecording(recorders[0]);
 2203  0
                 return true;
 2204   
             }
 2205   
         }
 2206  0
         return false;
 2207   
     }
 2208   
 
 2209  0
     private void selectComponent(Component c, boolean makeReference) {
 2210  0
         Log.debug("Selected: " + Robot.toString(c));
 2211   
         // We usually want the combo box itself, not its LAF button
 2212  0
         if (c != null
 2213   
             && (c.getParent() instanceof JComboBox)) 
 2214  0
             c = c.getParent();
 2215  0
         if (makeReference && getResolverContext() != null) {
 2216  0
             getResolverContext().addComponent(c);
 2217   
             // FIXME tell the reference browser that the list of references
 2218   
             // has changed. 
 2219   
         }
 2220  0
         view.getComponentBrowser().setSelectedComponent(c);
 2221   
     }
 2222   
 
 2223  0
     private void imageCaptureStart(Component ultimateComponent) {
 2224  0
         Log.debug("image capture locate");
 2225  0
         recording = false;
 2226  0
         capturingImage = true;
 2227  0
         captureComponent = ultimateComponent;
 2228  0
         innermostCaptureComponent = captureComponent;
 2229  0
         if (captureComponent != null) {
 2230  0
             highlighter = new Highlighter(captureComponent);
 2231   
         }
 2232  0
         setStatus("Image capture target is "
 2233   
                   + Robot.toString(captureComponent));
 2234   
     }
 2235   
 
 2236  0
     private void imageCaptureCancel() {
 2237  0
         Log.debug("stop image capture command");
 2238  0
         highlighter.dispose();
 2239  0
         setStatus("Image capture canceled");
 2240  0
         captureComponent = innermostCaptureComponent = null;
 2241  0
         capturingImage = false;
 2242   
     }
 2243   
 
 2244  0
     private void imageCaptureSelect(boolean up) {
 2245  0
         if (up) {
 2246  0
             Log.debug("image capture move up");
 2247  0
             Component parent = captureComponent.getParent();
 2248  0
             if (parent != null) {
 2249  0
                 Log.debug("Changing from "
 2250   
                           + Robot.toString(captureComponent)
 2251   
                           + " to " + Robot.toString(parent));
 2252  0
                 highlighter.dispose();
 2253  0
                 highlighter = new Highlighter(parent);
 2254  0
                 captureComponent = parent;
 2255   
             }
 2256   
         }
 2257   
         else {
 2258  0
             Log.debug("image capture move down");
 2259  0
             if (captureComponent instanceof Container) {
 2260  0
                 Component[] subs = ((Container)captureComponent).getComponents();
 2261  0
                 for (int i=0;i < subs.length;i++) {
 2262  0
                     if (SwingUtilities.
 2263   
                         isDescendingFrom(innermostCaptureComponent,
 2264   
                                          subs[i])) {
 2265  0
                         Log.debug("Changing from "
 2266   
                                   + Robot.toString(captureComponent)
 2267   
                                   + " to " + Robot.toString(subs[i]));
 2268  0
                         highlighter.dispose();
 2269  0
                         highlighter = new Highlighter(subs[i]);
 2270  0
                         captureComponent = subs[i];
 2271  0
                         break;
 2272   
                     }
 2273   
                 }
 2274   
             }
 2275   
         }
 2276  0
         setStatus("Image capture target is "
 2277   
                   + Robot.toString(captureComponent));
 2278   
     }
 2279   
 
 2280  0
     private void imageCapture() {
 2281  0
         Log.debug("image capture snapshot");
 2282  0
         setStatus("Capturing image...");
 2283  0
         view.repaint();
 2284  0
         highlighter.dispose();
 2285   
         // Must wait for the highlight to go away
 2286  0
         new Thread("wait for repaint") {
 2287  0
             public void run() {
 2288  0
                 while (Toolkit.getDefaultToolkit().getSystemEventQueue().
 2289   
                        peekEvent() != null) {
 2290  0
                     try { sleep(10); } catch(InterruptedException e) { }
 2291   
                 }
 2292  0
                 SwingUtilities.invokeLater(new Runnable() {
 2293  0
                     public void run() {
 2294  0
                         Step step = captureComponentImage(captureComponent);
 2295  0
                         if (step != null) {
 2296   
                             // If we capture while recording, add it to the
 2297   
                             // recorder's stream.  Otherwise, add it directly.
 2298  0
                             if (recorder != null) {
 2299  0
                                 recorder.insertStep(step);
 2300   
                             }
 2301   
                             else {
 2302  0
                                 addStep(step);
 2303   
                             }
 2304   
                         }
 2305  0
                         setStatus("Capturing image...done");
 2306  0
                         captureComponent = innermostCaptureComponent = null;
 2307  0
                         capturingImage = false;
 2308   
                     }
 2309   
                 });
 2310   
             }
 2311   
         }.start();
 2312   
     }
 2313   
 
 2314   
     /** Return a List of script filenames in the current suite. */
 2315  0
     private List getScripts() {
 2316  0
         if (testScriptList == null) {
 2317  0
             testScriptList = getScripts(testSuite);
 2318   
         }
 2319  0
         return testScriptList;
 2320   
     }
 2321   
 
 2322   
     /** Return a List of script filenames contained in the given Test. */
 2323  0
     private List getScripts(junit.framework.Test node) {
 2324  0
         ArrayList names = new ArrayList();
 2325  0
         if (node == null) {
 2326   
         }
 2327  0
         else if (node instanceof ScriptFixture) {
 2328  0
             names.add(((ScriptFixture)node).getName());
 2329   
         }
 2330  0
         else if (node instanceof junit.framework.TestSuite) {
 2331  0
             Enumeration e = ((junit.framework.TestSuite) node).tests();
 2332  0
             while (e.hasMoreElements()) {
 2333  0
                 junit.framework.Test test =
 2334   
                     (junit.framework.Test) e.nextElement();
 2335  0
                 names.addAll(getScripts(test));
 2336   
             }
 2337   
         }
 2338  0
         else if (node instanceof junit.extensions.TestDecorator) {
 2339  0
             junit.framework.Test base =
 2340   
                 ((junit.extensions.TestDecorator)node).getTest();
 2341  0
             names.addAll(getScripts(base));
 2342   
         }
 2343   
         //Log.debug("Test scripts under " + node + ": " + names.size());
 2344  0
         return names;
 2345   
     }
 2346   
 
 2347   
     /** Returns the test case at the given index.  */
 2348  0
     private Script getScriptAt(int index) {
 2349  0
         List filenames = getScripts();
 2350  0
         if (index >= filenames.size())
 2351  0
             index = filenames.size() - 1;
 2352  0
         return new Script((String)filenames.get(index), hierarchy);
 2353   
     }
 2354   
 
 2355   
     /** Create a new step and insert it at the cursor. */
 2356  0
     private void insertTesterCall(ComponentTester tester,
 2357   
                                   Method method,
 2358   
                                   Class componentClass,
 2359   
                                   String id, String[] argList,
 2360   
                                   boolean wait, boolean invert)
 2361   
         throws NoSuchReferenceException {
 2362  0
         String expectedResult = "true";
 2363  0
         String methodName = method.getName();
 2364   
 
 2365  0
         if (methodName.startsWith("assert")) {
 2366  0
             Assert step;
 2367  0
             if (id == null) {
 2368   
                 // Built-in ComponentTester assertion
 2369  0
                 step = new Assert(getResolverContext(), null,
 2370   
                                   method.getDeclaringClass().getName(),
 2371   
                                   methodName, argList, expectedResult,
 2372   
                                   invert);
 2373   
             }
 2374   
             else {
 2375   
                 // Property method on a component
 2376  0
                 ComponentReference ref = getComponentReference(id);
 2377  0
                 if (ref == null)
 2378  0
                     throw new NoSuchReferenceException(id);
 2379  0
                 step = new Assert(getResolverContext(), null,
 2380   
                                   methodName, argList,
 2381   
                                   method.getDeclaringClass(),
 2382   
                                   expectedResult, invert);
 2383   
             }
 2384  0
             step.setWait(wait);
 2385  0
             addStep(step);
 2386   
         }
 2387  0
         else if (methodName.startsWith("action")) {
 2388  0
             if (id == null) {
 2389   
                 // non-component action
 2390  0
                 addStep(new Action(getResolverContext(), null, methodName, argList));
 2391   
             }
 2392   
             else {
 2393  0
                 ComponentReference ref = getComponentReference(id);
 2394  0
                 if (ref == null)
 2395  0
                     throw new NoSuchReferenceException(id);
 2396  0
                 addStep(new Action(getResolverContext(), null,
 2397   
                                    methodName, argList,
 2398   
                                    method.getDeclaringClass()));
 2399   
             }
 2400   
         }
 2401   
         else {
 2402   
             // It's an tester-provided property method
 2403  0
             ComponentReference ref = getComponentReference(id);
 2404  0
             if (ref == null) 
 2405  0
                 throw new NoSuchReferenceException(id);
 2406  0
             addStep(new Assert(getResolverContext(), null,
 2407   
                                methodName, argList,
 2408   
                                tester.getTestedClass(componentClass), 
 2409   
                                expectedResult, invert));
 2410   
         }
 2411   
     }
 2412   
 
 2413   
     /** Update the UI to reflect the current state of the script's
 2414   
      * execution.
 2415   
      */ 
 2416  0
     private void reflectScriptExecutionState(StepEvent ev) {
 2417  0
         Step step = ev.getStep();
 2418  0
         String cmd = ev.getType();
 2419  0
         Log.debug("Got step event " + ev);
 2420  0
         if (cmd.equals(StepEvent.STEP_START)) {
 2421  0
             if (step == stopStep) {
 2422  0
                 runner.stop();
 2423  0
                 stopStep = null;
 2424   
             }
 2425  0
             if (step != testScript) {
 2426  0
                 final int row = scriptModel.getRowOf(step);
 2427  0
                 if (row == -1) {
 2428   
                     // Step not visible, ignore
 2429   
                 }
 2430   
                 else {
 2431  0
                     SwingUtilities.invokeLater(new Runnable() {
 2432  0
                         public void run() {
 2433  0
                             scriptTable.setRowSelectionInterval(row, row);
 2434  0
                             scriptTable.setCursorLocation(row + 1);
 2435  0
                             Rectangle rect = scriptTable.
 2436   
                                 getCellRect(row, 0, true);
 2437  0
                             scriptTable.scrollRectToVisible(rect);
 2438   
                         }
 2439   
                     });
 2440   
                 }
 2441  0
                 int i = testScript.indexOf(step);
 2442  0
                 if (i != -1) {
 2443  0
                     setStatus(Strings.get("RunningStep", new Object[] {
 2444   
                         String.valueOf(i+1),
 2445   
                         String.valueOf(testScript.size())
 2446   
                     }));
 2447   
                 }
 2448   
             }
 2449   
         }
 2450  0
         else if (cmd.equals(StepEvent.STEP_FAILURE)
 2451   
                  || cmd.equals(StepEvent.STEP_ERROR)) {
 2452   
             // Make sure the table updates its colors
 2453  0
             int index = scriptModel.getRowOf(step);
 2454  0
             if (index != -1) {
 2455  0
                 scriptModel.fireTableRowsUpdated(index, index);
 2456   
             }
 2457  0
             setStatusForStep(step);
 2458   
         }
 2459   
     }
 2460   
 
 2461  16
     Resolver getResolverContext() {
 2462  16
         return scriptTable.getScriptContext();
 2463   
     }
 2464   
 
 2465   
     /** From abbot.Resolver. */
 2466  0
     public ComponentReference getComponentReference(String refid) {
 2467  0
         return getResolverContext() != null
 2468   
             ? getResolverContext().getComponentReference(refid) : null;
 2469   
     }
 2470   
 
 2471   
     /** From abbot.Resolver. */
 2472  0
     public ComponentReference getComponentReference(Component comp) {
 2473  0
         return getResolverContext() != null
 2474   
             ? getResolverContext().getComponentReference(comp) : null;
 2475   
     }
 2476   
 
 2477   
     /** From abbot.Resolver. */
 2478  0
     public void addComponentReference(ComponentReference ref) {
 2479  0
         if (getResolverContext() == null) {
 2480  0
             throw new RuntimeException(Strings.get("NoContext"));
 2481   
         }
 2482  0
         getResolverContext().addComponentReference(ref);
 2483   
     }
 2484   
 
 2485   
     /** From abbot.Resolver. */
 2486  0
     public ComponentReference addComponent(Component comp) {
 2487  0
         if (getResolverContext() == null) {
 2488  0
             throw new RuntimeException(Strings.get("NoContext"));
 2489   
         }
 2490  0
         return getResolverContext().addComponent(comp);
 2491   
     }
 2492   
 
 2493   
     /** From abbot.Resolver. */
 2494  2
     public Collection getComponentReferences() {
 2495  2
         if (getResolverContext() == null) {
 2496  2
             return new HashSet();
 2497   
         }
 2498  0
         return getResolverContext().getComponentReferences();
 2499   
     }
 2500   
 
 2501   
     /** From abbot.Resolver. */
 2502  0
     public String getContext(Step step) {
 2503  0
         Resolver r = getResolverContext();
 2504  0
         if (r != null)
 2505  0
             return r.getContext(step);
 2506  0
         return "unknown";
 2507   
     }
 2508   
 
 2509   
     /** From abbot.Resolver. */
 2510  0
     public File getDirectory() {
 2511  0
         if (getResolverContext() == null) {
 2512  0
             return new File(System.getProperty("user.dir"));
 2513   
         }
 2514  0
         return getResolverContext().getDirectory();
 2515   
     }
 2516   
 
 2517   
     /** From abbot.Resolver. */
 2518  0
     public void setProperty(String name, Object value) {
 2519  0
         if (getResolverContext() != null) {
 2520  0
             getResolverContext().setProperty(name, value);
 2521   
         }
 2522   
     }
 2523   
 
 2524   
     /** From abbot.Resolver. */
 2525  0
     public Object getProperty(String name) {
 2526  0
         if (getResolverContext() != null) {
 2527  0
             return getResolverContext().getProperty(name);
 2528   
         }
 2529  0
         return null;
 2530   
     }
 2531   
 
 2532   
     /** From abbot.Resolver. */
 2533  0
     public ClassLoader getContextClassLoader() {
 2534  0
         if (getResolverContext() != null) {
 2535  0
             return getResolverContext().getContextClassLoader();
 2536   
         }
 2537  0
         return Thread.currentThread().getContextClassLoader();
 2538   
     }
 2539   
 
 2540  0
     public Hierarchy getHierarchy() {
 2541  0
         return hierarchy;
 2542   
     }
 2543   
 
 2544  0
     public String toString() { return name; }
 2545   
 
 2546  8
     private void stepSelectionChanged() {
 2547   
         // ensure the stop step colorization gets cleared 
 2548  8
         stopStep = null;
 2549  8
         setStepEditor();
 2550  8
         setActionsEnabledState();
 2551   
     }
 2552   
     
 2553   
     // FIXME this is slow on OSX
 2554  8
     private void setStepEditor(Step step) {
 2555  8
         final StepEditor editor;
 2556  8
         ignoreEvents = true;
 2557  ?
         if (step != null
 2558   
             && (editor = StepEditor.getEditor(step)) != null) {
 2559  2
             view.setEditor(editor);
 2560   
             // make sure the script model listens to changes from the
 2561   
             // step editor 
 2562  2
             editor.addStepChangeListener(new StepChangeListener() {
 2563  0
                 public void stepChanged(Step step) {
 2564  0
                     int row = scriptModel.getRowOf(step);
 2565  0
                     if (row != -1)
 2566  0
                         scriptModel.fireTableRowsUpdated(row, row);
 2567   
                 }
 2568   
             });
 2569   
         }
 2570   
         else {
 2571  6
             Log.debug("No editor available for '" + step + "'");
 2572  6
             view.setEditor(null);
 2573   
         }
 2574  8
         ignoreEvents = false;
 2575   
         // Update the component browser if the context changes
 2576  8
         view.getComponentBrowser().setResolver(getResolverContext());
 2577   
     }
 2578   
 
 2579  0
     private static String usage() {
 2580  0
         return ScriptEditor.class.getName() + " [suite classname]";
 2581   
     }
 2582   
 
 2583   
     private class LaunchAction implements Runnable {
 2584   
         private final Step which;
 2585   
         private final List savedState;
 2586   
         private final Runnable onCompletion;
 2587   
         private final String completionMessage;
 2588   
         private final boolean launch;
 2589   
 
 2590  0
         private LaunchAction(Step which, List savedState, Runnable onCompletion, 
 2591   
                                String completionMessage, boolean launch) {
 2592  0
             this.which = which;
 2593  0
             this.savedState = savedState;
 2594  0
             this.onCompletion = onCompletion;
 2595  0
             this.completionMessage = completionMessage;
 2596  0
             this.launch = launch;
 2597   
         }
 2598   
 
 2599  0
         public void run() {
 2600  0
             try {
 2601  0
                 if (launch) {
 2602  0
                     if (which instanceof Script) {
 2603  0
                         UIContext context = which instanceof UIContext
 2604   
                             ? (UIContext)which
 2605   
                             : ((Script)which).getUIContext();
 2606  0
                         if (context != null)
 2607  0
                             context.launch(runner);
 2608   
                     }
 2609   
                 }
 2610   
                 else {
 2611  0
                     runner.run(which);
 2612   
                 }
 2613  0
                 if (completionMessage != null)
 2614  0
                     setStatus(completionMessage);
 2615   
             }
 2616   
             catch(Throwable e) {
 2617   
                 // launch didn't work, get rid of it
 2618  0
                 if (launch)
 2619  0
                     terminate();
 2620  0
                 setStatus(e.getMessage(), getStackTrace(e), ERROR);
 2621   
             }
 2622  0
             SwingUtilities.invokeLater(new Runnable() {
 2623  0
                 public void run() {
 2624  0
                     AWT.reenableHierarchy(savedState);
 2625  0
                     isScriptRunning = false;
 2626  0
                     setActionsEnabledState();
 2627  0
                     if (onCompletion != null) {
 2628  0
                         onCompletion.run();
 2629   
                     }
 2630   
                 }
 2631   
             });
 2632   
         }
 2633   
     }
 2634   
 
 2635   
     /**
 2636   
      * This class responds to changes in the script table's selection.
 2637   
      */
 2638   
     private class ScriptTableSelectionHandler
 2639   
         implements ListSelectionListener {
 2640  8
         public void valueChanged(ListSelectionEvent ev) {
 2641  8
             if (ev.getValueIsAdjusting() || isScriptRunning)
 2642  0
                 return;
 2643  8
             stepSelectionChanged();
 2644   
         }
 2645   
     }
 2646   
 
 2647  8
     private void setStepEditor() {
 2648  8
         Step step = scriptTable.getSelectedRowCount() == 1
 2649   
             ? scriptModel.getStepAt(scriptTable.getSelectedRow()) : null;
 2650  8
         setStepEditor(step);
 2651  8
         setStatusForStep(step);
 2652  8
         setActionsEnabledState();
 2653   
     }
 2654   
 
 2655   
     /**
 2656   
      * Handle the current test case/script selection from the current test
 2657   
      * suite (if any).
 2658   
      */
 2659   
     private class ScriptSelectorItemHandler implements ItemListener {
 2660  0
         public void itemStateChanged(ItemEvent e) {
 2661  0
             if (ItemEvent.SELECTED == e.getStateChange() 
 2662   
                 && !ignoreComboBox) {
 2663  0
                 if (checkSaveBeforeClose()) {
 2664  0
                     setScript(new Script((String)e.getItem(), hierarchy));
 2665   
                 }
 2666   
                 else {
 2667  0
                     setScript(testScript);
 2668   
                 }
 2669   
             }
 2670   
         }
 2671   
     }
 2672   
 
 2673   
     /** Provide a global editor context for anyone else that wants to display
 2674   
      * an appropriate dialog message.
 2675   
      */
 2676   
     // FIXME move to ScriptEditorFrame
 2677   
     private static ScriptEditor editor = null;
 2678   
 
 2679   
     /** This action shows an input dialog to collect arguments for a given
 2680   
      * method on a ComponentTester class and adds an assertion or action to
 2681   
      * the current script. 
 2682   
      */
 2683   
     private class TesterMethodAction
 2684   
         extends EditorAction implements Comparable {
 2685   
         private Method method;
 2686   
         private ComponentTester tester;
 2687   
         private boolean wait;
 2688   
 
 2689  58
         public TesterMethodAction(ComponentTester t, Method m, boolean w) {
 2690  58
             super(m.getName());
 2691  58
             putValue(NAME, getName(m));
 2692  58
             putValue(SMALL_ICON, getIcon(m));
 2693  58
             wait = w;
 2694  58
             tester = t;
 2695  58
             method = m;
 2696   
         }
 2697   
 
 2698  114
         public int compareTo(Object o) {
 2699  114
             if (o instanceof TesterMethodAction) {
 2700  114
                 return getName().compareTo(((TesterMethodAction)o).getName());
 2701   
             }
 2702  0
             return 0;
 2703   
         }
 2704   
 
 2705  0
         public void actionPerformed(ActionEvent ev) {
 2706  0
             addTesterCall(method, tester, wait, getArgumentsDescription());
 2707   
         }
 2708   
 
 2709   
         /** Return the human-readable menu name for the action. */
 2710  58
         private String getName(Method m) {
 2711  58
             String name = m.getName();
 2712   
             // First try the resource bundle, then a system property
 2713  58
             String menu = Strings.get(name + ".menu", true);
 2714  58
             if (menu == null) {
 2715  58
                 menu = System.getProperty(name + ".menu");
 2716   
                 // Default to the stripped-down method name
 2717  58
                 if (menu == null) {
 2718  58
                     if (name.startsWith("action")
 2719   
                         || name.startsWith("assert"))
 2720  58
                         menu = name.substring(6);
 2721  0
                     else if (name.startsWith("is"))
 2722  0
                         menu = name.substring(2);
 2723  0
                     else if (name.startsWith("get"))
 2724  0
                         menu = name.substring(3);
 2725   
                     else
 2726  0
                         menu = name;
 2727   
                     // Break up words if we can
 2728  58
                     menu = TextFormat.wordBreak(menu);
 2729   
                 }
 2730   
             }
 2731  58
             return menu;
 2732   
         }
 2733   
     
 2734  58
         private Icon getIcon(Method m) {
 2735   
             // This loads the icon if it's in a jar file.  Doesn't seem to work
 2736   
             // when loading from a raw class file in the classpath.
 2737  58
             Icon icon = null;
 2738  58
             String path = Strings.get(m.getName() + ".icon", true);
 2739  58
             if (path == null) {
 2740  58
                 path = System.getProperty(m.getName() + ".icon");
 2741   
             }
 2742  58
             if (path != null) {
 2743  0
                 URL url = ScriptEditor.class.getResource(path);
 2744  0
                 if (url == null) {
 2745  0
                     url = ScriptEditor.class.getResource("icons/" + path + ".gif");
 2746   
                 }
 2747  0
                 if (url != null) {
 2748  0
                     icon = new ImageIcon(url);
 2749   
                 }
 2750   
             }
 2751  58
             return icon;
 2752   
         }
 2753   
     
 2754   
         /** Provide a description of required arguments for this method. */
 2755  0
         private String getArgumentsDescription() {
 2756  0
             String name = method.getName();
 2757  0
             String cname = Robot.simpleClassName(method.getDeclaringClass());
 2758  0
             String args = Strings.get(cname + "." + name + ".args", true);
 2759  0
             if (args == null) {
 2760  0
                 args = System.getProperty(cname + "." + name + ".args");
 2761  0
                 if (args == null) {
 2762  0
                     args = method.toString();
 2763   
                 }
 2764   
             }
 2765  0
             return args;
 2766   
         }
 2767   
         
 2768  274
         public String getName() { return (String)getValue(NAME); }
 2769  0
         public int hashCode() { return getName().hashCode(); }
 2770  0
         public boolean equals(Object o) {
 2771  0
             return o instanceof TesterMethodAction
 2772   
                 && getName().equals(((TesterMethodAction)o).getName());
 2773   
         }
 2774   
     }
 2775   
 
 2776   
     /** Security manager to prevent applications under test from exiting.
 2777   
      * StepRunner provides one of these, but we need to do additional 
 2778   
      * checking in the context of the script editor.
 2779   
      */
 2780   
     private class EditorSecurityManager
 2781   
         extends NoExitSecurityManager {
 2782   
 
 2783  1127
         public void checkRead(String file) {
 2784   
             // avoid annoying drive A: bug on w32
 2785   
         }
 2786   
         /** We do additional checking to allow exit from the editor itself. */
 2787  0
         public void checkExit(int status) {
 2788   
             // Only allow exits by the root script editor
 2789  0
             if (!rootIsExiting) {
 2790  0
                 super.checkExit(status);
 2791   
             }
 2792   
         }
 2793  0
         protected void exitCalled(int status) {
 2794  0
             terminate();
 2795   
         }
 2796   
     }
 2797   
 
 2798  2
     private void hideView() {
 2799  2
         hiding = true;
 2800  2
         view.hide();
 2801   
     }
 2802   
 
 2803  2
     private void disposeView() {
 2804   
         // Close this frame; flag to the view to allow it to be disposed
 2805  2
         exiting = true;
 2806  2
         view.dispose();
 2807   
     }
 2808   
 
 2809  2
     void dispose() {
 2810   
         // Close any application under test
 2811  2
         terminate();
 2812  2
         normalizer.stopListening();
 2813  2
         hideView();
 2814  2
         disposeView();
 2815   
 
 2816   
         // Get rid of the security manager
 2817  2
         if (securityManager != null) {
 2818  2
             System.setSecurityManager(oldSecurityManager);
 2819  2
             securityManager = null;
 2820   
         }
 2821   
     }
 2822   
 
 2823  0
     private static void bugCheck() {
 2824  0
         Window w = Costello.getSplashScreen();
 2825  0
         if (w != null) {
 2826   
             // Test for bugs that the user should know about
 2827  0
             String[] bugs = Bugs.bugCheck(Costello.getSplashScreen());
 2828  0
             for (int i=0;i < bugs.length;i++) {
 2829  0
                 String title = Strings.get("BugWarning.title");
 2830  0
                 String msg = TextFormat.dialog(bugs[i]);
 2831  0
                 JOptionPane.showMessageDialog(w, msg, title, 
 2832   
                                               JOptionPane.WARNING_MESSAGE);
 2833   
             }
 2834   
         }            
 2835   
     }
 2836   
 
 2837   
     /** Launch the script editor, with an argument of either a test suite
 2838   
      * class or a script filename.
 2839   
      */
 2840  0
     public static void main(String[] args) {
 2841  0
         try {
 2842  0
             args = Log.init(args);
 2843   
             
 2844  0
             bugCheck();
 2845   
 
 2846  0
             if (args.length > 1) {
 2847  0
                 System.err.println("usage: " + usage());
 2848  0
                 System.exit(1);
 2849   
             }
 2850   
 
 2851  0
             editor = new ScriptEditor();
 2852   
             
 2853   
             // Load the requested script or suite
 2854  0
             String arg = args.length == 1 ? args[0] : null;
 2855  0
             if (arg != null
 2856   
                 && new File(arg).exists()
 2857   
                 && Script.isScript(new File(arg))) {
 2858  0
                 editor.setTestSuite(null);
 2859  0
                 editor.setScript(arg);
 2860   
             }
 2861   
             else {
 2862  0
                 editor.setTestSuite(arg);
 2863  0
                 editor.setScript(0);
 2864   
             }
 2865   
             
 2866   
             // Make sure everything currently extant is
 2867   
             // ignored in the test hierarchy.
 2868  0
             editor.hierarchy.ignoreExisting();
 2869  0
             editor.view.pack();
 2870  0
             editor.view.show();
 2871   
             // Don't start listening to events until we're done generating them
 2872   
             // (the "show" above can trigger deadlocks when the framework is
 2873   
             // under test).
 2874  0
             editor.startListening();
 2875   
         }
 2876   
         catch(Throwable e) {
 2877  0
             if (editor != null)
 2878  0
                 editor.dispose();
 2879  0
             System.err.println("Unexpected exception trying to launch "
 2880   
                                + "the script editor");
 2881  0
             e.printStackTrace();
 2882  0
             System.exit(1);
 2883   
         }
 2884   
     }
 2885   
 
 2886   
     /**
 2887   
      * @return Returns whether the code under test is currently launched.
 2888   
      */
 2889  73
     private boolean isAppLaunched() {
 2890  73
         if (testScript != null) {
 2891  72
             UIContext ctxt = testScript.getUIContext();
 2892  72
             if (ctxt != null)
 2893  46
                 return ctxt.isLaunched();
 2894   
         }
 2895  27
         return false;
 2896   
     }
 2897   
 
 2898   
     private class RecordAllAction extends EditorAction {
 2899   
         private Recorder recorder;
 2900  4
         public RecordAllAction(String actionName, Recorder rec,
 2901   
                                boolean extraModifier) {
 2902  4
             super(actionName);
 2903  4
             recorder = rec;
 2904  4
             int mask = InputEvent.SHIFT_MASK;
 2905  4
             if (extraModifier)
 2906  2
                 mask |= InputEvent.ALT_MASK;
 2907  4
             putValue(ACCELERATOR_KEY, 
 2908   
                      KeyStroke.getKeyStroke(captureKey, mask));
 2909   
         }
 2910  0
         public void actionPerformed(ActionEvent ev) {
 2911  0
             Log.debug("Menu action: start recording due to "
 2912   
                       + ev.getActionCommand());
 2913  0
             startRecording(recorder);
 2914   
         }
 2915   
     }
 2916   
 
 2917   
     private class EditorAboutAction extends EditorAction {
 2918  2
         public EditorAboutAction() { super(ACTION_EDITOR_ABOUT); }
 2919  0
         public void actionPerformed(ActionEvent e) {
 2920  0
             view.showAboutBox();
 2921   
         }
 2922   
     }
 2923   
 
 2924   
     private class EditorEmailAction extends EditorAction {
 2925  2
         public EditorEmailAction() { super(ACTION_EDITOR_EMAIL); }
 2926  0
         public void actionPerformed(ActionEvent e) {
 2927  0
             new Thread("mailing-list") {
 2928  0
                 public void run() {
 2929  0
                     try {
 2930  0
                         Launcher.mail(Strings.get("editor.email.list"),
 2931   
                                       Strings.get("editor.email.subject"),
 2932   
                                       Strings.get("editor.email.body",
 2933   
                                                   new Object[] {
 2934   
                                                       BugReport.getSystemInfo()
 2935   
                                                   }));
 2936   
                     }
 2937   
                     catch(IOException e) {
 2938  0
                         view.showWarning(e.getMessage());
 2939   
                     }
 2940   
                 }
 2941   
             }.start();
 2942   
         }
 2943   
     }
 2944   
 
 2945   
     private class EditorBugReportAction extends EditorAction {
 2946  2
         public EditorBugReportAction() { super(ACTION_EDITOR_BUGREPORT); }
 2947  0
         public void actionPerformed(ActionEvent e) {
 2948  0
             new Thread("bug-report") {
 2949  0
                 public void run() {
 2950  0
                     try {
 2951  0
                         Launcher.open(Strings.get("editor.submit_bug"));
 2952   
                     }
 2953   
                     catch(IOException e) {
 2954  0
                         view.showWarning(e.getMessage());
 2955   
                     }
 2956   
                 }
 2957   
             }.start();
 2958   
         }
 2959   
     }
 2960   
 
 2961   
     private class EditorWebsiteAction extends EditorAction {
 2962  2
         public EditorWebsiteAction() { super(ACTION_EDITOR_WEBSITE); }
 2963  0
         public void actionPerformed(ActionEvent e) {
 2964  0
             new Thread("mailing-list") {
 2965  0
                 public void run() {
 2966  0
                     try {
 2967  0
                         Launcher.open(Strings.get("editor.website"));
 2968   
                     }
 2969   
                     catch(IOException e) {
 2970  0
                         view.showWarning(e.getMessage());
 2971   
                     }
 2972   
                 }
 2973   
             }.start();
 2974   
         }
 2975   
     }
 2976   
 
 2977   
     private class EditorUserGuideAction extends EditorAction {
 2978  2
         public EditorUserGuideAction() { super(ACTION_EDITOR_USERGUIDE); }
 2979  0
         public void actionPerformed(ActionEvent e) {
 2980  0
             new Thread("mailing-list") {
 2981  0
                 public void run() {
 2982  0
                     try {
 2983  0
                         Launcher.open(Strings.get("editor.userguide"));
 2984   
                     }
 2985   
                     catch(IOException e) {
 2986  0
                         view.showWarning(e.getMessage());
 2987   
                     }
 2988   
                 }
 2989   
             }.start();
 2990   
         }
 2991   
     }
 2992   
 
 2993   
     private class EditorQuitAction extends EditorAction {
 2994  2
         public EditorQuitAction() { super(ACTION_EDITOR_QUIT); }
 2995  0
         public void actionPerformed(ActionEvent e) {
 2996  0
             quitApplication();
 2997   
         }
 2998   
     }
 2999   
     private class ScriptNewAction extends EditorAction {
 3000  2
         public ScriptNewAction() { super(ACTION_SCRIPT_NEW); }
 3001  0
         public void actionPerformed(ActionEvent e) {
 3002  0
             newScript(false);
 3003   
         }
 3004