1 /*
   2  * Copyright (c) 2014, 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 javafx.scene.control.test.util;
  26 
  27 import com.sun.javafx.geom.PickRay;
  28 import com.sun.javafx.geom.Vec3d;
  29 import com.sun.javafx.scene.input.PickResultChooser;
  30 import java.util.AbstractMap;
  31 import java.util.ArrayList;
  32 import java.util.Collection;
  33 import java.util.HashMap;
  34 import java.util.List;
  35 import java.util.Map;
  36 import java.util.Map.Entry;
  37 import javafx.event.ActionEvent;
  38 import javafx.event.EventHandler;
  39 import javafx.geometry.Orientation;
  40 import javafx.geometry.Point2D;
  41 import javafx.scene.Node;
  42 import javafx.scene.Scene;
  43 import javafx.scene.control.*;
  44 import javafx.scene.control.test.ControlsTestBase;
  45 import javafx.scene.control.test.utils.CommonPropertiesScene;
  46 import javafx.scene.control.test.utils.ComponentsFactory.MultipleIndexFormComponent;
  47 import javafx.scene.control.test.utils.ptables.AbstractApplicationPropertiesRegystry;
  48 import javafx.scene.control.test.utils.ptables.AbstractEventsCounter;
  49 import javafx.scene.control.test.utils.ptables.AbstractPropertiesTable;
  50 import static javafx.scene.control.test.utils.ptables.AbstractPropertiesTable.BIDIR_PREFIX;
  51 import static javafx.scene.control.test.utils.ptables.AbstractPropertiesTable.UNIDIR_PREFIX;
  52 import javafx.scene.control.test.utils.ptables.AbstractPropertyController.SettingType;
  53 import javafx.scene.control.test.utils.ptables.AbstractPropertyValueListener;
  54 import static javafx.scene.control.test.utils.ptables.AbstractPropertyValueSetter.CONTROLLER_SUFFIX;
  55 import static javafx.scene.control.test.utils.ptables.AbstractPropertyValueSetter.SET_PREFIX;
  56 import javafx.scene.control.test.utils.ptables.AbstractStateCheckable.StateChangedException;
  57 import static javafx.scene.control.test.utils.ptables.NodesChoserFactory.*;
  58 import javafx.scene.control.test.utils.ptables.PropertiesTable;
  59 import static javafx.scene.control.test.utils.ptables.PropertyValueListener.LISTENER_SUFFIX;
  60 import static javafx.scene.control.test.utils.ptables.TabPaneWithControl.TAB_CONTENT_ID;
  61 import static javafx.scene.control.test.utils.ptables.TabPaneWithControl.TAB_PANE_WITH_CONTROL_ID;
  62 import static javafx.scene.control.test.utils.ptables.TextFieldEventsCounter.COUNTER_SUFFIX;
  63 import static javafx.scene.control.test.utils.ptables.ToggleBindingSwitcher.BIND_BUTTON_SUFFIX;
  64 import javafx.scene.input.ScrollEvent;
  65 import javafx.scene.input.ScrollEvent.HorizontalTextScrollUnits;
  66 import javafx.scene.input.ScrollEvent.VerticalTextScrollUnits;
  67 import javafx.scene.text.Font;
  68 import org.jemmy.Point;
  69 import org.jemmy.Rectangle;
  70 import org.jemmy.action.Action;
  71 import org.jemmy.action.GetAction;
  72 import org.jemmy.control.Wrap;
  73 import org.jemmy.env.Timeout;
  74 import org.jemmy.fx.ByID;
  75 import org.jemmy.fx.NodeWrap;
  76 import org.jemmy.fx.Root;
  77 import org.jemmy.fx.control.TabPaneDock;
  78 import static org.jemmy.fx.control.TextControlWrap.SELECTED_PROP_NAME;
  79 import org.jemmy.input.AbstractScroll;
  80 import org.jemmy.interfaces.Keyboard.KeyboardModifiers;
  81 import org.jemmy.interfaces.Parent;
  82 import org.jemmy.interfaces.Selectable;
  83 import org.jemmy.interfaces.Text;
  84 import org.jemmy.lookup.LookupCriteria;
  85 import org.jemmy.timing.State;
  86 import org.jemmy.timing.Waiter;
  87 import static org.junit.Assert.*;
  88 import test.javaclient.shared.TestUtil;
  89 import test.javaclient.shared.Utils;
  90 
  91 /**
  92  * @author Alexander Kirov
  93  *
  94  * Used for many my tests. Don't touch it until you are sure, what you do.
  95  */
  96 public class UtilTestFunctions extends ControlsTestBase {
  97 
  98     protected static Parent<Node> parent;
  99     protected static KeyboardModifiers CTRL_DOWN_MASK_OS;
 100     protected static ChangingController defaultController;
 101     protected static final int ITERATIONS = 3;
 102     protected static final int SLEEP = 300;
 103     protected static double ASSERT_CMP_PRECISION = 3.5;
 104     protected static SettingOption currentSettingOption = SettingOption.PROGRAM;
 105     private static Parent<Node> propertiesTableScrollPane = null;
 106     private static final boolean useCaching = true;
 107     private static String currentTabName = AbstractApplicationPropertiesRegystry.DEFAULT_DOMAIN_NAME;
 108     private static final String CUSTOM_STYLE_CONST = "imagescomparator/custom_font.css";
 109     private String rememberedStylesheet;
 110 
 111     static {
 112         if (Utils.isMacOS()) {
 113             CTRL_DOWN_MASK_OS = KeyboardModifiers.META_DOWN_MASK;
 114         } else {
 115             CTRL_DOWN_MASK_OS = KeyboardModifiers.CTRL_DOWN_MASK;
 116         }
 117     }
 118 
 119     protected void initChangingController(Parent<Node> parent) {
 120         defaultController = new ChangingController(parent);
 121     }
 122 
 123     /**
 124      * Clicks toggle button with according id.
 125      *
 126      * @param toggleButtonId
 127      */
 128     protected static void clickToggleButton(String toggleButtonId) {
 129         findToggleButton(toggleButtonId).mouse().click();
 130     }
 131 
 132     /**
 133      * Checks, whether the toggle button has a toggled state as asked.
 134      *
 135      * @param toggleButtonId
 136      * @param selectedExpected
 137      */
 138     protected static void checkToggleButtonSelection(String toggleButtonId, boolean selectedExpected) {
 139         findToggleButton(toggleButtonId).waitProperty(SELECTED_PROP_NAME, selectedExpected);
 140     }
 141 
 142     /**
 143      * Change toggle button's state to value, with set id.
 144      *
 145      * @param buttonId
 146      */
 147     protected static void setToggleButtonSelectedStateForTestPurpose(final String buttonId, final Boolean newValue) {
 148         final Wrap<? extends ToggleButton> btnWrap = findToggleButton(buttonId);
 149         Boolean curValue = new GetAction<Boolean>() {
 150             @Override
 151             public void run(Object... os) throws Exception {
 152                 setResult(btnWrap.getControl().isSelected());
 153             }
 154         }.dispatch(Root.ROOT.getEnvironment());
 155 
 156         switch (currentSettingOption) {
 157             case MANUAL:
 158                 if (curValue != newValue) {
 159                     clickToggleButtonByID(buttonId);
 160                 }
 161                 break;
 162             case PROGRAM:
 163                 if (curValue != newValue) {
 164                     new GetAction() {
 165                         @Override
 166                         public void run(Object... os) throws Exception {
 167                             findToggleButton(buttonId).getControl().setSelected(newValue);
 168                         }
 169                     }.dispatch(Root.ROOT.getEnvironment());
 170                 }
 171                 break;
 172         }
 173     }
 174 
 175     /**
 176      * Clicks button with set id.
 177      *
 178      * @param buttonId
 179      */
 180     protected static void clickButtonForTestPurpose(final String buttonId) {
 181         switch (currentSettingOption) {
 182             case MANUAL:
 183                 clickButtonByID(buttonId);
 184                 break;
 185             case PROGRAM:
 186                 new GetAction() {
 187                     @Override
 188                     public void run(Object... os) throws Exception {
 189                         Button btn = findButton(buttonId).getControl();
 190                         EventHandler<ActionEvent> onAction = btn.getOnAction();
 191                         if (null != onAction) {
 192                             onAction.handle(null);
 193                         } else {
 194                             System.err.format("onAction event handler is not set for [%s]%n", buttonId);
 195                             btn.fire();
 196                         }
 197                     }
 198                 }.dispatch(Root.ROOT.getEnvironment());
 199                 break;
 200             default:
 201                 throw new IllegalStateException("Unknown option.");
 202         }
 203     }
 204 
 205     protected static void clickToggleButtonByID(String buttonId) {
 206         findToggleButton(buttonId).mouse().click();
 207     }
 208 
 209     protected static void clickButtonByID(String buttonId) {
 210         findButton(buttonId).mouse().click();
 211     }
 212 
 213     /**
 214      * Checks focused state of control.
 215      *
 216      * @param nodeId
 217      * @param isFocused
 218      */
 219     protected static void checkFocus(String nodeId, boolean isFocused) {
 220         findControl(nodeId).waitProperty("isFocused", isFocused);
 221     }
 222 
 223     // Two functions clicking arrows of the scrollBar.
 224     // This function make click over scrollBar (left or up arrow)
 225     protected static void clickLess(Wrap<? extends ScrollBar> wrap) {
 226         wrap.mouse().click(1, new Point(5, 5));
 227     }
 228 
 229     // This function make click over scrollBar (right or down arrow)
 230     protected static void clickMore(Wrap<? extends ScrollBar> wrap) {
 231         wrap.mouse().click(1, new Point(wrap.getScreenBounds().width - 5, wrap.getScreenBounds().height - 5));
 232     }
 233 
 234     /**
 235      * *******************************************************************
 236      *
 237      * SECTION OF MANUAL CONTROLING
 238      *
 239      ********************************************************************
 240      */
 241     //               DIRECT PROPERTIES CONTROL
 242     protected void requestFocusOnControl(final Wrap<? extends Node> control) {
 243         new GetAction() {
 244             @Override
 245             public void run(Object... os) throws Exception {
 246                 control.getControl().requestFocus();
 247             }
 248         }.dispatch(Root.ROOT.getEnvironment());
 249     }
 250 
 251     protected static void setSliderPosition(String id, final double toPosition, SettingOption option) throws InterruptedException {
 252         Thread.sleep(SLEEP);
 253         setSliderPosition(findSlider(id), toPosition, option);
 254     }
 255 
 256     protected static void setSliderPosition(final Wrap<? extends Slider> propertySliderWrap, final double toPosition, SettingOption option) throws InterruptedException {
 257         switch (option) {
 258             case MANUAL:
 259                 AbstractScroll abstrScroll = propertySliderWrap.as(AbstractScroll.class);
 260                 abstrScroll.allowError(ASSERT_CMP_PRECISION / 4);
 261                 abstrScroll.caret().to(toPosition);
 262                 break;
 263             case PROGRAM:
 264                 new GetAction<Point2D>() {
 265                     @Override
 266                     public void run(Object... os) throws Exception {
 267                         propertySliderWrap.getControl().setValue(toPosition);
 268                     }
 269                 }.dispatch(Root.ROOT.getEnvironment());
 270                 Thread.sleep(SLEEP);
 271         }
 272     }
 273 
 274     protected static void setChoiceBoxValue(String controlID, final Object value) {
 275         switch (currentSettingOption) {
 276             case MANUAL:
 277                 findChoiceBox(controlID).as(Selectable.class).selector().select(value);
 278                 //new org.jemmy.fx.control.ChoiceBoxDock(parent, controlID).asSelectable().selector().select(value);
 279                 //parent.lookup(ChoiceBox.class, new ByID<ChoiceBox>(getPrefix(type) + propType.toString().toUpperCase() + CHOICE_BOX_SUFFIX)).wrap().as(Selectable.class).selector().select(propValue.name());
 280                 break;
 281             case PROGRAM:
 282                 final Object actualValue = getChoiceBoxState(controlID);
 283                 if (!value.equals(actualValue)) {
 284                     final ChoiceBox cb = findChoiceBox(controlID).getControl();
 285                     new GetAction() {
 286                         @Override
 287                         public void run(Object... os) throws Exception {
 288                             cb.setValue(value);
 289                         }
 290                     }.dispatch(Root.ROOT.getEnvironment());
 291                 }
 292                 break;
 293         }
 294     }
 295 
 296     protected static void changePropertyControlBindingToggleButtonState(SettingType type, Enum propType) {
 297         changeToggleButtonState(getPrefix(type) + propType.toString().toUpperCase() + BIND_BUTTON_SUFFIX);
 298     }
 299 
 300     protected static void changeToggleButtonState(String controlID) {
 301         switch (currentSettingOption) {
 302             case MANUAL:
 303                 findToggleButton(controlID).mouse().click();
 304                 break;
 305             case PROGRAM:
 306                 final boolean selected = getToggleButtonState(controlID);
 307                 final ToggleButton tb = findToggleButton(controlID).getControl();
 308                 new GetAction() {
 309                     @Override
 310                     public void run(Object... os) throws Exception {
 311                         tb.setSelected(!selected);
 312                     }
 313                 }.dispatch(Root.ROOT.getEnvironment());
 314                 break;
 315         }
 316     }
 317 
 318     protected static void changeTextFieldText(String controlID, final String newText) {
 319         switch (currentSettingOption) {
 320             case MANUAL:
 321                 Text text = findTextField(controlID).as(Text.class);
 322                 text.clear();
 323                 text.type(newText);
 324                 break;
 325             case PROGRAM:
 326                 final TextField tf = findTextField(controlID).getControl();
 327                 new GetAction() {
 328                     @Override
 329                     public void run(Object... os) throws Exception {
 330                         tf.setText(newText);
 331                     }
 332                 }.dispatch(Root.ROOT.getEnvironment());
 333                 break;
 334         }
 335     }
 336 
 337     protected static void switchToPropertiesTab(final String tabName) {
 338         if (useCaching) {
 339             Cache.clear();
 340         }
 341         final Wrap<? extends TabPane> tabPane = parent.lookup(TabPane.class, new ByID<TabPane>(TAB_PANE_WITH_CONTROL_ID)).wrap();
 342         switch (currentSettingOption) {
 343             case MANUAL:
 344                 Tab tab = new GetAction<Tab>() {
 345                     @Override
 346                     public void run(Object... os) throws Exception {
 347                         for (Tab tab : tabPane.getControl().getTabs()) {
 348                             if (tab.getText().equals(tabName)) {
 349                                 setResult(tab);
 350                             }
 351                         }
 352                     }
 353                 }.dispatch(Root.ROOT.getEnvironment());
 354                 TabPaneDock dock = new TabPaneDock(tabPane);
 355                 dock.asSelectable().selector().select(tab);
 356                 break;
 357             case PROGRAM:
 358                 new GetAction() {
 359                     @Override
 360                     public void run(Object... os) throws Exception {
 361                         Tab tabToSelect = null;
 362                         for (Tab tab : tabPane.getControl().getTabs()) {
 363                             if (tab.getText().equals(tabName)) {
 364                                 tabToSelect = tab;
 365                             }
 366                         }
 367                         if (tabToSelect == null) {
 368                             throw new Exception("Tab with name <" + tabName + "> could not be found");
 369                         } else {
 370                             tabPane.getControl().getSelectionModel().select(tabToSelect);
 371                         }
 372                     }
 373                 }.dispatch(Root.ROOT.getEnvironment());
 374                 break;
 375         }
 376 
 377         //Wait, until content of according Tab (root item of tab is scrollPane,
 378         //with preset Id), will be findable on the scene.
 379         new Waiter(new Timeout("", TestUtil.isEmbedded() ? 20000 : 2000)).ensureState(new State() {
 380             public Object reached() {
 381                 if (parent.lookup(ScrollPane.class, new ByID<ScrollPane>(tabName + TAB_CONTENT_ID)).size() == 1) {
 382                     return true;
 383                 } else {
 384                     return null;
 385                 }
 386             }
 387         });
 388 
 389         propertiesTableScrollPane = parent.lookup(ScrollPane.class, new ByID<ScrollPane>(tabName + javafx.scene.control.test.utils.ptables.TabPaneWithControl.TAB_CONTENT_ID)).wrap().as(Parent.class, Node.class);
 390         currentTabName = tabName;
 391     }
 392 
 393     //              WORKING WITH LISTENING TEXT FIELDS
 394     protected static void checkText(String textFieldId, String expectedText) {
 395         findTextField(textFieldId).waitProperty(Wrap.TEXT_PROP_NAME, expectedText);
 396     }
 397 
 398     protected static void checkTextFieldText(Enum propType, String expectedText) {
 399         findTextField(propType.toString().toUpperCase() + LISTENER_SUFFIX).waitProperty(Wrap.TEXT_PROP_NAME, expectedText);
 400         if (defaultController != null) {
 401             defaultController.removeProperty(currentTabName, propType);
 402         }
 403     }
 404 
 405     protected static void checkTextFieldTextContaining(final Enum propType, final String expectedContainedText) {
 406         final TextField tf = findTextField(propType.toString().toUpperCase() + LISTENER_SUFFIX).getControl();
 407         new Waiter(new Timeout("", TestUtil.isEmbedded() ? 20000 : 2000)).ensureState(new State() {
 408             public Object reached() {
 409                 if (tf.getText().contains(expectedContainedText)) {
 410                     return true;
 411                 } else {
 412                     return null;
 413                 }
 414             }
 415         });
 416 
 417         if (defaultController != null) {
 418             defaultController.removeProperty(currentTabName, propType);
 419         }
 420     }
 421 
 422     protected static void checkTextFieldValue(Enum propType, final double expectedValue) {
 423         final Wrap<? extends TextField> tfwrap = findTextField(propType.toString().toUpperCase() + LISTENER_SUFFIX);
 424         new Waiter(new Timeout("", TestUtil.isEmbedded() ? 20000 : 2000)).ensureState(new State() {
 425             public Object reached() {
 426                 if (Math.abs(Double.parseDouble(tfwrap.getControl().getText()) - expectedValue) < ASSERT_CMP_PRECISION) {
 427                     return true;
 428                 } else {
 429                     return null;
 430                 }
 431             }
 432         });
 433 
 434         if (defaultController != null) {
 435             defaultController.removeProperty(currentTabName, propType);
 436         }
 437     }
 438 
 439     protected static void checkTextFieldValue(Enum propType, final int expectedValue, final double COMPARISON_DELTA) {
 440         final Wrap<? extends TextField> tfwrap = findTextField(propType.toString().toUpperCase() + LISTENER_SUFFIX);
 441         new Waiter(new Timeout("", TestUtil.isEmbedded() ? 20000 : 2000)).ensureState(new State() {
 442             double actual;
 443 
 444             public Object reached() {
 445                 actual = Double.parseDouble(tfwrap.getControl().getText());
 446                 if (Math.abs(actual - expectedValue) <= COMPARISON_DELTA) {
 447                     return true;
 448                 } else {
 449                     return null;
 450                 }
 451             }
 452 
 453             @Override
 454             public String toString() {
 455                 return String.format("[Expected:%d, got:%.6f, delta:%.6f]",
 456                         expectedValue, actual, COMPARISON_DELTA);
 457             }
 458         });
 459 
 460         if (defaultController != null) {
 461             defaultController.removeProperty(currentTabName, propType);
 462         }
 463     }
 464 
 465     protected static void checkTextFieldValue(Enum propType, int expectedValue) {//more strict for int values.
 466         checkTextFieldValue(propType, expectedValue, ASSERT_CMP_PRECISION);
 467     }
 468 
 469     protected static void checkSimpleListenerValue(Enum propType, final int expectedValue) {
 470         final Wrap<? extends TextField> tfwrap = findTextField(propType.toString().toUpperCase() + LISTENER_SUFFIX);
 471         new Waiter(new Timeout("", TestUtil.isEmbedded() ? 20000 : 2000)).ensureState(new State() {
 472             public Object reached() {
 473                 if (Math.abs(Double.parseDouble(tfwrap.getControl().getText()) - expectedValue) == 0) {
 474                     return true;
 475                 } else {
 476                     System.err.println("Diff : tfwrap.getControl().getText() = " + tfwrap.getControl().getText() + "; expectedValue = " + expectedValue);
 477                     return null;
 478                 }
 479             }
 480         });
 481 
 482         if (defaultController != null) {
 483             defaultController.removeProperty(currentTabName, propType);
 484         }
 485     }
 486 
 487     protected static void checkFontSize(Enum property, Double expectedSize) {
 488         final Wrap<? extends TextField> tfwrap = findTextField(property.toString().toUpperCase() + LISTENER_SUFFIX);
 489         String fontDescription = new GetAction<String>() {
 490             @Override
 491             public void run(Object... os) throws Exception {
 492                 setResult(tfwrap.getControl().getText());
 493             }
 494         }.dispatch(Root.ROOT.getEnvironment());
 495 
 496         String fontSize = fontDescription.substring(fontDescription.indexOf("size=") + "size=".length(), fontDescription.indexOf(']', fontDescription.indexOf("size=")));
 497         assertEquals(Double.parseDouble(fontSize), expectedSize, 0.5);
 498     }
 499 
 500     protected static void checkSimpleListenerValue(Enum propType, String expectedText) {
 501         findTextField(propType.toString().toUpperCase() + LISTENER_SUFFIX).waitProperty(Wrap.TEXT_PROP_NAME, expectedText);
 502     }
 503 
 504     protected static void checkCounterValue(Enum propType, int expectedValue) {
 505         checkCounterValue(propType.toString().toUpperCase(), expectedValue);
 506     }
 507 
 508     protected static void checkCounterValue(String counterName, final int expectedValue) {
 509         final Wrap<? extends TextField> tfwrap = findTextField(counterName.toUpperCase() + COUNTER_SUFFIX);
 510         new Waiter(new Timeout("", TestUtil.isEmbedded() ? 20000 : 5000)).ensureState(new State() {
 511             public Object reached() {
 512                 if (Math.abs(Double.parseDouble(tfwrap.getControl().getText()) - expectedValue) == 0) {
 513                     return true;
 514                 } else {
 515                     return null;
 516                 }
 517             }
 518 
 519             @Override
 520             public String toString() {
 521                 return String.format("Expected value: %d, actual: %f.2", expectedValue, Double.parseDouble(tfwrap.getControl().getText()));
 522             }
 523         });
 524 
 525         if (defaultController != null) {
 526             defaultController.removeCounter(currentTabName, counterName);
 527         }
 528     }
 529 
 530     //                            SEARCHERS
 531     protected static Wrap<? extends ScrollBar> findScrollBar(final Parent<Node> parentWrap, final Orientation orientation, final Boolean visible) {
 532         final LookupCriteria<ScrollBar> lc = new LookupCriteria<ScrollBar>() {
 533             public boolean check(ScrollBar cntrl) {
 534                 return ((cntrl.isVisible() == visible) && (cntrl.getOrientation() == orientation));
 535             }
 536         };
 537 
 538         try {
 539             new Waiter(new Timeout("", TestUtil.isEmbedded() ? 20000 : 2000)).ensureState(new State() {
 540                 public Object reached() {
 541                     if (parentWrap.lookup(ScrollBar.class, lc).size() > 0) {
 542                         return true;
 543                     } else {
 544                         return null;
 545                     }
 546                 }
 547             });
 548         } catch (Exception ex) {
 549             return null;
 550         }
 551 
 552         return parentWrap.lookup(ScrollBar.class, lc).wrap();
 553     }
 554 
 555     protected static Wrap<? extends ScrollBar> findScrollBar(Parent<Node> parentWrap, Orientation orientation) {
 556         return findScrollBar(parentWrap, orientation, true);
 557     }
 558 
 559     protected static Wrap<? extends ScrollBar> findScrollBar(Parent<Node> parentWrap) {
 560         Wrap<? extends ScrollBar> sb1, sb2;
 561         sb1 = findScrollBar(parentWrap, Orientation.VERTICAL);
 562         sb2 = findScrollBar(parentWrap, Orientation.HORIZONTAL);
 563         if (sb1 != null) {
 564             return sb1;
 565         } else {
 566             return sb2;
 567         }
 568     }
 569 
 570     //                                CONTROL
 571     protected static void setPropertyBySlider(SettingType type, Enum propType, double toPosition) throws InterruptedException {
 572         if (type == SettingType.SETTER) {
 573             switchOffBinding(SettingType.UNIDIRECTIONAL, propType);
 574             setSliderPosition(getPrefix(SettingType.UNIDIRECTIONAL) + propType.toString().toUpperCase() + CONTROLLER_SUFFIX, toPosition, currentSettingOption);
 575             clickButtonForTestPurpose(SET_PREFIX + propType.toString().toUpperCase());
 576         } else {
 577             switchOnBinding(type, propType);
 578             setSliderPosition(getPrefix(type) + propType.toString().toUpperCase() + CONTROLLER_SUFFIX, toPosition, currentSettingOption);
 579         }
 580 
 581         if (defaultController != null) {
 582             defaultController.removeProperty(currentTabName, propType);
 583         }
 584     }
 585 
 586     protected static void setPropertyByToggleClick(SettingType type, Enum propType) {
 587         if (type == SettingType.SETTER) {
 588             switchOffBinding(SettingType.UNIDIRECTIONAL, propType);
 589             changeToggleButtonState(getPrefix(SettingType.UNIDIRECTIONAL) + propType.toString().toUpperCase() + CONTROLLER_SUFFIX);
 590             clickButtonForTestPurpose(SET_PREFIX + propType.toString().toUpperCase());
 591         } else {
 592             switchOnBinding(type, propType);
 593             changeToggleButtonState(getPrefix(type) + propType.toString().toUpperCase() + CONTROLLER_SUFFIX);
 594         }
 595 
 596         if (defaultController != null) {
 597             defaultController.removeProperty(currentTabName, propType);
 598         }
 599     }
 600 
 601     protected static void setPropertyByToggleClick(SettingType type, Enum propType, Boolean newVal) {
 602 
 603         if (type == SettingType.SETTER) {
 604             String btnID = getPrefix(SettingType.UNIDIRECTIONAL) + propType.toString().toUpperCase() + CONTROLLER_SUFFIX;
 605             final Wrap<? extends ToggleButton> btnWrap = findToggleButton(btnID);
 606 
 607             Boolean curVal = new GetAction<Boolean>() {
 608                 @Override
 609                 public void run(Object... os) throws Exception {
 610                     setResult(btnWrap.getControl().isSelected());
 611                 }
 612             }.dispatch(Root.ROOT.getEnvironment());
 613 
 614             switchOffBinding(SettingType.UNIDIRECTIONAL, propType);
 615             if (curVal != newVal) {
 616                 setToggleButtonSelectedStateForTestPurpose(btnID, newVal);
 617             }
 618             clickButtonForTestPurpose(SET_PREFIX + propType.toString().toUpperCase());
 619         } else {
 620             String btnID = getPrefix(type) + propType.toString().toUpperCase() + CONTROLLER_SUFFIX;
 621             setToggleButtonSelectedStateForTestPurpose(btnID, newVal);
 622             switchOnBinding(type, propType);
 623         }
 624 
 625         if (defaultController != null) {
 626             defaultController.removeProperty(currentTabName, propType);
 627         }
 628     }
 629 
 630     protected static void setPropertyByChoiceBox(SettingType type, Object propValue, Enum propType) {
 631         if (type == SettingType.SETTER) {
 632             switchOffBinding(SettingType.UNIDIRECTIONAL, propType);
 633             setChoiceBoxValue(getPrefix(SettingType.UNIDIRECTIONAL) + propType.toString().toUpperCase() + CONTROLLER_SUFFIX, propValue);
 634             clickButtonForTestPurpose(SET_PREFIX + propType.toString().toUpperCase());
 635         } else {
 636             switchOnBinding(type, propType);
 637             setChoiceBoxValue(getPrefix(type) + propType.toString().toUpperCase() + CONTROLLER_SUFFIX, propValue);
 638         }
 639 
 640         if (defaultController != null) {
 641             defaultController.removeProperty(currentTabName, propType);
 642         }
 643     }
 644 
 645     protected static void setPropertyByTextField(SettingType type, Enum propType, String value) {
 646         if (type == SettingType.SETTER) {
 647             switchOffBinding(SettingType.UNIDIRECTIONAL, propType);
 648             changeTextFieldText(getPrefix(SettingType.UNIDIRECTIONAL) + propType.toString().toUpperCase() + CONTROLLER_SUFFIX, value);
 649             clickButtonForTestPurpose(SET_PREFIX + propType.toString().toUpperCase());
 650         } else {
 651             switchOnBinding(type, propType);
 652             changeTextFieldText(getPrefix(type) + propType.toString().toUpperCase() + CONTROLLER_SUFFIX, value);
 653         }
 654 
 655         if (defaultController != null) {
 656             defaultController.removeProperty(currentTabName, propType);
 657         }
 658     }
 659 
 660     protected void selectControlFromFactory(final int index) {
 661         final Wrap<? extends ChoiceBox> choice = (Wrap<? extends ChoiceBox>) findControl(NODE_CHOSER_CHOICE_BOX_ID);
 662         new GetAction() {
 663             @Override
 664             public void run(Object... os) throws Exception {
 665                 choice.getControl().getSelectionModel().select(index);
 666             }
 667         }.dispatch(Root.ROOT.getEnvironment());
 668     }
 669 
 670     protected void selectControlFromFactory(final Class controlClass) {
 671         final Wrap<? extends ChoiceBox> choice = (Wrap<? extends ChoiceBox>) findControl(NODE_CHOSER_CHOICE_BOX_ID);
 672         new GetAction() {
 673             @Override
 674             public void run(Object... os) throws Exception {
 675                 for (Object item : choice.getControl().getItems()) {
 676                     if (item.getClass() == controlClass) {
 677                         choice.getControl().getSelectionModel().select(item);
 678                     }
 679                 }
 680 
 681             }
 682         }.dispatch(Root.ROOT.getEnvironment());
 683     }
 684 
 685     protected int controlsFactorySize() {
 686         final Wrap<? extends ChoiceBox> choice = (Wrap<? extends ChoiceBox>) findControl(NODE_CHOSER_CHOICE_BOX_ID);
 687         return new GetAction<Integer>() {
 688             @Override
 689             public void run(Object... os) throws Exception {
 690                 setResult(choice.getControl().getItems().size());
 691             }
 692         }.dispatch(Root.ROOT.getEnvironment());
 693     }
 694 
 695     /*
 696      * This is complex method. It should be able to select different fonts, and
 697      * different objects at all. So it will be executed always in a way, like
 698      * "program" (not manual) execution done.
 699      */
 700     protected static void selectObjectFromChoiceBox(SettingType settingType, Enum property, Object whichToChose) {
 701         Wrap<? extends ChoiceBox> choice;
 702         if (settingType == SettingType.SETTER) {
 703             switchOffBinding(SettingType.UNIDIRECTIONAL, property);
 704             choice = findChoiceBox(getPrefix(SettingType.UNIDIRECTIONAL) + property.toString().toUpperCase() + CONTROLLER_SUFFIX);
 705         } else {
 706             choice = findChoiceBox(getPrefix(settingType) + property.toString().toUpperCase() + CONTROLLER_SUFFIX);
 707         }
 708 
 709         selectObjectFromChoiceBox(choice, whichToChose);
 710 
 711         if (settingType.equals(SettingType.SETTER)) {
 712             clickButtonForTestPurpose(SET_PREFIX + property.toString().toUpperCase());
 713         } else {
 714             switchOnBinding(settingType, property);
 715         }
 716 
 717         if (defaultController != null) {
 718             defaultController.removeProperty(currentTabName, property);
 719         }
 720     }
 721 
 722     protected static void selectObjectFromChoiceBox(Wrap<? extends ChoiceBox> choiceBox, final Object whatToChose) {
 723         switch (currentSettingOption) {
 724             case MANUAL:
 725                 choiceBox.as(Selectable.class).selector().select(whatToChose);
 726                 break;
 727             case PROGRAM:
 728                 new GetAction<Object>() {
 729                     @Override
 730                     public void run(Object... os) throws Exception {
 731                         ChoiceBox choiceBox = (ChoiceBox) os[0];
 732                         Collection allObjects = choiceBox.getItems();
 733                         Object actualToChose = null;
 734 
 735                         if (whatToChose instanceof Font) {
 736 
 737                             for (Object font : allObjects) {
 738                                 //Supposed - font are different in their color.
 739                                 if ((font instanceof Font) && (((Font) font).getSize() == ((Font) whatToChose).getSize())) {
 740                                     actualToChose = font;
 741                                 }
 742                             }
 743                         } else {
 744                             for (Object obj : allObjects) {
 745                                 if ((obj != null) && obj.getClass().equals(whatToChose)) {
 746                                     actualToChose = obj;
 747                                 }
 748                                 if ((obj != null) && obj.equals(whatToChose)) {
 749                                     actualToChose = obj;
 750                                 }
 751                             }
 752                             if (actualToChose == null) {
 753                                 System.out.println("Select NULL (Possibly, nothing found to select).");
 754                             }
 755                         }
 756 
 757                         ChoiceBox choice = (ChoiceBox) os[0];
 758                         if (actualToChose == null) {
 759                             choice.setValue(null);
 760                         } else {
 761                             choice.getSelectionModel().select(actualToChose);
 762                         }
 763                     }
 764                 }.dispatch(Root.ROOT.getEnvironment(), choiceBox.getControl());
 765                 break;
 766         }
 767     }
 768 
 769     /**
 770      * Send ScrollEvent in the center of the control
 771      *
 772      * @param wrap Wrap, which will receive event
 773      * @param scrollX Number of pixels to scroll by x coordinate
 774      * @param scrollY Number of pixels to scroll by y coordinate
 775      */
 776     protected static void sendScrollEvent(final Wrap<? extends Scene> scene, Wrap<? extends Node> wrap, double scrollX, double scrollY) {
 777         double x = wrap.getScreenBounds().width / 4;
 778         double y = wrap.getScreenBounds().height / 4;
 779         sendScrollEvent(scene, wrap, scrollX, scrollY, HorizontalTextScrollUnits.NONE, scrollX, VerticalTextScrollUnits.NONE, scrollY, x, y, wrap.getScreenBounds().x + x, wrap.getScreenBounds().y + y);
 780     }
 781 
 782     protected static void sendScrollEvent(final Wrap<? extends Scene> scene, final Wrap<? extends Node> wrap,
 783             double _scrollX, double _scrollY,
 784             HorizontalTextScrollUnits _scrollTextXUnits, double _scrollTextX,
 785             VerticalTextScrollUnits _scrollTextYUnits, double _scrollTextY,
 786             double _x, double _y,
 787             double _screenX, double _screenY) {
 788         //For 2.1.0 :
 789         //final ScrollEvent scrollEvent = ScrollEvent.impl_scrollEvent(_scrollX, _scrollY, _scrollTextXUnits, _scrollTextX, _scrollTextYUnits, _scrollTextY, _x, _y, _screenX, _screenY, false, false, false, false);
 790         //For 2.2.0 :
 791         //Interpretation: EventType<ScrollEvent> eventType, double _scrollX, double _scrollY, double _totalScrollX, double _totalScrollY, HorizontalTextScrollUnits _scrollTextXUnits, double _scrollTextX, VerticalTextScrollUnits _scrollTextYUnits, double _scrollTextY, int _touchPoints, double _x, double _y, double _screenX, double _screenY, boolean _shiftDown, boolean _controlDown, boolean _altDown, boolean _metaDown, boolean _direct, boolean _inertia)
 792         //For 8.0 before b64 and RT-9383
 793         //final ScrollEvent scrollEvent = new ScrollEvent.impl_scrollEvent(ScrollEvent.SCROLL, _scrollX, _scrollY, _scrollX, _scrollY, _scrollTextXUnits, _scrollTextX, _scrollTextYUnits, _scrollTextY, 0, _x, _y, _screenX, _screenY, false, false, false, false, false, false);
 794 
 795         //new ScrollEvent(EventType<ScrollEvent> eventType,
 796         //double x, double y, double screenX, double screenY,
 797         //boolean shiftDown, boolean controlDown, boolean altDown, boolean metaDown,
 798         //boolean direct, boolean inertia, double deltaX, double deltaY, double gestureDeltaX, double gestureDeltaY,
 799         //ScrollEvent.HorizontalTextScrollUnits textDeltaXUnits, double textDeltaX,
 800         //ScrollEvent.VerticalTextScrollUnits textDeltaYUnits, double textDeltaY, int touchCount)
 801         final ScrollEvent scrollEvent = new ScrollEvent(ScrollEvent.SCROLL,
 802                 _x, _y, _screenX, _screenY,
 803                 false, false, false, false,
 804                 false, false, _scrollX, _scrollY, 0, 0,
 805                 _scrollTextXUnits, _scrollTextX,
 806                 _scrollTextYUnits, _scrollTextY, 0, null /* PickResult?*/);
 807 
 808         wrap.getEnvironment().getExecutor().execute(wrap.getEnvironment(), true, new Action() {
 809             @Override
 810             public void run(Object... os) throws Exception {
 811                 Wrap<? extends Node> wrap = ((Wrap<? extends Node>) os[2]);
 812                 Point2D pointOnScene = new GetAction<Point2D>() {
 813                     @Override
 814                     public void run(Object... os) throws Exception {
 815                         setResult(((Node) os[0]).localToScene((Double) os[1], (Double) os[2]));
 816                     }
 817                 }.dispatch(Root.ROOT.getEnvironment(), wrap.getControl(), Double.valueOf(wrap.getScreenBounds().width / 4), Double.valueOf(wrap.getScreenBounds().height / 4));
 818                 final PickResultChooser result = new PickResultChooser();
 819 
 820                 //public PickRay(Vec3d origin, Vec3d direction, double nearClip, double farClip) {
 821                 (((Wrap<? extends Scene>) os[0]).getControl()).getRoot().impl_pickNode(new PickRay(new Vec3d(pointOnScene.getX(), pointOnScene.getY(), -10), new Vec3d(0, 0, 1), 1.0, 100), result);
 822                 Node node = result.getIntersectedNode();
 823                 node.fireEvent(scrollEvent);
 824             }
 825         }, scene, scrollEvent, wrap);
 826     }
 827 
 828     protected static void switchOnBinding(SettingType type, Enum propType) {
 829         if (!getToggleButtonState(getPrefix(type) + propType.toString().toUpperCase() + BIND_BUTTON_SUFFIX)) {
 830             changePropertyControlBindingToggleButtonState(type, propType);
 831         }
 832     }
 833 
 834     protected static void switchOffBinding(SettingType type, Enum propType) {
 835         if (getToggleButtonState(getPrefix(type) + propType.toString().toUpperCase() + BIND_BUTTON_SUFFIX)) {
 836             changePropertyControlBindingToggleButtonState(type, propType);
 837         }
 838     }
 839 
 840     private static boolean getToggleButtonState(String controlID) {
 841         final ToggleButton tb = findToggleButton(controlID).getControl();
 842         return new GetAction<Boolean>() {
 843             @Override
 844             public void run(Object... os) throws Exception {
 845                 setResult(tb.selectedProperty().getValue());
 846             }
 847         }.dispatch(Root.ROOT.getEnvironment());
 848     }
 849 
 850     private static Object getChoiceBoxState(String controlID) {
 851         final ChoiceBox cb = findChoiceBox(controlID).getControl();
 852         return new GetAction<Object>() {
 853             @Override
 854             public void run(Object... os) throws Exception {
 855                 setResult(cb.valueProperty().getValue());
 856             }
 857         }.dispatch(Root.ROOT.getEnvironment());
 858     }
 859 
 860     private static String getPrefix(SettingType type) {
 861         if (type == SettingType.BIDIRECTIONAL) {
 862             return BIDIR_PREFIX;
 863         } else {
 864             return UNIDIR_PREFIX;
 865         }
 866     }
 867 
 868     protected static void setText(final Wrap<? extends TextField> tf, final int value) {
 869         setText(tf, String.valueOf(value));
 870     }
 871 
 872     protected static void setText(final Wrap<? extends TextField> tf, final String value) {
 873         new GetAction() {
 874             @Override
 875             public void run(Object... os) throws Exception {
 876                 tf.getControl().setText(value);
 877             }
 878         }.dispatch(Root.ROOT.getEnvironment());
 879     }
 880 
 881     protected static void setCheckBoxState(String controlId, final boolean checked) {
 882         final Wrap<? extends CheckBox> wrap = (Wrap<? extends CheckBox>) findControl(controlId);
 883         new GetAction() {
 884             @Override
 885             public void run(Object... os) throws Exception {
 886                 wrap.getControl().setSelected(checked);
 887             }
 888         }.dispatch(Root.ROOT.getEnvironment());
 889     }
 890 
 891     /*
 892      * FINDERS. Needed for ControlTabs. The problem is with TabPane. Its content
 893      * (tabs' content) is not removed from SceneGraph, but just made invisible.
 894      * So there are many controls with the same id, because they correspond to
 895      * the same properties (by name) of different controls of the same class.
 896      * So, sometimes, we need to find them in scrollPane, which is inside some
 897      * Tab and its ID corresponds to name of tab.
 898      */
 899     protected static Wrap<? extends Button> findButton(String id) {
 900         return (Wrap<? extends Button>) findControl(id);
 901     }
 902 
 903     protected static Wrap<? extends TextField> findTextField(String id) {
 904         return (Wrap<? extends TextField>) findControl(id);
 905     }
 906 
 907     protected static Wrap<? extends Slider> findSlider(String id) {
 908         return (Wrap<? extends Slider>) findControl(id);
 909     }
 910 
 911     protected static Wrap<? extends ToggleButton> findToggleButton(String id) {
 912         return (Wrap<? extends ToggleButton>) findControl(id);
 913     }
 914 
 915     protected static Wrap<? extends ChoiceBox> findChoiceBox(String id) {
 916         return (Wrap<? extends ChoiceBox>) findControl(id);
 917     }
 918 
 919     protected static Wrap<? extends CheckBox> findCheckBox(String id) {
 920         return (Wrap<? extends CheckBox>) findControl(id);
 921     }
 922 
 923     protected static Wrap<? extends Control> findControl(String id) {
 924         if (useCaching) {
 925             Wrap cached = Cache.search(id);
 926             if (cached != null) {
 927                 return cached;
 928             }
 929         }
 930         if ((propertiesTableScrollPane != null) && (propertiesTableScrollPane.lookup(Control.class, new ByID(id)).size() > 0)) {
 931             return propertiesTableScrollPane.lookup(Control.class, new ByID<Control>(id)).wrap();
 932         } else {
 933             if (parent.lookup(Control.class, new ByID(id)).size() == 1) {
 934                 Wrap found = parent.lookup(Control.class, new ByID<Control>(id)).wrap();
 935                 if (useCaching) {
 936                     if (Cache.search(id) != null) {
 937                         throw new IllegalStateException("We tried to lookup cached wrap. We shouldn't be here.");
 938                     } else {
 939                         Cache.add(id, found);
 940                     }
 941                 }
 942                 return found;
 943             } else {
 944                 if (parent.lookup(Control.class, new ByID(id)).size() > 1) {
 945                     /**
 946                      * If you are here, possibly, you didn't call
 947                      * switchToPropertiesTab() before specifying a property
 948                      * value, which appears on several tabs.
 949                      */
 950                     throw new RuntimeException("Not unique ID!!! (" + id + ").");
 951                 } else {
 952                     throw new RuntimeException("Not found!!! (" + id + ").");
 953                 }
 954             }
 955         }
 956     }
 957 
 958     protected double adjustValue(double min, double max, double value) {
 959         if (min > max) {
 960             throw new IllegalArgumentException("Min must be less then max: min=" + min + "; max=" + max);
 961         }
 962 
 963         if (value < min) {
 964             return min;
 965         }
 966 
 967         if (value > max) {
 968             return max;
 969         }
 970 
 971         return value;
 972     }
 973 
 974     protected void clearCache() {
 975         Cache.clear();
 976     }
 977 
 978     public static String convertMultiIndicesToString(int... indices) {
 979         StringBuilder builder = new StringBuilder();
 980         for (int i : indices) {
 981             builder.append(i);
 982             builder.append(MultipleIndexFormComponent.INDICES_DELIMITER);
 983         }
 984         builder.delete(builder.length() - MultipleIndexFormComponent.INDICES_DELIMITER.length(), builder.length());
 985         return builder.toString();
 986     }
 987 
 988     protected void provideSpaceForControl(int width, int height, boolean nonTestedControlsVisibility) {
 989         Wrap<? extends Scene> scene = Root.ROOT.lookup().wrap();
 990         ((CommonPropertiesScene) scene.getControl()).setNonTestedContentVisibility(nonTestedControlsVisibility);
 991         ((CommonPropertiesScene) scene.getControl()).setTestedControlContainerSize(width, height);
 992     }
 993 
 994     protected Wrap<? extends Node> getParentWrap(Wrap<? extends Node> node) {
 995         final Node control = node.getControl();
 996         return new NodeWrap(node.getEnvironment(), new GetAction<Node>(){
 997 
 998             @Override
 999             public void run(Object... os) throws Exception {
1000                 setResult(control.getParent());
1001             }
1002         }.dispatch(Root.ROOT.getEnvironment()));
1003     }
1004 
1005     /**
1006      * Checks, that rec1 rel rec2.
1007      */
1008     public static void checkRectanglesRelation(Rectangle rec1, RectanglesRelations rel, Rectangle rec2) {
1009         switch (rel) {
1010             case ABOVE:
1011                 assertTrue(rec1.y + rec1.height <= rec2.y);
1012                 break;
1013             case BELOW:
1014                 assertTrue(rec1.y >= rec2.y + rec2.height);
1015                 break;
1016             case CONTAINS:
1017                 assertTrue(rec1.contains(rec2));
1018                 break;
1019             case ISCONTAINED:
1020                 assertTrue(rec2.contains(rec1));
1021                 break;
1022             case RIGHTER:
1023                 assertTrue(rec1.x >= rec2.x + rec2.width);
1024                 break;
1025             case LEFTER:
1026                 assertTrue(rec1.x + rec1.width <= rec2.x);
1027                 break;
1028             case CENTERED_IN_HORIZONTAL:
1029                 //Centered, relative to horizontal dimension.
1030                 assertEquals(rec1.y + rec1.height / 2.0, rec2.y + rec2.height / 2.0, 1.0);
1031                 break;
1032             case CENTERED_IN_VERTICAL:
1033                 //Centered, relative to vertical dimension.
1034                 assertEquals(rec1.x + rec1.width / 2.0, rec2.x + rec2.width / 2.0, 1.0);
1035                 break;
1036             default:
1037                 throw new IllegalStateException("No case provided!");
1038         }
1039     }
1040 
1041     protected void removeStylesheet() {
1042         new GetAction() {
1043             @Override
1044             public void run(Object... os) throws Exception {
1045                 Scene scene = parent.lookup().wrap().getControl().getScene();
1046                 for (String str : scene.getStylesheets()) {
1047                     if (str.contains(CUSTOM_STYLE_CONST)) {
1048                         rememberedStylesheet = str;
1049                         scene.getStylesheets().remove(str);
1050                         return;
1051                     }
1052                 }
1053             }
1054         }.dispatch(Root.ROOT.getEnvironment());
1055     }
1056 
1057     protected void restoreStylesheet() {
1058         assertNotNull(rememberedStylesheet);
1059         new GetAction() {
1060             @Override
1061             public void run(Object... os) throws Exception {
1062                 Scene scene = parent.lookup().wrap().getControl().getScene();
1063                 scene.getStylesheets().add(rememberedStylesheet);
1064             }
1065         }.dispatch(Root.ROOT.getEnvironment());
1066         rememberedStylesheet = null;
1067     }
1068 
1069     private static class Cache {
1070 
1071         private static Map<String, Wrap> cache = new HashMap<String, Wrap>();
1072         private static Parent<Node> parentUsedForCaching = null;
1073 
1074         private static void add(String id, Wrap cachedValue) {
1075             if (parent.equals(parentUsedForCaching)) {
1076                 cache.put(id, cachedValue);
1077             } else {
1078                 cache.clear();
1079                 parentUsedForCaching = parent;
1080                 cache.put(id, cachedValue);
1081             }
1082         }
1083 
1084         private static Wrap search(String id) {
1085             if (parent.equals(parentUsedForCaching)) {
1086                 return cache.get(id);
1087             } else {
1088                 cache.clear();
1089                 parentUsedForCaching = parent;
1090                 return null;
1091             }
1092         }
1093 
1094         private static void clear() {
1095             cache.clear();
1096         }
1097     }
1098 
1099     /**
1100      * Use case:
1101      *
1102      * @Test public void testTest() throws InterruptedException {
1103      * initChangingController(parent);
1104      * defaultController.include().allTables().allProperties().allCounters().apply();
1105      * defaultController.fixCurrentState();
1106      * defaultController.provideInfoForCodeCompletion(TestBase.Properties.values(),
1107      * null);
1108      *
1109      * assertEquals((new Slider()).maxProperty().getValue(), 100,
1110      * ASSERT_CMP_PRECISION);//initial value
1111      *
1112      * setPropertyBySlider(AbstractPropertyController.SettingType.BIDIRECTIONAL,
1113      * TestBase.Properties.max, -10);
1114      * checkTextFieldValue(TestBase.Properties.value, -10);
1115      * checkTextFieldValue(TestBase.Properties.min, -10);
1116      * checkTextFieldValue(TestBase.Properties.max, -10);
1117      *
1118      * setPropertyBySlider(AbstractPropertyController.SettingType.BIDIRECTIONAL,
1119      * TestBase.Properties.max, 150); setSliderPosition(TESTED_SLIDER_ID, 130,
1120      * SettingOption.MANUAL); checkTextFieldValue(TestBase.Properties.value,
1121      * 130);
1122      *
1123      * testedControl.keyboard().pushKey(Keyboard.KeyboardButtons.END);
1124      * checkTextFieldValue(TestBase.Properties.value, 150);
1125      *
1126      * setPropertyBySlider(AbstractPropertyController.SettingType.UNIDIRECTIONAL,
1127      * TestBase.Properties.max, 30);
1128      * checkTextFieldValue(TestBase.Properties.min, -10);
1129      * checkTextFieldValue(TestBase.Properties.max, 30);
1130      * checkTextFieldValue(TestBase.Properties.value, 30);
1131      *
1132      * defaultController.check(); } Can be added into
1133      * javafx.scene.control.test.slider.SliderTest class.
1134      */
1135     protected static class ChangingController {
1136 
1137         //This variable can deactivate this class on failing tests, if occasionally it cannot be easily fixed.
1138         final private boolean FAIL_ON_ERROR_IN_NON_CHANGING_CHECKING = true;
1139         private Object[] propertiesEnum = null;
1140         private Object[] countersEnum = null;
1141         private Parent<Node> stageWithPropertiestables = null;
1142         private boolean codeCompletionInfoProvided = false;
1143         private boolean stateWasFixed = false;
1144         private boolean atLeastOneConfigDone = false;
1145         private String changingControllerVariableName;
1146         private List<Entry<AbstractPropertiesTable, String>> trackedProperties = new ArrayList<Entry<AbstractPropertiesTable, String>>();
1147         private List<Entry<AbstractPropertiesTable, String>> trackedCounters = new ArrayList<Entry<AbstractPropertiesTable, String>>();
1148 
1149         private ChangingController(Parent<Node> parent) {
1150             if (parent == null) {
1151                 throw new IllegalArgumentException("Stage for looking up the properties table cannot be null.");
1152             }
1153             this.stageWithPropertiestables = parent;
1154         }
1155 
1156         public void provideInfoForCodeCompletion(Object[] propertiesEnum, Object[] countersEnum) {
1157             this.changingControllerVariableName = "defaultController";
1158 //            if (changingControllerVariableName == null) {
1159 //                throw new IllegalArgumentException("Variable name cannot be null.");
1160 //            }
1161 
1162             if ((propertiesEnum == null) && (countersEnum == null)) {
1163                 throw new IllegalArgumentException("Both enums cannot be null.");
1164             }
1165             codeCompletionInfoProvided = true;
1166             this.propertiesEnum = propertiesEnum;
1167             this.countersEnum = countersEnum;
1168 //            this.changingControllerVariableName = changingControllerVariableName;
1169         }
1170 
1171         public void fixCurrentState() {
1172             Exception ex = new GetAction<Exception>() {
1173                 @Override
1174                 public void run(Object... os) throws Exception {
1175                     try {
1176                         for (Entry<AbstractPropertiesTable, String> entry : trackedProperties) {
1177                             for (AbstractPropertyValueListener listener : entry.getKey().getListeners()) {
1178                                 listener.rememberCurrentState();
1179                             }
1180                         }
1181 
1182                         for (Entry<AbstractPropertiesTable, String> entry : trackedCounters) {
1183                             for (AbstractEventsCounter counter : entry.getKey().getCounters()) {
1184                                 counter.rememberCurrentState();
1185                             }
1186                         }
1187                     } catch (Exception ex) {
1188                         setResult(ex);
1189                     }
1190                 }
1191             }.dispatch(Root.ROOT.getEnvironment());
1192 
1193             if (ex != null) {
1194                 System.err.println("Exception during state fixing happened : " + ex.getMessage());
1195                 ex.printStackTrace(System.err);
1196             }
1197 
1198             stateWasFixed = true;
1199         }
1200 
1201         public void check() {
1202             if (!atLeastOneConfigDone) {
1203                 throw new IllegalStateException("No configs were done.");
1204             }
1205             if (!stateWasFixed) {
1206                 throw new IllegalStateException("You need to call fixState() method, to fix the counters or properties.");
1207             }
1208 
1209             final List<FailedItem> failedItems = new ArrayList<FailedItem>();
1210 
1211             Throwable ex = new GetAction<Throwable>() {
1212                 @Override
1213                 public void run(Object... os) throws Exception {
1214                     try {
1215 
1216 
1217                         for (Entry<AbstractPropertiesTable, String> entry : trackedProperties) {
1218                             try {
1219                                 for (AbstractPropertyValueListener listener : entry.getKey().getListeners()) {
1220                                     if (listener.getPropertyName().toUpperCase().equals(entry.getValue().toUpperCase())) {
1221                                         listener.checkCurrentStateEquality();
1222                                     }
1223                                 }
1224                             } catch (StateChangedException exception) {
1225                                 failedItems.add(new FailedItem(FailedItem.FailedItemType.PROPERTY, entry.getKey().getDomainName(), entry.getValue(), exception));
1226                             }
1227                         }
1228 
1229                         for (Entry<AbstractPropertiesTable, String> entry : trackedCounters) {
1230                             try {
1231                                 for (AbstractEventsCounter counter : entry.getKey().getCounters()) {
1232                                     if (counter.getName().toUpperCase().equals(entry.getValue().toUpperCase())) {
1233                                         counter.checkCurrentStateEquality();
1234                                     }
1235                                 }
1236                             } catch (StateChangedException exception) {
1237                                 failedItems.add(new FailedItem(FailedItem.FailedItemType.COUNTER, entry.getKey().getDomainName(), entry.getValue(), exception));
1238                             }
1239                         }
1240                     } catch (Throwable ex) {
1241                         setResult(ex);
1242                     }
1243                 }
1244             }.dispatch(Root.ROOT.getEnvironment());
1245 
1246             if (ex != null) {
1247                 System.err.println("During checking an error occured : " + ex.getMessage());
1248                 ex.printStackTrace(System.err);
1249             }
1250 
1251             if (codeCompletionInfoProvided) {
1252                 generateCompletingCode(failedItems);
1253             }
1254 
1255             fail(failedItems);
1256         }
1257 
1258         public Config include() {
1259             if (stateWasFixed) {
1260                 throw new IllegalStateException("Inclusion cannot be done, because fixing has been done.");
1261             }
1262             return new Config(this, true);
1263         }
1264 
1265         public Config exclude() {
1266             return new Config(this, false);
1267         }
1268 
1269         protected void removeProperty(String domainName, Enum propertyName) {
1270             new Config(this, false).table(domainName).property(propertyName).apply();
1271         }
1272 
1273         protected void removeCounter(String domainName, String counterName) {
1274             new Config(this, false).table(domainName).counter(counterName).apply();
1275         }
1276 
1277         final public void setPropertiesEnum(Object[] propertiesEnum) {
1278             this.propertiesEnum = propertiesEnum;
1279         }
1280 
1281         final public void setCountersEnum(Object[] countersEnum) {
1282             this.countersEnum = countersEnum;
1283         }
1284 
1285         private void generateCompletingCode(List<FailedItem> failedItems) {
1286             if (!failedItems.isEmpty()) {
1287                 System.out.println("You can add this code to provide code completion : ");
1288             }
1289 
1290             try {
1291                 for (FailedItem failedItem : failedItems) {
1292                     if (failedItem.getFailedItemType().equals(FailedItem.FailedItemType.COUNTER)) {
1293                         if (countersEnum != null) {
1294                             int index = findItemIndex(countersEnum, failedItem.getName());
1295                             if (index != -1) {
1296                                 System.out.println(changingControllerVariableName + ".exclude().table(" + failedItem.getDomainName() + ").counter(Counters." + countersEnum[index].toString() + ").apply();");
1297                             }
1298                         }
1299                     }
1300 
1301                     if (failedItem.getFailedItemType().equals(FailedItem.FailedItemType.PROPERTY)) {
1302                         if (propertiesEnum != null) {
1303                             int index = findItemIndex(propertiesEnum, failedItem.getName());
1304                             if (index != -1) {
1305                                 System.out.println(changingControllerVariableName + ".exclude().table(" + failedItem.getDomainName() + ").property(Properties." + propertiesEnum[index].toString() + ").apply();");
1306                             }
1307                         }
1308                     }
1309                 }
1310             } catch (Exception ex) {
1311                 System.err.println("During code generating an error has occured : " + ex.getMessage());
1312                 ex.printStackTrace(System.err);
1313             }
1314         }
1315 
1316         private void fail(List<FailedItem> failedItems) {
1317             for (FailedItem failedItem : failedItems) {
1318                 System.err.println("Failed on checking : " + failedItem);
1319             }
1320 
1321             if (FAIL_ON_ERROR_IN_NON_CHANGING_CHECKING) {
1322                 if (!failedItems.isEmpty()) {
1323                     throw new IllegalStateException("Failures detected.");
1324                 }
1325             }
1326         }
1327 
1328         private int findItemIndex(Object[] enumm, String name) {
1329             for (int i = 0; i < enumm.length; i++) {
1330                 if (enumm[i].toString().toUpperCase().equals(name.toUpperCase())) {
1331                     return i;
1332                 }
1333             }
1334 
1335             return -1;
1336         }
1337 
1338         protected void applyConfig(final Config config) {
1339             Throwable ex = new GetAction<Throwable>() {
1340                 @Override
1341                 public void run(Object... os) throws Exception {
1342                     try {
1343                         Map<String, AbstractPropertiesTable> currentlyExistingTables = searchForTables();
1344 
1345                         List<String> listOfTables = new ArrayList<String>();
1346                         if (config.allTables) {
1347                             for (AbstractPropertiesTable table : currentlyExistingTables.values()) {
1348                                 listOfTables.add(table.getDomainName());
1349                             }
1350                         } else {
1351                             listOfTables = config.tableNames;
1352                         }
1353 
1354                         if (config.toInclude) {
1355                             for (String domainName : listOfTables) {
1356                                 if (config.allCounters) {
1357                                     for (AbstractEventsCounter counter : currentlyExistingTables.get(domainName).getCounters()) {
1358                                         trackedCounters.add(new AbstractMap.SimpleEntry<AbstractPropertiesTable, String>(currentlyExistingTables.get(domainName), counter.getName()));
1359                                     }
1360                                 } else {
1361                                     for (String counterName : config.counterNames) {
1362                                         trackedCounters.add(new AbstractMap.SimpleEntry<AbstractPropertiesTable, String>(currentlyExistingTables.get(domainName), counterName));
1363                                     }
1364                                 }
1365 
1366                                 if (config.allProperties) {
1367                                     for (AbstractPropertyValueListener listener : currentlyExistingTables.get(domainName).getListeners()) {
1368                                         trackedProperties.add(new AbstractMap.SimpleEntry<AbstractPropertiesTable, String>(currentlyExistingTables.get(domainName), listener.getPropertyName()));
1369                                     }
1370                                 } else {
1371                                     for (Enum property : config.propertiesNames) {
1372                                         trackedProperties.add(new AbstractMap.SimpleEntry<AbstractPropertiesTable, String>(currentlyExistingTables.get(domainName), property.name()));
1373                                     }
1374                                 }
1375                             }
1376                         } else { //ToExclude
1377                             for (String domainName : listOfTables) {
1378                                 if (config.allCounters) {
1379                                     List<Entry<AbstractPropertiesTable, String>> removeList = new ArrayList<Entry<AbstractPropertiesTable, String>>();
1380                                     for (Entry<AbstractPropertiesTable, String> entry : trackedCounters) {
1381                                         if (entry.getKey().getDomainName().equals(domainName)) {
1382                                             removeList.add(entry);
1383                                         }
1384                                     }
1385                                     for (Entry<AbstractPropertiesTable, String> entry : removeList) {
1386                                         trackedCounters.remove(entry);
1387                                     }
1388                                 } else {
1389                                     List<Entry<AbstractPropertiesTable, String>> removeList = new ArrayList<Entry<AbstractPropertiesTable, String>>();
1390                                     for (Entry<AbstractPropertiesTable, String> entry : trackedCounters) {
1391                                         for (String counterName : config.counterNames) {
1392                                             if (entry.getKey().getDomainName().equals(domainName) && entry.getValue().equalsIgnoreCase(counterName)) {
1393                                                 removeList.add(entry);
1394                                             }
1395                                         }
1396                                     }
1397                                     for (Entry<AbstractPropertiesTable, String> entry : removeList) {
1398                                         trackedCounters.remove(entry);
1399                                     }
1400                                 }
1401 
1402                                 if (config.allProperties) {
1403                                     List<Entry<AbstractPropertiesTable, String>> removeList = new ArrayList<Entry<AbstractPropertiesTable, String>>();
1404                                     for (Entry<AbstractPropertiesTable, String> entry : trackedProperties) {
1405                                         if (entry.getKey().getDomainName().equals(domainName)) {
1406                                             removeList.add(entry);
1407                                         }
1408                                     }
1409                                     for (Entry<AbstractPropertiesTable, String> entry : removeList) {
1410                                         trackedProperties.remove(entry);
1411                                     }
1412                                 } else {
1413                                     List<Entry<AbstractPropertiesTable, String>> removeList = new ArrayList<Entry<AbstractPropertiesTable, String>>();
1414                                     for (Entry<AbstractPropertiesTable, String> entry : trackedProperties) {
1415                                         for (Enum propertyName : config.propertiesNames) {
1416                                             if (entry.getKey().getDomainName().equals(domainName) && entry.getValue().equalsIgnoreCase(propertyName.name())) {
1417                                                 removeList.add(entry);
1418                                             }
1419                                         }
1420                                     }
1421                                     for (Entry<AbstractPropertiesTable, String> entry : removeList) {
1422                                         trackedProperties.remove(entry);
1423                                     }
1424                                 }
1425                             }
1426                         }
1427                     } catch (Throwable th) {
1428                         setResult(th);
1429                     }
1430                 }
1431             }.dispatch(Root.ROOT.getEnvironment());
1432 
1433             if (ex != null) {
1434                 ex.printStackTrace(System.err);
1435             }
1436         }
1437 
1438         private Map<String, AbstractPropertiesTable> searchForTables() {
1439             List<AbstractPropertiesTable> foundTables = new ArrayList<AbstractPropertiesTable>();
1440 
1441             int found = stageWithPropertiestables.lookup(PropertiesTable.class).size();
1442             for (int i = 0; i < found; i++) {
1443                 foundTables.add(stageWithPropertiestables.lookup(PropertiesTable.class).wrap(i).getControl());
1444             }
1445 
1446             Map<String, AbstractPropertiesTable> tableToIdMathing = new HashMap<String, AbstractPropertiesTable>();
1447 
1448             for (final AbstractPropertiesTable table : foundTables) {
1449                 String domainName = new GetAction<String>() {
1450                     @Override
1451                     public void run(Object... os) throws Exception {
1452                         setResult(table.getDomainName());
1453                     }
1454                 }.dispatch(Root.ROOT.getEnvironment());
1455                 tableToIdMathing.put(domainName, table);
1456             }
1457 
1458             return tableToIdMathing;
1459         }
1460 
1461         protected enum CHECKING_SET {
1462 
1463             COUNTERS, PROPERTIES, STANDART_SET, ALL
1464         };
1465 
1466         private static class FailedItem {
1467 
1468             final private FailedItemType failedItemType;
1469             final private String name;
1470             final private String domainName;
1471             final private Exception exception;
1472 
1473             public FailedItem(FailedItemType failedItemType, String domainName, String name, Exception exception) {
1474                 if (failedItemType == null) {
1475                     throw new IllegalArgumentException("Failed item type cannot be null.");
1476                 }
1477                 if (name == null) {
1478                     throw new IllegalArgumentException("Name cannot be null.");
1479                 }
1480                 if (domainName == null) {
1481                     throw new IllegalArgumentException("Domain name cannot be null.");
1482                 }
1483                 if (exception == null) {
1484                     throw new IllegalArgumentException("Create an exception.");
1485                 }
1486 
1487                 this.failedItemType = failedItemType;
1488                 this.name = name;
1489                 this.domainName = domainName;
1490                 this.exception = exception;
1491             }
1492 
1493             public FailedItemType getFailedItemType() {
1494                 return failedItemType;
1495             }
1496 
1497             public String getName() {
1498                 return name;
1499             }
1500 
1501             public String getDomainName() {
1502                 return domainName;
1503             }
1504 
1505             public Exception getException() {
1506                 return exception;
1507             }
1508 
1509             public enum FailedItemType {
1510 
1511                 PROPERTY, COUNTER
1512             };
1513 
1514             @Override
1515             public String toString() {
1516                 return "Failed <" + failedItemType.name() + "> with name <" + name + "> on table <" + domainName + "> with error : \n" + exception.getMessage();
1517             }
1518         }
1519 
1520         /**
1521          * The only applicable way of doing initialisation is : Create an
1522          * instance -> set tables -> set properties and counters in any order ->
1523          * apply.
1524          */
1525         public static class Config {
1526 
1527             private ConfigStage currentStage = ConfigStage.NOT_STARTED;
1528             private ChangingController controller = null;
1529             private List<String> tableNames = new ArrayList<String>();
1530             private List<Enum> propertiesNames = new ArrayList<Enum>();
1531             private List<String> counterNames = new ArrayList<String>();
1532             private boolean allTables = false;
1533             private boolean allCounters = false;
1534             private boolean allProperties = false;
1535             private Boolean toInclude = null;
1536 
1537             /**
1538              * @param controller that controller, which will receive this
1539              * config.
1540              * @param toInclude - true - to include, false - to exclude.
1541              */
1542             private Config(ChangingController controller, boolean toInclude) {
1543                 if (controller == null) {
1544                     throw new IllegalArgumentException("Controller instance cannot be null.");
1545                 }
1546                 this.controller = controller;
1547                 this.toInclude = toInclude;
1548             }
1549 
1550             public Config table(String name) {
1551                 if (name == null) {
1552                     throw new IllegalStateException("Name cannot be null.");
1553                 }
1554                 checkNonInitialisedOrTablesSetStage();
1555                 tableNames.add(name);
1556                 currentStage = ConfigStage.DOMAIN_SET;
1557                 return this;
1558             }
1559 
1560             public Config tables(String... names) {
1561                 if (names == null) {
1562                     throw new IllegalStateException("Names cannot be null.");
1563                 }
1564                 checkNonInitialisedOrTablesSetStage();
1565                 for (String name : names) {
1566                     if (name == null) {
1567                         throw new IllegalStateException("No one name can be null.");
1568                     }
1569                     tableNames.add(name);
1570                 }
1571                 currentStage = ConfigStage.DOMAIN_SET;
1572                 return this;
1573             }
1574 
1575             /**
1576              * You can write: tables("treeItem-<i>", 1, 4) to decribe such list
1577              * of items: "treeItem-1", "treeItem-2", "treeItem-3", "treeItem-4"
1578              *
1579              * @param pattern should contain <i>
1580              * @param start initial index.
1581              * @param end final index
1582              * @return self
1583              */
1584             public Config tables(String pattern, int start, int end) {
1585                 if (pattern == null) {
1586                     throw new IllegalStateException("Names cannot be null.");
1587                 }
1588 
1589                 if (pattern.indexOf("<i>") < 0) {
1590                     throw new IllegalStateException("Patter should contain \"<i>\" substring.");
1591                 }
1592 
1593                 checkNonInitialisedOrTablesSetStage();
1594                 for (int i = start; i <= end; i++) {
1595                     this.table(pattern.replace("<i>", String.valueOf(i)));
1596                 }
1597                 currentStage = ConfigStage.DOMAIN_SET;
1598                 return this;
1599             }
1600 
1601             public Config allTables() {
1602                 checkNonInitialisedOrTablesSetStage();
1603                 allTables = true;
1604                 currentStage = ConfigStage.DOMAIN_SET;
1605                 return this;
1606             }
1607 
1608             public Config counter(String name) {
1609                 if (name == null) {
1610                     throw new IllegalStateException("Name cannot be null.");
1611                 }
1612                 checkCanSetPropertyOrCounterDetermined();
1613                 counterNames.add(name);
1614                 currentStage = ConfigStage.PROPERTIES_OR_COUNTERS_SET;
1615                 return this;
1616             }
1617 
1618             public Config property(Enum name) {
1619                 if (name == null) {
1620                     throw new IllegalStateException("Name cannot be null.");
1621                 }
1622                 checkCanSetPropertyOrCounterDetermined();
1623                 propertiesNames.add(name);
1624                 currentStage = ConfigStage.PROPERTIES_OR_COUNTERS_SET;
1625                 return this;
1626             }
1627 
1628             public Config counters(String... names) {
1629                 if (names == null) {
1630                     throw new IllegalStateException("Names cannot be null.");
1631                 }
1632                 checkCanSetPropertyOrCounterDetermined();
1633                 for (String name : names) {
1634                     if (name == null) {
1635                         throw new IllegalStateException("No one name can be null.");
1636                     }
1637                     counterNames.add(name);
1638                 }
1639                 currentStage = ConfigStage.PROPERTIES_OR_COUNTERS_SET;
1640                 return this;
1641             }
1642 
1643             public Config properties(Enum... names) {
1644                 if (names == null) {
1645                     throw new IllegalStateException("Names cannot be null.");
1646                 }
1647                 checkCanSetPropertyOrCounterDetermined();
1648                 for (Enum name : names) {
1649                     if (name == null) {
1650                         throw new IllegalStateException("No one name can be null.");
1651                     }
1652                     propertiesNames.add(name);
1653                 }
1654                 currentStage = ConfigStage.PROPERTIES_OR_COUNTERS_SET;
1655                 return this;
1656             }
1657 
1658             public Config allCounters() {
1659                 checkCanSetPropertyOrCounterDetermined();
1660                 allCounters = true;
1661                 currentStage = ConfigStage.PROPERTIES_OR_COUNTERS_SET;
1662                 return this;
1663             }
1664 
1665             public Config allProperties() {
1666                 checkCanSetPropertyOrCounterDetermined();
1667                 allProperties = true;
1668                 currentStage = ConfigStage.PROPERTIES_OR_COUNTERS_SET;
1669                 return this;
1670             }
1671 
1672             public void apply() {
1673                 if (currentStage != ConfigStage.PROPERTIES_OR_COUNTERS_SET) {
1674                     throw new IllegalStateException("Cannot be applied, when properties or counters are not determined.");
1675                 }
1676                 controller.atLeastOneConfigDone = true;
1677                 controller.applyConfig(this);
1678                 currentStage = ConfigStage.APPLIED;
1679             }
1680 
1681             private void checkNonInitialisedOrTablesSetStage() {
1682                 if (currentStage != ConfigStage.NOT_STARTED && currentStage != ConfigStage.DOMAIN_SET) {
1683                     throw new IllegalStateException("Should be not initialised, or only tables initialised.");
1684                 }
1685             }
1686 
1687             private void checkCanSetPropertyOrCounterDetermined() {
1688                 if (!((currentStage == ConfigStage.DOMAIN_SET) || (currentStage == ConfigStage.PROPERTIES_OR_COUNTERS_SET))) {
1689                     throw new IllegalStateException();
1690                 }
1691             }
1692 
1693             private static enum ConfigStage {
1694 
1695                 NOT_STARTED, DOMAIN_SET, PROPERTIES_OR_COUNTERS_SET, APPLIED
1696             };
1697         }
1698     }
1699 
1700     static protected enum SettingOption {
1701 
1702         MANUAL, PROGRAM
1703     };
1704 
1705     static public enum RectanglesRelations {
1706 
1707         BELOW, LEFTER, RIGHTER, ABOVE, ISCONTAINED, CONTAINS, CENTERED_IN_HORIZONTAL, CENTERED_IN_VERTICAL
1708     }
1709 }