1 /*
   2  * Copyright (c) 2010, 2015, 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         });
  92 
  93         // Ensures that the default application user agent stylesheet is loaded
  94         if (Application.getUserAgentStylesheet() == null) {
  95             PlatformImpl.setDefaultPlatformUserAgentStylesheet();
  96         }
  97     }
  98 
  99     /**
 100      * Utility for loading a class in a manner that will work with multiple
 101      * class loaders, as is typically found in OSGI modular applications.
 102      * In particular, this method will attempt to just load the class
 103      * identified by className. If that fails, it attempts to load the
 104      * class using the current thread's context class loader. If that fails,
 105      * it attempts to use the class loader of the supplied "instance", and
 106      * if it still fails it walks up the class hierarchy of the instance
 107      * and attempts to use the class loader of each class in the super-type
 108      * hierarchy.
 109      *
 110      * @param className The name of the class we want to load
 111      * @param instance An optional instance used to help find the class to load
 112      * @return The class. Cannot return null
 113      * @throws ClassNotFoundException If the class cannot be found using any technique.
 114      */
 115     private static Class<?> loadClass(final String className, final Object instance)
 116             throws ClassNotFoundException
 117     {
 118         try {
 119             // Try just loading the class
 120             return Class.forName(className, false, Control.class.getClassLoader());
 121         } catch (ClassNotFoundException ex) {
 122             // RT-17525 : Use context class loader only if Class.forName fails.
 123             if (Thread.currentThread().getContextClassLoader() != null) {
 124                 try {
 125                     final ClassLoader ccl = Thread.currentThread().getContextClassLoader();
 126                     return Class.forName(className, false, ccl);
 127                 } catch (ClassNotFoundException ex2) {
 128                     // Do nothing, just fall through
 129                 }
 130             }
 131 
 132             // RT-14177: Try looking up the class using the class loader of the
 133             //           current class, walking up the list of superclasses
 134             //           and checking each of them, before bailing and using
 135             //           the context class loader.
 136             if (instance != null) {
 137                 Class<?> currentType = instance.getClass();
 138                 while (currentType != null) {
 139                     try {
 140                         final ClassLoader loader = currentType.getClassLoader();
 141                         return Class.forName(className, false, loader);
 142                     } catch (ClassNotFoundException ex2) {
 143                         currentType = currentType.getSuperclass();
 144                     }
 145                 }
 146             }
 147 
 148             // We failed to find the class using any of the above means, so we're going
 149             // to just throw the ClassNotFoundException that we caught earlier
 150             throw ex;
 151         }
 152     }
 153 
 154     /***************************************************************************
 155      *                                                                         *
 156      * Private fields                                                          *
 157      *                                                                         *
 158      **************************************************************************/
 159 
 160     private List<CssMetaData<? extends Styleable, ?>> styleableProperties;
 161 
 162     /**
 163      * A private reference directly to the SkinBase instance that is used as the
 164      * Skin for this Control. A Control's Skin doesn't have to be of type
 165      * SkinBase, although 98% of the time or greater it probably will be.
 166      * Because instanceof checks and reading a value from a property are
 167      * not cheap (on interpreters on slower hardware or mobile devices)
 168      * it pays to have a direct reference here to the skinBase. We simply
 169      * need to check this variable -- if it is not null then we know the
 170      * Skin is a SkinBase and this is a direct reference to it. If it is null
 171      * then we know the skin is not a SkinBase and we need to call getSkin().
 172      */
 173     private SkinBase<?> skinBase;
 174 
 175     /***************************************************************************
 176     *                                                                         *
 177     * Event Handlers / Listeners                                              *
 178     *                                                                         *
 179     **************************************************************************/
 180 
 181     /**
 182      * Handles context menu requests by popping up the menu.
 183      * Note that we use this pattern to remove some of the anonymous inner
 184      * classes which we'd otherwise have to create. When lambda expressions
 185      * are supported, we could do it that way instead (or use MethodHandles).
 186      */
 187     private final static EventHandler<ContextMenuEvent> contextMenuHandler = event -> {
 188         if (event.isConsumed()) return;
 189 
 190         // If a context menu was shown, consume the event to prevent multiple context menus
 191         Object source = event.getSource();
 192         if (source instanceof Control) {
 193             Control c = (Control) source;
 194             if (c.getContextMenu() != null) {
 195                 c.getContextMenu().show(c, event.getScreenX(), event.getScreenY());
 196                 event.consume();
 197             }
 198         }
 199     };
 200 
 201 
 202 
 203     /***************************************************************************
 204      *                                                                         *
 205      * Properties                                                              *
 206      *                                                                         *
 207      **************************************************************************/
 208 
 209 
 210 
 211     // --- skin
 212     /**
 213      * Skin is responsible for rendering this {@code Control}. From the
 214      * perspective of the {@code Control}, the {@code Skin} is a black box.
 215      * It listens and responds to changes in state in a {@code Control}.
 216      * <p>
 217      * There is a one-to-one relationship between a {@code Control} and its
 218      * {@code Skin}. Every {@code Skin} maintains a back reference to the
 219      * {@code Control} via the {@link Skin#getSkinnable()} method.
 220      * <p>
 221      * A skin may be null.
 222      */
 223     @Override public final ObjectProperty<Skin<?>> skinProperty() { return skin; }
 224     @Override public final void setSkin(Skin<?> value) {
 225         skinProperty().set(value);
 226     }
 227     @Override public final Skin<?> getSkin() { return skinProperty().getValue(); }
 228     private ObjectProperty<Skin<?>> skin = new StyleableObjectProperty<Skin<?>>() {
 229         // We store a reference to the oldValue so that we can handle
 230         // changes in the skin properly in the case of binding. This is
 231         // only needed because invalidated() does not currently take
 232         // a reference to the old value.
 233         private Skin<?> oldValue;
 234 
 235         @Override
 236         //This code is basically a kind of optimization that prevents a Skin that is equal but not instance equal.
 237         //Although it's not kosher from the property perspective (bindings won't pass through set), it should not do any harm.
 238         //But it should be evaluated in the future.
 239         public void set(Skin<?> v) {
 240             if (v == null
 241                 ? oldValue == null
 242                 : oldValue != null && v.getClass().equals(oldValue.getClass()))
 243                 return;
 244 
 245             super.set(v);
 246         }
 247 
 248         @Override protected void invalidated() {
 249             Skin<?> skin = get();
 250             // Collect the name of the currently installed skin class. We do this
 251             // so that subsequent updates from CSS to the same skin class will not
 252             // result in reinstalling the skin
 253             currentSkinClassName = skin == null ? null : skin.getClass().getName();
 254 
 255             // if someone calls setSkin, we need to make it look like they
 256             // called set on skinClassName in order to keep CSS from overwriting
 257             // the skin.
 258             skinClassNameProperty().set(currentSkinClassName);
 259 
 260 
 261             // Dispose of the old skin
 262             if (oldValue != null) oldValue.dispose();
 263 
 264             // Get the new value, and save it off as the new oldValue
 265             oldValue = skin;
 266 
 267             // Reset skinBase to null - it will be set to the new Skin if it
 268             // is a SkinBase, otherwise it will remain null, as expected
 269             skinBase = null;
 270 
 271             // We have two paths, one for "legacy" Skins, and one for
 272             // any Skin which extends from SkinBase. Legacy Skins will
 273             // produce a single node which will be the only child of
 274             // the Control via the getNode() method on the Skin. A
 275             // SkinBase will manipulate the children of the Control
 276             // directly. Further, we maintain a direct reference to
 277             // the skinBase for more optimal updates later.
 278             if (skin instanceof SkinBase) {
 279                 // record a reference of the skin, if it is a SkinBase, for
 280                 // performance reasons
 281                 skinBase = (SkinBase<?>) skin;
 282                 // Note I do not remove any children here, because the
 283                 // skin will have already configured all the children
 284                 // by the time setSkin has been called. This is because
 285                 // our Skin interface was lacking an initialize method (doh!)
 286                 // and so the Skin constructor is where it adds listeners
 287                 // and so forth. For SkinBase implementations, the
 288                 // constructor is also where it will take ownership of
 289                 // the children.
 290             } else {
 291                 final Node n = getSkinNode();
 292                 if (n != null) {
 293                     getChildren().setAll(n);
 294                 } else {
 295                     getChildren().clear();
 296                 }
 297             }
 298 
 299             // clear out the styleable properties so that the list is rebuilt
 300             // next time they are requested.
 301             styleableProperties = null;
 302 
 303             // calling NodeHelper.reapplyCSS() as the styleable properties may now
 304             // be different, as we will now be able to return styleable properties
 305             // belonging to the skin. If NodeHelper.reapplyCSS() is not called, the
 306             // getCssMetaData() method is never called, so the
 307             // skin properties are never exposed.
 308             NodeHelper.reapplyCSS(Control.this);
 309 
 310             // DEBUG: Log that we've changed the skin
 311             final PlatformLogger logger = Logging.getControlsLogger();
 312             if (logger.isLoggable(Level.FINEST)) {
 313                 logger.finest("Stored skin[" + getValue() + "] on " + this);
 314             }
 315         }
 316 
 317         // This method should be CssMetaData<Control,Skin> getCssMetaData(),
 318         // but SKIN is CssMetaData<Control,String>. This does not matter to
 319         // the CSS code which doesn't care about the actual type. Hence,
 320         // we'll suppress the warnings
 321         @Override @SuppressWarnings({"unchecked", "rawtype"})
 322         public CssMetaData getCssMetaData() {
 323             return StyleableProperties.SKIN;
 324         }
 325 
 326         @Override
 327         public Object getBean() {
 328             return Control.this;
 329         }
 330 
 331         @Override
 332         public String getName() {
 333             return "skin";
 334         }
 335     };
 336 
 337 
 338     // --- tooltip
 339     /**
 340      * The ToolTip for this control.
 341      */
 342     public final ObjectProperty<Tooltip> tooltipProperty() {
 343         if (tooltip == null) {
 344             tooltip = new ObjectPropertyBase<Tooltip>() {
 345                 private Tooltip old = null;
 346                 @Override protected void invalidated() {
 347                     Tooltip t = get();
 348                     // install / uninstall
 349                     if (t != old) {
 350                         if (old != null) {
 351                             Tooltip.uninstall(Control.this, old);
 352                         }
 353                         if (t != null) {
 354                             Tooltip.install(Control.this, t);
 355                         }
 356                         old = t;
 357                     }
 358                 }
 359 
 360                 @Override
 361                 public Object getBean() {
 362                     return Control.this;
 363                 }
 364 
 365                 @Override
 366                 public String getName() {
 367                     return "tooltip";
 368                 }
 369             };
 370         }
 371         return tooltip;
 372     }
 373     private ObjectProperty<Tooltip> tooltip;
 374     public final void setTooltip(Tooltip value) { tooltipProperty().setValue(value); }
 375     public final Tooltip getTooltip() { return tooltip == null ? null : tooltip.getValue(); }
 376 
 377 
 378     // --- context menu
 379     /**
 380      * The ContextMenu to show for this control.
 381      */
 382     private ObjectProperty<ContextMenu> contextMenu = new SimpleObjectProperty<ContextMenu>(this, "contextMenu") {
 383         private WeakReference<ContextMenu> contextMenuRef;
 384 
 385         @Override protected void invalidated() {
 386             ContextMenu oldMenu = contextMenuRef == null ? null : contextMenuRef.get();
 387             if (oldMenu != null) {
 388                 ControlAcceleratorSupport.removeAcceleratorsFromScene(oldMenu.getItems(), Control.this);
 389             }
 390 
 391             ContextMenu ctx = get();
 392             contextMenuRef = new WeakReference<>(ctx);
 393 
 394             if (ctx != null) {
 395                 // set this flag so contextmenu show will be relative to parent window not anchor
 396                 ctx.setShowRelativeToWindow(true); //RT-15160
 397 
 398                 // if a context menu is set, we need to install any accelerators
 399                 // belonging to its menu items ASAP into the scene that this
 400                 // Control is in (if the control is not in a Scene, we will need
 401                 // to wait until it is and then do it).
 402                 ControlAcceleratorSupport.addAcceleratorsIntoScene(ctx.getItems(), Control.this);
 403             }
 404         }
 405     };
 406     public final ObjectProperty<ContextMenu> contextMenuProperty() { return contextMenu; }
 407     public final void setContextMenu(ContextMenu value) { contextMenu.setValue(value); }
 408     public final ContextMenu getContextMenu() { return contextMenu == null ? null : contextMenu.getValue(); }
 409 
 410 
 411 
 412     /***************************************************************************
 413      *                                                                         *
 414      * Constructors                                                            *
 415      *                                                                         *
 416      **************************************************************************/
 417 
 418     {
 419         // To initialize the class helper at the begining each constructor of this class
 420         ControlHelper.initHelper(this);
 421     }
 422     /**
 423      *  Create a new Control.
 424      */
 425     protected Control() {
 426         // focusTraversable is styleable through css. Calling setFocusTraversable
 427         // makes it look to css like the user set the value and css will not
 428         // override. Initializing focusTraversable by calling applyStyle
 429         // with null for StyleOrigin ensures that css will be able to override
 430         // the value.
 431         final StyleableProperty<Boolean> prop = (StyleableProperty<Boolean>)(WritableValue<Boolean>)focusTraversableProperty();
 432         prop.applyStyle(null, Boolean.TRUE);
 433 
 434         // we add a listener for menu request events to show the context menu
 435         // that may be set on the Control
 436         this.addEventHandler(ContextMenuEvent.CONTEXT_MENU_REQUESTED, contextMenuHandler);
 437 
 438         // TODO re-enable when InputMap moves back to Node / Control
 439 //        // Most controls need an input map, so we set this to be non-null in
 440 //        // Control to save people from running into NPEs.
 441 //        setInputMap(new InputMap(this));
 442     }
 443 
 444 
 445 
 446     /***************************************************************************
 447      *                                                                         *
 448      * Public API                                                              *
 449      *                                                                         *
 450      **************************************************************************/
 451 
 452     // Proposed dispose() API.
 453     // Note that there is impl code for a dispose method in TableRowSkinBase
 454     // and TableCell (just search for dispose())
 455 //    public void dispose() {
 456 //        Skin skin = getSkin();
 457 //        if (skin != null) {
 458 //            skin.dispose();
 459 //        }
 460 //    }
 461 
 462     /**
 463      * Returns <code>true</code> since all Controls are resizable.
 464      * @return whether this node can be resized by its parent during layout
 465      */
 466     @Override public boolean isResizable() {
 467         return true;
 468     }
 469 
 470     // Implementation of the Resizable interface.
 471     // Because only the skin can know the min, pref, and max sizes, these
 472     // functions are implemented to delegate to skin. If there is no skin then
 473     // we simply return 0 for all the values since a Control without a Skin
 474     // doesn't render
 475     /**
 476      * Computes the minimum allowable width of the Control, based on the provided
 477      * height. The minimum width is not calculated within the Control, instead
 478      * the calculation is delegated to the {@link Node#minWidth(double)} method
 479      * of the {@link Skin}. If the Skin is null, the returned value is 0.
 480      *
 481      * @param height The height of the Control, in case this value might dictate
 482      *      the minimum width.
 483      * @return A double representing the minimum width of this control.
 484      */
 485     @Override protected double computeMinWidth(final double height) {
 486         if (skinBase != null) {
 487             return skinBase.computeMinWidth(height, snappedTopInset(), snappedRightInset(), snappedBottomInset(), snappedLeftInset());
 488         } else {
 489             final Node skinNode = getSkinNode();
 490             return skinNode == null ? 0 : skinNode.minWidth(height);
 491         }
 492     }
 493 
 494     /**
 495      * Computes the minimum allowable height of the Control, based on the provided
 496      * width. The minimum height is not calculated within the Control, instead
 497      * the calculation is delegated to the {@link Node#minHeight(double)} method
 498      * of the {@link Skin}. If the Skin is null, the returned value is 0.
 499      *
 500      * @param width The width of the Control, in case this value might dictate
 501      *      the minimum height.
 502      * @return A double representing the minimum height of this control.
 503      */
 504     @Override protected double computeMinHeight(final double width) {
 505         if (skinBase != null) {
 506             return skinBase.computeMinHeight(width, snappedTopInset(), snappedRightInset(), snappedBottomInset(), snappedLeftInset());
 507         } else {
 508             final Node skinNode = getSkinNode();
 509             return skinNode == null ? 0 : skinNode.minHeight(width);
 510         }
 511     }
 512 
 513     /**
 514      * Computes the maximum allowable width of the Control, based on the provided
 515      * height. The maximum width is not calculated within the Control, instead
 516      * the calculation is delegated to the {@link Node#maxWidth(double)} method
 517      * of the {@link Skin}. If the Skin is null, the returned value is 0.
 518      *
 519      * @param height The height of the Control, in case this value might dictate
 520      *      the maximum width.
 521      * @return A double representing the maximum width of this control.
 522      */
 523     @Override protected double computeMaxWidth(double height) {
 524         if (skinBase != null) {
 525             return skinBase.computeMaxWidth(height, snappedTopInset(), snappedRightInset(), snappedBottomInset(), snappedLeftInset());
 526         } else {
 527             final Node skinNode = getSkinNode();
 528             return skinNode == null ? 0 : skinNode.maxWidth(height);
 529         }
 530     }
 531 
 532     /**
 533      * Computes the maximum allowable height of the Control, based on the provided
 534      * width. The maximum height is not calculated within the Control, instead
 535      * the calculation is delegated to the {@link Node#maxHeight(double)} method
 536      * of the {@link Skin}. If the Skin is null, the returned value is 0.
 537      *
 538      * @param width The width of the Control, in case this value might dictate
 539      *      the maximum height.
 540      * @return A double representing the maximum height of this control.
 541      */
 542     @Override protected double computeMaxHeight(double width) {
 543         if (skinBase != null) {
 544             return skinBase.computeMaxHeight(width, snappedTopInset(), snappedRightInset(), snappedBottomInset(), snappedLeftInset());
 545         } else {
 546             final Node skinNode = getSkinNode();
 547             return skinNode == null ? 0 : skinNode.maxHeight(width);
 548         }
 549     }
 550 
 551     /** {@inheritDoc} */
 552     @Override protected double computePrefWidth(double height) {
 553         if (skinBase != null) {
 554             return skinBase.computePrefWidth(height, snappedTopInset(), snappedRightInset(), snappedBottomInset(), snappedLeftInset());
 555         } else {
 556             final Node skinNode = getSkinNode();
 557             return skinNode == null ? 0 : skinNode.prefWidth(height);
 558         }
 559     }
 560 
 561     /** {@inheritDoc} */
 562     @Override protected double computePrefHeight(double width) {
 563         if (skinBase != null) {
 564             return skinBase.computePrefHeight(width, snappedTopInset(), snappedRightInset(), snappedBottomInset(), snappedLeftInset());
 565         } else {
 566             final Node skinNode = getSkinNode();
 567             return skinNode == null ? 0 : skinNode.prefHeight(width);
 568         }
 569     }
 570 
 571     /** {@inheritDoc} */
 572     @Override public double getBaselineOffset() {
 573         if (skinBase != null) {
 574             return skinBase.computeBaselineOffset(snappedTopInset(), snappedRightInset(), snappedBottomInset(), snappedLeftInset());
 575         } else {
 576             final Node skinNode = getSkinNode();
 577             return skinNode == null ? 0 : skinNode.getBaselineOffset();
 578         }
 579     }
 580 
 581     /***************************************************************************
 582      * Implementation of layout bounds for the Control. We want to preserve    *
 583      * the lazy semantics of layout bounds. So whenever the width/height       *
 584      * changes on the node, we end up invalidating layout bounds. We then      *
 585      * recompute it on demand.                                                 *
 586      **************************************************************************/
 587 
 588     /** {@inheritDoc} */
 589     @Override protected void layoutChildren() {
 590         if (skinBase != null) {
 591             final double x = snappedLeftInset();
 592             final double y = snappedTopInset();
 593             final double w = snapSizeX(getWidth()) - x - snappedRightInset();
 594             final double h = snapSizeY(getHeight()) - y - snappedBottomInset();
 595             skinBase.layoutChildren(x, y, w, h);
 596         } else {
 597             Node n = getSkinNode();
 598             if (n != null) {
 599                 n.resizeRelocate(0, 0, getWidth(), getHeight());
 600             }
 601         }
 602     }
 603 
 604     /***************************************************************************
 605      * Forward the following to the skin                                       *
 606      **************************************************************************/
 607 
 608     /**
 609      * Create a new instance of the default skin for this control. This is called to create a skin for the control if
 610      * no skin is provided via CSS {@code -fx-skin} or set explicitly in a sub-class with {@code  setSkin(...)}.
 611      *
 612      * @return  new instance of default skin for this control. If null then the control will have no skin unless one
 613      *          is provided by css.
 614      * @since JavaFX 8.0
 615      */
 616     protected Skin<?> createDefaultSkin() {
 617         return null;
 618     }
 619 
 620     /***************************************************************************
 621      *                                                                         *
 622      * Package API for SkinBase                                                *
 623      *                                                                         *
 624      **************************************************************************/
 625 
 626     // package private for SkinBase
 627     ObservableList<Node> getControlChildren() {
 628         return getChildren();
 629     }
 630 
 631 
 632     /***************************************************************************
 633      *                                                                         *
 634      * Private implementation                                                  *
 635      *                                                                         *
 636      **************************************************************************/
 637 
 638     /**
 639      * Gets the Skin's node, or returns null if there is no Skin.
 640      * Convenience method for getting the node of the skin. This is null-safe,
 641      * meaning if skin is null then it will return null instead of throwing
 642      * a NullPointerException.
 643      *
 644      * @return The Skin's node, or null.
 645      */
 646     private Node getSkinNode() {
 647         assert skinBase == null;
 648         Skin<?> skin = getSkin();
 649         return skin == null ? null : skin.getNode();
 650     }
 651 
 652     /**
 653      * Keeps a reference to the name of the class currently acting as the skin.
 654      */
 655     private String currentSkinClassName = null;
 656     private StringProperty skinClassName;
 657 
 658     /**
 659      * @treatAsPrivate
 660      * @since JavaFX 2.1
 661      */
 662     @Deprecated protected 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 }