/*******************************************************************************
* 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.lang.reflect.Field;
import java.util.List;
import java.util.concurrent.Callable;
import org.eclipse.jubula.rc.common.adaptable.AdapterFactoryRegistry;
import org.eclipse.jubula.rc.common.driver.ClickOptions;
import org.eclipse.jubula.rc.common.exception.StepExecutionException;
import org.eclipse.jubula.rc.common.tester.adapter.interfaces.IComboComponent;
import org.eclipse.jubula.rc.common.tester.adapter.interfaces.IComponent;
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.NodeTraverseHelper;
import com.sun.javafx.scene.control.skin.ComboBoxBaseSkin;
import com.sun.javafx.scene.control.skin.ComboBoxListViewSkin;
import javafx.collections.ObservableList;
import javafx.scene.Node;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.Skin;
/**
* ComboBox Adapter
*
* @param <T> (sub)-class of ComboBox
*
* @author BREDEX GmbH
* @created 20.03.2014
*/
public class ComboBoxAdapter<T extends ComboBox<?>> extends
JavaFXComponentAdapter<T> implements IComboComponent {
/** the name of the arrow button field of a combo box skin */
private static final String ARROW_BUTTON_FIELD_NAME = "arrowButton"; //$NON-NLS-1$
/**
* Creates an object with the adapted Label.
*
* @param objectToAdapt
* this must be an object of the Type <code>ComboBox</code>
*/
public ComboBoxAdapter(T objectToAdapt) {
super(objectToAdapt);
}
@Override
public String getText() {
return EventThreadQueuerJavaFXImpl.invokeAndWait(
"getText", new Callable<String>() { //$NON-NLS-1$
@Override
public String call() throws Exception {
ObservableList<Node> children = getRealComponent()
.getChildrenUnmodifiable();
for (Node node : children) {
if (node instanceof ListCell) {
IComponent adapter = (IComponent)
AdapterFactoryRegistry.getInstance()
.getAdapter(IComponent.class, node);
if (adapter != null
&& adapter instanceof ITextComponent) {
return ((ITextComponent) adapter).getText();
}
}
}
return null;
}
});
}
@Override
public boolean isEditable() {
return EventThreadQueuerJavaFXImpl.invokeAndWait(
"isEditable", new Callable<Boolean>() { //$NON-NLS-1$
@Override
public Boolean call() throws Exception {
return getRealComponent().isEditable();
}
});
}
@Override
public void selectAll() {
StepExecutionException.throwUnsupportedAction();
}
@Override
public int getSelectedIndex() {
return EventThreadQueuerJavaFXImpl.invokeAndWait("getSelectedIndex", //$NON-NLS-1$
new Callable<Integer>() {
@Override
public Integer call() throws Exception {
return getRealComponent().getSelectionModel()
.getSelectedIndex();
}
});
}
@Override
public void select(final int index) {
final ListView<?> lv = getComboBoxList();
T comboBox = getRealComponent();
setOpenedStatus(comboBox, true);
try {
ListViewAdapter<ListView<?>> listViewAdapter =
new ListViewAdapter<ListView<?>>(lv);
listViewAdapter.clickOnIndex(index, ClickOptions.create().
setClickCount(1).setMouseButton(1));
} finally {
setOpenedStatus(comboBox, false);
}
}
@Override
public void input(String text, boolean replace)
throws StepExecutionException, IllegalArgumentException {
StepExecutionException.throwUnsupportedAction();
}
@Override
public String[] getValues() {
final ListView<?> lv = getComboBoxList();
T comboBox = getRealComponent();
setOpenedStatus(comboBox, true);
String[] values = new String[0];
try {
ListViewAdapter<?> listViewAdapter =
new ListViewAdapter<ListView<?>>(lv);
values = listViewAdapter.getValues();
} finally {
setOpenedStatus(comboBox, false);
}
return values;
}
/**
* Returns the list with the items of the combo box.
* @return the list
*/
private ListView<?> getComboBoxList() {
return EventThreadQueuerJavaFXImpl.invokeAndWait("getValues", //$NON-NLS-1$
new Callable<ListView<?>>() {
/** {@inheritDoc} **/
public ListView<?> call() throws Exception {
T comboBox = getRealComponent();
@SuppressWarnings("rawtypes")
List<ListView> listViewList = NodeTraverseHelper
.getInstancesOf(comboBox, ListView.class);
if (listViewList.size() == 1) {
return listViewList.get(0);
}
// If there is not exactly one list inside the combo box,
// then use internal API
ComboBoxListViewSkin<?> comboBoxListViewSkin =
(ComboBoxListViewSkin<?>) comboBox.getSkin();
return (ListView<?>)comboBoxListViewSkin
.getPopupContent();
}
});
}
/**
* Opens or closes the combo box list.
* @param comboBox the combo box
* @param openStatus whether the combo box should be opened or not
*/
private void setOpenedStatus(T comboBox, boolean openStatus) {
if (comboBox.isShowing() != openStatus) {
getRobot().click(getArrowButton(comboBox), null,
ClickOptions.create().setClickCount(1).setMouseButton(1));
}
}
/**
* @param comboBox combo box
* @return the arrow button of the combo box or the combo box itself
* if the arrow was not found
*/
private Node getArrowButton(T comboBox) {
Skin<?> skin = comboBox.getSkin();
if (skin instanceof ComboBoxBaseSkin) {
try {
Field arrowButton = ComboBoxBaseSkin.class
.getDeclaredField(ARROW_BUTTON_FIELD_NAME);
arrowButton.setAccessible(true);
return (Node) arrowButton.get(skin);
} catch (Exception e) {
// ignore, return combo box
}
}
return comboBox;
}
}