org.assertj.swing.driver.AbstractJTableCellWriter Maven / Gradle / Ivy
Show all versions of assertj-swing Show documentation
/*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* Copyright 2012-2018 the original author or authors.
*/
package org.assertj.swing.driver;
import static java.lang.String.valueOf;
import static org.assertj.core.util.Preconditions.checkNotNull;
import static org.assertj.core.util.Strings.concat;
import static org.assertj.swing.driver.ComponentPreconditions.checkEnabledAndShowing;
import static org.assertj.swing.driver.JTableCancelCellEditingTask.cancelEditing;
import static org.assertj.swing.driver.JTableCellEditorQuery.cellEditorIn;
import static org.assertj.swing.driver.JTableCellPreconditions.checkCellIndicesInBounds;
import static org.assertj.swing.driver.JTableCellPreconditions.validateCellIsEditable;
import static org.assertj.swing.driver.JTableStopCellEditingTask.checkStateAndStopEditing;
import static org.assertj.swing.driver.JTableStopCellEditingTask.stopEditing;
import static org.assertj.swing.edt.GuiActionRunner.execute;
import static org.assertj.swing.exception.ActionFailedException.actionFailure;
import static org.assertj.swing.timing.Pause.pause;
import java.awt.Component;
import java.awt.Point;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.swing.JTable;
import javax.swing.table.TableCellEditor;
import org.assertj.swing.annotation.RunsInCurrentThread;
import org.assertj.swing.annotation.RunsInEDT;
import org.assertj.swing.cell.JTableCellWriter;
import org.assertj.swing.core.ComponentFoundCondition;
import org.assertj.swing.core.ComponentMatcher;
import org.assertj.swing.core.Robot;
import org.assertj.swing.core.TypeMatcher;
import org.assertj.swing.exception.ActionFailedException;
import org.assertj.swing.exception.WaitTimedOutError;
/**
* Template for implementations of {@link JTableCellWriter}.
*
* @author Alex Ruiz
* @author Yvonne Wang
*/
public abstract class AbstractJTableCellWriter implements JTableCellWriter {
protected final Robot robot;
private final JTableLocation location = new JTableLocation();
private TableCellEditor cellEditor;
private static final long EDITOR_LOOKUP_TIMEOUT = 5000;
public AbstractJTableCellWriter(@Nonnull Robot robot) {
this.robot = robot;
}
@RunsInEDT
@Override
public void cancelCellEditing(@Nonnull JTable table, int row, int column) {
if (cellEditor == null) {
doCancelCellEditing(table, row, column);
return;
}
doCancelCellEditing();
}
@RunsInEDT
private void doCancelCellEditing(@Nonnull JTable table, int row, int column) {
cancelEditing(table, row, column);
robot.waitForIdle();
}
@RunsInEDT
private void doCancelCellEditing() {
cancelEditing(cellEditor);
robot.waitForIdle();
}
@RunsInEDT
@Override
public void stopCellEditing(@Nonnull JTable table, int row, int column) {
if (cellEditor == null) {
doStopCellEditing(table, row, column);
return;
}
doStopCellEditing();
}
@RunsInEDT
private void doStopCellEditing(@Nonnull JTable table, int row, int column) {
checkStateAndStopEditing(table, row, column);
robot.waitForIdle();
}
@RunsInEDT
private void doStopCellEditing() {
stopEditing(cellEditor);
robot.waitForIdle();
}
/**
* Returns the editor for the given {@code JTable} cell. This method is executed in the EDT.
*
* @param table the given {@code JTable}.
* @param row the row index of the cell.
* @param column the column index of the cell.
* @return the editor for the given {@code JTable} cell.
*/
@RunsInEDT
@Nullable protected static TableCellEditor cellEditor(final @Nonnull JTable table, final int row, final int column) {
return execute(() -> table.getCellEditor(row, column));
}
/**
*
* Scrolls the given {@code JTable} to the given cell.
*
*
*
* Note: This method is accessed in the current executing thread. Such thread may or may not be the event
* dispatch thread (EDT). Client code must call this method from the EDT.
*
*
* @param table the given {@code JTable}.
* @param row the row index of the cell.
* @param column the column index of the cell.
* @param location obtains the bounds of the given cell.
*/
@RunsInCurrentThread
protected static void scrollToCell(JTable table, int row, int column, JTableLocation location) {
table.scrollRectToVisible(location.cellBounds(table, row, column));
}
@RunsInEDT
@Override
@Nullable public Component editorForCell(@Nonnull JTable table, int row, int column) {
return cellEditorComponent(table, row, column);
}
@RunsInEDT
@Nullable private static Component cellEditorComponent(final @Nonnull JTable table, final int row, final int column) {
return execute(() -> {
checkCellIndicesInBounds(table, row, column);
return cellEditorIn(table, row, column);
});
}
/**
*
* Finds the AWT or Swing {@code Component} used as editor for the given {@code JTable}.
*
*
*
* Note: This method is accessed in the current executing thread. Such thread may or may not be the event
* dispatch thread (EDT). Client code must call this method from the EDT.
*
*
* @param table the given {@code JTable}.
* @param row the row index of the cell.
* @param column the column index of the cell.
* @param the type of component we expect as editor.
* @param supportedType the class of the type of component.
* @return the component used as editor for the given table cell.
* @throws IndexOutOfBoundsException if any of the indices is out of bounds or if the {@code JTable} does not have any
* rows.
* @throws IllegalStateException if the {@code JTable} is disabled.
* @throws IllegalStateException if the {@code JTable} is not showing on the screen.
* @throws IllegalStateException if the table cell in the given coordinates is not editable.
* @throws IndexOutOfBoundsException if any of the indices is out of bounds or if the {@code JTable} does not have any
* rows.
* @throws ActionFailedException if an editor for the given cell cannot be found or cannot be activated.
*/
@RunsInCurrentThread
@Nonnull protected static T editor(@Nonnull JTable table, int row, int column,
@Nonnull Class supportedType) {
validate(table, row, column);
Component editor = cellEditorIn(table, row, column);
if (supportedType.isInstance(editor)) {
return supportedType.cast(editor);
}
throw cannotFindOrActivateEditor(row, column);
}
/**
* Returns the location of the given table cell.
*
* @param table the given {@code JTable}.
* @param row the row index of the cell.
* @param column the column index of the cell.
* @param location knows how to get the location of a table cell.
* @return the location of the given table cell.
* @throws IllegalStateException if the {@code JTable} is disabled.
* @throws IllegalStateException if the {@code JTable} is not showing on the screen.
* @throws IndexOutOfBoundsException if any of the indices is out of bounds or if the {@code JTable} does not have any
* rows.
* @throws IllegalStateException if the table cell in the given coordinates is not editable.
*/
@RunsInEDT
@Nonnull protected static Point cellLocation(final @Nonnull JTable table, final int row, final int column,
final @Nonnull JTableLocation location) {
Point result = execute(() -> {
validate(table, row, column);
scrollToCell(table, row, column, location);
return location.pointAt(table, row, column);
});
return checkNotNull(result);
}
/**
*
* Validates that:
*
*
* - the given {@code JTable} is enabled and showing on the screen
* - the row and column indices are correct (not out of bounds)
* - the table cell at the given indices is editable
*
*
*
* Note: This method is accessed in the current executing thread. Such thread may or may not be the event
* dispatch thread (EDT). Client code must call this method from the EDT.
*
*
* @param table the given {@code JTable}.
* @param row the row index of the cell.
* @param column the column index of the cell.
* @throws IllegalStateException if the {@code JTable} is disabled.
* @throws IllegalStateException if the {@code JTable} is not showing on the screen.
* @throws IndexOutOfBoundsException if any of the indices is out of bounds or if the {@code JTable} does not have any
* rows.
* @throws IllegalStateException if the table cell in the given coordinates is not editable.
*/
@RunsInCurrentThread
protected static void validate(final @Nonnull JTable table, final int row, final int column) {
checkCellIndicesInBounds(table, row, column);
checkEnabledAndShowing(table);
validateCellIsEditable(table, row, column);
}
/**
* Waits until the editor of the given table cell is showing on the screen. Component lookup is performed by type.
*
* @param table the given {@code JTable}.
* @param row the row index of the cell.
* @param column the column index of the cell.
* @param the type of component we expect as editor.
* @param supportedType the class of the type of component.
* @return the editor of the given table cell once it is showing on the screen.
* @throws ActionFailedException if an editor for the given cell cannot be found or cannot be activated.
*/
@RunsInEDT
protected final @Nullable T waitForEditorActivation(@Nonnull JTable table, int row, int column,
@Nonnull Class supportedType) {
return waitForEditorActivation(new TypeMatcher(supportedType, true), table, row, column, supportedType);
}
/**
* Waits until the editor of the given table cell is showing on the screen.
*
* @param matcher the condition that the cell editor to look for needs to satisfy.
* @param table the given {@code JTable}.
* @param row the row index of the cell.
* @param column the column index of the cell.
* @param the type of component we expect as editor.
* @param supportedType the class of the type of component.
* @return the editor of the given table cell once it is showing on the screen.
* @throws ActionFailedException if an editor for the given cell cannot be found or cannot be activated.
*/
@RunsInEDT
protected final @Nullable T waitForEditorActivation(@Nonnull ComponentMatcher matcher,
@Nonnull JTable table, int row, int column,
@Nonnull Class supportedType) {
ComponentFoundCondition condition = new ComponentFoundCondition("", robot.finder(), matcher, table);
try {
pause(condition, EDITOR_LOOKUP_TIMEOUT);
} catch (WaitTimedOutError e) {
throw cannotFindOrActivateEditor(row, column);
}
return supportedType.cast(condition.found());
}
/**
* Throws a {@link ActionFailedException} if this {@link JTableCellWriter} could not find or activate the cell editor
* of the supported type.
*
* @param row the row index of the cell.
* @param column the column index of the cell.
* @return the thrown exception.
*/
@Nonnull protected static ActionFailedException cannotFindOrActivateEditor(int row, int column) {
String msg = concat("Unable to find or activate editor for cell [", valueOf(row), ",", valueOf(column), "]");
throw actionFailure(msg);
}
/**
* @return the cell editor being currently used, or {@code null} if no table cell is being currently edited.
*/
protected final @Nullable TableCellEditor cellEditor() {
return cellEditor;
}
/**
* Sets the cell editor being currently used.
*
* @param newCellEditor the cell editor being currently used.
*/
protected final void cellEditor(@Nullable TableCellEditor newCellEditor) {
cellEditor = newCellEditor;
}
protected final @Nonnull JTableLocation location() {
return location;
}
}