1 /* 2 * Copyright (c) 2007, 2017 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.jemmy.control; 24 25 import java.lang.reflect.InvocationTargetException; 26 import java.lang.reflect.Method; 27 import java.util.HashMap; 28 import org.jemmy.JemmyException; 29 import org.jemmy.Point; 30 import org.jemmy.Rectangle; 31 import org.jemmy.TimeoutExpiredException; 32 import org.jemmy.action.GetAction; 33 import org.jemmy.env.Environment; 34 import org.jemmy.env.TestOut; 35 import org.jemmy.env.Timeout; 36 import org.jemmy.image.Image; 37 import org.jemmy.interfaces.*; 38 import org.jemmy.timing.State; 39 40 /** 41 * This is a wrap which holds reference to a control without UI hierarchy. It 42 * also encapsulates all the logic to deal with the underlying control, in terms 43 * of implementations of ControlInterface. 44 * 45 * @see Wrap#as(java.lang.Class) 46 * @see Wrap#is(java.lang.Class) 47 * @param <CONTROL> type of the encapsulated object. 48 * @author shura, erikgreijus 49 */ 50 @ControlType(Object.class) 51 @ControlInterfaces({Mouse.class, Keyboard.class, Drag.class}) 52 public abstract class Wrap<CONTROL extends Object> { 53 54 /** 55 * 56 */ 57 public static final String BOUNDS_PROP_NAME = "bounds"; 58 /** 59 * 60 */ 61 public static final String CLICKPOINT_PROP_NAME = "clickPoint"; 62 /** 63 * 64 */ 65 public static final String CONTROL_CLASS_PROP_NAME = "control.class"; 66 /** 67 * 68 */ 69 public static final String CONTROL_PROP_NAME = "control"; 70 /** 71 * 72 */ 73 public static final String INPUT_FACTORY_PROPERTY = "input.control.interface.factory"; 74 /** 75 * 76 */ 77 public static final String IMAGE_LOADER_PROPERTY = "image.loader"; 78 /** 79 * 80 */ 81 public static final String IMAGE_CAPTURER_PROPERTY = "image.capturer"; 82 /** 83 * 84 */ 85 public static final String TEXT_PROP_NAME = "text"; 86 /** 87 * 88 */ 89 public static final String POSITION_PROP_NAME = "position"; 90 /** 91 * 92 */ 93 public static final String VALUE_PROP_NAME = "value"; 94 /** 95 * 96 */ 97 public static final String WRAPPER_CLASS_PROP_NAME = "wrapper.class"; 98 /** 99 * 100 */ 101 public static final String TOOLTIP_PROP_NAME = "tooltip"; 102 /** 103 * 104 */ 105 public static final String NAME_PROP_NAME = "name"; 106 /** 107 * 108 */ 109 public static final Timeout WAIT_STATE_TIMEOUT = new Timeout("wait.state", 1000); 110 /** 111 * 112 */ 113 public static final String OUTPUT = Wrap.class.getName() + ".OUTPUT"; 114 private static DefaultWrapper theWrapper = new DefaultWrapper(Environment.getEnvironment()); 115 116 static { 117 Environment.getEnvironment().initTimeout(WAIT_STATE_TIMEOUT); 118 Environment.getEnvironment().initOutput(OUTPUT, TestOut.getNullOutput()); 119 Environment.getEnvironment().initTimeout(Mouse.CLICK); 120 Environment.getEnvironment().initTimeout(Drag.BEFORE_DRAG_TIMEOUT); 121 Environment.getEnvironment().initTimeout(Drag.BEFORE_DROP_TIMEOUT); 122 Environment.getEnvironment().initTimeout(Drag.IN_DRAG_TIMEOUT); 123 Environment.getEnvironment().initTimeout(Keyboard.PUSH); 124 } 125 126 /** 127 * 128 * @return 129 */ 130 public static DefaultWrapper getWrapper() { 131 return theWrapper; 132 } 133 CONTROL node; 134 Environment env; 135 136 /** 137 * Fur null source. 138 * 139 * @see org.jemmy.env.Environment 140 * @param env The environment 141 */ 142 protected Wrap(Environment env) { 143 this.env = env; 144 node = null; 145 fillTheProps(false); 146 } 147 148 /** 149 * 150 * @see org.jemmy.env.Environment 151 * @param env The environment 152 * @param node The encapsulated object 153 */ 154 protected Wrap(Environment env, CONTROL node) { 155 this.env = env; 156 this.node = node; 157 } 158 159 /** 160 * 161 * @see org.jemmy.env.Environment 162 * @return environment instance used by this 163 */ 164 public Environment getEnvironment() { 165 return env; 166 } 167 168 public void setEnvironment(Environment env) { 169 this.env = env; 170 } 171 172 /** 173 * 174 * @return The encapsulated object 175 */ 176 @Property(CONTROL_PROP_NAME) 177 public CONTROL getControl() { 178 return node; 179 } 180 181 /** 182 * Return default point to click, drag. This implementation returns the 183 * center must be overriden if something different is desired. 184 * 185 * @return 186 */ 187 @Property(CLICKPOINT_PROP_NAME) 188 public Point getClickPoint() { 189 return new Point(getScreenBounds().width / 2, (getScreenBounds().height / 2)); 190 } 191 192 /** 193 * Returns control bounds in screen coordinates. These bounds could include 194 * parts that are covered by other controls or clipped out by parent 195 * components. If the control is not shown {@linkplain 196 * JemmyException JemmyException} will be thrown. 197 * 198 * @return control bounds in screen coordinates. 199 * @throws JemmyException if the control is not visible 200 */ 201 @Property(BOUNDS_PROP_NAME) 202 public abstract Rectangle getScreenBounds(); 203 204 /** 205 * Transforms point in local control coordinate system to screen 206 * coordinates. 207 * 208 * @param local 209 * @return 210 * @see #toLocal(org.jemmy.Point) 211 */ 212 public Point toAbsolute(Point local) { 213 Rectangle bounds = getScreenBounds(); 214 return local.translate(bounds.x, bounds.y); 215 } 216 217 /** 218 * Transforms point in screen coordinates to local control coordinate 219 * system. 220 * 221 * @param local 222 * @return coordinates which should be used for mouse operations. 223 * @see #toAbsolute(org.jemmy.Point) 224 */ 225 public Point toLocal(Point local) { 226 Rectangle bounds = getScreenBounds(); 227 return local.translate(-bounds.x, -bounds.y); 228 } 229 230 /** 231 * Captures the screen area held by the component. ImageFactory performs the 232 * actual capturing. 233 * 234 * @return TODO find a replacement 235 */ 236 public Image getScreenImage() { 237 Rectangle bounds = getScreenBounds(); 238 return getScreenImage(new Rectangle(0, 0, bounds.width, bounds.height)); 239 } 240 241 /** 242 * Captures portion of the screen area held by the component. ImageFactory 243 * performs the actual capturing. 244 * 245 * @param rect Part of the control to capture 246 * @return TODO find a replacement 247 */ 248 public Image getScreenImage(Rectangle rect) { 249 if (getEnvironment().getImageCapturer() == null) { 250 throw new JemmyException("Image capturer is not specified."); 251 } 252 return getEnvironment().getImageCapturer().capture(this, rect); 253 } 254 255 /** 256 * Waits for a portion of image to be exact the same as the parameter. 257 * 258 * @see Wrap#as(java.lang.Class) 259 * @param golden 260 * @param rect A portion of control to compare. 261 * @param resID ID of a result image to save in case of failure. No image 262 * saved if null. 263 * @param diffID ID of a diff image to save in case of failure. No image 264 * saved if null. 265 */ 266 public void waitImage(final Image golden, final Rectangle rect, String resID, String diffID) { 267 try { 268 waitState(new State<Object>() { 269 270 public Object reached() { 271 return (getScreenImage(rect).compareTo(golden) == null) ? true : null; 272 } 273 274 @Override 275 public String toString() { 276 return "Control having expected image"; 277 } 278 }); 279 } catch (TimeoutExpiredException e) { 280 if (diffID != null) { 281 getEnvironment().getOutput(OUTPUT).println("Saving difference to " + diffID); 282 getScreenImage(rect).compareTo(golden).save(diffID); 283 } 284 throw e; 285 } finally { 286 if (resID != null) { 287 getEnvironment().getOutput(OUTPUT).println("Saving result to " + resID); 288 getScreenImage(rect).save(resID); 289 } 290 } 291 } 292 293 /** 294 * Waits for image to be exact the same as the parameter. 295 * 296 * @see Wrap#as(java.lang.Class) 297 * @param golden 298 * @param resID ID of a result image to save in case of failure. No image 299 * saved if null. 300 * @param diffID ID of a diff image to save in case of failure. No image 301 * saved if null. 302 */ 303 public void waitImage(final Image golden, String resID, String diffID) { 304 Rectangle bounds = getScreenBounds(); 305 waitImage(golden, new Rectangle(0, 0, bounds.width, bounds.height), resID, diffID); 306 } 307 308 /** 309 * TODO javadoc 310 * 311 * @param <V> 312 * @param state 313 * @param value 314 * @return last returned State value 315 * @throws TimeoutExpiredException in case the wait is unsuccessful. 316 */ 317 public <V> V waitState(State<V> state, V value) { 318 return getEnvironment().getWaiter(WAIT_STATE_TIMEOUT).ensureValue(value, state); 319 } 320 321 /** 322 * TODO javadoc 323 * 324 * @param <V> 325 * @param state 326 * @return last returned State value 327 * @throws TimeoutExpiredException in case the wait is unsuccessful. 328 */ 329 public <V> V waitState(State<V> state) { 330 return getEnvironment().getWaiter(WAIT_STATE_TIMEOUT).ensureState(state); 331 } 332 333 /** 334 * *********************************************************************** 335 */ 336 /* 337 * INTERFACES 338 */ 339 /** 340 * *********************************************************************** 341 */ 342 private Method findAsMethod(Class<? extends ControlInterface> interfaceClass, Class type) { 343 while (type != null) { 344 for (Method m : getClass().getMethods()) { 345 As as = m.getAnnotation(As.class); 346 Class returnType = m.getReturnType(); 347 if (as != null && interfaceClass.isAssignableFrom(returnType) && as.value().equals(type)) { 348 if (m.getParameterTypes().length > 0 && type.equals(Void.class) 349 || m.getParameterTypes().length > 1 && !type.equals(Void.class)) { 350 throw new IllegalStateException("wrong number of parameters in an @As method"); 351 } 352 return m; 353 } 354 } 355 type = type.getSuperclass(); 356 } 357 return null; 358 } 359 360 /** 361 * Checks if the control could be treated as a ControlInterface. If it is, 362 * <code>Wrap#as(java.lang.Class)</code> will be called. This implementation 363 * checks whether the class implements the necessary interface. It also 364 * works for root interfaces such as 365 * <code>MouseTarget</code> and 366 * <code>KeyTarget</code>, which implementations are encapsulated. If some 367 * other functionality is desired, must be overriden together with 368 * <code>as(java.lang.Class)</code> 369 * 370 * @see Wrap#is(java.lang.Class) 371 * @param <INTERFACE> 372 * @param interfaceClass 373 * @return 374 */ 375 public <INTERFACE extends ControlInterface> boolean is(Class<INTERFACE> interfaceClass) { 376 if (interfaceClass.isInstance(this)) { 377 return true; 378 } 379 return findAsMethod(interfaceClass, Void.class) != null; 380 } 381 382 /** 383 * Checks if the control could be treated as a parametrized 384 * ControlInterface. If it is, 385 * <code>Wrap#as(java.lang.Class, java.lang.Class)</code> will be called. 386 * This implementation checks whether the class implements the necessary 387 * interface. It also works for root interfaces such as 388 * <code>MouseTarget</code> and 389 * <code>KeyTarget</code>, which implementations are encapsulated. If some 390 * other functionality is desired, must be overriden together with 391 * <code>as(java.lang.Class)</code> 392 * 393 * @see Wrap#is(java.lang.Class) 394 * @param <TYPE> 395 * @param <INTERFACE> 396 * @param interfaceClass 397 * @param type The parameter class. 398 * @return 399 */ 400 public <TYPE, INTERFACE extends TypeControlInterface<TYPE>> boolean is(Class<INTERFACE> interfaceClass, Class<TYPE> type) { 401 if (interfaceClass.isInstance(this)) { 402 if (interfaceClass.cast(this).getType().isAssignableFrom(type)) { 403 return true; 404 } 405 } 406 return findAsMethod(interfaceClass, type) != null; 407 } 408 409 private Object callAsMethod(Class<? extends ControlInterface> interfaceClass, Class type) { 410 Method m = findAsMethod(interfaceClass, type); 411 if (m != null) { 412 try { 413 if (m.getParameterTypes().length == 0) { 414 return m.invoke(this); 415 } else if (m.getParameterTypes().length == 1) { 416 return m.invoke(this, !type.equals(Void.class) ? type : Object.class); 417 } else { 418 throw new InterfaceException(this, interfaceClass); 419 } 420 } catch (IllegalAccessException ex) { 421 throw new JemmyException("Unable to call method \"" + m.getName() + "()\"", ex, this); 422 } catch (IllegalArgumentException ex) { 423 throw new JemmyException("Unable to call method \"" + m.getName() + "()\"", ex, this); 424 } catch (InvocationTargetException ex) { 425 throw new JemmyException("Unable to call method \"" + m.getName() + "()\"", ex, this); 426 } 427 } 428 return null; 429 } 430 431 /** 432 * Returns an implementation of interface associated with this object. First 433 * it checks 434 * 435 * @see Wrap#is(java.lang.Class) 436 * @param <INTERFACE> 437 * @param interfaceClass 438 * @return 439 */ 440 public <INTERFACE extends ControlInterface> INTERFACE as(Class<INTERFACE> interfaceClass) { 441 if (interfaceClass.isInstance(this)) { 442 return interfaceClass.cast(this); 443 } 444 445 Object res = callAsMethod(interfaceClass, Void.class); 446 if (res != null) { 447 return (INTERFACE) res; 448 } 449 450 throw new InterfaceException(this, interfaceClass); 451 } 452 453 /** 454 * Returns an implementation of interface associated with the object. 455 * 456 * @see Wrap#is(java.lang.Class) 457 * @param <TYPE> 458 * @param <INTERFACE> 459 * @param interfaceClass 460 * @param type The parameter class. 461 * @return 462 */ 463 public <TYPE, INTERFACE extends TypeControlInterface<TYPE>> INTERFACE as(Class<INTERFACE> interfaceClass, Class<TYPE> type) { 464 if (interfaceClass.isInstance(this)) { 465 if (interfaceClass.cast(this).getType().isAssignableFrom(type)) { 466 return interfaceClass.cast(this); 467 } 468 } 469 470 Object res = callAsMethod(interfaceClass, type); 471 if (res != null) { 472 return (INTERFACE) res; 473 } 474 475 throw new InterfaceException(this, interfaceClass); 476 } 477 /** 478 * *********************************************************************** 479 */ 480 /* 481 * INPUT 482 */ 483 /** 484 * *********************************************************************** 485 */ 486 private Mouse mouse = null; 487 private Drag drag = null; 488 private Keyboard keyboard = null; 489 490 /** 491 * A shortcut to 492 * <code>as(MouseTarget.class).mouse()</code> 493 * 494 * @return 495 */ 496 @As(Mouse.class) 497 public Mouse mouse() { 498 if (mouse == null) { 499 mouse = getEnvironment().getInputFactory().create(this, Mouse.class); 500 } 501 return mouse; 502 } 503 504 /** 505 * A shortcut to 506 * <code>as(MouseTarget.class).drag()</code> 507 * 508 * @return 509 */ 510 @As(Drag.class) 511 public Drag drag() { 512 if (drag == null) { 513 drag = getEnvironment().getInputFactory().create(this, Drag.class); 514 } 515 return drag; 516 } 517 518 /** 519 * A shortcut to 520 * <code>as(KeyTarget.class).wrap()</code> 521 * 522 * @return 523 */ 524 @As(Keyboard.class) 525 public Keyboard keyboard() { 526 if (keyboard == null) { 527 keyboard = getEnvironment().getInputFactory().create(this, Keyboard.class); 528 } 529 return keyboard; 530 } 531 /** 532 * *********************************************************************** 533 */ 534 /* 535 * PROPERTIES 536 */ 537 /** 538 * *********************************************************************** 539 */ 540 private HashMap<String, Object> properties = new HashMap<String, Object>(); 541 542 /** 543 * 544 * @return 545 */ 546 @Property(CONTROL_CLASS_PROP_NAME) 547 public Class<?> getControlClass() { 548 return getControl().getClass(); 549 } 550 551 private void fillTheProps(boolean quiet) { 552 properties.clear(); 553 properties.put(WRAPPER_CLASS_PROP_NAME, getClass()); 554 readAnnotationProps(quiet); 555 readControlProps(quiet); 556 } 557 558 private void readControlProps(boolean quiet) { 559 Class<?> cls = getClass(); 560 do { 561 if (cls.isAnnotationPresent(FieldProperties.class)) { 562 for (String s : cls.getAnnotation(FieldProperties.class).value()) { 563 Object value; 564 try { 565 value = getFieldProperty(s); 566 } catch (Exception e) { 567 getEnvironment().getOutput().printStackTrace(e); 568 value = e.toString(); 569 if (!(e instanceof JemmyException) && !quiet) { 570 throw new JemmyException("Exception while getting property \"" + s + "\"", e); 571 } 572 } 573 properties.put(s, value); 574 } 575 } 576 if (cls.isAnnotationPresent(MethodProperties.class)) { 577 for (String s : cls.getAnnotation(MethodProperties.class).value()) { 578 Object value; 579 try { 580 value = getMethodProperty(s); 581 } catch (Exception e) { 582 getEnvironment().getOutput().printStackTrace(e); 583 value = e.toString(); 584 if (!(e instanceof JemmyException) && !quiet) { 585 throw new JemmyException("Exception while getting property \"" + s + "\"", e); 586 } 587 } 588 properties.put(s, value); 589 } 590 } 591 } while ((cls = cls.getSuperclass()) != null); 592 } 593 594 private void addAnnotationProps(Class cls, boolean quiet) { 595 for (Method m : cls.getMethods()) { 596 if (m.isAnnotationPresent(Property.class)) { 597 String name = m.getAnnotation(Property.class).value(); 598 if (!properties.containsKey(name)) { 599 Object value; 600 try { 601 value = getProperty(this, m); 602 } catch (Exception e) { 603 if (quiet) { 604 getEnvironment().getOutput().printStackTrace(e); 605 value = e.toString(); 606 } else { 607 throw new JemmyException("Exception while getting property \"" + name + "\"", e); 608 } 609 } 610 properties.put(name, value); 611 } 612 } 613 } 614 } 615 616 private void readAnnotationProps(boolean quiet) { 617 Class cls = getClass(); 618 do { 619 addAnnotationProps(cls, quiet); 620 } while ((cls = cls.getSuperclass()) != null); 621 for (Class intf : getClass().getInterfaces()) { 622 addAnnotationProps(intf, quiet); 623 } 624 } 625 626 private void checkPropertyMethod(Method m) { 627 if (m.getParameterTypes().length > 0) { 628 throw new JemmyException("Method marked by @Property must not have parameters: " 629 + m.getDeclaringClass().getName() + "." + m.getName()); 630 } 631 } 632 633 private Method getPropertyMethod(Class cls, String name) { 634 Class scls = cls; 635 do { 636 for (Method m : scls.getMethods()) { 637 if (m.isAnnotationPresent(Property.class) && m.getAnnotation(Property.class).value().equals(name)) { 638 checkPropertyMethod(m); 639 return m; 640 } 641 } 642 } while ((scls = scls.getSuperclass()) != null); 643 for (Class intf : cls.getInterfaces()) { 644 for (Method m : intf.getMethods()) { 645 if (m.isAnnotationPresent(Property.class) && m.getAnnotation(Property.class).value().equals(name)) { 646 checkPropertyMethod(m); 647 return m; 648 } 649 } 650 } 651 return null; 652 } 653 654 private Object getProperty(Object object, Method m) { 655 Property prop = m.getAnnotation(Property.class); 656 try { 657 return m.invoke(object); 658 } catch (IllegalAccessException ex) { 659 throw new JemmyException("Unable to obtain property \"" + ((prop != null) ? prop.value() : "null") + "\"", ex, this); 660 } catch (IllegalArgumentException ex) { 661 throw new JemmyException("Unable to obtain property \"" + ((prop != null) ? prop.value() : "null") + "\"", ex, this); 662 } catch (InvocationTargetException ex) { 663 throw new JemmyException("Unable to obtain property \"" + ((prop != null) ? prop.value() : "null") + "\"", ex, this); 664 } 665 } 666 667 /** 668 * Get property of the wrapped object. Uses first available from <nl> 669 * <li>methods annotated by 670 * <code>org.jemmy.control.Property</code></li> <li>wrapped object methods 671 * listed in 672 * <code>org.jemmy.control.MethodProperties</code></li> <li>wrapped object 673 * fields listed in 674 * <code>org.jemmy.control.FieldProperties</code></li> </nl> 675 * 676 * @param name property name 677 * @throws JemmyException if no property found 678 * @see Property 679 * @see MethodProperties 680 * @see FieldProperties 681 * @return property value 682 */ 683 public Object getProperty(String name) { 684 if (WRAPPER_CLASS_PROP_NAME.equals(name)) { 685 return getClass(); 686 } 687 Method m = getPropertyMethod(this.getClass(), name); 688 if (m != null) { 689 return getProperty(this, m); 690 } 691 if (hasMethodProperty(name)) { 692 return getMethodProperty(name); 693 } 694 if (hasFieldProperty(name)) { 695 return getFieldProperty(name); 696 } 697 throw new JemmyException("No property \"" + name + "\"", this); 698 } 699 700 private Object getInterfaceProperty(Class cls, Object instance, String name) { 701 Method m = getPropertyMethod(cls, name); 702 if (m != null) { 703 return getProperty(instance, m); 704 } 705 throw new JemmyException("No property \"" + name + "\" in interface " + cls.getName(), instance); 706 } 707 708 /** 709 * Get property out of the control interface. Refer to the interface doc to 710 * find out what properties are provided. 711 * 712 * @param <INTERFACE> 713 * @param name 714 * @param intrfc 715 * @return 716 */ 717 public <INTERFACE extends ControlInterface> Object getProperty(String name, Class<INTERFACE> intrfc) { 718 return getInterfaceProperty(intrfc, as(intrfc), name); 719 } 720 721 /** 722 * Get property out of the control interface. Refer to the interface doc to 723 * find out what properties are provided. 724 * 725 * @param <TYPE> 726 * @param <INTERFACE> 727 * @param name 728 * @param intrfc 729 * @param type 730 * @return 731 */ 732 public <TYPE, INTERFACE extends TypeControlInterface<TYPE>> Object getProperty(String name, Class<INTERFACE> intrfc, Class<TYPE> type) { 733 return getInterfaceProperty(intrfc, as(intrfc, type), name); 734 } 735 736 /** 737 * Wait for the property 738 * <code>property</code> to get the specified value. 739 * <code>WAIT_STATE_TIMOUT</code> timeout is used 740 * 741 * @param property name of the property being waited for 742 * @param value property value to wait 743 */ 744 public void waitProperty(final String property, final Object value) { 745 getEnvironment().getWaiter(WAIT_STATE_TIMEOUT).ensureValue(value, new State<Object>() { 746 747 public Object reached() { 748 return getProperty(property); 749 } 750 751 @Override 752 public String toString() { 753 return "Control having property " + property + " expected value '" + value + "' (Property = '" + getProperty(property) + "')"; 754 } 755 }); 756 } 757 758 /** 759 * Wait for the property 760 * <code>property</code> of control interface to get the specified value. 761 * <code>WAIT_STATE_TIMOUT</code> timeout is used 762 * 763 * @param <INTERFACE> 764 * @param property 765 * @param intrfc 766 * @param value 767 */ 768 public <INTERFACE extends ControlInterface> void waitProperty(final String property, final Class<INTERFACE> intrfc, final Object value) { 769 Object instance = as(intrfc); 770 getEnvironment().getWaiter(WAIT_STATE_TIMEOUT).ensureValue(value, new State<Object>() { 771 772 public Object reached() { 773 return getProperty(property, intrfc); 774 } 775 776 @Override 777 public String toString() { 778 return "Interface " + intrfc.getName() + " having property " + property + " expected value '" + value + "' (Property = '" + getProperty(property, intrfc) + "')"; 779 } 780 }); 781 } 782 783 /** 784 * Wait for the property 785 * <code>property</code> of control interface to get the specified value. 786 * <code>WAIT_STATE_TIMOUT</code> timeout is used 787 * 788 * @param <TYPE> 789 * @param <INTERFACE> 790 * @param property 791 * @param intrfc 792 * @param type 793 * @param value 794 */ 795 public <TYPE, INTERFACE extends TypeControlInterface<TYPE>> void waitProperty(final String property, final Class<INTERFACE> intrfc, final Class<TYPE> type, final Object value) { 796 getEnvironment().getWaiter(WAIT_STATE_TIMEOUT).ensureValue(value, new State<Object>() { 797 798 public Object reached() { 799 return getProperty(property, intrfc, type); 800 } 801 802 @Override 803 public String toString() { 804 return "Interface " + intrfc.getName() + " having property " + property + " expected value '" + value + "' (Property = '" + getProperty(property) + "')"; 805 } 806 }); 807 } 808 809 /** 810 * 811 * @param name 812 * @return 813 */ 814 public boolean hasFieldProperty(String name) { 815 Class<?> cls = getClass(); 816 do { 817 if (cls.isAnnotationPresent(FieldProperties.class)) { 818 FieldProperties props = cls.getAnnotation(FieldProperties.class); 819 if (contains(props.value(), name)) { 820 return true; 821 } 822 } 823 } while ((cls = cls.getSuperclass()) != null); 824 return false; 825 } 826 827 /** 828 * 829 * @param name 830 * @return 831 */ 832 public boolean hasMethodProperty(String name) { 833 Class<?> cls = getClass(); 834 do { 835 if (cls.isAnnotationPresent(MethodProperties.class)) { 836 MethodProperties props = cls.getAnnotation(MethodProperties.class); 837 if (contains(props.value(), name)) { 838 return true; 839 } 840 } 841 } while ((cls = cls.getSuperclass()) != null); 842 return false; 843 } 844 845 private boolean contains(String[] values, String name) { 846 for (int i = 0; i < values.length; i++) { 847 if (name.equals(values[i])) { 848 return true; 849 } 850 851 } 852 return false; 853 } 854 855 /** 856 * 857 * @param name 858 * @return 859 */ 860 public Object getFieldProperty(final String name) { 861 if (!hasFieldProperty(name)) { 862 throw new JemmyException("No \"" + name + "\" field property specified on " + getClass().getName()); 863 } 864 GetAction action = new GetAction() { 865 866 @Override 867 public void run(Object... parameters) throws Exception { 868 setResult(getControl().getClass().getField(name).get(getControl())); 869 } 870 }; 871 Object result = action.dispatch(env); 872 if (action.getThrowable() != null) { 873 throw new JemmyException("Unable to obtain property \"" + name + "\"", action.getThrowable(), this); 874 } 875 return result; 876 } 877 878 /** 879 * 880 * @param name 881 * @return 882 */ 883 public Object getMethodProperty(final String name) { 884 if (!hasMethodProperty(name)) { 885 throw new JemmyException("No \"" + name + "\" method property specified on " + getClass().getName()); 886 } 887 GetAction action = new GetAction() { 888 889 @Override 890 public void run(Object... parameters) throws Exception { 891 setResult(getControl().getClass().getMethod(name).invoke(getControl())); 892 } 893 894 @Override 895 public String toString() { 896 return "Getting property \"" + name + "\" on " + getClass().getName(); 897 } 898 }; 899 Object result = action.dispatch(env); 900 if (action.getThrowable() != null) { 901 throw new JemmyException("Unable to obtain property \"" + name + "\"", action.getThrowable(), this); 902 } 903 return result; 904 } 905 906 /** 907 * 908 * @param <P> 909 * @param valueClass 910 * @param name 911 * @return 912 */ 913 public <P> P getProperty(Class<P> valueClass, String name) { 914 return valueClass.cast(getProperty(name)); 915 } 916 917 /** 918 * Returns a a map of all known controls properties including values from 919 * methods marked by 920 * <code>@Property</code> and values of methods/field from 921 * <code>@MethodProperties</code>/ 922 * <code>FieldProperties</code> correspondingly. 923 * 924 * @return a map of properties 925 * @throws Runtime exception should there be an exception thrown while 926 * getting a property 927 */ 928 public HashMap<String, Object> getProperties() { 929 fillTheProps(false); 930 return properties; 931 } 932 933 /** 934 * Returns a a map of all controls properties which is possible to obtain. 935 * Similar to 936 * <code>getProperties()</code> only exception is swallowed should there be 937 * an exception thrown while getting a property. 938 * 939 * @return a map of properties which were possible to obtain. 940 */ 941 public HashMap<String, Object> getPropertiesQiuet() { 942 fillTheProps(true); 943 return properties; 944 } 945 }