package abbot.swt; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.WeakHashMap; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.dnd.DragSource; import org.eclipse.swt.dnd.DropTarget; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Caret; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.CoolBar; import org.eclipse.swt.widgets.CoolItem; import org.eclipse.swt.widgets.Decorations; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.MenuItem; import org.eclipse.swt.widgets.ScrollBar; import org.eclipse.swt.widgets.Scrollable; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.TabFolder; import org.eclipse.swt.widgets.TabItem; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableColumn; import org.eclipse.swt.widgets.TableItem; import org.eclipse.swt.widgets.Text; import org.eclipse.swt.widgets.ToolBar; import org.eclipse.swt.widgets.ToolItem; import org.eclipse.swt.widgets.Tracker; import org.eclipse.swt.widgets.Tree; import org.eclipse.swt.widgets.TreeItem; import org.eclipse.swt.widgets.Widget; import com.windowtester.runtime.util.StringComparator; import abbot.Log; import abbot.finder.swt.MultipleWidgetsFoundException; import abbot.finder.swt.WidgetNotFoundException; import abbot.script.swt.ButtonReference; import abbot.script.swt.WidgetReference; import abbot.tester.swt.DecorationsTracker; import abbot.tester.swt.Robot; import abbot.tester.swt.WidgetTester; /** * * @deprecated Use the new matcher API in abbot.finder.swt. The class * abbot.finder.swt.BasicFinder most closely duplicates the functionality * of this class. * */ public class DefaultWidgetFinder implements WidgetFinder { public static final String copyright = "Licensed Materials -- Property of IBM\n"+ "(c) Copyright International Business Machines Corporation, 2003\nUS Government "+ "Users Restricted Rights - Use, duplication or disclosure restricted by GSA "+ "ADP Schedule Contract with IBM Corp."; /** Match weight for a strong match. */ public static final int MW_STRONG_MATCH = 50; /** Match weight for a weak match. */ public static final int MW_WEAK_MATCH = 1; public static final String ROOT_MENU_FLAG ="ROOT_MENU"; // crude, very Eclipse-dependent, and un-i14ed // attempt to identify main workbench window public static final String WORKBENCH_TITLE = "Resource - "; // Matching weights for various attributes private static final int MW_TEXT = 10; private static final int MW_NAME = 100; private static final int MW_TAG = 50; private static final int MW_PARENT = 25; private static final int MW_INVOKER = 25; private static final int MW_INDEX = 25; private static final int MW_WINDOW = 10; private static final int MW_TITLE = 10; private static final int MW_CLASS = 1; /** Match weight corresponding to no possible match. */ public static final int MW_FAILURE = 0; /** Timeout for waiting on a popup menu. */ public static int POPUP_TIMEOUT = 5000; private WeakHashMap filteredWidgets = new WeakHashMap(); /** Allow chaining to existing filter sets. */ private WidgetFinder parent = null; private boolean filter = true; //private WindowTracker tracker = WindowTracker.getInstance(); static { String to = System.getProperty("abbot.finder.popup_timeout"); if (to != null) { try { POPUP_TIMEOUT = Integer.parseInt(to); } catch(Exception e) { } } } private static DefaultWidgetFinder defaultFinder = null; // static vars used for inner classes (ie, runnables). // Only use these in synchronized methods private boolean boolT; private Widget widgetT; private String stringT; private int intT; private Object objT; /** This is the factory method to use unless you already know otherwise :) */ public static WidgetFinder getFinder() { return getFinder(null); } /** This factory method allows the finder to be used where a finder * already exists and is filtering components. This should only be * required in very rare cases like when the script editor needs to test * itself. */ public static synchronized WidgetFinder getFinder(WidgetFinder context) { if (defaultFinder == null) { defaultFinder = new DefaultWidgetFinder(context); } return defaultFinder; } /** Only factory creation is allowed. */ private DefaultWidgetFinder(WidgetFinder parent) { this.parent = parent; } /** * Attempt to find the component corresponding to the given reference * among existing, visible components. * * @param ref A Widget reference to use to identify a component * @throws WidgetNotFoundException if a Widget cannot be located via * the passed in WidgetReference * @throws MultipleWidgetsFoundException if more then one component is * found via the passed in reference * @return The component which matches the passed in WidgetReference */ public Widget findWidget(WidgetReference ref) throws WidgetNotFoundException, MultipleWidgetsFoundException { return findWidget(ref, null); } /** * Attempt to find the component corresponding to the given reference * among existing, visible components. * * @param ref A Widget reference to use to identify a component * @param s a Shell in which to look * @throws WidgetNotFoundException if a Widget cannot be located via * the passed in WidgetReference * @throws MultipleWidgetsFoundException if more then one component is * found via the passed in reference * @return The component which matches the passed in WidgetReference */ public Widget findWidget(WidgetReference ref, Shell s) throws WidgetNotFoundException, MultipleWidgetsFoundException { HashSet comps = findMatches(s, ref, MATCH_WEAK); Iterator iter = null; Widget comp = null; int max = -99999; ArrayList multiples = new ArrayList(); if (comps.isEmpty()) { throw new WidgetNotFoundException("Widget '" + ref + "' not found"); } if (comps.size() == 1) { iter = comps.iterator(); return (Widget)iter.next(); } iter = comps.iterator(); comp = (Widget)iter.next(); max = getWidgetMatchWeight(comp, ref); while(iter.hasNext()) { Widget next = (Widget)iter.next(); int val = getWidgetMatchWeight(next, ref); if (val > max) { max = val; comp = next; multiples.clear(); } else if (val == max) { if (multiples.size() == 0) { multiples.add(comp); } multiples.add(next); } } if (multiples.size() > 0) { String msg = "More than one match for '" + ref + "' found"; final Widget[] list = (Widget[]) multiples.toArray(new Widget[multiples.size()]); System.err.println("MULTIPLE WIDGETS FOUND:"); for(int j=0; j<list.length;j++){ final int index = j; Robot.syncExec(((Widget)list[j]).getDisplay(),null,new Runnable(){ public void run(){ System.err.println("\t"+list[index]); } }); } System.err.println("------------------------\n"); throw new MultipleWidgetsFoundException(msg, list); } return comp; } /** * Find a <code>MenuItem</code> given a suitable <code>Reference</code>. * CONTRACT: suitability is not checked! */ public static MenuItem findMenuItem(WidgetReference ref) throws WidgetNotFoundException, MultipleWidgetsFoundException { return (MenuItem)(getFinder().findWidget(ref)); } /** * Find a <code>MenuItem</code> in the indicated <code>Shell</code> * given a suitable <code>Reference</code>. * CONTRACT: suitability is not checked! */ public static MenuItem findMenuItemInShell(WidgetReference ref, Shell sh) throws WidgetNotFoundException, MultipleWidgetsFoundException { return (MenuItem)(getFinder().findWidget(ref, sh)); } /** * Find a <code>Text</code> given a suitable <code>Reference</code>. * CONTRACT: suitability is not checked! */ public static Text findText(WidgetReference ref) throws WidgetNotFoundException, MultipleWidgetsFoundException { return (Text)(getFinder().findWidget(ref)); } /** * Find a <code>StyledText</code> given a suitable <code>Reference</code>. * CONTRACT: suitability is not checked! */ public static StyledText findStyledText(WidgetReference ref) throws WidgetNotFoundException, MultipleWidgetsFoundException { return (StyledText)(getFinder().findWidget(ref)); } /** * Find a <code>Text</code> in the indicated <code>Shell</code> * given a suitable <code>Reference</code>. * CONTRACT: suitability is not checked! */ public static Text findTextInShell(WidgetReference ref, Shell sh) throws WidgetNotFoundException, MultipleWidgetsFoundException { return (Text)(getFinder().findWidget(ref, sh)); } /** * Find a <code>StyledText</code> in the indicated <code>Shell</code> * given a suitable <code>Reference</code>. * CONTRACT: suitability is not checked! */ public static StyledText findStyledTextInShell(WidgetReference ref, Shell sh) throws WidgetNotFoundException, MultipleWidgetsFoundException { return (StyledText)(getFinder().findWidget(ref, sh)); } /** * Find a <code>Label</code> given a suitable <code>Reference</code>. * CONTRACT: suitability is not checked! */ public static Label findLabel(WidgetReference ref) throws WidgetNotFoundException, MultipleWidgetsFoundException { return (Label)(getFinder().findWidget(ref)); } /** * Find a <code>Label</code> in the indicated <code>Shell</code> * given a suitable <code>Reference</code>. * CONTRACT: suitability is not checked! */ public static Label findLabelInShell(WidgetReference ref, Shell sh) throws WidgetNotFoundException, MultipleWidgetsFoundException { return (Label)(getFinder().findWidget(ref, sh)); } /** * Find a <code>Button</code> given a suitable <code>Reference</code>. * CONTRACT: suitability is not checked! */ public static Button findButton(WidgetReference ref) throws WidgetNotFoundException, MultipleWidgetsFoundException { return (Button)(getFinder().findWidget(ref)); } /** * Find a <code>Button</code> in the indicated <code>Shell</code> * given a suitable <code>Reference</code>. * CONTRACT: suitability is not checked! */ public static Button findButtonInShell(WidgetReference ref, Shell sh) throws WidgetNotFoundException, MultipleWidgetsFoundException { return (Button)(getFinder().findWidget(ref, sh)); } /** Return the window with the given name or title. Attempts to find a * named window first, since that search is more restrictive. */ public Decorations findDecorations(String nameOrTitle) { Decorations w = findDecorationsByName(nameOrTitle); if (w == null) w = findDecorationsByTitle(nameOrTitle); return w; } /** * Locates a window using the name as the match criterion. * @param match The string to match with a window name * @return A window with a name equal to the passed in string, null * otherwise */ public synchronized Decorations findDecorationsByName(String match) { Decorations[] windows = getDecorations(); for (int i=0;i < windows.length;i++) { widgetT = windows[i]; Decorations w = windows[i]; Robot.syncExec(w.getDisplay(),this,new Runnable(){ public void run(){ boolT = ((Decorations)widgetT).isVisible(); } }); if (boolT) { String name = getWidgetName(w); if ((match != null && name!=null && match.equals(name)) || name == match) return w; } } return null; } /** Return the first visible window whose title matches the given * pattern. */ public synchronized Decorations findDecorationsByTitle(String title) { Decorations[] windows = getDecorations(); for (int i=0;i < windows.length;i++) { Decorations w = windows[i]; widgetT = w; Robot.syncExec(w.getDisplay(),this,new Runnable(){ public void run(){ boolT = ((Decorations)widgetT).isVisible(); intT = ((Decorations)widgetT).getStyle(); stringT = ((Decorations)widgetT).getText(); } }); if (boolT && (intT&SWT.TITLE)==SWT.TITLE) { if(titlesMatch(title,stringT)) return w; } } return null; } public synchronized Widget findActivePopupMenu(final Widget root) { objT = null; if (root == null) { Shell[] shells = getRootShells(); for (int i=0;i < shells.length;i++) { Widget widget = (Widget)shells[i]; Widget menu = findActivePopupMenu(widget); if (menu != null) return menu; } } else{ Robot.syncExec(root.getDisplay(),this,new Runnable(){ public void run(){ if (root instanceof Menu && ( ((Menu)root).getStyle() & SWT.POP_UP )==SWT.POP_UP && ((Menu)root).isVisible() ) objT = root; else if(root instanceof Control){ Menu menu = ((Control)root).getMenu(); if(menu != null && ((Menu)root).isVisible() ) objT = menu; } else if (root instanceof Composite) { Widget[] subs = ((Composite)root).getChildren(); for (int i=0;i < subs.length;i++) { Widget menu = findActivePopupMenu(subs[i]); if (menu != null) objT = menu; } } } }); } return (Widget)objT; } public synchronized Widget findMenuItemByText(final Widget root, final String name){ widgetT = null; Robot.syncExec(root.getDisplay(),this,new Runnable(){ public void run(){ if(root instanceof Menu){ MenuItem[] items = ((Menu)root).getItems(); for(int i=0; i<items.length;i++){ Widget widget = findMenuItemByText(items[i],name); if(widget!=null) widgetT = widget; } } else if(root instanceof MenuItem){ if(((MenuItem)root).getText().equals(name)) widgetT = root; if(((MenuItem)root).getMenu()!=null) widgetT = findMenuItemByText(((MenuItem)root).getMenu(),name); } else if (root instanceof Decorations){ Widget widget=null; if(((Decorations)root).getMenu()!=null) widget = findMenuItemByText(((Decorations)root).getMenu(),name); if(widget!=null) widgetT =widget; else if(((Decorations)root).getMenuBar()!=null) widget = findMenuItemByText(((Decorations)root).getMenuBar(),name); if(widget!=null) widgetT = widget; } else if (root instanceof Control){ if(((Control)root).getMenu()!=null) widgetT = findMenuItemByText(((Control)root).getMenu(),name); } } }); return widgetT; } /** * Return the set of all components under the given component's * hierarchy (inclusive) which match the given reference. */ /* * Note that <code>ancestor</code> can also be the widget itself: * recursive implementation. */ private HashSet findMatches(Widget ancestor, WidgetReference ref, int type){ Log.debug("Scanning " + /*Robot.toString*/(ancestor)); // // DEBUG //// if (ancestor instanceof Text) { // if ((ancestor instanceof Text) && (ancestor.getData() != null)) { // String tag = (String)(ancestor.getData("name")); //// if ((tag != null) && (tag.length() > 0)) { // Log.debug("got Text"); //// } // } // // END DEBUG HashSet set = new HashSet(); if (isWidgetFiltered(ancestor)) return set; if (ancestor == null) { // Examine all top-level components and their owned windows. // Shell[] shells = getRootShells(); List allShells = getAllShells(); Shell[] shells = (Shell[])(allShells.toArray(new Shell[allShells.size()])); for (int f=0;f < shells.length;f++) { set.addAll(findMatches(shells[f], ref, type)); } return set; } if (widgetsMatch(ancestor, ref, type)) { Log.debug("Found match"); set.add(ancestor); } Widget[] children = getWidgetChildren(ancestor,true); for(int i=0; i<children.length;i++){ Widget child = children[i]; // COULD skip this hierarchy if the child is a Decorations // and 1) frame titles don't match; and 2) the child is not showing set.addAll(findMatches(child, ref, type)); // TODO this makes many unnecesary recursive checks, so FIX } return set; } /** * Return the first shell matching the passed title. */ public Shell findShellByTitle(String title) { List allshells = getAllShells(); // DEBUG return findShellByTitle(title, (Shell[])(allshells.toArray(new Shell[allshells.size()]))); } /** * Return the first shell in the shell array matching the passed title. */ public Shell findShellByTitle(String title, Shell[] sa){ return findShellByTitle(title, sa, false); } /** * Return the first shell in the shell array matching the passed title. */ public Shell findShellByTitle( final String title, final Shell[] sa, final boolean filterWorkbench) { Log.debug("start findShellByTitle"); if (title == null) { throw new IllegalArgumentException("must pass title of shell sought"); } if (sa == null) { throw new IllegalArgumentException("must pass array of shells"); } // first, breadth-first ... List forDepth = new ArrayList(sa.length); for (int j = 0; j < sa.length; j++) { final Shell s = sa[j]; if (s != null) { // String t = s.getText(); // SWTException! // if (title.equals(t)) return s; // if (!filterWorkbench || !t.equals(WORKBENCH_TITLE)) forDepth.add(s); // // use boolT as flag, since we can't return inside the runnable // boolT = false; stringT = null; s.getDisplay().syncExec(new Runnable() { public void run() { stringT = s.getText(); // if (title.equals(stringT)) boolT = true; } }); // TODO: reverse tests, or test stringT. For now, I wanna know if it's crap. if (stringT.equals(title)) return s; if (!filterWorkbench || !stringT.equals(WORKBENCH_TITLE)) forDepth.add(s); } } // then go for depth. inefficient in sa.length ... for (Iterator it = forDepth.iterator(); it.hasNext();) { Shell s = findShellByTitleInHierarchy(title, (Shell)(it.next())); if (s != null) return s; } return null; } // WARNING: THERE MAY BE SOME UNRESOLVED SYNCHRONIZATION ISSUES HERE... /** * Return the first Shell with the passed title * among parent Shell or its children. */ public Shell findShellByTitleInHierarchy( final String title, final Shell parent){ Log.debug("start findShellByTitleInHierarchy"); if (title == null) { throw new IllegalArgumentException("must pass title of shell sought"); } if (parent == null) { throw new IllegalArgumentException("must pass parent shell"); } // if (title.equals(parent.getText())) return parent; // SWTException! // get the title using our private stringT stringT = null; parent.getDisplay().syncExec(new Runnable() { public void run() { stringT = parent.getText(); } }); // TODO: reverse test, or test stringT. For now, I wanna know if it's crap. if (stringT.equals(title)) { return parent; } else { return findShellByTitleInList(title, getChildren(parent)); } } // WARNING: THERE MAY BE SOME UNRESOLVED SYNCHRONIZATION ISSUES HERE... private Control[] kids; // children of parent Shell /** * Return a List of Control's that are children of the parent Shell. */ public List getChildren(final Shell parent){ kids = null; parent.getDisplay().syncExec(new Runnable() { public void run() { kids = parent.getChildren(); } }); if ((kids == null) || (kids.length <= 0)) return null; // nothing to do ArrayList seek = new ArrayList(kids.length); seek.addAll(Arrays.asList(kids)); return seek; } /** * Return the first Shell with the passed title * among a List of Control's. */ public Shell findShellByTitleInList(final String title, List seek){ if ((seek == null) || (seek.size() <= 0)) return null; // nothing to do for (Iterator it = seek.iterator(); it.hasNext();) { Control topC = (Control)(it.next()); if ((topC == null) || (!(topC instanceof Shell))) continue; final Shell topS = (Shell)topC; if (topS == null) continue; // if (title.equals(topS.getText())) { // else get its title using our private stringT stringT = null; topS.getDisplay().syncExec(new Runnable() { public void run() { stringT = topS.getText(); } }); // TODO: reverse test, or test stringT. For now, I wanna know if it's crap. if (stringT.equals(title)) { return topS; } else { seek.addAll(Arrays.asList(topS.getChildren())); } } // found nothing? return null; } /** Return the total weight required for an exact match. */ private int getExactMatchWeight(WidgetReference ref) { int weight = MW_CLASS; if (ref.getName() != null) weight += MW_NAME; if (ref.getTag() != null) weight += MW_TAG; if (ref.getInvokerID() != null) weight += MW_INVOKER; if (ref.getParentID() != null) weight += MW_PARENT; if (ref.getWindowID() != null) weight += MW_WINDOW; if (ref.getTitle() != null) weight += MW_TITLE; if (ref.getIndex() != -1) weight += MW_INDEX; return weight; } // FIXME should probably throw if more than one match is found. public WidgetReference matchWidget(Widget comp, Iterator iter, int matchType) { //Log.debug("Looking for " + Robot.toString(comp) + " in a group"); WidgetReference match = null; int matchWeight = -1000; while (iter.hasNext()) { WidgetReference ref = (WidgetReference)iter.next(); if (widgetsMatch(comp, ref, matchType)) { int wt = getWidgetMatchWeight(comp, ref); if (wt > matchWeight) { match = ref; matchWeight = wt; } else if (wt == matchWeight) { // FIXME work out how to throw and handle this properly Log.warn("Multiple references match"); throw new RuntimeException("Multiple references match"); } } } //Log.debug(match != null ? "Found" : "Not found"); return match; } /** Return a measure of how well the given component matches the given * component reference. */ public int getWidgetMatchWeight(Widget comp, WidgetReference ref) { int weight = MW_FAILURE; if (null == comp || null == ref){ return MW_FAILURE; } if (!ref.isAssignableFrom(comp.getClass())) { return MW_FAILURE; } else { weight += MW_CLASS; // Exact class matches are better than non-exact matches if (ref.getRefClassName().equals(comp.getClass().getName())) weight += MW_CLASS; } String compTag = WidgetTester.getTag(comp); String refTag = ref.getTag(); if (null != compTag && null != refTag) { if (compTag.equals(refTag)) { weight += MW_TAG; } else { return MW_FAILURE; } } // NEW STUFF String compText = getWidgetText(comp); String refText = ref.getText(); if(null != compText && null != refText) { if(compText.equals(refText)) weight+= MW_TEXT; else return MW_FAILURE; } // END NEW STUFF String compName = getWidgetName(comp); String refName = ref.getName(); if (null != compName && null != refName) { if (compName.equals(refName)) { weight += MW_NAME; } else { weight -= MW_NAME; } } if (null != ref.getParentID()) { if (widgetsMatch(getWidgetParent(comp), ref.getParentReference(), MATCH_EXACT)) { weight += MW_PARENT; } // Don't detract on parent mismatch, since changing a parent is // not that big a change (e.g. adding a scroll pane) } if (null != ref.getWindowID()) { if (widgetsMatch(getWidgetDecorations(comp), ref.getWindowReference(), MATCH_EXACT)) { weight += MW_WINDOW; } else { // Changing windows is a big change and not very likely weight -= MW_WINDOW; } } if (ref.getTitle() != null) { if (titlesMatch(ref.getTitle(), getWidgetDecorationsTitle(comp))) { weight += MW_TITLE; } // Don't subtract on mismatch, since title changes are common } if (ref.getIndex() != -1) { Widget parent = getWidgetParent(comp); if (null != parent) { Widget[] children = getWidgetChildren(parent,true); for (int i = 0; i < children.length; ++i) { if (children[i] == comp && i == ref.getIndex()) { weight += MW_INDEX; break; } } } } /* Log.debug("Comparing " + Robot.toString(comp) + " to '" + ref + "', weight is " + weight); */ return weight; } /** Determine the best we can whether the component is the one referred to * by the reference. The match may be a strong or a weak one. */ public boolean widgetsMatch(Widget comp, WidgetReference ref, int type){ int weight = getWidgetMatchWeight(comp, ref); int min = 0; if (ref==null) return false; // can't be a match if the passed reference is null if (type == MATCH_EXACT) { min = getExactMatchWeight(ref); if (weight >= min && min < MW_STRONG_MATCH) { Log.warn("Exact match weight comparing " + ref + " to " + /*Robot.toString*/(comp) + " is less than a strong match"); } } else if (type == MATCH_STRONG) { min = Math.min(MW_STRONG_MATCH, getExactMatchWeight(ref)); } else if (type == MATCH_WEAK) { min = Math.min(MW_WEAK_MATCH, getExactMatchWeight(ref)); } else throw new IllegalArgumentException("Type must be one of MATCH_EXACT, MATCH_STRONG, or MATCH_WEAK"); return weight >= min; } private boolean isWidgetFiltered(Widget comp) { if (comp == null) return false; return filter && (filteredWidgets.containsKey(comp) || (parent != null && parent.isFiltered(comp))); } /** Returns if any ancestor composite is filtered */ private boolean isAncestorFiltered(Widget widget) { Widget parent = getWidgetParent(widget); if (parent == null) { return false; } return isFiltered(parent); } /** Returns true if the component or its Window ancestor is filtered. */ public boolean isFiltered(Widget comp) { return (comp != null) && (isWidgetFiltered(comp) || isAncestorFiltered(comp)); } public void discardAllWidgets() { Iterator iter = getDecorationsList().iterator(); while(iter.hasNext()) { discardWidget((Decorations)iter.next()); } } public void setFilterEnabled(boolean enable) { this.filter = enable; } // COMPLETED::::: /** Send an explicit window close event to all showing windows. Note that this is not guaranteed to actually make the window go away. */ public synchronized void closeWindows() { Iterator iter = getDecorationsList().iterator(); while(iter.hasNext()){ Decorations dec = (Decorations)iter.next(); widgetT = dec; Robot.syncExec(dec.getDisplay(),this,new Runnable(){ public void run(){ if(((Decorations)widgetT).isVisible()){ ((Decorations)widgetT).setVisible(false); if(((Decorations)widgetT) instanceof Shell) ((Shell)widgetT).close(); } } }); } } /** * Return whether the the given title matches the given pattern. */ private boolean titlesMatch(String pattern, String actual) { boolean match = StringComparator.matches(actual, pattern); return match; } /** Look up the apparent parent of a component. A * popup menu's parent is the menu or component that spawned it. */ public synchronized Widget getWidgetParent(final Widget widget) { widgetT = null; if(widget==null) System.err.println("NULL WIDGET"); if(this==null) System.err.println("THIS IS NULL"); Robot.syncExec(widget.getDisplay(),this,new Runnable(){ public void run(){ if(widget instanceof Control) widgetT=((Control)widget).getParent(); if(widget instanceof Caret) widgetT=((Caret)widget).getParent(); if(widget instanceof Menu) widgetT=((Menu)widget).getParent(); if(widget instanceof ScrollBar) widgetT=((ScrollBar)widget).getParent(); if(widget instanceof CoolItem) widgetT=((CoolItem)widget).getParent(); if(widget instanceof MenuItem) widgetT=((MenuItem)widget).getParent(); if(widget instanceof TabItem) widgetT=((TabItem)widget).getParent(); if(widget instanceof TableColumn) widgetT=((TableColumn)widget).getParent(); if(widget instanceof TableItem) widgetT=((TableItem)widget).getParent(); if(widget instanceof ToolItem) widgetT=((ToolItem)widget).getParent(); if(widget instanceof TreeItem) widgetT=((TreeItem)widget).getParent(); if(widget instanceof DragSource) widgetT=((DragSource)widget).getControl().getParent(); if(widget instanceof DropTarget) widgetT=((DropTarget)widget).getControl().getParent(); if(widget instanceof Tracker) Log.debug("requested the parent of a Tracker- UNFINDABLE"); } }); return widgetT; } /* public Widget[] getWidgetChildren(Widget widget, boolean recurse){ LinkedList children = new LinkedList(); if(widget instanceof Control){ if(((Control)widget).getMenu()!=null) children.add(((Control)widget).getMenu()); } if(widget instanceof Decorations){ if(((Decorations)widget).getMenuBar()!=null) children.add(((Decorations)widget).getMenu()); } if(widget instanceof Composite){ Widget[] widgets = ((Composite)widget).getChildren(); if(widgets.length!=0) children.addAll(Arrays.asList(widgets)); } if(widget instanceof CoolBar){ Widget[] widgets = ((CoolBar)widget).getItems(); if(widgets.length!=0) children.addAll(Arrays.asList(widgets)); } if(widget instanceof TabFolder){ Widget[] widgets = ((TabFolder)widget).getItems(); if(widgets.length!=0) children.addAll(Arrays.asList(widgets)); } if(widget instanceof Table){ Widget[] widgets = ((Table)widget).getItems(); if(widgets.length!=0) children.addAll(Arrays.asList(widgets)); widgets = ((Table)widget).getColumns(); if(widgets.length!=0) children.addAll(Arrays.asList(widgets)); } if(widget instanceof ToolBar){ Widget[] widgets = ((ToolBar)widget).getItems(); if(widgets.length!=0) children.addAll(Arrays.asList(widgets)); } if(widget instanceof Tree){ Widget[] widgets = ((Tree)widget).getItems(); if(widgets.length!=0) children.addAll(Arrays.asList(widgets)); } if(recurse){ LinkedList extendedFamily = new LinkedList(); Iterator iter = children.iterator(); Widget w; while(iter.hasNext()){ w = (Widget)iter.next(); extendedFamily.addAll(Arrays.asList(getWidgetChildren(w,recurse))); } children.addAll(extendedFamily); } return (Widget[])(children.toArray(new Widget[children.size()])); } */ boolean disposed; public synchronized boolean isDisposed(final Widget w){ w.getDisplay().syncExec(new Runnable(){ public void run(){ disposed = w.isDisposed(); } }); return disposed; } public synchronized Widget[] getWidgetChildren(final Widget widget, final boolean recurse){ //LinkedList children = new LinkedList(); objT = new LinkedList(); if(widget==null || isDisposed(widget) ) return new Widget[0]; Robot.syncExec(widget.getDisplay(),this,new Runnable(){ public void run(){ if(widget instanceof Control){ if(((Control)widget).getMenu()!=null) ((LinkedList)objT).add(((Control)widget).getMenu()); } if(widget instanceof Scrollable){ if(((Scrollable)widget).getVerticalBar()!=null) ((LinkedList)objT).add(((Scrollable)widget).getVerticalBar()); if(((Scrollable)widget).getHorizontalBar()!=null) ((LinkedList)objT).add(((Scrollable)widget).getHorizontalBar()); } if(widget instanceof Decorations){ if(((Decorations)widget).getMenuBar()!=null) ((LinkedList)objT).add(((Decorations)widget).getMenuBar()); } if(widget instanceof Composite){ Widget[] widgets = ((Composite)widget).getChildren(); if(widgets.length!=0) ((LinkedList)objT).addAll(Arrays.asList(widgets)); } if(widget instanceof CoolBar){ Widget[] widgets = ((CoolBar)widget).getItems(); if(widgets.length!=0) ((LinkedList)objT).addAll(Arrays.asList(widgets)); } if(widget instanceof TabFolder){ Widget[] widgets = ((TabFolder)widget).getItems(); if(widgets.length!=0) ((LinkedList)objT).addAll(Arrays.asList(widgets)); } if(widget instanceof Table){ Widget[] widgets = ((Table)widget).getItems(); if(widgets.length!=0) ((LinkedList)objT).addAll(Arrays.asList(widgets)); widgets = ((Table)widget).getColumns(); if(widgets.length!=0) ((LinkedList)objT).addAll(Arrays.asList(widgets)); } if(widget instanceof ToolBar){ Widget[] widgets = ((ToolBar)widget).getItems(); if(widgets.length!=0) ((LinkedList)objT).addAll(Arrays.asList(widgets)); } if(widget instanceof Tree){ Widget[] widgets = ((Tree)widget).getItems(); if(widgets.length!=0) ((LinkedList)objT).addAll(Arrays.asList(widgets)); } // BEGIN added hmceuen 20040526 if(widget instanceof TreeItem){ Widget[] widgets = ((TreeItem)widget).getItems(); if(widgets.length!=0) ((LinkedList)objT).addAll(Arrays.asList(widgets)); } // END added hmceuen 20040526 if(widget instanceof Menu){ Widget[] widgets = ((Menu)widget).getItems(); if(widgets.length!=0) ((LinkedList)objT).addAll(Arrays.asList(widgets)); } if(widget instanceof MenuItem){ Widget childMenu = ((MenuItem)widget).getMenu(); if(childMenu!=null) ((LinkedList)objT).add(childMenu); } } }); LinkedList children = (LinkedList)objT; if(recurse&&children.size()>0){ LinkedList extendedFamily = new LinkedList(); Widget w; for(int i=0; i<children.size();i++){ w = (Widget)children.get(i); extendedFamily.addAll(Arrays.asList(getWidgetChildren(w,recurse))); } children.addAll(extendedFamily); } return (Widget[])(children.toArray(new Widget[children.size()])); } /** * Return the title of the nearest ancestor Decorations with a title. * If no title is found, return null. */ public synchronized String getWidgetDecorationsTitle(Widget widget) { Decorations dec = getWidgetDecorations(widget); objT = dec; stringT = null; if(dec==null) return null; Robot.syncExec(dec.getDisplay(),this,new Runnable(){ public void run(){ if( (((Decorations)objT).getStyle()&SWT.TITLE)==SWT.TITLE && !((Decorations)objT).getText().equals("")) stringT = ((Decorations)objT).getText(); } }); if(stringT!=null) return stringT; Widget parent = getWidgetParent(dec); if(parent!=null) return getWidgetDecorationsTitle(parent); return null; } /** Return the component's owning shell. There will * <b>always</b> one of these. */ public Shell getWidgetShell(Widget widget) { Widget parent = widget; while (!(parent instanceof Shell) && (widget = getWidgetParent(parent)) != null) { parent = widget; } return (Shell)widget; } /** Return the nearest Decorations ancestor of the given Widget. */ public Decorations getWidgetDecorations(Widget widget) { Widget parent = widget; while (!(parent instanceof Decorations) && (widget = getWidgetParent(parent)) != null) { parent = widget; } return (Decorations)widget; } /** Dispose of all available windows, and does not return until they have been disposed of. */ public void disposeWindows() { Log.debug("Disposing all windows"); Decorations[] windows = getDecorations(); for (int i=0;i < windows.length;i++) { final Decorations win = windows[i]; discardWidget(win); System.setProperty("abbot.finder.disposal", "true"); try { Log.debug("Disposing of " + win); win.dispose(); } catch(NullPointerException npe) { Log.log(npe); } System.setProperty("abbot.finder.disposal", "false"); } } /** Discard and no longer reference the given component. */ public void discardWidget(Widget comp) { Log.debug("Discarding " + comp); filterWidget(comp); if (parent != null) { parent.discardWidget(comp); } } public void filterWidget(Widget comp) { Log.debug("Now filtering " + comp); filteredWidgets.put(comp, comp); } public synchronized boolean isDecorationsShowing(final Decorations w) { Robot.syncExec(w.getDisplay(),this,new Runnable(){ public void run(){ boolT = w.isVisible(); } }); return boolT; } /** Return the component's name, ensuring that null is returned if the * name appears to be auto-generated. */ public synchronized String getWidgetName(final Widget widget) { Robot.syncExec(widget.getDisplay(),this,new Runnable(){ public void run(){ stringT = (String)widget.getData("name"); } }); return stringT; // synchronized(widget.getDisplay()){ // widget.getDisplay().syncExec(new Runnable(){ // public void run(){ // stringT = (String)widget.getData("name"); // } // }); // return stringT; // } } /** * Stringify the widget. */ public synchronized String widgetToString(final Widget widget) { Robot.syncExec(widget.getDisplay(),this,new Runnable(){ public void run(){ stringT = (String)(widget.toString()); } }); return stringT; } /** * Return an array of all available root Shells */ // WARNING: THERE MAY BE SOME UNRESOLVED SYNCHRONIZATION ISSUES HERE... static Shell[] shells0; public static Display[] displays; static int i; static boolean flag; static Shell[] shells; public synchronized Shell[] getRootShells(){ List allShells = getAllShells(); final Shell[] shells = (Shell[])(allShells.toArray(new Shell[allShells.size()])); ArrayList rootShells = new ArrayList(); //System.out.println("Length(start)="+shells.length); for(i=0; i<shells.length;i++){ //System.out.println("\tShell("+i+")"+shells[i].getText()); flag = false; if(shells[i].getDisplay().getThread()==Thread.currentThread()) flag = (shells[i].getParent()==null); else{ Robot.syncExec(shells[i].getDisplay(),null,new Runnable(){ public void run(){ if(shells[i].getParent()==null) flag = true; } }); } if(flag) rootShells.add(shells[i]); } //System.out.println("end"); return (Shell[])(rootShells.toArray(new Shell[rootShells.size()])); } /** * get all shells for all displays */ public static synchronized List getAllShells(){ ArrayList allShells = new ArrayList(); displays = DecorationsTracker.getDisplays(); for(i=0; i<displays.length;i++){ if(!displays[i].isDisposed() &&displays[i]!=null){ if(displays[i].getThread()==Thread.currentThread()) shells0 = displays[i].getShells(); else{ try{ Thread.sleep(0,10); }catch(Exception e){ } Robot.syncExec(displays[i],null,new Runnable(){ public void run(){ shells0 = displays[i].getShells(); //System.out.println("Executed inside 1st sync block"); } }); } } if(shells0!=null) allShells.addAll(Arrays.asList(shells0)); } return allShells; } // /** Return all decorations owned by a given decorations object. */ // private List getDecorationsList(Decorations parent) { // return getDecorationsList(parent, filter, false); // } /** Return all windows owned by the given window, optionally filtered. */ private List getDecorationsList(Decorations parent, boolean filter, boolean recurse){ Widget[] children = getWidgetChildren(parent,recurse); ArrayList list = new ArrayList(); for (int i=0;i < children.length;i++) { if(children[i] instanceof Decorations){ if (!filter || !isWidgetFiltered(children[i])) { list.add(children[i]); if (recurse) { list.addAll(getDecorationsList((Decorations)children[i], filter, recurse)); } } } } return list; } /** Return all windows owned by the given window that have not been * filtered. */ public Decorations[] getDecorations(Decorations parent) { List list = getDecorationsList(parent, filter, false); return (Decorations[])list.toArray(new Decorations[list.size()]); } private List getDecorationsList() { Shell[] rootShells = getRootShells(); ArrayList list = new ArrayList(); for(int i=0; i<rootShells.length;i++){ if(!filter || !isWidgetFiltered(rootShells[i])){ list.add(rootShells[i]); list.addAll(getDecorationsList(rootShells[i],filter,true)); } } return list; } /** Returns the set of all available decorations that have not been * filtered. This includes shells and decorations. */ public Decorations[] getDecorations() { List list = getDecorationsList(); return (Decorations[])list.toArray(new Decorations[list.size()]); } /** Returns all components below the GUI hierarchy of the given Control, * including Windows and MenuElements. */ public Widget[] getWidgets(Widget widget){ return getWidgetChildren(widget,true); } //String widgetText = null; public String getWidgetText(final Widget widget){ String res = null; Method getText=null; WidgetTester tester=null; Class[] paramTypes = {widget.getClass()}; Object[] params = {widget}; boolean foundMethod = false; Class widgetClass = widget.getClass(); while(!foundMethod && (tester==null||tester.getClass()!=WidgetTester.class)){ try{ //getText = widget.getClass().getMethod("getText",null); tester = WidgetTester.getTester(widgetClass); getText =tester.getClass().getMethod("getText",paramTypes); foundMethod = true; } catch(NoSuchMethodException nsme){ widgetClass = widgetClass.getSuperclass(); paramTypes[0]=widgetClass; } } if(getText==null) return null; try{ res = (String)getText.invoke(tester,params); } catch(Exception e){ e.printStackTrace(); return null; } return res; } /** Copy/mod of Tom MacDougall: get widgets of the given class */ public List getMatchesForClass(Class widgetClass) { List ret = null; WidgetReference ref = new WidgetReference(null, widgetClass, null, null, null, null); HashSet comps = findMatches(null, ref, MATCH_WEAK); if (comps == null) { return null; } else if (comps.isEmpty()) { // TODO_Tom: use tracing System.err.println("Widget '" + ref + "' not found (" + widgetClass.getName() +") "); return Collections.EMPTY_LIST; } else { ret = new ArrayList(comps.size()); for (Iterator iterator = comps.iterator(); iterator.hasNext();) { ret.add((Widget)(iterator.next())); } } return ret; } /** Copy/mod of Tom MacDougall: print widgets of the given class */ public String printMatchesForClass(Class widgetClass) { List matches = getMatchesForClass(widgetClass); StringBuffer sb = new StringBuffer(); if ((matches == null) || (matches.isEmpty())) { sb.append("Widget not found for class \"").append(widgetClass.getName()). append("\"\n"); } else { sb.append("Found:\n"); for (Iterator iterator = matches.iterator(); iterator.hasNext();) { Widget element = (Widget)(iterator.next()); sb.append("\t").append(element.toString()).append("\n"); } } return sb.toString(); } /** * @param text either on or labelling the <code>Button</code> * @param title of the frame containing the <code>Button</code> * @param shell containing the <code>Button</code> */ public static Button findButtonByTextTitleShell( String text, String title, Shell shell) throws WidgetNotFoundException, MultipleWidgetsFoundException { Button ret = null; ButtonReference ref = new ButtonReference(null, null, null, title, text); if (ref != null) return DefaultWidgetFinder.findButtonInShell(ref, shell); return ret; } }