/******************************************************************************* * 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.locator; import java.awt.Point; import java.util.concurrent.Callable; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableColumn; import org.eclipse.swt.widgets.TableItem; import org.eclipse.swt.widgets.Widget; import com.windowtester.runtime.IClickDescription; import com.windowtester.runtime.IUIContext; import com.windowtester.runtime.WT; import com.windowtester.runtime.WidgetSearchException; import com.windowtester.runtime.condition.HasText; import com.windowtester.runtime.condition.HasTextCondition; import com.windowtester.runtime.condition.IUICondition; import com.windowtester.runtime.locator.IWidgetLocator; import com.windowtester.runtime.locator.IWidgetReference; import com.windowtester.runtime.locator.WidgetReference; import com.windowtester.runtime.swt.internal.drivers.MenuDriver; import com.windowtester.runtime.swt.internal.finder.legacy.InternalMatcherBuilder; import com.windowtester.runtime.swt.internal.matchers.TableCellMatcher; import com.windowtester.runtime.swt.internal.operation.SWTLocation; import com.windowtester.runtime.swt.internal.operation.SWTMenuOperation; import com.windowtester.runtime.swt.internal.operation.SWTShowMenuOperation; import com.windowtester.runtime.swt.internal.operation.SWTWidgetLocation; import com.windowtester.runtime.swt.internal.selector.TableColumnSelector; import com.windowtester.runtime.swt.internal.selector.TableItemSelector2; import com.windowtester.runtime.swt.internal.selector.UIDriver; import com.windowtester.runtime.swt.internal.widgets.ISWTWidgetMatcher; import com.windowtester.runtime.swt.internal.widgets.ISWTWidgetReference; import com.windowtester.runtime.swt.internal.widgets.MenuItemReference; import com.windowtester.runtime.swt.internal.widgets.MenuReference; import com.windowtester.runtime.swt.internal.widgets.TableItemReference; /** * Locates {@link Table} cells. * <p> * * * TableCellLocator(int row, int column) * <br> * TableCellLocator(int row, String columnName) * <br> * TableCellLocator(String rowText, int column) * <br> * TableCellLocator(String rowText, String columnName) * * <p> * *<dl> * <dt>int row</dt> * <dd>the index of the row where 1 is the first row and 0 is a column header</dd> * <br><dt>int column</dt> <dd>the index of the column where 1 is the first column </dd> * <br><dt>String columnName </dt><dd>the setData("name",...) of the column </dd> * <dd>the text in the column header</dd> * <br><dt>String rowName</dt><dd>the setData("name", ...) of the TableItem</dd> * <dd>the unique text of the row in the specified column</dd> * <dd>the unique text of that row in the entire table.</dd> * </dl> * <p> * Example<p> *<table> * <tr><td>Name</td><td>Age</td><td>Sex</td></tr> * <tr><td>Bob Smith</td><td>16</td><td>Male</td></tr> * <tr><td>John Doe</td><td>32</td><td>Male</td></tr> * <tr><td>Kate Baker</td><td>32</td><td>Male</td></tr> * </table> * *<dl> *<dt>Edit the age of "Bob Smith" ...</dt> * * <dd>ui.click(new TableCellLocator("16", "Age"));</dd> * <dd>ui.enterText("29");</dd> * *<br> * <dt>Edit the age of "John Doe" ...</dt> * * <dd>ui.click(new TableCellLocator("John Doe", "Age"));</dd> * <dd>ui.enterText("29");</dd> * *<br> * <dt>Change the sex field of "Kate Baker"'s record ...</dt> * * <dd>ui.click(new TableCellLocator("Kate Baker", "Sex"));</dd> * <dd>ui.click(new ComboItemLocator("Female"));</dd> *<br> * <dt>Double click on the "Age" column header...</dt> * * <dd>ui.click(2, new TableCellLocator(0, "Age")); </dd> *</dl> */ public class TableCellLocator extends TableItemLocator implements HasText { private static final long serialVersionUID = -7312641272372817767L; public static final int UNSPECIFIED = -1; /** the row no, with 0 being column header, 1 being first row */ private int _row = UNSPECIFIED; /** the name or text for the column */ private String _colNameOrText = null; /** the name or text for row */ private String _rowNameOrText = null; /** * Constructor for the table cell locator * @param row - the row index * @param column - the column index * @throws Exception */ public TableCellLocator(int row, int column) throws WidgetSearchException{ super(); _row = row; if (column !=0) _column = column - 1; else throw new WidgetSearchException("Column index starts from 1 for first column; " + "0 is an invalid index"); } /** * Constructor * @param row - the row index * @param columnName - the name or text for the column */ public TableCellLocator(int row, String columnName){ super(); _row = row; _colNameOrText = columnName; } /** * Constructor * @param rowText - the text to be matched * @param col - the column index * @throws Exception */ public TableCellLocator(String rowText,int column) throws WidgetSearchException{ super(); _rowNameOrText = rowText; if (column != 0) _column = column - 1; else throw new WidgetSearchException("Column index starts from 1 for first column; " + "0 is an invalid index"); } /** * Constructor * @param rowText - the text to be matched * @param colName - the column header */ public TableCellLocator(String rowText,String colName){ super(); _rowNameOrText = rowText; _colNameOrText = colName; } protected ISWTWidgetMatcher buildMatcher() { // IWidgetMatcher matcher = // new AdapterFactory() // .adapt(new TableCellMatcher(getRow(),getColumn(),getRowNameOrText(),getColNameOrText())); // if (getParentInfo() != null) // matcher = new SWTHierarchyMatcher(matcher, getIndex(), getParentInfo()); // return matcher; TableCellMatcher matcher = new TableCellMatcher(getRow(),getColumn(),getRowNameOrText(),getColNameOrText()); if (getParentInfo() != null) return matcher.in(getIndex(), InternalMatcherBuilder.adaptToMatcher(getParentInfo())); return matcher; } /** * * <strong>PROVISIONAL</strong>. This method has been added as * part of a work in progress. There is no guarantee that this API will * work or that it will remain the same. Please do not use this API for more than * experimental purpose without consulting with the WindowTester team. * </p> * * Method used specify the parent widget locator * @param parent the target's parent * @return the TableCellLocator */ public TableCellLocator in(SWTWidgetLocator parent){ if (!(parent.getTargetClass().isAssignableFrom(Table.class))) parent = new SWTWidgetLocator(Table.class, parent); setParentInfo(parent); return this; } /** * * <strong>PROVISIONAL</strong>. This method has been added as * part of a work in progress. There is no guarantee that this API will * work or that it will remain the same. Please do not use this API for more than * experimental purpose without consulting with the WindowTester team. * </p> * * Method used to specify the parent locator, and also indicate the index of the table * in the parent * @param index the target's index relative to its parent * @param parent the target's parent * @return */ public TableCellLocator in(int index, SWTWidgetLocator parent){ if (!(parent.getTargetClass().isAssignableFrom(Table.class))){ parent = new SWTWidgetLocator(Table.class, parent); parent.setIndex(index); } else this.setIndex(index); setParentInfo(parent); return this; } /* (non-Javadoc) * @see com.windowtester.runtime.swt.locator.SWTWidgetLocator#click(com.windowtester.runtime.IUIContext, com.windowtester.runtime.locator.WidgetReference, com.windowtester.runtime.IClickDescription) */ public IWidgetLocator click(IUIContext ui, IWidgetReference reference, IClickDescription click) throws WidgetSearchException { Widget w = (Widget)reference.getWidget(); if (w instanceof TableItem) super.click(ui, reference, click); // it is a table column else { TableColumn tableColumn = (TableColumn)w; Point offset = getXYOffset(reference, click); preClick(reference, offset, ui); doClick(tableColumn, click.clicks(),click.modifierMask(),offset); postClick(reference, ui); } //here we add a slight pause -- note that a condition would be better UIDriver.pause(1500); return WidgetReference.create(w, this); } @Override protected void doClick(TableItemReference item, int clicks, int modifiers, Point offset) throws WidgetSearchException { if (_column == UNSPECIFIED) setColumnIndex(item.getWidget()); new TableItemSelector2().click(clicks, item.getWidget(), getColumn(), convertPoint(offset), modifiers); } protected void doClick(TableColumn item, int clicks, int modifiers, Point offset) throws WidgetSearchException { new TableColumnSelector().click(clicks, item, convertPoint(offset), modifiers); // throw new UnsupportedOperationException("Click on Column Header to be implemented"); } /* (non-Javadoc) * @see com.windowtester.runtime.swt.locator.SWTWidgetLocator#contextClick(com.windowtester.runtime.IUIContext, com.windowtester.runtime.locator.WidgetReference, com.windowtester.runtime.IClickDescription, java.lang.String) */ public IWidgetLocator contextClick(IUIContext ui, final WidgetReference widget, final IClickDescription click, String menuItemPath) throws WidgetSearchException { // TableItem item = (TableItem) widget.getWidget(); // if (_column == UNSPECIFIED) // setColumnIndex(item); // Point offset = getXYOffset(item, click); // preClick(item, offset, ui); // Widget clicked = new TableItemSelector2().contextClick(item, getColumn(), convertPoint(offset), menuItemPath); // postClick(clicked, ui); MenuItemReference clicked = new MenuDriver().resolveAndSelect(new Callable<MenuReference>() { public MenuReference call() throws Exception { return showTableCellContextMenu(widget, click); } }, menuItemPath); return WidgetReference.create(clicked, this); } // TODO move this into TabelCellReference#showContextMenu(...) private MenuReference showTableCellContextMenu(WidgetReference widget, IClickDescription click) { TableItem item = (TableItem) widget.getWidget(); if (_column == UNSPECIFIED) setColumnIndex(item); org.eclipse.swt.graphics.Point offset = new TableItemSelector2().getTableCellClickOffset(item, _column); SWTLocation location = SWTWidgetLocation.withDefaultCenter((ISWTWidgetReference<?>) widget, click, offset); SWTMenuOperation op = new SWTShowMenuOperation(null).waitForIdle().click(WT.BUTTON3, location, false); op.execute(); return op.getMenu(); } private void setColumnIndex(final TableItem item){ item.getDisplay().syncExec(new Runnable() { public void run() { Table table = item.getParent(); TableColumn[] columns = table.getColumns(); for (int i = 0;i < columns.length;i++) if (columns[i].getText().equals(_colNameOrText)){ setColumn(i); break; } } }); } public int getRow() { return _row; } public void setRow(int row) { this._row = row; } public String getColNameOrText() { return _colNameOrText; } public void setColNameOrText(String nameOrText) { _colNameOrText = nameOrText; } public String getRowNameOrText() { return _rowNameOrText; } public void setRowNameOrText(String nameOrText) { _rowNameOrText = nameOrText; } /** * Create a condition that tests if the given table cell has the * expected text. */ public IUICondition hasText(String expectedText) { return new HasTextCondition(this, expectedText); } /** * Resolve the locator to a single cell and answer the text associated with it. * * @param ui the UI context in which to find the widgets * @return the text associated with that object (may be null) */ public String getText(IUIContext ui) throws WidgetSearchException { IWidgetLocator found = ui.find(this); if (found instanceof IWidgetReference) { final Object widget = ((IWidgetReference) found).getWidget(); if (widget instanceof TableItem) { final TableItem item = (TableItem) widget; final String[] result = new String[1]; final Exception[] exception = new Exception[1]; Display.getDefault().syncExec(new Runnable() { public void run() { try { int column = getColumn(); //This is a crude hack -- there is temporal coupling for the named column case //Essentially, column index is cached DURING A CLICK -- if no click is made, column is not resolved //To work-around, we set the column (as in a click) if it is unset if (column == UNSPECIFIED_COLUMN) setColumnIndex(item); result[0] = item.getText(getColumn()); } catch (Exception e) { exception[0] = e; } } }); if (exception[0] != null) throw new WidgetSearchException(exception[0]); return result[0]; } if (widget instanceof TableColumn){ final TableColumn item = (TableColumn) widget; final String[] result = new String[1]; final Exception[] exception = new Exception[1]; Display.getDefault().syncExec(new Runnable() { public void run() { try { result[0] = item.getText(); } catch (Exception e) { exception[0] = e; } } }); if (exception[0] != null) throw new WidgetSearchException(exception[0]); return result[0]; } } return null; } /* (non-Javadoc) * @see com.windowtester.runtime.swt.locator.SWTWidgetLocator#toString() */ public String toString() { if (_row != UNSPECIFIED && _column != UNSPECIFIED) return "TableCellLocator(\"" + getRow()+ ","+ (getColumn()+1) +"\")"; if (_row != UNSPECIFIED && _colNameOrText != null) return "TableCellLocator(\"" + getRow() + ","+ getColNameOrText()+"\")"; return "TableCellLocator"; } }