1 /* 2 * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 package org.netbeans.jemmy; 24 25 import java.awt.Component; 26 import java.awt.Frame; 27 import java.awt.Window; 28 import java.util.stream.Stream; 29 30 /** 31 * A WindowWaiter is a utility class used to look or wait for Windows. It 32 * contains methods to search for a Window among the currently showing Windows 33 * as well as methods that wait for a Window to show within an allotted time 34 * period. 35 * 36 * Searches and waits always involve search criteria applied by a 37 * ComponentChooser instance. Searches and waits can both be restricted to 38 * windows owned by a given window. 39 * 40 * <BR>Timeouts used: <BR> 41 * WindowWaiter.WaitWindowTimeout - time to wait window displayed <BR> 42 * WindowWaiter.AfterWindowTimeout - time to sleep after window has been 43 * dispayed <BR> 44 * 45 * @see org.netbeans.jemmy.Timeouts 46 * 47 * @author Alexandre Iline (alexandre.iline@oracle.com) 48 */ 49 public class WindowWaiter extends Waiter<Window, Void> implements Timeoutable { 50 51 public static boolean FIND_INVISIBLE_WINDOWS = false; 52 53 private final static long WAIT_TIME = 60000; 54 private final static long AFTER_WAIT_TIME = 0; 55 56 private ComponentChooser chooser; 57 private Window owner = null; 58 private int index = 0; 59 private Timeouts timeouts; 60 61 /** 62 * Constructor. 63 */ 64 public WindowWaiter() { 65 super(); 66 setTimeouts(JemmyProperties.getProperties().getTimeouts()); 67 } 68 69 /** 70 * Searches for a window. The search proceeds among the currently showing 71 * windows for the {@code index+1}'th window that is both owned by the 72 * {@code java.awt.Window} {@code owner} and that meets the 73 * criteria defined and applied by the {@code ComponentChooser} 74 * parameter. 75 * 76 * @param owner The owner window of all the windows to be searched. 77 * @param cc A component chooser used to define and apply the search 78 * criteria. 79 * @param index The ordinal index of the window in the set of currently 80 * displayed windows with the proper window ownership and a suitable title. 81 * The first index is 0. 82 * @return a reference to the {@code index+1}'th window that is 83 * showing, has the proper window ownership, and that meets the search 84 * criteria. If there are fewer than {@code index+1} windows, a 85 * {@code null} reference is returned. 86 */ 87 public static Window getWindow(Window owner, ComponentChooser cc, int index) { 88 return getAWindow(owner, new IndexChooser(cc, index)); 89 } 90 91 /** 92 * Searches for a window. Search among the currently showing windows for the 93 * first that is both owned by the {@code java.awt.Window} 94 * {@code owner} and that meets the search criteria applied by the 95 * {@code ComponentChooser} parameter. 96 * 97 * @param owner The owner window of the windows to be searched. 98 * @param cc A component chooser used to define and apply the search 99 * criteria. 100 * @return a reference to the first window that is showing, has a proper 101 * owner window, and that meets the search criteria. If no such window can 102 * be found, a {@code null} reference is returned. 103 */ 104 public static Window getWindow(Window owner, ComponentChooser cc) { 105 return getWindow(owner, cc, 0); 106 } 107 108 /** 109 * Searches for a window. The search proceeds among the currently showing 110 * windows for the {@code index+1}'th window that meets the criteria 111 * defined and applied by the {@code ComonentChooser} parameter. 112 * 113 * @param cc A component chooser used to define and apply the search 114 * criteria. 115 * @param index The ordinal index of the window in the set of currently 116 * displayed windows. The first index is 0. 117 * @return a reference to the {@code index+1}'th window that is showing 118 * and that meets the search criteria. If there are fewer than 119 * {@code index+1} windows, a {@code null} reference is returned. 120 */ 121 public static Window getWindow(ComponentChooser cc, int index) { 122 return getAWindow(new IndexChooser(cc, index)); 123 } 124 125 /** 126 * Searches for a window. Search among the currently showing windows for one 127 * that meets the search criteria applied by the 128 * {@code ComponentChooser} parameter. 129 * 130 * @param cc A component chooser used to define and apply the search 131 * criteria. 132 * @return a reference to the first window that is showing and that meets 133 * the search criteria. If no such window can be found, a {@code null} 134 * reference is returned. 135 */ 136 public static Window getWindow(ComponentChooser cc) { 137 return getWindow(cc, 0); 138 } 139 140 static { 141 Timeouts.initDefault("WindowWaiter.WaitWindowTimeout", WAIT_TIME); 142 Timeouts.initDefault("WindowWaiter.AfterWindowTimeout", AFTER_WAIT_TIME); 143 } 144 145 /** 146 * Defines current timeouts. 147 * 148 * @param timeouts A collection of timeout assignments. 149 * @see org.netbeans.jemmy.Timeoutable 150 * @see org.netbeans.jemmy.Timeouts 151 * @see #getTimeouts 152 */ 153 @Override 154 public void setTimeouts(Timeouts timeouts) { 155 this.timeouts = timeouts; 156 Timeouts times = timeouts.cloneThis(); 157 times.setTimeout("Waiter.WaitingTime", 158 timeouts.getTimeout("WindowWaiter.WaitWindowTimeout")); 159 times.setTimeout("Waiter.AfterWaitingTime", 160 timeouts.getTimeout("WindowWaiter.AfterWindowTimeout")); 161 setWaitingTimeOrigin("WindowWaiter.WaitWindowTimeout"); 162 super.setTimeouts(times); 163 } 164 165 /** 166 * Return current timeouts. 167 * 168 * @return the collection of current timeout assignments. 169 * @see org.netbeans.jemmy.Timeoutable 170 * @see org.netbeans.jemmy.Timeouts 171 * @see #setTimeouts 172 */ 173 @Override 174 public Timeouts getTimeouts() { 175 return timeouts; 176 } 177 178 /** 179 * Action producer--get a window. Get a window. The search uses constraints 180 * on window ownership, ordinal index, and search criteria defined by an 181 * instance of {@code org.netbeans.jemmy.ComponentChooser}. 182 * 183 * @param obj Not used. 184 * @return the window waited upon. If a window cannot be found then a 185 * {@code null} reference is returned. 186 * @see org.netbeans.jemmy.Action 187 */ 188 @Override 189 public Window actionProduced(Void obj) { 190 return WindowWaiter.getWindow(owner, chooser, index); 191 } 192 193 /** 194 * Waits for a window to show. Wait for the {@code index+1}'th window 195 * that meets the criteria defined and applied by the 196 * {@code ComonentChooser} parameter to show up. 197 * 198 * @param ch A component chooser used to define and apply the search 199 * criteria. 200 * @param index The ordinal index of the window in the set of currently 201 * displayed windows. The first index is 0. 202 * @return a reference to the {@code index+1}'th window that shows and 203 * that meets the search criteria. If fewer than {@code index+1} 204 * windows show up in the allotted time period then a {@code null} 205 * reference is returned. 206 * @throws TimeoutExpiredException 207 * @see #actionProduced(Object) 208 * @exception InterruptedException 209 */ 210 public Window waitWindow(ComponentChooser ch, int index) 211 throws InterruptedException { 212 chooser = ch; 213 owner = null; 214 this.index = index; 215 return waitWindow(); 216 } 217 218 /** 219 * Waits for a window to show. Wait for a window that meets the search 220 * criteria applied by the {@code ComponentChooser} parameter to show 221 * up. 222 * 223 * @param ch A component chooser used to define and apply the search 224 * criteria. 225 * @return a reference to the first window that shows and that meets the 226 * search criteria. If no such window can be found within the time period 227 * allotted, a {@code null} reference is returned. 228 * @throws TimeoutExpiredException 229 * @see #actionProduced(Object) 230 * @exception InterruptedException 231 */ 232 public Window waitWindow(ComponentChooser ch) 233 throws InterruptedException { 234 return waitWindow(ch, 0); 235 } 236 237 /** 238 * Waits for a window to show. Wait for the {@code index+1}'th window 239 * to show that is both owned by the {@code java.awt.Window} 240 * {@code o} and that meets the criteria defined and applied by the 241 * {@code ComponentChooser} parameter. 242 * 243 * @param o The owner window of all the windows to be searched. 244 * @param ch A component chooser used to define and apply the search 245 * criteria. 246 * @param index The ordinal index of the window in the set of currently 247 * displayed windows with the proper window ownership and a suitable title. 248 * The first index is 0. 249 * @return a reference to the {@code index+1}'th window to show that 250 * has the proper window ownership, and that meets the search criteria. If 251 * there are fewer than {@code index+1} windows, a {@code null} 252 * reference is returned. 253 * @throws TimeoutExpiredException 254 * @see #actionProduced(Object) 255 * @exception InterruptedException 256 */ 257 public Window waitWindow(Window o, ComponentChooser ch, int index) 258 throws InterruptedException { 259 owner = o; 260 chooser = ch; 261 this.index = index; 262 return waitAction(null); 263 } 264 265 /** 266 * Waits for a window to show. Wait for the first window to show that is 267 * both owned by the {@code java.awt.Window} {@code o} and that 268 * meets the criteria defined and applied by the 269 * {@code ComponentChooser} parameter. 270 * 271 * @param o The owner window of all the windows to be searched. 272 * @param ch A component chooser used to define and apply the search 273 * criteria. 274 * @return a reference to the first window to show that has the proper 275 * window ownership, and that meets the search criteria. If there is no such 276 * window, a {@code null} reference is returned. 277 * @throws TimeoutExpiredException 278 * @see #actionProduced(Object) 279 * @exception InterruptedException 280 */ 281 public Window waitWindow(Window o, ComponentChooser ch) 282 throws InterruptedException { 283 return waitWindow(o, ch, 0); 284 } 285 286 /** 287 * Wait till the count of windows which meet the the search criteria becomes 288 * equal to count. 289 * 290 * @param ch a component chooser used to define and apply the search 291 * criteria. 292 * @param count the number of expected windows meeting the search criteria. 293 * @throws InterruptedException 294 */ 295 public static void waitWindowCount(ComponentChooser ch, int count) 296 throws InterruptedException { 297 waitWindowCount(null, ch, count); 298 } 299 300 /** 301 * Wait till the count of windows which meet the the search criteria becomes 302 * equal to count. 303 * 304 * @param owner The owner window of all the windows to be checked 305 * @param ch a component chooser used to define and apply the search 306 * criteria. 307 * @param count the number of expected windows meeting the search criteria. 308 * @throws InterruptedException 309 */ 310 public static void waitWindowCount(Window owner, ComponentChooser ch, int count) 311 throws InterruptedException { 312 Waiter<String, Void> stateWaiter = new Waiter<>(new Waitable<String, Void>() { 313 @Override 314 public String actionProduced(Void obj) { 315 return countWindows(owner, ch) == count ? "" : null; 316 } 317 318 @Override 319 public String getDescription() { 320 return "Wait till the count of windows matching the criteria " 321 + "specified by ComponentChooser reaches :" + count; 322 } 323 324 @Override 325 public String toString() { 326 return "Operator.waitState.Waitable{description = " 327 + getDescription() + '}'; 328 } 329 }); 330 stateWaiter.waitAction(null); 331 } 332 333 /** 334 * Counts all the windows owned by the owner window which match the 335 * criterion specified by component chooser. 336 * 337 * @param owner The owner window of all the windows to be checked 338 * @param ch A component chooser used to define and apply the search 339 * criteria 340 * @return the number of matched windows 341 */ 342 public static int countWindows(Window owner, ComponentChooser ch) { 343 return new QueueTool().invokeAndWait(new QueueTool.QueueAction<Integer>(null) { 344 345 @Override 346 public Integer launch() { 347 Window[] windows; 348 if (owner == null) { 349 windows = Window.getWindows(); 350 } else { 351 windows = owner.getOwnedWindows(); 352 } 353 return (int) Stream.of(windows) 354 .filter(x -> ch.checkComponent(x)).count(); 355 } 356 }); 357 } 358 359 /** 360 * Counts all the windows which match the criterion specified by component 361 * chooser. 362 * 363 * @param ch A component chooser used to define and apply the search 364 * criteria 365 * @return the number of matched windows 366 */ 367 public static int countWindows(ComponentChooser ch) { 368 return countWindows(null, ch); 369 } 370 371 @Override 372 public String getDescription() { 373 return chooser.getDescription(); 374 } 375 376 @Override 377 public String toString() { 378 return "WindowWaiter{" + "chooser=" + chooser + ", owner=" + owner + ", index=" + index + '}'; 379 } 380 381 /** 382 * Method can be used by a subclass to define chooser. 383 * 384 * @param ch a chooser specifying searching criteria. 385 * @see #getComponentChooser 386 */ 387 protected void setComponentChooser(ComponentChooser ch) { 388 chooser = ch; 389 } 390 391 /** 392 * Method can be used by a subclass to define chooser. 393 * 394 * @return a chooser specifying searching criteria. 395 * @see #setComponentChooser 396 */ 397 protected ComponentChooser getComponentChooser() { 398 return chooser; 399 } 400 401 /** 402 * Method can be used by a subclass to define window owner. 403 * 404 * @param owner Window-owner of the set of windows. 405 * @see #getOwner 406 */ 407 protected void setOwner(Window owner) { 408 this.owner = owner; 409 } 410 411 /** 412 * Method can be used by a subclass to define window owner. 413 * 414 * @return Window-owner of the set of windows. 415 * @see #setOwner 416 */ 417 protected Window getOwner() { 418 return owner; 419 } 420 421 /** 422 * @see org.netbeans.jemmy.Waiter#getWaitingStartedMessage() 423 */ 424 @Override 425 protected String getWaitingStartedMessage() { 426 return "Start to wait window \"" + chooser.getDescription() + "\" opened"; 427 } 428 429 /** 430 * Overrides Waiter.getTimeoutExpiredMessage. 431 * 432 * @see org.netbeans.jemmy.Waiter#getTimeoutExpiredMessage(long) 433 * @param timeSpent time from waiting start (milliseconds) 434 * @return a message. 435 */ 436 @Override 437 protected String getTimeoutExpiredMessage(long timeSpent) { 438 return ("Window \"" + chooser.getDescription() + "\" has not been opened in " 439 + timeSpent + " milliseconds"); 440 } 441 442 /** 443 * Overrides Waiter.getActionProducedMessage. 444 * 445 * @see org.netbeans.jemmy.Waiter#getActionProducedMessage(long, Object) 446 * @param timeSpent time from waiting start (milliseconds) 447 * @param result result of Waitable.actionproduced method. 448 * @return a message. 449 */ 450 @Override 451 protected String getActionProducedMessage(long timeSpent, final Object result) { 452 String resultToString; 453 if (result instanceof Component) { 454 // run toString in dispatch thread 455 resultToString = new QueueTool().invokeSmoothly( 456 new QueueTool.QueueAction<String>("result.toString()") { 457 @Override 458 public String launch() { 459 return result.toString(); 460 } 461 } 462 ); 463 } else { 464 resultToString = result.toString(); 465 } 466 return ("Window \"" + chooser.getDescription() + "\" has been opened in " 467 + timeSpent + " milliseconds" 468 + "\n " + resultToString); 469 } 470 471 /** 472 * @return a message. 473 * @see org.netbeans.jemmy.Waiter#getGoldenWaitingStartedMessage() 474 */ 475 @Override 476 protected String getGoldenWaitingStartedMessage() { 477 return "Start to wait window \"" + chooser.getDescription() + "\" opened"; 478 } 479 480 /** 481 * @return a message. 482 * @see org.netbeans.jemmy.Waiter#getGoldenTimeoutExpiredMessage() 483 */ 484 @Override 485 protected String getGoldenTimeoutExpiredMessage() { 486 return "Window \"" + chooser.getDescription() + "\" has not been opened"; 487 } 488 489 /** 490 * @return a message. 491 * @see org.netbeans.jemmy.Waiter#getGoldenActionProducedMessage() 492 */ 493 @Override 494 protected String getGoldenActionProducedMessage() { 495 return "Window \"" + chooser.getDescription() + "\" has been opened"; 496 } 497 498 private static Window getAWindow(Window owner, ComponentChooser cc) { 499 if (owner == null) { 500 return WindowWaiter.getAWindow(cc); 501 } else { 502 Window result = null; 503 Window[] windows = owner.getOwnedWindows(); 504 for (Window window : windows) { 505 if (cc.checkComponent(window)) { 506 return window; 507 } 508 if ((result = WindowWaiter.getWindow(window, cc)) != null) { 509 return result; 510 } 511 } 512 return null; 513 } 514 } 515 516 private static Window getAWindow(ComponentChooser cc) { 517 Window result = null; 518 Frame[] frames = Frame.getFrames(); 519 for (Frame frame : frames) { 520 if (cc.checkComponent(frame)) { 521 return frame; 522 } 523 if ((result = WindowWaiter.getWindow(frame, cc)) != null) { 524 return result; 525 } 526 } 527 return null; 528 } 529 530 private Window waitWindow() 531 throws InterruptedException { 532 return waitAction(null); 533 } 534 535 private static class IndexChooser implements ComponentChooser { 536 537 private int curIndex = 0; 538 private int index; 539 private ComponentChooser chooser; 540 541 public IndexChooser(ComponentChooser ch, int i) { 542 index = i; 543 chooser = ch; 544 curIndex = 0; 545 } 546 547 @Override 548 public boolean checkComponent(Component comp) { 549 if ((FIND_INVISIBLE_WINDOWS || (comp.isShowing() && comp.isVisible())) 550 && chooser.checkComponent(comp)) { 551 if (curIndex == index) { 552 return true; 553 } 554 curIndex++; 555 } 556 return false; 557 } 558 559 @Override 560 public String getDescription() { 561 return chooser.getDescription(); 562 } 563 564 @Override 565 public String toString() { 566 return "IndexChooser{" + "curIndex=" + curIndex + ", index=" + index + ", chooser=" + chooser + '}'; 567 } 568 } 569 }