001    /*
002     * Created on Sep 10, 2007
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 @2007-2010 the original author or authors.
015     */
016    package org.fest.swing.fixture;
017    
018    import static org.fest.swing.core.MouseButton.LEFT_BUTTON;
019    import static org.fest.swing.core.MouseButton.RIGHT_BUTTON;
020    
021    import java.awt.Component;
022    import java.util.regex.Pattern;
023    
024    import javax.swing.JTable;
025    
026    import org.fest.swing.cell.JTableCellReader;
027    import org.fest.swing.cell.JTableCellWriter;
028    import org.fest.swing.core.MouseButton;
029    import org.fest.swing.core.MouseClickInfo;
030    import org.fest.swing.data.TableCell;
031    import org.fest.swing.driver.JTableDriver;
032    import org.fest.swing.exception.ActionFailedException;
033    import org.fest.swing.exception.ComponentLookupException;
034    
035    /**
036     * Understands functional testing of single cells in <code>{@link JTable}</code>s:
037     * <ul>
038     * <li>user input simulation</li>
039     * <li>state verification</li>
040     * <li>property value query</li>
041     * </ul>
042     * <p>
043     * Example:
044     * <pre>
045     * // import static org.fest.swing.data.TableCell.row;
046     * {@link JTableCellFixture} cell = dialog.{@link JTableFixture table}("records").cell({@link TableCell#row(int) row}(3).column(0));
047     * cell.select().showPopupMenu();
048     * </pre>
049     * </p>
050     *
051     * @author Alex Ruiz
052     * @author Yvonne Wang
053     *
054     * @see TableCell
055     */
056    public class JTableCellFixture implements ItemFixture {
057    
058      private final JTableFixture table;
059      private final TableCell cell;
060    
061      /**
062       * Creates a new <code>{@link JTableCellFixture}</code>.
063       * @param table handles the <code>JTable</code> containing the cell in this fixture.
064       * @param cell row and column indices of the table cell to be managed by this fixture.
065       * @throws NullPointerException if <code>table</code> is <code>null</code>.
066       * @throws NullPointerException if <code>cell</code> is <code>null</code>.
067       */
068      protected JTableCellFixture(JTableFixture table, TableCell cell) {
069        validateNotNull(table);
070        validateNotNull(cell);
071        this.table = table;
072        this.cell = cell;
073      }
074    
075      private void validateNotNull(JTableFixture newTable) {
076        if (newTable == null) throw new NullPointerException("The JTableFixture should not be null");
077      }
078    
079      private void validateNotNull(TableCell newCell) {
080        if (newCell == null) throw new NullPointerException("The TableCell should not be null");
081      }
082    
083      JTableFixture table() { return table; }
084      TableCell cell() { return cell; }
085    
086      /**
087       * Simulates a user selecting this fixture's table cell.
088       * @return this fixture.
089       * @throws IllegalStateException if this fixture's <code>JTable</code> is disabled.
090       * @throws IllegalStateException if this fixture's <code>JTable</code> is not showing on the screen.
091       */
092      public JTableCellFixture select() {
093        table.selectCell(cell);
094        return this;
095      }
096    
097      /**
098       * Simulates a user clicking this fixture's table cell.
099       * @return this fixture.
100       * @throws IllegalStateException if this fixture's <code>JTable</code> is disabled.
101       * @throws IllegalStateException if this fixture's <code>JTable</code> is not showing on the screen.
102       */
103      public JTableCellFixture click() {
104        table.click(cell, LEFT_BUTTON);
105        return this;
106      }
107    
108      /**
109       * Simulates a user clicking this fixture's table cell.
110       * @param mouseClickInfo specifies the button to click and the times the button should be clicked.
111       * @return this fixture.
112       * @throws NullPointerException if the given <code>MouseClickInfo</code> is <code>null</code>.
113       * @throws IllegalStateException if this fixture's <code>JTable</code> is disabled.
114       * @throws IllegalStateException if this fixture's <code>JTable</code> is not showing on the screen.
115       */
116      public JTableCellFixture click(MouseClickInfo mouseClickInfo) {
117        table.click(cell, mouseClickInfo);
118        return this;
119      }
120    
121      /**
122       * Simulates a user double-clicking this fixture's table cell.
123       * @return this fixture.
124       * @throws IllegalStateException if this fixture's <code>JTable</code> is disabled.
125       * @throws IllegalStateException if this fixture's <code>JTable</code> is not showing on the screen.
126       */
127      public JTableCellFixture doubleClick() {
128        return click(LEFT_BUTTON, 2);
129      }
130    
131      /**
132       * Simulates a user right-clicking this fixture's table cell.
133       * @return this fixture.
134       * @throws IllegalStateException if this fixture's <code>JTable</code> is disabled.
135       * @throws IllegalStateException if this fixture's <code>JTable</code> is not showing on the screen.
136       */
137      public JTableCellFixture rightClick() {
138        return click(RIGHT_BUTTON);
139      }
140    
141      /**
142       * Simulates a user clicking a cell in this fixture's table cell once, using the specified mouse button.
143       * @param button the mouse button to use.
144       * @return this fixture.
145       * @throws NullPointerException if the given <code>MouseButton</code> is <code>null</code>.
146       * @throws IllegalStateException if this fixture's <code>JTable</code> is disabled.
147       * @throws IllegalStateException if this fixture's <code>JTable</code> is not showing on the screen.
148       */
149      public JTableCellFixture click(MouseButton button) {
150        table.click(cell, button);
151        return this;
152      }
153    
154      private JTableCellFixture click(MouseButton button, int times) {
155        table.click(cell, button, times);
156        return this;
157      }
158    
159      /**
160       * Starts editing this fixture's table cell. This method should be called <strong>before</strong> manipulating the
161       * <code>{@link Component}</code> returned by <code>{@link #editor()}</code>.
162       * <p>
163       * This method uses the <code>{@link JTableCellWriter}</code> from the <code>{@link JTableFixture}</code> that
164       * created this fixture.
165       * </p>
166       * @return this fixture.
167       * @throws IllegalStateException if this fixture's <code>JTable</code> is disabled.
168       * @throws IllegalStateException if this fixture's <code>JTable</code> is not showing on the screen.
169       * @throws IllegalStateException if this cell is not editable.
170       * @throws IndexOutOfBoundsException if any of the indices (row and column) is out of bounds.
171       * @throws ActionFailedException if this writer is unable to handle the underlying cell editor.
172       * @see JTableFixture#cellWriter(JTableCellWriter)
173       * @see JTableCellWriter
174       * @see #editor()
175       */
176      public JTableCellFixture startEditing() {
177        driver().startCellEditing(target(), cell);
178        return this;
179      }
180    
181      /**
182       * Stops editing this fixture's table cell. This method should be called <strong>after</strong> manipulating the
183       * <code>{@link Component}</code> returned by <code>{@link #editor()}</code>.
184       * <p>
185       * This method uses the <code>{@link JTableCellWriter}</code> from the <code>{@link JTableFixture}</code> that
186       * created this fixture.
187       * </p>
188       * @return this fixture.
189       * @throws IllegalStateException if this fixture's <code>JTable</code> is disabled.
190       * @throws IllegalStateException if this fixture's <code>JTable</code> is not showing on the screen.
191       * @throws IllegalStateException if this cell is not editable.
192       * @throws IndexOutOfBoundsException if any of the indices (row and column) is out of bounds.
193       * @throws ActionFailedException if this writer is unable to handle the underlying cell editor.
194       * @see JTableFixture#cellWriter(JTableCellWriter)
195       * @see JTableCellWriter
196       * @see #editor()
197       */
198      public JTableCellFixture stopEditing() {
199        driver().stopCellEditing(target(), cell);
200        return this;
201      }
202    
203      /**
204       * Cancels editing this fixture's table cell. This method should be called <strong>after</strong> manipulating the
205       * <code>{@link Component}</code> returned by <code>{@link #editor()}</code>.
206       * <p>
207       *
208       * <pre>
209       * TableCellFixture cell = table.cell(row(6).column(8));
210       * Component editor = cell.editor();
211       * // assume editor is a JTextField
212       * JTextComponentFixture editorFixture = new JTextComponentFixture(robot, (JTextField) editor);
213       * cell.{@link #startEditing()};
214       * editorFixture.enterText(&quot;Hello&quot;);
215       * // discard any entered value
216       * cell.cancelEditing();
217       * </pre>
218       *
219       * </p>
220       * <p>
221       * This method uses the <code>{@link JTableCellWriter}</code> from the <code>{@link JTableFixture}</code> that
222       * created this fixture.
223       * </p>
224       * @return this fixture.
225       * @throws IllegalStateException if this fixture's <code>JTable</code> is disabled.
226       * @throws IllegalStateException if this fixture's <code>JTable</code> is not showing on the screen.
227       * @throws IllegalStateException if this cell is not editable.
228       * @throws IndexOutOfBoundsException if any of the indices (row and column) is out of bounds.
229       * @throws ActionFailedException if this writer is unable to handle the underlying cell editor.
230       * @see JTableFixture#cellWriter(JTableCellWriter)
231       * @see JTableCellWriter
232       * @see #editor()
233       */
234      public JTableCellFixture cancelEditing() {
235        driver().cancelCellEditing(target(), cell);
236        return this;
237      }
238    
239      /**
240       * Returns the editor of this fixture's table cell. To manipulate the editor (e.g. wrapping it with a
241       * <code>ComponentFixture</code>,) the method <code>{@link #startEditing()}</code> should be called first. To
242       * apply any changes back to the table cell, the method <code>{@link #stopEditing()}</code> should be called. This
243       * method uses the <code>{@link JTableCellWriter}</code> from the <code>{@link JTableFixture}</code> that created
244       * this fixture.
245       * <p>
246       * Example:
247       *
248       * <pre>
249       * TableCellFixture cell = table.cell(row(6).column(8));
250       * Component editor = cell.editor();
251       * // assume editor is a JTextField
252       * JTextComponentFixture editorFixture = new JTextComponentFixture(robot, (JTextField) editor);
253       * cell.{@link #startEditing()};
254       * editorFixture.enterText(&quot;Hello&quot;);
255       * cell.{@link #stopEditing()};
256       * </pre>
257       *
258       * </p>
259       * @return the editor of this fixture's table cell.
260       * @see JTableFixture#cellWriter(JTableCellWriter)
261       * @see JTableCellWriter
262       */
263      public Component editor() {
264        return driver().cellEditor(target(), cell);
265      }
266    
267      /**
268       * Enters the given value to this fixture's table cell. This method starts cell edition, enters the given value and
269       * stops cell edition. To change the value of a cell, only a call to this method is necessary. If you need more
270       * flexibility, you can retrieve the cell editor with <code>{@link #editor()}</code>.
271       * <p>
272       * This method uses the <code>{@link JTableCellWriter}</code> from the <code>{@link JTableFixture}</code> that
273       * created this fixture.
274       * </p>
275       * @param value the value to enter in the cell.
276       * @return this fixture.
277       * @throws IllegalStateException if this fixture's <code>JTable</code> is disabled.
278       * @throws IllegalStateException if this fixture's <code>JTable</code> is not showing on the screen.
279       * @throws IllegalStateException if this cell is not editable.
280       * @throws IndexOutOfBoundsException if any of the indices (row and column) is out of bounds.
281       * @throws ActionFailedException if this driver's <code>JTableCellValueReader</code> is unable to enter the given
282       * value.
283       * @see JTableFixture#cellWriter(JTableCellWriter)
284       * @see JTableCellWriter
285       */
286      public JTableCellFixture enterValue(String value) {
287        driver().enterValueInCell(target(), cell, value);
288        return this;
289      }
290    
291      private JTableDriver driver() { return table.driver(); }
292      private JTable target() { return table.target; }
293    
294      /**
295       * Asserts that the value of this fixture's table cell matches the given value.
296       * @param value the expected value of this fixture's table cell. It can be a regular expression.
297       * @return this fixture.
298       * @throws AssertionError if the value of this fixture's table cell does not match the expected one.
299       */
300      public JTableCellFixture requireValue(String value) {
301        table.requireCellValue(cell, value);
302        return this;
303      }
304    
305      /**
306       * Asserts that the value of this fixture's table cell matches the given regular expression pattern.
307       * @param pattern the regular expression pattern to match.
308       * @return this fixture.
309       * @throws NullPointerException if the given regular expression pattern is <code>null</code>.
310       * @throws AssertionError if the value of this fixture's table cell does not match the expected the given regular
311       * expression pattern.
312       * @since 1.2
313       */
314      public JTableCellFixture requireValue(Pattern pattern) {
315        table.requireCellValue(cell, pattern);
316        return this;
317      }
318    
319      /**
320       * Returns a fixture that verifies the font of this fixture's table cell. This method uses the
321       * <code>{@link JTableCellReader}</code> from the <code>{@link JTableFixture}</code> that created this fixture.
322       * @return a fixture that verifies the font of this fixture's table cell.
323       * @see JTableFixture#cellReader(JTableCellReader)
324       * @see JTableCellReader
325       */
326      public FontFixture font() {
327        return table.fontAt(cell);
328      }
329    
330      /**
331       * Returns a fixture that verifies the background color of this fixture's table cell. This method uses the
332       * <code>{@link JTableCellReader}</code> from the <code>{@link JTableFixture}</code> that created this fixture.
333       * @return a fixture that verifies the background color of this fixture's table cell.
334       * @see JTableFixture#cellReader(JTableCellReader)
335       * @see JTableCellReader
336       */
337      public ColorFixture background() {
338        return table.backgroundAt(cell);
339      }
340    
341      /**
342       * Returns a fixture that verifies the foreground color of this fixture's table cell. This method uses the
343       * <code>{@link JTableCellReader}</code> from the <code>{@link JTableFixture}</code> that created this fixture.
344       * @return a fixture that verifies the foreground color of this fixture's table cell.
345       * @see JTableFixture#cellReader(JTableCellReader)
346       * @see JTableCellReader
347       */
348      public ColorFixture foreground() {
349        return table.foregroundAt(cell);
350      }
351    
352      /**
353       * Returns the <code>String</code> representation of the value of this fixture's table cell. This method uses the
354       * <code>{@link JTableCellReader}</code> from the <code>{@link JTableFixture}</code> that created this fixture.
355       * @return the <code>String</code> representation of the value of this fixture's table cell.
356       * @see JTableFixture#cellReader(JTableCellReader)
357       * @see JTableCellReader
358       */
359      public String value() {
360        return table.valueAt(cell);
361      }
362    
363      /**
364       * Simulates a user dragging this fixture's table cell.
365       * @return this fixture.
366       */
367      public JTableCellFixture drag() {
368        table.drag(cell);
369        return this;
370      }
371    
372      /**
373       * Simulates a user dropping into this fixture's table cell.
374       * @return this fixture.
375       */
376      public JTableCellFixture drop() {
377        table.drop(cell);
378        return this;
379      }
380    
381      /**
382       * Shows a pop-up menu using this fixture's table cell as the invoker of the pop-up menu.
383       * @return a fixture that manages the displayed pop-up menu.
384       * @throws ComponentLookupException if a pop-up menu cannot be found.
385       */
386      public JPopupMenuFixture showPopupMenu() {
387        return table.showPopupMenuAt(cell);
388      }
389    
390      /**
391       * Asserts that this fixture's table cell is editable.
392       * @return this fixture.
393       * @throws AssertionError if this fixture's table cell is not editable.
394       */
395      public JTableCellFixture requireEditable() {
396        table.requireEditable(cell);
397        return this;
398      }
399    
400    
401      /**
402       * Asserts that this fixture's table cell is not editable.
403       * @return this fixture.
404       * @throws AssertionError if this fixture's table cell is editable.
405       */
406      public JTableCellFixture requireNotEditable() {
407        table.requireNotEditable(cell);
408        return this;
409      }
410    
411      /**
412       * Returns the row index of this fixture's table cell.
413       * @return the row index of this fixture's table cell.
414       */
415      public int row() { return cell.row; }
416    
417      /**
418       * Returns the column index of this fixture's table cell.
419       * @return the column index of this fixture's table cell.
420       */
421      public int column() { return cell.column; }
422    
423    }