001 /* 002 * Created on Jun 10, 2008 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 @2008-2010 the original author or authors. 015 */ 016 package org.fest.swing.driver; 017 018 import static java.lang.String.valueOf; 019 import static org.fest.swing.driver.ComponentStateValidator.validateIsEnabledAndShowing; 020 import static org.fest.swing.driver.JTableCancelCellEditingTask.cancelEditing; 021 import static org.fest.swing.driver.JTableCellEditorQuery.cellEditorIn; 022 import static org.fest.swing.driver.JTableCellValidator.validateCellIsEditable; 023 import static org.fest.swing.driver.JTableCellValidator.validateIndices; 024 import static org.fest.swing.driver.JTableStopCellEditingTask.stopEditing; 025 import static org.fest.swing.driver.JTableStopCellEditingTask.validateAndStopEditing; 026 import static org.fest.swing.edt.GuiActionRunner.execute; 027 import static org.fest.swing.exception.ActionFailedException.actionFailure; 028 import static org.fest.swing.timing.Pause.pause; 029 import static org.fest.util.Strings.concat; 030 031 import java.awt.Component; 032 import java.awt.Point; 033 034 import javax.swing.JTable; 035 import javax.swing.table.TableCellEditor; 036 037 import org.fest.swing.annotation.RunsInCurrentThread; 038 import org.fest.swing.annotation.RunsInEDT; 039 import org.fest.swing.cell.JTableCellWriter; 040 import org.fest.swing.core.*; 041 import org.fest.swing.edt.GuiQuery; 042 import org.fest.swing.exception.ActionFailedException; 043 import org.fest.swing.exception.WaitTimedOutError; 044 045 /** 046 * Understands the base class for implementations of <code>{@link JTableCellWriter}</code>. 047 * 048 * @author Alex Ruiz 049 * @author Yvonne Wang 050 */ 051 public abstract class AbstractJTableCellWriter implements JTableCellWriter { 052 053 protected final Robot robot; 054 protected final JTableLocation location = new JTableLocation(); 055 private TableCellEditor cellEditor; 056 057 private static final long EDITOR_LOOKUP_TIMEOUT = 5000; 058 059 public AbstractJTableCellWriter(Robot robot) { 060 this.robot = robot; 061 } 062 063 /** {@inheritDoc} */ 064 @RunsInEDT 065 public void cancelCellEditing(JTable table, int row, int column) { 066 if (cellEditor == null) { 067 doCancelCellEditing(table, row, column); 068 return; 069 } 070 doCancelCellEditing(); 071 } 072 073 @RunsInEDT 074 private void doCancelCellEditing(JTable table, int row, int column) { 075 cancelEditing(table, row, column); 076 robot.waitForIdle(); 077 } 078 079 @RunsInEDT 080 private void doCancelCellEditing() { 081 cancelEditing(cellEditor); 082 robot.waitForIdle(); 083 } 084 085 /** {@inheritDoc} */ 086 @RunsInEDT 087 public void stopCellEditing(JTable table, int row, int column) { 088 if (cellEditor == null) { 089 doStopCellEditing(table, row, column); 090 return; 091 } 092 doStopCellEditing(); 093 } 094 095 @RunsInEDT 096 private void doStopCellEditing(JTable table, int row, int column) { 097 validateAndStopEditing(table, row, column); 098 robot.waitForIdle(); 099 } 100 101 @RunsInEDT 102 private void doStopCellEditing() { 103 stopEditing(cellEditor); 104 robot.waitForIdle(); 105 } 106 107 /** 108 * Returns the editor for the given table cell. This method is executed in the EDT. 109 * @param table the target <code>JTable</code>. 110 * @param row the row index of the cell. 111 * @param column the column index of the cell. 112 * @return the editor for the given table cell. 113 */ 114 @RunsInEDT 115 protected static TableCellEditor cellEditor(final JTable table, final int row, final int column) { 116 return execute(new GuiQuery<TableCellEditor>() { 117 protected TableCellEditor executeInEDT() throws Throwable { 118 return table.getCellEditor(row, column); 119 } 120 }); 121 } 122 123 /** 124 * Scrolls the given <code>{@link JTable}</code> to the given cell. 125 * <p> 126 * <b>Note:</b> This method is <b>not</b> guaranteed to be executed in the event dispatch thread (EDT.) Clients are 127 * responsible for calling this method from the EDT. 128 * </p> 129 * @param table the target <code>JTable</code>. 130 * @param row the row index of the cell. 131 * @param column the column index of the cell. 132 * @param location understands how to get the bounds of the given cell. 133 */ 134 @RunsInCurrentThread 135 protected static void scrollToCell(JTable table, int row, int column, JTableLocation location) { 136 table.scrollRectToVisible(location.cellBounds(table, row, column)); 137 } 138 139 /** {@inheritDoc} */ 140 @RunsInEDT 141 public Component editorForCell(JTable table, int row, int column) { 142 return cellEditorComponent(table, row, column); 143 } 144 145 @RunsInEDT 146 private static Component cellEditorComponent(final JTable table, final int row, final int column) { 147 return execute(new GuiQuery<Component>() { 148 protected Component executeInEDT() { 149 validateIndices(table, row, column); 150 return cellEditorIn(table, row, column); 151 } 152 }); 153 } 154 155 /** 156 * Finds the component used as editor for the given <code>{@link JTable}</code>. 157 * <p> 158 * <b>Note:</b> This method is <b>not</b> guaranteed to be executed in the event dispatch thread (EDT.) Clients are 159 * responsible for calling this method from the EDT. 160 * </p> 161 * @param <T> the generic type of the supported editor type. 162 * @param table the target <code>JTable</code>. 163 * @param row the row index of the cell. 164 * @param column the column index of the cell. 165 * @param supportedType the type of component we expect as editor. 166 * @return the component used as editor for the given table cell. 167 * @throws IndexOutOfBoundsException if any of the indices is out of bounds or if the <code>JTable</code> does not 168 * have any rows. 169 * @throws IllegalStateException if the <code>JTable</code> is disabled. 170 * @throws IllegalStateException if the <code>JTable</code> is not showing on the screen. 171 * @throws IllegalStateException if the table cell in the given coordinates is not editable. 172 * @throws IndexOutOfBoundsException if any of the indices is out of bounds or if the <code>JTable</code> does not 173 * have any rows. 174 * @throws ActionFailedException if an editor for the given cell cannot be found or cannot be activated. 175 */ 176 @RunsInCurrentThread 177 protected static <T extends Component> T editor(JTable table, int row, int column, Class<T> supportedType) { 178 validate(table, row, column); 179 Component editor = cellEditorIn(table, row, column); 180 if (supportedType.isInstance(editor)) return supportedType.cast(editor); 181 throw cannotFindOrActivateEditor(row, column); 182 } 183 184 /** 185 * Returns the location of the given table cell. 186 * @param table the target <code>JTable</code>. 187 * @param row the row index of the cell. 188 * @param column the column index of the cell. 189 * @param location knows how to get the location of a table cell. 190 * @return the location of the given table cell. 191 * @throws IllegalStateException if the <code>JTable</code> is disabled. 192 * @throws IllegalStateException if the <code>JTable</code> is not showing on the screen. 193 * @throws IndexOutOfBoundsException if any of the indices is out of bounds or if the <code>JTable</code> does not 194 * have any rows. 195 * @throws IllegalStateException if the table cell in the given coordinates is not editable. 196 */ 197 @RunsInEDT 198 protected static Point cellLocation(final JTable table, final int row, final int column, final JTableLocation location) { 199 return execute(new GuiQuery<Point>() { 200 protected Point executeInEDT() { 201 validate(table, row, column); 202 scrollToCell(table, row, column, location); 203 return location.pointAt(table, row, column); 204 } 205 }); 206 } 207 208 /** 209 * Validates that: 210 * <ol> 211 * <li>the given <code>JTable</code> is enabled and showing on the screen</li> 212 * <li>the row and column indices are correct (not out of bounds)</li> 213 * <li>the table cell at the given indices is editable</li> 214 * </ol> 215 * <p> 216 * <b>Note:</b> This method is <b>not</b> guaranteed to be executed in the event dispatch thread (EDT.) Clients are 217 * responsible for calling this method from the EDT. 218 * </p> 219 * @param table the target <code>JTable</code>. 220 * @param row the row index of the cell. 221 * @param column the column index of the cell. 222 * @throws IllegalStateException if the <code>JTable</code> is disabled. 223 * @throws IllegalStateException if the <code>JTable</code> is not showing on the screen. 224 * @throws IndexOutOfBoundsException if any of the indices is out of bounds or if the <code>JTable</code> does not 225 * have any rows. 226 * @throws IllegalStateException if the table cell in the given coordinates is not editable. 227 */ 228 @RunsInCurrentThread 229 protected static void validate(final JTable table, final int row, final int column) { 230 validateIndices(table, row, column); 231 validateIsEnabledAndShowing(table); 232 validateCellIsEditable(table, row, column); 233 } 234 235 /** 236 * Waits until the editor of the given table cell is showing on the screen. Component lookup is performed by type. 237 * @param <T> the generic type of the cell editor. 238 * @param table the target <code>JTable</code>. 239 * @param row the row index of the cell. 240 * @param column the column index of the cell. 241 * @param supportedType the type of component we expect as editor. 242 * @return the editor of the given table cell once it is showing on the screen. 243 * @throws ActionFailedException if an editor for the given cell cannot be found or cannot be activated. 244 */ 245 @RunsInEDT 246 protected final <T extends Component> T waitForEditorActivation(JTable table, int row, 247 int column, Class<T> supportedType) { 248 return waitForEditorActivation(new TypeMatcher(supportedType, true), table, row, column, supportedType); 249 } 250 251 /** 252 * Waits until the editor of the given table cell is showing on the screen. 253 * @param <T> the generic type of the cell editor. 254 * @param matcher the condition that the cell editor to look for needs to satisfy. 255 * @param table the target <code>JTable</code>. 256 * @param row the row index of the cell. 257 * @param column the column index of the cell. 258 * @param supportedType the type of component we expect as editor. 259 * @return the editor of the given table cell once it is showing on the screen. 260 * @throws ActionFailedException if an editor for the given cell cannot be found or cannot be activated. 261 */ 262 @RunsInEDT 263 protected final <T extends Component> T waitForEditorActivation(ComponentMatcher matcher, JTable table, int row, 264 int column, Class<T> supportedType) { 265 ComponentFoundCondition condition = new ComponentFoundCondition("", robot.finder(), matcher, table); 266 try { 267 pause(condition, EDITOR_LOOKUP_TIMEOUT); 268 } catch (WaitTimedOutError e) { 269 throw cannotFindOrActivateEditor(row, column); 270 } 271 return supportedType.cast(condition.found()); 272 } 273 274 /** 275 * Throws a <code>{@link ActionFailedException}</code> if this <code>{@link JTableCellWriter}</code> could not find or 276 * activate the cell editor of the supported type. 277 * @param row the row index of the cell. 278 * @param column the column index of the cell. 279 * @return the thrown exception. 280 */ 281 protected static ActionFailedException cannotFindOrActivateEditor(int row, int column) { 282 String msg = concat("Unable to find or activate editor for cell [", valueOf(row), ",", valueOf(column), "]"); 283 throw actionFailure(msg); 284 } 285 286 287 /** 288 * Returns the cell editor being currently used. This method will return <code>null</code> if no table cell is being 289 * currently edited. 290 * @return the cell editor being currently used, or <code>null</code> if no table cell is being currently edited. 291 */ 292 protected final TableCellEditor cellEditor() { return cellEditor; } 293 294 /** 295 * Sets the cell editor being currently used. 296 * @param newCellEditor the cell editor being currently used. 297 */ 298 protected final void cellEditor(TableCellEditor newCellEditor) { 299 cellEditor = newCellEditor; 300 } 301 }