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