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