io.magentys.cinnamon.webdriver.elements.TableElementImpl Maven / Gradle / Ivy
package io.magentys.cinnamon.webdriver.elements;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import io.magentys.cinnamon.webdriver.elements.TableElement.CellAdapter;
import io.magentys.cinnamon.webdriver.elements.TableElement.MatchingCell;
import io.magentys.cinnamon.webdriver.elements.TableElement.MultiCellAdapter;
import io.magentys.cinnamon.webdriver.conditions.Condition;
import org.openqa.selenium.By;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.pagefactory.ElementLocator;
import java.util.LinkedList;
import java.util.List;
import static io.magentys.cinnamon.webdriver.WebDriverUtils.unwrapDriver;
import static org.openqa.selenium.support.pagefactory.ElementLocator.constructFrom;
public class TableElementImpl implements Table {
private final ElementCache cache;
private final By columnFinder = By.cssSelector("thead th");
private final By rowLocator = By.cssSelector("tbody tr");
private final By cellLocator = By.cssSelector("td,th");
private int rowHeaderColspan = 1;
public TableElementImpl(final WebElement element) {
this(constructFrom(unwrapDriver(element), element), element);
}
public TableElementImpl(final ElementLocator elementLocator, final WebElement element) {
this.cache = new ElementCache(elementLocator, element);
}
@Override
public Table withRowHeaderColspan(final int n) {
rowHeaderColspan = n;
return this;
}
@Override
public List asList(final Class type) {
return asList(new ColumnHeadingToClassFieldMappingAdapter<>(type));
}
@Override
public List asList(final RowAdapter adapter) {
final List columnHeaderElements = cache.getElement().findElements(columnFinder);
final List rows = findAllRows();
final List mappedRows = new LinkedList<>();
for (final WebElement row : rows) {
final List cells = row.findElements(cellLocator);
mappedRows.add(adapter.adapt(columnHeaderElements, cells));
}
return mappedRows;
}
@Override
public List asPivot(final CellAdapter adapter) {
final CellVisitor> collatingCellAdapter = new CellVisitor>() {
final List adaptedCells = new LinkedList<>();
@Override
public void visit(final List columnHeaders, final WebElement rowHeader, final WebElement cell) {
// Adapt the first column header only - maintains backward
// compatability for CellAdapter
adaptedCells.add(adapter.adapt(columnHeaders.get(0), rowHeader, cell));
}
@Override
public List result() {
return adaptedCells;
}
@Override
public boolean isFinished() {
// process all cells
return false;
}
};
return visitCells(collatingCellAdapter);
}
@Override
public List asPivot(final MultiCellAdapter adapter) {
final List adaptedCells = new LinkedList<>();
final List rows = findAllRows();
for (final WebElement row : rows) {
final List cells = row.findElements(cellLocator);
final WebElement rowHeader = cells.remove(0);
// +1 for index to ordinal
// +roHeaderColspan to skip the row header column(s)
final int columnOrdinalOffset = 1 + rowHeaderColspan;
for (int i = 0; i < cells.size(); i++) {
final List columnHeaders = cache.getElement().findElements(byColumn(i + columnOrdinalOffset));
final WebElement cell = cells.get(i);
adaptedCells.add(adapter.adapt(columnHeaders, rowHeader, cell));
}
}
return adaptedCells;
}
private By byColumn(final int ordinal) {
return By.cssSelector(String.format("thead th:nth-child(%s)", ordinal));
}
private List findAllRows() {
return cache.getElement().findElements(rowLocator);
}
public List searchForRows(final Condition condition) {
final Iterable allRows = Iterables.filter(findAllRows(), condition);
return Lists.newArrayList(allRows);
}
@Override
public MatchingCell firstMatch(final CellAdapter matcher) throws NoSuchElementException {
final CellVisitor firstMatchVisitor = new CellVisitor() {
private MatchingCell firstMatch = null;
@Override
public void visit(final List columnHeaders, final WebElement rowHeader, final WebElement cell) {
for (final WebElement columnHeader : columnHeaders) {
if (matcher.adapt(columnHeader, rowHeader, cell)) {
firstMatch = new MatchingCell() {
@Override
public WebElement getColumn() {
return columnHeader;
}
@Override
public WebElement getRow() {
return rowHeader;
}
@Override
public WebElement getCell() {
return cell;
}
};
// We're done
return;
}
}
}
@Override
public MatchingCell result() {
return firstMatch;
}
@Override
public boolean isFinished() {
return firstMatch != null;
}
};
final MatchingCell result = visitCells(firstMatchVisitor);
if (result == null) {
throw new NoSuchElementException("No matching cell found");
}
return result;
}
private T visitCells(final CellVisitor v) {
final List rows = findAllRows();
for (final WebElement row : rows) {
final List cells = row.findElements(cellLocator);
final WebElement rowHeader = cells.remove(0);
// +1 for index to ordinal
// +roHeaderColspan to skip the row header column(s)
final int columnOrdinalOffset = 1 + rowHeaderColspan;
for (int i = 0; i < cells.size(); i++) {
final List columnHeaders = cache.getElement().findElements(byColumn(i + columnOrdinalOffset));
final WebElement cell = cells.get(i);
v.visit(columnHeaders, rowHeader, cell);
if (v.isFinished()) {
return v.result();
}
}
}
return v.result();
}
private interface CellVisitor {
/**
* Visit a particular cell
*
* @param columnHeaders
* @param rowHeader
* @param cell
*/
void visit(List columnHeaders, WebElement rowHeader, WebElement cell);
/**
* Return the current result for this visitor.
*
* @return
*/
T result();
/**
* Has this visitor finished processing? Allows a shortcut to prevent traversing across all cells.
*
* If all cells should be traversed, return false.
*
* @return
*/
boolean isFinished();
}
}