/** * Used by the {@link #translateCompoundSelector(UiSelector, AccessibilityNodeInfo, boolean)} to * translate the pattern_selector portion. It has the following format: * * <p>pattern_selector = ... PATTERN=By[instance=x PATTERN=[regular_selector]]<br> * * <p>pattern_selectors requires search to be performed as regular_selector but where * regular_selector search returns immediately upon a successful match, the search for * pattern_selector continues until the requested matched instance of that pattern is encountered. * * <p>Counting UI objects requires using pattern_selectors. The counting search is the same as a * pattern_search however we're not looking to match an instance of the pattern but rather * continuously walking the accessibility node hierarchy while counting patterns until the end of * the tree. * * @param subSelector * @param fromNode * @param isCounting * @return null of node is not found or if counting mode is true. See {@link * #translateCompoundSelector(UiSelector, AccessibilityNodeInfo, boolean)} */ private AccessibilityNodeInfo translatePatternSelector( UiSelector subSelector, AccessibilityNodeInfo fromNode, boolean isCounting) { if (subSelector.hasPatternSelector()) { // Since pattern_selectors are also the type of selectors used when counting, // we check if this is a counting run or an indexing run if (isCounting) // since we're counting, we reset the indexer so to terminates the search when // the end of tree is reached. The count will be in mPatternCount mPatternIndexer = -1; else // terminates the search once we match the pattern's instance mPatternIndexer = subSelector.getInstance(); // A pattern is wrapped in a PATTERN[instance=x PATTERN[the_pattern]] subSelector = subSelector.getPatternSelector(); if (subSelector == null) { Log.e(LOG_TAG, "Pattern portion of the selector is null or not defined"); return null; // there is an implementation fault } // save the current indent level as parent indent before pattern searches // begin under the current tree position. mLogParentIndent = ++mLogIndent; return findNodePatternRecursive(subSelector, fromNode, 0, subSelector); } Log.e(LOG_TAG, "Selector must have a pattern selector defined"); // implementation fault? return null; }
/** * A compoundSelector encapsulate both Regular and Pattern selectors. The formats follows: * * <p>regular_selector = By[attributes... CHILD=By[attributes... CHILD=By[....]]] <br> * pattern_selector = ...CONTAINER=By[..] PATTERN=By[instance=x PATTERN=[regular_selector] <br> * compound_selector = [regular_selector [pattern_selector]] * * <p>regular_selectors are the most common form of selectors and the search for them is * straightforward. On the other hand pattern_selectors requires search to be performed as in * regular_selector but where regular_selector search returns immediately upon a successful match, * the search for pattern_selector continues until the requested matched _instance_ of that * pattern is matched. * * <p>Counting UI objects requires using pattern_selectors. The counting search is the same as a * pattern_search however we're not looking to match an instance of the pattern but rather * continuously walking the accessibility node hierarchy while counting matched patterns, until * the end of the tree. * * <p>If both present, order of parsing begins with CONTAINER followed by PATTERN then the top * most selector is processed as regular_selector within the context of the previous CONTAINER and * its PATTERN information. If neither is present then the top selector is directly treated as * regular_selector. So the presence of a CONTAINER and PATTERN within a selector simply dictates * that the selector matching will be constraint to the sub tree node where the CONTAINER and its * child PATTERN have identified. * * @param selector * @param fromNode * @param isCounting * @return AccessibilityNodeInfo */ private AccessibilityNodeInfo translateCompoundSelector( UiSelector selector, AccessibilityNodeInfo fromNode, boolean isCounting) { // Start translating compound selectors by translating the regular_selector first // The regular_selector is then used as a container for any optional pattern_selectors // that may or may not be specified. if (selector.hasContainerSelector()) // nested pattern selectors if (selector.getContainerSelector().hasContainerSelector()) { fromNode = translateCompoundSelector(selector.getContainerSelector(), fromNode, false); initializeNewSearch(); } else fromNode = translateReqularSelector(selector.getContainerSelector(), fromNode); else fromNode = translateReqularSelector(selector, fromNode); if (fromNode == null) { if (DEBUG) Log.d(LOG_TAG, "Container selector not found: " + selector.dumpToString(false)); return null; } if (selector.hasPatternSelector()) { fromNode = translatePatternSelector(selector.getPatternSelector(), fromNode, isCounting); if (isCounting) { Log.i(LOG_TAG, String.format("Counted %d instances of: %s", mPatternCounter, selector)); return null; } else { if (fromNode == null) { if (DEBUG) Log.d(LOG_TAG, "Pattern selector not found: " + selector.dumpToString(false)); return null; } } } // translate any additions to the selector that may have been added by tests // with getChild(By selector) after a container and pattern selectors if (selector.hasContainerSelector() || selector.hasPatternSelector()) { if (selector.hasChildSelector() || selector.hasParentSelector()) fromNode = translateReqularSelector(selector, fromNode); } if (fromNode == null) { if (DEBUG) Log.d(LOG_TAG, "Object Not Found for selector " + selector); return null; } Log.i(LOG_TAG, String.format("Matched selector: %s <<==>> [%s]", selector, fromNode)); return fromNode; }