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 }