/*******************************************************************************
* Copyright (c) 2014 BREDEX GmbH.
* 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:
* BREDEX GmbH - initial API and implementation and/or initial documentation
*******************************************************************************/
package org.eclipse.jubula.rc.javafx.tester.adapter;
import java.awt.Rectangle;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Callable;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.eclipse.jubula.rc.common.adaptable.AdapterFactoryRegistry;
import org.eclipse.jubula.rc.common.driver.ClickOptions;
import org.eclipse.jubula.rc.common.exception.RobotException;
import org.eclipse.jubula.rc.common.exception.StepExecutionException;
import org.eclipse.jubula.rc.common.tester.adapter.interfaces.IComponent;
import org.eclipse.jubula.rc.common.tester.adapter.interfaces.IListComponent;
import org.eclipse.jubula.rc.common.tester.adapter.interfaces.ITextComponent;
import org.eclipse.jubula.rc.javafx.driver.EventThreadQueuerJavaFXImpl;
import org.eclipse.jubula.rc.javafx.tester.util.NodeBounds;
import org.eclipse.jubula.rc.javafx.tester.util.NodeTraverseHelper;
import org.eclipse.jubula.rc.javafx.tester.util.Rounding;
import org.eclipse.jubula.tools.internal.objects.event.EventFactory;
import org.eclipse.jubula.tools.internal.objects.event.TestErrorEvent;
import javafx.collections.ObservableList;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
/**
* ListView Adapter
*
* @param <T>
* (sub)-class of ListView
*
* @author BREDEX GmbH
* @created 14.03.2014
*/
public class ListViewAdapter<T extends ListView<?>> extends
JavaFXComponentAdapter<T> implements IListComponent<ListCell<?>> {
/**
* Creates an object with the adapted Label.
*
* @param objectToAdapt
* this must be an object of the Type <code>ListView</code>
*/
public ListViewAdapter(T objectToAdapt) {
super(objectToAdapt);
}
/** {@inheritDoc} **/
public String getText() {
String result = EventThreadQueuerJavaFXImpl.invokeAndWait("getText", //$NON-NLS-1$
new Callable<String>() {
/** {@inheritDoc} **/
public String call() throws Exception {
ObservableList<?> sItems = getRealComponent()
.getSelectionModel().getSelectedItems();
if (!sItems.isEmpty()) {
return String.valueOf(sItems.get(0));
}
throw new StepExecutionException("No selection found", //$NON-NLS-1$
EventFactory.createActionError(
TestErrorEvent.NO_SELECTION));
}
});
return result;
}
/** {@inheritDoc} **/
public void clickOnIndex(final Integer index, ClickOptions co) {
final int actualItemCount = EventThreadQueuerJavaFXImpl
.invokeAndWait("scrollIndexVisible", //$NON-NLS-1$
new Callable<Integer>() {
public Integer call() throws Exception {
final ObservableList<?> items =
getRealComponent().getItems();
int itemCount = items != null ? items.size()
: -1;
return new Integer(itemCount);
}
})
.intValue();
if (index >= actualItemCount || (index < 0)) {
throw new StepExecutionException(
"List index '" + index //$NON-NLS-1$
+ "' is out of range", //$NON-NLS-1$
EventFactory
.createActionError(TestErrorEvent.INVALID_INDEX));
}
Rectangle r = EventThreadQueuerJavaFXImpl.invokeAndWait(
"scrollIndexVisible", //$NON-NLS-1$
new Callable<Rectangle>() {
public Rectangle call() throws Exception {
final T listView = getRealComponent();
listView.scrollTo(index.intValue());
listView.layout();
List<ListCell> lCells = NodeTraverseHelper
.getInstancesOf(listView, ListCell.class);
for (ListCell<?> cell : lCells) {
if (cell.getIndex() == index.intValue()
&& cell.getListView() == listView) {
Rectangle b = NodeBounds
.getAbsoluteBounds(cell);
Rectangle tableB = NodeBounds
.getAbsoluteBounds(listView);
double widthOfClickableRectangle = b.getWidth();
double prefWidth = cell
.prefWidth(cell.getHeight());
// If the clickable rectangle wider than the listview, then the width of the clickable
// rectangle will be the width of the listview
if (widthOfClickableRectangle > listView
.getWidth()) {
widthOfClickableRectangle = listView
.getWidth();
}
if (StringUtils.isBlank(cell.getText())) {
widthOfClickableRectangle = b.getWidth();
}
return new Rectangle(Math.abs(tableB.x - b.x),
Math.abs(tableB.y - b.y),
Rounding.round(
widthOfClickableRectangle),
Rounding.round(b.getHeight()));
}
}
return null;
}
});
getRobot().click(getRealComponent(), r,
co.setClickType(ClickOptions.ClickType.RELEASED));
}
/** {@inheritDoc} **/
public int[] getSelectedIndices() {
return EventThreadQueuerJavaFXImpl.invokeAndWait("getSelectedIndices", //$NON-NLS-1$
new Callable<int[]>() {
/** {@inheritDoc} **/
public int[] call() throws Exception {
ObservableList<Integer> sIndices = getRealComponent()
.getSelectionModel().getSelectedIndices();
return ArrayUtils
.toPrimitive(sIndices.toArray(new Integer[0]));
}
});
}
/** {@inheritDoc} **/
public String[] getSelectedValues() {
return EventThreadQueuerJavaFXImpl.invokeAndWait("getSelectedValues", //$NON-NLS-1$
new Callable<String[]>() {
/** {@inheritDoc} **/
public String[] call() throws Exception {
final T listView = getRealComponent();
ObservableList<Integer> sIndices = listView
.getSelectionModel().getSelectedIndices();
List<String> selectedValues = new LinkedList<String>();
for (Integer i : sIndices) {
int index = i.intValue();
listView.scrollTo(index);
listView.layout();
List<ListCell> lCells = NodeTraverseHelper
.getInstancesOf(listView, ListCell.class);
for (ListCell<?> cell : lCells) {
if (cell.getIndex() == index
&& cell.getListView() == listView) {
selectedValues.add(getCellText(cell));
break;
}
}
}
return selectedValues.toArray(new String[0]);
}
});
}
/** {@inheritDoc} **/
public String[] getValues() {
return EventThreadQueuerJavaFXImpl.invokeAndWait("getValues", //$NON-NLS-1$
new Callable<String[]>() {
/** {@inheritDoc} **/
public String[] call() throws Exception {
List<String> values = new LinkedList<String>();
final T listView = getRealComponent();
ObservableList<?> items = listView.getItems();
int itemCount = items != null ? items.size() : -1;
for (int i = 0; i < itemCount; i++) {
listView.scrollTo(i);
listView.layout();
List<ListCell> lCells = NodeTraverseHelper
.getInstancesOf(listView, ListCell.class);
for (ListCell<?> cell : lCells) {
if (cell.getIndex() == i
&& cell.getListView() == listView) {
values.add(getCellText(cell));
break;
}
}
}
return values.toArray(new String[0]);
}
});
}
/**
* {@inheritDoc}
*/
public String getPropertyValueOfCell(String name, ListCell<?> cell) {
Object prop = EventThreadQueuerJavaFXImpl.invokeAndWait("getProperty", //$NON-NLS-1$
new Callable<String>() {
@Override
public String call() throws Exception {
try {
IComponent adapter =
(IComponent) AdapterFactoryRegistry
.getInstance()
.getAdapter(IComponent.class, cell);
if (adapter != null
&& adapter instanceof ITextComponent) {
return ((ITextComponent) adapter)
.getPropteryValue(name);
}
return null;
} catch (RobotException e) {
throw new StepExecutionException(e.getMessage(),
EventFactory.createActionError(
TestErrorEvent.PROPERTY_NOT_ACCESSABLE));
}
}
});
return String.valueOf(prop);
}
/**
* Get the rendered cell text
*
* @param cell
* the cell
* @return the rendered text
*/
private String getCellText(ListCell<?> cell) {
IComponent adapter = (IComponent) AdapterFactoryRegistry.getInstance()
.getAdapter(IComponent.class, cell);
if (adapter != null && adapter instanceof ITextComponent) {
return ((ITextComponent) adapter).getText();
}
return null;
}
/**
* Returns the cell based on the given index, or null if no cell was found
*
* @param index the index of the cell to look for
* @return the cell or null
*/
public ListCell<?> getCell(int index) {
return EventThreadQueuerJavaFXImpl.invokeAndWait("getCell", //$NON-NLS-1$
new Callable<ListCell<?>>() {
public ListCell<?> call() throws Exception {
final T listView = getRealComponent();
listView.scrollTo(index);
listView.layout();
List<ListCell> lCells = NodeTraverseHelper
.getInstancesOf(listView, ListCell.class);
for (ListCell<?> cell : lCells) {
if (cell.getIndex() == index
&& cell.getListView() == listView) {
return cell;
}
}
return null;
}
});
}
/**
* Returns the cell based on the given value, or null if no cell was found
*
* @param value the value of the cell to look for
* @return the cell or null
*/
public ListCell<?> getCell(String value) {
return EventThreadQueuerJavaFXImpl.invokeAndWait("getCell", //$NON-NLS-1$
new Callable<ListCell<?>>() {
public ListCell<?> call() throws Exception {
final T listView = getRealComponent();
ObservableList<?> items = listView.getItems();
int itemCount = items != null ? items.size() : -1;
for (int i = 0; i < itemCount; i++) {
listView.scrollTo(i);
listView.layout();
List<ListCell> lCells = NodeTraverseHelper
.getInstancesOf(listView, ListCell.class);
for (ListCell<?> cell : lCells) {
if (cell.getIndex() == i
&& cell.getListView() == listView
&& getCellText(cell).equals(value)) {
return cell;
}
}
}
return null;
}
});
}
}