Package org.uispec4j

Source Code of org.uispec4j.Table

package org.uispec4j;

import org.uispec4j.assertion.Assertion;
import org.uispec4j.assertion.testlibrairies.AssertAdapter;
import org.uispec4j.utils.ArrayUtils;
import org.uispec4j.utils.ColorUtils;
import org.uispec4j.utils.ComponentColorChecker;
import org.uispec4j.utils.Utils;

import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableModel;
import java.awt.*;
import java.util.*;

import junit.framework.AssertionFailedError;

/**
* Wrapper for JTable components.<p/>
* The contents of the underlying table can be usually checked with String or Boolean values,
* as in the following example:
* <pre><code>
* assertTrue(table.contentEquals(new String[]{
*   {"Bart", "Simpson"},
*   {"Marge", "Simpson"}
* }));
* </code></pre>
* The conversion between the values (Strings) given in the test and the values
* actually displayed by the table renderer is performed by a dedicated
* {@link TableCellValueConverter}, which retrieves the graphical component that draws
* the table cells and determines the displayed value accordingly.
* A {@link DefaultTableCellValueConverter} is used by default by the Table component.
*/
public class Table extends AbstractSwingUIComponent {
  public static final String TYPE_NAME = "table";
  public static final Class[] SWING_CLASSES = {JTable.class};

  private JTable jTable;
  private Header header = new Header();
  private TableCellValueConverter defaultCellValueConverter = new DefaultTableCellValueConverter();
  private Map<Integer, TableCellValueConverter> cellValuesConvertersByColumn =
    new HashMap<Integer, TableCellValueConverter>();

  public Table(JTable table) {
    this.jTable = table;
  }

  public String getDescriptionTypeName() {
    return TYPE_NAME;
  }

  public JTable getAwtComponent() {
    return jTable;
  }

  public JTable getJTable() {
    return jTable;
  }

  /**
   * Returns a helper interface which gives access to the table header.
   */
  public Header getHeader() {
    return header;
  }

  /**
   * Sets a new converter for analyzing the table cells content.
   */
  public void setDefaultCellValueConverter(TableCellValueConverter cellValueConverter) {
    this.defaultCellValueConverter = cellValueConverter;
  }

  /**
   * Sets a new converter for analyzing the cells of a given column.
   */
  public void setCellValueConverter(int column, TableCellValueConverter tableCellValueConverter) {
    cellValuesConvertersByColumn.put(column, tableCellValueConverter);
  }

  public void click(int row, int column) {
    click(row, column, Key.Modifier.NONE);
  }

  public void click(int row, int column, Key.Modifier modifier) {
    Rectangle rect = jTable.getCellRect(row, column, false);
    Mouse.doClickInRectangle(this, rect, false, modifier);
  }

  public void rightClick(int row, int column) {
    Rectangle rect = jTable.getCellRect(row, column, false);
    Mouse.doClickInRectangle(this, rect, true, Key.Modifier.NONE);
  }

  public void doubleClick(int row, int column) {
    Rectangle rect = jTable.getCellRect(row, column, false);
    Mouse.doDoubleClickInRectangle(jTable, rect);
  }

  public Trigger triggerClick(final int row, final int column, final Key.Modifier modifier) {
    return new Trigger() {
      public void run() throws Exception {
        click(row, column, modifier);
      }
    };
  }

  public Trigger triggerRightClick(final int row, final int column) {
    return new Trigger() {
      public void run() throws Exception {
        rightClick(row, column);
      }
    };
  }

  public Trigger triggerDoubleClick(final int row, final int column) {
    return new Trigger() {
      public void run() throws Exception {
        doubleClick(row, column);
      }
    };
  }

  public int getRowCount() {
    return jTable.getRowCount();
  }

  public Assertion rowCountEquals(final int count) {
    return new Assertion() {
      public void check() {
        AssertAdapter.assertEquals("Unexpected number of rows -", count, getRowCount());
      }
    };
  }

  public int getColumnCount() {
    return jTable.getColumnCount();
  }

  public Assertion columnCountEquals(final int count) {
    return new Assertion() {
      public void check() {
        AssertAdapter.assertEquals("Unexpected number of columns -", count, getColumnCount());
      }
    };
  }

  /**
   * Returns the object (String or Boolean) displayed in a given cell.<p/>
   * The returned object is that returned by the current TableCellValueConverter
   * used by the table.
   *
   * @see #setCellValueConverter(int,TableCellValueConverter)
   * @see #setDefaultCellValueConverter(TableCellValueConverter)
   */
  public Object getContentAt(int row, int column) {
    return getValueAt(row, column);
  }

  /**
   * Returns the value displayed in a given cell using a specific converter.
   */
  public Object getContentAt(int row, int column, TableCellValueConverter converter) {
    return converter.getValue(row, column,
                              getSwingRendererComponentAt(row, column),
                              jTable.getValueAt(row, column));
  }

  /**
   * Returns a {@link Cell} object for interacting with the content of an individual table cell.<p/>
   * Sample usage:
   * <pre><code>
   * ComboBox comboBox = table.editCell(0, 0).getComboBox();
   * assertTrue(comboBox.contentEquals(choices));
   * comboBox.select("b");
   * </code></pre>
   */
  public Cell editCell(int row, int column) {
    AssertAdapter.assertTrue("Cell (" + row + "," + column + ") is not editable",
                             jTable.isCellEditable(row, column));
    Component cellEditor = getSwingEditorComponentAt(row, column);
    JPanel cellPanel = new JPanel();
    cellPanel.add(cellEditor);
    return new Cell(cellPanel);
  }

  public int getRowIndex(int column, Object value) {
    int count = jTable.getRowCount();
    for (int i = 0; i < count; i++) {
      if (value.equals(getValueAt(i, column))) {
        return i;
      }
    }
    return -1;
  }

  public int[] getRowIndices(int column, Object value) {
    java.util.List<Integer> list = new ArrayList<Integer>();
    int count = jTable.getRowCount();
    for (int i = 0; i < count; i++) {
      if (value.equals(getValueAt(i, column))) {
        list.add(i);
      }
    }
    int[] indices = new int[list.size()];
    int i = 0;
    for (Integer index : list) {
      indices[i] = index;
      i++;
    }
    return indices;
  }

  /**
   * Represents a table cell. This class extends Panel, so that all the component searching
   * methods available in Panel can be used to access the specific component displayed in the cell.
   */
  public class Cell extends Panel {
    public Cell(Container container) {
      super(container);
    }
  }

  /**
   * Inputs some text in a given cell.<p/>
   * This method only works when the underlying editor is a JTextField or a JComboBox -
   * it will throw an exception if this is not the case, or if the cell is not editable.
   * Please refer to {@link #editCell(int,int)} for a more flexible edition method.<p/>
   */
  public void editCell(int row, int column, String value, boolean validateChange) {
    if (!jTable.isCellEditable(row, column)) {
      throw new RuntimeException("Cell (" + row + ", " + column + ") is not editable");
    }
    Component cellEditor = getSwingEditorComponentAt(row, column);
    if (JTextField.class.isInstance(cellEditor)) {
      JTextField textField = ((JTextField)cellEditor);
      textField.setText(value);
      if (validateChange) {
        textField.postActionEvent();
      }
    }
    else {
      if (JComboBox.class.isInstance(cellEditor)) {
        JComboBox jCombo = (JComboBox)cellEditor;
        if (validateChange) {
          ComboBox comboBox = new ComboBox(jCombo);
          comboBox.select(value);
        }
      }
      else {
        throw new RuntimeException("Unexpected editor at (" + row + ", " + column + "): " + cellEditor.getClass().getName());
      }
    }
  }

  /**
   * Checks whether a header is displayed for this table.
   */
  public Assertion hasHeader() {
    return new Assertion() {
      public void check() {
        if (jTable.getTableHeader() == null) {
          AssertAdapter.fail("The table contains an header");
        }
      }
    };
  }

  /**
   * Checks the values displayed in the table.<p/>
   * Sample usage:
   * <pre><code>
   * assertTrue(table.contentEquals(new Object[][]{
   *   {"a", Boolean.TRUE, "3"},
   *   {"c", Boolean.FALSE, "4"}
   * }));
   * </code></pre>
   * The conversion between the displayed values and the objects to
   * be given in the array can be customized with
   * {@link #setCellValueConverter(int,TableCellValueConverter)}
   */
  public Assertion contentEquals(final Object[][] expected) {
    return new Assertion() {
      public void check() {
        try {
          int expectedLength = expected.length;
          AssertAdapter.assertEquals(lengthErrorMessage(expectedLength),
                                     expectedLength, getRowCount());
          for (int i = 0; i < expectedLength; i++) {
            checkRow(i, expected[i]);
          }
        }
        catch (Error e) {
          AssertAdapter.assertEquals(ArrayUtils.toString(expected), getContent());
          throw e;
        }
      }
    };
  }

  public Assertion blockEquals(final int fromRowIndex, final int fromColumnIndex,
                               final int columnCount, final int rowCount, final Object[][] expected) {
    return new Assertion() {
      public void check() {
        try {
          AssertAdapter.assertTrue(lengthErrorMessage(fromRowIndex),
                                     fromColumnIndex + rowCount <= getRowCount());
          for (int i = fromColumnIndex; i < fromColumnIndex + rowCount; i++) {
            checkRow(i, fromRowIndex, columnCount, expected[i - fromColumnIndex]);
          }
        }
        catch (Error e) {
          e.printStackTrace();
          AssertAdapter.assertEquals(ArrayUtils.toString(expected), getContent());
          throw e;
        }
      }
    };
  }

  /**
   * Checks the values displayed in the table for a given set of columns.
   *
   * @see #contentEquals(Object[][])
   */
  public Assertion contentEquals(final String[] columnNames, final Object[][] expected) {
    return new Assertion() {
      public void check() {
        int rowCount = jTable.getRowCount();
        if (expected.length != rowCount) {
          throwError("Expected " + expected.length + " rows but found " + rowCount,
                     columnNames, expected);
        }

        for (int rowIndex = 0; rowIndex < expected.length; rowIndex++) {
          Object[] row = expected[rowIndex];
          if (columnNames.length != row.length) {
            AssertAdapter.fail("Expected array should have " + columnNames.length + " elements for each row " +
                               "- invalid row " + rowIndex + ": " + ArrayUtils.toString(row));
          }

          for (int columnIndex = 0; columnIndex < columnNames.length; columnIndex++) {
            int actualIndex = getColumnIndex(columnNames[columnIndex]);
            if (!Utils.equals(expected[rowIndex][columnIndex], getValueAt(rowIndex, actualIndex))) {
              throwError("Error at (" + rowIndex + ", " + columnIndex + ")", columnNames, expected);
            }
          }
        }
      }
    };
  }

  private void throwError(String message, String[] columnNames, Object[][] expected) {
    String actualContent = getContent(columnNames);
    AssertAdapter.assertEquals(message, ArrayUtils.toString(expected), actualContent);
    AssertAdapter.fail("Actual: " + actualContent);// in case the string comparison didn't fail
  }

  /**
   * Checks the contents of a given cell.
   * The conversion between the displayed values and the objects
   * provided in this method can be customized with
   * {@link #setCellValueConverter(int,TableCellValueConverter)}
   *
   * @see #getContentAt(int,int)
   */
  public Assertion cellEquals(final int row, final int column, final Object expectedValue) {
    return cellEquals(row, column, expectedValue, getCellValueConverter(column));
  }

  /**
   * Checks the contents of a given cell using a specific data converter.
   * The conversion between the displayed values and the objects
   * provided in this method can be customized with
   * {@link #setCellValueConverter(int,TableCellValueConverter)}
   *
   * @see #getContentAt(int,int,TableCellValueConverter)
   */
  public Assertion cellEquals(final int row, final int column,
                              final Object expectedValue,
                              final TableCellValueConverter converter) {
    return new Assertion() {
      public void check() {
        AssertAdapter.assertEquals("Error at (" + row + "," + column + ") -",
                                   expectedValue, getContentAt(row, column, converter));
      }
    };
  }

  public Assertion rowEquals(final int rowIndex, final Object[] expectedRow) {
    return new Assertion() {
      public void check() {
        if (rowIndex < 0) {
          AssertAdapter.fail("Row index should be positive");
        }
        if (rowIndex >= jTable.getRowCount()) {
          AssertAdapter.fail("Table contains only " + jTable.getRowCount() + " rows, unable to access row " + rowIndex);
        }
        try {
          checkRow(rowIndex, expectedRow);
        }
        catch (Error e) {
          StringBuffer buffer = new StringBuffer();
          dumpRow(jTable, rowIndex, buffer, ",");
          AssertAdapter.assertEquals(ArrayUtils.toString(expectedRow), buffer);
        }
      }
    };
  }

  public Assertion rowEquals(final int rowIndex, final int fromColumnIndex, final int columnCount, final Object[] expectedRow) {
    return new Assertion() {
      public void check() {
        if (rowIndex < 0) {
          AssertAdapter.fail("Row index should be positive");
        }
        if (rowIndex >= jTable.getRowCount()) {
          AssertAdapter.fail("Table contains only " + jTable.getRowCount() + " rows, unable to access row " + rowIndex);
        }
        try {
          checkRow(rowIndex, fromColumnIndex, columnCount, expectedRow);
        }
        catch (Error e) {
          StringBuffer buffer = new StringBuffer();
          dumpRow(jTable, rowIndex, buffer, ",");
          AssertAdapter.assertEquals(ArrayUtils.toString(expectedRow), buffer);
        }
      }
    };
  }

  public Assertion rowEquals(final int rowIndex, final String[] columnNames, final Object[] expected) {
    return new Assertion() {
      public void check() {
        if (rowIndex < 0) {
          AssertAdapter.fail("Row index should be positive");
        }
        if (rowIndex >= jTable.getRowCount()) {
          AssertAdapter.fail("Table contains only " + jTable.getRowCount() + " rows, unable to access row " + rowIndex);
        }
        if (columnNames.length != expected.length) {
          AssertAdapter.fail("Expected array should have " + columnNames.length + " elements for each row " +
                             "- invalid row " + rowIndex + ": " + ArrayUtils.toString(expected));
        }
        Object[] actual = new Object[expected.length];
        for (int columnIndex = 0; columnIndex < columnNames.length; columnIndex++) {
          int actualIndex = getColumnIndex(columnNames[columnIndex]);
          actual[columnIndex] = getValueAt(rowIndex, actualIndex);
        }
        ArrayUtils.assertEquals("Unexpected content at row " + rowIndex, expected, actual);
      }
    };
  }

  public Assertion columnEquals(final int columnIndex, final Object[] expectedColumn) {
    return new Assertion() {
      public void check() {
        if (columnIndex < 0) {
          AssertAdapter.fail("Column index should be positive");
        }
        if (columnIndex >= jTable.getColumnCount()) {
          AssertAdapter.fail("Table contains only " + jTable.getColumnCount() + " columns, unable to access column " + columnIndex);
        }
        try {
          checkColumn(columnIndex, expectedColumn);
        }
        catch (Error e) {
          StringBuffer buffer = new StringBuffer();
          dumpColumn(jTable, columnIndex, buffer, ",");
          AssertAdapter.assertEquals(ArrayUtils.toString(expectedColumn), buffer.toString());
        }
      }
    };
  }

  public Assertion isEmpty() {
    return new Assertion() {
      public void check() {
        try {
          AssertAdapter.assertEquals(0, jTable.getRowCount());
        }
        catch (Error e) {
          AssertAdapter.fail("Expected: empty table but was:" + getContent());
        }
      }
    };
  }

  /**
   * Checks the foreground color of the table cells using either Color or String objects
   *
   * @see <a href="http://www.uispec4j.org/colors">Using colors</a>
   */
  public Assertion foregroundEquals(final Object[][] colors) {
    return new Assertion() {
      public void check() {
        checkColors(colors, ComponentColorChecker.FOREGROUND);
      }
    };
  }

  public Assertion foregroundNear(final int row, final int column, final Object expected) {
    return new Assertion() {
      public void check() {
        final Component component = getSwingRendererComponentAt(row, column);
        ColorUtils.assertSimilar("Error at (" + row + ", " + column + ")",
                                 expected, component.getForeground());
      }
    };
  }

  public Assertion backgroundNear(final int row, final int column, final Object expected) {
    return new Assertion() {
      public void check() {
        final Component component = getSwingRendererComponentAt(row, column);
        ColorUtils.assertSimilar("Error at (" + row + ", " + column + ")",
                                 expected, component.getBackground());
      }
    };
  }

  /**
   * Checks the background color of the table cells using either Color or String objects
   *
   * @see <a href="http://www.uispec4j.org/colors">Using colors</a>
   */
  public Assertion backgroundEquals(final Object[][] colors) {
    return new Assertion() {
      public void check() {
        checkColors(colors, ComponentColorChecker.BACKGROUND);
      }
    };
  }

  private interface ComponentPropertyAccessor {
    Object getProperty(Component component);
  }

  public Assertion borderEquals(final Border[][] borders) {
    return new Assertion() {
      public void check() {
        assertCellPropertyEquals(borders, new ComponentPropertyAccessor() {
          public Object getProperty(Component component) {
            if (!JComponent.class.isInstance(component)) {
              throw new RuntimeException("Component '" + component.getClass() + "' does not support borders");
            }
            return ((JComponent)component).getBorder();
          }
        });
      }
    };
  }

  public int getColumnIndex(String columnName) {
    for (int columnIndex = 0; columnIndex < jTable.getColumnCount(); columnIndex++) {
      if (jTable.getColumnName(columnIndex).equalsIgnoreCase(columnName)) {
        return columnIndex;
      }
    }

    java.util.List<String> names = new ArrayList<String>();
    for (int columnIndex = 0; columnIndex < jTable.getColumnCount(); columnIndex++) {
      names.add(jTable.getColumnName(columnIndex));
    }
    AssertAdapter.fail("Column '" + columnName + "' not found - actual names: " + names);
    return -1;
  }

  public Assertion isEditable(final boolean[][] expected) {
    return new Assertion() {
      public void check() {
        Boolean[][] actual = new Boolean[jTable.getRowCount()][jTable.getColumnCount()];
        for (int i = 0; i < actual.length; i++) {
          Boolean[] row = actual[i];
          for (int j = 0; j < row.length; j++) {
            actual[i][j] = jTable.isCellEditable(i, j);
          }
        }
        ArrayUtils.assertEquals(ArrayUtils.toBooleanObjects(expected), actual);
      }
    };
  }

  public Assertion columnIsEditable(final int columnIndex, final boolean isEditable) {
    return new Assertion() {
      public void check() {
        for (int i = 0; i < jTable.getRowCount(); i++) {
          if (jTable.isCellEditable(i, columnIndex) != isEditable) {
            if (isEditable) {
              AssertAdapter.fail("Cell at row " + i + " is not editable");
            }
            else {
              AssertAdapter.fail("Cell at row " + i + " is editable");
            }
          }
        }
      }
    };
  }

  public Assertion cellIsEditable(final int rowIndex, final int columnIndex) {
    return new Assertion() {
      public void check() {
        AssertAdapter.assertTrue(jTable.isCellEditable(rowIndex, columnIndex));
      }
    };
  }

  public Assertion columnIsEditable(final String columnName, final boolean shouldBeEditable) {
    return columnIsEditable(getColumnIndex(columnName), shouldBeEditable);
  }

  public Assertion selectionIsEmpty() {
    return new Assertion() {
      public void check() {
        AssertAdapter.assertTrue("Selection is not empty", jTable.getSelectionModel().isSelectionEmpty());
      }
    };
  }

  /**
   * Checks the selection on a cell-by-cell basis.
   */
  public Assertion selectionEquals(final boolean[][] expected) {
    return new Assertion() {
      public void check() {
        int rowCount = expected.length;
        int columnCount = expected[0].length;
        Boolean[][] actual = new Boolean[rowCount][columnCount];
        if (jTable.getCellSelectionEnabled()) {
          for (int row = 0; row < rowCount; row++) {
            for (int column = 0; column < columnCount; column++) {
              actual[row][column] = jTable.isCellSelected(row, column);
            }
          }
        }
        else {
          for (int row = 0; row < rowCount; row++) {
            boolean isRowSelected = jTable.isRowSelected(row);
            for (int column = 0; column < columnCount; column++) {
              actual[row][column] = isRowSelected;
            }
          }
        }
        ArrayUtils.orderedCompare(ArrayUtils.toBooleanObjects(expected), actual);
      }
    };
  }

  public Assertion rowIsSelected(final int rowIndex) {
    return new Assertion() {
      public void check() {
        AssertAdapter.assertTrue(jTable.isRowSelected(rowIndex));
      }
    };
  }

  public Assertion cellIsSelected(final int rowIndex, final int columnIndex) {
    return new Assertion() {
      public void check() {
        if (!jTable.getCellSelectionEnabled()) {
          AssertAdapter.fail("Cell-level selection is not supported on this table");
        }
        AssertAdapter.assertTrue(jTable.isCellSelected(rowIndex, columnIndex));
      }
    };
  }

  public String toString() {
    return getContent();
  }

  private String getContent() {
    StringBuffer buffer = new StringBuffer();
    buffer.append('[');
    for (int row = 0, rowCount = jTable.getRowCount(); row < rowCount; row++) {
      if (row > 0) {
        buffer.append("\n ");
      }
      buffer.append('[');
      dumpRow(jTable, row, buffer, ",\t");
      buffer.append(']');
    }
    buffer.append(']');
    return buffer.toString();
  }

  private String getContent(String[] columnNames) {
    StringBuffer buffer = new StringBuffer();
    buffer.append('[');
    for (int row = 0, rowCount = jTable.getRowCount(); row < rowCount; row++) {
      if (row > 0) {
        buffer.append("\n ");
      }
      buffer.append('[');
      for (int col = 0, colCount = columnNames.length; col < colCount; col++) {
        buffer.append(getValueAt(row, getColumnIndex(columnNames[col])));
        if (col < (colCount - 1)) {
          buffer.append(",\t");
        }
      }
      buffer.append(']');
    }
    buffer.append(']');
    return buffer.toString();
  }

  private String getColumnContent(int column) {
    StringBuilder builder = new StringBuilder();
    builder.append("[");
    for (int row = 0; row < jTable.getRowCount(); row++) {
      builder.append(getContentAt(row, column));
      if (row < jTable.getRowCount() - 1) {
        builder.append(", ");
      }
    }
    builder.append("]");
    return builder.toString();
  }

  public Component getSwingEditorComponentAt(int row, int column) {
    jTable.editCellAt(row, column);
    return jTable.getEditorComponent();
  }

  public Component getSwingRendererComponentAt(int row, int column) {
    return jTable
      .getCellRenderer(row, column)
      .getTableCellRendererComponent(jTable,
                                     jTable.getValueAt(row, column),
                                     jTable.isCellSelected(row, column),
                                     false,
                                     row, column);
  }

  public void resizeColumn(String columnName, int width) {
    findColumn(columnName).setPreferredWidth(width);
  }

  public Assertion columnSizeEquals(final String columnName, final int expectedWidth) {
    return new Assertion() {
      public void check() {
        AssertAdapter.assertEquals(expectedWidth, findColumn(columnName).getPreferredWidth());
      }
    };
  }

  public Assertion columnSizeEquals(final int columnIndex, final int expectedWidth) {
    return new Assertion() {
      public void check() {
        AssertAdapter.assertEquals(expectedWidth,
                                   jTable.getColumnModel().getColumn(columnIndex).getPreferredWidth());
      }
    };
  }

  public Assertion rowsAreSelected(final int... rowIndexes) {
    return new Assertion() {
      public void check() {
        int[] actualSelection = jTable.getSelectedRows();
        Arrays.sort(actualSelection);
        int[] expectedSelection = rowIndexes.clone();
        Arrays.sort(expectedSelection);
        ArrayUtils.assertEquals(expectedSelection, actualSelection);
      }
    };
  }

  public void selectCell(int row, int column) {
    if (!jTable.getCellSelectionEnabled()) {
      AssertAdapter.fail("Individual cell selection is not allowed on this table");
    }
    jTable.setRowSelectionInterval(row, row);
    jTable.setColumnSelectionInterval(column, column);
  }

  public void selectAllRows() {
    final int rowCount = getRowCount();
    if (rowCount == 0) {
      return;
    }
    selectRowSpan(0, rowCount - 1);
  }

  public void selectRow(int row) {
    jTable.setRowSelectionInterval(row, row);
    if (jTable.getCellSelectionEnabled()) {
      jTable.setColumnSelectionInterval(0, jTable.getColumnCount() - 1);
    }
  }

  public void selectRows(int... rowIndexes) {
    jTable.getSelectionModel().setValueIsAdjusting(true);
    try {
      jTable.clearSelection();
      for (int row : rowIndexes) {
        jTable.addRowSelectionInterval(row, row);
      }
      if (jTable.getCellSelectionEnabled()) {
        jTable.setColumnSelectionInterval(0, jTable.getColumnCount() - 1);
      }
    }
    finally {
      jTable.getSelectionModel().setValueIsAdjusting(false);
    }
  }

  /**
   * Selects all rows where the content of the given column is one of the given labels.
   */
  public void selectRowsWithText(int column, String... labels) {
    Set<Integer> rows = new HashSet<Integer>();
    for (String label : labels) {
      int[] indices = getRowIndices(column, label);
      if (indices.length == 0) {
        throw new AssertionFailedError("Text '" + label + "' not found in column " + column + " - actual content: " + getColumnContent(column));
      }
      for (int index : indices) {
        rows.add(index);
      }
    }
    int[] selection = new int[rows.size()];
    int i = 0;
    for (Integer row : rows) {
      selection[i++] = row;
    }
    selectRows(selection);
  }

  public void selectRowSpan(int start, int end) {
    if (start > end) {
      throw new IllegalArgumentException("Invalid indexes: " + start + " > " + end);
    }
    jTable.setRowSelectionInterval(start, end);
  }

  public void selectBlock(int top, int left, int bottom, int right) {
    AssertAdapter.assertTrue("Only row-level selection is allowed on this table",
                             jTable.getCellSelectionEnabled());
    if ((top > bottom) && (left > right)) {
      throw new IllegalArgumentException("Invalid block definition - expected top <= bottom and left <= right");
    }
    jTable.setRowSelectionInterval(top, bottom);
    jTable.setColumnSelectionInterval(left, right);
  }

  public void addRowToSelection(int row) {
    jTable.addRowSelectionInterval(row, row);
    if (jTable.getCellSelectionEnabled()) {
      jTable.setColumnSelectionInterval(0, jTable.getColumnCount() - 1);
    }
  }

  public void removeRowFromSelection(int row) {
    AssertAdapter.assertTrue("Row " + row + " is not selected", jTable.isRowSelected(row));
    jTable.removeRowSelectionInterval(row, row);
  }

  public void clearSelection() {
    jTable.clearSelection();
  }

  /**
   * Asserts that the contents of the table starts with the specified rows.
   * This is useful for dealing with tables with hundreds of rows, as you can
   * just check that the first few are correct.
   */
  public Assertion startsWith(final Object[][] expectedFirstRows) {
    return new Assertion() {
      public void check() {
        int expectedLength = expectedFirstRows.length;
        checkLengthGreaterThan(expectedLength);
        for (int i = 0; i < expectedLength; i++) {
          rowEquals(i, expectedFirstRows[i]).check();
        }
      }
    };
  }

  /**
   * Asserts that the contents of the table ends with the specified rows.
   * This is useful for dealing with tables with hundreds of rows, as you can
   * just check that the last few are correct.
   */
  public Assertion endsWith(final Object[][] expectedEndRows) {
    return new Assertion() {
      public void check() {
        int expectedLength = expectedEndRows.length;
        checkLengthGreaterThan(expectedLength);
        for (int i = 0; i < expectedLength; i++) {
          rowEquals(i + getRowCount() - expectedLength, expectedEndRows[i]).check();
        }
      }
    };
  }

  /**
   * Checks that the table contains a complete row.
   */
  public Assertion containsRow(final Object[] expectedRow) {
    return new Assertion() {
      public void check() {
        for (int i = 0; i < getRowCount(); i++) {
          if (rowEquals(i, expectedRow).isTrue()) {
            return;
          }
        }
        AssertAdapter.fail("row " + ArrayUtils.toString(expectedRow) + " not found in table.");
      }
    };
  }

  /**
   * Checks that the table contains a row with a given cell.
   */
  public Assertion containsRow(final int columnIndex, final Object cellContent) {
    return new Assertion() {
      public void check() {
        int index = getRowIndex(columnIndex, cellContent);
        if (index < 0) {
          AssertAdapter.fail("No row found with '" + cellContent + "' in column " + columnIndex);
        }
      }
    };
  }

  public class Header {

    private Header() {
    }

    public String[] getColumnNames() {
      String[] columnNames = new String[jTable.getColumnCount()];
      for (int columnIndex = 0; columnIndex < jTable.getColumnCount(); columnIndex++) {
        columnNames[columnIndex] = jTable.getColumnName(columnIndex);
      }
      return columnNames;
    }

    public int findColumnIndex(String columnName) {
      return Table.this.getColumnIndex(columnName);
    }

    /**
     * Checks the column names.
     */
    public Assertion contentEquals(final String... expectedHeaders) {
      return new Assertion() {
        public void check() {
          checkHeader();
          try {
            AssertAdapter.assertEquals(expectedHeaders.length, jTable.getColumnCount());
            for (int i = 0; i < expectedHeaders.length; i++) {
              AssertAdapter.assertEquals(expectedHeaders[i], jTable.getColumnName(i));
            }
          }
          catch (Error e) {
            AssertAdapter.assertEquals(ArrayUtils.toString(expectedHeaders), ArrayUtils.toString(getColumnNames()));
            throw e;
          }
        }
      };
    }

    public Assertion contentEquals(final int countHeaderToCheck, final String... expectedHeaders) {
      return new Assertion() {
        public void check() {
          checkHeader();
          try {
            AssertAdapter.assertTrue(countHeaderToCheck <= jTable.getColumnCount());
            for (int i = 0; i < expectedHeaders.length; i++) {
              AssertAdapter.assertEquals(expectedHeaders[i], jTable.getColumnName(i));
            }
          }
          catch (Error e) {
            AssertAdapter.assertEquals(ArrayUtils.toString(expectedHeaders), ArrayUtils.toString(getColumnNames()));
            throw e;
          }
        }
      };
    }

    /**
     * Returns a string description of the default background color.
     */
    public String getDefaultBackground() {
      checkHeader();
      return ColorUtils.getColorDescription(jTable.getTableHeader().getBackground());
    }

    /**
     * Checks the background color on each column of the table header.
     */
    public Assertion backgroundEquals(final Object[] expectedColors) {
      return new Assertion() {
        public void check() {
          checkHeader();
          TableModel model = jTable.getModel();
          TableCellRenderer headerRenderer = jTable.getTableHeader().getDefaultRenderer();
          for (int columnIndex = 0; columnIndex < model.getColumnCount(); columnIndex++) {
            Component component =
              headerRenderer.getTableCellRendererComponent(jTable,
                                                           model.getColumnName(columnIndex),
                                                           false,
                                                           false,
                                                           -1,
                                                           columnIndex);
            ColorUtils.assertEquals("Unexpected color at column " + columnIndex,
                                    expectedColors[columnIndex], component.getBackground());
          }
        }
      };
    }

    public void click(int columnIndex) {
      checkHeader();
      JTableHeader tableHeader = jTable.getTableHeader();
      Mouse.doClickInRectangle(tableHeader, tableHeader.getHeaderRect(columnIndex), false, Key.Modifier.NONE);
    }

    public void click(String columnName) {
      checkHeader();
      JTableHeader tableHeader = jTable.getTableHeader();
      int columnIndex = findColumnIndex(columnName);
      Mouse.doClickInRectangle(tableHeader, tableHeader.getHeaderRect(columnIndex), false, Key.Modifier.NONE);
    }

    public Trigger triggerClick(final String columnName) {
      return new Trigger() {
        public void run() throws Exception {
          click(columnName);
        }
      };
    }

    public Trigger triggerClick(final int columnIndex) {
      return new Trigger() {
        public void run() throws Exception {
          click(columnIndex);
        }
      };
    }

    public void rightClick(int columnIndex) {
      checkHeader();
      JTableHeader tableHeader = jTable.getTableHeader();
      Mouse.doClickInRectangle(tableHeader, tableHeader.getHeaderRect(columnIndex), true, Key.Modifier.NONE);
    }

    public void rightClick(String columnName) {
      checkHeader();
      JTableHeader tableHeader = jTable.getTableHeader();
      int columnIndex = findColumnIndex(columnName);
      Mouse.doClickInRectangle(tableHeader, tableHeader.getHeaderRect(columnIndex), true, Key.Modifier.NONE);
    }

    public Trigger triggerRightClick(final int columnIndex) {
      return new Trigger() {
        public void run() throws Exception {
          rightClick(columnIndex);
        }
      };
    }

    public Trigger triggerRightClick(final String columnName) {
      return new Trigger() {
        public void run() throws Exception {
          rightClick(columnName);
        }
      };
    }

    private void checkHeader() {
      AssertAdapter.assertNotNull("The table contains no header", jTable.getTableHeader());
    }
  }

  private void checkColors(Object[][] colors, ComponentColorChecker colorChecker) {
    AssertAdapter.assertEquals(colors.length, jTable.getRowCount());
    for (int row = 0; row < colors.length; row++) {
      for (int col = 0; col < colors[row].length; col++) {
        colorChecker.check("Error at (" + row + ", " + col + ")", colors[row][col],
                           getSwingRendererComponentAt(row,  col));
      }
    }
  }

  private TableColumn findColumn(String columnName) {
    int columnIndex = getColumnIndex(columnName);
    if (columnIndex == -1) {
      throw new RuntimeException("Column '" + columnName + "' not found");
    }
    return jTable.getColumnModel().getColumn(columnIndex);
  }

  private void dumpRow(JTable table, int row, StringBuffer buffer, String separator) {
    for (int col = 0, colCount = table.getColumnCount(); col < colCount; col++) {
      buffer.append(getValueAt(row, col));
      if (col < (colCount - 1)) {
        buffer.append(separator);
      }
    }
  }

  private void dumpColumn(JTable table, int col, StringBuffer buffer, String separator) {
    for (int row = 0, rowCount = table.getRowCount(); row < rowCount; row++) {
      buffer.append(getValueAt(row, col));
      if (row < (rowCount - 1)) {
        buffer.append(separator);
      }
    }
  }

  private void checkRow(int rowIndex, Object[] expectedRow) {
    AssertAdapter.assertEquals(expectedRow.length, jTable.getColumnCount());
    for (int columnIndex = 0; columnIndex < expectedRow.length; columnIndex++) {
      checkValueAt(rowIndex, columnIndex, expectedRow[columnIndex]);
    }
  }

  private void checkRow(int rowIndex, int fromColumnIndex, int columnCount, Object[] expectedRow) {
    AssertAdapter.assertTrue(fromColumnIndex + columnCount <= jTable.getColumnCount());
    for (int columnIndex = fromColumnIndex; columnIndex < fromColumnIndex + columnCount; columnIndex++) {
      checkValueAt(rowIndex, columnIndex, expectedRow[columnIndex - fromColumnIndex]);
    }
  }

  private void checkColumn(int columnIndex, Object[] expectedColumn) {
    AssertAdapter.assertEquals(expectedColumn.length, jTable.getRowCount());
    for (int rowIndex = 0; rowIndex < expectedColumn.length; rowIndex++) {
      checkValueAt(rowIndex, columnIndex, expectedColumn[rowIndex]);
    }
  }

  private void checkValueAt(int rowIndex, int columnIndex, Object expectedValue) {
    AssertAdapter.assertEquals("Element at (" + rowIndex + ", " + columnIndex + ") does not match",
                               expectedValue,
                               getValueAt(rowIndex, columnIndex));
  }

  private Object getValueAt(int rowIndex, int columnIndex) {
    return getCellValueConverter(columnIndex).getValue(rowIndex, columnIndex,
                                                       getSwingRendererComponentAt(rowIndex, columnIndex),
                                                       jTable.getValueAt(rowIndex, columnIndex));
  }

  private TableCellValueConverter getCellValueConverter(int columnIndex) {
    TableCellValueConverter specificConverter = cellValuesConvertersByColumn.get(columnIndex);
    if (specificConverter != null) {
      return specificConverter;
    }
    return defaultCellValueConverter;
  }

  private void assertCellPropertyEquals(Object[][] properties, ComponentPropertyAccessor accessor) {
    AssertAdapter.assertEquals(properties.length, jTable.getRowCount());
    for (int row = 0; row < properties.length; row++) {
      for (int col = 0; col < properties[row].length; col++) {
        TableCellRenderer cellRenderer = jTable.getCellRenderer(row, col);
        Component component =
          cellRenderer.getTableCellRendererComponent(jTable,
                                                     jTable.getModel().getValueAt(row, col),
                                                     jTable.isCellSelected(row, col), false, row, col);
        AssertAdapter.assertEquals("Error at (" + row + ", " + col + ")",
                                   properties[row][col], accessor.getProperty(component));
      }
    }
  }

  private void checkLengthGreaterThan(int expectedLength) {
    AssertAdapter.assertTrue(lengthErrorMessage(expectedLength), getRowCount() > expectedLength);
  }

  private String lengthErrorMessage(int expectedLength) {
    return "Table contains only " + getRowCount() + " rows whereas " + expectedLength + " rows are expected.";
  }
}
TOP

Related Classes of org.uispec4j.Table

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.