/******************************************************************************* * 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.tester.util; import java.awt.AWTEvent; import java.awt.Color; import java.awt.Component; import java.awt.Container; import java.awt.event.ContainerEvent; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.StringTokenizer; import javax.swing.AbstractButton; import javax.swing.JLabel; import javax.swing.JPopupMenu; import javax.swing.JToggleButton; import javax.swing.text.JTextComponent; import org.eclipse.jubula.rc.common.AUTServer; import org.eclipse.jubula.rc.common.adaptable.AdapterFactoryRegistry; import org.eclipse.jubula.rc.common.adaptable.ITextRendererAdapter; 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.logger.AutServerLogger; import org.eclipse.jubula.rc.common.util.KeyStrokeUtil; import org.eclipse.jubula.rc.swing.driver.KeyCodeConverter; import org.eclipse.jubula.rc.swing.driver.RobotFactoryConfig; import org.eclipse.jubula.tools.internal.constants.StringConstants; import org.eclipse.jubula.tools.internal.objects.event.EventFactory; import org.eclipse.jubula.tools.internal.objects.event.TestErrorEvent; /** * Util class for Swing specific aspects. * * @author BREDEX GmbH */ public class TesterUtil { /** * <code>RENDERER_FALLBACK_TEXT_GETTER_METHOD_1</code> */ public static final String RENDERER_FALLBACK_TEXT_GETTER_METHOD_1 = "getTestableText"; //$NON-NLS-1$ /** * <code>RENDERER_FALLBACK_TEXT_GETTER_METHOD_2</code> */ public static final String RENDERER_FALLBACK_TEXT_GETTER_METHOD_2 = "getText"; //$NON-NLS-1$ /** the logger */ private static AutServerLogger log = new AutServerLogger( TesterUtil.class); /** the high lighter for object mapping */ private static final HighLighter HIGHLIGHTER = new HighLighter(); /** * Is true, if a popup menu is shown */ public static class PopupShownCondition implements EventListener.Condition { /** * the popup menu */ private JPopupMenu m_popup = null; /** * * @return the popup menu */ public JPopupMenu getPopup() { return m_popup; } /** * {@inheritDoc} * @param event event * @return result of the condition */ public boolean isTrue(AWTEvent event) { if (event.getID() != ContainerEvent.COMPONENT_ADDED) { return false; } ContainerEvent ce = (ContainerEvent)event; if (ce.getChild() instanceof JPopupMenu) { m_popup = (JPopupMenu)ce.getChild(); return true; } else if (ce.getChild() instanceof Container) { Container popupContainer = (Container)ce.getChild(); final int length = popupContainer.getComponents().length; for (int i = 0; i < length; i++) { if (popupContainer.getComponents()[i] instanceof JPopupMenu) { m_popup = (JPopupMenu)popupContainer.getComponents()[i]; return true; } } } return false; } } /** * The robot factory. */ private static IRobotFactory robotFactory; /** * */ private TesterUtil() { } /** * * @return the Robot */ public static IRobot getRobot() { return AUTServer.getInstance().getRobot(); } /** * Gets the Robot factory. The factory is created once per instance. * * @return The Robot factory. */ protected static IRobotFactory getRobotFactory() { if (robotFactory == null) { robotFactory = new RobotFactoryConfig().getRobotFactory(); } return robotFactory; } /** * @return The event thread queuer. */ protected static IEventThreadQueuer getEventThreadQueuer() { return getRobotFactory().getEventThreadQueuer(); } /** * Presses or releases the given modifier. * @param modifier the modifier. * @param press if true, the modifier will be pressed. * if false, the modifier will be released. */ public static void pressOrReleaseModifiers(String modifier, boolean press) { final IRobot robot = getRobot(); final StringTokenizer modTok = new StringTokenizer( KeyStrokeUtil.getModifierString(modifier), " "); //$NON-NLS-1$ while (modTok.hasMoreTokens()) { final String mod = modTok.nextToken(); final int keyCode = KeyCodeConverter.getKeyCode(mod); if (press) { robot.keyPress(null, keyCode); } else { robot.keyRelease(null, keyCode); } } } /** * Casts the passed renderer component to a known type and extracts the * rendered text. * * @param renderer * The renderer. * @param queueInEventThread * If <code>true</code>, the text extraction is executed in * the event queue thread. * @return The rendered text. * @throws StepExecutionException * If the passed renderer is not supported. Supported types are * <code>JLabel</code>, <code>JToggleButton</code>, * <code>AbstractButton</code> and <code>JTextComponent</code> * */ public static String getRenderedText(final Component renderer, boolean queueInEventThread) throws StepExecutionException { if (queueInEventThread) { return getEventThreadQueuer().invokeAndWait( "getRenderedText", new IRunnable<String>() { //$NON-NLS-1$ public String run() { return getRenderedText(renderer); } }); } return getRenderedText(renderer); } /** * @param renderer * The component which is used as the renderer * @return The string that the renderer displays. * @throws StepExecutionException * If the renderer component is not of type <code>JLabel</code>, * <code>JToggleButton</code>, <code>AbstractButton</code>, * <code>JTextComponent</code> or supports one of the fallback * methods */ public static String getRenderedText(Component renderer) throws StepExecutionException { String renderedText = resolveRenderedText(renderer); if (renderedText != null) { return renderedText; } return StringConstants.EMPTY; } /** * @param renderer * The component which is used as the renderer * @return The string that the renderer displays or <code>null</code> if it * could not be resolved. */ private static String resolveRenderedText(Component renderer) throws StepExecutionException { if (renderer instanceof JLabel) { return ((JLabel)renderer).getText(); } else if (renderer instanceof JToggleButton) { return ((JToggleButton)renderer).isSelected() ? Boolean.TRUE .toString() : Boolean.FALSE.toString(); } else if (renderer instanceof AbstractButton) { return ((AbstractButton)renderer).getText(); } else if (renderer instanceof JTextComponent) { return ((JTextComponent)renderer).getText(); } // Check if an adapter exists ITextRendererAdapter textRendererAdapter = ((ITextRendererAdapter) AdapterFactoryRegistry .getInstance().getAdapter( ITextRendererAdapter.class, renderer)); if (textRendererAdapter != null) { return textRendererAdapter.getText(); } else if (renderer != null) { String[] methodNames = new String[] { RENDERER_FALLBACK_TEXT_GETTER_METHOD_1, RENDERER_FALLBACK_TEXT_GETTER_METHOD_2 }; for (int i = 0; i < methodNames.length; i++) { String text; try { text = getTextFromComponent( renderer, methodNames[i]); return text; } catch (SecurityException e) { // ignore - continue with next fall back approach } catch (IllegalArgumentException e) { // ignore - continue with next fall back approach } catch (NoSuchMethodException e) { // ignore - continue with next fall back approach } catch (IllegalAccessException e) { // ignore - continue with next fall back approach } catch (InvocationTargetException e) { // ignore - continue with next fall back approach } } } log.warn("Renderer not supported: " + renderer.getClass()); //$NON-NLS-1$ throw new StepExecutionException( "Renderer not supported: " + renderer.getClass(), //$NON-NLS-1$ EventFactory.createActionError( TestErrorEvent.RENDERER_NOT_SUPPORTED)); } /** * @param obj * the object to invoke the method for * @param getterName * the name of the getter Method for string retrieval * @return the return value of the given method name * @throws NoSuchMethodException * may arise during reflection * @throws SecurityException * may arise during reflection * @throws InvocationTargetException * may arise during reflection * @throws IllegalAccessException * may arise during reflection * @throws IllegalArgumentException * may arise during reflection */ private static String getTextFromComponent(Object obj, String getterName) throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException { Method getter = null; Class objClass = obj.getClass(); try { getter = objClass.getDeclaredMethod(getterName, null); } catch (NoSuchMethodException e) { // ignore } catch (SecurityException e) { // ignore } if (getter == null) { getter = objClass.getMethod(getterName, null); } getter.setAccessible(true); if (String.class == getter.getReturnType()) { return (String) getter.invoke(obj, null); } throw new NoSuchMethodException(); } /** * High light the given component, called during object mapping * @param component the component to high light * @param border the color we want to highlight with */ public static void highLight(Component component, Color border) { try { final Component comp = component; final Color col = border; getEventThreadQueuer().invokeLater( "highLight", new Runnable() { //$NON-NLS-1$ public void run() { HIGHLIGHTER.highLight(comp, col); } }); } catch (StepExecutionException bsee) { log.error(bsee); } } /** * Low light the given component, called during object mapping * @param component the component to remove the 'highlight' */ public static void lowLight(Component component) { try { final Component comp = component; getEventThreadQueuer().invokeLater( "lowLight", new Runnable() { //$NON-NLS-1$ public void run() { HIGHLIGHTER.lowLight(comp); } }); } catch (StepExecutionException bsee) { log.error(bsee); } } }