/******************************************************************************* * 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.internal.finder.matchers; import org.eclipse.swt.widgets.Widget; import com.windowtester.runtime.locator.IWidgetMatcher; import com.windowtester.runtime.swt.internal.finder.WidgetLocatorService; /** * * A matcher that matches widgets first against a target matcher (which might match * a given class and possibly name or label) and then by checking that parent criteria are met. * <p> * HierarchyMatchers are handy in making matches based on a widget's location in the * widget hierarchy. * * For instance, to match a Text widget contained in the Group labeled "guests" * in a Shell called "Party Planner", we might write a HierarchyMatcher like this:<p> * * <pre> * new HierarchyMatcher(Text.class, * new HierarchyMatcher(Group.class, "guests", * new NameMatcher("Party Planner", Shell.class))); * * </pre> * * Where containment is not enough, we can augment with indexes. For example, * suppose we want the second Text widget (indexes are zero-indexed): * * <pre> * - group: ------------------- * | Text Text Text | * ---------------------------- * * new HierarchyMatcher(Text.class, 1, * new HierarchyMatcher(Group.class, "group")); * </pre> * * <b>Note:</b> using the 0-index for an "only child" will not have the desired effect. In fact, it will fail. * Only-child matches should NOT specify an index value (though they could use -1). * <p> * In other words, <code>new HierarchyMatcher(Text.class, 0, new HierarchyMatcher(Group.class))</code>, will match the first Text * child of a Group but only in the event that that Text has siblings. To match a Text only-child of a group, * use a matcher constructs like this <code>new HierarchyMatcher(Text.class, Group.class)</code> (or possibly * like this <code>new HierarchyMatcher(Text.class, -1, Group.class)</code>). * */ public class SWTHierarchyMatcher implements IWidgetMatcher { /** A matcher composed from target class and name info */ private final IWidgetMatcher _matcher; /** A matcher to check parent criteria. */ private final IWidgetMatcher _parentMatcher; /** The index that identifies the target with respect to its siblings in * the parent's list of children. */ private int _index; /** The default index value for an unspecified index */ private static final int DEFAULT_INDEX = -1; /////////////////////////////////////////////////////////////////////////////////////////////////// // // Constructors // /////////////////////////////////////////////////////////////////////////////////////////////////// /** * Create an instance. * @param targetMatcher - a matcher to check target criteria. * @param parentMatcher - a matcher to check parent criteria. */ public SWTHierarchyMatcher(IWidgetMatcher targetMatcher, IWidgetMatcher parentMatcher) { this(targetMatcher, DEFAULT_INDEX, parentMatcher); } /** * Create an instance. * @param targetMatcher - a matcher to check target criteria. * @param index - index that locates child with respect to its parent (in the parent's * list of children). * @param parentMatcher - a matcher to check parent criteria. */ public SWTHierarchyMatcher(IWidgetMatcher targetMatcher, int index, IWidgetMatcher parentMatcher) { _matcher = targetMatcher; _index = index; _parentMatcher = parentMatcher; } /////////////////////////////////////////////////////////////////////////////////////////////////// // // Matching // /////////////////////////////////////////////////////////////////////////////////////////////////// /* (non-Javadoc) * @see com.windowtester.runtime.locator.IWidgetMatcher#matches(java.lang.Object) */ public boolean matches(Object widget) { if (!(widget instanceof Widget)) return false; return matches((Widget)widget); } /** * Check for a match by first checking if the class (and possibly name or label) * match and then checking that parent criteria are met. * @see com.windowtester.runtime.swt.widgets.IWidgetMatcher#matches(org.eclipse.swt.widgets.Widget) */ public boolean matches(Widget widget) { //is this log-worthy? if (widget == null) return false; /* * various fast-fail optimizations */ //check target matcher if (!_matcher.matches(widget)) return false; //fast fail //if target matches, turn to parent //first, short-circuit if there is no parent matcher if (_parentMatcher == null) return true; //next, check parent matcher WidgetLocatorService infoService = new WidgetLocatorService(); Widget parent = infoService.getParent(widget); if (parent == null) return false; //if there is no parent, but there is a matcher, return false if (!_parentMatcher.matches(parent)) return false; //fail if parent does not match //lastly, check index return testIndex(widget, infoService, parent); } private boolean testIndex(Widget widget, WidgetLocatorService infoService, Widget parent) { //NOTE: some matchers override the infoService indexer... if (_parentMatcher instanceof IComponentIndexer) { //in case no index is assigned, any index match will do //this handles the case where a user wants ALL children of a widget //TODO: this logic might apply to the general case as well... if (_index == DEFAULT_INDEX) return true; return ((IComponentIndexer)_parentMatcher).getIndex(widget, _matcher) == _index; } // check if there is an index , only then do a match if (_index == DEFAULT_INDEX) return true; return infoService.getIndex(widget, parent) == _index; } /////////////////////////////////////////////////////////////////////////////////////////////////// // // Accessors // /////////////////////////////////////////////////////////////////////////////////////////////////// /** * Get the matcher identifying the target of this match. */ public IWidgetMatcher getTargetMatcher() { return _matcher; } /** * Get the matcher identifying the parent of the target of this match. */ public IWidgetMatcher getParentMatcher() { return _parentMatcher; } /////////////////////////////////////////////////////////////////////////////////////////////////// // // Debugging // /////////////////////////////////////////////////////////////////////////////////////////////////// /** * @see java.lang.Object#toString() */ public String toString() { return "Hierarchy matcher (" + _matcher + ", " + _parentMatcher + ")"; } }