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