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