1 /*
   2  * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package javax.swing;
  27 
  28 
  29 import javax.swing.plaf.ComponentUI;
  30 import javax.swing.border.*;
  31 import javax.swing.event.SwingPropertyChangeSupport;
  32 
  33 import java.lang.reflect.*;
  34 import java.util.HashMap;
  35 import java.util.Map;
  36 import java.util.Enumeration;
  37 import java.util.Hashtable;
  38 import java.util.ResourceBundle;
  39 import java.util.ResourceBundle.Control;
  40 import java.util.Locale;
  41 import java.util.Vector;
  42 import java.util.MissingResourceException;
  43 import java.awt.Font;
  44 import java.awt.Color;
  45 import java.awt.Insets;
  46 import java.awt.Dimension;
  47 import java.beans.PropertyChangeListener;
  48 import java.security.AccessController;
  49 import java.security.AccessControlContext;
  50 import java.security.PrivilegedAction;
  51 
  52 import sun.reflect.misc.MethodUtil;
  53 import sun.reflect.misc.ReflectUtil;
  54 import sun.swing.SwingUtilities2;
  55 import sun.util.CoreResourceBundleControl;
  56 
  57 /**
  58  * A table of defaults for Swing components.  Applications can set/get
  59  * default values via the <code>UIManager</code>.
  60  * <p>
  61  * <strong>Warning:</strong>
  62  * Serialized objects of this class will not be compatible with
  63  * future Swing releases. The current serialization support is
  64  * appropriate for short term storage or RMI between applications running
  65  * the same version of Swing.  As of 1.4, support for long term storage
  66  * of all JavaBeans&trade;
  67  * has been added to the <code>java.beans</code> package.
  68  * Please see {@link java.beans.XMLEncoder}.
  69  *
  70  * @see UIManager
  71  * @author Hans Muller
  72  * @since 1.2
  73  */
  74 @SuppressWarnings("serial") // Same-version serialization only
  75 public class UIDefaults extends Hashtable<Object,Object>
  76 {
  77     private static final Object PENDING = new Object();
  78 
  79     private SwingPropertyChangeSupport changeSupport;
  80 
  81     private Vector<String> resourceBundles;
  82 
  83     private Locale defaultLocale = Locale.getDefault();
  84 
  85     /**
  86      * Maps from a Locale to a cached Map of the ResourceBundle. This is done
  87      * so as to avoid an exception being thrown when a value is asked for.
  88      * Access to this should be done while holding a lock on the
  89      * UIDefaults, eg synchronized(this).
  90      */
  91     private Map<Locale, Map<String, Object>> resourceCache;
  92 
  93     /**
  94      * Creates an empty defaults table.
  95      */
  96     public UIDefaults() {
  97         this(700, .75f);
  98     }
  99 
 100     /**
 101      * Creates an empty defaults table with the specified initial capacity and
 102      * load factor.
 103      *
 104      * @param initialCapacity   the initial capacity of the defaults table
 105      * @param loadFactor        the load factor of the defaults table
 106      * @see java.util.Hashtable
 107      * @since 1.6
 108      */
 109     public UIDefaults(int initialCapacity, float loadFactor) {
 110         super(initialCapacity, loadFactor);
 111         resourceCache = new HashMap<Locale, Map<String, Object>>();
 112     }
 113 
 114 
 115     /**
 116      * Creates a defaults table initialized with the specified
 117      * key/value pairs.  For example:
 118      * <pre>
 119         Object[] uiDefaults = {
 120              "Font", new Font("Dialog", Font.BOLD, 12),
 121             "Color", Color.red,
 122              "five", new Integer(5)
 123         }
 124         UIDefaults myDefaults = new UIDefaults(uiDefaults);
 125      * </pre>
 126      * @param keyValueList  an array of objects containing the key/value
 127      *          pairs
 128      */
 129     public UIDefaults(Object[] keyValueList) {
 130         super(keyValueList.length / 2);
 131         for(int i = 0; i < keyValueList.length; i += 2) {
 132             super.put(keyValueList[i], keyValueList[i + 1]);
 133         }
 134     }
 135 
 136     /**
 137      * Returns the value for key.  If the value is a
 138      * <code>UIDefaults.LazyValue</code> then the real
 139      * value is computed with <code>LazyValue.createValue()</code>,
 140      * the table entry is replaced, and the real value is returned.
 141      * If the value is an <code>UIDefaults.ActiveValue</code>
 142      * the table entry is not replaced - the value is computed
 143      * with <code>ActiveValue.createValue()</code> for each
 144      * <code>get()</code> call.
 145      *
 146      * If the key is not found in the table then it is searched for in the list
 147      * of resource bundles maintained by this object.  The resource bundles are
 148      * searched most recently added first using the locale returned by
 149      * <code>getDefaultLocale</code>.  <code>LazyValues</code> and
 150      * <code>ActiveValues</code> are not supported in the resource bundles.
 151 
 152      *
 153      * @param key the desired key
 154      * @return the value for <code>key</code>
 155      * @see LazyValue
 156      * @see ActiveValue
 157      * @see java.util.Hashtable#get
 158      * @see #getDefaultLocale
 159      * @see #addResourceBundle
 160      * @since 1.4
 161      */
 162     public Object get(Object key) {
 163         Object value = getFromHashtable( key );
 164         return (value != null) ? value : getFromResourceBundle(key, null);
 165     }
 166 
 167     /**
 168      * Looks up the given key in our Hashtable and resolves LazyValues
 169      * or ActiveValues.
 170      */
 171     private Object getFromHashtable(final Object key) {
 172         /* Quickly handle the common case, without grabbing
 173          * a lock.
 174          */
 175         Object value = super.get(key);
 176         if ((value != PENDING) &&
 177             !(value instanceof ActiveValue) &&
 178             !(value instanceof LazyValue)) {
 179             return value;
 180         }
 181 
 182         /* If the LazyValue for key is being constructed by another
 183          * thread then wait and then return the new value, otherwise drop
 184          * the lock and construct the ActiveValue or the LazyValue.
 185          * We use the special value PENDING to mark LazyValues that
 186          * are being constructed.
 187          */
 188         synchronized(this) {
 189             value = super.get(key);
 190             if (value == PENDING) {
 191                 do {
 192                     try {
 193                         this.wait();
 194                     }
 195                     catch (InterruptedException e) {
 196                     }
 197                     value = super.get(key);
 198                 }
 199                 while(value == PENDING);
 200                 return value;
 201             }
 202             else if (value instanceof LazyValue) {
 203                 super.put(key, PENDING);
 204             }
 205             else if (!(value instanceof ActiveValue)) {
 206                 return value;
 207             }
 208         }
 209 
 210         /* At this point we know that the value of key was
 211          * a LazyValue or an ActiveValue.
 212          */
 213         if (value instanceof LazyValue) {
 214             try {
 215                 /* If an exception is thrown we'll just put the LazyValue
 216                  * back in the table.
 217                  */
 218                 value = ((LazyValue)value).createValue(this);
 219             }
 220             finally {
 221                 synchronized(this) {
 222                     if (value == null) {
 223                         super.remove(key);
 224                     }
 225                     else {
 226                         super.put(key, value);
 227                     }
 228                     this.notifyAll();
 229                 }
 230             }
 231         }
 232         else {
 233             value = ((ActiveValue)value).createValue(this);
 234         }
 235 
 236         return value;
 237     }
 238 
 239 
 240     /**
 241      * Returns the value for key associated with the given locale.
 242      * If the value is a <code>UIDefaults.LazyValue</code> then the real
 243      * value is computed with <code>LazyValue.createValue()</code>,
 244      * the table entry is replaced, and the real value is returned.
 245      * If the value is an <code>UIDefaults.ActiveValue</code>
 246      * the table entry is not replaced - the value is computed
 247      * with <code>ActiveValue.createValue()</code> for each
 248      * <code>get()</code> call.
 249      *
 250      * If the key is not found in the table then it is searched for in the list
 251      * of resource bundles maintained by this object.  The resource bundles are
 252      * searched most recently added first using the given locale.
 253      * <code>LazyValues</code> and <code>ActiveValues</code> are not supported
 254      * in the resource bundles.
 255      *
 256      * @param key the desired key
 257      * @param l the desired <code>locale</code>
 258      * @return the value for <code>key</code>
 259      * @see LazyValue
 260      * @see ActiveValue
 261      * @see java.util.Hashtable#get
 262      * @see #addResourceBundle
 263      * @since 1.4
 264      */
 265     public Object get(Object key, Locale l) {
 266         Object value = getFromHashtable( key );
 267         return (value != null) ? value : getFromResourceBundle(key, l);
 268     }
 269 
 270     /**
 271      * Looks up given key in our resource bundles.
 272      */
 273     private Object getFromResourceBundle(Object key, Locale l) {
 274 
 275         if( resourceBundles == null ||
 276             resourceBundles.isEmpty() ||
 277             !(key instanceof String) ) {
 278             return null;
 279         }
 280 
 281         // A null locale means use the default locale.
 282         if( l == null ) {
 283             if( defaultLocale == null )
 284                 return null;
 285             else
 286                 l = defaultLocale;
 287         }
 288 
 289         synchronized(this) {
 290             return getResourceCache(l).get(key);
 291         }
 292     }
 293 
 294     /**
 295      * Returns a Map of the known resources for the given locale.
 296      */
 297     private Map<String, Object> getResourceCache(Locale l) {
 298         Map<String, Object> values = resourceCache.get(l);
 299 
 300         if (values == null) {
 301             values = new TextAndMnemonicHashMap();
 302             for (int i=resourceBundles.size()-1; i >= 0; i--) {
 303                 String bundleName = resourceBundles.get(i);
 304                 try {
 305                     Control c = CoreResourceBundleControl.getRBControlInstance(bundleName);
 306                     ResourceBundle b;
 307                     if (c != null) {
 308                         b = ResourceBundle.getBundle(bundleName, l, c);
 309                     } else {
 310                         b = ResourceBundle.getBundle(bundleName, l);
 311                     }
 312                     Enumeration<String> keys = b.getKeys();
 313 
 314                     while (keys.hasMoreElements()) {
 315                         String key = keys.nextElement();
 316 
 317                         if (values.get(key) == null) {
 318                             Object value = b.getObject(key);
 319 
 320                             values.put(key, value);
 321                         }
 322                     }
 323                 } catch( MissingResourceException mre ) {
 324                     // Keep looking
 325                 }
 326             }
 327             resourceCache.put(l, values);
 328         }
 329         return values;
 330     }
 331 
 332     /**
 333      * Sets the value of <code>key</code> to <code>value</code> for all locales.
 334      * If <code>key</code> is a string and the new value isn't
 335      * equal to the old one, fire a <code>PropertyChangeEvent</code>.
 336      * If value is <code>null</code>, the key is removed from the table.
 337      *
 338      * @param key    the unique <code>Object</code> who's value will be used
 339      *          to retrieve the data value associated with it
 340      * @param value  the new <code>Object</code> to store as data under
 341      *          that key
 342      * @return the previous <code>Object</code> value, or <code>null</code>
 343      * @see #putDefaults
 344      * @see java.util.Hashtable#put
 345      */
 346     public Object put(Object key, Object value) {
 347         Object oldValue = (value == null) ? super.remove(key) : super.put(key, value);
 348         if (key instanceof String) {
 349             firePropertyChange((String)key, oldValue, value);
 350         }
 351         return oldValue;
 352     }
 353 
 354 
 355     /**
 356      * Puts all of the key/value pairs in the database and
 357      * unconditionally generates one <code>PropertyChangeEvent</code>.
 358      * The events oldValue and newValue will be <code>null</code> and its
 359      * <code>propertyName</code> will be "UIDefaults".  The key/value pairs are
 360      * added for all locales.
 361      *
 362      * @param keyValueList  an array of key/value pairs
 363      * @see #put
 364      * @see java.util.Hashtable#put
 365      */
 366     public void putDefaults(Object[] keyValueList) {
 367         for(int i = 0, max = keyValueList.length; i < max; i += 2) {
 368             Object value = keyValueList[i + 1];
 369             if (value == null) {
 370                 super.remove(keyValueList[i]);
 371             }
 372             else {
 373                 super.put(keyValueList[i], value);
 374             }
 375         }
 376         firePropertyChange("UIDefaults", null, null);
 377     }
 378 
 379 
 380     /**
 381      * If the value of <code>key</code> is a <code>Font</code> return it,
 382      * otherwise return <code>null</code>.
 383      * @param key the desired key
 384      * @return if the value for <code>key</code> is a <code>Font</code>,
 385      *          return the <code>Font</code> object; otherwise return
 386      *          <code>null</code>
 387      */
 388     public Font getFont(Object key) {
 389         Object value = get(key);
 390         return (value instanceof Font) ? (Font)value : null;
 391     }
 392 
 393 
 394     /**
 395      * If the value of <code>key</code> for the given <code>Locale</code>
 396      * is a <code>Font</code> return it, otherwise return <code>null</code>.
 397      * @param key the desired key
 398      * @param l the desired locale
 399      * @return if the value for <code>key</code> and <code>Locale</code>
 400      *          is a <code>Font</code>,
 401      *          return the <code>Font</code> object; otherwise return
 402      *          <code>null</code>
 403      * @since 1.4
 404      */
 405     public Font getFont(Object key, Locale l) {
 406         Object value = get(key,l);
 407         return (value instanceof Font) ? (Font)value : null;
 408     }
 409 
 410     /**
 411      * If the value of <code>key</code> is a <code>Color</code> return it,
 412      * otherwise return <code>null</code>.
 413      * @param key the desired key
 414      * @return if the value for <code>key</code> is a <code>Color</code>,
 415      *          return the <code>Color</code> object; otherwise return
 416      *          <code>null</code>
 417      */
 418     public Color getColor(Object key) {
 419         Object value = get(key);
 420         return (value instanceof Color) ? (Color)value : null;
 421     }
 422 
 423 
 424     /**
 425      * If the value of <code>key</code> for the given <code>Locale</code>
 426      * is a <code>Color</code> return it, otherwise return <code>null</code>.
 427      * @param key the desired key
 428      * @param l the desired locale
 429      * @return if the value for <code>key</code> and <code>Locale</code>
 430      *          is a <code>Color</code>,
 431      *          return the <code>Color</code> object; otherwise return
 432      *          <code>null</code>
 433      * @since 1.4
 434      */
 435     public Color getColor(Object key, Locale l) {
 436         Object value = get(key,l);
 437         return (value instanceof Color) ? (Color)value : null;
 438     }
 439 
 440 
 441     /**
 442      * If the value of <code>key</code> is an <code>Icon</code> return it,
 443      * otherwise return <code>null</code>.
 444      * @param key the desired key
 445      * @return if the value for <code>key</code> is an <code>Icon</code>,
 446      *          return the <code>Icon</code> object; otherwise return
 447      *          <code>null</code>
 448      */
 449     public Icon getIcon(Object key) {
 450         Object value = get(key);
 451         return (value instanceof Icon) ? (Icon)value : null;
 452     }
 453 
 454 
 455     /**
 456      * If the value of <code>key</code> for the given <code>Locale</code>
 457      * is an <code>Icon</code> return it, otherwise return <code>null</code>.
 458      * @param key the desired key
 459      * @param l the desired locale
 460      * @return if the value for <code>key</code> and <code>Locale</code>
 461      *          is an <code>Icon</code>,
 462      *          return the <code>Icon</code> object; otherwise return
 463      *          <code>null</code>
 464      * @since 1.4
 465      */
 466     public Icon getIcon(Object key, Locale l) {
 467         Object value = get(key,l);
 468         return (value instanceof Icon) ? (Icon)value : null;
 469     }
 470 
 471 
 472     /**
 473      * If the value of <code>key</code> is a <code>Border</code> return it,
 474      * otherwise return <code>null</code>.
 475      * @param key the desired key
 476      * @return if the value for <code>key</code> is a <code>Border</code>,
 477      *          return the <code>Border</code> object; otherwise return
 478      *          <code>null</code>
 479      */
 480     public Border getBorder(Object key) {
 481         Object value = get(key);
 482         return (value instanceof Border) ? (Border)value : null;
 483     }
 484 
 485 
 486     /**
 487      * If the value of <code>key</code> for the given <code>Locale</code>
 488      * is a <code>Border</code> return it, otherwise return <code>null</code>.
 489      * @param key the desired key
 490      * @param l the desired locale
 491      * @return if the value for <code>key</code> and <code>Locale</code>
 492      *          is a <code>Border</code>,
 493      *          return the <code>Border</code> object; otherwise return
 494      *          <code>null</code>
 495      * @since 1.4
 496      */
 497     public Border getBorder(Object key, Locale l)  {
 498         Object value = get(key,l);
 499         return (value instanceof Border) ? (Border)value : null;
 500     }
 501 
 502 
 503     /**
 504      * If the value of <code>key</code> is a <code>String</code> return it,
 505      * otherwise return <code>null</code>.
 506      * @param key the desired key
 507      * @return if the value for <code>key</code> is a <code>String</code>,
 508      *          return the <code>String</code> object; otherwise return
 509      *          <code>null</code>
 510      */
 511     public String getString(Object key) {
 512         Object value = get(key);
 513         return (value instanceof String) ? (String)value : null;
 514     }
 515 
 516     /**
 517      * If the value of <code>key</code> for the given <code>Locale</code>
 518      * is a <code>String</code> return it, otherwise return <code>null</code>.
 519      * @param key the desired key
 520      * @param l the desired <code>Locale</code>
 521      * @return if the value for <code>key</code> for the given
 522      *          <code>Locale</code> is a <code>String</code>,
 523      *          return the <code>String</code> object; otherwise return
 524      *          <code>null</code>
 525      * @since 1.4
 526      */
 527     public String getString(Object key, Locale l) {
 528         Object value = get(key,l);
 529         return (value instanceof String) ? (String)value : null;
 530     }
 531 
 532     /**
 533      * If the value of <code>key</code> is an <code>Integer</code> return its
 534      * integer value, otherwise return 0.
 535      * @param key the desired key
 536      * @return if the value for <code>key</code> is an <code>Integer</code>,
 537      *          return its value, otherwise return 0
 538      */
 539     public int getInt(Object key) {
 540         Object value = get(key);
 541         return (value instanceof Integer) ? ((Integer)value).intValue() : 0;
 542     }
 543 
 544 
 545     /**
 546      * If the value of <code>key</code> for the given <code>Locale</code>
 547      * is an <code>Integer</code> return its integer value, otherwise return 0.
 548      * @param key the desired key
 549      * @param l the desired locale
 550      * @return if the value for <code>key</code> and <code>Locale</code>
 551      *          is an <code>Integer</code>,
 552      *          return its value, otherwise return 0
 553      * @since 1.4
 554      */
 555     public int getInt(Object key, Locale l) {
 556         Object value = get(key,l);
 557         return (value instanceof Integer) ? ((Integer)value).intValue() : 0;
 558     }
 559 
 560 
 561     /**
 562      * If the value of <code>key</code> is boolean, return the
 563      * boolean value, otherwise return false.
 564      *
 565      * @param key an <code>Object</code> specifying the key for the desired boolean value
 566      * @return if the value of <code>key</code> is boolean, return the
 567      *         boolean value, otherwise return false.
 568      * @since 1.4
 569      */
 570     public boolean getBoolean(Object key) {
 571         Object value = get(key);
 572         return (value instanceof Boolean) ? ((Boolean)value).booleanValue() : false;
 573     }
 574 
 575 
 576     /**
 577      * If the value of <code>key</code> for the given <code>Locale</code>
 578      * is boolean, return the boolean value, otherwise return false.
 579      *
 580      * @param key an <code>Object</code> specifying the key for the desired boolean value
 581      * @param l the desired locale
 582      * @return if the value for <code>key</code> and <code>Locale</code>
 583      *         is boolean, return the
 584      *         boolean value, otherwise return false.
 585      * @since 1.4
 586      */
 587     public boolean getBoolean(Object key, Locale l) {
 588         Object value = get(key,l);
 589         return (value instanceof Boolean) ? ((Boolean)value).booleanValue() : false;
 590     }
 591 
 592 
 593     /**
 594      * If the value of <code>key</code> is an <code>Insets</code> return it,
 595      * otherwise return <code>null</code>.
 596      * @param key the desired key
 597      * @return if the value for <code>key</code> is an <code>Insets</code>,
 598      *          return the <code>Insets</code> object; otherwise return
 599      *          <code>null</code>
 600      */
 601     public Insets getInsets(Object key) {
 602         Object value = get(key);
 603         return (value instanceof Insets) ? (Insets)value : null;
 604     }
 605 
 606 
 607     /**
 608      * If the value of <code>key</code> for the given <code>Locale</code>
 609      * is an <code>Insets</code> return it, otherwise return <code>null</code>.
 610      * @param key the desired key
 611      * @param l the desired locale
 612      * @return if the value for <code>key</code> and <code>Locale</code>
 613      *          is an <code>Insets</code>,
 614      *          return the <code>Insets</code> object; otherwise return
 615      *          <code>null</code>
 616      * @since 1.4
 617      */
 618     public Insets getInsets(Object key, Locale l) {
 619         Object value = get(key,l);
 620         return (value instanceof Insets) ? (Insets)value : null;
 621     }
 622 
 623 
 624     /**
 625      * If the value of <code>key</code> is a <code>Dimension</code> return it,
 626      * otherwise return <code>null</code>.
 627      * @param key the desired key
 628      * @return if the value for <code>key</code> is a <code>Dimension</code>,
 629      *          return the <code>Dimension</code> object; otherwise return
 630      *          <code>null</code>
 631      */
 632     public Dimension getDimension(Object key) {
 633         Object value = get(key);
 634         return (value instanceof Dimension) ? (Dimension)value : null;
 635     }
 636 
 637 
 638     /**
 639      * If the value of <code>key</code> for the given <code>Locale</code>
 640      * is a <code>Dimension</code> return it, otherwise return <code>null</code>.
 641      * @param key the desired key
 642      * @param l the desired locale
 643      * @return if the value for <code>key</code> and <code>Locale</code>
 644      *          is a <code>Dimension</code>,
 645      *          return the <code>Dimension</code> object; otherwise return
 646      *          <code>null</code>
 647      * @since 1.4
 648      */
 649     public Dimension getDimension(Object key, Locale l) {
 650         Object value = get(key,l);
 651         return (value instanceof Dimension) ? (Dimension)value : null;
 652     }
 653 
 654 
 655     /**
 656      * The value of <code>get(uidClassID)</code> must be the
 657      * <code>String</code> name of a
 658      * class that implements the corresponding <code>ComponentUI</code>
 659      * class.  If the class hasn't been loaded before, this method looks
 660      * up the class with <code>uiClassLoader.loadClass()</code> if a non
 661      * <code>null</code>
 662      * class loader is provided, <code>classForName()</code> otherwise.
 663      * <p>
 664      * If a mapping for <code>uiClassID</code> exists or if the specified
 665      * class can't be found, return <code>null</code>.
 666      * <p>
 667      * This method is used by <code>getUI</code>, it's usually
 668      * not necessary to call it directly.
 669      *
 670      * @param uiClassID  a string containing the class ID
 671      * @param uiClassLoader the object which will load the class
 672      * @return the value of <code>Class.forName(get(uidClassID))</code>
 673      * @see #getUI
 674      */
 675     public Class<? extends ComponentUI>
 676         getUIClass(String uiClassID, ClassLoader uiClassLoader)
 677     {
 678         try {
 679             String className = (String)get(uiClassID);
 680             if (className != null) {
 681                 ReflectUtil.checkPackageAccess(className);
 682 
 683                 Class<?> cls = (Class)get(className);
 684                 if (cls == null) {
 685                     if (uiClassLoader == null) {
 686                         cls = SwingUtilities.loadSystemClass(className);
 687                     }
 688                     else {
 689                         cls = uiClassLoader.loadClass(className);
 690                     }
 691                     if (cls != null) {
 692                         // Save lookup for future use, as forName is slow.
 693                         put(className, cls);
 694                     }
 695                 }
 696                 @SuppressWarnings("unchecked")
 697                 Class<? extends ComponentUI> tmp = (Class<? extends ComponentUI>)cls;
 698                 return tmp;
 699             }
 700         }
 701         catch (ClassNotFoundException | ClassCastException e) {
 702             return null;
 703         }
 704         return null;
 705     }
 706 
 707 
 708     /**
 709      * Returns the L&amp;F class that renders this component.
 710      *
 711      * @param uiClassID a string containing the class ID
 712      * @return the Class object returned by
 713      *          <code>getUIClass(uiClassID, null)</code>
 714      */
 715     public Class<? extends ComponentUI> getUIClass(String uiClassID) {
 716         return getUIClass(uiClassID, null);
 717     }
 718 
 719 
 720     /**
 721      * If <code>getUI()</code> fails for any reason,
 722      * it calls this method before returning <code>null</code>.
 723      * Subclasses may choose to do more or less here.
 724      *
 725      * @param msg message string to print
 726      * @see #getUI
 727      */
 728     protected void getUIError(String msg) {
 729         System.err.println("UIDefaults.getUI() failed: " + msg);
 730         try {
 731             throw new Error();
 732         }
 733         catch (Throwable e) {
 734             e.printStackTrace();
 735         }
 736     }
 737 
 738     /**
 739      * Creates an <code>ComponentUI</code> implementation for the
 740      * specified component.  In other words create the look
 741      * and feel specific delegate object for <code>target</code>.
 742      * This is done in two steps:
 743      * <ul>
 744      * <li> Look up the name of the <code>ComponentUI</code> implementation
 745      * class under the value returned by <code>target.getUIClassID()</code>.
 746      * <li> Use the implementation classes static <code>createUI()</code>
 747      * method to construct a look and feel delegate.
 748      * </ul>
 749      * @param target  the <code>JComponent</code> which needs a UI
 750      * @return the <code>ComponentUI</code> object
 751      */
 752     public ComponentUI getUI(JComponent target) {
 753 
 754         Object cl = get("ClassLoader");
 755         ClassLoader uiClassLoader =
 756             (cl != null) ? (ClassLoader)cl : target.getClass().getClassLoader();
 757         Class<? extends ComponentUI> uiClass = getUIClass(target.getUIClassID(), uiClassLoader);
 758         Object uiObject = null;
 759 
 760         if (uiClass == null) {
 761             getUIError("no ComponentUI class for: " + target);
 762         }
 763         else {
 764             try {
 765                 Method m = (Method)get(uiClass);
 766                 if (m == null) {
 767                     m = uiClass.getMethod("createUI", new Class<?>[]{JComponent.class});
 768                     put(uiClass, m);
 769                 }
 770                 uiObject = MethodUtil.invoke(m, null, new Object[]{target});
 771             }
 772             catch (NoSuchMethodException e) {
 773                 getUIError("static createUI() method not found in " + uiClass);
 774             }
 775             catch (Exception e) {
 776                 getUIError("createUI() failed for " + target + " " + e);
 777             }
 778         }
 779 
 780         return (ComponentUI)uiObject;
 781     }
 782 
 783     /**
 784      * Adds a <code>PropertyChangeListener</code> to the listener list.
 785      * The listener is registered for all properties.
 786      * <p>
 787      * A <code>PropertyChangeEvent</code> will get fired whenever a default
 788      * is changed.
 789      *
 790      * @param listener  the <code>PropertyChangeListener</code> to be added
 791      * @see java.beans.PropertyChangeSupport
 792      */
 793     public synchronized void addPropertyChangeListener(PropertyChangeListener listener) {
 794         if (changeSupport == null) {
 795             changeSupport = new SwingPropertyChangeSupport(this);
 796         }
 797         changeSupport.addPropertyChangeListener(listener);
 798     }
 799 
 800 
 801     /**
 802      * Removes a <code>PropertyChangeListener</code> from the listener list.
 803      * This removes a <code>PropertyChangeListener</code> that was registered
 804      * for all properties.
 805      *
 806      * @param listener  the <code>PropertyChangeListener</code> to be removed
 807      * @see java.beans.PropertyChangeSupport
 808      */
 809     public synchronized void removePropertyChangeListener(PropertyChangeListener listener) {
 810         if (changeSupport != null) {
 811             changeSupport.removePropertyChangeListener(listener);
 812         }
 813     }
 814 
 815 
 816     /**
 817      * Returns an array of all the <code>PropertyChangeListener</code>s added
 818      * to this UIDefaults with addPropertyChangeListener().
 819      *
 820      * @return all of the <code>PropertyChangeListener</code>s added or an empty
 821      *         array if no listeners have been added
 822      * @since 1.4
 823      */
 824     public synchronized PropertyChangeListener[] getPropertyChangeListeners() {
 825         if (changeSupport == null) {
 826             return new PropertyChangeListener[0];
 827         }
 828         return changeSupport.getPropertyChangeListeners();
 829     }
 830 
 831 
 832     /**
 833      * Support for reporting bound property changes.  If oldValue and
 834      * newValue are not equal and the <code>PropertyChangeEvent</code>x
 835      * listener list isn't empty, then fire a
 836      * <code>PropertyChange</code> event to each listener.
 837      *
 838      * @param propertyName  the programmatic name of the property
 839      *          that was changed
 840      * @param oldValue  the old value of the property
 841      * @param newValue  the new value of the property
 842      * @see java.beans.PropertyChangeSupport
 843      */
 844     protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
 845         if (changeSupport != null) {
 846             changeSupport.firePropertyChange(propertyName, oldValue, newValue);
 847         }
 848     }
 849 
 850 
 851     /**
 852      * Adds a resource bundle to the list of resource bundles that are
 853      * searched for localized values.  Resource bundles are searched in the
 854      * reverse order they were added.  In other words, the most recently added
 855      * bundle is searched first.
 856      *
 857      * @param bundleName  the base name of the resource bundle to be added
 858      * @see java.util.ResourceBundle
 859      * @see #removeResourceBundle
 860      * @since 1.4
 861      */
 862     public synchronized void addResourceBundle( String bundleName ) {
 863         if( bundleName == null ) {
 864             return;
 865         }
 866         if( resourceBundles == null ) {
 867             resourceBundles = new Vector<String>(5);
 868         }
 869         if (!resourceBundles.contains(bundleName)) {
 870             resourceBundles.add( bundleName );
 871             resourceCache.clear();
 872         }
 873     }
 874 
 875 
 876     /**
 877      * Removes a resource bundle from the list of resource bundles that are
 878      * searched for localized defaults.
 879      *
 880      * @param bundleName  the base name of the resource bundle to be removed
 881      * @see java.util.ResourceBundle
 882      * @see #addResourceBundle
 883      * @since 1.4
 884      */
 885     public synchronized void removeResourceBundle( String bundleName ) {
 886         if( resourceBundles != null ) {
 887             resourceBundles.remove( bundleName );
 888         }
 889         resourceCache.clear();
 890     }
 891 
 892     /**
 893      * Sets the default locale.  The default locale is used in retrieving
 894      * localized values via <code>get</code> methods that do not take a
 895      * locale argument.  As of release 1.4, Swing UI objects should retrieve
 896      * localized values using the locale of their component rather than the
 897      * default locale.  The default locale exists to provide compatibility with
 898      * pre 1.4 behaviour.
 899      *
 900      * @param l the new default locale
 901      * @see #getDefaultLocale
 902      * @see #get(Object)
 903      * @see #get(Object,Locale)
 904      * @since 1.4
 905      */
 906     public void setDefaultLocale( Locale l ) {
 907         defaultLocale = l;
 908     }
 909 
 910     /**
 911      * Returns the default locale.  The default locale is used in retrieving
 912      * localized values via <code>get</code> methods that do not take a
 913      * locale argument.  As of release 1.4, Swing UI objects should retrieve
 914      * localized values using the locale of their component rather than the
 915      * default locale.  The default locale exists to provide compatibility with
 916      * pre 1.4 behaviour.
 917      *
 918      * @return the default locale
 919      * @see #setDefaultLocale
 920      * @see #get(Object)
 921      * @see #get(Object,Locale)
 922      * @since 1.4
 923      */
 924     public Locale getDefaultLocale() {
 925         return defaultLocale;
 926     }
 927 
 928     /**
 929      * This class enables one to store an entry in the defaults
 930      * table that isn't constructed until the first time it's
 931      * looked up with one of the <code>getXXX(key)</code> methods.
 932      * Lazy values are useful for defaults that are expensive
 933      * to construct or are seldom retrieved.  The first time
 934      * a <code>LazyValue</code> is retrieved its "real value" is computed
 935      * by calling <code>LazyValue.createValue()</code> and the real
 936      * value is used to replace the <code>LazyValue</code> in the
 937      * <code>UIDefaults</code>
 938      * table.  Subsequent lookups for the same key return
 939      * the real value.  Here's an example of a <code>LazyValue</code>
 940      * that constructs a <code>Border</code>:
 941      * <pre>
 942      *  Object borderLazyValue = new UIDefaults.LazyValue() {
 943      *      public Object createValue(UIDefaults table) {
 944      *          return new BorderFactory.createLoweredBevelBorder();
 945      *      }
 946      *  };
 947      *
 948      *  uiDefaultsTable.put("MyBorder", borderLazyValue);
 949      * </pre>
 950      *
 951      * @see UIDefaults#get
 952      */
 953     public interface LazyValue {
 954         /**
 955          * Creates the actual value retrieved from the <code>UIDefaults</code>
 956          * table. When an object that implements this interface is
 957          * retrieved from the table, this method is used to create
 958          * the real value, which is then stored in the table and
 959          * returned to the calling method.
 960          *
 961          * @param table  a <code>UIDefaults</code> table
 962          * @return the created <code>Object</code>
 963          */
 964         Object createValue(UIDefaults table);
 965     }
 966 
 967 
 968     /**
 969      * This class enables one to store an entry in the defaults
 970      * table that's constructed each time it's looked up with one of
 971      * the <code>getXXX(key)</code> methods. Here's an example of
 972      * an <code>ActiveValue</code> that constructs a
 973      * <code>DefaultListCellRenderer</code>:
 974      * <pre>
 975      *  Object cellRendererActiveValue = new UIDefaults.ActiveValue() {
 976      *      public Object createValue(UIDefaults table) {
 977      *          return new DefaultListCellRenderer();
 978      *      }
 979      *  };
 980      *
 981      *  uiDefaultsTable.put("MyRenderer", cellRendererActiveValue);
 982      * </pre>
 983      *
 984      * @see UIDefaults#get
 985      */
 986     public interface ActiveValue {
 987         /**
 988          * Creates the value retrieved from the <code>UIDefaults</code> table.
 989          * The object is created each time it is accessed.
 990          *
 991          * @param table  a <code>UIDefaults</code> table
 992          * @return the created <code>Object</code>
 993          */
 994         Object createValue(UIDefaults table);
 995     }
 996 
 997     /**
 998      * This class provides an implementation of <code>LazyValue</code>
 999      * which can be
1000      * used to delay loading of the Class for the instance to be created.
1001      * It also avoids creation of an anonymous inner class for the
1002      * <code>LazyValue</code>
1003      * subclass.  Both of these improve performance at the time that a
1004      * a Look and Feel is loaded, at the cost of a slight performance
1005      * reduction the first time <code>createValue</code> is called
1006      * (since Reflection APIs are used).
1007      * @since 1.3
1008      */
1009     public static class ProxyLazyValue implements LazyValue {
1010         private AccessControlContext acc;
1011         private String className;
1012         private String methodName;
1013         private Object[] args;
1014 
1015         /**
1016          * Creates a <code>LazyValue</code> which will construct an instance
1017          * when asked.
1018          *
1019          * @param c    a <code>String</code> specifying the classname
1020          *             of the instance to be created on demand
1021          */
1022         public ProxyLazyValue(String c) {
1023             this(c, (String)null);
1024         }
1025         /**
1026          * Creates a <code>LazyValue</code> which will construct an instance
1027          * when asked.
1028          *
1029          * @param c    a <code>String</code> specifying the classname of
1030          *              the class
1031          *              containing a static method to be called for
1032          *              instance creation
1033          * @param m    a <code>String</code> specifying the static
1034          *              method to be called on class c
1035          */
1036         public ProxyLazyValue(String c, String m) {
1037             this(c, m, null);
1038         }
1039         /**
1040          * Creates a <code>LazyValue</code> which will construct an instance
1041          * when asked.
1042          *
1043          * @param c    a <code>String</code> specifying the classname
1044          *              of the instance to be created on demand
1045          * @param o    an array of <code>Objects</code> to be passed as
1046          *              paramaters to the constructor in class c
1047          */
1048         public ProxyLazyValue(String c, Object[] o) {
1049             this(c, null, o);
1050         }
1051         /**
1052          * Creates a <code>LazyValue</code> which will construct an instance
1053          * when asked.
1054          *
1055          * @param c    a <code>String</code> specifying the classname
1056          *              of the class
1057          *              containing a static method to be called for
1058          *              instance creation.
1059          * @param m    a <code>String</code> specifying the static method
1060          *              to be called on class c
1061          * @param o    an array of <code>Objects</code> to be passed as
1062          *              paramaters to the static method in class c
1063          */
1064         public ProxyLazyValue(String c, String m, Object[] o) {
1065             acc = AccessController.getContext();
1066             className = c;
1067             methodName = m;
1068             if (o != null) {
1069                 args = o.clone();
1070             }
1071         }
1072 
1073         /**
1074          * Creates the value retrieved from the <code>UIDefaults</code> table.
1075          * The object is created each time it is accessed.
1076          *
1077          * @param table  a <code>UIDefaults</code> table
1078          * @return the created <code>Object</code>
1079          */
1080         public Object createValue(final UIDefaults table) {
1081             // In order to pick up the security policy in effect at the
1082             // time of creation we use a doPrivileged with the
1083             // AccessControlContext that was in place when this was created.
1084             if (acc == null && System.getSecurityManager() != null) {
1085                 throw new SecurityException("null AccessControlContext");
1086             }
1087             return AccessController.doPrivileged(new PrivilegedAction<Object>() {
1088                 public Object run() {
1089                     try {
1090                         Class<?> c;
1091                         Object cl;
1092                         // See if we should use a separate ClassLoader
1093                         if (table == null || !((cl = table.get("ClassLoader"))
1094                                                instanceof ClassLoader)) {
1095                             cl = Thread.currentThread().
1096                                         getContextClassLoader();
1097                             if (cl == null) {
1098                                 // Fallback to the system class loader.
1099                                 cl = ClassLoader.getSystemClassLoader();
1100                             }
1101                         }
1102                         ReflectUtil.checkPackageAccess(className);
1103                         c = Class.forName(className, true, (ClassLoader)cl);
1104                         SwingUtilities2.checkAccess(c.getModifiers());
1105                         if (methodName != null) {
1106                             Class<?>[] types = getClassArray(args);
1107                             Method m = c.getMethod(methodName, types);
1108                             return MethodUtil.invoke(m, c, args);
1109                         } else {
1110                             Class<?>[] types = getClassArray(args);
1111                             Constructor<?> constructor = c.getConstructor(types);
1112                             SwingUtilities2.checkAccess(constructor.getModifiers());
1113                             return constructor.newInstance(args);
1114                         }
1115                     } catch(Exception e) {
1116                         // Ideally we would throw an exception, unfortunately
1117                         // often times there are errors as an initial look and
1118                         // feel is loaded before one can be switched. Perhaps a
1119                         // flag should be added for debugging, so that if true
1120                         // the exception would be thrown.
1121                     }
1122                     return null;
1123                 }
1124             }, acc);
1125         }
1126 
1127         /*
1128          * Coerce the array of class types provided into one which
1129          * looks the way the Reflection APIs expect.  This is done
1130          * by substituting primitive types for their Object counterparts,
1131          * and superclasses for subclasses used to add the
1132          * <code>UIResource</code> tag.
1133          */
1134         private Class<?>[] getClassArray(Object[] args) {
1135             Class<?>[] types = null;
1136             if (args!=null) {
1137                 types = new Class<?>[args.length];
1138                 for (int i = 0; i< args.length; i++) {
1139                     /* PENDING(ges): At present only the primitive types
1140                        used are handled correctly; this should eventually
1141                        handle all primitive types */
1142                     if (args[i] instanceof java.lang.Integer) {
1143                         types[i]=Integer.TYPE;
1144                     } else if (args[i] instanceof java.lang.Boolean) {
1145                         types[i]=Boolean.TYPE;
1146                     } else if (args[i] instanceof javax.swing.plaf.ColorUIResource) {
1147                         /* PENDING(ges) Currently the Reflection APIs do not
1148                            search superclasses of parameters supplied for
1149                            constructor/method lookup.  Since we only have
1150                            one case where this is needed, we substitute
1151                            directly instead of adding a massive amount
1152                            of mechanism for this.  Eventually this will
1153                            probably need to handle the general case as well.
1154                            */
1155                         types[i]=java.awt.Color.class;
1156                     } else {
1157                         types[i]=args[i].getClass();
1158                     }
1159                 }
1160             }
1161             return types;
1162         }
1163 
1164         private String printArgs(Object[] array) {
1165             String s = "{";
1166             if (array !=null) {
1167                 for (int i = 0 ; i < array.length-1; i++) {
1168                     s = s.concat(array[i] + ",");
1169                 }
1170                 s = s.concat(array[array.length-1] + "}");
1171             } else {
1172                 s = s.concat("}");
1173             }
1174             return s;
1175         }
1176     }
1177 
1178 
1179     /**
1180      * <code>LazyInputMap</code> will create a <code>InputMap</code>
1181      * in its <code>createValue</code>
1182      * method. The bindings are passed in the constructor.
1183      * The bindings are an array with
1184      * the even number entries being string <code>KeyStrokes</code>
1185      * (eg "alt SPACE") and
1186      * the odd number entries being the value to use in the
1187      * <code>InputMap</code> (and the key in the <code>ActionMap</code>).
1188      * @since 1.3
1189      */
1190     public static class LazyInputMap implements LazyValue {
1191         /** Key bindings are registered under. */
1192         private Object[] bindings;
1193 
1194         /**
1195          * Constructs a {@code LazyInputMap}.
1196          * @param bindings the bindings
1197          */
1198         public LazyInputMap(Object[] bindings) {
1199             this.bindings = bindings;
1200         }
1201 
1202         /**
1203          * Creates an <code>InputMap</code> with the bindings that are
1204          * passed in.
1205          *
1206          * @param table a <code>UIDefaults</code> table
1207          * @return the <code>InputMap</code>
1208          */
1209         public Object createValue(UIDefaults table) {
1210             if (bindings != null) {
1211                 InputMap km = LookAndFeel.makeInputMap(bindings);
1212                 return km;
1213             }
1214             return null;
1215         }
1216     }
1217 
1218     /**
1219      * <code>TextAndMnemonicHashMap</code> stores swing resource strings. Many of strings
1220      * can have a mnemonic. For example:
1221      *   FileChooser.saveButton.textAndMnemonic=&Save
1222      * For this case method get returns "Save" for the key "FileChooser.saveButtonText" and
1223      * mnemonic "S" for the key "FileChooser.saveButtonMnemonic"
1224      *
1225      * There are several patterns for the text and mnemonic suffixes which are checked by the
1226      * <code>TextAndMnemonicHashMap</code> class.
1227      * Patterns which are converted to the xxx.textAndMnemonic key:
1228      * (xxxNameText, xxxNameMnemonic)
1229      * (xxxNameText, xxxMnemonic)
1230      * (xxx.nameText, xxx.mnemonic)
1231      * (xxxText, xxxMnemonic)
1232      *
1233      * These patterns can have a mnemonic index in format
1234      * (xxxDisplayedMnemonicIndex)
1235      *
1236      * Pattern which is converted to the xxx.titleAndMnemonic key:
1237      * (xxxTitle, xxxMnemonic)
1238      *
1239      */
1240     private static class TextAndMnemonicHashMap extends HashMap<String, Object> {
1241 
1242         static final String AND_MNEMONIC = "AndMnemonic";
1243         static final String TITLE_SUFFIX = ".titleAndMnemonic";
1244         static final String TEXT_SUFFIX = ".textAndMnemonic";
1245 
1246         @Override
1247         public Object get(Object key) {
1248 
1249             Object value = super.get(key);
1250 
1251             if (value == null) {
1252 
1253                 boolean checkTitle = false;
1254 
1255                 String stringKey = key.toString();
1256                 String compositeKey = null;
1257 
1258                 if (stringKey.endsWith(AND_MNEMONIC)) {
1259                     return null;
1260                 }
1261 
1262                 if (stringKey.endsWith(".mnemonic")) {
1263                     compositeKey = composeKey(stringKey, 9, TEXT_SUFFIX);
1264                 } else if (stringKey.endsWith("NameMnemonic")) {
1265                     compositeKey = composeKey(stringKey, 12, TEXT_SUFFIX);
1266                 } else if (stringKey.endsWith("Mnemonic")) {
1267                     compositeKey = composeKey(stringKey, 8, TEXT_SUFFIX);
1268                     checkTitle = true;
1269                 }
1270 
1271                 if (compositeKey != null) {
1272                     value = super.get(compositeKey);
1273                     if (value == null && checkTitle) {
1274                         compositeKey = composeKey(stringKey, 8, TITLE_SUFFIX);
1275                         value = super.get(compositeKey);
1276                     }
1277 
1278                     return value == null ? null : getMnemonicFromProperty(value.toString());
1279                 }
1280 
1281                 if (stringKey.endsWith("NameText")) {
1282                     compositeKey = composeKey(stringKey, 8, TEXT_SUFFIX);
1283                 } else if (stringKey.endsWith(".nameText")) {
1284                     compositeKey = composeKey(stringKey, 9, TEXT_SUFFIX);
1285                 } else if (stringKey.endsWith("Text")) {
1286                     compositeKey = composeKey(stringKey, 4, TEXT_SUFFIX);
1287                 } else if (stringKey.endsWith("Title")) {
1288                     compositeKey = composeKey(stringKey, 5, TITLE_SUFFIX);
1289                 }
1290 
1291                 if (compositeKey != null) {
1292                     value = super.get(compositeKey);
1293                     return value == null ? null : getTextFromProperty(value.toString());
1294                 }
1295 
1296                 if (stringKey.endsWith("DisplayedMnemonicIndex")) {
1297                     compositeKey = composeKey(stringKey, 22, TEXT_SUFFIX);
1298                     value = super.get(compositeKey);
1299                     if (value == null) {
1300                         compositeKey = composeKey(stringKey, 22, TITLE_SUFFIX);
1301                         value = super.get(compositeKey);
1302                     }
1303                     return value == null ? null : getIndexFromProperty(value.toString());
1304                 }
1305             }
1306 
1307             return value;
1308         }
1309 
1310         String composeKey(String key, int reduce, String sufix) {
1311             return key.substring(0, key.length() - reduce) + sufix;
1312         }
1313 
1314         String getTextFromProperty(String text) {
1315             return text.replace("&", "");
1316         }
1317 
1318         String getMnemonicFromProperty(String text) {
1319             int index = text.indexOf('&');
1320             if (0 <= index && index < text.length() - 1) {
1321                 char c = text.charAt(index + 1);
1322                 return Integer.toString((int) Character.toUpperCase(c));
1323             }
1324             return null;
1325         }
1326 
1327         String getIndexFromProperty(String text) {
1328             int index = text.indexOf('&');
1329             return (index == -1) ? null : Integer.toString(index);
1330         }
1331     }
1332 
1333 }