1 /*
   2  * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package javafx.scene.control;
  27 
  28 import java.lang.ref.WeakReference;
  29 import java.lang.reflect.Constructor;
  30 import java.lang.reflect.InvocationTargetException;
  31 import java.util.ArrayList;
  32 import java.util.Collections;
  33 import java.util.List;
  34 
  35 import com.sun.javafx.scene.control.ControlAcceleratorSupport;
  36 import javafx.application.Application;
  37 import javafx.beans.property.ObjectProperty;
  38 import javafx.beans.property.ObjectPropertyBase;
  39 import javafx.beans.property.SimpleObjectProperty;
  40 import javafx.beans.property.StringProperty;
  41 import javafx.beans.value.WritableValue;
  42 import javafx.collections.ObservableList;
  43 import javafx.css.CssParser;
  44 import javafx.event.EventHandler;
  45 import javafx.scene.AccessibleAction;
  46 import javafx.scene.AccessibleAttribute;
  47 import javafx.scene.Node;
  48 import javafx.scene.input.ContextMenuEvent;
  49 import javafx.scene.layout.Region;
  50 import com.sun.javafx.application.PlatformImpl;
  51 import javafx.css.CssMetaData;
  52 import com.sun.javafx.css.StyleManager;
  53 import com.sun.javafx.scene.NodeHelper;
  54 import com.sun.javafx.scene.control.ControlHelper;
  55 import javafx.css.StyleableObjectProperty;
  56 import javafx.css.StyleableStringProperty;
  57 import javafx.css.converter.StringConverter;
  58 import com.sun.javafx.scene.control.Logging;
  59 import javafx.css.Styleable;
  60 import javafx.css.StyleableProperty;
  61 import sun.util.logging.PlatformLogger;
  62 import sun.util.logging.PlatformLogger.Level;
  63 
  64 
  65 /**
  66  * Base class for all user interface controls. A "Control" is a node in the
  67  * scene graph which can be manipulated by the user. Controls provide
  68  * additional variables and behaviors beyond those of Node to support
  69  * common user interactions in a manner which is consistent and predictable
  70  * for the user.
  71  * <p>
  72  * Additionally, controls support explicit skinning to make it easy to
  73  * leverage the functionality of a control while customizing its appearance.
  74  * <p>
  75  * See specific Control subclasses for information on how to use individual
  76  * types of controls.
  77  * <p> Most controls have their focusTraversable property set to true by default, however
  78  * read-only controls such as {@link Label} and {@link ProgressIndicator}, and some
  79  * controls that are containers {@link ScrollPane} and {@link ToolBar} do not.
  80  * Consult individual control documentation for details.
  81  * @since JavaFX 2.0
  82  */
  83 public abstract class Control extends Region implements Skinnable {
  84 
  85     static {
  86         ControlHelper.setControlAccessor(new ControlHelper.ControlAccessor() {
  87             @Override
  88             public void doProcessCSS(Node node) {
  89                 ((Control) node).doProcessCSS();
  90             }
  91             @Override
  92             public StringProperty skinClassNameProperty(Control control) {
  93                 return control.skinClassNameProperty();
  94             }
  95         });
  96 
  97         // Ensures that the default application user agent stylesheet is loaded
  98         if (Application.getUserAgentStylesheet() == null) {
  99             PlatformImpl.setDefaultPlatformUserAgentStylesheet();
 100         }
 101     }
 102 
 103     /**
 104      * Utility for loading a class in a manner that will work with multiple
 105      * class loaders, as is typically found in OSGI modular applications.
 106      * In particular, this method will attempt to just load the class
 107      * identified by className. If that fails, it attempts to load the
 108      * class using the current thread's context class loader. If that fails,
 109      * it attempts to use the class loader of the supplied "instance", and
 110      * if it still fails it walks up the class hierarchy of the instance
 111      * and attempts to use the class loader of each class in the super-type
 112      * hierarchy.
 113      *
 114      * @param className The name of the class we want to load
 115      * @param instance An optional instance used to help find the class to load
 116      * @return The class. Cannot return null
 117      * @throws ClassNotFoundException If the class cannot be found using any technique.
 118      */
 119     private static Class<?> loadClass(final String className, final Object instance)
 120             throws ClassNotFoundException
 121     {
 122         try {
 123             // Try just loading the class
 124             return Class.forName(className, false, Control.class.getClassLoader());
 125         } catch (ClassNotFoundException ex) {
 126             // RT-17525 : Use context class loader only if Class.forName fails.
 127             if (Thread.currentThread().getContextClassLoader() != null) {
 128                 try {
 129                     final ClassLoader ccl = Thread.currentThread().getContextClassLoader();
 130                     return Class.forName(className, false, ccl);
 131                 } catch (ClassNotFoundException ex2) {
 132                     // Do nothing, just fall through
 133                 }
 134             }
 135 
 136             // RT-14177: Try looking up the class using the class loader of the
 137             //           current class, walking up the list of superclasses
 138             //           and checking each of them, before bailing and using
 139             //           the context class loader.
 140             if (instance != null) {
 141                 Class<?> currentType = instance.getClass();
 142                 while (currentType != null) {
 143                     try {
 144                         final ClassLoader loader = currentType.getClassLoader();
 145                         return Class.forName(className, false, loader);
 146                     } catch (ClassNotFoundException ex2) {
 147                         currentType = currentType.getSuperclass();
 148                     }
 149                 }
 150             }
 151 
 152             // We failed to find the class using any of the above means, so we're going
 153             // to just throw the ClassNotFoundException that we caught earlier
 154             throw ex;
 155         }
 156     }
 157 
 158     /***************************************************************************
 159      *                                                                         *
 160      * Private fields                                                          *
 161      *                                                                         *
 162      **************************************************************************/
 163 
 164     private List<CssMetaData<? extends Styleable, ?>> styleableProperties;
 165 
 166     /**
 167      * A private reference directly to the SkinBase instance that is used as the
 168      * Skin for this Control. A Control's Skin doesn't have to be of type
 169      * SkinBase, although 98% of the time or greater it probably will be.
 170      * Because instanceof checks and reading a value from a property are
 171      * not cheap (on interpreters on slower hardware or mobile devices)
 172      * it pays to have a direct reference here to the skinBase. We simply
 173      * need to check this variable -- if it is not null then we know the
 174      * Skin is a SkinBase and this is a direct reference to it. If it is null
 175      * then we know the skin is not a SkinBase and we need to call getSkin().
 176      */
 177     private SkinBase<?> skinBase;
 178 
 179     /***************************************************************************
 180     *                                                                         *
 181     * Event Handlers / Listeners                                              *
 182     *                                                                         *
 183     **************************************************************************/
 184 
 185     /**
 186      * Handles context menu requests by popping up the menu.
 187      * Note that we use this pattern to remove some of the anonymous inner
 188      * classes which we'd otherwise have to create. When lambda expressions
 189      * are supported, we could do it that way instead (or use MethodHandles).
 190      */
 191     private final static EventHandler<ContextMenuEvent> contextMenuHandler = event -> {
 192         if (event.isConsumed()) return;
 193 
 194         // If a context menu was shown, consume the event to prevent multiple context menus
 195         Object source = event.getSource();
 196         if (source instanceof Control) {
 197             Control c = (Control) source;
 198             if (c.getContextMenu() != null) {
 199                 c.getContextMenu().show(c, event.getScreenX(), event.getScreenY());
 200                 event.consume();
 201             }
 202         }
 203     };
 204 
 205 
 206 
 207     /***************************************************************************
 208      *                                                                         *
 209      * Properties                                                              *
 210      *                                                                         *
 211      **************************************************************************/
 212 
 213 
 214 
 215     // --- skin
 216     /**
 217      * Skin is responsible for rendering this {@code Control}. From the
 218      * perspective of the {@code Control}, the {@code Skin} is a black box.
 219      * It listens and responds to changes in state in a {@code Control}.
 220      * <p>
 221      * There is a one-to-one relationship between a {@code Control} and its
 222      * {@code Skin}. Every {@code Skin} maintains a back reference to the
 223      * {@code Control} via the {@link Skin#getSkinnable()} method.
 224      * <p>
 225      * A skin may be null.
 226      * @return the skin property for this control
 227      */
 228     @Override public final ObjectProperty<Skin<?>> skinProperty() { return skin; }
 229     @Override public final void setSkin(Skin<?> value) {
 230         skinProperty().set(value);
 231     }
 232     @Override public final Skin<?> getSkin() { return skinProperty().getValue(); }
 233     private ObjectProperty<Skin<?>> skin = new StyleableObjectProperty<Skin<?>>() {
 234         // We store a reference to the oldValue so that we can handle
 235         // changes in the skin properly in the case of binding. This is
 236         // only needed because invalidated() does not currently take
 237         // a reference to the old value.
 238         private Skin<?> oldValue;
 239 
 240         @Override
 241         //This code is basically a kind of optimization that prevents a Skin that is equal but not instance equal.
 242         //Although it's not kosher from the property perspective (bindings won't pass through set), it should not do any harm.
 243         //But it should be evaluated in the future.
 244         public void set(Skin<?> v) {
 245             if (v == null
 246                 ? oldValue == null
 247                 : oldValue != null && v.getClass().equals(oldValue.getClass()))
 248                 return;
 249 
 250             super.set(v);
 251         }
 252 
 253         @Override protected void invalidated() {
 254             Skin<?> skin = get();
 255             // Collect the name of the currently installed skin class. We do this
 256             // so that subsequent updates from CSS to the same skin class will not
 257             // result in reinstalling the skin
 258             currentSkinClassName = skin == null ? null : skin.getClass().getName();
 259 
 260             // if someone calls setSkin, we need to make it look like they
 261             // called set on skinClassName in order to keep CSS from overwriting
 262             // the skin.
 263             skinClassNameProperty().set(currentSkinClassName);
 264 
 265 
 266             // Dispose of the old skin
 267             if (oldValue != null) oldValue.dispose();
 268 
 269             // Get the new value, and save it off as the new oldValue
 270             oldValue = skin;
 271 
 272             // Reset skinBase to null - it will be set to the new Skin if it
 273             // is a SkinBase, otherwise it will remain null, as expected
 274             skinBase = null;
 275 
 276             // We have two paths, one for "legacy" Skins, and one for
 277             // any Skin which extends from SkinBase. Legacy Skins will
 278             // produce a single node which will be the only child of
 279             // the Control via the getNode() method on the Skin. A
 280             // SkinBase will manipulate the children of the Control
 281             // directly. Further, we maintain a direct reference to
 282             // the skinBase for more optimal updates later.
 283             if (skin instanceof SkinBase) {
 284                 // record a reference of the skin, if it is a SkinBase, for
 285                 // performance reasons
 286                 skinBase = (SkinBase<?>) skin;
 287                 // Note I do not remove any children here, because the
 288                 // skin will have already configured all the children
 289                 // by the time setSkin has been called. This is because
 290                 // our Skin interface was lacking an initialize method (doh!)
 291                 // and so the Skin constructor is where it adds listeners
 292                 // and so forth. For SkinBase implementations, the
 293                 // constructor is also where it will take ownership of
 294                 // the children.
 295             } else {
 296                 final Node n = getSkinNode();
 297                 if (n != null) {
 298                     getChildren().setAll(n);
 299                 } else {
 300                     getChildren().clear();
 301                 }
 302             }
 303 
 304             // clear out the styleable properties so that the list is rebuilt
 305             // next time they are requested.
 306             styleableProperties = null;
 307 
 308             // calling NodeHelper.reapplyCSS() as the styleable properties may now
 309             // be different, as we will now be able to return styleable properties
 310             // belonging to the skin. If NodeHelper.reapplyCSS() is not called, the
 311             // getCssMetaData() method is never called, so the
 312             // skin properties are never exposed.
 313             NodeHelper.reapplyCSS(Control.this);
 314 
 315             // DEBUG: Log that we've changed the skin
 316             final PlatformLogger logger = Logging.getControlsLogger();
 317             if (logger.isLoggable(Level.FINEST)) {
 318                 logger.finest("Stored skin[" + getValue() + "] on " + this);
 319             }
 320         }
 321 
 322         // This method should be CssMetaData<Control,Skin> getCssMetaData(),
 323         // but SKIN is CssMetaData<Control,String>. This does not matter to
 324         // the CSS code which doesn't care about the actual type. Hence,
 325         // we'll suppress the warnings
 326         @Override @SuppressWarnings({"unchecked", "rawtype"})
 327         public CssMetaData getCssMetaData() {
 328             return StyleableProperties.SKIN;
 329         }
 330 
 331         @Override
 332         public Object getBean() {
 333             return Control.this;
 334         }
 335 
 336         @Override
 337         public String getName() {
 338             return "skin";
 339         }
 340     };
 341 
 342 
 343     // --- tooltip
 344     /**
 345      * The ToolTip for this control.
 346      * @return the tool tip for this control
 347      */
 348     public final ObjectProperty<Tooltip> tooltipProperty() {
 349         if (tooltip == null) {
 350             tooltip = new ObjectPropertyBase<Tooltip>() {
 351                 private Tooltip old = null;
 352                 @Override protected void invalidated() {
 353                     Tooltip t = get();
 354                     // install / uninstall
 355                     if (t != old) {
 356                         if (old != null) {
 357                             Tooltip.uninstall(Control.this, old);
 358                         }
 359                         if (t != null) {
 360                             Tooltip.install(Control.this, t);
 361                         }
 362                         old = t;
 363                     }
 364                 }
 365 
 366                 @Override
 367                 public Object getBean() {
 368                     return Control.this;
 369                 }
 370 
 371                 @Override
 372                 public String getName() {
 373                     return "tooltip";
 374                 }
 375             };
 376         }
 377         return tooltip;
 378     }
 379     private ObjectProperty<Tooltip> tooltip;
 380     public final void setTooltip(Tooltip value) { tooltipProperty().setValue(value); }
 381     public final Tooltip getTooltip() { return tooltip == null ? null : tooltip.getValue(); }
 382 
 383 
 384     // --- context menu
 385     /**
 386      * The ContextMenu to show for this control.
 387      */
 388     private ObjectProperty<ContextMenu> contextMenu = new SimpleObjectProperty<ContextMenu>(this, "contextMenu") {
 389         private WeakReference<ContextMenu> contextMenuRef;
 390 
 391         @Override protected void invalidated() {
 392             ContextMenu oldMenu = contextMenuRef == null ? null : contextMenuRef.get();
 393             if (oldMenu != null) {
 394                 ControlAcceleratorSupport.removeAcceleratorsFromScene(oldMenu.getItems(), Control.this);
 395             }
 396 
 397             ContextMenu ctx = get();
 398             contextMenuRef = new WeakReference<>(ctx);
 399 
 400             if (ctx != null) {
 401                 // set this flag so contextmenu show will be relative to parent window not anchor
 402                 ctx.setShowRelativeToWindow(true); //RT-15160
 403 
 404                 // if a context menu is set, we need to install any accelerators
 405                 // belonging to its menu items ASAP into the scene that this
 406                 // Control is in (if the control is not in a Scene, we will need
 407                 // to wait until it is and then do it).
 408                 ControlAcceleratorSupport.addAcceleratorsIntoScene(ctx.getItems(), Control.this);
 409             }
 410         }
 411     };
 412     public final ObjectProperty<ContextMenu> contextMenuProperty() { return contextMenu; }
 413     public final void setContextMenu(ContextMenu value) { contextMenu.setValue(value); }
 414     public final ContextMenu getContextMenu() { return contextMenu == null ? null : contextMenu.getValue(); }
 415 
 416 
 417 
 418     /***************************************************************************
 419      *                                                                         *
 420      * Constructors                                                            *
 421      *                                                                         *
 422      **************************************************************************/
 423 
 424     {
 425         // To initialize the class helper at the begining each constructor of this class
 426         ControlHelper.initHelper(this);
 427     }
 428     /**
 429      *  Create a new Control.
 430      */
 431     protected Control() {
 432         // focusTraversable is styleable through css. Calling setFocusTraversable
 433         // makes it look to css like the user set the value and css will not
 434         // override. Initializing focusTraversable by calling applyStyle
 435         // with null for StyleOrigin ensures that css will be able to override
 436         // the value.
 437         final StyleableProperty<Boolean> prop = (StyleableProperty<Boolean>)(WritableValue<Boolean>)focusTraversableProperty();
 438         prop.applyStyle(null, Boolean.TRUE);
 439 
 440         // we add a listener for menu request events to show the context menu
 441         // that may be set on the Control
 442         this.addEventHandler(ContextMenuEvent.CONTEXT_MENU_REQUESTED, contextMenuHandler);
 443 
 444         // TODO re-enable when InputMap moves back to Node / Control
 445 //        // Most controls need an input map, so we set this to be non-null in
 446 //        // Control to save people from running into NPEs.
 447 //        setInputMap(new InputMap(this));
 448     }
 449 
 450 
 451 
 452     /***************************************************************************
 453      *                                                                         *
 454      * Public API                                                              *
 455      *                                                                         *
 456      **************************************************************************/
 457 
 458     // Proposed dispose() API.
 459     // Note that there is impl code for a dispose method in TableRowSkinBase
 460     // and TableCell (just search for dispose())
 461 //    public void dispose() {
 462 //        Skin skin = getSkin();
 463 //        if (skin != null) {
 464 //            skin.dispose();
 465 //        }
 466 //    }
 467 
 468     /**
 469      * Returns <code>true</code> since all Controls are resizable.
 470      * @return whether this node can be resized by its parent during layout
 471      */
 472     @Override public boolean isResizable() {
 473         return true;
 474     }
 475 
 476     // Implementation of the Resizable interface.
 477     // Because only the skin can know the min, pref, and max sizes, these
 478     // functions are implemented to delegate to skin. If there is no skin then
 479     // we simply return 0 for all the values since a Control without a Skin
 480     // doesn't render
 481     /**
 482      * Computes the minimum allowable width of the Control, based on the provided
 483      * height. The minimum width is not calculated within the Control, instead
 484      * the calculation is delegated to the {@link Node#minWidth(double)} method
 485      * of the {@link Skin}. If the Skin is null, the returned value is 0.
 486      *
 487      * @param height The height of the Control, in case this value might dictate
 488      *      the minimum width.
 489      * @return A double representing the minimum width of this control.
 490      */
 491     @Override protected double computeMinWidth(final double height) {
 492         if (skinBase != null) {
 493             return skinBase.computeMinWidth(height, snappedTopInset(), snappedRightInset(), snappedBottomInset(), snappedLeftInset());
 494         } else {
 495             final Node skinNode = getSkinNode();
 496             return skinNode == null ? 0 : skinNode.minWidth(height);
 497         }
 498     }
 499 
 500     /**
 501      * Computes the minimum allowable height of the Control, based on the provided
 502      * width. The minimum height is not calculated within the Control, instead
 503      * the calculation is delegated to the {@link Node#minHeight(double)} method
 504      * of the {@link Skin}. If the Skin is null, the returned value is 0.
 505      *
 506      * @param width The width of the Control, in case this value might dictate
 507      *      the minimum height.
 508      * @return A double representing the minimum height of this control.
 509      */
 510     @Override protected double computeMinHeight(final double width) {
 511         if (skinBase != null) {
 512             return skinBase.computeMinHeight(width, snappedTopInset(), snappedRightInset(), snappedBottomInset(), snappedLeftInset());
 513         } else {
 514             final Node skinNode = getSkinNode();
 515             return skinNode == null ? 0 : skinNode.minHeight(width);
 516         }
 517     }
 518 
 519     /**
 520      * Computes the maximum allowable width of the Control, based on the provided
 521      * height. The maximum width is not calculated within the Control, instead
 522      * the calculation is delegated to the {@link Node#maxWidth(double)} method
 523      * of the {@link Skin}. If the Skin is null, the returned value is 0.
 524      *
 525      * @param height The height of the Control, in case this value might dictate
 526      *      the maximum width.
 527      * @return A double representing the maximum width of this control.
 528      */
 529     @Override protected double computeMaxWidth(double height) {
 530         if (skinBase != null) {
 531             return skinBase.computeMaxWidth(height, snappedTopInset(), snappedRightInset(), snappedBottomInset(), snappedLeftInset());
 532         } else {
 533             final Node skinNode = getSkinNode();
 534             return skinNode == null ? 0 : skinNode.maxWidth(height);
 535         }
 536     }
 537 
 538     /**
 539      * Computes the maximum allowable height of the Control, based on the provided
 540      * width. The maximum height is not calculated within the Control, instead
 541      * the calculation is delegated to the {@link Node#maxHeight(double)} method
 542      * of the {@link Skin}. If the Skin is null, the returned value is 0.
 543      *
 544      * @param width The width of the Control, in case this value might dictate
 545      *      the maximum height.
 546      * @return A double representing the maximum height of this control.
 547      */
 548     @Override protected double computeMaxHeight(double width) {
 549         if (skinBase != null) {
 550             return skinBase.computeMaxHeight(width, snappedTopInset(), snappedRightInset(), snappedBottomInset(), snappedLeftInset());
 551         } else {
 552             final Node skinNode = getSkinNode();
 553             return skinNode == null ? 0 : skinNode.maxHeight(width);
 554         }
 555     }
 556 
 557     /** {@inheritDoc} */
 558     @Override protected double computePrefWidth(double height) {
 559         if (skinBase != null) {
 560             return skinBase.computePrefWidth(height, snappedTopInset(), snappedRightInset(), snappedBottomInset(), snappedLeftInset());
 561         } else {
 562             final Node skinNode = getSkinNode();
 563             return skinNode == null ? 0 : skinNode.prefWidth(height);
 564         }
 565     }
 566 
 567     /** {@inheritDoc} */
 568     @Override protected double computePrefHeight(double width) {
 569         if (skinBase != null) {
 570             return skinBase.computePrefHeight(width, snappedTopInset(), snappedRightInset(), snappedBottomInset(), snappedLeftInset());
 571         } else {
 572             final Node skinNode = getSkinNode();
 573             return skinNode == null ? 0 : skinNode.prefHeight(width);
 574         }
 575     }
 576 
 577     /** {@inheritDoc} */
 578     @Override public double getBaselineOffset() {
 579         if (skinBase != null) {
 580             return skinBase.computeBaselineOffset(snappedTopInset(), snappedRightInset(), snappedBottomInset(), snappedLeftInset());
 581         } else {
 582             final Node skinNode = getSkinNode();
 583             return skinNode == null ? 0 : skinNode.getBaselineOffset();
 584         }
 585     }
 586 
 587     /***************************************************************************
 588      * Implementation of layout bounds for the Control. We want to preserve    *
 589      * the lazy semantics of layout bounds. So whenever the width/height       *
 590      * changes on the node, we end up invalidating layout bounds. We then      *
 591      * recompute it on demand.                                                 *
 592      **************************************************************************/
 593 
 594     /** {@inheritDoc} */
 595     @Override protected void layoutChildren() {
 596         if (skinBase != null) {
 597             final double x = snappedLeftInset();
 598             final double y = snappedTopInset();
 599             final double w = snapSizeX(getWidth()) - x - snappedRightInset();
 600             final double h = snapSizeY(getHeight()) - y - snappedBottomInset();
 601             skinBase.layoutChildren(x, y, w, h);
 602         } else {
 603             Node n = getSkinNode();
 604             if (n != null) {
 605                 n.resizeRelocate(0, 0, getWidth(), getHeight());
 606             }
 607         }
 608     }
 609 
 610     /***************************************************************************
 611      * Forward the following to the skin                                       *
 612      **************************************************************************/
 613 
 614     /**
 615      * Create a new instance of the default skin for this control. This is called to create a skin for the control if
 616      * no skin is provided via CSS {@code -fx-skin} or set explicitly in a sub-class with {@code  setSkin(...)}.
 617      *
 618      * @return  new instance of default skin for this control. If null then the control will have no skin unless one
 619      *          is provided by css.
 620      * @since JavaFX 8.0
 621      */
 622     protected Skin<?> createDefaultSkin() {
 623         return null;
 624     }
 625 
 626     /***************************************************************************
 627      *                                                                         *
 628      * Package API for SkinBase                                                *
 629      *                                                                         *
 630      **************************************************************************/
 631 
 632     // package private for SkinBase
 633     ObservableList<Node> getControlChildren() {
 634         return getChildren();
 635     }
 636 
 637 
 638     /***************************************************************************
 639      *                                                                         *
 640      * Private implementation                                                  *
 641      *                                                                         *
 642      **************************************************************************/
 643 
 644     /**
 645      * Gets the Skin's node, or returns null if there is no Skin.
 646      * Convenience method for getting the node of the skin. This is null-safe,
 647      * meaning if skin is null then it will return null instead of throwing
 648      * a NullPointerException.
 649      *
 650      * @return The Skin's node, or null.
 651      */
 652     private Node getSkinNode() {
 653         assert skinBase == null;
 654         Skin<?> skin = getSkin();
 655         return skin == null ? null : skin.getNode();
 656     }
 657 
 658     /**
 659      * Keeps a reference to the name of the class currently acting as the skin.
 660      */
 661     private String currentSkinClassName = null;
 662     private StringProperty skinClassName;
 663 
 664     StringProperty skinClassNameProperty() {
 665         if (skinClassName == null) {
 666             skinClassName = new StyleableStringProperty() {
 667 
 668                 @Override
 669                 public void set(String v) {
 670                     // do not allow the skin to be set to null through CSS
 671                     if (v == null || v.isEmpty() || v.equals(get())) return;
 672                     super.set(v);
 673                 }
 674 
 675                 @Override
 676                 public void invalidated() {
 677 
 678                     if (get() != null) {
 679                         if (!get().equals(currentSkinClassName)) {
 680                             loadSkinClass(Control.this, skinClassName.get());
 681                         }
 682                         // Note: CSS should not set skin to null
 683                     }
 684                 }
 685 
 686                 @Override
 687                 public Object getBean() {
 688                     return Control.this;
 689                 }
 690 
 691                 @Override
 692                 public String getName() {
 693                     return "skinClassName";
 694                 }
 695 
 696                 @Override
 697                 public CssMetaData<Control,String> getCssMetaData() {
 698                     return StyleableProperties.SKIN;
 699                 }
 700 
 701             };
 702         }
 703         return skinClassName;
 704     }
 705 
 706     static void loadSkinClass(final Skinnable control, final String skinClassName) {
 707         if (skinClassName == null || skinClassName.isEmpty()) {
 708             final String msg =
 709                 "Empty -fx-skin property specified for control " + control;
 710             final List<CssParser.ParseError> errors = StyleManager.getErrors();
 711             if (errors != null) {
 712                 CssParser.ParseError error = new CssParser.ParseError(msg);
 713                 errors.add(error); // RT-19884
 714             }
 715             Logging.getControlsLogger().severe(msg);
 716             return;
 717         }
 718 
 719         try {
 720             final Class<?> skinClass = Control.loadClass(skinClassName, control);
 721             if (!Skin.class.isAssignableFrom(skinClass)) {
 722                 final String msg =
 723                     "'" + skinClassName + "' is not a valid Skin class for control " + control;
 724                 final List<CssParser.ParseError> errors = StyleManager.getErrors();
 725                 if (errors != null) {
 726                     CssParser.ParseError error = new CssParser.ParseError(msg);
 727                     errors.add(error); // RT-19884
 728                 }
 729                 Logging.getControlsLogger().severe(msg);
 730                 return;
 731             }
 732             Constructor<?>[] constructors = skinClass.getConstructors();
 733             Constructor<?> skinConstructor = null;
 734             for (Constructor<?> c : constructors) {
 735                 Class<?>[] parameterTypes = c.getParameterTypes();
 736                 if (parameterTypes.length == 1 && Skinnable.class.isAssignableFrom(parameterTypes[0])) {
 737                     skinConstructor = c;
 738                     break;
 739                 }
 740             }
 741 
 742             if (skinConstructor == null) {
 743                 final String msg =
 744                     "No valid constructor defined in '" + skinClassName + "' for control " + control +
 745                         ".\r\nYou must provide a constructor that accepts a single "
 746                         + "Skinnable (e.g. Control or PopupControl) parameter in " + skinClassName + ".";
 747                 final List<CssParser.ParseError> errors = StyleManager.getErrors();
 748                 if (errors != null) {
 749                     CssParser.ParseError error = new CssParser.ParseError(msg);
 750                     errors.add(error); // RT-19884
 751                 }
 752                 Logging.getControlsLogger().severe(msg);
 753             } else {
 754                 Skin<?> skinInstance = (Skin<?>) skinConstructor.newInstance(control);
 755                 // Do not call setSkin here since it has the side effect of
 756                 // also setting the skinClassName!
 757                 control.skinProperty().set(skinInstance);
 758             }
 759         } catch (InvocationTargetException e) {
 760             final String msg =
 761                 "Failed to load skin '" + skinClassName + "' for control " + control;
 762             final List<CssParser.ParseError> errors = StyleManager.getErrors();
 763             if (errors != null) {
 764                 CssParser.ParseError error = new CssParser.ParseError(msg + " :" + e.getLocalizedMessage());
 765                 errors.add(error); // RT-19884
 766             }
 767             Logging.getControlsLogger().severe(msg, e.getCause());
 768         } catch (Exception e) {
 769             final String msg =
 770                 "Failed to load skin '" + skinClassName + "' for control " + control;
 771             final List<CssParser.ParseError> errors = StyleManager.getErrors();
 772             if (errors != null) {
 773                 CssParser.ParseError error = new CssParser.ParseError(msg + " :" + e.getLocalizedMessage());
 774                 errors.add(error); // RT-19884
 775             }
 776             Logging.getControlsLogger().severe(msg, e);
 777         }
 778     }
 779 
 780     /***************************************************************************
 781      *                                                                         *
 782      * StyleSheet Handling                                                     *
 783      *                                                                         *
 784      **************************************************************************/
 785 
 786     private static class StyleableProperties {
 787         private static final CssMetaData<Control,String> SKIN =
 788             new CssMetaData<Control,String>("-fx-skin",
 789                 StringConverter.getInstance()) {
 790 
 791             @Override
 792             public boolean isSettable(Control n) {
 793                 return (n.skin == null || !n.skin.isBound());
 794             }
 795 
 796             @Override
 797             public StyleableProperty<String> getStyleableProperty(Control n) {
 798                 return (StyleableProperty<String>)(WritableValue<String>)n.skinClassNameProperty();
 799             }
 800         };
 801 
 802         private static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES;
 803         static {
 804             final List<CssMetaData<? extends Styleable, ?>> styleables =
 805                 new ArrayList<CssMetaData<? extends Styleable, ?>>(Region.getClassCssMetaData());
 806             styleables.add(SKIN);
 807             STYLEABLES = Collections.unmodifiableList(styleables);
 808         }
 809     }
 810 
 811     /**
 812      * @return The CssMetaData associated with this class, which may include the
 813      * CssMetaData of its superclasses.
 814      * @since JavaFX 8.0
 815      */
 816     public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() {
 817         return StyleableProperties.STYLEABLES;
 818     }
 819 
 820     /**
 821      * This method returns a {@link List} containing all {@link CssMetaData} for
 822      * both this Control (returned from {@link #getControlCssMetaData()} and its
 823      * {@link Skin}, assuming the {@link #skinProperty() skin property} is a
 824      * {@link SkinBase}.
 825      *
 826      * <p>Developers who wish to provide custom CssMetaData are therefore
 827      * encouraged to override {@link Control#getControlCssMetaData()} or
 828      * {@link SkinBase#getCssMetaData()}, depending on where the CssMetaData
 829      * resides.
 830      * @since JavaFX 8.0
 831      */
 832     @Override
 833     public final List<CssMetaData<? extends Styleable, ?>> getCssMetaData() {
 834         if (styleableProperties == null) {
 835 
 836             // RT-29162: make sure properties only show up once in the list
 837             java.util.Map<String, CssMetaData<? extends Styleable, ?>> map =
 838                 new java.util.HashMap<String, CssMetaData<? extends Styleable, ?>>();
 839 
 840             List<CssMetaData<? extends Styleable, ?>> list =  getControlCssMetaData();
 841 
 842             for (int n=0, nMax = list != null ? list.size() : 0; n<nMax; n++) {
 843 
 844                 CssMetaData<? extends Styleable, ?> metaData = list.get(n);
 845                 if (metaData == null) continue;
 846 
 847                 map.put(metaData.getProperty(), metaData);
 848             }
 849 
 850             //
 851             // if both control and skin base have the same property, use the
 852             // one from skin base since it may be a specialization of the
 853             // property in the control. For instance, Label has -fx-font and
 854             // so does LabeledText which is Label's skin.
 855             //
 856             list =  skinBase != null ? skinBase.getCssMetaData() : null;
 857 
 858             for (int n=0, nMax = list != null ? list.size() : 0; n<nMax; n++) {
 859 
 860                 CssMetaData<? extends Styleable, ?> metaData = list.get(n);
 861                 if (metaData == null) continue;
 862 
 863                 map.put(metaData.getProperty(), metaData);
 864             }
 865 
 866             styleableProperties = new ArrayList<CssMetaData<? extends Styleable, ?>>();
 867             styleableProperties.addAll(map.values());
 868         }
 869         return styleableProperties;
 870     }
 871 
 872     /**
 873      * @return unmodifiable list of the controls css styleable properties
 874      * @since JavaFX 8.0
 875      */
 876     protected List<CssMetaData<? extends Styleable, ?>> getControlCssMetaData() {
 877         return getClassCssMetaData();
 878     }
 879 
 880     /*
 881      * Note: This method MUST only be called via its accessor method.
 882      */
 883     private boolean skinCreationLocked = false;
 884     private void doProcessCSS() {
 885 
 886         ControlHelper.superProcessCSS(this);
 887 
 888         if (getSkin() == null) {
 889             if (skinCreationLocked) {
 890                 return;
 891             }
 892 
 893             try {
 894                 skinCreationLocked = true;
 895 
 896                 // try to create default skin
 897                 final Skin<?> defaultSkin = createDefaultSkin();
 898                 if (defaultSkin != null) {
 899                     skinProperty().set(defaultSkin);
 900                     ControlHelper.superProcessCSS(this);
 901                 } else {
 902                     final String msg = "The -fx-skin property has not been defined in CSS for " + this +
 903                             " and createDefaultSkin() returned null.";
 904                     final List<CssParser.ParseError> errors = StyleManager.getErrors();
 905                     if (errors != null) {
 906                         CssParser.ParseError error = new CssParser.ParseError(msg);
 907                         errors.add(error); // RT-19884
 908                     }
 909                     Logging.getControlsLogger().severe(msg);
 910                 }
 911             } finally {
 912                 skinCreationLocked = false;
 913             }
 914         }
 915     }
 916 
 917     /**
 918      * Returns the initial focus traversable state of this control, for use
 919      * by the JavaFX CSS engine to correctly set its initial value. By default all
 920      * UI controls are focus traversable, so this method is overridden in Control
 921      * to set the initial traversable state to true.
 922      *
 923      * @return the initial focus traversable state of this control
 924      * @since 9
 925      */
 926     @Override protected Boolean getInitialFocusTraversable() {
 927         return Boolean.TRUE;
 928     }
 929 
 930 
 931     /***************************************************************************
 932      *                                                                         *
 933      * Accessibility handling                                                  *
 934      *                                                                         *
 935      **************************************************************************/
 936 
 937     /** {@inheritDoc} */
 938     @Override
 939     public Object queryAccessibleAttribute(AccessibleAttribute attribute, Object... parameters) {
 940         switch (attribute) {
 941             case HELP:
 942                 String help = getAccessibleHelp();
 943                 if (help != null && !help.isEmpty()) return help;
 944                 Tooltip tooltip = getTooltip();
 945                 return tooltip == null ? "" : tooltip.getText();
 946             default:
 947         }
 948         if (skinBase != null) {
 949             Object result = skinBase.queryAccessibleAttribute(attribute, parameters);
 950             if (result != null) return result;
 951         }
 952         return super.queryAccessibleAttribute(attribute, parameters);
 953     }
 954 
 955     /** {@inheritDoc} */
 956     @Override
 957     public void executeAccessibleAction(AccessibleAction action, Object... parameters) {
 958         if (skinBase != null) {
 959             skinBase.executeAccessibleAction(action, parameters);
 960         }
 961         super.executeAccessibleAction(action, parameters);
 962     }
 963 }