1 /*
   2  * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  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      */
 227     @Override public final ObjectProperty<Skin<?>> skinProperty() { return skin; }
 228     @Override public final void setSkin(Skin<?> value) {
 229         skinProperty().set(value);
 230     }
 231     @Override public final Skin<?> getSkin() { return skinProperty().getValue(); }
 232     private ObjectProperty<Skin<?>> skin = new StyleableObjectProperty<Skin<?>>() {
 233         // We store a reference to the oldValue so that we can handle
 234         // changes in the skin properly in the case of binding. This is
 235         // only needed because invalidated() does not currently take
 236         // a reference to the old value.
 237         private Skin<?> oldValue;
 238 
 239         @Override
 240         //This code is basically a kind of optimization that prevents a Skin that is equal but not instance equal.
 241         //Although it's not kosher from the property perspective (bindings won't pass through set), it should not do any harm.
 242         //But it should be evaluated in the future.
 243         public void set(Skin<?> v) {
 244             if (v == null
 245                 ? oldValue == null
 246                 : oldValue != null && v.getClass().equals(oldValue.getClass()))
 247                 return;
 248 
 249             super.set(v);
 250         }
 251 
 252         @Override protected void invalidated() {
 253             Skin<?> skin = get();
 254             // Collect the name of the currently installed skin class. We do this
 255             // so that subsequent updates from CSS to the same skin class will not
 256             // result in reinstalling the skin
 257             currentSkinClassName = skin == null ? null : skin.getClass().getName();
 258 
 259             // if someone calls setSkin, we need to make it look like they
 260             // called set on skinClassName in order to keep CSS from overwriting
 261             // the skin.
 262             skinClassNameProperty().set(currentSkinClassName);
 263 
 264 
 265             // Dispose of the old skin
 266             if (oldValue != null) oldValue.dispose();
 267 
 268             // Get the new value, and save it off as the new oldValue
 269             oldValue = skin;
 270 
 271             // Reset skinBase to null - it will be set to the new Skin if it
 272             // is a SkinBase, otherwise it will remain null, as expected
 273             skinBase = null;
 274 
 275             // We have two paths, one for "legacy" Skins, and one for
 276             // any Skin which extends from SkinBase. Legacy Skins will
 277             // produce a single node which will be the only child of
 278             // the Control via the getNode() method on the Skin. A
 279             // SkinBase will manipulate the children of the Control
 280             // directly. Further, we maintain a direct reference to
 281             // the skinBase for more optimal updates later.
 282             if (skin instanceof SkinBase) {
 283                 // record a reference of the skin, if it is a SkinBase, for
 284                 // performance reasons
 285                 skinBase = (SkinBase<?>) skin;
 286                 // Note I do not remove any children here, because the
 287                 // skin will have already configured all the children
 288                 // by the time setSkin has been called. This is because
 289                 // our Skin interface was lacking an initialize method (doh!)
 290                 // and so the Skin constructor is where it adds listeners
 291                 // and so forth. For SkinBase implementations, the
 292                 // constructor is also where it will take ownership of
 293                 // the children.
 294             } else {
 295                 final Node n = getSkinNode();
 296                 if (n != null) {
 297                     getChildren().setAll(n);
 298                 } else {
 299                     getChildren().clear();
 300                 }
 301             }
 302 
 303             // clear out the styleable properties so that the list is rebuilt
 304             // next time they are requested.
 305             styleableProperties = null;
 306 
 307             // calling NodeHelper.reapplyCSS() as the styleable properties may now
 308             // be different, as we will now be able to return styleable properties
 309             // belonging to the skin. If NodeHelper.reapplyCSS() is not called, the
 310             // getCssMetaData() method is never called, so the
 311             // skin properties are never exposed.
 312             NodeHelper.reapplyCSS(Control.this);
 313 
 314             // DEBUG: Log that we've changed the skin
 315             final PlatformLogger logger = Logging.getControlsLogger();
 316             if (logger.isLoggable(Level.FINEST)) {
 317                 logger.finest("Stored skin[" + getValue() + "] on " + this);
 318             }
 319         }
 320 
 321         // This method should be CssMetaData<Control,Skin> getCssMetaData(),
 322         // but SKIN is CssMetaData<Control,String>. This does not matter to
 323         // the CSS code which doesn't care about the actual type. Hence,
 324         // we'll suppress the warnings
 325         @Override @SuppressWarnings({"unchecked", "rawtype"})
 326         public CssMetaData getCssMetaData() {
 327             return StyleableProperties.SKIN;
 328         }
 329 
 330         @Override
 331         public Object getBean() {
 332             return Control.this;
 333         }
 334 
 335         @Override
 336         public String getName() {
 337             return "skin";
 338         }
 339     };
 340 
 341 
 342     // --- tooltip
 343     /**
 344      * The ToolTip for this control.
 345      */
 346     public final ObjectProperty<Tooltip> tooltipProperty() {
 347         if (tooltip == null) {
 348             tooltip = new ObjectPropertyBase<Tooltip>() {
 349                 private Tooltip old = null;
 350                 @Override protected void invalidated() {
 351                     Tooltip t = get();
 352                     // install / uninstall
 353                     if (t != old) {
 354                         if (old != null) {
 355                             Tooltip.uninstall(Control.this, old);
 356                         }
 357                         if (t != null) {
 358                             Tooltip.install(Control.this, t);
 359                         }
 360                         old = t;
 361                     }
 362                 }
 363 
 364                 @Override
 365                 public Object getBean() {
 366                     return Control.this;
 367                 }
 368 
 369                 @Override
 370                 public String getName() {
 371                     return "tooltip";
 372                 }
 373             };
 374         }
 375         return tooltip;
 376     }
 377     private ObjectProperty<Tooltip> tooltip;
 378     public final void setTooltip(Tooltip value) { tooltipProperty().setValue(value); }
 379     public final Tooltip getTooltip() { return tooltip == null ? null : tooltip.getValue(); }
 380 
 381 
 382     // --- context menu
 383     /**
 384      * The ContextMenu to show for this control.
 385      */
 386     private ObjectProperty<ContextMenu> contextMenu = new SimpleObjectProperty<ContextMenu>(this, "contextMenu") {
 387         private WeakReference<ContextMenu> contextMenuRef;
 388 
 389         @Override protected void invalidated() {
 390             ContextMenu oldMenu = contextMenuRef == null ? null : contextMenuRef.get();
 391             if (oldMenu != null) {
 392                 ControlAcceleratorSupport.removeAcceleratorsFromScene(oldMenu.getItems(), Control.this);
 393             }
 394 
 395             ContextMenu ctx = get();
 396             contextMenuRef = new WeakReference<>(ctx);
 397 
 398             if (ctx != null) {
 399                 // set this flag so contextmenu show will be relative to parent window not anchor
 400                 ctx.setShowRelativeToWindow(true); //RT-15160
 401 
 402                 // if a context menu is set, we need to install any accelerators
 403                 // belonging to its menu items ASAP into the scene that this
 404                 // Control is in (if the control is not in a Scene, we will need
 405                 // to wait until it is and then do it).
 406                 ControlAcceleratorSupport.addAcceleratorsIntoScene(ctx.getItems(), Control.this);
 407             }
 408         }
 409     };
 410     public final ObjectProperty<ContextMenu> contextMenuProperty() { return contextMenu; }
 411     public final void setContextMenu(ContextMenu value) { contextMenu.setValue(value); }
 412     public final ContextMenu getContextMenu() { return contextMenu == null ? null : contextMenu.getValue(); }
 413 
 414 
 415 
 416     /***************************************************************************
 417      *                                                                         *
 418      * Constructors                                                            *
 419      *                                                                         *
 420      **************************************************************************/
 421 
 422     {
 423         // To initialize the class helper at the begining each constructor of this class
 424         ControlHelper.initHelper(this);
 425     }
 426     /**
 427      *  Create a new Control.
 428      */
 429     protected Control() {
 430         // focusTraversable is styleable through css. Calling setFocusTraversable
 431         // makes it look to css like the user set the value and css will not
 432         // override. Initializing focusTraversable by calling applyStyle
 433         // with null for StyleOrigin ensures that css will be able to override
 434         // the value.
 435         final StyleableProperty<Boolean> prop = (StyleableProperty<Boolean>)(WritableValue<Boolean>)focusTraversableProperty();
 436         prop.applyStyle(null, Boolean.TRUE);
 437 
 438         // we add a listener for menu request events to show the context menu
 439         // that may be set on the Control
 440         this.addEventHandler(ContextMenuEvent.CONTEXT_MENU_REQUESTED, contextMenuHandler);
 441 
 442         // TODO re-enable when InputMap moves back to Node / Control
 443 //        // Most controls need an input map, so we set this to be non-null in
 444 //        // Control to save people from running into NPEs.
 445 //        setInputMap(new InputMap(this));
 446     }
 447 
 448 
 449 
 450     /***************************************************************************
 451      *                                                                         *
 452      * Public API                                                              *
 453      *                                                                         *
 454      **************************************************************************/
 455 
 456     // Proposed dispose() API.
 457     // Note that there is impl code for a dispose method in TableRowSkinBase
 458     // and TableCell (just search for dispose())
 459 //    public void dispose() {
 460 //        Skin skin = getSkin();
 461 //        if (skin != null) {
 462 //            skin.dispose();
 463 //        }
 464 //    }
 465 
 466     /**
 467      * Returns <code>true</code> since all Controls are resizable.
 468      * @return whether this node can be resized by its parent during layout
 469      */
 470     @Override public boolean isResizable() {
 471         return true;
 472     }
 473 
 474     // Implementation of the Resizable interface.
 475     // Because only the skin can know the min, pref, and max sizes, these
 476     // functions are implemented to delegate to skin. If there is no skin then
 477     // we simply return 0 for all the values since a Control without a Skin
 478     // doesn't render
 479     /**
 480      * Computes the minimum allowable width of the Control, based on the provided
 481      * height. The minimum width is not calculated within the Control, instead
 482      * the calculation is delegated to the {@link Node#minWidth(double)} method
 483      * of the {@link Skin}. If the Skin is null, the returned value is 0.
 484      *
 485      * @param height The height of the Control, in case this value might dictate
 486      *      the minimum width.
 487      * @return A double representing the minimum width of this control.
 488      */
 489     @Override protected double computeMinWidth(final double height) {
 490         if (skinBase != null) {
 491             return skinBase.computeMinWidth(height, snappedTopInset(), snappedRightInset(), snappedBottomInset(), snappedLeftInset());
 492         } else {
 493             final Node skinNode = getSkinNode();
 494             return skinNode == null ? 0 : skinNode.minWidth(height);
 495         }
 496     }
 497 
 498     /**
 499      * Computes the minimum allowable height of the Control, based on the provided
 500      * width. The minimum height is not calculated within the Control, instead
 501      * the calculation is delegated to the {@link Node#minHeight(double)} method
 502      * of the {@link Skin}. If the Skin is null, the returned value is 0.
 503      *
 504      * @param width The width of the Control, in case this value might dictate
 505      *      the minimum height.
 506      * @return A double representing the minimum height of this control.
 507      */
 508     @Override protected double computeMinHeight(final double width) {
 509         if (skinBase != null) {
 510             return skinBase.computeMinHeight(width, snappedTopInset(), snappedRightInset(), snappedBottomInset(), snappedLeftInset());
 511         } else {
 512             final Node skinNode = getSkinNode();
 513             return skinNode == null ? 0 : skinNode.minHeight(width);
 514         }
 515     }
 516 
 517     /**
 518      * Computes the maximum allowable width of the Control, based on the provided
 519      * height. The maximum width is not calculated within the Control, instead
 520      * the calculation is delegated to the {@link Node#maxWidth(double)} method
 521      * of the {@link Skin}. If the Skin is null, the returned value is 0.
 522      *
 523      * @param height The height of the Control, in case this value might dictate
 524      *      the maximum width.
 525      * @return A double representing the maximum width of this control.
 526      */
 527     @Override protected double computeMaxWidth(double height) {
 528         if (skinBase != null) {
 529             return skinBase.computeMaxWidth(height, snappedTopInset(), snappedRightInset(), snappedBottomInset(), snappedLeftInset());
 530         } else {
 531             final Node skinNode = getSkinNode();
 532             return skinNode == null ? 0 : skinNode.maxWidth(height);
 533         }
 534     }
 535 
 536     /**
 537      * Computes the maximum allowable height of the Control, based on the provided
 538      * width. The maximum height is not calculated within the Control, instead
 539      * the calculation is delegated to the {@link Node#maxHeight(double)} method
 540      * of the {@link Skin}. If the Skin is null, the returned value is 0.
 541      *
 542      * @param width The width of the Control, in case this value might dictate
 543      *      the maximum height.
 544      * @return A double representing the maximum height of this control.
 545      */
 546     @Override protected double computeMaxHeight(double width) {
 547         if (skinBase != null) {
 548             return skinBase.computeMaxHeight(width, snappedTopInset(), snappedRightInset(), snappedBottomInset(), snappedLeftInset());
 549         } else {
 550             final Node skinNode = getSkinNode();
 551             return skinNode == null ? 0 : skinNode.maxHeight(width);
 552         }
 553     }
 554 
 555     /** {@inheritDoc} */
 556     @Override protected double computePrefWidth(double height) {
 557         if (skinBase != null) {
 558             return skinBase.computePrefWidth(height, snappedTopInset(), snappedRightInset(), snappedBottomInset(), snappedLeftInset());
 559         } else {
 560             final Node skinNode = getSkinNode();
 561             return skinNode == null ? 0 : skinNode.prefWidth(height);
 562         }
 563     }
 564 
 565     /** {@inheritDoc} */
 566     @Override protected double computePrefHeight(double width) {
 567         if (skinBase != null) {
 568             return skinBase.computePrefHeight(width, snappedTopInset(), snappedRightInset(), snappedBottomInset(), snappedLeftInset());
 569         } else {
 570             final Node skinNode = getSkinNode();
 571             return skinNode == null ? 0 : skinNode.prefHeight(width);
 572         }
 573     }
 574 
 575     /** {@inheritDoc} */
 576     @Override public double getBaselineOffset() {
 577         if (skinBase != null) {
 578             return skinBase.computeBaselineOffset(snappedTopInset(), snappedRightInset(), snappedBottomInset(), snappedLeftInset());
 579         } else {
 580             final Node skinNode = getSkinNode();
 581             return skinNode == null ? 0 : skinNode.getBaselineOffset();
 582         }
 583     }
 584 
 585     /***************************************************************************
 586      * Implementation of layout bounds for the Control. We want to preserve    *
 587      * the lazy semantics of layout bounds. So whenever the width/height       *
 588      * changes on the node, we end up invalidating layout bounds. We then      *
 589      * recompute it on demand.                                                 *
 590      **************************************************************************/
 591 
 592     /** {@inheritDoc} */
 593     @Override protected void layoutChildren() {
 594         if (skinBase != null) {
 595             final double x = snappedLeftInset();
 596             final double y = snappedTopInset();
 597             final double w = snapSizeX(getWidth()) - x - snappedRightInset();
 598             final double h = snapSizeY(getHeight()) - y - snappedBottomInset();
 599             skinBase.layoutChildren(x, y, w, h);
 600         } else {
 601             Node n = getSkinNode();
 602             if (n != null) {
 603                 n.resizeRelocate(0, 0, getWidth(), getHeight());
 604             }
 605         }
 606     }
 607 
 608     /***************************************************************************
 609      * Forward the following to the skin                                       *
 610      **************************************************************************/
 611 
 612     /**
 613      * Create a new instance of the default skin for this control. This is called to create a skin for the control if
 614      * no skin is provided via CSS {@code -fx-skin} or set explicitly in a sub-class with {@code  setSkin(...)}.
 615      *
 616      * @return  new instance of default skin for this control. If null then the control will have no skin unless one
 617      *          is provided by css.
 618      * @since JavaFX 8.0
 619      */
 620     protected Skin<?> createDefaultSkin() {
 621         return null;
 622     }
 623 
 624     /***************************************************************************
 625      *                                                                         *
 626      * Package API for SkinBase                                                *
 627      *                                                                         *
 628      **************************************************************************/
 629 
 630     // package private for SkinBase
 631     ObservableList<Node> getControlChildren() {
 632         return getChildren();
 633     }
 634 
 635 
 636     /***************************************************************************
 637      *                                                                         *
 638      * Private implementation                                                  *
 639      *                                                                         *
 640      **************************************************************************/
 641 
 642     /**
 643      * Gets the Skin's node, or returns null if there is no Skin.
 644      * Convenience method for getting the node of the skin. This is null-safe,
 645      * meaning if skin is null then it will return null instead of throwing
 646      * a NullPointerException.
 647      *
 648      * @return The Skin's node, or null.
 649      */
 650     private Node getSkinNode() {
 651         assert skinBase == null;
 652         Skin<?> skin = getSkin();
 653         return skin == null ? null : skin.getNode();
 654     }
 655 
 656     /**
 657      * Keeps a reference to the name of the class currently acting as the skin.
 658      */
 659     private String currentSkinClassName = null;
 660     private StringProperty skinClassName;
 661 
 662     StringProperty skinClassNameProperty() {
 663         if (skinClassName == null) {
 664             skinClassName = new StyleableStringProperty() {
 665 
 666                 @Override
 667                 public void set(String v) {
 668                     // do not allow the skin to be set to null through CSS
 669                     if (v == null || v.isEmpty() || v.equals(get())) return;
 670                     super.set(v);
 671                 }
 672 
 673                 @Override
 674                 public void invalidated() {
 675 
 676                     if (get() != null) {
 677                         if (!get().equals(currentSkinClassName)) {
 678                             loadSkinClass(Control.this, skinClassName.get());
 679                         }
 680                         // Note: CSS should not set skin to null
 681                     }
 682                 }
 683 
 684                 @Override
 685                 public Object getBean() {
 686                     return Control.this;
 687                 }
 688 
 689                 @Override
 690                 public String getName() {
 691                     return "skinClassName";
 692                 }
 693 
 694                 @Override
 695                 public CssMetaData<Control,String> getCssMetaData() {
 696                     return StyleableProperties.SKIN;
 697                 }
 698 
 699             };
 700         }
 701         return skinClassName;
 702     }
 703 
 704     static void loadSkinClass(final Skinnable control, final String skinClassName) {
 705         if (skinClassName == null || skinClassName.isEmpty()) {
 706             final String msg =
 707                 "Empty -fx-skin property specified for control " + control;
 708             final List<CssParser.ParseError> errors = StyleManager.getErrors();
 709             if (errors != null) {
 710                 CssParser.ParseError error = new CssParser.ParseError(msg);
 711                 errors.add(error); // RT-19884
 712             }
 713             Logging.getControlsLogger().severe(msg);
 714             return;
 715         }
 716 
 717         try {
 718             final Class<?> skinClass = Control.loadClass(skinClassName, control);
 719             if (!Skin.class.isAssignableFrom(skinClass)) {
 720                 final String msg =
 721                     "'" + skinClassName + "' is not a valid Skin class for control " + control;
 722                 final List<CssParser.ParseError> errors = StyleManager.getErrors();
 723                 if (errors != null) {
 724                     CssParser.ParseError error = new CssParser.ParseError(msg);
 725                     errors.add(error); // RT-19884
 726                 }
 727                 Logging.getControlsLogger().severe(msg);
 728                 return;
 729             }
 730             Constructor<?>[] constructors = skinClass.getConstructors();
 731             Constructor<?> skinConstructor = null;
 732             for (Constructor<?> c : constructors) {
 733                 Class<?>[] parameterTypes = c.getParameterTypes();
 734                 if (parameterTypes.length == 1 && Skinnable.class.isAssignableFrom(parameterTypes[0])) {
 735                     skinConstructor = c;
 736                     break;
 737                 }
 738             }
 739 
 740             if (skinConstructor == null) {
 741                 final String msg =
 742                     "No valid constructor defined in '" + skinClassName + "' for control " + control +
 743                         ".\r\nYou must provide a constructor that accepts a single "
 744                         + "Skinnable (e.g. Control or PopupControl) parameter in " + skinClassName + ".";
 745                 final List<CssParser.ParseError> errors = StyleManager.getErrors();
 746                 if (errors != null) {
 747                     CssParser.ParseError error = new CssParser.ParseError(msg);
 748                     errors.add(error); // RT-19884
 749                 }
 750                 Logging.getControlsLogger().severe(msg);
 751             } else {
 752                 Skin<?> skinInstance = (Skin<?>) skinConstructor.newInstance(control);
 753                 // Do not call setSkin here since it has the side effect of
 754                 // also setting the skinClassName!
 755                 control.skinProperty().set(skinInstance);
 756             }
 757         } catch (InvocationTargetException e) {
 758             final String msg =
 759                 "Failed to load skin '" + skinClassName + "' for control " + control;
 760             final List<CssParser.ParseError> errors = StyleManager.getErrors();
 761             if (errors != null) {
 762                 CssParser.ParseError error = new CssParser.ParseError(msg + " :" + e.getLocalizedMessage());
 763                 errors.add(error); // RT-19884
 764             }
 765             Logging.getControlsLogger().severe(msg, e.getCause());
 766         } catch (Exception e) {
 767             final String msg =
 768                 "Failed to load skin '" + skinClassName + "' for control " + control;
 769             final List<CssParser.ParseError> errors = StyleManager.getErrors();
 770             if (errors != null) {
 771                 CssParser.ParseError error = new CssParser.ParseError(msg + " :" + e.getLocalizedMessage());
 772                 errors.add(error); // RT-19884
 773             }
 774             Logging.getControlsLogger().severe(msg, e);
 775         }
 776     }
 777 
 778     /***************************************************************************
 779      *                                                                         *
 780      * StyleSheet Handling                                                     *
 781      *                                                                         *
 782      **************************************************************************/
 783 
 784     private static class StyleableProperties {
 785         private static final CssMetaData<Control,String> SKIN =
 786             new CssMetaData<Control,String>("-fx-skin",
 787                 StringConverter.getInstance()) {
 788 
 789             @Override
 790             public boolean isSettable(Control n) {
 791                 return (n.skin == null || !n.skin.isBound());
 792             }
 793 
 794             @Override
 795             public StyleableProperty<String> getStyleableProperty(Control n) {
 796                 return (StyleableProperty<String>)(WritableValue<String>)n.skinClassNameProperty();
 797             }
 798         };
 799 
 800         private static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES;
 801         static {
 802             final List<CssMetaData<? extends Styleable, ?>> styleables =
 803                 new ArrayList<CssMetaData<? extends Styleable, ?>>(Region.getClassCssMetaData());
 804             styleables.add(SKIN);
 805             STYLEABLES = Collections.unmodifiableList(styleables);
 806         }
 807     }
 808 
 809     /**
 810      * @return The CssMetaData associated with this class, which may include the
 811      * CssMetaData of its super classes.
 812      * @since JavaFX 8.0
 813      */
 814     public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() {
 815         return StyleableProperties.STYLEABLES;
 816     }
 817 
 818     /**
 819      * This method returns a {@link List} containing all {@link CssMetaData} for
 820      * both this Control (returned from {@link #getControlCssMetaData()} and its
 821      * {@link Skin}, assuming the {@link #skinProperty() skin property} is a
 822      * {@link SkinBase}.
 823      *
 824      * <p>Developers who wish to provide custom CssMetaData are therefore
 825      * encouraged to override {@link Control#getControlCssMetaData()} or
 826      * {@link SkinBase#getCssMetaData()}, depending on where the CssMetaData
 827      * resides.
 828      * @since JavaFX 8.0
 829      */
 830     @Override
 831     public final List<CssMetaData<? extends Styleable, ?>> getCssMetaData() {
 832         if (styleableProperties == null) {
 833 
 834             // RT-29162: make sure properties only show up once in the list
 835             java.util.Map<String, CssMetaData<? extends Styleable, ?>> map =
 836                 new java.util.HashMap<String, CssMetaData<? extends Styleable, ?>>();
 837 
 838             List<CssMetaData<? extends Styleable, ?>> list =  getControlCssMetaData();
 839 
 840             for (int n=0, nMax = list != null ? list.size() : 0; n<nMax; n++) {
 841 
 842                 CssMetaData<? extends Styleable, ?> metaData = list.get(n);
 843                 if (metaData == null) continue;
 844 
 845                 map.put(metaData.getProperty(), metaData);
 846             }
 847 
 848             //
 849             // if both control and skin base have the same property, use the
 850             // one from skin base since it may be a specialization of the
 851             // property in the control. For instance, Label has -fx-font and
 852             // so does LabeledText which is Label's skin.
 853             //
 854             list =  skinBase != null ? skinBase.getCssMetaData() : null;
 855 
 856             for (int n=0, nMax = list != null ? list.size() : 0; n<nMax; n++) {
 857 
 858                 CssMetaData<? extends Styleable, ?> metaData = list.get(n);
 859                 if (metaData == null) continue;
 860 
 861                 map.put(metaData.getProperty(), metaData);
 862             }
 863 
 864             styleableProperties = new ArrayList<CssMetaData<? extends Styleable, ?>>();
 865             styleableProperties.addAll(map.values());
 866         }
 867         return styleableProperties;
 868     }
 869 
 870     /**
 871      * @return unmodifiable list of the controls css styleable properties
 872      * @since JavaFX 8.0
 873      */
 874     protected List<CssMetaData<? extends Styleable, ?>> getControlCssMetaData() {
 875         return getClassCssMetaData();
 876     }
 877 
 878     /*
 879      * Note: This method MUST only be called via its accessor method.
 880      */
 881     private void doProcessCSS() {
 882 
 883         ControlHelper.superProcessCSS(this);
 884 
 885         if (getSkin() == null) {
 886             // try to create default skin
 887             final Skin<?> defaultSkin = createDefaultSkin();
 888             if (defaultSkin != null) {
 889                 skinProperty().set(defaultSkin);
 890                 ControlHelper.superProcessCSS(this);
 891             } else {
 892                 final String msg = "The -fx-skin property has not been defined in CSS for " + this +
 893                                    " and createDefaultSkin() returned null.";
 894                 final List<CssParser.ParseError> errors = StyleManager.getErrors();
 895                 if (errors != null) {
 896                     CssParser.ParseError error = new CssParser.ParseError(msg);
 897                     errors.add(error); // RT-19884
 898                 }
 899                 Logging.getControlsLogger().severe(msg);
 900             }
 901         }
 902     }
 903 
 904     /**
 905      * Returns the initial focus traversable state of this control, for use
 906      * by the JavaFX CSS engine to correctly set its initial value. By default all
 907      * UI controls are focus traversable, so this method is overridden in Control
 908      * to set the initial traversable state to true.
 909      *
 910      * @since 9
 911      */
 912     @Override protected Boolean getInitialFocusTraversable() {
 913         return Boolean.TRUE;
 914     }
 915 
 916 
 917     /***************************************************************************
 918      *                                                                         *
 919      * Accessibility handling                                                  *
 920      *                                                                         *
 921      **************************************************************************/
 922 
 923     @Override
 924     public Object queryAccessibleAttribute(AccessibleAttribute attribute, Object... parameters) {
 925         switch (attribute) {
 926             case HELP:
 927                 String help = getAccessibleHelp();
 928                 if (help != null && !help.isEmpty()) return help;
 929                 Tooltip tooltip = getTooltip();
 930                 return tooltip == null ? "" : tooltip.getText();
 931             default:
 932         }
 933         if (skinBase != null) {
 934             Object result = skinBase.queryAccessibleAttribute(attribute, parameters);
 935             if (result != null) return result;
 936         }
 937         return super.queryAccessibleAttribute(attribute, parameters);
 938     }
 939 
 940     @Override
 941     public void executeAccessibleAction(AccessibleAction action, Object... parameters) {
 942         if (skinBase != null) {
 943             skinBase.executeAccessibleAction(action, parameters);
 944         }
 945         super.executeAccessibleAction(action, parameters);
 946     }
 947 }