001 /* 002 * Created on Dec 28, 2009 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 005 * in compliance with the License. You may obtain a copy of the License at 006 * 007 * http://www.apache.org/licenses/LICENSE-2.0 008 * 009 * Unless required by applicable law or agreed to in writing, software distributed under the License 010 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 011 * or implied. See the License for the specific language governing permissions and limitations under 012 * the License. 013 * 014 * Copyright @2009-2010 the original author or authors. 015 */ 016 package org.fest.swing.data; 017 018 import static org.fest.swing.edt.GuiActionRunner.execute; 019 import static org.fest.swing.exception.ActionFailedException.actionFailure; 020 import static org.fest.util.Arrays.format; 021 import static org.fest.util.Objects.areEqual; 022 import static org.fest.util.Strings.concat; 023 024 import javax.swing.JTable; 025 026 import org.fest.swing.annotation.RunsInCurrentThread; 027 import org.fest.swing.annotation.RunsInEDT; 028 import org.fest.swing.cell.JTableCellReader; 029 import org.fest.swing.edt.GuiQuery; 030 import org.fest.swing.exception.ActionFailedException; 031 032 /** 033 * Understands lookup of a cell in the first row in <code>{@link JTable}</code> whose values match the given ones. 034 * <p> 035 * Example: 036 * <pre> 037 * // import static org.fest.swing.data.TableCellInSelectedRow.row; 038 * <code>{@link TableCell}</code> cell = dialog.table("records").cell({@link TableCellInRowByValue#rowWithValue(String...) rowWithValue}("column1", "column2", "column3").column(2)); 039 * </pre> 040 * </p> 041 * 042 * @author Alex Ruiz 043 * 044 * @since 1.2 045 */ 046 public class TableCellInRowByValue implements TableCellFinder { 047 048 /** 049 * Starting point for the creation of a <code>{@link TableCellInRowByValue}</code>. 050 * <p> 051 * Example: 052 * <pre> 053 * // import static org.fest.swing.data.TableCellInRowByValue.rowWithValue; 054 * TableCellByColumnId cell = rowWithValue("column1", "column2", "column3").column(3); 055 * </pre> 056 * </p> 057 * @param values the values in the cells of the row we are looking for. 058 * @return the created builder. 059 * @throws NullPointerException if the given array of values is <code>null</code>. 060 */ 061 public static TableCellBuilder rowWithValue(String...values) { 062 if (values == null) throw new NullPointerException("The array of values should not be null"); 063 return new TableCellBuilder(values); 064 } 065 066 /** 067 * Understands creation of <code>{@link TableCellInSelectedRow}</code>s. 068 * 069 * @author Alex Ruiz 070 */ 071 public static class TableCellBuilder { 072 private final String[] values; 073 074 /** 075 * Creates a new </code>{@link TableCellBuilder}</code>. 076 * @param values the values of the cells of the row to find. 077 */ 078 TableCellBuilder(String[] values) { 079 this.values = values; 080 } 081 082 /** 083 * Creates a new table cell finder using the row cell values specified in 084 * <code>{@link TableCellInRowByValue#rowWithValue(String...)}</code> 085 * and the column index specified as the argument in this method. 086 * @param column the index of the column in the table cell to find. 087 * @return the created finder. 088 */ 089 public TableCellInRowByValue column(int column) { 090 return new TableCellInRowByValue(values, column); 091 } 092 } 093 094 private final String[] values; 095 private final int column; 096 097 /** 098 * Creates a new </code>{@link TableCellInRowByValue}</code>. 099 * @param values the values in the cells of the row we are looking for. 100 * @param column the index of the column in the table cell to find. 101 */ 102 protected TableCellInRowByValue(String[] values, int column) { 103 this.values = values; 104 this.column = column; 105 } 106 107 /** 108 * Finds a cell in the given <code>{@link JTable}</code> that: 109 * <ol> 110 * <li>is located in the first row whose values match the given ones</li> 111 * <li>has a matching row index</li> 112 * </ol> 113 * @param table the target <code>JTable</code>. 114 * @param cellReader knows how to read the contents of a cell in a <code>JTable</code>. 115 * @return the cell found, if any. 116 * @throws IllegalStateException if the size of values to look up is not equal to the number of columns in the given 117 * <code>JTable</code>. 118 * @throws ActionFailedException if a matching cell could not be found. 119 */ 120 @RunsInEDT 121 public TableCell findCell(JTable table, JTableCellReader cellReader) { 122 int row = findRowIndex(table, cellReader, values); 123 if (row == -1) 124 throw actionFailure(concat("Unable to find a row with values:<", format(values), ">")); 125 return new TableCell(row, column); 126 } 127 128 @RunsInEDT 129 private static int findRowIndex(final JTable table, final JTableCellReader cellReader, final String[] values) { 130 return execute(new GuiQuery<Integer>() { 131 protected Integer executeInEDT() { 132 validateEqualSize(table, values); 133 int rowCount = table.getRowCount(); 134 for (int row = 0; row < rowCount; row++) 135 if (matchingRow(table, cellReader, values, row)) return row; 136 return -1; 137 } 138 139 }); 140 } 141 142 @RunsInCurrentThread 143 private static void validateEqualSize(final JTable table, final String[] values) { 144 int columnCount = table.getColumnCount(); 145 if (values.length != columnCount) 146 throw new IllegalStateException(concat("The array of values should have size:<", columnCount,">")); 147 } 148 149 @RunsInCurrentThread 150 private static boolean matchingRow(JTable table, JTableCellReader cellReader, String[] values, int row) { 151 int columnCount = table.getColumnCount(); 152 for (int col = 0; col < columnCount; col++) { 153 if (!areEqual(cellReader.valueAt(table, row, col), values[col])) return false; 154 } 155 return true; 156 } 157 158 @Override public String toString() { 159 return concat(getClass().getName(), "[values=", format(values), ", column=", column, "]"); 160 } 161 }