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 
  29 /**
  30  * A WindowWaiter is a utility class used to look or wait for Windows. It
  31  * contains methods to search for a Window among the currently showing Windows
  32  * as well as methods that wait for a Window to show within an allotted time
  33  * period.
  34  *
  35  * Searches and waits always involve search criteria applied by a
  36  * ComponentChooser instance. Searches and waits can both be restricted to
  37  * windows owned by a given window.
  38  *
  39  * <BR>Timeouts used: <BR>
  40  * WindowWaiter.WaitWindowTimeout - time to wait window displayed <BR>
  41  * WindowWaiter.AfterWindowTimeout - time to sleep after window has been
  42  * dispayed <BR>
  43  *
  44  * @see org.netbeans.jemmy.Timeouts
  45  *
  46  * @author Alexandre Iline (alexandre.iline@oracle.com)
  47  */
  48 public class WindowWaiter extends Waiter<Window, Void> implements Timeoutable {
  49 
  50     public static boolean FIND_INVISIBLE_WINDOWS = false;
  51 
  52     private final static long WAIT_TIME = 60000;
  53     private final static long AFTER_WAIT_TIME = 0;
  54 
  55     private ComponentChooser chooser;
  56     private Window owner = null;
  57     private int index = 0;
  58     private Timeouts timeouts;
  59 
  60     /**
  61      * Constructor.
  62      */
  63     public WindowWaiter() {
  64         super();
  65         setTimeouts(JemmyProperties.getProperties().getTimeouts());
  66     }
  67 
  68     /**
  69      * Searches for a window. The search proceeds among the currently showing
  70      * windows for the {@code index+1}'th window that is both owned by the
  71      * {@code java.awt.Window} {@code owner} and that meets the
  72      * criteria defined and applied by the {@code ComponentChooser}
  73      * parameter.
  74      *
  75      * @param owner The owner window of all the windows to be searched.
  76      * @param cc A component chooser used to define and apply the search
  77      * criteria.
  78      * @param index The ordinal index of the window in the set of currently
  79      * displayed windows with the proper window ownership and a suitable title.
  80      * The first index is 0.
  81      * @return a reference to the {@code index+1}'th window that is
  82      * showing, has the proper window ownership, and that meets the search
  83      * criteria. If there are fewer than {@code index+1} windows, a
  84      * {@code null} reference is returned.
  85      */
  86     public static Window getWindow(Window owner, ComponentChooser cc, int index) {
  87         return getAWindow(owner, new IndexChooser(cc, index));
  88     }
  89 
  90     /**
  91      * Searches for a window. Search among the currently showing windows for the
  92      * first that is both owned by the {@code java.awt.Window}
  93      * {@code owner} and that meets the search criteria applied by the
  94      * {@code ComponentChooser} parameter.
  95      *
  96      * @param owner The owner window of the windows to be searched.
  97      * @param cc A component chooser used to define and apply the search
  98      * criteria.
  99      * @return a reference to the first window that is showing, has a proper
 100      * owner window, and that meets the search criteria. If no such window can
 101      * be found, a {@code null} reference is returned.
 102      */
 103     public static Window getWindow(Window owner, ComponentChooser cc) {
 104         return getWindow(owner, cc, 0);
 105     }
 106 
 107     /**
 108      * Searches for a window. The search proceeds among the currently showing
 109      * windows for the {@code index+1}'th window that meets the criteria
 110      * defined and applied by the {@code ComonentChooser} parameter.
 111      *
 112      * @param cc A component chooser used to define and apply the search
 113      * criteria.
 114      * @param index The ordinal index of the window in the set of currently
 115      * displayed windows. The first index is 0.
 116      * @return a reference to the {@code index+1}'th window that is showing
 117      * and that meets the search criteria. If there are fewer than
 118      * {@code index+1} windows, a {@code null} reference is returned.
 119      */
 120     public static Window getWindow(ComponentChooser cc, int index) {
 121         return getAWindow(new IndexChooser(cc, index));
 122     }
 123 
 124     /**
 125      * Searches for a window. Search among the currently showing windows for one
 126      * that meets the search criteria applied by the
 127      * {@code ComponentChooser} parameter.
 128      *
 129      * @param cc A component chooser used to define and apply the search
 130      * criteria.
 131      * @return a reference to the first window that is showing and that meets
 132      * the search criteria. If no such window can be found, a {@code null}
 133      * reference is returned.
 134      */
 135     public static Window getWindow(ComponentChooser cc) {
 136         return getWindow(cc, 0);
 137     }
 138 
 139     static {
 140         Timeouts.initDefault("WindowWaiter.WaitWindowTimeout", WAIT_TIME);
 141         Timeouts.initDefault("WindowWaiter.AfterWindowTimeout", AFTER_WAIT_TIME);
 142     }
 143 
 144     /**
 145      * Defines current timeouts.
 146      *
 147      * @param timeouts A collection of timeout assignments.
 148      * @see org.netbeans.jemmy.Timeoutable
 149      * @see org.netbeans.jemmy.Timeouts
 150      * @see #getTimeouts
 151      */
 152     @Override
 153     public void setTimeouts(Timeouts timeouts) {
 154         this.timeouts = timeouts;
 155         Timeouts times = timeouts.cloneThis();
 156         times.setTimeout("Waiter.WaitingTime",
 157                 timeouts.getTimeout("WindowWaiter.WaitWindowTimeout"));
 158         times.setTimeout("Waiter.AfterWaitingTime",
 159                 timeouts.getTimeout("WindowWaiter.AfterWindowTimeout"));
 160         setWaitingTimeOrigin("WindowWaiter.WaitWindowTimeout");
 161         super.setTimeouts(times);
 162     }
 163 
 164     /**
 165      * Return current timeouts.
 166      *
 167      * @return the collection of current timeout assignments.
 168      * @see org.netbeans.jemmy.Timeoutable
 169      * @see org.netbeans.jemmy.Timeouts
 170      * @see #setTimeouts
 171      */
 172     @Override
 173     public Timeouts getTimeouts() {
 174         return timeouts;
 175     }
 176 
 177     /**
 178      * Action producer--get a window. Get a window. The search uses constraints
 179      * on window ownership, ordinal index, and search criteria defined by an
 180      * instance of {@code org.netbeans.jemmy.ComponentChooser}.
 181      *
 182      * @param obj Not used.
 183      * @return the window waited upon. If a window cannot be found then a
 184      * {@code null} reference is returned.
 185      * @see org.netbeans.jemmy.Action
 186      */
 187     @Override
 188     public Window actionProduced(Void obj) {
 189         return WindowWaiter.getWindow(owner, chooser, index);
 190     }
 191 
 192     /**
 193      * Waits for a window to show. Wait for the {@code index+1}'th window
 194      * that meets the criteria defined and applied by the
 195      * {@code ComonentChooser} parameter to show up.
 196      *
 197      * @param ch A component chooser used to define and apply the search
 198      * criteria.
 199      * @param index The ordinal index of the window in the set of currently
 200      * displayed windows. The first index is 0.
 201      * @return a reference to the {@code index+1}'th window that shows and
 202      * that meets the search criteria. If fewer than {@code index+1}
 203      * windows show up in the allotted time period then a {@code null}
 204      * reference is returned.
 205      * @throws TimeoutExpiredException
 206      * @see #actionProduced(Object)
 207      * @exception InterruptedException
 208      */
 209     public Window waitWindow(ComponentChooser ch, int index)
 210             throws InterruptedException {
 211         chooser = ch;
 212         owner = null;
 213         this.index = index;
 214         return waitWindow();
 215     }
 216 
 217     /**
 218      * Waits for a window to show. Wait for a window that meets the search
 219      * criteria applied by the {@code ComponentChooser} parameter to show
 220      * up.
 221      *
 222      * @param ch A component chooser used to define and apply the search
 223      * criteria.
 224      * @return a reference to the first window that shows and that meets the
 225      * search criteria. If no such window can be found within the time period
 226      * allotted, a {@code null} reference is returned.
 227      * @throws TimeoutExpiredException
 228      * @see #actionProduced(Object)
 229      * @exception InterruptedException
 230      */
 231     public Window waitWindow(ComponentChooser ch)
 232             throws InterruptedException {
 233         return waitWindow(ch, 0);
 234     }
 235 
 236     /**
 237      * Waits for a window to show. Wait for the {@code index+1}'th window
 238      * to show that is both owned by the {@code java.awt.Window}
 239      * {@code o} and that meets the criteria defined and applied by the
 240      * {@code ComponentChooser} parameter.
 241      *
 242      * @param o The owner window of all the windows to be searched.
 243      * @param ch A component chooser used to define and apply the search
 244      * criteria.
 245      * @param index The ordinal index of the window in the set of currently
 246      * displayed windows with the proper window ownership and a suitable title.
 247      * The first index is 0.
 248      * @return a reference to the {@code index+1}'th window to show that
 249      * has the proper window ownership, and that meets the search criteria. If
 250      * there are fewer than {@code index+1} windows, a {@code null}
 251      * reference is returned.
 252      * @throws TimeoutExpiredException
 253      * @see #actionProduced(Object)
 254      * @exception InterruptedException
 255      */
 256     public Window waitWindow(Window o, ComponentChooser ch, int index)
 257             throws InterruptedException {
 258         owner = o;
 259         chooser = ch;
 260         this.index = index;
 261         return waitAction(null);
 262     }
 263 
 264     /**
 265      * Waits for a window to show. Wait for the first window to show that is
 266      * both owned by the {@code java.awt.Window} {@code o} and that
 267      * meets the criteria defined and applied by the
 268      * {@code ComponentChooser} parameter.
 269      *
 270      * @param o The owner window of all the windows to be searched.
 271      * @param ch A component chooser used to define and apply the search
 272      * criteria.
 273      * @return a reference to the first window to show that has the proper
 274      * window ownership, and that meets the search criteria. If there is no such
 275      * window, a {@code null} reference is returned.
 276      * @throws TimeoutExpiredException
 277      * @see #actionProduced(Object)
 278      * @exception InterruptedException
 279      */
 280     public Window waitWindow(Window o, ComponentChooser ch)
 281             throws InterruptedException {
 282         return waitWindow(o, ch, 0);
 283     }
 284 
 285     @Override
 286     public String getDescription() {
 287         return chooser.getDescription();
 288     }
 289 
 290     @Override
 291     public String toString() {
 292         return "WindowWaiter{" + "chooser=" + chooser + ", owner=" + owner + ", index=" + index + '}';
 293     }
 294 
 295     /**
 296      * Method can be used by a subclass to define chooser.
 297      *
 298      * @param ch a chooser specifying searching criteria.
 299      * @see #getComponentChooser
 300      */
 301     protected void setComponentChooser(ComponentChooser ch) {
 302         chooser = ch;
 303     }
 304 
 305     /**
 306      * Method can be used by a subclass to define chooser.
 307      *
 308      * @return a chooser specifying searching criteria.
 309      * @see #setComponentChooser
 310      */
 311     protected ComponentChooser getComponentChooser() {
 312         return chooser;
 313     }
 314 
 315     /**
 316      * Method can be used by a subclass to define window owner.
 317      *
 318      * @param owner Window-owner of the set of windows.
 319      * @see #getOwner
 320      */
 321     protected void setOwner(Window owner) {
 322         this.owner = owner;
 323     }
 324 
 325     /**
 326      * Method can be used by a subclass to define window owner.
 327      *
 328      * @return Window-owner of the set of windows.
 329      * @see #setOwner
 330      */
 331     protected Window getOwner() {
 332         return owner;
 333     }
 334 
 335     /**
 336      * @see org.netbeans.jemmy.Waiter#getWaitingStartedMessage()
 337      */
 338     @Override
 339     protected String getWaitingStartedMessage() {
 340         return "Start to wait window \"" + chooser.getDescription() + "\" opened";
 341     }
 342 
 343     /**
 344      * Overrides Waiter.getTimeoutExpiredMessage.
 345      *
 346      * @see org.netbeans.jemmy.Waiter#getTimeoutExpiredMessage(long)
 347      * @param timeSpent time from waiting start (milliseconds)
 348      * @return a message.
 349      */
 350     @Override
 351     protected String getTimeoutExpiredMessage(long timeSpent) {
 352         return ("Window \"" + chooser.getDescription() + "\" has not been opened in "
 353                 + timeSpent + " milliseconds");
 354     }
 355 
 356     /**
 357      * Overrides Waiter.getActionProducedMessage.
 358      *
 359      * @see org.netbeans.jemmy.Waiter#getActionProducedMessage(long, Object)
 360      * @param timeSpent time from waiting start (milliseconds)
 361      * @param result result of Waitable.actionproduced method.
 362      * @return a message.
 363      */
 364     @Override
 365     protected String getActionProducedMessage(long timeSpent, final Object result) {
 366         String resultToString;
 367         if (result instanceof Component) {
 368             // run toString in dispatch thread
 369             resultToString = new QueueTool().invokeSmoothly(
 370                     new QueueTool.QueueAction<String>("result.toString()") {
 371                 @Override
 372                 public String launch() {
 373                     return result.toString();
 374                 }
 375             }
 376             );
 377         } else {
 378             resultToString = result.toString();
 379         }
 380         return ("Window \"" + chooser.getDescription() + "\" has been opened in "
 381                 + timeSpent + " milliseconds"
 382                 + "\n    " + resultToString);
 383     }
 384 
 385     /**
 386      * @return a message.
 387      * @see org.netbeans.jemmy.Waiter#getGoldenWaitingStartedMessage()
 388      */
 389     @Override
 390     protected String getGoldenWaitingStartedMessage() {
 391         return "Start to wait window \"" + chooser.getDescription() + "\" opened";
 392     }
 393 
 394     /**
 395      * @return a message.
 396      * @see org.netbeans.jemmy.Waiter#getGoldenTimeoutExpiredMessage()
 397      */
 398     @Override
 399     protected String getGoldenTimeoutExpiredMessage() {
 400         return "Window \"" + chooser.getDescription() + "\" has not been opened";
 401     }
 402 
 403     /**
 404      * @return a message.
 405      * @see org.netbeans.jemmy.Waiter#getGoldenActionProducedMessage()
 406      */
 407     @Override
 408     protected String getGoldenActionProducedMessage() {
 409         return "Window \"" + chooser.getDescription() + "\" has been opened";
 410     }
 411 
 412     private static Window getAWindow(Window owner, ComponentChooser cc) {
 413         if (owner == null) {
 414             return WindowWaiter.getAWindow(cc);
 415         } else {
 416             Window result = null;
 417             Window[] windows = owner.getOwnedWindows();
 418             for (Window window : windows) {
 419                 if (cc.checkComponent(window)) {
 420                     return window;
 421                 }
 422                 if ((result = WindowWaiter.getWindow(window, cc)) != null) {
 423                     return result;
 424                 }
 425             }
 426             return null;
 427         }
 428     }
 429 
 430     private static Window getAWindow(ComponentChooser cc) {
 431         Window result = null;
 432         Frame[] frames = Frame.getFrames();
 433         for (Frame frame : frames) {
 434             if (cc.checkComponent(frame)) {
 435                 return frame;
 436             }
 437             if ((result = WindowWaiter.getWindow(frame, cc)) != null) {
 438                 return result;
 439             }
 440         }
 441         return null;
 442     }
 443 
 444     private Window waitWindow()
 445             throws InterruptedException {
 446         return waitAction(null);
 447     }
 448 
 449     private static class IndexChooser implements ComponentChooser {
 450 
 451         private int curIndex = 0;
 452         private int index;
 453         private ComponentChooser chooser;
 454 
 455         public IndexChooser(ComponentChooser ch, int i) {
 456             index = i;
 457             chooser = ch;
 458             curIndex = 0;
 459         }
 460 
 461         @Override
 462         public boolean checkComponent(Component comp) {
 463             if ((FIND_INVISIBLE_WINDOWS || (comp.isShowing() && comp.isVisible()))
 464                     && chooser.checkComponent(comp)) {
 465                 if (curIndex == index) {
 466                     return true;
 467                 }
 468                 curIndex++;
 469             }
 470             return false;
 471         }
 472 
 473         @Override
 474         public String getDescription() {
 475             return chooser.getDescription();
 476         }
 477 
 478         @Override
 479         public String toString() {
 480             return "IndexChooser{" + "curIndex=" + curIndex + ", index=" + index + ", chooser=" + chooser + '}';
 481         }
 482     }
 483 }