/******************************************************************************* * Copyright (c) 2004, 2010 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 and/or initial documentation *******************************************************************************/ package org.eclipse.jubula.rc.swing.driver; import java.awt.AWTEvent; import java.awt.Component; import java.awt.Container; import java.awt.EventQueue; import java.awt.Toolkit; import java.awt.event.AWTEventListener; import java.awt.event.MouseEvent; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import org.eclipse.jubula.rc.common.driver.DragAndDropHelper; import org.eclipse.jubula.rc.common.driver.IEventMatcher; import org.eclipse.jubula.rc.common.driver.IRobotEventConfirmer; import org.eclipse.jubula.rc.common.driver.InterceptorOptions; import org.eclipse.jubula.rc.common.driver.RobotTiming; import org.eclipse.jubula.rc.common.exception.RobotException; import org.eclipse.jubula.rc.common.logger.AutServerLogger; import org.eclipse.jubula.rc.common.util.WorkaroundUtil; import org.eclipse.jubula.tools.internal.objects.event.EventFactory; import org.eclipse.jubula.tools.internal.objects.event.TestErrorEvent; /** * <p> This event confirmer works on a class of AWT events defined by an * <code>InterceptorOptions</code> instance. The confirmer adds a * {@link java.awt.event.AWTEventListener} to the AWT event queue * using the <code>InterceptorOptions</code> event mask.</p> * * <p> To confirm an event, call <code>waitToConfirm()</code>.</p> * * @author BREDEX GmbH * @created 17.03.2005 */ class RobotEventConfirmerAwtImpl implements IRobotEventConfirmer, AWTEventListener { /** * The logger. */ private static AutServerLogger log = new AutServerLogger(RobotEventConfirmerAwtImpl.class); /** * Stores if the confirmer is enabled. */ private boolean m_enabled = false; /** * Stores if the confirmer is being waiting for an event to confirm. */ private boolean m_waiting = false; /** * The interceptor options. */ private InterceptorOptions m_options; /** * The graphics component on which the event occurs. */ private Object m_eventTarget; /** * The event matcher. */ private IEventMatcher m_eventMatcher; /** * Stores all events of a given class after the confirmer has been enabled. */ private List m_eventList = new LinkedList(); /** * Creates a new confirmer for a class of events defined by * <code>options</code>. * * @param options * The options. */ RobotEventConfirmerAwtImpl(InterceptorOptions options) { m_options = options; } /** * Checks if the given event matches. * @param event the event. * @return <code>true</code> if the event matches, otherwise * <code>false</code>. */ private boolean isEventMatching(AWTEvent event) { final Object eventSource = event.getSource(); return ((m_eventTarget == null // we get no events while dragging so matchComponent(...)! // for the drop-component does not work! || event.getID() == MouseEvent.MOUSE_DRAGGED || matchComponent(m_eventTarget, eventSource)) && m_eventMatcher.isMatching(event)); } /** * Calls isComponentMatching(...) interchanging the parameters, * so that the event-target-component and its children will be checked for * matching or the event-source-component and its children. * @param evTarget evTarget * @param evSource evSource * @return boolean */ private boolean matchComponent(Object evTarget, Object evSource) { return (isComponentMatching(evTarget, evSource) || isComponentMatching(evSource, evTarget)); } /** * Checks if the current component matches to the expected component. * If the current Component does not match, its parents will be checked * recursive. * @param expComp the expected Component * @param currComp the current component to check. * @return true or false. */ private boolean isComponentMatching(Object expComp, Object currComp) { if (expComp == currComp) { return true; } // if no matching, try to match children. boolean match = false; if (currComp instanceof Container) { Container curr = (Container)currComp; Component[] children = curr.getComponents(); final int childLength = children.length; for (int i = 0; i < childLength; i++) { match = isComponentMatching(expComp, children[i]); if (match) { return match; } } } return match; } /** * Checks if one of the events stored into the given list matches. * * @param eventList * The list of events * @return <code>true</code> if one or more of the event matches, * otherwise <code>false</code>. */ private synchronized boolean isEventMatching(List eventList) { for (Iterator it = eventList.iterator(); it.hasNext();) { AWTEvent event = (AWTEvent)it.next(); if (isEventMatching(event)) { return true; } } return false; } /** * Adds the event to the event list. * * @param event * The event. */ private synchronized void addEventToList(AWTEvent event) { ((LinkedList)m_eventList).addFirst(event); if (log.isDebugEnabled()) { log.debug("Received event: " + String.valueOf(event.toString())); //$NON-NLS-1$ } } /** * Stops the waiting status. That means, that the current waiting thread is * being notified and the <code>m_waiting</code> property is set to * <code>false</code>. */ private synchronized void stopWaiting() { m_waiting = false; notify(); } /** * {@inheritDoc} */ public synchronized void eventDispatched(AWTEvent event) { if (!m_enabled) { return; } addEventToList(event); if (m_waiting && isEventMatching(m_eventList)) { stopWaiting(); } } /** * Enables or disables the confirmer. If the confirmer is enabled, the AWT * listener is added to the AWT event queue so that the confirmer starts * storing events of the configured class of events. If it is disabled, the * listener is removed from the AWT event queue. * * @param enabled * <code>true</code> or <code>false</code>. */ void setEnabled(boolean enabled) { m_enabled = enabled; m_eventList.clear(); if (enabled) { for (int i = 0; i < m_options.getEventMask().length; i++) { Toolkit.getDefaultToolkit().addAWTEventListener(this, m_options.getEventMask()[i]); } } else { Toolkit.getDefaultToolkit().removeAWTEventListener(this); } } /** * {@inheritDoc} */ public synchronized void waitToConfirm(Object eventTarget, IEventMatcher matcher, long pTimeout) throws RobotException { if (DragAndDropHelper.getInstance().isDragMode()) { setEnabled(false); return; // With a pressed mouse button, we get no events! } m_eventTarget = eventTarget; m_eventMatcher = matcher; if (log.isDebugEnabled()) { log.debug("Waiting for EventID: " + String.valueOf(matcher) //$NON-NLS-1$ + " on Component: " + String.valueOf(m_eventTarget)); //$NON-NLS-1$ } try { if (isEventMatching(m_eventList)) { return; } if (EventQueue.isDispatchThread()) { throw new IllegalThreadStateException(); } m_waiting = true; try { long timeout = pTimeout; long done = System.currentTimeMillis() + timeout; long now; do { wait(pTimeout); now = System.currentTimeMillis(); timeout = done - now; } while (m_waiting && (timeout > 0)); } catch (InterruptedException e) { throw new RobotException(e); } if (m_waiting) { // I'm still waiting. This means that the event could not // be confirmed during the confirm time interval, that means // the event matcher didn't find a matching event. // But the event matcher may accept a different event, which has // already dispatched, as a fall back. boolean fallBackMatching = m_eventMatcher .isFallBackEventMatching(m_eventList, m_eventTarget); if (!fallBackMatching && !WorkaroundUtil.isIgnoreTimeout()) { throw new RobotException( "Timeout received before confirming the posted event: " //$NON-NLS-1$ + m_eventMatcher.getEventId(), EventFactory.createActionError( TestErrorEvent.CONFIRMATION_TIMEOUT)); } } } finally { setEnabled(false); } } /** * {@inheritDoc} */ public void waitToConfirm(Object eventTarget, IEventMatcher matcher) throws RobotException { waitToConfirm(eventTarget, matcher, RobotTiming.getEventConfirmTimeout()); } }