001 /*
002 * Created on Jan 19, 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.awt.event.KeyEvent.VK_SHIFT;
019 import static java.util.Arrays.sort;
020 import static org.fest.assertions.Assertions.assertThat;
021 import static org.fest.assertions.Fail.fail;
022 import static org.fest.swing.awt.AWT.visibleCenterOf;
023 import static org.fest.swing.core.MouseButton.LEFT_BUTTON;
024 import static org.fest.swing.driver.CommonValidations.validateCellReader;
025 import static org.fest.swing.driver.JListContentQuery.contents;
026 import static org.fest.swing.driver.JListItemCountQuery.itemCountIn;
027 import static org.fest.swing.driver.JListItemIndexValidator.validateIndices;
028 import static org.fest.swing.driver.JListItemValueQuery.itemValue;
029 import static org.fest.swing.driver.JListMatchingItemQuery.centerOfMatchingItemCell;
030 import static org.fest.swing.driver.JListMatchingItemQuery.matchingItemIndex;
031 import static org.fest.swing.driver.JListMatchingItemQuery.matchingItemIndices;
032 import static org.fest.swing.driver.JListMatchingItemQuery.matchingItemValues;
033 import static org.fest.swing.driver.JListScrollToItemTask.ITEM_NOT_FOUND;
034 import static org.fest.swing.driver.JListScrollToItemTask.scrollToItem;
035 import static org.fest.swing.driver.JListScrollToItemTask.scrollToItemIfNotSelectedYet;
036 import static org.fest.swing.driver.JListSelectedIndexQuery.selectedIndexOf;
037 import static org.fest.swing.driver.JListSelectionIndicesQuery.selectedIndices;
038 import static org.fest.swing.driver.JListSelectionValueQuery.NO_SELECTION_VALUE;
039 import static org.fest.swing.driver.JListSelectionValueQuery.singleSelectionValue;
040 import static org.fest.swing.driver.JListSelectionValuesQuery.selectionValues;
041 import static org.fest.swing.driver.TextAssert.verifyThat;
042 import static org.fest.swing.edt.GuiActionRunner.execute;
043 import static org.fest.swing.util.Arrays.isEmptyIntArray;
044 import static org.fest.util.Arrays.format;
045 import static org.fest.util.Strings.concat;
046
047 import java.awt.Point;
048 import java.util.List;
049 import java.util.regex.Pattern;
050
051 import javax.swing.JList;
052 import javax.swing.JPopupMenu;
053
054 import org.fest.assertions.Description;
055 import org.fest.swing.annotation.RunsInEDT;
056 import org.fest.swing.cell.JListCellReader;
057 import org.fest.swing.core.MouseButton;
058 import org.fest.swing.core.Robot;
059 import org.fest.swing.edt.GuiQuery;
060 import org.fest.swing.edt.GuiTask;
061 import org.fest.swing.exception.ActionFailedException;
062 import org.fest.swing.exception.ComponentLookupException;
063 import org.fest.swing.exception.LocationUnavailableException;
064 import org.fest.swing.util.Pair;
065 import org.fest.swing.util.PatternTextMatcher;
066 import org.fest.swing.util.StringTextMatcher;
067 import org.fest.swing.util.TextMatcher;
068 import org.fest.swing.util.Range.From;
069 import org.fest.swing.util.Range.To;
070
071 /**
072 * Understands functional testing of <code>{@link JList}</code>s:
073 * <ul>
074 * <li>user input simulation</li>
075 * <li>state verification</li>
076 * <li>property value query</li>
077 * </ul>
078 * This class is intended for internal use only. Please use the classes in the package
079 * <code>{@link org.fest.swing.fixture}</code> in your tests.
080 *
081 * @author Alex Ruiz
082 * @author Yvonne Wang
083 */
084 public class JListDriver extends JComponentDriver {
085
086 private static final String SELECTED_INDICES_PROPERTY = "selectedIndices";
087 private static final String SELECTED_INDEX_PROPERTY = "selectedIndex";
088
089 private JListCellReader cellReader;
090
091 /**
092 * Creates a new </code>{@link JListDriver}</code>.
093 * @param robot the robot to use to simulate user input.
094 */
095 public JListDriver(Robot robot) {
096 super(robot);
097 cellReader(new BasicJListCellReader());
098 }
099
100 /**
101 * Returns an array of <code>String</code>s that represents the contents of the given <code>{@link JList}</code>,
102 * using this driver's <code>{@link JListCellReader}</code>.
103 * @param list the target <code>JList</code>.
104 * @return an array of <code>String</code>s that represents the contents of the given <code>JList</code>.
105 * @see #cellReader(JListCellReader)
106 */
107 @RunsInEDT
108 public String[] contentsOf(JList list) {
109 return contents(list, cellReader);
110 }
111
112 /**
113 * Selects the items matching the given values.
114 * @param list the target <code>JList</code>.
115 * @param values the values to match. Each <code>String</code> can be a regular expression.
116 * @throws NullPointerException if the given array is <code>null</code>.
117 * @throws IllegalArgumentException if the given array is empty.
118 * @throws IllegalStateException if the <code>JList</code> is disabled.
119 * @throws IllegalStateException if the <code>JList</code> is not showing on the screen.
120 * @throws LocationUnavailableException if an element matching the any of the given values cannot be found.
121 */
122 @RunsInEDT
123 public void selectItems(JList list, String[] values) {
124 selectItems(list, new StringTextMatcher(values));
125 }
126
127 /**
128 * Selects the items matching the given regular expression patterns.
129 * @param list the target <code>JList</code>.
130 * @param patterns the regular expression patterns to match.
131 * @throws NullPointerException if the given array is <code>null</code>.
132 * @throws NullPointerException if any of the regular expression patterns is <code>null</code>.
133 * @throws IllegalArgumentException if the given array is empty.
134 * @throws IllegalStateException if the <code>JList</code> is disabled.
135 * @throws IllegalStateException if the <code>JList</code> is not showing on the screen.
136 * @throws LocationUnavailableException if an element matching the any of the given regular expression patterns cannot
137 * be found.
138 * @since 1.2
139 */
140 @RunsInEDT
141 public void selectItems(JList list, Pattern[] patterns) {
142 selectItems(list, new PatternTextMatcher(patterns));
143 }
144
145 @RunsInEDT
146 private void selectItems(final JList list, final TextMatcher matcher) {
147 final List<Integer> indices = matchingItemIndices(list, matcher, cellReader);
148 if (indices.isEmpty()) throw failMatchingNotFound(list, matcher);
149 clearSelection(list);
150 new MultipleSelectionTemplate(robot) {
151 int elementCount() { return indices.size(); }
152 void selectElement(int index) { selectItem(list, indices.get(index)); }
153 }.multiSelect();
154 }
155
156 /**
157 * Selects the item in the given <code>{@link JList}</code> whose value matches the given one.
158 * @param list the target <code>JList</code>.
159 * @param value the value to match.
160 * @throws IllegalStateException if the <code>JList</code> is disabled.
161 * @throws IllegalStateException if the <code>JList</code> is not showing on the screen.
162 * @throws LocationUnavailableException if an element matching the given value cannot be found.
163 */
164 @RunsInEDT
165 public void selectItem(JList list, String value) {
166 selectItem(list, new StringTextMatcher(value));
167 }
168
169 /**
170 * Selects the item in the given <code>{@link JList}</code> whose value matches the given regular expression pattern.
171 * @param list the target <code>JList</code>.
172 * @param pattern the regular expression to match.
173 * @throws IllegalStateException if the <code>JList</code> is disabled.
174 * @throws IllegalStateException if the <code>JList</code> is not showing on the screen.
175 * @throws LocationUnavailableException if an element matching the given value cannot be found.
176 * @throws NullPointerException if the given regular expression pattern is <code>null</code>.
177 * @since 1.2
178 */
179 @RunsInEDT
180 public void selectItem(JList list, Pattern pattern) {
181 selectItem(list, new PatternTextMatcher(pattern));
182 }
183
184 @RunsInEDT
185 private void selectItem(JList list, TextMatcher matcher) {
186 Pair<Integer, Point> scrollInfo = scrollToItemIfNotSelectedYet(list, matcher, cellReader);
187 robot.waitForIdle();
188 verify(list, scrollInfo, matcher);
189 if (scrollInfo.ii == null) return; // already selected cell.
190 robot.click(list, cellCenterIn(scrollInfo));
191 }
192
193 /**
194 * Clicks the first item matching the given value, using the specified mouse button, the given number times.
195 * @param list the target <code>JList</code>.
196 * @param value the value to match.
197 * @param button the button to use.
198 * @param times the number of times to click.
199 * @throws IllegalStateException if the <code>JList</code> is disabled.
200 * @throws IllegalStateException if the <code>JList</code> is not showing on the screen.
201 * @throws LocationUnavailableException if an element matching the given value cannot be found.
202 */
203 public void clickItem(JList list, String value, MouseButton button, int times) {
204 clickItem(list, new StringTextMatcher(value), button, times);
205 }
206
207 /**
208 * Clicks the first item matching the given regular expression pattern, using the specified mouse button, the given
209 * number times.
210 * @param list the target <code>JList</code>.
211 * @param pattern the regular expression pattern to match.
212 * @param button the button to use.
213 * @param times the number of times to click.
214 * @throws IllegalStateException if the <code>JList</code> is disabled.
215 * @throws IllegalStateException if the <code>JList</code> is not showing on the screen.
216 * @throws NullPointerException if the given regular expression pattern is <code>null</code>.
217 * @throws LocationUnavailableException if an element matching the given regular expression pattern cannot be found.
218 * @since 1.2
219 */
220 public void clickItem(JList list, Pattern pattern, MouseButton button, int times) {
221 clickItem(list, new PatternTextMatcher(pattern), button, times);
222 }
223
224 private void clickItem(JList list, TextMatcher matcher, MouseButton button, int times) {
225 Pair<Integer, Point> scrollInfo = scrollToItem(list, matcher, cellReader);
226 robot.waitForIdle();
227 verify(list, scrollInfo, matcher);
228 robot.click(list, cellCenterIn(scrollInfo), button, times);
229 }
230
231 /**
232 * Selects the items under the given indices.
233 * @param list the target <code>JList</code>.
234 * @param indices the indices of the items to select.
235 * @throws NullPointerException if the given array is <code>null</code>.
236 * @throws IllegalArgumentException if the given array is empty.
237 * @throws IllegalStateException if the <code>JList</code> is disabled.
238 * @throws IllegalStateException if the <code>JList</code> is not showing on the screen.
239 * @throws IndexOutOfBoundsException if any of the indices is negative or greater than the index of the last item in
240 * the <code>JList</code>.
241 */
242 public void selectItems(final JList list, final int[] indices) {
243 validateArrayOfIndices(indices);
244 clearSelection(list);
245 new MultipleSelectionTemplate(robot) {
246 int elementCount() { return indices.length; }
247 void selectElement(int index) { selectItem(list, indices[index]); }
248 }.multiSelect();
249 }
250
251 /**
252 * Clears the selection in the given <code>{@link JList}</code>. Since this method does not simulate user input, it
253 * does not verifies that the <code>JList</code> is enabled and showing.
254 * @param list the target <code>JList</code>.
255 * @since 1.2
256 */
257 public void clearSelection(JList list) {
258 clearSelectionOf(list);
259 robot.waitForIdle();
260 }
261
262 @RunsInEDT
263 private static void clearSelectionOf(final JList list) {
264 execute(new GuiTask() {
265 protected void executeInEDT() {
266 list.clearSelection();
267 }
268 });
269 }
270
271 /**
272 * Selects the items in the specified range.
273 * @param list the target <code>JList</code>.
274 * @param from the starting point of the selection.
275 * @param to the last item to select.
276 * @throws IllegalStateException if the <code>JList</code> is disabled.
277 * @throws IllegalStateException if the <code>JList</code> is not showing on the screen.
278 * @throws IndexOutOfBoundsException if the any index is negative or greater than the index of the last item in the
279 * <code>JList</code>.
280 */
281 @RunsInEDT
282 public void selectItems(JList list, From from, To to) {
283 selectItems(list, from.value, to.value);
284 }
285
286 /**
287 * Selects the items in the specified range.
288 * @param list the target <code>JList</code>.
289 * @param start the starting point of the selection.
290 * @param end the last item to select (inclusive.)
291 * @throws IllegalStateException if the <code>JList</code> is disabled.
292 * @throws IllegalStateException if the <code>JList</code> is not showing on the screen.
293 * @throws IndexOutOfBoundsException if the any index is negative or greater than the index of the last item in the
294 * <code>JList</code>.
295 */
296 @RunsInEDT
297 public void selectItems(JList list, int start, int end) {
298 validateIndicesAndClearSelection(list, start, end);
299 selectItem(list, start);
300 robot.pressKey(VK_SHIFT);
301 try {
302 clickItem(list, end, LEFT_BUTTON, 1);
303 } finally {
304 robot.releaseKey(VK_SHIFT);
305 }
306 }
307
308 @RunsInEDT
309 private static void validateIndicesAndClearSelection(final JList list, final int...indices) {
310 execute(new GuiTask() {
311 protected void executeInEDT() {
312 validateIndices(list, indices);
313 list.clearSelection();
314 }
315 });
316 }
317
318 /**
319 * Selects the item under the given index using left mouse button once.
320 * @param list the target <code>JList</code>.
321 * @param index the index of the item to click.
322 * @throws IllegalStateException if the <code>JList</code> is disabled.
323 * @throws IllegalStateException if the <code>JList</code> is not showing on the screen.
324 * @throws IndexOutOfBoundsException if the given index is negative or greater than the index of the last item in the
325 * <code>JList</code>.
326 */
327 @RunsInEDT
328 public void selectItem(JList list, int index) {
329 Point cellCenter = scrollToItemIfNotSelectedYet(list, index);
330 robot.waitForIdle();
331 if (cellCenter == null) return; // cell already selected
332 robot.click(list, cellCenter);
333 }
334
335 /**
336 * Clicks the item under the given index, using the specified mouse button, the given number times.
337 * @param list the target <code>JList</code>.
338 * @param index the index of the item to click.
339 * @param button the button to use.
340 * @param times the number of times to click.
341 * @throws IllegalStateException if the <code>JList</code> is disabled.
342 * @throws IllegalStateException if the <code>JList</code> is not showing on the screen.
343 * @throws IndexOutOfBoundsException if the given index is negative or greater than the index of the last item in the
344 * <code>JList</code>.
345 */
346 @RunsInEDT
347 public void clickItem(JList list, int index, MouseButton button, int times) {
348 Point cellCenter = scrollToItem(list, index);
349 robot.waitForIdle();
350 robot.click(list, cellCenter, button, times);
351 }
352
353 /**
354 * Verifies that the selected item in the <code>{@link JList}</code> matches the given value.
355 * @param list the target <code>JList</code>.
356 * @param value the value to match. It can be a regular expression pattern.
357 * @throws AssertionError if the selected item does not match the value.
358 * @see #cellReader(JListCellReader)
359 */
360 @RunsInEDT
361 public void requireSelection(final JList list, String value) {
362 String selection = requiredSelection(list);
363 verifyThat(selection).as(selectedIndexProperty(list)).isEqualOrMatches(value);
364 }
365
366 /**
367 * Verifies that the selected item in the <code>{@link JList}</code> matches the given regular expression pattern.
368 * @param list the target <code>JList</code>.
369 * @param pattern the regular expression pattern to match.
370 * @throws AssertionError if the selected item does not match the given regular expression pattern.
371 * @throws NullPointerException if the given regular expression pattern is <code>null</code>.
372 * @see #cellReader(JListCellReader)
373 * @since 1.2
374 */
375 @RunsInEDT
376 public void requireSelection(JList list, Pattern pattern) {
377 String selection = requiredSelection(list);
378 verifyThat(selection).as(selectedIndexProperty(list)).matches(pattern);
379 }
380
381 private String requiredSelection(final JList list) {
382 Object selection = singleSelectionValue(list, cellReader);
383 if (NO_SELECTION_VALUE == selection) failNoSelection(list);
384 return (String)selection;
385 }
386
387 /**
388 * Verifies that the selected index in the <code>{@link JList}</code> matches the given value.
389 * @param list the target <code>JList</code>.
390 * @param index the selection index to match.
391 * @throws AssertionError if the selected index does not match the value.
392 * @since 1.2
393 */
394 @RunsInEDT
395 public void requireSelection(final JList list, int index) {
396 int selectedIndex = selectedIndexOf(list);
397 if (selectedIndex == -1) failNoSelection(list);
398 assertThat(selectedIndex).as(selectedIndexProperty(list)).isEqualTo(index);
399 }
400
401 /**
402 * Returns an array of <code>String</code>s that represents the selection in the given <code>{@link JList}</code>,
403 * using this driver's <code>{@link JListCellReader}</code>.
404 * @param list the target <code>JList</code>.
405 * @return an array of <code>String</code>s that represents the selection in the given <code>JList</code>.
406 * @see #cellReader(JListCellReader)
407 */
408 @RunsInEDT
409 public String[] selectionOf(JList list) {
410 List<String> selection = selectionValues(list, cellReader);
411 return selection.toArray(new String[selection.size()]);
412 }
413
414 /**
415 * Verifies that the selected items in the <code>{@link JList}</code> match the given values.
416 * @param list the target <code>JList</code>.
417 * @param items the values to match. Each value can be a regular expression pattern.
418 * @throws NullPointerException if the given array is <code>null</code>.
419 * @throws IllegalArgumentException if the given array is empty.
420 * @throws AssertionError if the selected items do not match the given values.
421 */
422 @RunsInEDT
423 public void requireSelectedItems(JList list, String... items) {
424 requireSelectedItems(list, new StringTextMatcher(items));
425 }
426
427 /**
428 * Verifies that the selected items in the <code>{@link JList}</code> match the given regular expression patterns.
429 * @param list the target <code>JList</code>.
430 * @param patterns the regular expression patterns to match.
431 * @throws NullPointerException if the given array is <code>null</code>.
432 * @throws IllegalArgumentException if the given array is empty.
433 * @throws NullPointerException if any of the patterns in the array is <code>null</code>.
434 * @throws AssertionError if the selected items do not match the given values.
435 * @see #cellReader(JListCellReader)
436 * @since 1.2
437 */
438 @RunsInEDT
439 public void requireSelectedItems(JList list, Pattern... patterns) {
440 requireSelectedItems(list, new PatternTextMatcher(patterns));
441 }
442
443 @RunsInEDT
444 private void requireSelectedItems(JList list, TextMatcher matcher) {
445 List<String> matchingValues = matchingItemValues(list, matcher, cellReader);
446 assertThat(selectionValues(list, cellReader)).as(propertyName(list, SELECTED_INDICES_PROPERTY)).isEqualTo(matchingValues);
447 }
448
449 /**
450 * Verifies that the given item indices are selected in the <code>{@link JList}</code>.
451 * @param list the target <code>JList</code>.
452 * @param indices the expected indices of the selected items.
453 * @throws NullPointerException if the given array is <code>null</code>.
454 * @throws IllegalArgumentException if the given array is empty.
455 * @throws AssertionError if the selection in the <code>JList</code> does not match the given one.
456 */
457 @RunsInEDT
458 public void requireSelectedItems(JList list, int... indices) {
459 validateArrayOfIndices(indices);
460 sort(indices);
461 assertThat(selectedIndices(list)).as(propertyName(list, SELECTED_INDICES_PROPERTY)).isEqualTo(indices);
462 }
463
464 private void validateArrayOfIndices(int[] indices) {
465 if (indices == null) throw new NullPointerException("The array of indices should not be null");
466 if (isEmptyIntArray(indices)) throw new IllegalArgumentException("The array of indices should not be empty");
467 }
468
469 /**
470 * Verifies that the <code>{@link JList}</code> does not have a selection.
471 * @param list the target <code>JList</code>.
472 * @throws AssertionError if the <code>JList</code> has a selection.
473 */
474 @RunsInEDT
475 public void requireNoSelection(JList list) {
476 assertThat(selectedIndexOf(list)).as(selectedIndexProperty(list)).isEqualTo(-1);
477 }
478
479 @RunsInEDT
480 private void failNoSelection(JList list) {
481 fail(concat("[", selectedIndexProperty(list).value(), "] No selection"));
482 }
483
484 @RunsInEDT
485 private Description selectedIndexProperty(JList list) {
486 return propertyName(list, SELECTED_INDEX_PROPERTY);
487 }
488
489 /**
490 * Starts a drag operation at the location of the first item matching the given value.
491 * @param list the target <code>JList</code>.
492 * @param value the value to match. It can be a regular expression.
493 * @throws IllegalStateException if the <code>JList</code> is disabled.
494 * @throws IllegalStateException if the <code>JList</code> is not showing on the screen.
495 * @throws LocationUnavailableException if an element matching the given value cannot be found.
496 * @see #cellReader(JListCellReader)
497 */
498 @RunsInEDT
499 public void drag(JList list, String value) {
500 drag(list, new StringTextMatcher(value));
501 }
502
503 /**
504 * Starts a drag operation at the location of the first item matching the given regular expression pattern.
505 * @param list the target <code>JList</code>.
506 * @param pattern the regular expression pattern to match.
507 * @throws IllegalStateException if the <code>JList</code> is disabled.
508 * @throws IllegalStateException if the <code>JList</code> is not showing on the screen.
509 * @throws NullPointerException if the regular expression pattern is <code>null</code>.
510 * @throws LocationUnavailableException if an element matching the given regular expression pattern cannot be found.
511 * @see #cellReader(JListCellReader)
512 * @since 1.2
513 */
514 @RunsInEDT
515 public void drag(JList list, Pattern pattern) {
516 drag(list, new PatternTextMatcher(pattern));
517 }
518
519 private void drag(JList list, TextMatcher matcher) {
520 Pair<Integer, Point> scrollInfo = scrollToItem(list, matcher, cellReader);
521 robot.waitForIdle();
522 verify(list, scrollInfo, matcher);
523 super.drag(list, cellCenterIn(scrollInfo));
524 }
525
526 /**
527 * Ends a drag operation at the location of the first item matching the given value.
528 * @param list the target <code>JList</code>.
529 * @param value the value to match. It can be a regular expression.
530 * @throws IllegalStateException if the <code>JList</code> is disabled.
531 * @throws IllegalStateException if the <code>JList</code> is not showing on the screen.
532 * @throws LocationUnavailableException if an element matching the given value cannot be found.
533 * @throws ActionFailedException if there is no drag action in effect.
534 */
535 @RunsInEDT
536 public void drop(JList list, String value) {
537 drop(list, new StringTextMatcher(value));
538 }
539
540 /**
541 * Ends a drag operation at the location of the first item matching the given regular expression pattern.
542 * @param list the target <code>JList</code>.
543 * @param pattern the regular expression pattern to match.
544 * @throws IllegalStateException if the <code>JList</code> is disabled.
545 * @throws IllegalStateException if the <code>JList</code> is not showing on the screen.
546 * @throws NullPointerException if the given regular expression pattern is <code>null</code>.
547 * @throws LocationUnavailableException if an element matching the given value cannot be found.
548 * @throws ActionFailedException if there is no drag action in effect.
549 * @since 1.2
550 */
551 public void drop(JList list, Pattern pattern) {
552 drop(list, new PatternTextMatcher(pattern));
553 }
554
555 private void drop(JList list, TextMatcher matcher) {
556 Pair<Integer, Point> scrollInfo = scrollToItem(list, matcher, cellReader);
557 robot.waitForIdle();
558 verify(list, scrollInfo, matcher);
559 super.drop(list, cellCenterIn(scrollInfo));
560 }
561
562 private void verify(JList list, Pair<Integer, Point> scrollInfo, TextMatcher matcher) {
563 if (ITEM_NOT_FOUND.equals(scrollInfo)) throw failMatchingNotFound(list, matcher);
564 }
565
566 /**
567 * Starts a drag operation at the location of the given index.
568 * @param list the target <code>JList</code>.
569 * @param index the given index.
570 * @throws IllegalStateException if the <code>JList</code> is disabled.
571 * @throws IllegalStateException if the <code>JList</code> is not showing on the screen.
572 * @throws IndexOutOfBoundsException if the given index is negative or greater than the index of the last item in the
573 * <code>JList</code>.
574 */
575 @RunsInEDT
576 public void drag(JList list, int index) {
577 Point cellCenter = scrollToItem(list, index);
578 robot.waitForIdle();
579 super.drag(list, cellCenter);
580 }
581
582 /**
583 * Ends a drag operation at the location of the given index.
584 * @param list the target <code>JList</code>.
585 * @param index the given index.
586 * @throws IllegalStateException if the <code>JList</code> is disabled.
587 * @throws IllegalStateException if the <code>JList</code> is not showing on the screen.
588 * @throws IndexOutOfBoundsException if the given index is negative or greater than the index of the last item in the
589 * <code>JList</code>.
590 * @throws ActionFailedException if there is no drag action in effect.
591 */
592 @RunsInEDT
593 public void drop(JList list, int index) {
594 Point cellCenter = scrollToItem(list, index);
595 robot.waitForIdle();
596 super.drop(list, cellCenter);
597 }
598
599
600 /**
601 * Ends a drag operation at the center of the <code>{@link JList}</code>.
602 * @param list the target <code>JList</code>.
603 * @throws IllegalStateException if the <code>JList</code> is disabled.
604 * @throws IllegalStateException if the <code>JList</code> is not showing on the screen.
605 * @throws ActionFailedException if there is no drag action in effect.
606 */
607 @RunsInEDT
608 public void drop(JList list) {
609 assertIsEnabledAndShowing(list);
610 super.drop(list, visibleCenterOf(list));
611 }
612
613 /**
614 * Shows a pop-up menu at the location of the specified item in the <code>{@link JList}</code>.
615 * @param list the target <code>JList</code>.
616 * @param value the value to match. It can be a regular expression pattern.
617 * @return a fixture that manages the displayed pop-up menu.
618 * @throws IllegalStateException if the <code>JList</code> is disabled.
619 * @throws IllegalStateException if the <code>JList</code> is not showing on the screen.
620 * @throws ComponentLookupException if a pop-up menu cannot be found.
621 * @throws LocationUnavailableException if an element matching the given value cannot be found.
622 */
623 @RunsInEDT
624 public JPopupMenu showPopupMenu(JList list, String value) {
625 return showPopupMenu(list, new StringTextMatcher(value));
626 }
627
628 /**
629 * Shows a pop-up menu at the location of the specified item in the <code>{@link JList}</code>.
630 * @param list the target <code>JList</code>.
631 * @param pattern the regular expression pattern to match.
632 * @return a fixture that manages the displayed pop-up menu.
633 * @throws IllegalStateException if the <code>JList</code> is disabled.
634 * @throws IllegalStateException if the <code>JList</code> is not showing on the screen.
635 * @throws NullPointerException if the regular expression pattern is <code>null</code>.
636 * @throws ComponentLookupException if a pop-up menu cannot be found.
637 * @throws LocationUnavailableException if an element matching the given value cannot be found.
638 * @since 1.2
639 */
640 @RunsInEDT
641 public JPopupMenu showPopupMenu(JList list, Pattern pattern) {
642 return showPopupMenu(list, new PatternTextMatcher(pattern));
643 }
644
645 @RunsInEDT
646 private JPopupMenu showPopupMenu(JList list, TextMatcher matcher) {
647 Pair<Integer, Point> scrollInfo = scrollToItem(list, matcher, cellReader);
648 robot.waitForIdle();
649 verify(list, scrollInfo, matcher);
650 return robot.showPopupMenu(list, cellCenterIn(scrollInfo));
651 }
652
653 private Point cellCenterIn(Pair<Integer, Point> scrollInfo) {
654 return scrollInfo.ii;
655 }
656
657 /**
658 * Shows a pop-up menu at the location of the specified item in the <code>{@link JList}</code>.
659 * @param list the target <code>JList</code>.
660 * @param index the index of the item.
661 * @return a driver that manages the displayed pop-up menu.
662 * @throws IllegalStateException if the <code>JList</code> is disabled.
663 * @throws IllegalStateException if the <code>JList</code> is not showing on the screen.
664 * @throws ComponentLookupException if a pop-up menu cannot be found.
665 * @throws IndexOutOfBoundsException if the given index is negative or greater than the index of the last item in the
666 * <code>JList</code>.
667 */
668 @RunsInEDT
669 public JPopupMenu showPopupMenu(JList list, int index) {
670 Point cellCenter = scrollToItem(list, index);
671 robot.waitForIdle();
672 return robot.showPopupMenu(list, cellCenter);
673 }
674
675 /**
676 * Returns the coordinates of the first item matching the given value.
677 * @param list the target <code>JList</code>.
678 * @param value the value to match.
679 * @return the coordinates of the item at the given item.
680 * @throws LocationUnavailableException if an element matching the given value cannot be found.
681 */
682 @RunsInEDT
683 public Point pointAt(JList list, String value) {
684 return centerOfMatchingItemCell(list, value, cellReader);
685 }
686
687 /**
688 * Returns the index of the first item matching the given value.
689 * @param list the target <code>JList</code>
690 * @param value the value to match. It can be a regular expression.
691 * @return the index of the first item matching the given value.
692 * @throws LocationUnavailableException if an element matching the given value cannot be found.
693 */
694 @RunsInEDT
695 public int indexOf(JList list, String value) {
696 return indexOf(list, new StringTextMatcher(value));
697 }
698
699 /**
700 * Returns the index of the first item matching the given regular expression pattern.
701 * @param list the target <code>JList</code>.
702 * @param pattern the regular expression pattern to match.
703 * @return the index of the first item matching the given regular expression pattern.
704 * @throws LocationUnavailableException if an element matching the given value cannot be found.
705 * @throws NullPointerException if the given regular expression pattern is <code>null</code>.
706 * @since 1.2
707 */
708 @RunsInEDT
709 public int indexOf(JList list, Pattern pattern) {
710 return indexOf(list, new PatternTextMatcher(pattern));
711 }
712
713 @RunsInEDT
714 private int indexOf(JList list, TextMatcher matcher) {
715 int index = itemIndex(list, matcher, cellReader);
716 if (index >= 0) return index;
717 throw failMatchingNotFound(list, matcher);
718 }
719
720 @RunsInEDT
721 private static int itemIndex(final JList list, final TextMatcher matcher, final JListCellReader cellReader) {
722 return execute(new GuiQuery<Integer>() {
723 protected Integer executeInEDT() {
724 return matchingItemIndex(list, matcher, cellReader);
725 }
726 });
727 }
728
729 private LocationUnavailableException failMatchingNotFound(JList list, TextMatcher matcher) {
730 throw new LocationUnavailableException(concat(
731 "Unable to find item matching the ", matcher.description(), " ", matcher.formattedValues(),
732 " among the JList contents ", format(contents(list, cellReader))));
733 }
734
735 /**
736 * Returns the <code>String</code> representation of the element under the given index, using this driver's
737 * <code>{@link JListCellReader}</code>.
738 * @param list the target <code>JList</code>.
739 * @param index the given index.
740 * @return the value of the element under the given index.
741 * @throws IndexOutOfBoundsException if the given index is negative or greater than the index of the last item in the
742 * <code>JList</code>.
743 * @see #cellReader(JListCellReader)
744 */
745 @RunsInEDT
746 public String value(JList list, int index) {
747 return itemValue(list, index, cellReader);
748 }
749
750 /**
751 * Updates the implementation of <code>{@link JListCellReader}</code> to use when comparing internal values of a
752 * <code>{@link JList}</code> and the values expected in a test.
753 * @param newCellReader the new <code>JListCellValueReader</code> to use.
754 * @throws NullPointerException if <code>newCellReader</code> is <code>null</code>.
755 */
756 public void cellReader(JListCellReader newCellReader) {
757 validateCellReader(newCellReader);
758 cellReader = newCellReader;
759 }
760
761 /**
762 * Verifies that number of items in the given <code>{@link JList}</code> is equal to the expected one.
763 * @param list the target <code>JList</code>.
764 * @param expected the expected number of items.
765 * @throws AssertionError if the number of items in the given <code>{@link JList}</code> is not equal to the expected
766 * one.
767 * @since 1.2
768 */
769 @RunsInEDT
770 public void requireItemCount(JList list, int expected) {
771 int actual = itemCountIn(list);
772 assertThat(actual).as(propertyName(list, "itemCount")).isEqualTo(expected);
773 }
774 }