Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarkus Tiede2013-01-04 09:01:32 +0000
committerMarkus Tiede2013-01-04 09:01:32 +0000
commitbd45bc8860028ca7010b772532e8458a11ba30ce (patch)
treedb27a6a023ad17ca4b2a1beb1344e6cc480a551d
parentee9ce223396c5bd5232aa31a9912849b35979750 (diff)
downloadorg.eclipse.jubula.core-bd45bc8860028ca7010b772532e8458a11ba30ce.tar.gz
org.eclipse.jubula.core-bd45bc8860028ca7010b772532e8458a11ba30ce.tar.xz
org.eclipse.jubula.core-bd45bc8860028ca7010b772532e8458a11ba30ce.zip
Sprint task - second patch 'application refactoring' for enhancement 394179 applied.
-rw-r--r--org.eclipse.jubula.rc.common/src/org/eclipse/jubula/rc/common/caps/AbstractApplicationCAPs.java749
-rw-r--r--org.eclipse.jubula.rc.swing/src/org/eclipse/jubula/rc/swing/swing/caps/SwingApplicationCAPs.java577
-rw-r--r--org.eclipse.jubula.rc.swt/src/org/eclipse/jubula/rc/swt/caps/SwtApplicationCAPs.java548
-rw-r--r--org.eclipse.jubula.toolkit.provider.swing/resources/xml/ComponentConfiguration.xml2
-rw-r--r--org.eclipse.jubula.toolkit.provider.swt/resources/xml/ComponentConfiguration.xml2
5 files changed, 1876 insertions, 2 deletions
diff --git a/org.eclipse.jubula.rc.common/src/org/eclipse/jubula/rc/common/caps/AbstractApplicationCAPs.java b/org.eclipse.jubula.rc.common/src/org/eclipse/jubula/rc/common/caps/AbstractApplicationCAPs.java
new file mode 100644
index 000000000..ce7b602d1
--- /dev/null
+++ b/org.eclipse.jubula.rc.common/src/org/eclipse/jubula/rc/common/caps/AbstractApplicationCAPs.java
@@ -0,0 +1,749 @@
+/*******************************************************************************
+ * Copyright (c) 2012 BREDEX GmbH.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * BREDEX GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jubula.rc.common.caps;
+
+import java.awt.AWTException;
+import java.awt.Dimension;
+import java.awt.Graphics2D;
+import java.awt.Rectangle;
+import java.awt.RenderingHints;
+import java.awt.Toolkit;
+import java.awt.datatransfer.StringSelection;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+
+import javax.imageio.ImageIO;
+
+import org.eclipse.jubula.rc.common.driver.ClickOptions;
+import org.eclipse.jubula.rc.common.driver.IRobot;
+import org.eclipse.jubula.rc.common.driver.KeyTyper;
+import org.eclipse.jubula.rc.common.exception.ExecutionEvent;
+import org.eclipse.jubula.rc.common.exception.OsNotSupportedException;
+import org.eclipse.jubula.rc.common.exception.RobotException;
+import org.eclipse.jubula.rc.common.exception.StepExecutionException;
+import org.eclipse.jubula.rc.common.implclasses.Comparer;
+import org.eclipse.jubula.rc.common.implclasses.IBaseImplementationClass;
+import org.eclipse.jubula.rc.common.implclasses.Verifier;
+import org.eclipse.jubula.rc.common.logger.AutServerLogger;
+import org.eclipse.jubula.rc.common.util.KeyStrokeUtil;
+import org.eclipse.jubula.tools.constants.StringConstants;
+import org.eclipse.jubula.tools.objects.event.EventFactory;
+import org.eclipse.jubula.tools.objects.event.TestErrorEvent;
+import org.eclipse.jubula.tools.utils.ExternalCommandExecutor;
+import org.eclipse.jubula.tools.utils.TimeUtil;
+import org.eclipse.jubula.tools.utils.ExternalCommandExecutor.MonitorTask;
+/**
+ *
+ * @author BREDEX GmbH
+ *
+ */
+public abstract class AbstractApplicationCAPs
+ implements IBaseImplementationClass {
+ /**
+ * String for sequential numbering for screenshots
+ */
+ public static final String RENAME = "rename"; //$NON-NLS-1$
+
+ /**
+ * String for overwriting for screenshots
+ */
+ public static final String OVERWRITE = "overwrite"; //$NON-NLS-1$
+
+ /**
+ * The default format to use when writing images to disk.
+ */
+ private static final String DEFAULT_IMAGE_FORMAT = "png"; //$NON-NLS-1$
+
+ /**
+ * The string used to separate filename and file extension.
+ */
+ private static final String EXTENSION_SEPARATOR = "."; //$NON-NLS-1$
+
+ /** constants for communication */
+ private static final String POS_UNIT_PIXEL = "Pixel"; //$NON-NLS-1$
+
+ /** constants for communication */
+ private static final String POS_UNI_PERCENT = "Percent"; //$NON-NLS-1$
+
+ /**
+ * The logging.
+ */
+ private static AutServerLogger log =
+ new AutServerLogger(AbstractApplicationCAPs.class);
+
+ /**
+ * @param text text to type
+ */
+ public void gdInputText(String text) {
+ getRobot().type(getFocusOwner(), text);
+ }
+
+ /**
+ * Executes the given command and waits for it to finish. If the
+ * execution does not finish in good time, a timeout will occur. If the
+ * exit code for the execution is not the same as the expected code, the
+ * test step fails.
+ *
+ * @param cmd The command to execute.
+ * @param expectedExitCode The expected exit code of the command.
+ * @param local <code>true</code> if the command should be executed on the
+ * local (client) machine. Otherwise (should run on the server side),
+ * this value should be <code>false</code>.
+ * @param timeout The amount of time (in milliseconds) to wait for the
+ * execution to finish.
+ */
+ public void gdExecuteExternalCommand(String cmd, int expectedExitCode,
+ boolean local, int timeout) {
+
+ if (!local) {
+ MonitorTask mt = new ExternalCommandExecutor().executeCommand(
+ null, cmd, timeout);
+
+ if (!mt.wasCmdValid()) {
+ throw new StepExecutionException(
+ "Command not found.", //$NON-NLS-1$
+ EventFactory.createActionError(
+ TestErrorEvent.NO_SUCH_COMMAND));
+ }
+
+ if (mt.hasTimeoutOccurred()) {
+ throw new StepExecutionException(
+ "Timeout received before completing execution of script.", //$NON-NLS-1$
+ EventFactory.createActionError(
+ TestErrorEvent.CONFIRMATION_TIMEOUT));
+ }
+
+ int actualExitValue = mt.getExitCode();
+ if (actualExitValue != expectedExitCode) {
+ throw new StepExecutionException(
+ "Verification of exit code failed.", //$NON-NLS-1$
+ EventFactory.createVerifyFailed(
+ String.valueOf(expectedExitCode),
+ String.valueOf(actualExitValue)));
+ }
+ }
+
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setComponent(Object graphicsComponent) {
+ // Do nothing; Application has no correspaonding component
+ }
+
+ /**
+ * Takes a screenshot and saves the image to disk.
+ *
+ * @param destination
+ * Path and filename for the created image. If the extension is not
+ * ".jpeg" (case-insensitive), ".jpeg" will be appended to the
+ * filename.
+ * @param delay
+ * Amount of time to wait (in milliseconds) before taking the
+ * screenshot.
+ * @param fileAccess
+ * Determines how the file will be created if a file with the
+ * given name and path already exists:<br>
+ * <code>SwingApplicationImplClass.RENAME</code> -
+ * The screenshot will be saved with a sequential integer appended
+ * to the filename.<br>
+ * <code>SwingApplicationImplClass.OVERWRITE</code> -
+ * The screenshot will overwrite the file.
+ * @param scaling
+ * Degree to which the image should be scaled, in percent. A
+ * <code>scaling</code> value of <code>100</code> produces an
+ * unscaled image. This value must be greater than <code>0</code>
+ * and less than or equal to <code>200</code>.
+ * @param createDirs
+ * Determines whether a path will be created if it does not already
+ * exist. A value of <code>true</code> means that all necessary
+ * directories that do not exist will be created automatically.
+ */
+ public void gdTakeScreenshot(String destination, int delay,
+ String fileAccess, int scaling, boolean createDirs) {
+ // Determine current screen size
+ Toolkit toolkit = Toolkit.getDefaultToolkit();
+ Dimension screenSize = toolkit.getScreenSize();
+
+ // If screen !(resolution%2==0) --> bad scaling
+ int screenWidth = (int)screenSize.getWidth();
+ int screenHeight = (int)screenSize.getHeight();
+ if (!(screenWidth % 2 == 0)) {
+ screenWidth = screenWidth - 1;
+ }
+ if (!(screenHeight % 2 == 0)) {
+ screenHeight = screenHeight - 1;
+ }
+
+ screenSize.setSize(screenWidth, screenHeight);
+
+ Rectangle screenRect = new Rectangle(screenSize);
+
+ takeScreenshot(destination, delay, fileAccess, scaling, createDirs,
+ screenRect);
+ }
+
+ /**
+ * Takes a screenshot and saves the image to disk.
+ *
+ * @param destination
+ * Path and filename for the created image. If the extension is
+ * not ".jpeg" (case-insensitive), ".jpeg" will be appended to
+ * the filename.
+ * @param delay
+ * Amount of time to wait (in milliseconds) before taking the
+ * screenshot.
+ * @param fileAccess
+ * Determines how the file will be created if a file with the
+ * given name and path already exists:<br>
+ * <code>SwingApplicationImplClass.RENAME</code> - The screenshot
+ * will be saved with a sequential integer appended to the
+ * filename.<br>
+ * <code>SwingApplicationImplClass.OVERWRITE</code> - The
+ * screenshot will overwrite the file.
+ * @param scaling
+ * Degree to which the image should be scaled, in percent. A
+ * <code>scaling</code> value of <code>100</code> produces an
+ * unscaled image. This value must be greater than <code>0</code>
+ * and less than or equal to <code>200</code>.
+ * @param createDirs
+ * Determines whether a path will be created if it does not
+ * already exist. A value of <code>true</code> means that all
+ * necessary directories that do not exist will be created
+ * automatically.
+ * @param marginTop
+ * the extra top margin
+ * @param marginRight
+ * the extra right margin
+ * @param marginBottom
+ * the extra bottom margin
+ * @param marginLeft
+ * the extra left margin
+ */
+ public void gdTakeScreenshotOfActiveWindow(String destination, int delay,
+ String fileAccess, int scaling, boolean createDirs, int marginTop,
+ int marginRight, int marginBottom, int marginLeft) {
+ Rectangle activeWindowBounds = getActiveWindowBounds();
+
+ if (activeWindowBounds == null) {
+ throw new StepExecutionException("No active window found", //$NON-NLS-1$
+ EventFactory
+ .createActionError(TestErrorEvent.NO_ACTIVE_WINDOW));
+ }
+
+ int x = activeWindowBounds.x - marginLeft;
+ int y = activeWindowBounds.y - marginTop;
+ int width = activeWindowBounds.width + marginLeft + marginRight;
+ int height = activeWindowBounds.height + marginTop + marginBottom;
+
+ if (width < 1 || height < 1) {
+ throw new StepExecutionException("Margin parameter lead to negative height or width", //$NON-NLS-1$
+ EventFactory
+ .createActionError(TestErrorEvent.INVALID_INPUT));
+ }
+
+ Rectangle screenRect = new Rectangle(x, y, width, height);
+
+ takeScreenshot(destination, delay, fileAccess, scaling, createDirs,
+ screenRect);
+ }
+
+ /**
+ * @return an awt rectangle which represents the absolute active window
+ * bounds; may return null e.g. if no active window could be found
+ */
+ public abstract Rectangle getActiveWindowBounds();
+
+
+ /**
+ * Takes a screenshot and saves the image to disk, in JPEG format.
+ *
+ * @param destination
+ * Path and filename for the created image. If the extension is not
+ * ".jpeg" (case-insensitive), ".jpeg" will be appended to the
+ * filename.
+ * @param delay
+ * Amount of time to wait (in milliseconds) before taking the
+ * screenshot.
+ * @param fileAccess
+ * Determines how the file will be created if a file with the
+ * given name and path already exists:<br>
+ * <code>SwingApplicationImplClass.RENAME</code> -
+ * The screenshot will be saved with a sequential integer appended
+ * to the filename.<br>
+ * <code>SwingApplicationImplClass.OVERWRITE</code> -
+ * The screenshot will overwrite the file.
+ * @param scaling
+ * Degree to which the image should be scaled, in percent. A
+ * <code>scaling</code> value of <code>100</code> produces an
+ * unscaled image. This value must be greater than <code>0</code>
+ * and less than or equal to <code>200</code>.
+ * @param createDirs
+ * Determines whether a path will be created if it does not already
+ * exist. A value of <code>true</code> means that all necessary
+ * directories that do not exist will be created automatically.
+ * @param screenShotRect
+ * the rectangle to take the screenshot of
+ */
+ private void takeScreenshot(String destination, int delay,
+ String fileAccess, int scaling, boolean createDirs,
+ Rectangle screenShotRect) {
+ if (scaling <= 0 || scaling > 200) {
+ throw new StepExecutionException(
+ "Invalid scaling factor: Must be between 1 and 200", //$NON-NLS-1$
+ EventFactory.createActionError(
+ TestErrorEvent.INVALID_PARAM_VALUE));
+ }
+
+ double scaleFactor = scaling * 0.01;
+
+ // Check if file name is valid
+ String outFileName = destination;
+ String imageExtension = getExtension(outFileName);
+ if (imageExtension.length() == 0) {
+ // If not, then we simply append the default extension
+ imageExtension = DEFAULT_IMAGE_FORMAT;
+ outFileName += EXTENSION_SEPARATOR + imageExtension;
+ }
+
+ // Wait for a user-specified time
+ if (delay > 0) {
+ TimeUtil.delay(delay);
+ }
+
+ // Create path, if necessary
+ File pic = new File(outFileName);
+ if (pic.getParent() == null) {
+ throw new StepExecutionException(
+ "Invalid file name: specify a file name", //$NON-NLS-1$
+ EventFactory.createActionError(
+ TestErrorEvent.INVALID_PARAM_VALUE));
+ }
+
+ File path = new File(pic.getParent());
+ if (createDirs && !path.exists() && !path.mkdirs()) {
+ throw new StepExecutionException(
+ "Directory path does not exist and could not be created", //$NON-NLS-1$
+ EventFactory.createActionError(
+ TestErrorEvent.FILE_IO_ERROR));
+ }
+
+ // Rename file if file already exists
+ // FIXME zeb This naming scheme can lead to sorting problems when
+ // filenames have varying numbers of digits (ex. "pic_9" and
+ // "pic_10")
+ if (fileAccess.equals(RENAME)) {
+ String completeExtension =
+ EXTENSION_SEPARATOR + imageExtension.toLowerCase();
+ int extensionIndex =
+ pic.getName().toLowerCase().lastIndexOf(completeExtension);
+ String fileName = pic.getName().substring(0, extensionIndex);
+ for (int i = 1; pic.exists(); i++) {
+ pic = new File(pic.getParent(), fileName + "_" + i + completeExtension); //$NON-NLS-1$
+ }
+ }
+
+ takeScreenshot(screenShotRect, scaleFactor, pic);
+ }
+
+ /**
+ * Takes a screenshot and saves the image to disk. This method will attempt
+ * to encode the image according to the file extension of the given
+ * output file. If this is not possible (because the encoding type
+ * is not supported), then the default encoding type will be used. If
+ * the default encoding type is used, an appropriate extension will be added
+ * to the filename.
+ *
+ * @param captureRect
+ * Rect to capture in screen coordinates.
+ * @param scaleFactor
+ * Degree to which the image should be scaled, in percent. A
+ * <code>scaleFactor</code> of <code>100</code> produces an
+ * unscaled image. This value must be greater than <code>0</code>
+ * and less than or equal to <code>200</code>.
+ * @param outputFile
+ * Path and filename for the created image.
+ */
+ public void takeScreenshot(
+ Rectangle captureRect, double scaleFactor, File outputFile) {
+ // Create screenshot
+ java.awt.Robot robot;
+ File out = outputFile;
+
+ try {
+ robot = new java.awt.Robot();
+ BufferedImage image = robot.createScreenCapture(captureRect);
+
+ int scaledWidth = (int) Math.floor(image.getWidth() * scaleFactor);
+ int scaledHeight =
+ (int) Math.floor(image.getHeight() * scaleFactor);
+ BufferedImage imageOut =
+ new BufferedImage(scaledWidth,
+ scaledHeight, BufferedImage.TYPE_INT_RGB);
+ // Scale it to the new size on-the-fly
+ Graphics2D graphics2D = imageOut.createGraphics();
+ graphics2D.setRenderingHint(RenderingHints.KEY_RENDERING,
+ RenderingHints.VALUE_RENDER_QUALITY);
+ graphics2D.drawImage(image, 0, 0, scaledWidth, scaledHeight, null);
+
+ // Save captured image using given format, if supported.
+ String extension = getExtension(out.getName());
+ if (extension.length() == 0
+ || !ImageIO.getImageWritersBySuffix(extension).hasNext()
+ || !ImageIO.write(imageOut, extension, out)) {
+
+ // Otherwise, save using default format
+ out = new File(outputFile.getPath()
+ + EXTENSION_SEPARATOR + DEFAULT_IMAGE_FORMAT);
+ if (!ImageIO.write(imageOut, DEFAULT_IMAGE_FORMAT, out)) {
+
+ // This should never happen, so log as error if it does.
+ // In this situation, the screenshot will not be saved, but
+ // the test step will still be marked as successful.
+ log.error("Screenshot could not be saved. " + //$NON-NLS-1$
+ "Default image format (" + DEFAULT_IMAGE_FORMAT //$NON-NLS-1$
+ + ") is not supported."); //$NON-NLS-1$
+ }
+ }
+
+ } catch (AWTException e) {
+ throw new RobotException(e);
+ } catch (IOException e) {
+ throw new StepExecutionException(
+ "Screenshot could not be saved", //$NON-NLS-1$
+ EventFactory.createActionError(
+ TestErrorEvent.FILE_IO_ERROR));
+ }
+ }
+
+ /**
+ * Waits a specified time.
+ * @param timeMilliSec the time to wait in MilliSec
+ */
+ public void gdWait(int timeMilliSec) {
+ TimeUtil.delay(timeMilliSec);
+ }
+
+ /**
+ * shows a ConfirmDialog and Pause the Execution of the Test until Window
+ * is closed
+ */
+ public void gdPause() {
+ throw new ExecutionEvent(ExecutionEvent.PAUSE_EXECUTION);
+ }
+
+ /**
+ * Does nothing! The restart is implemented in the client but the server
+ * must have an action to execute.
+ */
+ public void gdRestart() {
+ // nothing
+ }
+
+ /**
+ * Types the given text without checking location or event confirmation.
+ *
+ * @param text The text to type.
+ */
+ public void gdNativeInputText(String text) {
+ try {
+ KeyTyper.getInstance().nativeTypeString(text);
+ } catch (AWTException e) {
+ throw new RobotException(e);
+ }
+ }
+
+ /**
+ * Action to perform a manual test step on server side; opens a window and
+ * wait's for real user interaction
+ *
+ * @param actionToPerform
+ * a textual description of the action to perform in the AUT
+ * @param expectedBehavior
+ * a textual description of the expected behaviour
+ * @param timeout
+ * the timeout
+ */
+ public void gdManualTestStep(String actionToPerform,
+ String expectedBehavior, int timeout) {
+ // empty implementation: implementation can be found in the corresponding
+ // postExecutionCommand
+ }
+
+ /**
+ * Perform a keystroke specified according <a
+ * href=http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/KeyStroke.html#getKeyStroke(java.lang.String)>
+ * string representation of a keystroke </a>.
+ * This method does not wait for event confirmation, as we have no way of
+ * confirming events on OS-native widgets.
+ *
+ * @param modifierSpec the string representation of the modifiers
+ * @param keySpec the string representation of the key
+ */
+ public void gdNativeKeyStroke(String modifierSpec, String keySpec) {
+ if (keySpec == null || keySpec.trim().length() == 0) {
+ throw new StepExecutionException("The base key of the key stroke " //$NON-NLS-1$
+ + "must not be null or empty", //$NON-NLS-1$
+ EventFactory.createActionError());
+ }
+
+ try {
+
+ KeyTyper typer = KeyTyper.getInstance();
+ String keyStrokeSpec = keySpec.trim().toUpperCase();
+ String mod = KeyStrokeUtil.getModifierString(modifierSpec);
+ if (mod.length() > 0) {
+ keyStrokeSpec = mod + " " + keyStrokeSpec; //$NON-NLS-1$
+ }
+
+ typer.type(keyStrokeSpec, null, null, null);
+
+ } catch (AWTException e) {
+ throw new RobotException(e);
+ }
+
+ }
+
+ /**
+ * Action to set the value of a variable in the Client.
+ *
+ * @param variable The name of the variable.
+ * @param value The new value for the variable.
+ * @return the new value for the variable.
+ */
+ public String gdSetValue(String variable, String value) {
+ return value;
+ }
+
+ /**
+ * @return The Robot instance
+ */
+ protected abstract IRobot getRobot();
+
+ /**
+ *
+ * @param filename A filename for which to find the extension.
+ * @return the file extension for the given filename. For example,
+ * "png" for "example.png". Returns an empty string if the given
+ * name has no extension. This is the case if the given name does
+ * not contain an instance of the extension separator or ends with
+ * the extension separator. For example, "example" or "example.".
+ */
+ private String getExtension(String filename) {
+ File file = new File(filename);
+ int extensionIndex = file.getName().lastIndexOf(EXTENSION_SEPARATOR);
+ return extensionIndex == -1
+ || extensionIndex == file.getName().length() - 1
+ ? StringConstants.EMPTY
+ : file.getName().substring(extensionIndex + 1);
+ }
+
+ /**
+ * method to copy a string to the system clipboard
+ *
+ * @param text The text to copy
+ */
+ public void gdCopyToClipboard(final String text) {
+ StringSelection strSel = new StringSelection(text);
+ try {
+ Toolkit.getDefaultToolkit().getSystemClipboard()
+ .setContents(strSel, null);
+ } catch (IllegalStateException ise) {
+ throw new StepExecutionException(
+ "Clipboard not available.", //$NON-NLS-1$
+ EventFactory.createActionError(
+ TestErrorEvent.CLIPBOARD_NOT_AVAILABLE));
+ }
+ }
+
+ /**
+ * method to compare to values
+ *
+ * @param value1
+ * the first value for comparison
+ * @param comparisonMethod
+ * the comparison method
+ * @param value2
+ * the second value for comparison
+ */
+ public void gdCheckValues(final String value1,
+ final String comparisonMethod, final String value2) {
+ Comparer.compare(value1, value2, comparisonMethod);
+ }
+
+ /**
+ * method to compare to strings
+ *
+ * @param value1
+ * the first value for comparison
+ * @param operator
+ * the comparison method
+ * @param value2
+ * the second value for comparison
+ */
+ public void gdCheckStringValues(final String value1,
+ final String operator, final String value2) {
+ Verifier.match(value1, value2, operator);
+ }
+
+ /**
+ * Does nothing! The start timer is implemented in the client but the server
+ * must have an action to execute.
+ * @param timerName the name for the timer
+ * @param variableName the variable name to store the current time in millisecs in
+ */
+ public void gdStartTimer(String timerName, String variableName) {
+ // empty
+ }
+
+ /**
+ * Does nothing! The read timer is implemented in the client but the server
+ * must have an action to execute.
+ * @param timerName the name for the timer
+ * @param variableName the variable name to store the current time delta in millisecs in
+ */
+ public void gdReadTimer(String timerName, String variableName) {
+ // empty
+ }
+
+ /**
+ * activate the AUT
+ *
+ * @param method activation method
+ */
+ public void gdActivate(String method) {
+ getRobot().activateApplication(method);
+ }
+
+ /**
+ * clicks into the active window.
+ *
+ * @param count amount of clicks
+ * @param button what mouse button should be used
+ * @param xPos what x position
+ * @param xUnits should x position be pixel or percent values
+ * @param yPos what y position
+ * @param yUnits should y position be pixel or percent values
+ * @throws StepExecutionException error
+ */
+ public void gdClickDirect(int count, int button,
+ int xPos, String xUnits, int yPos, String yUnits)
+ throws StepExecutionException {
+
+ Object activeWindow = getActiveWindow();
+ if (activeWindow != null) {
+ getRobot().click(activeWindow, null,
+ ClickOptions.create()
+ .setClickCount(count)
+ .setConfirmClick(false)
+ .setMouseButton(button),
+ xPos,
+ xUnits.equalsIgnoreCase(POS_UNIT_PIXEL),
+ yPos,
+ yUnits.equalsIgnoreCase(POS_UNIT_PIXEL));
+ } else {
+ throw new StepExecutionException("No active window.", //$NON-NLS-1$
+ EventFactory.createActionError(
+ TestErrorEvent.NO_ACTIVE_WINDOW));
+ }
+ }
+
+ /**
+ * Checks for the existence of a window with the given title
+ *
+ * @param title
+ * the title
+ * @param operator
+ * the comparing operator
+ * @param exists
+ * <code>True</code> if the window is expected to exist and be
+ * visible, otherwise <code>false</code>.
+ */
+// public void gdCheckExistenceOfWindow(final String title, String operator,
+// boolean exists) {
+// Verifier.equals(exists, isWindowOpen(title, operator));
+// }
+
+ /**
+ * Just a server side method, not useable as action.
+ *
+ * @param keyCode The key code
+ */
+ public void gdKeyType(int keyCode) {
+ getRobot().keyType(null, keyCode);
+ }
+
+ /**
+ * Just a server side method, not useable as action.
+ *
+ * note : this action only works if application got focus,
+ * because using defaultToolkit does not work. You have to
+ * use component.getToolKit()s
+ * @param key to set
+ * numlock Num Lock 1
+ * caplock Caps Lock 2
+ * scolllock Scroll 3
+ * @param activated
+ * boolean
+ */
+ public void gdToggle(int key, boolean activated) {
+
+ int event = getEventCode(key);
+ if (event != 0) {
+ try {
+ getRobot().keyToggle(getFocusOwner(),
+ event, activated);
+ } catch (UnsupportedOperationException usoe) {
+ throw new StepExecutionException(
+ TestErrorEvent.UNSUPPORTED_OPERATION_ERROR,
+ EventFactory.createActionError(
+ TestErrorEvent.UNSUPPORTED_OPERATION_ERROR));
+ } catch (OsNotSupportedException e) {
+ throw new StepExecutionException(
+ TestErrorEvent.UNSUPPORTED_OPERATION_ERROR,
+ EventFactory.createActionError(
+ TestErrorEvent.UNSUPPORTED_OPERATION_ERROR));
+ }
+ }
+ }
+
+ /**
+ * perform a keystroke
+ * @param modifierSpec the string representation of the modifiers
+ * @param keySpec the string representation of the key
+ */
+ public abstract void gdKeyStroke(String modifierSpec, String keySpec);
+ /**
+ *
+ * @return the Focus Owner
+ */
+ protected abstract Object getFocusOwner();
+ /**
+ *
+ * @param key to set
+ * numlock Num Lock 1
+ * caplock Caps Lock 2
+ * scolllock Scroll 3
+ * @return the toolkit specific eventcode
+ */
+ protected abstract int getEventCode(int key);
+
+ /**
+ *
+ * @return The active application window, or <code>null</code> if no
+ * application window is currently active.
+ */
+ protected abstract Object getActiveWindow();
+
+
+}
diff --git a/org.eclipse.jubula.rc.swing/src/org/eclipse/jubula/rc/swing/swing/caps/SwingApplicationCAPs.java b/org.eclipse.jubula.rc.swing/src/org/eclipse/jubula/rc/swing/swing/caps/SwingApplicationCAPs.java
new file mode 100644
index 000000000..b96294437
--- /dev/null
+++ b/org.eclipse.jubula.rc.swing/src/org/eclipse/jubula/rc/swing/swing/caps/SwingApplicationCAPs.java
@@ -0,0 +1,577 @@
+/*******************************************************************************
+ * Copyright (c) 2012 BREDEX GmbH.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * BREDEX GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jubula.rc.swing.swing.caps;
+
+import java.awt.AWTEvent;
+import java.awt.Component;
+import java.awt.Dialog;
+import java.awt.Frame;
+import java.awt.Rectangle;
+import java.awt.Toolkit;
+import java.awt.Window;
+import java.awt.event.AWTEventListener;
+import java.awt.event.ComponentEvent;
+import java.awt.event.KeyEvent;
+import java.awt.event.WindowEvent;
+import java.util.Collection;
+import java.util.ConcurrentModificationException;
+import java.util.Iterator;
+
+import org.eclipse.jubula.rc.common.AUTServer;
+import org.eclipse.jubula.rc.common.caps.AbstractApplicationCAPs;
+import org.eclipse.jubula.rc.common.driver.ClickOptions;
+import org.eclipse.jubula.rc.common.driver.IRobot;
+import org.eclipse.jubula.rc.common.exception.StepExecutionException;
+import org.eclipse.jubula.rc.common.implclasses.MatchUtil;
+import org.eclipse.jubula.rc.common.implclasses.Verifier;
+import org.eclipse.jubula.rc.common.listener.EventLock;
+import org.eclipse.jubula.rc.common.logger.AutServerLogger;
+import org.eclipse.jubula.rc.common.util.KeyStrokeUtil;
+import org.eclipse.jubula.rc.swing.components.SwingComponent;
+import org.eclipse.jubula.rc.swing.listener.ComponentHandler;
+import org.eclipse.jubula.rc.swing.listener.FocusTracker;
+import org.eclipse.jubula.rc.swing.swing.implclasses.EventListener;
+import org.eclipse.jubula.rc.swing.swing.implclasses.WindowHelper;
+import org.eclipse.jubula.rc.swing.swing.interfaces.IGraphicApplication;
+import org.eclipse.jubula.tools.constants.StringConstants;
+import org.eclipse.jubula.tools.objects.event.EventFactory;
+import org.eclipse.jubula.tools.objects.event.TestErrorEvent;
+import org.eclipse.jubula.tools.utils.TimeUtil;
+/**
+ *
+ * @author BREDEX GmbH
+ *
+ */
+public class SwingApplicationCAPs extends AbstractApplicationCAPs implements
+ IGraphicApplication {
+
+ /**
+ * This condition is true if the event is an 'window opened' event
+ * and the event source is a frame/dialog with a certain title.
+ * It is also true if the event is a 'component shown' event and the
+ * event source is a frame/dialog with a certain title.
+ */
+ private static class WindowOpenedCondition
+ implements EventListener.Condition {
+ /**
+ * the title
+ */
+ private final String m_title;
+
+ /** the matches operation */
+ private final String m_operator;
+
+ /**
+ * constructor
+ *
+ * @param title the title
+ * @param operator the matches operation
+ */
+ public WindowOpenedCondition(String title, String operator) {
+ m_title = title;
+ m_operator = operator;
+ }
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isTrue(AWTEvent event) {
+ if (event.getID() != WindowEvent.WINDOW_OPENED
+ && event.getID() != ComponentEvent.COMPONENT_SHOWN) {
+ return false;
+ }
+ if (event.getSource() instanceof Frame) {
+ Frame frame = (Frame)event.getSource();
+ return MatchUtil.getInstance().match(
+ frame.getTitle(), m_title, m_operator);
+ } else if (event.getSource() instanceof Dialog) {
+ Dialog dialog = (Dialog)event.getSource();
+ return MatchUtil.getInstance().match(
+ dialog.getTitle(), m_title, m_operator);
+ } else {
+ return false;
+ }
+ }
+ }
+
+ /**
+ * This condition is true if the event is an 'window activated' event
+ * and the event source is a frame/dialog with a certain title.
+ */
+ private static class WindowActivatedCondition
+ implements EventListener.Condition {
+ /**
+ * the title
+ */
+ private final String m_title;
+
+ /** the matches operation */
+ private final String m_operator;
+
+ /**
+ * constructor
+ *
+ * @param title the title
+ * @param operator the matches operation
+ */
+ public WindowActivatedCondition(String title, String operator) {
+ m_title = title;
+ m_operator = operator;
+ }
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isTrue(AWTEvent event) {
+ if (event.getID() != WindowEvent.WINDOW_ACTIVATED) {
+ return false;
+ }
+ if (event.getSource() instanceof Frame) {
+ Frame frame = (Frame)event.getSource();
+ return MatchUtil.getInstance().match(
+ frame.getTitle(), m_title, m_operator);
+ } else if (event.getSource() instanceof Dialog) {
+ Dialog dialog = (Dialog)event.getSource();
+ return MatchUtil.getInstance().match(
+ dialog.getTitle(), m_title, m_operator);
+ } else {
+ return false;
+ }
+ }
+ }
+
+ /**
+ * This condition is true if the event is an 'window closed' event
+ * and the event source is a frame/dialog with a certain title.
+ * It is also true if the event is a 'component hidden' event and the
+ * event source is a frame/dialog with a certain title.
+ */
+ private static class WindowClosedCondition
+ implements EventListener.Condition {
+ /**
+ * the title
+ */
+ private final String m_title;
+
+ /** the matches operation */
+ private final String m_operator;
+
+ /**
+ * constructor
+ *
+ * @param title the title
+ * @param operator the matches operation
+ */
+ public WindowClosedCondition(String title, String operator) {
+ m_title = title;
+ m_operator = operator;
+ }
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isTrue(AWTEvent event) {
+ if (event.getID() != WindowEvent.WINDOW_CLOSED
+ && event.getID() != ComponentEvent.COMPONENT_HIDDEN) {
+ return false;
+ }
+ if (event.getSource() instanceof Frame) {
+ Frame frame = (Frame)event.getSource();
+ return MatchUtil.getInstance().match(
+ frame.getTitle(), m_title, m_operator);
+ } else if (event.getSource() instanceof Dialog) {
+ Dialog dialog = (Dialog)event.getSource();
+ return MatchUtil.getInstance().match(
+ dialog.getTitle(), m_title, m_operator);
+ } else {
+ return false;
+ }
+ }
+ }
+
+ /**
+ * The logging.
+ */
+ private static AutServerLogger log =
+ new AutServerLogger(SwingApplicationCAPs.class);
+
+ /**
+ * {@inheritDoc}
+ */
+ public String[] getTextArrayFromComponent() {
+ return null;
+ }
+
+
+ /**
+ * Types <code>text</code> into the component. This replaces the shown
+ * content.
+ * @param text the text to type in
+ * @deprecated Removed without substitution:
+ * Testcases with this action are fragile, because this action assumes the
+ * availabality of a text component. Any other case breaks the test.
+ */
+ public void gdReplaceText(String text) {
+ getRobot().click(FocusTracker.getFocusOwner(), null,
+ ClickOptions.create().setClickCount(3).left());
+ if (StringConstants.EMPTY.equals(text)) {
+ getRobot().keyStroke("DELETE"); //$NON-NLS-1$
+ }
+ gdInputText(text);
+ }
+
+ /**
+ * Waits <code>timeMillSec</code> if the application opens a window
+ * with the given title.
+ *
+ * @param title the title
+ * @param operator the comparing operator
+ * @param pTimeout the time in ms
+ * @param delay delay after the window is shown
+ */
+ public void gdWaitForWindow(final String title, String operator,
+ int pTimeout, int delay) {
+
+ EventListener.Condition cond =
+ new WindowOpenedCondition(title, operator);
+ EventLock lock = new EventLock();
+ AWTEventListener listener = new EventListener(lock, cond);
+ Toolkit.getDefaultToolkit().addAWTEventListener(listener,
+ AWTEvent.WINDOW_EVENT_MASK | AWTEvent.COMPONENT_EVENT_MASK);
+
+ if (isWindowOpen(title, operator)) {
+ lock.release();
+ }
+ try {
+ synchronized (lock) {
+ long timeout = pTimeout;
+ long done = System.currentTimeMillis() + timeout;
+ long now;
+ while (!lock.isReleased() && (timeout > 0)) {
+ try {
+ lock.wait(timeout);
+ now = System.currentTimeMillis();
+ timeout = done - now;
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ }
+ }
+ } finally {
+ Toolkit.getDefaultToolkit().removeAWTEventListener(listener);
+ }
+ if (!lock.isReleased() && !isWindowOpen(title, operator)) {
+ throw new StepExecutionException("window did not open", //$NON-NLS-1$
+ EventFactory.createActionError(
+ TestErrorEvent.TIMEOUT_EXPIRED));
+ }
+ TimeUtil.delay(delay);
+ }
+ /**
+ * Waits <code>timeMillSec</code> if the application activates a window
+ * with the given title.
+ *
+ * @param title the title
+ * @param operator the comparing operator
+ * @param pTimeout the time in ms
+ * @param delay delay after the window is activated
+ */
+ public void gdWaitForWindowActivation(final String title, String operator,
+ int pTimeout, int delay) {
+
+ EventListener.Condition cond = new WindowActivatedCondition(title,
+ operator);
+ EventLock lock = new EventLock();
+ AWTEventListener listener = new EventListener(lock, cond);
+ Toolkit.getDefaultToolkit().addAWTEventListener(listener,
+ AWTEvent.WINDOW_EVENT_MASK);
+
+ if (isWindowActive(title, operator)) {
+ lock.release();
+ }
+ try {
+ synchronized (lock) {
+ long timeout = pTimeout;
+ long done = System.currentTimeMillis() + timeout;
+ long now;
+ while (!lock.isReleased() && (timeout > 0)) {
+ try {
+ lock.wait(timeout);
+ now = System.currentTimeMillis();
+ timeout = done - now;
+ } catch (InterruptedException e) {
+ // ignore
+ }
+
+ }
+ }
+ } finally {
+ Toolkit.getDefaultToolkit().removeAWTEventListener(listener);
+ }
+ if (!lock.isReleased() && !isWindowActive(title, operator)) {
+ throw new StepExecutionException("window was not activated", //$NON-NLS-1$
+ EventFactory.createActionError(
+ TestErrorEvent.TIMEOUT_EXPIRED));
+ }
+ TimeUtil.delay(delay);
+ }
+
+ /**
+ * Waits <code>timeMillSec</code> if the application closes (or hides)
+ * a window with the given title. If no window with the given title can
+ * be found, then it is assumed that the window has already closed.
+ *
+ * @param title the title
+ * @param operator the comparing operator
+ * @param pTimeout the time in ms
+ * @param delay delay after the window is closed
+ */
+ public void gdWaitForWindowToClose(final String title, String operator,
+ int pTimeout, int delay) {
+
+ EventListener.Condition cond =
+ new WindowClosedCondition(title, operator);
+ EventLock lock = new EventLock();
+ AWTEventListener listener = new EventListener(lock, cond);
+
+ Toolkit.getDefaultToolkit().addAWTEventListener(listener,
+ AWTEvent.WINDOW_EVENT_MASK);
+ if (!isWindowOpen(title, operator)) {
+ lock.release();
+ }
+
+ try {
+ synchronized (lock) {
+ long timeout = pTimeout;
+ long done = System.currentTimeMillis() + timeout;
+ long now;
+ while (!lock.isReleased() && (timeout > 0)) {
+ try {
+ lock.wait(timeout);
+ now = System.currentTimeMillis();
+ timeout = done - now;
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ }
+ }
+ } finally {
+ Toolkit.getDefaultToolkit().removeAWTEventListener(listener);
+ }
+ if (!lock.isReleased() && isWindowOpen(title, operator)) {
+ throw new StepExecutionException("window did not close", //$NON-NLS-1$
+ EventFactory.createActionError(
+ TestErrorEvent.TIMEOUT_EXPIRED));
+ }
+
+ TimeUtil.delay(delay);
+ }
+
+ /**
+ * Checks for the existence of a window with the given title
+ *
+ * @param title
+ * the title
+ * @param operator
+ * the comparing operator
+ * @param exists
+ * <code>True</code> if the window is expected to exist and be
+ * visible, otherwise <code>false</code>.
+ */
+ public void gdCheckExistenceOfWindow(final String title, String operator,
+ boolean exists) {
+ Verifier.equals(exists, isWindowOpen(title, operator));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Rectangle getActiveWindowBounds() {
+ Window activeWindow = WindowHelper.getActiveWindow();
+ if (activeWindow != null) {
+ Rectangle activeWindowBounds =
+ new Rectangle(activeWindow.getBounds());
+ activeWindowBounds.setLocation(activeWindow.getLocationOnScreen());
+
+ return activeWindowBounds;
+ }
+ return null;
+ }
+ /**
+ * {@inheritDoc}
+ */
+ protected IRobot getRobot() {
+ return AUTServer.getInstance().getRobot();
+ }
+
+ /**
+ * perform a keystroke specified according <a
+ * href=http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/KeyStroke.html#getKeyStroke(java.lang.String)>
+ * string representation of a keystroke </a>,
+ *
+ * @param modifierSpec the string representation of the modifiers
+ * @param keySpec the string representation of the key
+ */
+ public void gdKeyStroke(String modifierSpec, String keySpec) {
+ if (keySpec == null || keySpec.trim().length() == 0) {
+ throw new StepExecutionException(
+ "The base key of the key stroke must not be null or empty", //$NON-NLS-1$
+ EventFactory.createActionError());
+ }
+ String key = keySpec.trim().toUpperCase();
+ String mod = KeyStrokeUtil.getModifierString(modifierSpec);
+ if (mod.length() > 0) {
+ getRobot().keyStroke(mod.toString() + " " + key); //$NON-NLS-1$
+ } else {
+ int code = getKeyCode(key);
+ if (code != -1) {
+ gdKeyType(code);
+ } else {
+ getRobot().keyStroke(key);
+ }
+ }
+ }
+ /**
+ * {@inheritDoc}
+ */
+ protected Object getFocusOwner() {
+ return FocusTracker.getFocusOwner();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ protected int getEventCode(int key) {
+ int event = 0;
+ switch (key) {
+ case 1 :
+ event = KeyEvent.VK_NUM_LOCK;
+ break;
+ case 2 :
+ event = KeyEvent.VK_CAPS_LOCK;
+ break;
+ case 3 :
+ event = KeyEvent.VK_SCROLL_LOCK;
+ break;
+ default :
+ break;
+ }
+ return event;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ protected Object getActiveWindow() {
+ return WindowHelper.getActiveWindow();
+ }
+
+ /**
+ * Returns <code>true</code> if a window with the given title is open and
+ * visible.
+ *
+ * @param title the title
+ * @param operator the matches/equals operator
+ * @return if the window is open and visible
+ */
+ private boolean isWindowOpen(String title, String operator) {
+ boolean wasInterrupted;
+ do {
+ try {
+ wasInterrupted = false;
+ Collection components = ComponentHandler.getAutHierarchy()
+ .getHierarchyMap().keySet();
+ for (Iterator it = components.iterator(); it.hasNext();) {
+ Component c = ((SwingComponent)it.next())
+ .getRealComponent();
+ if (c.isShowing()) {
+ if (c instanceof Frame) {
+ Frame frame = (Frame)c;
+ if (MatchUtil.getInstance().match(frame.getTitle(),
+ title, operator)) {
+
+ return true;
+ }
+ }
+ if (c instanceof Dialog) {
+ Dialog dialog = (Dialog)c;
+ if (MatchUtil.getInstance().match(dialog.getTitle(),
+ title, operator)) {
+
+ return true;
+ }
+ }
+ }
+ }
+ } catch (ConcurrentModificationException e) {
+ log.debug("hierarchy modified while traversing", e); //$NON-NLS-1$
+ wasInterrupted = true;
+ }
+ } while (wasInterrupted);
+ return false;
+ }
+
+ /**
+ * Returns <code>true</code> if a window with the given title has focus
+ *
+ * @param title the title
+ * @param operator the matches/equals operator
+ * @return if the window has focus
+ */
+ private boolean isWindowActive(String title, String operator) {
+
+ Window activeWindow = WindowHelper.getActiveWindow();
+ if (activeWindow != null) {
+ String windowTitle = null;
+ if (activeWindow instanceof Dialog) {
+ windowTitle = ((Dialog)activeWindow).getTitle();
+ } else if (activeWindow instanceof Frame) {
+ windowTitle = ((Frame)activeWindow).getTitle();
+ }
+
+ if (MatchUtil.getInstance().match(windowTitle, title, operator)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * @param keyCodeName
+ * The name of a key code, e.g. <code>TAB</code> for a
+ * tabulator key code
+ * @return The key code or <code>-1</code>, if the key code name doesn't
+ * exist in the <code>KeyEvent</code> class
+ * @throws StepExecutionException
+ * If the key code name cannot be converted to a key code due to
+ * the reflection call
+ */
+ public int getKeyCode(String keyCodeName) throws StepExecutionException {
+ int code = -1;
+ String codeName = "VK_" + keyCodeName; //$NON-NLS-1$
+ try {
+ code = KeyEvent.class.getField(codeName).getInt(KeyEvent.class);
+ } catch (IllegalArgumentException e) {
+ throw new StepExecutionException(e.getMessage(), EventFactory
+ .createActionError());
+ } catch (SecurityException e) {
+ throw new StepExecutionException(e.getMessage(), EventFactory
+ .createActionError());
+ } catch (IllegalAccessException e) {
+ throw new StepExecutionException(e.getMessage(), EventFactory
+ .createActionError());
+ } catch (NoSuchFieldException e) {
+ if (log.isInfoEnabled()) {
+ log.info("The key expression '" + keyCodeName //$NON-NLS-1$
+ + "' is not a key code, typed as key stroke instead"); //$NON-NLS-1$
+ }
+ }
+ return code;
+ }
+}
diff --git a/org.eclipse.jubula.rc.swt/src/org/eclipse/jubula/rc/swt/caps/SwtApplicationCAPs.java b/org.eclipse.jubula.rc.swt/src/org/eclipse/jubula/rc/swt/caps/SwtApplicationCAPs.java
new file mode 100644
index 000000000..8b70a01ff
--- /dev/null
+++ b/org.eclipse.jubula.rc.swt/src/org/eclipse/jubula/rc/swt/caps/SwtApplicationCAPs.java
@@ -0,0 +1,548 @@
+/*******************************************************************************
+ * Copyright (c) 2012 BREDEX GmbH.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * BREDEX GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jubula.rc.swt.caps;
+
+import java.awt.Rectangle;
+import java.util.Collection;
+import java.util.ConcurrentModificationException;
+import java.util.Iterator;
+
+import org.eclipse.jubula.rc.common.AUTServer;
+import org.eclipse.jubula.rc.common.caps.AbstractApplicationCAPs;
+import org.eclipse.jubula.rc.common.driver.ClickOptions;
+import org.eclipse.jubula.rc.common.driver.IEventThreadQueuer;
+import org.eclipse.jubula.rc.common.driver.IRobot;
+import org.eclipse.jubula.rc.common.driver.IRobotFactory;
+import org.eclipse.jubula.rc.common.driver.IRunnable;
+import org.eclipse.jubula.rc.common.exception.StepExecutionException;
+import org.eclipse.jubula.rc.common.implclasses.MatchUtil;
+import org.eclipse.jubula.rc.common.implclasses.Verifier;
+import org.eclipse.jubula.rc.common.listener.EventLock;
+import org.eclipse.jubula.rc.common.logger.AutServerLogger;
+import org.eclipse.jubula.rc.common.util.KeyStrokeUtil;
+import org.eclipse.jubula.rc.common.util.WorkaroundUtil;
+import org.eclipse.jubula.rc.swt.SwtAUTServer;
+import org.eclipse.jubula.rc.swt.components.SwtComponent;
+import org.eclipse.jubula.rc.swt.driver.EventThreadQueuerSwtImpl;
+import org.eclipse.jubula.rc.swt.driver.RobotFactoryConfig;
+import org.eclipse.jubula.rc.swt.implclasses.EventListener;
+import org.eclipse.jubula.rc.swt.implclasses.EventListener.Condition;
+import org.eclipse.jubula.rc.swt.interfaces.IGraphicApplication;
+import org.eclipse.jubula.rc.swt.listener.ComponentHandler;
+import org.eclipse.jubula.rc.swt.listener.FocusTracker;
+import org.eclipse.jubula.rc.swt.utils.SwtPointUtil;
+import org.eclipse.jubula.tools.constants.StringConstants;
+import org.eclipse.jubula.tools.objects.event.EventFactory;
+import org.eclipse.jubula.tools.objects.event.TestErrorEvent;
+import org.eclipse.jubula.tools.utils.EnvironmentUtils;
+import org.eclipse.jubula.tools.utils.TimeUtil;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Widget;
+/**
+ *
+ * @author BREDEX GmbH
+ *
+ */
+public class SwtApplicationCAPs extends AbstractApplicationCAPs implements
+ IGraphicApplication {
+
+ /** The logging. */
+ private static AutServerLogger log =
+ new AutServerLogger(SwtApplicationCAPs.class);
+
+ /**
+ * This condition is true if the event source is a Shell with a matching
+ * title.
+ *
+ * @author BREDEX GmbH
+ * @created Jun 17, 2009
+ */
+ private static class WindowEventCondition implements Condition {
+
+ /** the expected window title */
+ private String m_windowTitle;
+
+ /** the operator used for matching the window title */
+ private String m_matchingOperator;
+
+ /**
+ * determines whether the event source being disposed should be
+ * treated as a match
+ */
+ private boolean m_valForDisposed;
+
+ /**
+ * Constructor
+ *
+ * @param windowTitle The expected window title.
+ * @param matchingOperator The operator used for matching the
+ * window title.
+ * @param valForDisposed Whether the event source being disposed
+ * should be treated as a match.
+ */
+ public WindowEventCondition(String windowTitle,
+ String matchingOperator, boolean valForDisposed) {
+ m_windowTitle = windowTitle;
+ m_matchingOperator = matchingOperator;
+ m_valForDisposed = valForDisposed;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isTrue(Event event) {
+ if (event.widget instanceof Shell) {
+ Shell window = (Shell)event.widget;
+ if (window.isDisposed()) {
+ return m_valForDisposed;
+ }
+ return MatchUtil.getInstance().match(window.getText(),
+ m_windowTitle, m_matchingOperator);
+
+ }
+
+ return false;
+ }
+
+ }
+
+ /** The Robot factory. */
+ private IRobotFactory m_robotFactory;
+
+ /**
+ * @return The Robot factory instance
+ */
+ private IRobotFactory getRobotFactory() {
+ if (m_robotFactory == null) {
+ m_robotFactory = new RobotFactoryConfig().getRobotFactory();
+ }
+ return m_robotFactory;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String[] getTextArrayFromComponent() {
+ return null;
+ }
+
+ /**
+ * Types <code>text</code> into the component. This replaces the shown
+ * content.
+ * @param text the text to type in
+ * @deprecated Removed without substitution:
+ * Testcases with this action are fragile, because this action assumes the
+ * availabality of a text component. Any other case breaks the test.
+ */
+ public void gdReplaceText(String text) {
+ // The number of clicks differs from the Swing implementation
+ // because a double-click selects all of the text
+ getRobot().click(FocusTracker.getFocusOwner(), null,
+ ClickOptions.create().setClickCount(2).left());
+ if (StringConstants.EMPTY.equals(text)) {
+ getRobot().keyStroke("DELETE"); //$NON-NLS-1$
+ }
+ gdInputText(text);
+ }
+
+ /**
+ * Waits <code>timeMillSec</code> if the application opens a window with the given title.
+ * @param title the title
+ * @param operator the comparing operator
+ * @param timeout the time in ms
+ * @param delay delay after the window is shown
+ */
+ public void gdWaitForWindow(final String title, final String operator,
+ int timeout, int delay) {
+
+ final EventListener.Condition cond =
+ new WindowEventCondition(title, operator, false);
+ final EventLock lock = new EventLock();
+ final Listener listener = new EventListener(lock, cond);
+ final Display display =
+ ((SwtAUTServer)AUTServer.getInstance()).getAutDisplay();
+ final IEventThreadQueuer queuer = new EventThreadQueuerSwtImpl();
+
+ queuer.invokeAndWait("addWindowOpenedListeners", new IRunnable() { //$NON-NLS-1$
+ public Object run() {
+ display.addFilter(SWT.Activate, listener);
+ display.addFilter(SWT.Show, listener);
+ if (isWindowOpen(title, operator)) {
+ lock.release();
+ }
+
+ return null;
+ }
+ });
+
+ try {
+ synchronized (lock) {
+ long currentTimeout = timeout;
+ long done = System.currentTimeMillis() + timeout;
+ long now;
+ while (!lock.isReleased() && (currentTimeout > 0)) {
+ try {
+ lock.wait(currentTimeout);
+ now = System.currentTimeMillis();
+ currentTimeout = done - now;
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ }
+ }
+ } finally {
+ queuer.invokeAndWait("removeWindowOpenedListeners", new IRunnable() { //$NON-NLS-1$
+ public Object run() {
+ display.removeFilter(SWT.Activate, listener);
+ display.removeFilter(SWT.Show, listener);
+
+ return null;
+ }
+ });
+ }
+ if (!lock.isReleased()) {
+ throw new StepExecutionException("window did not open", //$NON-NLS-1$
+ EventFactory.createActionError(
+ TestErrorEvent.TIMEOUT_EXPIRED));
+ }
+ TimeUtil.delay(delay);
+ }
+
+ /**
+ * Waits <code>timeMillSec</code> if the application activates a window
+ * with the given title.
+ *
+ * @param title the title
+ * @param operator the comparing operator
+ * @param timeout the time in ms
+ * @param delay delay after the window is activated
+ */
+ public void gdWaitForWindowActivation(final String title,
+ final String operator, final int timeout, int delay) {
+
+ final EventListener.Condition cond =
+ new WindowEventCondition(title, operator, false);
+ final EventLock lock = new EventLock();
+ final Listener listener = new EventListener(lock, cond);
+ final Display display =
+ ((SwtAUTServer)AUTServer.getInstance()).getAutDisplay();
+ final IEventThreadQueuer queuer = new EventThreadQueuerSwtImpl();
+
+ queuer.invokeAndWait("addWindowActiveListeners", new IRunnable() { //$NON-NLS-1$
+ public Object run() {
+ display.addFilter(SWT.Activate, listener);
+ if (isWindowActive(title, operator)) {
+ lock.release();
+ }
+
+ return null;
+ }
+ });
+
+ try {
+ synchronized (lock) {
+ long currentTimeout = timeout;
+ long done = System.currentTimeMillis() + timeout;
+ long now;
+ while (!lock.isReleased() && (currentTimeout > 0)) {
+ try {
+ lock.wait(currentTimeout);
+ now = System.currentTimeMillis();
+ currentTimeout = done - now;
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ }
+ }
+ } finally {
+ queuer.invokeAndWait("removeWindowActiveListeners", new IRunnable() { //$NON-NLS-1$
+ public Object run() {
+ display.removeFilter(SWT.Activate, listener);
+
+ return null;
+ }
+ });
+ }
+ if (!lock.isReleased()) {
+ throw new StepExecutionException("window was not activated", //$NON-NLS-1$
+ EventFactory.createActionError(
+ TestErrorEvent.TIMEOUT_EXPIRED));
+ }
+ TimeUtil.delay(delay);
+ }
+
+ /**
+ * Waits <code>timeMillSec</code> if the application closes (or hides)
+ * a window with the given title. If no window with the given title can
+ * be found, then it is assumed that the window has already closed.
+ *
+ * @param title the title
+ * @param operator the comparing operator
+ * @param timeout the time in ms
+ * @param delay delay after the window is activated
+ */
+ public void gdWaitForWindowToClose(final String title,
+ final String operator, int timeout, int delay) {
+
+ final EventListener.Condition cond =
+ new WindowEventCondition(title, operator, true);
+ final EventLock lock = new EventLock();
+ final Listener listener = new EventListener(lock, cond);
+ final Display display =
+ ((SwtAUTServer)AUTServer.getInstance()).getAutDisplay();
+ final IEventThreadQueuer queuer = new EventThreadQueuerSwtImpl();
+
+ queuer.invokeAndWait("addWindowClosedListeners", new IRunnable() { //$NON-NLS-1$
+ public Object run() {
+ display.addFilter(SWT.Close, listener);
+ display.addFilter(SWT.Hide, listener);
+ display.addFilter(SWT.Dispose, listener);
+ if (!isWindowOpen(title, operator)) {
+ lock.release();
+ }
+
+ return null;
+ }
+ });
+
+ try {
+ synchronized (lock) {
+ long currentTimeout = timeout;
+ long done = System.currentTimeMillis() + timeout;
+ long now;
+ while (!lock.isReleased() && (currentTimeout > 0)) {
+ try {
+ lock.wait(currentTimeout);
+ now = System.currentTimeMillis();
+ currentTimeout = done - now;
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ }
+ }
+ } finally {
+ queuer.invokeAndWait("removeWindowClosedListeners", new IRunnable() { //$NON-NLS-1$
+ public Object run() {
+ display.removeFilter(SWT.Close, listener);
+ display.removeFilter(SWT.Hide, listener);
+ display.removeFilter(SWT.Dispose, listener);
+
+ return null;
+ }
+ });
+ }
+ if (!lock.isReleased()) {
+ throw new StepExecutionException("window did not close", //$NON-NLS-1$
+ EventFactory.createActionError(
+ TestErrorEvent.TIMEOUT_EXPIRED));
+ }
+ TimeUtil.delay(delay);
+ }
+
+ /**
+ * Returns <code>true</code> if a window with the given title is open and
+ * visible
+ *
+ * @param title the title
+ * @param operator the matches/equals operator
+ * @return if the window is open and visible
+ */
+ private boolean isWindowOpen(final String title, final String operator) {
+ boolean wasInterrupted = false;
+ boolean equal = false;
+ do {
+ try {
+ wasInterrupted = false;
+ Collection components = ComponentHandler
+ .getAutHierarchy().getHierarchyMap()
+ .keySet();
+ for (Iterator it = components.iterator(); it.hasNext();) {
+
+ Widget comp = ((SwtComponent)it.next()).getRealComponent();
+ if (comp instanceof Shell
+ && !comp.isDisposed()
+ && ((Shell)comp).isVisible()) {
+
+ Shell frame = (Shell)comp;
+ if (MatchUtil.getInstance().match(
+ frame.getText(), title, operator)) {
+
+ equal = true;
+ break;
+ }
+ }
+ }
+
+ } catch (ConcurrentModificationException e) {
+ log.debug("hierarchy modified while traversing", e); //$NON-NLS-1$
+ wasInterrupted = true;
+ }
+ } while (wasInterrupted);
+ return equal;
+ }
+
+ /**
+ * Checks for the existence of a window with the given title
+ *
+ * @param title
+ * the title
+ * @param operator
+ * the comparing operator
+ * @param exists
+ * <code>True</code> if the component is expected to exist and be
+ * visible, otherwise <code>false</code>.
+ */
+ public void gdCheckExistenceOfWindow(final String title,
+ final String operator, final boolean exists) {
+ IEventThreadQueuer queuer = new EventThreadQueuerSwtImpl();
+ Boolean windowExists = (Boolean)queuer.invokeAndWait(
+ "isWindowOpen", new IRunnable() { //$NON-NLS-1$
+ public Object run() throws StepExecutionException {
+ return new Boolean(isWindowOpen(title, operator));
+ }
+ });
+ Verifier.equals(exists, windowExists.booleanValue());
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Rectangle getActiveWindowBounds() {
+ org.eclipse.swt.graphics.Rectangle activeWindowSize =
+ (org.eclipse.swt.graphics.Rectangle)getRobotFactory()
+ .getEventThreadQueuer().invokeAndWait(
+ this.getClass().getName() + ".getActiveWindowBounds", //$NON-NLS-1$
+ new IRunnable() {
+ public Object run() { // SYNCH THREAD START
+ Display d = ((SwtAUTServer)AUTServer
+ .getInstance()).getAutDisplay();
+ if (d != null && d.getActiveShell() != null) {
+ return d.getActiveShell().getBounds();
+ }
+ return null;
+ }
+ });
+ if (activeWindowSize != null) {
+ return SwtPointUtil.toAwtRectangle(activeWindowSize);
+ }
+ return null;
+ }
+ /**
+ * {@inheritDoc}
+ */
+ protected IRobot getRobot() {
+ return AUTServer.getInstance().getRobot();
+ }
+
+ /**
+ * perform a keystroke
+ * @param modifierSpec the string representation of the modifiers
+ * @param keySpec the string representation of the key
+ */
+ public void gdKeyStroke(String modifierSpec, String keySpec) {
+ if (keySpec == null || keySpec.trim().length() == 0) {
+ throw new StepExecutionException(
+ "The base key of the key stroke must not be null or empty", //$NON-NLS-1$
+ EventFactory.createActionError(
+ TestErrorEvent.INVALID_PARAM_VALUE));
+ }
+ String keyStrokeSpec = keySpec.trim();
+ String mod = KeyStrokeUtil.getModifierString(modifierSpec);
+ if (mod.length() > 0) {
+ keyStrokeSpec = mod + " " + keyStrokeSpec; //$NON-NLS-1$
+ }
+ String keySpecification = keySpec.trim().toLowerCase();
+ if (EnvironmentUtils.isMacOS() && keySpecification.length() == 1
+ && keySpecification.charAt(0) == WorkaroundUtil.CHAR_B) {
+ gdNativeKeyStroke(modifierSpec, keySpec);
+ } else {
+ // at this the key stroke specification is not fully fullfilled as the
+ // key stroke spec base key is not definitly upper case
+ getRobot().keyStroke(keyStrokeSpec);
+ }
+ }
+ /**
+ * {@inheritDoc}
+ */
+ protected Object getFocusOwner() {
+ return FocusTracker.getFocusOwner();
+ }
+ /**
+ * {@inheritDoc}
+ */
+ protected int getEventCode(int key) {
+ int event = 0;
+ switch (key) {
+ case 1 :
+ event = SWT.NUM_LOCK;
+ break;
+ case 2 :
+ event = SWT.CAPS_LOCK;
+ break;
+ case 3 :
+ event = SWT.SCROLL_LOCK;
+ break;
+ default :
+ break;
+ }
+ return event;
+ }
+ /**
+ * {@inheritDoc}
+ */
+ protected Object getActiveWindow() {
+ Shell activeWindow = (Shell)getRobotFactory().getEventThreadQueuer()
+ .invokeAndWait(this.getClass().getName() + ".getActiveWindow", //$NON-NLS-1$
+
+ new IRunnable() {
+ public Object run() { // SYNCH THREAD START
+ Display d =
+ ((SwtAUTServer)AUTServer.getInstance()).getAutDisplay();
+ return d.getActiveShell();
+
+ }
+ }
+
+ );
+
+ return activeWindow;
+ }
+
+ /**
+ * Returns <code>true</code> if a window with the given title is active
+ * (the window with focus).
+ *
+ * @param title the title
+ * @param operator the matches/equals operator
+ * @return if the window is open and visible
+ */
+ private boolean isWindowActive(final String title, final String operator) {
+ final Shell activeWindow = (Shell) getActiveWindow();
+
+ if (activeWindow == null) {
+ if (log.isWarnEnabled()) {
+ log.warn("No active Window found while searching for Window with title: '" //$NON-NLS-1$
+ + String.valueOf(title) + "'! " + //$NON-NLS-1$
+ "(SwtApplicationImplClass#isWindowActive(String, String))"); //$NON-NLS-1$
+ }
+ return false;
+ }
+
+ final String windowTitle = activeWindow.getText();
+
+ return MatchUtil.getInstance().match(windowTitle, title, operator);
+ }
+
+}
diff --git a/org.eclipse.jubula.toolkit.provider.swing/resources/xml/ComponentConfiguration.xml b/org.eclipse.jubula.toolkit.provider.swing/resources/xml/ComponentConfiguration.xml
index 5379fddc7..adb632332 100644
--- a/org.eclipse.jubula.toolkit.provider.swing/resources/xml/ComponentConfiguration.xml
+++ b/org.eclipse.jubula.toolkit.provider.swing/resources/xml/ComponentConfiguration.xml
@@ -21,7 +21,7 @@
<typeFactory>org.eclipse.jubula.rc.common.implclasses.DefaultComponentFactory</typeFactory>
</defaultMapping>
<realizes>guidancer.concrete.GraphicApplication</realizes>
- <testerClass>org.eclipse.jubula.rc.swing.swing.implclasses.SwingApplicationImplClass</testerClass>
+ <testerClass>org.eclipse.jubula.rc.swing.swing.caps.SwingApplicationCAPs</testerClass>
<componentClass name="com.bredexsw.guidancer.autserver.swing.implclasses.GraphicApplication" />
</toolkitComponent>
diff --git a/org.eclipse.jubula.toolkit.provider.swt/resources/xml/ComponentConfiguration.xml b/org.eclipse.jubula.toolkit.provider.swt/resources/xml/ComponentConfiguration.xml
index 1e2594078..7353acf7e 100644
--- a/org.eclipse.jubula.toolkit.provider.swt/resources/xml/ComponentConfiguration.xml
+++ b/org.eclipse.jubula.toolkit.provider.swt/resources/xml/ComponentConfiguration.xml
@@ -194,7 +194,7 @@
<typeFactory>org.eclipse.jubula.rc.common.implclasses.DefaultComponentFactory</typeFactory>
</defaultMapping>
<realizes>guidancer.concrete.GraphicApplication</realizes>
- <testerClass>org.eclipse.jubula.rc.swt.implclasses.SwtApplicationImplClass</testerClass>
+ <testerClass>org.eclipse.jubula.rc.swt.caps.SwtApplicationCAPs</testerClass>
<componentClass name="com.bredexsw.guidancer.autswtserver.implclasses.GraphicApplication" />
</toolkitComponent>

Back to the top