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. 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.netbeans.jemmy;
  26 
  27 import java.io.BufferedReader;
  28 import java.io.FileInputStream;
  29 import java.io.IOException;
  30 import java.io.InputStream;
  31 import java.io.InputStreamReader;
  32 import java.lang.reflect.InvocationTargetException;
  33 import java.util.Enumeration;
  34 import java.util.Hashtable;
  35 import java.util.Properties;
  36 import java.util.Stack;
  37 import java.util.StringTokenizer;
  38 
  39 import org.netbeans.jemmy.drivers.APIDriverInstaller;
  40 import org.netbeans.jemmy.drivers.DefaultDriverInstaller;
  41 import org.netbeans.jemmy.drivers.DriverInstaller;
  42 import org.netbeans.jemmy.drivers.InputDriverInstaller;
  43 import org.netbeans.jemmy.explorer.GUIBrowser;
  44 
  45 /**
  46  *
  47  * Keeps default Jemmy properties.
  48  *
  49  * @author Alexandre Iline (alexandre.iline@oracle.com)
  50  *
  51  */
  52 public class JemmyProperties {
  53 
  54     /**
  55      * The event queue model mask.
  56      *
  57      * @see #getCurrentDispatchingModel()
  58      * @see #setCurrentDispatchingModel(int)
  59      */
  60     public static final int QUEUE_MODEL_MASK = 1;
  61 
  62     /**
  63      * The robot using model mask.
  64      *
  65      * @see #getCurrentDispatchingModel()
  66      * @see #setCurrentDispatchingModel(int)
  67      */
  68     public static final int ROBOT_MODEL_MASK = 2;
  69 
  70     /**
  71      * Event shorcutting model mask. Should not be used together with robot
  72      * mask.
  73      *
  74      * @see #getCurrentDispatchingModel()
  75      * @see #setCurrentDispatchingModel(int)
  76      */
  77     public static final int SHORTCUT_MODEL_MASK = 4;
  78 
  79     /**
  80      * The robot using model mask.
  81      *
  82      * @see #getCurrentDispatchingModel()
  83      * @see #setCurrentDispatchingModel(int)
  84      */
  85     public static final int SMOOTH_ROBOT_MODEL_MASK = 8;
  86 
  87     private static final int DEFAULT_DRAG_AND_DROP_STEP_LENGTH = 100;
  88     private static final Stack<JemmyProperties> propStack = new Stack<>();
  89 
  90     Hashtable<String, Object> properties;
  91 
  92     /**
  93      *
  94      */
  95     protected JemmyProperties() {
  96         super();
  97         properties = new Hashtable<>();
  98         setProperty("timeouts", new Timeouts());
  99         setProperty("output", new TestOut());
 100         setProperty("resources", new BundleManager());
 101         setProperty("binding.map", new DefaultCharBindingMap());
 102         setProperty("dispatching.model", getDefaultDispatchingModel());
 103         setProperty("drag_and_drop.step_length", DEFAULT_DRAG_AND_DROP_STEP_LENGTH);
 104     }
 105 
 106     /**
 107      * Returns major version (like 1.0).
 108      *
 109      * @return a String representing the major version value.
 110      */
 111     public static String getMajorVersion() {
 112         return (extractValue(getProperties().getClass().
 113                 getClassLoader().getResourceAsStream("org/netbeans/jemmy/version_info"),
 114                 "Jemmy-MajorVersion"));
 115     }
 116 
 117     /**
 118      * Returns minor version (like 1).
 119      *
 120      * @return a String representing the minor version value.
 121      */
 122     public static String getMinorVersion() {
 123         return (extractValue(getProperties().getClass().
 124                 getClassLoader().getResourceAsStream("org/netbeans/jemmy/version_info"),
 125                 "Jemmy-MinorVersion"));
 126     }
 127 
 128     /**
 129      * Returns build (like 20011231 (yyyymmdd)).
 130      *
 131      * @return a String representing the build value.
 132      */
 133     public static String getBuild() {
 134         return (extractValue(getProperties().getClass().
 135                 getClassLoader().getResourceAsStream("org/netbeans/jemmy/version_info"),
 136                 "Jemmy-Build"));
 137     }
 138 
 139     /**
 140      * Returns full version string (like 1.0.1-20011231).
 141      *
 142      * @return a String representing the full version value.
 143      */
 144     public static String getFullVersion() {
 145         return (getMajorVersion() + "."
 146                 + getMinorVersion() + "-"
 147                 + getBuild());
 148     }
 149 
 150     /**
 151      * Returns version string (like 1.0.1).
 152      *
 153      * @return a String representing the short version value.
 154      */
 155     public static String getVersion() {
 156         return (getMajorVersion() + "."
 157                 + getMinorVersion());
 158     }
 159 
 160     /**
 161      * Creates a copy of the current JemmyProperties object and pushes it into
 162      * the properties stack.
 163      *
 164      * @return New current properties.
 165      */
 166     public static JemmyProperties push() {
 167         return push(getProperties().cloneThis());
 168     }
 169 
 170     /**
 171      * Pops last pushed properties from the properties stack. If stack has just
 172      * one element, does nothing.
 173      *
 174      * @return Poped properties.
 175      */
 176     public static JemmyProperties pop() {
 177         JemmyProperties result = propStack.pop();
 178         if (propStack.isEmpty()) {
 179             propStack.push(result);
 180         }
 181         return result;
 182     }
 183 
 184     /**
 185      * Just like getProperties().getProperty(propertyName).
 186      *
 187      * @param propertyName a property key
 188      * @return a property value
 189      * @see #setCurrentProperty
 190      * @see #setCurrentTimeout
 191      */
 192     public static Object getCurrentProperty(String propertyName) {
 193         return getProperties().getProperty(propertyName);
 194     }
 195 
 196     /**
 197      * Just like getProperties().setProperty(propertyName, propertyValue).
 198      *
 199      * @param propertyName a property key
 200      * @param propertyValue a property value
 201      * @return previous property value
 202      * @see #getCurrentProperty
 203      * @see #getCurrentTimeout
 204      */
 205     public static Object setCurrentProperty(String propertyName, Object propertyValue) {
 206         return getProperties().setProperty(propertyName, propertyValue);
 207     }
 208 
 209     /**
 210      * Removes a property from current properties list.
 211      *
 212      * @param propertyName a property key.
 213      * @return previous property value
 214      */
 215     public static Object removeCurrentProperty(String propertyName) {
 216         return getProperties().removeProperty(propertyName);
 217     }
 218 
 219     /**
 220      * Returns the current key values.
 221      *
 222      * @return an array of Strings representing the current key values
 223      */
 224     public static String[] getCurrentKeys() {
 225         return getProperties().getKeys();
 226     }
 227 
 228     /**
 229      * Just like getProperties().getTimeouts().
 230      *
 231      * @return a Timeouts object representing the current timeouts.
 232      * @see #setCurrentTimeouts
 233      */
 234     public static Timeouts getCurrentTimeouts() {
 235         return getProperties().getTimeouts();
 236     }
 237 
 238     /**
 239      * Just like getProperties().setTimeouts(to).
 240      *
 241      * @param to New timeouts
 242      * @return old timeouts.
 243      * @see #getCurrentTimeouts
 244      */
 245     public static Timeouts setCurrentTimeouts(Timeouts to) {
 246         return getProperties().setTimeouts(to);
 247     }
 248 
 249     /**
 250      * Just like getProperties().getTimeouts().setTimeout(name, newValue).
 251      *
 252      * @param name a timeout name
 253      * @param newValue a timeout value
 254      * @return previous timeout value
 255      * @see #getCurrentTimeout
 256      */
 257     public static long setCurrentTimeout(String name, long newValue) {
 258         return getProperties().getTimeouts().setTimeout(name, newValue);
 259     }
 260 
 261     /**
 262      * Just like getProperties().getTimeouts().getTimeout(name).
 263      *
 264      * @param name a timeout name
 265      * @return a timeout value
 266      * @see #setCurrentTimeout
 267      */
 268     public static long getCurrentTimeout(String name) {
 269         return getProperties().getTimeouts().getTimeout(name);
 270     }
 271 
 272     /**
 273      * Just like getProperties().getTimeouts().initTimeout(name, newValue).
 274      *
 275      * @param name a timeout name
 276      * @param newValue a timeout value
 277      * @return a timeout value
 278      * @see #setCurrentTimeout
 279      */
 280     public static long initCurrentTimeout(String name, long newValue) {
 281         return getProperties().getTimeouts().initTimeout(name, newValue);
 282     }
 283 
 284     /**
 285      * Just like getProperties().getOutput().
 286      *
 287      * @return a TestOut object representing the current output.
 288      * @see #setCurrentOutput
 289      */
 290     public static TestOut getCurrentOutput() {
 291         return getProperties().getOutput();
 292     }
 293 
 294     /**
 295      * Just like getProperties().setOutput(out).
 296      *
 297      * @param out new output
 298      * @return a TestOut object representing the current output.
 299      * @see #getCurrentOutput
 300      */
 301     public static TestOut setCurrentOutput(TestOut out) {
 302         return getProperties().setOutput(out);
 303     }
 304 
 305     /**
 306      * Just like getProperties().getBundleManager().
 307      *
 308      * @return a BundleManager object representing the current bundle manager.
 309      * @see #setCurrentBundleManager
 310      */
 311     public static BundleManager getCurrentBundleManager() {
 312         return getProperties().getBundleManager();
 313     }
 314 
 315     /**
 316      * Just like getProperties().setBundleManager(resources).
 317      *
 318      * @param resources new BundleManager
 319      * @return a BundleManager object representing the current bundle manager.
 320      * @see #getCurrentBundleManager
 321      */
 322     public static BundleManager setCurrentBundleManager(BundleManager resources) {
 323         return getProperties().setBundleManager(resources);
 324     }
 325 
 326     /**
 327      * Just like getProperties().getBundleManager().getResource(key).
 328      *
 329      * @param key a resource key.
 330      * @return a resource value
 331      */
 332     public static String getCurrentResource(String key) {
 333         return getProperties().getBundleManager().getResource(key);
 334     }
 335 
 336     /**
 337      * Just like getProperties().getBundleManager().getResource(bundleID, key).
 338      *
 339      * @param key a resource key.
 340      * @param bundleID a bundle ID
 341      * @return a resource value
 342      */
 343     public static String getCurrentResource(String bundleID, String key) {
 344         return getProperties().getBundleManager().getResource(bundleID, key);
 345     }
 346 
 347     /**
 348      * Just like getProperties().getCharBindingMap().
 349      *
 350      * @return a CharBindingMap object representing the current char binding
 351      * map.
 352      * @see #setCurrentCharBindingMap
 353      */
 354     public static CharBindingMap getCurrentCharBindingMap() {
 355         return getProperties().getCharBindingMap();
 356     }
 357 
 358     /**
 359      * Just like getProperties().setCharBindingMap(map).
 360      *
 361      * @param map new CharBindingMap.
 362      * @return old CharBindingMap object.
 363      * @see #getCurrentCharBindingMap
 364      */
 365     public static CharBindingMap setCurrentCharBindingMap(CharBindingMap map) {
 366         return getProperties().setCharBindingMap(map);
 367     }
 368 
 369     /**
 370      * Returns the current dispatching model.
 371      *
 372      * @return Event dispatching model.
 373      * @see #getDispatchingModel()
 374      * @see #setCurrentDispatchingModel(int)
 375      * @see #QUEUE_MODEL_MASK
 376      * @see #ROBOT_MODEL_MASK
 377      */
 378     public static int getCurrentDispatchingModel() {
 379         return getProperties().getDispatchingModel();
 380     }
 381 
 382     /**
 383      * Defines event dispatching model. If (model & ROBOT_MODEL_MASK) != 0
 384      * java.awt.Robot class is used to reproduce user actions, otherwise actions
 385      * are reproduced by event posting. If (model & QUEUE_MODEL_MASK) != 0
 386      * actions are reproduced through event queue.
 387      *
 388      * @param model New dispatching model value.
 389      * @return Previous dispatching model value.
 390      * @see #setDispatchingModel(int)
 391      * @see #getCurrentDispatchingModel()
 392      * @see #QUEUE_MODEL_MASK
 393      * @see #ROBOT_MODEL_MASK
 394      * @see #initDispatchingModel(boolean, boolean)
 395      * @see #initDispatchingModel()
 396      */
 397     public static int setCurrentDispatchingModel(int model) {
 398         return getProperties().setDispatchingModel(model);
 399     }
 400 
 401     /**
 402      * Returns default event dispatching model.
 403      *
 404      * @return QUEUE_MODEL_MASK
 405      * @see #setCurrentDispatchingModel(int)
 406      * @see #QUEUE_MODEL_MASK
 407      * @see #ROBOT_MODEL_MASK
 408      */
 409     public static int getDefaultDispatchingModel() {
 410         return SHORTCUT_MODEL_MASK | QUEUE_MODEL_MASK;
 411     }
 412 
 413     /**
 414      * Returns the current drag and drop step length value.
 415      *
 416      * @return Pixel count to move mouse during one drag'n'drop step.
 417      * @see #getDragAndDropStepLength()
 418      * @see #setCurrentDragAndDropStepLength(int)
 419      */
 420     public static int getCurrentDragAndDropStepLength() {
 421         return getProperties().getDragAndDropStepLength();
 422     }
 423 
 424     /**
 425      * Specifies the current drag and drop step length value.
 426      *
 427      * @param model Pixel count to move mouse during one drag'n'drop step.
 428      * @return Previous value.
 429      * @see #setDragAndDropStepLength(int)
 430      * @see #getCurrentDragAndDropStepLength()
 431      */
 432     public static int setCurrentDragAndDropStepLength(int model) {
 433         return getProperties().setDragAndDropStepLength(model);
 434     }
 435 
 436     /**
 437      * Peeks upper JemmyProperties instance from stack.
 438      *
 439      * @return a JemmyProperties object representing the properties value.
 440      */
 441     public static JemmyProperties getProperties() {
 442         if (propStack.empty()) {
 443             propStack.add(new JemmyProperties());
 444         }
 445         return propStack.peek();
 446     }
 447 
 448     /**
 449      * Prints full version into standard output.
 450      *
 451      * @param argv Application args.
 452      */
 453     public static void main(String[] argv) {
 454         if (argv.length == 0) {
 455             System.out.println("Jemmy version : " + getVersion());
 456         } else if (argv.length == 1
 457                 && argv[0].equals("-f")) {
 458             System.out.println("Jemmy full version : " + getFullVersion());
 459         } else if (argv.length > 0
 460                 && argv[0].equals("-e")) {
 461             String[] newArgv = new String[argv.length - 1];
 462             System.arraycopy(argv, 1, newArgv, 0, argv.length - 1);
 463             GUIBrowser.main(newArgv);
 464         } else {
 465             System.out.println("Parameters: ");
 466             System.out.println("<no parameters> - report Jemmy version.");
 467             System.out.println("\"-f\" - report full jemmy version.");
 468         }
 469     }
 470 
 471     /**
 472      * Pushes properties stack.
 473      *
 474      * @param props a JemmyProperties instance to put into the stack head.
 475      * @return a JemmyProperties object.
 476      */
 477     protected static JemmyProperties push(JemmyProperties props) {
 478         return propStack.push(props);
 479     }
 480 
 481     static {
 482         setCurrentDispatchingModel(getDefaultDispatchingModel());
 483     }
 484 
 485     /**
 486      * Method to initialize timeouts and resources.
 487      *
 488      * @param prop_file File to get filenames from. <BR>
 489      * Can contain definition of variables TIMEOUTS_FILE - full path to timeouts
 490      * file, <BR>
 491      * RESOURCE_FILE - full path to resource file.
 492      * @see org.netbeans.jemmy.JemmyProperties#initProperties()
 493      */
 494     public void initProperties(String prop_file) {
 495         try {
 496             getOutput().printLine("Loading properties from " + prop_file + " file");
 497             Properties props = new Properties();
 498             try (FileInputStream fileStream = new FileInputStream(prop_file)) {
 499                 props.load(fileStream);
 500             }
 501             if (props.getProperty("TIMEOUTS_FILE") != null
 502                     && !props.getProperty("TIMEOUTS_FILE").equals("")) {
 503                 getOutput().printLine("Loading timeouts from " + props.getProperty("TIMEOUTS_FILE")
 504                         + " file");
 505                 getTimeouts().loadDefaults(props.getProperty("TIMEOUTS_FILE"));
 506             }
 507             if (props.getProperty("RESOURCE_FILE") != null
 508                     && !props.getProperty("RESOURCE_FILE").equals("")) {
 509                 getOutput().printLine("Loading resources from " + props.getProperty("RESOURCE_FILE")
 510                         + " file");
 511                 getBundleManager().loadBundleFromFile(props.getProperty("RESOURCE_FILE"), "");
 512             }
 513         } catch (IOException e) {
 514             getOutput().printStackTrace(e);
 515         }
 516     }
 517 
 518     /**
 519      * Method to initialize timeouts and resources. <BR>
 520      * Uses jemmy.properties system property to find file.
 521      *
 522      * @see org.netbeans.jemmy.JemmyProperties#initProperties(String)
 523      */
 524     public void initProperties() {
 525         if (System.getProperty("jemmy.properties") != null
 526                 && !System.getProperty("jemmy.properties").equals("")) {
 527             initProperties(System.getProperty("jemmy.properties"));
 528         } else {
 529             try {
 530                 getTimeouts().load();
 531                 getBundleManager().load();
 532             } catch (IOException e) {
 533                 getOutput().printStackTrace(e);
 534             }
 535         }
 536     }
 537 
 538     /**
 539      * Initializes dispatching model.
 540      *
 541      * @param queue Notifies that event queue dispatching should be used.
 542      * @param robot Notifies that robot dispatching should be used.
 543      * @param shortcut Notifies that event shorcutting should be used.
 544      */
 545     public void initDispatchingModel(boolean queue, boolean robot, boolean shortcut) {
 546         initDispatchingModel(queue, robot, shortcut, false);
 547     }
 548 
 549     /**
 550      * Initializes dispatching model.
 551      *
 552      * @param queue Notifies that event queue dispatching should be used.
 553      * @param robot Notifies that robot dispatching should be used.
 554      * @param shortcut Notifies that event shorcutting should be used.
 555      */
 556     public void initDispatchingModel(boolean queue, boolean robot, boolean shortcut, boolean smooth) {
 557         int model = getDefaultDispatchingModel();
 558         getOutput().print("Reproduce user actions ");
 559         if (queue) {
 560             model = QUEUE_MODEL_MASK;
 561             getOutput().printLine("through event queue.");
 562         } else {
 563             model = model - (model & QUEUE_MODEL_MASK);
 564             getOutput().printLine("directly.");
 565         }
 566         getOutput().print("Use ");
 567         if (robot) {
 568             model = model | ROBOT_MODEL_MASK;
 569             getOutput().print("java.awt.Robot class");
 570         } else {
 571             model = model - (model & ROBOT_MODEL_MASK);
 572             getOutput().print("event dispatching");
 573         }
 574         if (smooth) {
 575             model = model | SMOOTH_ROBOT_MODEL_MASK;
 576         } else {
 577             model = model - (model & SMOOTH_ROBOT_MODEL_MASK);
 578         }
 579         getOutput().printLine(" to reproduce user actions");
 580         if (shortcut) {
 581             model = model | SHORTCUT_MODEL_MASK;
 582             getOutput().print("Shortcut");
 583         } else {
 584             model = model - (model & SHORTCUT_MODEL_MASK);
 585             getOutput().print("Dispatch");
 586         }
 587         getOutput().printLine(" test events");
 588         setDispatchingModel(model);
 589     }
 590 
 591     /**
 592      * Initializes dispatching model.
 593      *
 594      * @param queue Notifies that event queue dispatching should be used.
 595      * @param robot Notifies that robot dispatching should be used.
 596      */
 597     public void initDispatchingModel(boolean queue, boolean robot) {
 598         this.initDispatchingModel(queue, robot, false);
 599     }
 600 
 601     /**
 602      * Initializes dispatching model. Uses "jemmy.queue_dispatching" and
 603      * "jemmy.robot_dispatching" system properties to determine what model
 604      * should be used. Possible values for the both properties: <BR>
 605      * "off" - switch mode off. <BR>
 606      * "on" - switch mode on. <BR>
 607      * "" - use default value.
 608      *
 609      * @see #getDefaultDispatchingModel()
 610      */
 611     public void initDispatchingModel() {
 612         boolean qmask = ((getDefaultDispatchingModel() & QUEUE_MODEL_MASK) != 0);
 613         boolean rmask = ((getDefaultDispatchingModel() & ROBOT_MODEL_MASK) != 0);
 614         boolean srmask = ((getDefaultDispatchingModel() & SMOOTH_ROBOT_MODEL_MASK) != 0);
 615         boolean smask = ((getDefaultDispatchingModel() & SHORTCUT_MODEL_MASK) != 0);
 616         if (System.getProperty("jemmy.queue_dispatching") != null
 617                 && !System.getProperty("jemmy.queue_dispatching").equals("")) {
 618             qmask = System.getProperty("jemmy.queue_dispatching").equals("on");
 619         }
 620         if (System.getProperty("jemmy.robot_dispatching") != null
 621                 && !System.getProperty("jemmy.robot_dispatching").equals("")) {
 622             rmask = System.getProperty("jemmy.robot_dispatching").equals("on");
 623         }
 624         if (System.getProperty("jemmy.smooth_robot_dispatching") != null
 625                 && !System.getProperty("jemmy.smooth_robot_dispatching").equals("")) {
 626             srmask = System.getProperty("jemmy.smooth_robot_dispatching").equals("on");
 627         }
 628         if (System.getProperty("jemmy.shortcut_events") != null
 629                 && !System.getProperty("jemmy.shortcut_events").equals("")) {
 630             smask = System.getProperty("jemmy.shortcut_events").equals("on");
 631         }
 632         initDispatchingModel(qmask, rmask, smask, srmask);
 633     }
 634 
 635     /**
 636      * Inits properties and dispatching model from system environment variables.
 637      *
 638      * @see #initProperties()
 639      * @see #initDispatchingModel()
 640      */
 641     public void init() {
 642         initProperties();
 643         initDispatchingModel();
 644     }
 645 
 646     /**
 647      * Returns timeouts.
 648      *
 649      * @return the Timeouts value.
 650      * @see #setTimeouts
 651      */
 652     public Timeouts getTimeouts() {
 653         return (Timeouts) getProperty("timeouts");
 654     }
 655 
 656     /**
 657      * Changes timeouts.
 658      *
 659      * @param to new timeouts.
 660      * @return old timeouts.
 661      * @see #getTimeouts
 662      */
 663     public Timeouts setTimeouts(Timeouts to) {
 664         return (Timeouts) setProperty("timeouts", to);
 665     }
 666 
 667     /**
 668      * Changes a timeouts value.
 669      *
 670      * @param name Timeout name
 671      * @param newValue New timeout value
 672      * @return previous timeout value
 673      * @see #getTimeout
 674      */
 675     public long setTimeout(String name, long newValue) {
 676         return getTimeouts().setTimeout(name, newValue);
 677     }
 678 
 679     /**
 680      * Returns a timeouts value.
 681      *
 682      * @param name Timeout name
 683      * @return a timeout value
 684      * @see #setTimeout
 685      */
 686     public long getTimeout(String name) {
 687         return getTimeouts().getTimeout(name);
 688     }
 689 
 690     /**
 691      * Inits a timeouts value.
 692      *
 693      * @param name Timeout name
 694      * @param newValue New timeout value
 695      * @return a timeout value
 696      */
 697     public long initTimeout(String name, long newValue) {
 698         return getTimeouts().initTimeout(name, newValue);
 699     }
 700 
 701     /**
 702      * Returns output.
 703      *
 704      * @return a TestOut object representing the output value
 705      * @see #setOutput
 706      */
 707     public TestOut getOutput() {
 708         return (TestOut) getProperty("output");
 709     }
 710 
 711     /**
 712      * Changes output.
 713      *
 714      * @param out new output.
 715      * @return old output.
 716      * @see #getOutput
 717      */
 718     public TestOut setOutput(TestOut out) {
 719         return (TestOut) setProperty("output", out);
 720     }
 721 
 722     /**
 723      * Returns bundle manager.
 724      *
 725      * @return a BundleManager object representing the bundle manager value.
 726      * @see #setBundleManager
 727      */
 728     public BundleManager getBundleManager() {
 729         return (BundleManager) getProperty("resources");
 730     }
 731 
 732     /**
 733      * Changes bundle manager.
 734      *
 735      * @param resources new bundle manager.
 736      * @return old bundle manager
 737      * @see #getBundleManager
 738      */
 739     public BundleManager setBundleManager(BundleManager resources) {
 740         return (BundleManager) setProperty("resources", resources);
 741     }
 742 
 743     /**
 744      * Returns resource value.
 745      *
 746      * @param key Resource key.
 747      * @return resource value
 748      */
 749     public String getResource(String key) {
 750         return getBundleManager().getResource(key);
 751     }
 752 
 753     /**
 754      * Returns resource value from the specified bundle.
 755      *
 756      * @param bundleID Id of a bundle to get resource from.
 757      * @param key Resource key.
 758      * @return resource value
 759      */
 760     public String getResource(String bundleID, String key) {
 761         return getBundleManager().getResource(bundleID, key);
 762     }
 763 
 764     /**
 765      * Returns char binding map.
 766      *
 767      * @return the char binding map.
 768      * @see #setCharBindingMap
 769      */
 770     public CharBindingMap getCharBindingMap() {
 771         return (CharBindingMap) getProperty("binding.map");
 772     }
 773 
 774     /**
 775      * Changes char binding map.
 776      *
 777      * @param map new char binding map.
 778      * @return old char binding map.
 779      * @see #getCharBindingMap
 780      */
 781     public CharBindingMap setCharBindingMap(CharBindingMap map) {
 782         return (CharBindingMap) setProperty("binding.map", map);
 783     }
 784 
 785     /**
 786      * Returns the dispatching model.
 787      *
 788      * @return Event dispatching model.
 789      * @see #getCurrentDispatchingModel()
 790      * @see #setDispatchingModel(int)
 791      * @see #QUEUE_MODEL_MASK
 792      * @see #ROBOT_MODEL_MASK
 793      */
 794     public int getDispatchingModel() {
 795         return (Integer) getProperty("dispatching.model");
 796     }
 797 
 798     private static DriverInstaller getDriverInstaller(int model) {
 799         String name = System.getProperty("jemmy.drivers.installer");
 800         DriverInstaller installer = null;
 801         try {
 802             if (name != null && !(name.length() == 0)) {
 803                 installer = (DriverInstaller) new ClassReference(name).newInstance(null, null);
 804             }
 805         } catch (ClassNotFoundException
 806                 | IllegalAccessException
 807                 | NoSuchMethodException
 808                 | InstantiationException
 809                 | InvocationTargetException e) {
 810             getCurrentOutput().printLine("Cannot init driver installer:");
 811             getCurrentOutput().printStackTrace(e);
 812         }
 813         if (installer == null) {
 814             if (System.getProperty("os.name").startsWith("Mac OS X")) {
 815                 installer = new APIDriverInstaller((model & SHORTCUT_MODEL_MASK) != 0);
 816             } else {
 817                 installer = new DefaultDriverInstaller((model & SHORTCUT_MODEL_MASK) != 0);
 818             }
 819         }
 820         getCurrentOutput().printLine("Using " + installer.getClass().getName() + " driver installer");
 821         return installer;
 822     }
 823 
 824     /**
 825      * Specifies the dispatching model value.
 826      *
 827      * @param model New dispatching model value.
 828      * @return Previous dispatching model value.
 829      * @see #setCurrentDispatchingModel(int)
 830      * @see #getDispatchingModel()
 831      * @see #QUEUE_MODEL_MASK
 832      * @see #ROBOT_MODEL_MASK
 833      */
 834     public int setDispatchingModel(int model) {
 835         new InputDriverInstaller((model & ROBOT_MODEL_MASK) == 0, (model & SMOOTH_ROBOT_MODEL_MASK) != 0).install();
 836         getDriverInstaller(model).install();
 837         return (Integer) setProperty("dispatching.model", model);
 838     }
 839 
 840     /**
 841      * Returns the drag and drop step length value.
 842      *
 843      * @return Pixel count to move mouse during one drag'n'drop step.
 844      * @see #getCurrentDragAndDropStepLength()
 845      * @see #setDragAndDropStepLength(int)
 846      */
 847     public int getDragAndDropStepLength() {
 848         return (Integer) getProperty("drag_and_drop.step_length");
 849     }
 850 
 851     /**
 852      * Specifies the drag and drop step length value.
 853      *
 854      * @param length Pixel count to move mouse during one drag'n'drop step.
 855      * @return Previous value.
 856      * @see #setCurrentDragAndDropStepLength(int)
 857      * @see #getDragAndDropStepLength()
 858      */
 859     public int setDragAndDropStepLength(int length) {
 860         return (Integer) setProperty("drag_and_drop.step_length", length);
 861     }
 862 
 863     /**
 864      * Checks if "name" propery currently has a value.
 865      *
 866      * @param name Property name. Should by unique.
 867      * @return true if property was defined.
 868      * @see #setProperty(String, Object)
 869      * @see #getProperty(String)
 870      */
 871     public boolean contains(String name) {
 872         return properties.containsKey(name);
 873     }
 874 
 875     /**
 876      * Saves object as a static link to be used by other objects.
 877      *
 878      * @param name Property name. Should by unique.
 879      * @param newValue Property value.
 880      * @return Previous value of "name" property.
 881      * @see #setCurrentProperty(String, Object)
 882      * @see #getProperty(String)
 883      * @see #contains(String)
 884      */
 885     public Object setProperty(String name, Object newValue) {
 886         Object oldValue = null;
 887         if (contains(name)) {
 888             oldValue = properties.get(name);
 889             properties.remove(name);
 890         }
 891         properties.put(name, newValue);
 892         return oldValue;
 893     }
 894 
 895     /**
 896      * Returns the property value.
 897      *
 898      * @param name Property name. Should by unique.
 899      * @return Property value stored by setProperty(String, Object) method.
 900      * @see #getCurrentProperty(String)
 901      * @see #setProperty(String, Object)
 902      * @see #contains(String)
 903      */
 904     public Object getProperty(String name) {
 905         if (contains(name)) {
 906             return properties.get(name);
 907         } else {
 908             return null;
 909         }
 910     }
 911 
 912     /**
 913      * Removes the property.
 914      *
 915      * @param name A name of the property to be removed.
 916      * @return previous property value
 917      */
 918     public Object removeProperty(String name) {
 919         if (contains(name)) {
 920             return properties.remove(name);
 921         } else {
 922             return null;
 923         }
 924     }
 925 
 926     /**
 927      * Returns the key values.
 928      *
 929      * @return an array of Strings representing the key values.
 930      */
 931     public String[] getKeys() {
 932         Enumeration<String> keys = properties.keys();
 933         String[] result = new String[properties.size()];
 934         int i = 0;
 935         while (keys.hasMoreElements()) {
 936             result[i] = keys.nextElement();
 937             i++;
 938         }
 939         return result;
 940     }
 941 
 942     /**
 943      * Copy all properties from this instance into another.
 944      *
 945      * @param properties a JemmyProperties instance to copy properties into.
 946      */
 947     public void copyTo(JemmyProperties properties) {
 948         String[] keys = getKeys();
 949         for (String key : keys) {
 950             properties.setProperty(key, getProperty(key));
 951         }
 952         //some should be cloned
 953         properties.setTimeouts(getTimeouts().cloneThis());
 954         properties.setBundleManager(getBundleManager().cloneThis());
 955     }
 956 
 957     /**
 958      * Creates an exact copy on this instance.
 959      *
 960      * @return new JemmyProperties object.
 961      */
 962     protected JemmyProperties cloneThis() {
 963         JemmyProperties result = new JemmyProperties();
 964         copyTo(result);
 965         return result;
 966     }
 967 
 968     private static String extractValue(InputStream stream, String varName) {
 969         try {
 970             BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
 971             StringTokenizer token;
 972             String nextLine;
 973             while ((nextLine = reader.readLine()) != null) {
 974                 token = new StringTokenizer(nextLine, ":");
 975                 String nextToken = token.nextToken();
 976                 if (nextToken.trim().equals(varName)) {
 977                     return token.nextToken().trim();
 978                 }
 979             }
 980             return "";
 981         } catch (IOException e) {
 982             getCurrentOutput().printStackTrace(e);
 983             return "";
 984         }
 985     }
 986 
 987 }