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