Coverage Report - org.eclipse.swtbot.swt.finder.finders.ControlFinder
 
Classes in this File Line Coverage Branch Coverage Complexity
ControlFinder
91%
43/47
83%
20/24
2.045
ControlFinder$1
100%
3/3
N/A
2.045
ControlFinder$2
100%
8/8
100%
4/4
2.045
ControlFinder$3
100%
3/3
N/A
2.045
ControlFinder$4
100%
3/3
N/A
2.045
ControlFinder$5
85%
6/7
75%
3/4
2.045
ControlFinder$6
100%
9/9
100%
2/2
2.045
 
 1  2
 /*******************************************************************************
 2  
  * Copyright (c) 2008 Ketan Padegaonkar and others.
 3  
  * All rights reserved. This program and the accompanying materials
 4  
  * are made available under the terms of the Eclipse Public License v1.0
 5  
  * which accompanies this distribution, and is available at
 6  
  * http://www.eclipse.org/legal/epl-v10.html
 7  
  *
 8  
  * Contributors:
 9  
  *     Ketan Padegaonkar - initial API and implementation
 10  
  *******************************************************************************/
 11  
 package org.eclipse.swtbot.swt.finder.finders;
 12  
 
 13  
 import java.util.ArrayList;
 14  
 import java.util.Collections;
 15  
 import java.util.LinkedHashSet;
 16  
 import java.util.LinkedList;
 17  
 import java.util.List;
 18  
 
 19  
 import org.apache.log4j.Logger;
 20  
 import org.eclipse.swt.widgets.Composite;
 21  
 import org.eclipse.swt.widgets.Control;
 22  
 import org.eclipse.swt.widgets.Display;
 23  
 import org.eclipse.swt.widgets.Shell;
 24  
 import org.eclipse.swt.widgets.Widget;
 25  
 import org.eclipse.swtbot.swt.finder.resolvers.DefaultChildrenResolver;
 26  
 import org.eclipse.swtbot.swt.finder.resolvers.DefaultParentResolver;
 27  
 import org.eclipse.swtbot.swt.finder.resolvers.IChildrenResolver;
 28  
 import org.eclipse.swtbot.swt.finder.resolvers.IParentResolver;
 29  
 import org.eclipse.swtbot.swt.finder.results.ArrayResult;
 30  
 import org.eclipse.swtbot.swt.finder.results.ListResult;
 31  
 import org.eclipse.swtbot.swt.finder.results.WidgetResult;
 32  
 import org.eclipse.swtbot.swt.finder.utils.MessageFormat;
 33  
 import org.eclipse.swtbot.swt.finder.utils.SWTUtils;
 34  
 import org.eclipse.swtbot.swt.finder.utils.TreePath;
 35  
 import org.hamcrest.Matcher;
 36  
 
 37  
 /**
 38  
  * Finds controls matching a particular matcher.
 39  
  *
 40  
  * @see UIThreadRunnable
 41  
  * @author Ketan Padegaonkar <KetanPadegaonkar [at] gmail [dot] com>
 42  
  * @version $Id$
 43  
  */
 44  1
 public class ControlFinder {
 45  
 
 46  
         /**
 47  
          * The logging instance for this class.
 48  
          */
 49  1
         private static final Logger                        log                                                        = Logger.getLogger(ControlFinder.class);
 50  
 
 51  
         /** The childrenResolver */
 52  
         protected final IChildrenResolver        childrenResolver;
 53  
 
 54  
         /** The display */
 55  
         protected Display                                        display;
 56  
 
 57  
         /** The parentResolver */
 58  
         protected final IParentResolver                parentResolver;
 59  
 
 60  
         /**
 61  
          * Set to true if the control finder should find invisible controls. Invisible controls are ones hidden from the
 62  
          * display (isVisible() = false)
 63  
          *
 64  
          * @since 1.0
 65  
          */
 66  4026
         public boolean                                                shouldFindInVisibleControls        = false;
 67  
 
 68  
         /**
 69  
          * Creates a Control finder using {@link DefaultChildrenResolver} and {@link DefaultParentResolver}.
 70  
          */
 71  
         public ControlFinder() {
 72  4024
                 this(new DefaultChildrenResolver(), new DefaultParentResolver());
 73  4024
         }
 74  
 
 75  
         /**
 76  
          * Creates a control finder using the given resolvers.
 77  
          *
 78  
          * @param childrenResolver the resolver used to resolve children of a control.
 79  
          * @param parentResolver the resolver used to resolve parent of a control.
 80  
          */
 81  4026
         public ControlFinder(IChildrenResolver childrenResolver, IParentResolver parentResolver) {
 82  4026
                 display = SWTUtils.display();
 83  4026
                 this.childrenResolver = childrenResolver;
 84  4026
                 this.parentResolver = parentResolver;
 85  4026
         }
 86  
 
 87  
         /**
 88  
          * Finds the controls in the active shell matching the given matcher.
 89  
          * <p>
 90  
          * Note: This method is thread safe.
 91  
          * </p>
 92  
          *
 93  
          * @param matcher the matcher used to find controls in the active shell.
 94  
          * @return all controls in the active shell that the matcher matches.
 95  
          * @see Display#getActiveShell()
 96  
          */
 97  
         public <T extends Widget> List<T> findControls(Matcher<T> matcher) {
 98  2118
                 return findControls(activeShell(), matcher, true);
 99  
         }
 100  
 
 101  
         /**
 102  
          * Finds the controls matching one of the widgets using the given matcher. This will also go recursively though the
 103  
          * {@code widgets} provided.
 104  
          *
 105  
          * @param widgets the list of widgets.
 106  
          * @param matcher the matcher used to match the widgets.
 107  
          * @param recursive if the match should be recursive.
 108  
          * @return all visible widgets in the children that the matcher matches. If recursive is <code>true</code> then find
 109  
          *         the widgets within each of the widget.
 110  
          */
 111  
         public <T extends Widget> List<T> findControls(final List<Widget> widgets, final Matcher<T> matcher, final boolean recursive) {
 112  0
                 return findControlsInternal(widgets, matcher, recursive);
 113  
         }
 114  
 
 115  
         /**
 116  
          * Returns true if the widget is a control and it is visible.
 117  
          * <p>
 118  
          * This method is not thread safe and must be invoked from the UI thread.
 119  
          * </p>
 120  
          * <p>
 121  
          * TODO visibility of tab items.
 122  
          * </p>
 123  
          *
 124  
          * @param w the widget
 125  
          * @return <code>true</code> if the control is visible, <code>false</code> otherwise.
 126  
          * @see Control#getVisible()
 127  
          * @since 1.0
 128  
          */
 129  
         protected boolean visible(Widget w) {
 130  177070
                 if (shouldFindInVisibleControls)
 131  0
                         return true;
 132  177070
                 return !((w instanceof Control) && !((Control) w).getVisible());
 133  
         }
 134  
 
 135  
         /**
 136  
          * Finds the controls starting with the given parent widget and uses the given matcher. If recursive is set, it will
 137  
          * attempt to find the controls recursively in each child widget if they exist.
 138  
          * <p>
 139  
          * This method is thread safe.
 140  
          * </p>
 141  
          *
 142  
          * @param parentWidget the parent widget in which controls should be found.
 143  
          * @param matcher the matcher used to match the widgets.
 144  
          * @param recursive if the match should be recursive.
 145  
          * @return all visible widgets in the parentWidget that the matcher matches. If recursive is <code>true</code> then
 146  
          *         find the widget within each of the parentWidget.
 147  
          */
 148  
         public <T extends Widget> List<T> findControls(final Widget parentWidget, final Matcher<T> matcher, final boolean recursive) {
 149  2144
                 return UIThreadRunnable.syncExec(display, new ListResult<T>() {
 150  
                         public List<T> run() {
 151  2144
                                 return findControlsInternal(parentWidget, matcher, recursive);
 152  
                         }
 153  
                 });
 154  
         }
 155  
 
 156  
         /**
 157  
          * This finds controls using the list of widgets and the matcher. If recursive is set, it will attempt to find the
 158  
          * controls recursively in each child widget if they exist.
 159  
          * <p>
 160  
          * This method is not thread safe and must be invoked from the UI thread.
 161  
          * </p>
 162  
          *
 163  
          * @see #findControls(List, Matcher, boolean)
 164  
          */
 165  
         private <T extends Widget> List<T> findControlsInternal(final List<Widget> widgets, final Matcher<T> matcher, final boolean recursive) {
 166  145483
                 LinkedHashSet<T> list = new LinkedHashSet<T>();
 167  465893
                 for (Widget w : widgets) {
 168  174927
                         list.addAll(findControlsInternal(w, matcher, recursive));
 169  
                 }
 170  145483
                 return new ArrayList<T>(list);
 171  
         }
 172  
 
 173  
         /**
 174  
          * Find controls starting from the parent widget using the given matcher. If recursive is set, it will attempt to
 175  
          * find the controls recursively in each child widget if they exist.
 176  
          * <p>
 177  
          * This method is not thread safe and must be invoked from the UI thread.
 178  
          * </p>
 179  
          *
 180  
          * @see #findControlsInternal(Widget, Matcher, boolean)
 181  
          * @throws IllegalArgumentException if the matcher matches an object that is the wrong declared type. For example, a Matcher&lt;Table&gt; that would match a Tree
 182  
          */
 183  
         @SuppressWarnings("unchecked")
 184  2144
         private <T extends Widget> List<T> findControlsInternal(final Widget parentWidget, final Matcher<T> matcher, final boolean recursive) {
 185  177071
                 if ((parentWidget == null) || parentWidget.isDisposed())
 186  1
                         return new ArrayList<T>();
 187  177070
                 if (!visible(parentWidget)) {
 188  31587
                         if (!isComposite(parentWidget))
 189  145
                                 log.trace(MessageFormat.format("{0} is not visible, skipping.", parentWidget)); //$NON-NLS-1$
 190  31587
                         return new ArrayList<T>();
 191  
                 }
 192  145483
                 LinkedHashSet<T> controls = new LinkedHashSet<T>();
 193  145483
                 if (matcher.matches(parentWidget) && !controls.contains(parentWidget))
 194  
                         try {
 195  19200
                                 controls.add((T) parentWidget);
 196  0
                         } catch (ClassCastException exception) {
 197  0
                                 throw new IllegalArgumentException("The specified matcher should only match against is declared type.", exception);
 198  
                         }
 199  145483
                 if (recursive) {
 200  145483
                         List<Widget> children = getChildrenResolver().getChildren(parentWidget);
 201  145483
                         controls.addAll(findControlsInternal(children, matcher, recursive));
 202  
                 }
 203  145483
                 return new ArrayList<T>(controls);
 204  
         }
 205  
 
 206  
         private boolean isComposite(Widget parentWidget) {
 207  31587
                 return parentWidget.getClass().equals(Composite.class);
 208  
         }
 209  
 
 210  
         /**
 211  
          * Finds the shell matching the given text (shell.getText()).
 212  
          *
 213  
          * @param text The text on the Shell
 214  
          * @return A Shell containing the specified text
 215  
          */
 216  
         public List<Shell> findShells(final String text) {
 217  1
                 return UIThreadRunnable.syncExec(new ListResult<Shell>() {
 218  
                         public List<Shell> run() {
 219  1
                                 ArrayList<Shell> list = new ArrayList<Shell>();
 220  1
                                 Shell[] shells = getShells();
 221  9
                                 for (Shell shell : shells) {
 222  8
                                         if (shell.getText().equals(text))
 223  1
                                                 list.add(shell);
 224  
                                 }
 225  1
                                 return list;
 226  
                         }
 227  
                 });
 228  
         }
 229  
 
 230  
         /**
 231  
          * Gets the registered children resolver. If the resolver had never been set a default resolver will be created.
 232  
          *
 233  
          * @return the childrenResolver
 234  
          */
 235  
         public IChildrenResolver getChildrenResolver() {
 236  145483
                 return childrenResolver;
 237  
         }
 238  
 
 239  
         /**
 240  
          * Gets the registered parent resolver. If the resolver was not registered then a default instance will be returned.
 241  
          *
 242  
          * @return the parentResolver
 243  
          */
 244  
         public IParentResolver getParentResolver() {
 245  4366
                 return parentResolver;
 246  
         }
 247  
 
 248  
         /**
 249  
          * Gets the path to the widget. The path is the list of all parent containers of the widget.
 250  
          *
 251  
          * @param w the widget.
 252  
          * @return the path to the widget w.
 253  
          */
 254  
         public TreePath getPath(Widget w) {
 255  671
                 return new TreePath(getParents(w).toArray());
 256  
         }
 257  
 
 258  
         /**
 259  
          * Gets the shells registered with the display.
 260  
          *
 261  
          * @return the shells
 262  
          */
 263  
         public Shell[] getShells() {
 264  63
                 return UIThreadRunnable.syncExec(display, new ArrayResult<Shell>() {
 265  
                         public Shell[] run() {
 266  63
                                 return display.getShells();
 267  
                         }
 268  
                 });
 269  
         }
 270  
 
 271  
         /**
 272  
          * Return the active shell.
 273  
          *
 274  
          * @return the active shell.
 275  
          * @see Display#getActiveShell()
 276  
          */
 277  
         public Shell activeShell() {
 278  2132
                 Shell activeShell = UIThreadRunnable.syncExec(display, new WidgetResult<Shell>() {
 279  
                         public Shell run() {
 280  2132
                                 return display.getActiveShell();
 281  
                         }
 282  
                 });
 283  2132
                 if (activeShell != null)
 284  2131
                         return activeShell;
 285  1
                 return UIThreadRunnable.syncExec(display, new WidgetResult<Shell>() {
 286  
                         public Shell run() {
 287  1
                                 Shell[] shells = getShells();
 288  9
                                 for (Shell shell : shells)
 289  8
                                         if (shell.isFocusControl())
 290  0
                                                 return shell;
 291  1
                                 return null;
 292  
                         }
 293  
                 });
 294  
         }
 295  
 
 296  
         private List<Widget> getParents(final Widget w) {
 297  671
                 return UIThreadRunnable.syncExec(display, new ListResult<Widget>() {
 298  
                         public List<Widget> run() {
 299  671
                                 Widget parent = w;
 300  671
                                 List<Widget> parents = new LinkedList<Widget>();
 301  5708
                                 while (parent != null) {
 302  4366
                                         parents.add(parent);
 303  4366
                                         parent = getParentResolver().getParent(parent);
 304  
                                 }
 305  671
                                 Collections.reverse(parents);
 306  671
                                 return parents;
 307  
                         }
 308  
                 });
 309  
         }
 310  
 
 311  
 }