package TestTools; import java.awt.*; import java.awt.event.*; import javax.swing.*; import java.io.*; import java.util.*; import java.lang.reflect.*; /** * SwingRobot is a front end to the java.awt.Robot class. * This program reads an ASCII script file (.rsf) that contains commands * to the Swing robot. The commands are then translated into method calls * to the AWT Robot class. * @author David Cumberland. * Modifications by John Dalbey, Jason Scaroni * @version 2.0 4/15/06 * Modified to invoke the target application's main method, if it exists. * Read main method arguments from a AUT.propeties file (key is "commandline"). * @version 2.1 6/10/2006 Added support for the colon character, used in web protocols. * @version 3.0 2/4/2007 Added semicolon character. * Added newClient script command. * @verssion 4.0 2014/1/18 Refactored to allow calling with a Scanner * * Script File Commands * -- two dashes indicate a comment * press_key keyname --depress a key * release_key keyname --release a key * type_key keyname --depress and release a key * type_string string --types a string of characters (not enclosed in quotes) * -- cannot contain control characters * mouse_move x,y * mouse_down * mouse_up * mouse_press x,y --combines a mouse move with a mouse down and up * */ public class SwingRobot { private static HashMap mKeys = new HashMap(); private String appName; // AUT name private Class targetClass = null; // This Class represents the user's application private int delayDuration; // time to delay after each command private String scriptFilename; private Scanner scriptInput; private boolean logging = false; // is logging on or off static { createHashtable(); } public SwingRobot(){} public SwingRobot(String aut, Scanner scriptScanner) { this.appName = aut; this.scriptInput = scriptScanner; } public void setDelayDuration(int durationInMillis) { if (durationInMillis > 0) { delayDuration = durationInMillis; } } public void setLogging(boolean logging) { this.logging = logging; } private boolean processParameters(String[] args) { boolean result = true; //process parameters if (args.length < 1) { System.err.println("Ver 3.0 Usage: provide the application name, a robot script file (.rsf)\n" + "and an optional delay (in ms)."); result = false; } appName = args[0]; if (args.length < 2) { System.err.println("Missing command line parameter: robot script file (.rsf)"); result = false; } scriptFilename = args[1]; //Process the third command line argument, if it exists // which contains a delay duration if (args.length >= 3) { delayDuration = Integer.parseInt(args[2]); } else { //Note: When I had this 500 the robot would issue duplicate key presses. Mystery. delayDuration = 250; // default = .25 seconds } return result; } private boolean loadScriptFile() { boolean result = true; //open the script file for processing try { scriptInput = new Scanner(new File(scriptFilename)); } catch (Exception ex) { ex.printStackTrace(); System.err.println("Unable to open script file: " + scriptFilename); result = false; } return result; } public void runScript() { Robot rob = null; // the instance of AWT Robot String line; // input line from script file StringTokenizer strTok; // to parse input line Integer keyEvent; // user keystroke launchClient(appName); // create a robot to feed in GUI events try { rob = new Robot(); } catch (AWTException ex) { System.err.println("Can't start Robot: " + ex); System.exit(0); } rob.delay(1000); //delay to let the application load rob.setAutoDelay(delayDuration); // set delay that happens after every command try { //process all the lines in file while (scriptInput.hasNextLine()) { line = scriptInput.nextLine(); line = line.trim(); if (logging) { System.out.println("script command: " + line); } //if not a comment and not a short lines (from Unix -> Windows transfer) if (line.length() > 0 && !(line.startsWith("//") || line.startsWith("--"))) { //process the line, interpreting commands // and calling the Robot methods if (line.startsWith("newClient")) { // Create an instance of the application to be controlled launchClient(appName); } else if (line.startsWith("mouse_move")) { line = line.replace(',', ' '); strTok = new StringTokenizer(line, " "); if (strTok.countTokens() < 3) { throw new Exception("***** invalid script command: " + line); } strTok.nextToken(); //Skip the first token rob.mouseMove(Integer.parseInt(strTok.nextToken()), Integer.parseInt(strTok.nextToken())); } else if (line.startsWith("mouse_press")) { line = line.replace(',', ' '); strTok = new StringTokenizer(line, " "); if (strTok.countTokens() < 3) { throw new Exception("***** invalid script command: " + line); } strTok.nextToken(); //Skip the first token rob.mouseMove(Integer.parseInt(strTok.nextToken()), Integer.parseInt(strTok.nextToken())); rob.mousePress(InputEvent.BUTTON1_MASK); rob.mouseRelease(InputEvent.BUTTON1_MASK); } else if (line.startsWith("mouse_down")) { rob.mousePress(InputEvent.BUTTON1_MASK); } else if (line.startsWith("mouse_up")) { rob.mouseRelease(InputEvent.BUTTON1_MASK); } else if (line.startsWith("press_key")) { strTok = new StringTokenizer(line, " "); if (strTok.countTokens() < 2) { throw new Exception("***** invalid script command: " + line); } strTok.nextToken(); //Skip the first token keyEvent = (Integer) mKeys.get(strTok.nextToken().toLowerCase()); if (keyEvent == null) { throw new Exception("***** unrecognized key name: " + line); } rob.keyPress(keyEvent.intValue()); } else if (line.startsWith("release_key")) { strTok = new StringTokenizer(line, " "); if (strTok.countTokens() < 2) { throw new Exception("***** invalid script command: " + line); } strTok.nextToken(); //Skip the first token keyEvent = (Integer) mKeys.get(strTok.nextToken().toLowerCase()); if (keyEvent == null) { throw new Exception("***** unrecognized key name: " + line); } rob.keyRelease(keyEvent.intValue()); } else if (line.startsWith("type_key")) { strTok = new StringTokenizer(line, " "); if (strTok.countTokens() < 2) { throw new Exception("***** invalid script command: " + line); } strTok.nextToken(); //Skip the first token String toke = strTok.nextToken(); //Skip the first token keyEvent = (Integer) mKeys.get(toke.toLowerCase()); //System.out.println("typing key: " + toke); if (keyEvent == null) { throw new Exception("***** unrecognized key name: " + line); } rob.keyPress(keyEvent.intValue()); rob.keyRelease(keyEvent.intValue()); //System.out.println("done typing key: " + toke); } else if (line.startsWith("type_string")) { line = line.substring(12).trim(); rob.setAutoDelay(1); // turn off delay for entering string for (int index = 0; index < line.length(); index++) { char letter = line.charAt(index); if (letter >= 'A' && letter <= 'Z') { rob.keyPress(KeyEvent.VK_SHIFT); } keyEvent = (Integer) mKeys.get(String.valueOf(letter)); rob.keyPress(keyEvent.intValue()); rob.keyRelease(keyEvent.intValue()); rob.keyRelease(KeyEvent.VK_SHIFT); } rob.setAutoDelay(delayDuration); // restore delay } else if (line.startsWith("wait")) { strTok = new StringTokenizer(line, " "); if (strTok.countTokens() < 2) { throw new Exception("***** invalid script command: " + line); } strTok.nextToken(); //Skip the first token rob.delay(Integer.parseInt(strTok.nextToken())); } else { throw new Exception("***** invalid script command: " + line); } //rob.delay(500); } } } catch (Exception ex) { ex.printStackTrace(); } //close the file try { scriptInput.close(); } catch (Exception ex) { ex.printStackTrace(); } //make sure all special mKeys have been turned off rob.keyRelease(KeyEvent.VK_CONTROL); rob.keyRelease(KeyEvent.VK_ALT); } private String[] getProperties() { String[] result = {""}; try { FileInputStream propertiesFile = new FileInputStream("AUT.properties"); // if file was found if (propertiesFile != null) { // Load the properties Properties defaultProperties = new Properties(); defaultProperties.load(propertiesFile); // Get the command line parameters for the target main String cmdString =(String)defaultProperties.get("commandline"); if (cmdString != null) { // put each token into a string array result = cmdString.split(" "); } } } catch (FileNotFoundException ex) { //System.out.println("No properties file found in current directory."); } catch(Exception ex) { ex.printStackTrace(); } return result; } // Create an instance of the application to be controlled private void launchClient(String className) { // Create an instance of the application to be controlled try { targetClass = Class.forName(className); // Check for main, and if not found, call default constructor if (! callMain(targetClass) ) { System.out.println("Invoking default constructor of " + targetClass); Object o = targetClass.newInstance(); } } catch (Exception err) { System.out.println("Error: " + err); err.printStackTrace(); System.exit(1); } } public void killClient() { targetClass = null; } @SuppressWarnings("unchecked") private boolean callMain(Class appClass) { String[] cmdLine = getProperties(); //System.out.println("prepared command line: " + cmdLine[0]); /* Use Reflection to invoke the main method in the target application */ // define the String array type - use getClass on OUR string array. Class[] parameterTypes = new Class[] {cmdLine.getClass()}; Method mainMethod; Object[] arguments = {cmdLine}; try { mainMethod = appClass.getMethod("main", parameterTypes); System.out.println("Invoking main method of " + appClass); mainMethod.invoke(null,arguments); } catch (NoSuchMethodException e) { return false; } catch (IllegalAccessException e) { System.out.println(e); } catch (InvocationTargetException e) { System.out.println(e); } return true; } // end call Main private static void createHashtable() { //create hashtable of input characters mKeys.put("0", new Integer(KeyEvent.VK_0)); mKeys.put("1", new Integer(KeyEvent.VK_1)); mKeys.put("2", new Integer(KeyEvent.VK_2)); mKeys.put("3", new Integer(KeyEvent.VK_3)); mKeys.put("4", new Integer(KeyEvent.VK_4)); mKeys.put("5", new Integer(KeyEvent.VK_5)); mKeys.put("6", new Integer(KeyEvent.VK_6)); mKeys.put("7", new Integer(KeyEvent.VK_7)); mKeys.put("8", new Integer(KeyEvent.VK_8)); mKeys.put("9", new Integer(KeyEvent.VK_9)); mKeys.put("a", new Integer(KeyEvent.VK_A)); mKeys.put("b", new Integer(KeyEvent.VK_B)); mKeys.put("c", new Integer(KeyEvent.VK_C)); mKeys.put("d", new Integer(KeyEvent.VK_D)); mKeys.put("e", new Integer(KeyEvent.VK_E)); mKeys.put("f", new Integer(KeyEvent.VK_F)); mKeys.put("g", new Integer(KeyEvent.VK_G)); mKeys.put("h", new Integer(KeyEvent.VK_H)); mKeys.put("i", new Integer(KeyEvent.VK_I)); mKeys.put("j", new Integer(KeyEvent.VK_J)); mKeys.put("k", new Integer(KeyEvent.VK_K)); mKeys.put("l", new Integer(KeyEvent.VK_L)); mKeys.put("m", new Integer(KeyEvent.VK_M)); mKeys.put("n", new Integer(KeyEvent.VK_N)); mKeys.put("o", new Integer(KeyEvent.VK_O)); mKeys.put("p", new Integer(KeyEvent.VK_P)); mKeys.put("q", new Integer(KeyEvent.VK_Q)); mKeys.put("r", new Integer(KeyEvent.VK_R)); mKeys.put("s", new Integer(KeyEvent.VK_S)); mKeys.put("t", new Integer(KeyEvent.VK_T)); mKeys.put("u", new Integer(KeyEvent.VK_U)); mKeys.put("v", new Integer(KeyEvent.VK_V)); mKeys.put("w", new Integer(KeyEvent.VK_W)); mKeys.put("x", new Integer(KeyEvent.VK_X)); mKeys.put("y", new Integer(KeyEvent.VK_Y)); mKeys.put("z", new Integer(KeyEvent.VK_Z)); mKeys.put("A", new Integer(KeyEvent.VK_A)); mKeys.put("B", new Integer(KeyEvent.VK_B)); mKeys.put("C", new Integer(KeyEvent.VK_C)); mKeys.put("D", new Integer(KeyEvent.VK_D)); mKeys.put("E", new Integer(KeyEvent.VK_E)); mKeys.put("F", new Integer(KeyEvent.VK_F)); mKeys.put("G", new Integer(KeyEvent.VK_G)); mKeys.put("H", new Integer(KeyEvent.VK_H)); mKeys.put("I", new Integer(KeyEvent.VK_I)); mKeys.put("J", new Integer(KeyEvent.VK_J)); mKeys.put("K", new Integer(KeyEvent.VK_K)); mKeys.put("L", new Integer(KeyEvent.VK_L)); mKeys.put("M", new Integer(KeyEvent.VK_M)); mKeys.put("N", new Integer(KeyEvent.VK_N)); mKeys.put("O", new Integer(KeyEvent.VK_O)); mKeys.put("P", new Integer(KeyEvent.VK_P)); mKeys.put("Q", new Integer(KeyEvent.VK_Q)); mKeys.put("R", new Integer(KeyEvent.VK_R)); mKeys.put("S", new Integer(KeyEvent.VK_S)); mKeys.put("T", new Integer(KeyEvent.VK_T)); mKeys.put("U", new Integer(KeyEvent.VK_U)); mKeys.put("V", new Integer(KeyEvent.VK_V)); mKeys.put("W", new Integer(KeyEvent.VK_W)); mKeys.put("X", new Integer(KeyEvent.VK_X)); mKeys.put("Y", new Integer(KeyEvent.VK_Y)); mKeys.put("Z", new Integer(KeyEvent.VK_Z)); //extra characters mKeys.put(" ", new Integer(KeyEvent.VK_SPACE)); mKeys.put("space", new Integer(KeyEvent.VK_SPACE)); mKeys.put("enter", new Integer(KeyEvent.VK_ENTER)); mKeys.put("shift", new Integer(KeyEvent.VK_SHIFT)); mKeys.put("esc", new Integer(KeyEvent.VK_ESCAPE)); mKeys.put("del", new Integer(KeyEvent.VK_DELETE)); mKeys.put("backspace", new Integer(KeyEvent.VK_BACK_SPACE)); mKeys.put("capslock", new Integer(KeyEvent.VK_CAPS_LOCK)); mKeys.put("ctrl", new Integer(KeyEvent.VK_CONTROL)); mKeys.put("alt", new Integer(KeyEvent.VK_ALT)); mKeys.put("tab", new Integer(KeyEvent.VK_TAB)); mKeys.put(".", new Integer(KeyEvent.VK_PERIOD)); mKeys.put("down", new Integer(KeyEvent.VK_DOWN)); mKeys.put("up", new Integer(KeyEvent.VK_UP)); mKeys.put("left", new Integer(KeyEvent.VK_LEFT)); mKeys.put("right", new Integer(KeyEvent.VK_RIGHT)); mKeys.put("-", new Integer(KeyEvent.VK_MINUS)); mKeys.put("f4", new Integer(KeyEvent.VK_F4)); mKeys.put("/", new Integer(KeyEvent.VK_SLASH)); mKeys.put(";", new Integer(KeyEvent.VK_SEMICOLON)); /* Colon character requires special processing. Technically there is no colon on the US keyboard, only a shifted semicolon, so a special check is added in type_string handler to press shift. */ mKeys.put(":", new Integer(KeyEvent.VK_SEMICOLON)); } public static void main(String[] args) { // try // { SwingRobot robot = new SwingRobot(); if (!robot.processParameters(args)) { System.out.println("SwingRobot failed processing command line arguments"); } if (!robot.loadScriptFile()) { System.out.println("SwingRobot failed loading script file."); } robot.runScript(); // } // catch (AWTException ex) // { // System.out.println("Exception running awt.Robot"); // ex.printStackTrace(); // } } }