/******************************************************************************* * Copyright (c) 2012 Google, Inc. * 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: * Google, Inc. - initial API and implementation *******************************************************************************/ package com.windowtester.runtime.swt.locator; import static com.windowtester.runtime.swt.internal.matchers.WidgetMatchers.ofClass; import static com.windowtester.runtime.swt.internal.matchers.WidgetMatchers.visible; import static com.windowtester.runtime.swt.internal.matchers.WidgetMatchers.withText; import java.awt.Point; import java.util.concurrent.Callable; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Link; import org.eclipse.swt.widgets.ToolItem; import org.eclipse.swt.widgets.Widget; import com.windowtester.internal.runtime.ClassReference; import com.windowtester.internal.runtime.locator.IUISelector; import com.windowtester.runtime.IClickDescription; import com.windowtester.runtime.IUIContext; import com.windowtester.runtime.InaccessableWidgetException; import com.windowtester.runtime.WT; import com.windowtester.runtime.WidgetLocator; import com.windowtester.runtime.WidgetSearchException; import com.windowtester.runtime.condition.HasFocus; import com.windowtester.runtime.condition.HasText; import com.windowtester.runtime.condition.IUICondition; import com.windowtester.runtime.condition.IUIConditionHandler; import com.windowtester.runtime.condition.IsEnabled; import com.windowtester.runtime.condition.IsVisible; import com.windowtester.runtime.condition.IsVisibleCondition; import com.windowtester.runtime.internal.factory.WTRuntimeManager; import com.windowtester.runtime.locator.IWidgetLocator; import com.windowtester.runtime.locator.IWidgetMatcher; import com.windowtester.runtime.locator.IWidgetReference; import com.windowtester.runtime.locator.WidgetReference; import com.windowtester.runtime.swt.internal.condition.HasFocusConditionHandler; import com.windowtester.runtime.swt.internal.drivers.MenuDriver; import com.windowtester.runtime.swt.internal.matchers.SWTMatcherBuilder; import com.windowtester.runtime.swt.internal.matchers.WidgetMatchers; import com.windowtester.runtime.swt.internal.operation.BasicSWTWidgetClickOperation; import com.windowtester.runtime.swt.internal.operation.SWTLocation; import com.windowtester.runtime.swt.internal.operation.SWTMenuOperation; import com.windowtester.runtime.swt.internal.operation.SWTShowMenuOperation; import com.windowtester.runtime.swt.internal.operation.SWTWidgetLocation; import com.windowtester.runtime.swt.internal.selector.UIDriver; import com.windowtester.runtime.swt.internal.widgets.ISWTWidgetMatcher; import com.windowtester.runtime.swt.internal.widgets.ISWTWidgetReference; import com.windowtester.runtime.swt.internal.widgets.LinkReference; import com.windowtester.runtime.swt.internal.widgets.MenuReference; import com.windowtester.runtime.swt.internal.widgets.finder.SWTWidgetFinder; import com.windowtester.runtime.util.StringComparator; /** * A base class for SWT widget locators. Instances capture hierarchy (containment) * relationships between widgets for use in widget identification. * <p> * For example, a widget identified by class and position relative to its * parent composite could be described so: * <pre> * new SWTWidgetLocator(Text.class, 2, * new SWTWidgetLocator(Group.class, "addressGroup")); * </pre> * * Effectively, this SWTWidgetLocator instance describes the third Text widget in * the Group labeled "addressGroup". * <p> * To make text matching more robust, WindowTester supports wild cards based on the regular expression support * provided by Java. Regular expressions can be used wherever text matches are performed. * For more on String comparison, see the {@link StringComparator} utility. * */ public class SWTWidgetLocator extends WidgetLocator implements IUISelector, IsVisible { private static final long serialVersionUID = 7538392706559282881L; /* * Having these fields transient is essential to having UIEvents safely transmitted across the wire. */ private transient ISWTWidgetMatcher matcher; // private transient BasicWidgetSelector _selector = new BasicWidgetSelector(); protected transient SWTMatcherBuilder matcherBuilder = new SWTMatcherBuilder(); /** * Create an instance that identifies an SWT widget by its class name. * @param className the target widget's fully qualified class name * @since 3.8.1 */ public SWTWidgetLocator(String className) { this(className, null); } /** * Create an instance that identifies an SWT widget by its class name. * @param className the target widget's fully qualified class name * @param parent the target's parent * @since 3.8.1 */ public SWTWidgetLocator(String className, SWTWidgetLocator parent) { this(className, UNASSIGNED, parent); } /** * Create an instance that identifies an SWT widget by its class name. * @param className the target widget's fully qualified class name * @param index the target's index relative to its parent * @param parent the target's parent * @since 3.8.1 */ public SWTWidgetLocator(String className, int index, SWTWidgetLocator parent) { super(ClassReference.forName(className), null, index, parent); } /** * Create an instance. * @param cls the target class */ public SWTWidgetLocator(Class<?> cls) { super(cls); } /** * Create an instance. * @param cls the target class * @param text the target's label text */ public SWTWidgetLocator(Class<?> cls, String text) { super(cls, text); } /** * Create an instance. * @param cls the target class * @param text the target's text label * @param parent the target's parent */ public SWTWidgetLocator(Class<?> cls, String text, SWTWidgetLocator parent) { super(cls, text, parent); } /** * Create an instance. * @param cls the target class * @param text the target's text label * @param index the target's index relative to its parent * @param parent the target's parent */ public SWTWidgetLocator(Class<?> cls, String text, int index, SWTWidgetLocator parent) { super(cls, text, index, parent); } /** * Create an instance. * @param cls the target class * @param parent the target's parent */ public SWTWidgetLocator(Class<?> cls, SWTWidgetLocator parent) { super(cls, parent); } /** * Create an instance. * @param cls the target class * @param index the target's index relative to its parent * @param parent the target's parent */ public SWTWidgetLocator(Class<?> cls, int index, SWTWidgetLocator parent) { super(cls, index, parent); } /////////////////////////////////////////////////////////////////////////// // // Scoping // /////////////////////////////////////////////////////////////////////////// public SWTWidgetLocator /* THIS_TYPE */ in(int index, SWTWidgetLocator parent) { // SWTWidgetLocator locator = this; SWTWidgetLocator locator = getTopScope(); locator.setParentInfo(parent); locator.setIndex(index); //add collected specs (e.g., name) // locator.matcherBuilder.specify(matcherBuilder.criteria()); // locator.matcherBuilder.setParent(index, parent.buildMatcher()); return this; } private SWTWidgetLocator getTopScope() { WidgetLocator parent = this; while (parent.getParentInfo() != null) parent = parent.getParentInfo(); return (SWTWidgetLocator) parent; } public SWTWidgetLocator /* THIS_TYPE */ in(SWTWidgetLocator parent) { return in(UNASSIGNED, parent); } public SWTWidgetLocator /* THIS_TYPE */ named(String name) { matcherBuilder.specify(WidgetMatchers.named(name)); return this; } /** * Similar to containedIn(int, SWTWidgetLocator), but expects a single matching Widgets. * Not always the same as containedIn(0, ancestor)! * * @param ancestor * @return This object * * @author Max Hohenegger * @see SWTWidgetLocator#containedIn(int, SWTWidgetLocator) */ public <T> T containedIn(SWTWidgetLocator ancestor) { return containedIn(UNASSIGNED, ancestor); } /** * Similar to in(int, SWTWidgetLocator), but works with arbitrary ancestors. * * * @param index The index of the widget, since multiple widgets are expected to be found. * @param ancestor Ancestor of the Widget in question. * @return This object * * @author Max Hohenegger * @see SWTWidgetLocator#in(int, SWTWidgetLocator) */ public <T> T containedIn(int index, SWTWidgetLocator ancestor) { setIndex(index); setAncestorInfo(ancestor); return (T) this; } /////////////////////////////////////////////////////////////////////////// // // Access // /////////////////////////////////////////////////////////////////////////// /* (non-Javadoc) * @see com.windowtester.runtime.WidgetLocator#setParentInfo(com.windowtester.runtime.WidgetLocator) */ public void setParentInfo(WidgetLocator parentInfo) { /* * this is a bit weird but since we're caching matchers, we need to invalidate them when parents * are added */ matcher = null; super.setParentInfo(parentInfo); } /* (non-Javadoc) * @see com.windowtester.runtime.WidgetLocator#setParentInfo(com.windowtester.runtime.WidgetLocator) */ public void setAncestorInfo(WidgetLocator ancestorInfo) { /* * this is a bit weird but since we're caching matchers, we need to invalidate them when parents * are added */ matcher = null; super.setAncestorInfo(ancestorInfo); } /////////////////////////////////////////////////////////////////////////// // // Matching criteria // /////////////////////////////////////////////////////////////////////////// /* (non-Javadoc) * @see com.windowtester.runtime.WidgetLocator#matches(java.lang.Object) */ public boolean matches(Object widget) { /* * EXPERIMENTAL: create a reference on demand */ if (widget instanceof Widget) widget = WTRuntimeManager.asReference(widget); if (!(widget instanceof ISWTWidgetReference<?>)) return false; // TODO[pq]: should we build a ref here for matching? ISWTWidgetReference<?> ref = (ISWTWidgetReference<?>)widget; return getMatcher().matches(ref); } /** * Get the associated matcher. * NOTE: since this class is serialized (and matchers may or may not be) * the matcher is lazily initialized. */ protected ISWTWidgetMatcher getMatcher() { if (matcher == null) matcher = buildMatcher(); return matcher; } /** * Build the associated matcher */ protected ISWTWidgetMatcher buildMatcher() { /* * First, query locator for identifying details. */ Class<?> cls = getTargetClass(); String nameOrLabel = getNameOrLabel(); int index = getIndex(); IWidgetMatcher<?> parentInfo = getParentInfo(); IWidgetMatcher<?> ancestorInfo = getAncestorInfo(); matcherBuilder.specify(ofClass(cls), visible()); if (nameOrLabel != null) { matcherBuilder.specify(withText(nameOrLabel)); } if (ancestorInfo != null) { matcherBuilder.specify(new ISWTWidgetMatcher() { public boolean matches(ISWTWidgetReference<?> widgetReference) { if (getAncestorInfo().matches(widgetReference)) { return true; } while (widgetReference != null) { widgetReference = widgetReference.getParent(); if (widgetReference == null) return false; if (getAncestorInfo().matches(widgetReference)) return true; } return false; } }); } if (parentInfo != null) { matcherBuilder.scope(index, parentInfo); } return matcherBuilder.build(); // return InternalMatcherBuilder.build2(this); } /////////////////////////////////////////////////////////////////////////// // // Finding // /////////////////////////////////////////////////////////////////////////// /* (non-Javadoc) * @see com.windowtester.runtime.WidgetLocator#findAll(com.windowtester.runtime.IUIContext) */ @Override public IWidgetLocator[] findAll(IUIContext ui) { ISWTWidgetMatcher matcher = buildMatcher(); ISWTWidgetReference<?>[] allWidgetsFound = SWTWidgetFinder.forActiveShell().findAll(matcher); if (getIndex() == UNASSIGNED) { return allWidgetsFound; } if (getIndex() > allWidgetsFound.length - 1) { return new ISWTWidgetReference<?>[0]; } ISWTWidgetReference<?>[] singleWidgetFound = new ISWTWidgetReference<?>[1]; singleWidgetFound[0] = allWidgetsFound[getIndex()]; return singleWidgetFound; // return Finder.findWidgets(matcher).in(Finder.activeShell()); } /////////////////////////////////////////////////////////////////////////// // // Selector Click Actions // /////////////////////////////////////////////////////////////////////////// /* (non-Javadoc) * @see com.windowtester.runtime.locator.IUISelector#click(com.windowtester.runtime.IUIContext, com.windowtester.runtime.locator.WidgetReference, com.windowtester.runtime.IClickDescription) */ public IWidgetLocator click(IUIContext ui, IWidgetReference widget, IClickDescription click) throws WidgetSearchException { IWidgetReference ref = widget; if (widget == null) ref = (IWidgetReference)ui.find(this); //only do this lookup if necessary Widget w = (Widget)ref.getWidget(); Point offset = getXYOffset(ref, click); preClick(ref, offset, ui); //gtkPreClickHack(w); Widget clicked; // try { // clicked = doClick(click.clicks(), w, offset, click.modifierMask()); clicked = w; new BasicSWTWidgetClickOperation((com.windowtester.runtime.swt.internal.widgets.SWTWidgetReference<Widget>)ref).forClick(click).atOffset(offset.x, offset.y).execute(); // } finally { // gtkPostClickHack(w); // } //note: this creates a legacy ref IWidgetReference clickedRef = WidgetReference.create(clicked, this); postClick(clickedRef, ui); return clickedRef; } // /** // * @nooverride This method is not intended to be re-implemented or extended by clients. // * @noreference This method is not intended to be referenced by clients. // * @since 3.7.1 // */ // protected void gtkPreClickHack(Widget w) { // // display thread will be blocked after the click so add a filter to catch ESC before the click // if (OS.isLinux()) { ///* final Display d = w.getDisplay(); // d.syncExec(new Runnable() { // public void run() { // escInterceptor.setCharged(true); // escInterceptor.setDisplay(d); // closeInterceptor.setCharged(true); // closeInterceptor.setDisplay(d); // traverseListener.setCharged(true); // traverseListener.setDisplay(d); // // // d.addFilter(SWT.KeyUp, escInterceptor); // // d.addFilter(SWT.Close, closeInterceptor); // d.addFilter(SWT.Traverse, traverseListener); // // } // });*/ // } // } // /** // * @nooverride This method is not intended to be re-implemented or extended by clients. // * @noreference This method is not intended to be referenced by clients. // * @since 3.7.1 // */ // protected void gtkPostClickHack(Widget w) { // if (OS.isLinux()) { ///* Display d1; // if(!w.isDisposed()){ // d1 = w.getDisplay(); // }else{ // d1 = Display.getDefault(); // } // // final Display d = d1; // // new abbot.swt.Robot().keyRelease(SWT.ESC); // // if( OS.isLinux() ){ // try { // Thread.sleep(50); // } catch (InterruptedException e) { // e.printStackTrace(); // } // } // d.syncExec(new Runnable() { // public void run() { // d.removeFilter(SWT.KeyUp, escInterceptor); // d.removeFilter(SWT.Close, closeInterceptor); // d.removeFilter(SWT.Traverse, traverseListener); // } // }); //*/ } // } /* (non-Javadoc) * @see com.windowtester.runtime.locator.IUISelector#contextClick(com.windowtester.runtime.IUIContext, com.windowtester.runtime.locator.WidgetReference, com.windowtester.runtime.IClickDescription, java.lang.String) */ public IWidgetLocator contextClick(IUIContext ui, final IWidgetReference widget, final IClickDescription click, String menuItemPath) throws WidgetSearchException { // IWidgetReference ref = widget; // if (widget == null) // ref = (IWidgetReference)ui.find(this); //only do this lookup if necessary // Widget w = (Widget)ref.getWidget(); // Point offset = getXYOffset(w, click); // preClick(w, offset, ui); // Widget clicked = doContextClick(w, offset, menuItemPath); // postClick(clicked, ui); // //note: this creates a legacy ref // return WidgetReference.create(clicked, this); return new MenuDriver().resolveAndSelect(new Callable<MenuReference>() { public MenuReference call() throws Exception { return showContextMenu(widget, click); } }, menuItemPath); } // TODO move this method into a reference class... but which one? private MenuReference showContextMenu(IWidgetReference widget, IClickDescription click) { // Default context click location is NOT in the center of the widget SWTLocation location = SWTWidgetLocation.withDefaultTopLeft33(widget, click); SWTMenuOperation op = new SWTShowMenuOperation(null).waitForIdle().click(WT.BUTTON3, location, false); op.execute(); return op.getMenu(); } // /** // * Perform the context click. This is intended to be overridden in subclasses. // */ // protected Widget doContextClick(Widget w, Point offset, String menuItemPath) throws WidgetNotFoundException, MultipleWidgetsFoundException { // return _selector.contextClick(w, offset.x, offset.y, menuItemPath); // } // /** // * Perform the click. This is intended to be overridden in subclasses // * @param clicks - the number of clicks // * @param w - the widget to click // * @param offset - the x,y offset (from top left corner) // * @param modifierMask - the mouse modifier mask // * @return the clicked widget // */ // protected Widget doClick(int clicks, Widget w, Point offset, int modifierMask) { // return _selector.click(w, offset.x, offset.y, modifierMask, clicks); // } /////////////////////////////////////////////////////////////////////////// // // Selector participation hooks // /////////////////////////////////////////////////////////////////////////// protected void preClick(IWidgetReference reference, Point offset, IUIContext ui) { Widget w = (Widget) reference.getWidget(); UIDriver driver = getLegacyUIDriver(ui); //use the driver to move the mouse button (if highlighting is on, this will //be delayed appropriately if (offset != null) driver.mouseMove(w, offset.x, offset.y); else driver.mouseMove(w); //use the driver to (possibly highlight) driver.highlight(w); } protected void postClick(IWidgetReference reference, IUIContext ui) { //use the driver to (possibly) pause getLegacyUIDriver(ui).postClickPause(); } /////////////////////////////////////////////////////////////////////////// // // XY helpers // /////////////////////////////////////////////////////////////////////////// /** * Test this click to see if an offset is specified. */ protected boolean unspecifiedXY(IClickDescription click) { //dummy sentinel for now return click.relative() == -1; } /** * Get the x,y offset for the click. * @param click */ public Point getXYOffset(IWidgetReference reference, IClickDescription click) { Widget w = (Widget) reference.getWidget(); // TODO[pq]: we want this method to stay public but want to use an alternative version that uses our new widget refs... if (unspecifiedXY(click)) { /* * crude hack to handle links where we need to find the offset of the href. * NOTE: this only handles Links with exactly one href. If the need arises * we will need to revisit. */ if (w instanceof Link) { Rectangle offset = new LinkReference((Link)w).getOffset(0); //sanity check in case y is unset (in win32 case) if (offset.y == 0) offset.y = 4; if (offset != null) return new Point(offset.x + offset.width/2, offset.y/2); } // TODO[pq]: this reference should already be calculated and passed in ISWTWidgetReference<?> ref = (ISWTWidgetReference<?>) WTRuntimeManager.asReference(w); Rectangle rect = ref.getDisplayBounds(); return getUnspecifiedXYOffset(rect); } return new Point(click.x(), click.y()); } protected Point getUnspecifiedXYOffset(Rectangle rect) { return new Point(rect.width/2, rect.height/2); } /////////////////////////////////////////////////////////////////////////// // // Adaptation // /////////////////////////////////////////////////////////////////////////// public static SWTWidgetLocator adapt(IWidgetLocator parent) { if (parent instanceof IWidgetReference) return new SWTWidgetReference((IWidgetReference)parent); if (parent instanceof SWTWidgetLocator) return (SWTWidgetLocator) parent; // fall-through return null; } protected UIDriver getLegacyUIDriver(IUIContext ui) { return (UIDriver)ui.getAdapter(UIDriver.class); } /* (non-Javadoc) * @see com.windowtester.runtime.WidgetLocator#getAdapter(java.lang.Class) */ @Override public Object getAdapter(Class<?> adapter) { if (adapter == ISWTWidgetMatcher.class) return getMatcher(); return super.getAdapter(adapter); } /////////////////////////////////////////////////////////////////////////// // // Default reference Implementation // /////////////////////////////////////////////////////////////////////////// // TODO[pq]: how does this untyped proxying widget ref play with our new typed widget ref story? static class SWTWidgetReference extends SWTWidgetLocator implements IWidgetReference { private static final long serialVersionUID = -4977467342559356305L; private final IWidgetReference _ref; public SWTWidgetReference(IWidgetReference ref) { super(Widget.class); //ugh? _ref = ref; } /* (non-Javadoc) * @see com.windowtester.runtime.WidgetLocator#findAll(com.windowtester.runtime.IUIContext) */ public IWidgetLocator[] findAll(IUIContext ui) { return new IWidgetLocator[] {this}; } /* (non-Javadoc) * @see com.windowtester.runtime.locator.IWidgetReference#getWidget() */ public Object getWidget() { return _ref.getWidget(); } /* (non-Javadoc) * @see com.windowtester.runtime.swt.locator.SWTWidgetLocator#matches(java.lang.Object) */ public boolean matches(Object widget) { return getWidget() == widget; } } //////////////////////////////////////////////////////////////////////////// // // IsVisibleLocator // //////////////////////////////////////////////////////////////////////////// public boolean isVisible(IUIContext ui) throws WidgetSearchException { return ui.findAll(this).length > 0; } /** * Create a condition that tests if the widget is visible. * Note that this is a convenience method, equivalent to: * <code>isSelected(true)</code> */ public IUICondition isVisible() { return isVisible(true); } /** * Create a condition that tests if the given the widget is visible. * @param selected * @param expected <code>true</code> if the widget is expected to be selected, else * <code>false</code> */ public IUICondition isVisible(boolean expected) { return new IsVisibleCondition(this, expected); } //////////////////////////////////////////////////////////////////////////// // // HasFocusLocator // //////////////////////////////////////////////////////////////////////////// /** * Resolve the locator to a single object and determine if that object has focus. * This method is ONLY supported for those subclasses that implement the * {@link HasFocus} interface. * * @param ui the UI context in which to find the widgets * @return <code>true</code> if the object has focus, else false * @see IsVisible#isVisible() */ public boolean hasFocus(IUIContext ui) throws WidgetSearchException { return new HasFocusConditionHandler(this).hasFocus(ui); } /** * Resolve the locator to a single object and determine if that object has focus.<p/> * Used in an {@link IUIContext#ensureThat(com.windowtester.runtime.condition.IConditionHandler)} clause, the resulting condition * can be used to ensure that the associated widget has focus. For example: * <p> * <code> * ui.ensureThat(new ButtonLocator("OK").hasFocus()); * </code> * </p> * tests if the "OK" button has focus and if it does not, gives it focus. * */ public IUIConditionHandler hasFocus() { return new HasFocusConditionHandler(this); } // ////////////////////////////////////////////////////////////////////////// // // IsEnabledLocator // //////////////////////////////////////////////////////////////////////////// /** * Resolve the locator to a single object and determine if that object is enabled. * This method is ONLY supported for those subclasses that implement the * {@link IsEnabled} interface. This method finds the widget then calls the * {@link #isWidgetEnabled(Control)} on the UI thread to determine if the widget is * enabled. * * @param ui the UI context in which to find the widgets * @return <code>true</code> if the object is enabled, else false * @see IsEnabled#isEnabled() */ public boolean isEnabled(IUIContext ui) throws WidgetSearchException { IWidgetLocator found = ui.find(this); if (found instanceof IWidgetReference) { final Object widget = ((IWidgetReference) found).getWidget(); final boolean[] result = new boolean[1]; final Exception[] exception = new Exception[1]; Display.getDefault().syncExec(new Runnable() { public void run() { try { result[0] = isWidgetEnabled((Widget)widget); } catch (Exception e) { exception[0] = e; } } }); if (exception[0] != null) throw new WidgetSearchException(exception[0]); return result[0]; } return false; } /** * This is called by {@link #isEnabled(IUIContext)} on the UI thread to determine if * the widget is enabled. Subclasses may override to provide additional or alternate * behavior. This is only intended to be called by the {@link #isEnabled(IUIContext)} * method and not by clients. * * @param swtWidget the widget to be tested (not <code>null</code>) * @return <code>true</code> if enabled, else <code>false</code> */ protected boolean isWidgetEnabled(final Widget swtWidget) throws WidgetSearchException { if (swtWidget instanceof Control) { Control control = (Control)swtWidget; return !control.isDisposed() && control.isEnabled(); } if (swtWidget instanceof ToolItem){ ToolItem item = (ToolItem)swtWidget; return !item.isDisposed() && item.isEnabled(); } return false; } //TODO [author=Jaime] need for isVisible, or are we done with the existing isWidgetEnabled method just above? //////////////////////////////////////////////////////////////////////////// // // HasText // //////////////////////////////////////////////////////////////////////////// /** * Resolve the locator to a single object and answer the text associated with it. * This method is ONLY supported for those subclasses that implement the * {@link HasText} interface. This method finds the widget then calls the * {@link #getWidgetText(Control)} on the UI thread to obtain the widget text. * * @param ui the UI context in which to find the widgets * @return the text associated with that object (may be null) */ public String getText(IUIContext ui) throws WidgetSearchException { IWidgetLocator found = ui.find(this); if (found instanceof IWidgetReference) { final Object widget = ((IWidgetReference) found).getWidget(); if (widget instanceof Control) { final String[] result = new String[1]; final Exception[] exception = new Exception[1]; Display.getDefault().syncExec(new Runnable() { public void run() { try { result[0] = getWidgetText((Control) widget); } catch (Exception e) { exception[0] = e; } } }); if (exception[0] != null) throw new WidgetSearchException(exception[0]); return result[0]; } } return null; } /** * This is called by {@link #getText(IUIContext)} on the UI thread to obtain the * widget's text. Subclasses that implement {@link HasText} should override * {@link #getText(IUIContext)} or this method to return text for the widget, because * this method always throws a Runtime "not implemented" exception. This is only * intended to be called by the {@link #getText(IUIContext)} method and not by * clients. * * @param swtWidget the widget from which text is to be obtained (not <code>null</code>) * @return the widget's text or <code>null</code> if none * @throws RuntimeException if this is not supported by this type of locator */ protected String getWidgetText(Control widget) throws WidgetSearchException { throw new InaccessableWidgetException("HasText not implemented by this locator: " + getClass().getName()); } /////////////////////////////////////////////////////////////////////////// // // Debugging // /////////////////////////////////////////////////////////////////////////// /* (non-Javadoc) * @see com.windowtester.runtime.WidgetLocator#getWidgetLocatorStringName() */ protected String getWidgetLocatorStringName() { return getClass().getSimpleName(); } /* (non-Javadoc) * @see com.windowtester.runtime.WidgetLocator#toString() */ public String toString() { StringBuffer sb = new StringBuffer(); sb.append(getWidgetLocatorStringName()); sb.append("("); boolean needsDelim = false; String detail = getToStringDetail(); if (detail != null) { sb.append(detail); needsDelim = true; } String label = getNameOrLabel(); if (label != null) { if (needsDelim) sb.append(", "); //either way, we need to make sure we get a delim next time needsDelim = true; sb.append(label); } WidgetLocator parentInfo = getParentInfo(); if (parentInfo != null) { if (needsDelim) sb.append(", "); int index = getIndex(); if (index != UNASSIGNED) { sb.append(index).append(", "); } sb.append(parentInfo); } sb.append(")"); return sb.toString(); } /** * Details used in <code>toString()</code> to specify things like name, type or * path info. Default returns <code>null</code>. */ protected String getToStringDetail() { /* * A bit of a kludge. We want subclasses to have no behavior but * for the base class, use targetClass info. */ if (!getClass().equals(SWTWidgetLocator.class)) return null; /* Class targetClass = getTargetClass(); if (targetClass == null) return null; return targetClass.getName(); */ String clsName = getTargetClassName(); if (clsName == null) return null; return clsName; } }