/*******************************************************************************
* 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.drivers;
import com.windowtester.internal.runtime.util.StringUtils;
import com.windowtester.runtime.MultipleWidgetsFoundException;
import com.windowtester.runtime.WidgetNotFoundException;
import com.windowtester.runtime.internal.concurrent.VoidCallable;
import com.windowtester.runtime.swt.internal.widgets.TreeItemReference;
import com.windowtester.runtime.swt.internal.widgets.TreeItemReferenceContainer;
import com.windowtester.runtime.swt.internal.widgets.TreeReference;
import com.windowtester.runtime.util.ScreenCapture;
import com.windowtester.runtime.util.StringComparator;
/**
* Given a path, this class drives tree selection by interacting with tree and tree item
* references. This class collects state and thus should be re-used.
*/
public class TreeDriver
{
/**
* Used internally by
* {@link #getNextTreeItem(TreeItemReferenceContainer, int, String)}
*/
private TreeItemReference[] items;
/**
* Used internally by
* {@link #getNextTreeItem(TreeItemReferenceContainer, int, String)}
*/
private String[][] textForItems;
/**
* Expand the items in the tree to show the specified tree item
*
* @param tree the tree containing the tree item
* @param itemPath the slash separated path to the tree item
* @return the tree item revealed
*/
public TreeItemReference reveal(TreeReference tree, String itemPath) throws WidgetNotFoundException,
MultipleWidgetsFoundException
{
// TreeItemReference[] items = tree.getItemsWithText();
// PathString path = new PathString(itemPath);
// for (String nodeText : path) {
// for (TreeItemReference item : items){
// if (item.isMatchedBy(new ByTextMatcher(nodeText))){
//// System.out.println("item: " + item + " matched by: " + nodeText);
// if (!path.hasNext())
// return item;
//// show(tree, item);
// item.expand();
//// //debugging
//// try {
//// Thread.sleep(2000);
//// } catch(Exception e){ }
// //need a wait here -- perhaps just until items appear?
// items = item.getItemsWithText();
// break;
// }
// }
// }
//
// //mirrors call in com.windowtester.runtime.swt.internal.selector.TreeItemSelector2.click(int, Tree, String, int, Point, int)
// ScreenCapture.createScreenCapture();
// // TODO[pq]: improve error message
// throw new WidgetNotFoundException("Path: \'"+ itemPath + "\' not found in " + tree);
PathString path = new PathString(itemPath);
TreeItemReferenceContainer container = tree;
int columnCount = Math.max(tree.getColumnCount(), 1);
while (true) {
String nodeText = path.next();
TreeItemReference itemRef;
long startTime = System.currentTimeMillis();
while (true) {
try {
itemRef = getNextTreeItem(container, columnCount, nodeText);
break;
}
catch (WidgetNotFoundException e) {
// Wait for up to 5 seconds for a dynamic tree to populate its elements
// TODO make this configurable?
if (System.currentTimeMillis() - startTime < 5000) {
System.out.println("No tree items found for \'" + nodeText + "\' - wait then try again");
try {
Thread.sleep(100);
}
catch (InterruptedException ignored) {
// Ignored
}
continue;
}
ScreenCapture.createScreenCapture();
throw e;
}
catch (MultipleWidgetsFoundException e) {
ScreenCapture.createScreenCapture();
throw e;
}
}
if (!path.hasNext())
return itemRef;
container = itemRef;
}
}
/**
* Execute the specified operation then find a tree item with the matching node text.
*
* @param container the reference to the container of the tree items to be searched
* @param columnCount the number of columns in the tree (1 or greater)
* @param nodeText the text to be matched
* @return the matching tree item (not <code>null</code>)
*/
private TreeItemReference getNextTreeItem(final TreeItemReferenceContainer container, final int columnCount, String nodeText)
throws WidgetNotFoundException, MultipleWidgetsFoundException
{
container.expand();
container.getDisplayRef().execute(new VoidCallable() {
public void call() throws Exception {
items = container.getItems();
textForItems = new String[items.length][columnCount];
for (int row = 0; row < items.length; row++) {
TreeItemReference item = items[row];
String[] rowText = textForItems[row];
for (int column = 0; column < columnCount; column++)
rowText[column] = item.getText(column);
}
}
});
// Look for an exact match
TreeItemReference found = null;
for (int row = 0; row < textForItems.length; row++) {
String[] rowTexts = textForItems[row];
for (String text : rowTexts) {
if (nodeText.equals(text)) {
if (found == null) {
found = items[row];
}
else
throw new MultipleWidgetsFoundException("Multiple tree items found for \'" + nodeText
+ "\' in " + getAllItemText(textForItems));
}
}
}
if (found != null)
return found;
// Look for a pattern match
for (int row = 0; row < textForItems.length; row++) {
String[] rowTexts = textForItems[row];
for (String text : rowTexts) {
if (StringComparator.matches(StringUtils.trimMenuText(text), nodeText)) {
if (found == null)
found = items[row];
else
throw new MultipleWidgetsFoundException("Multiple tree items found for \'" + nodeText
+ "\' in " + getAllItemText(textForItems));
}
}
}
if (found != null)
return found;
throw new WidgetNotFoundException("No tree items found for \'" + nodeText + "\' in "
+ getAllItemText(textForItems));
}
/**
* Answer all text used in the text comparison as a single string.
*
* @param op the operation containing the matching text
* @return a string for inclusion in a {@link WidgetNotFoundException} or
* {@link MultipleWidgetsFoundException} message
*/
private StringBuilder getAllItemText(String[][] itemTexts) {
StringBuilder allItemText = new StringBuilder(128);
for (String[] rowTexts : itemTexts) {
String separator = "\n";
for (String text : rowTexts) {
allItemText.append(separator);
allItemText.append(text);
separator = " | ";
}
}
return allItemText;
}
}