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<String> keys = b.getKeys();
 315 
 316                     while (keys.hasMoreElements()) {
 317                         String key = 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                 @SuppressWarnings("unchecked")
 699                 Class<? extends ComponentUI> tmp = (Class<? extends ComponentUI>)cls;
 700                 return tmp;
 701             }
 702         }
 703         catch (ClassNotFoundException | ClassCastException e) {
 704             return null;
 705         }
 706         return null;
 707     }
 708 
 709 
 710     /**
 711      * Returns the L&amp;F class that renders this component.
 712      *
 713      * @param uiClassID a string containing the class ID
 714      * @return the Class object returned by
 715      *          <code>getUIClass(uiClassID, null)</code>
 716      */
 717     public Class<? extends ComponentUI> getUIClass(String uiClassID) {
 718         return getUIClass(uiClassID, null);
 719     }
 720 
 721 
 722     /**
 723      * If <code>getUI()</code> fails for any reason,
 724      * it calls this method before returning <code>null</code>.
 725      * Subclasses may choose to do more or less here.
 726      *
 727      * @param msg message string to print
 728      * @see #getUI
 729      */
 730     protected void getUIError(String msg) {
 731         System.err.println("UIDefaults.getUI() failed: " + msg);
 732         try {
 733             throw new Error();
 734         }
 735         catch (Throwable e) {
 736             e.printStackTrace();
 737         }
 738     }
 739 
 740     /**
 741      * Creates an <code>ComponentUI</code> implementation for the
 742      * specified component.  In other words create the look
 743      * and feel specific delegate object for <code>target</code>.
 744      * This is done in two steps:
 745      * <ul>
 746      * <li> Look up the name of the <code>ComponentUI</code> implementation
 747      * class under the value returned by <code>target.getUIClassID()</code>.
 748      * <li> Use the implementation classes static <code>createUI()</code>
 749      * method to construct a look and feel delegate.
 750      * </ul>
 751      * @param target  the <code>JComponent</code> which needs a UI
 752      * @return the <code>ComponentUI</code> object
 753      */
 754     public ComponentUI getUI(JComponent target) {
 755 
 756         Object cl = get("ClassLoader");
 757         ClassLoader uiClassLoader =
 758             (cl != null) ? (ClassLoader)cl : target.getClass().getClassLoader();
 759         Class<? extends ComponentUI> uiClass = getUIClass(target.getUIClassID(), uiClassLoader);
 760         Object uiObject = null;
 761 
 762         if (uiClass == null) {
 763             getUIError("no ComponentUI class for: " + target);
 764         }
 765         else {
 766             try {
 767                 Method m = (Method)get(uiClass);
 768                 if (m == null) {
 769                     m = uiClass.getMethod("createUI", new Class<?>[]{JComponent.class});
 770                     put(uiClass, m);
 771                 }
 772                 uiObject = MethodUtil.invoke(m, null, new Object[]{target});
 773             }
 774             catch (NoSuchMethodException e) {
 775                 getUIError("static createUI() method not found in " + uiClass);
 776             }
 777             catch (Exception e) {
 778                 getUIError("createUI() failed for " + target + " " + e);
 779             }
 780         }
 781 
 782         return (ComponentUI)uiObject;
 783     }
 784 
 785     /**
 786      * Adds a <code>PropertyChangeListener</code> to the listener list.
 787      * The listener is registered for all properties.
 788      * <p>
 789      * A <code>PropertyChangeEvent</code> will get fired whenever a default
 790      * is changed.
 791      *
 792      * @param listener  the <code>PropertyChangeListener</code> to be added
 793      * @see java.beans.PropertyChangeSupport
 794      */
 795     public synchronized void addPropertyChangeListener(PropertyChangeListener listener) {
 796         if (changeSupport == null) {
 797             changeSupport = new SwingPropertyChangeSupport(this);
 798         }
 799         changeSupport.addPropertyChangeListener(listener);
 800     }
 801 
 802 
 803     /**
 804      * Removes a <code>PropertyChangeListener</code> from the listener list.
 805      * This removes a <code>PropertyChangeListener</code> that was registered
 806      * for all properties.
 807      *
 808      * @param listener  the <code>PropertyChangeListener</code> to be removed
 809      * @see java.beans.PropertyChangeSupport
 810      */
 811     public synchronized void removePropertyChangeListener(PropertyChangeListener listener) {
 812         if (changeSupport != null) {
 813             changeSupport.removePropertyChangeListener(listener);
 814         }
 815     }
 816 
 817 
 818     /**
 819      * Returns an array of all the <code>PropertyChangeListener</code>s added
 820      * to this UIDefaults with addPropertyChangeListener().
 821      *
 822      * @return all of the <code>PropertyChangeListener</code>s added or an empty
 823      *         array if no listeners have been added
 824      * @since 1.4
 825      */
 826     public synchronized PropertyChangeListener[] getPropertyChangeListeners() {
 827         if (changeSupport == null) {
 828             return new PropertyChangeListener[0];
 829         }
 830         return changeSupport.getPropertyChangeListeners();
 831     }
 832 
 833 
 834     /**
 835      * Support for reporting bound property changes.  If oldValue and
 836      * newValue are not equal and the <code>PropertyChangeEvent</code>x
 837      * listener list isn't empty, then fire a
 838      * <code>PropertyChange</code> event to each listener.
 839      *
 840      * @param propertyName  the programmatic name of the property
 841      *          that was changed
 842      * @param oldValue  the old value of the property
 843      * @param newValue  the new value of the property
 844      * @see java.beans.PropertyChangeSupport
 845      */
 846     protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
 847         if (changeSupport != null) {
 848             changeSupport.firePropertyChange(propertyName, oldValue, newValue);
 849         }
 850     }
 851 
 852 
 853     /**
 854      * Adds a resource bundle to the list of resource bundles that are
 855      * searched for localized values.  Resource bundles are searched in the
 856      * reverse order they were added.  In other words, the most recently added
 857      * bundle is searched first.
 858      *
 859      * @param bundleName  the base name of the resource bundle to be added
 860      * @see java.util.ResourceBundle
 861      * @see #removeResourceBundle
 862      * @since 1.4
 863      */
 864     public synchronized void addResourceBundle( String bundleName ) {
 865         if( bundleName == null ) {
 866             return;
 867         }
 868         if( resourceBundles == null ) {
 869             resourceBundles = new Vector<String>(5);
 870         }
 871         if (!resourceBundles.contains(bundleName)) {
 872             resourceBundles.add( bundleName );
 873             resourceCache.clear();
 874         }
 875     }
 876 
 877 
 878     /**
 879      * Removes a resource bundle from the list of resource bundles that are
 880      * searched for localized defaults.
 881      *
 882      * @param bundleName  the base name of the resource bundle to be removed
 883      * @see java.util.ResourceBundle
 884      * @see #addResourceBundle
 885      * @since 1.4
 886      */
 887     public synchronized void removeResourceBundle( String bundleName ) {
 888         if( resourceBundles != null ) {
 889             resourceBundles.remove( bundleName );
 890         }
 891         resourceCache.clear();
 892     }
 893 
 894     /**
 895      * Sets the default locale.  The default locale is used in retrieving
 896      * localized values via <code>get</code> methods that do not take a
 897      * locale argument.  As of release 1.4, Swing UI objects should retrieve
 898      * localized values using the locale of their component rather than the
 899      * default locale.  The default locale exists to provide compatibility with
 900      * pre 1.4 behaviour.
 901      *
 902      * @param l the new default locale
 903      * @see #getDefaultLocale
 904      * @see #get(Object)
 905      * @see #get(Object,Locale)
 906      * @since 1.4
 907      */
 908     public void setDefaultLocale( Locale l ) {
 909         defaultLocale = l;
 910     }
 911 
 912     /**
 913      * Returns the default locale.  The default locale is used in retrieving
 914      * localized values via <code>get</code> methods that do not take a
 915      * locale argument.  As of release 1.4, Swing UI objects should retrieve
 916      * localized values using the locale of their component rather than the
 917      * default locale.  The default locale exists to provide compatibility with
 918      * pre 1.4 behaviour.
 919      *
 920      * @return the default locale
 921      * @see #setDefaultLocale
 922      * @see #get(Object)
 923      * @see #get(Object,Locale)
 924      * @since 1.4
 925      */
 926     public Locale getDefaultLocale() {
 927         return defaultLocale;
 928     }
 929 
 930     /**
 931      * This class enables one to store an entry in the defaults
 932      * table that isn't constructed until the first time it's
 933      * looked up with one of the <code>getXXX(key)</code> methods.
 934      * Lazy values are useful for defaults that are expensive
 935      * to construct or are seldom retrieved.  The first time
 936      * a <code>LazyValue</code> is retrieved its "real value" is computed
 937      * by calling <code>LazyValue.createValue()</code> and the real
 938      * value is used to replace the <code>LazyValue</code> in the
 939      * <code>UIDefaults</code>
 940      * table.  Subsequent lookups for the same key return
 941      * the real value.  Here's an example of a <code>LazyValue</code>
 942      * that constructs a <code>Border</code>:
 943      * <pre>
 944      *  Object borderLazyValue = new UIDefaults.LazyValue() {
 945      *      public Object createValue(UIDefaults table) {
 946      *          return new BorderFactory.createLoweredBevelBorder();
 947      *      }
 948      *  };
 949      *
 950      *  uiDefaultsTable.put("MyBorder", borderLazyValue);
 951      * </pre>
 952      *
 953      * @see UIDefaults#get
 954      */
 955     public interface LazyValue {
 956         /**
 957          * Creates the actual value retrieved from the <code>UIDefaults</code>
 958          * table. When an object that implements this interface is
 959          * retrieved from the table, this method is used to create
 960          * the real value, which is then stored in the table and
 961          * returned to the calling method.
 962          *
 963          * @param table  a <code>UIDefaults</code> table
 964          * @return the created <code>Object</code>
 965          */
 966         Object createValue(UIDefaults table);
 967     }
 968 
 969 
 970     /**
 971      * This class enables one to store an entry in the defaults
 972      * table that's constructed each time it's looked up with one of
 973      * the <code>getXXX(key)</code> methods. Here's an example of
 974      * an <code>ActiveValue</code> that constructs a
 975      * <code>DefaultListCellRenderer</code>:
 976      * <pre>
 977      *  Object cellRendererActiveValue = new UIDefaults.ActiveValue() {
 978      *      public Object createValue(UIDefaults table) {
 979      *          return new DefaultListCellRenderer();
 980      *      }
 981      *  };
 982      *
 983      *  uiDefaultsTable.put("MyRenderer", cellRendererActiveValue);
 984      * </pre>
 985      *
 986      * @see UIDefaults#get
 987      */
 988     public interface ActiveValue {
 989         /**
 990          * Creates the value retrieved from the <code>UIDefaults</code> table.
 991          * The object is created each time it is accessed.
 992          *
 993          * @param table  a <code>UIDefaults</code> table
 994          * @return the created <code>Object</code>
 995          */
 996         Object createValue(UIDefaults table);
 997     }
 998 
 999     /**
1000      * This class provides an implementation of <code>LazyValue</code>
1001      * which can be
1002      * used to delay loading of the Class for the instance to be created.
1003      * It also avoids creation of an anonymous inner class for the
1004      * <code>LazyValue</code>
1005      * subclass.  Both of these improve performance at the time that a
1006      * a Look and Feel is loaded, at the cost of a slight performance
1007      * reduction the first time <code>createValue</code> is called
1008      * (since Reflection APIs are used).
1009      * @since 1.3
1010      */
1011     public static class ProxyLazyValue implements LazyValue {
1012         private AccessControlContext acc;
1013         private String className;
1014         private String methodName;
1015         private Object[] args;
1016 
1017         /**
1018          * Creates a <code>LazyValue</code> which will construct an instance
1019          * when asked.
1020          *
1021          * @param c    a <code>String</code> specifying the classname
1022          *             of the instance to be created on demand
1023          */
1024         public ProxyLazyValue(String c) {
1025             this(c, (String)null);
1026         }
1027         /**
1028          * Creates a <code>LazyValue</code> which will construct an instance
1029          * when asked.
1030          *
1031          * @param c    a <code>String</code> specifying the classname of
1032          *              the class
1033          *              containing a static method to be called for
1034          *              instance creation
1035          * @param m    a <code>String</code> specifying the static
1036          *              method to be called on class c
1037          */
1038         public ProxyLazyValue(String c, String m) {
1039             this(c, m, null);
1040         }
1041         /**
1042          * Creates a <code>LazyValue</code> which will construct an instance
1043          * when asked.
1044          *
1045          * @param c    a <code>String</code> specifying the classname
1046          *              of the instance to be created on demand
1047          * @param o    an array of <code>Objects</code> to be passed as
1048          *              paramaters to the constructor in class c
1049          */
1050         public ProxyLazyValue(String c, Object[] o) {
1051             this(c, null, o);
1052         }
1053         /**
1054          * Creates a <code>LazyValue</code> which will construct an instance
1055          * when asked.
1056          *
1057          * @param c    a <code>String</code> specifying the classname
1058          *              of the class
1059          *              containing a static method to be called for
1060          *              instance creation.
1061          * @param m    a <code>String</code> specifying the static method
1062          *              to be called on class c
1063          * @param o    an array of <code>Objects</code> to be passed as
1064          *              paramaters to the static method in class c
1065          */
1066         public ProxyLazyValue(String c, String m, Object[] o) {
1067             acc = AccessController.getContext();
1068             className = c;
1069             methodName = m;
1070             if (o != null) {
1071                 args = o.clone();
1072             }
1073         }
1074 
1075         /**
1076          * Creates the value retrieved from the <code>UIDefaults</code> table.
1077          * The object is created each time it is accessed.
1078          *
1079          * @param table  a <code>UIDefaults</code> table
1080          * @return the created <code>Object</code>
1081          */
1082         public Object createValue(final UIDefaults table) {
1083             // In order to pick up the security policy in effect at the
1084             // time of creation we use a doPrivileged with the
1085             // AccessControlContext that was in place when this was created.
1086             if (acc == null && System.getSecurityManager() != null) {
1087                 throw new SecurityException("null AccessControlContext");
1088             }
1089             return AccessController.doPrivileged(new PrivilegedAction<Object>() {
1090                 public Object run() {
1091                     try {
1092                         Class<?> c;
1093                         Object cl;
1094                         // See if we should use a separate ClassLoader
1095                         if (table == null || !((cl = table.get("ClassLoader"))
1096                                                instanceof ClassLoader)) {
1097                             cl = Thread.currentThread().
1098                                         getContextClassLoader();
1099                             if (cl == null) {
1100                                 // Fallback to the system class loader.
1101                                 cl = ClassLoader.getSystemClassLoader();
1102                             }
1103                         }
1104                         ReflectUtil.checkPackageAccess(className);
1105                         c = Class.forName(className, true, (ClassLoader)cl);
1106                         SwingUtilities2.checkAccess(c.getModifiers());
1107                         if (methodName != null) {
1108                             Class<?>[] types = getClassArray(args);
1109                             Method m = c.getMethod(methodName, types);
1110                             return MethodUtil.invoke(m, c, args);
1111                         } else {
1112                             Class<?>[] types = getClassArray(args);
1113                             Constructor<?> constructor = c.getConstructor(types);
1114                             SwingUtilities2.checkAccess(constructor.getModifiers());
1115                             return constructor.newInstance(args);
1116                         }
1117                     } catch(Exception e) {
1118                         // Ideally we would throw an exception, unfortunately
1119                         // often times there are errors as an initial look and
1120                         // feel is loaded before one can be switched. Perhaps a
1121                         // flag should be added for debugging, so that if true
1122                         // the exception would be thrown.
1123                     }
1124                     return null;
1125                 }
1126             }, acc);
1127         }
1128 
1129         /*
1130          * Coerce the array of class types provided into one which
1131          * looks the way the Reflection APIs expect.  This is done
1132          * by substituting primitive types for their Object counterparts,
1133          * and superclasses for subclasses used to add the
1134          * <code>UIResource</code> tag.
1135          */
1136         private Class<?>[] getClassArray(Object[] args) {
1137             Class<?>[] types = null;
1138             if (args!=null) {
1139                 types = new Class<?>[args.length];
1140                 for (int i = 0; i< args.length; i++) {
1141                     /* PENDING(ges): At present only the primitive types
1142                        used are handled correctly; this should eventually
1143                        handle all primitive types */
1144                     if (args[i] instanceof java.lang.Integer) {
1145                         types[i]=Integer.TYPE;
1146                     } else if (args[i] instanceof java.lang.Boolean) {
1147                         types[i]=Boolean.TYPE;
1148                     } else if (args[i] instanceof javax.swing.plaf.ColorUIResource) {
1149                         /* PENDING(ges) Currently the Reflection APIs do not
1150                            search superclasses of parameters supplied for
1151                            constructor/method lookup.  Since we only have
1152                            one case where this is needed, we substitute
1153                            directly instead of adding a massive amount
1154                            of mechanism for this.  Eventually this will
1155                            probably need to handle the general case as well.
1156                            */
1157                         types[i]=java.awt.Color.class;
1158                     } else {
1159                         types[i]=args[i].getClass();
1160                     }
1161                 }
1162             }
1163             return types;
1164         }
1165 
1166         private String printArgs(Object[] array) {
1167             String s = "{";
1168             if (array !=null) {
1169                 for (int i = 0 ; i < array.length-1; i++) {
1170                     s = s.concat(array[i] + ",");
1171                 }
1172                 s = s.concat(array[array.length-1] + "}");
1173             } else {
1174                 s = s.concat("}");
1175             }
1176             return s;
1177         }
1178     }
1179 
1180 
1181     /**
1182      * <code>LazyInputMap</code> will create a <code>InputMap</code>
1183      * in its <code>createValue</code>
1184      * method. The bindings are passed in in the constructor.
1185      * The bindings are an array with
1186      * the even number entries being string <code>KeyStrokes</code>
1187      * (eg "alt SPACE") and
1188      * the odd number entries being the value to use in the
1189      * <code>InputMap</code> (and the key in the <code>ActionMap</code>).
1190      * @since 1.3
1191      */
1192     public static class LazyInputMap implements LazyValue {
1193         /** Key bindings are registered under. */
1194         private Object[] bindings;
1195 
1196         public LazyInputMap(Object[] bindings) {
1197             this.bindings = bindings;
1198         }
1199 
1200         /**
1201          * Creates an <code>InputMap</code> with the bindings that are
1202          * passed in.
1203          *
1204          * @param table a <code>UIDefaults</code> table
1205          * @return the <code>InputMap</code>
1206          */
1207         public Object createValue(UIDefaults table) {
1208             if (bindings != null) {
1209                 InputMap km = LookAndFeel.makeInputMap(bindings);
1210                 return km;
1211             }
1212             return null;
1213         }
1214     }
1215 
1216     /**
1217      * <code>TextAndMnemonicHashMap</code> stores swing resource strings. Many of strings
1218      * can have a mnemonic. For example:
1219      *   FileChooser.saveButton.textAndMnemonic=&Save
1220      * For this case method get returns "Save" for the key "FileChooser.saveButtonText" and
1221      * mnemonic "S" for the key "FileChooser.saveButtonMnemonic"
1222      *
1223      * There are several patterns for the text and mnemonic suffixes which are checked by the
1224      * <code>TextAndMnemonicHashMap</code> class.
1225      * Patterns which are converted to the xxx.textAndMnemonic key:
1226      * (xxxNameText, xxxNameMnemonic)
1227      * (xxxNameText, xxxMnemonic)
1228      * (xxx.nameText, xxx.mnemonic)
1229      * (xxxText, xxxMnemonic)
1230      *
1231      * These patterns can have a mnemonic index in format
1232      * (xxxDisplayedMnemonicIndex)
1233      *
1234      * Pattern which is converted to the xxx.titleAndMnemonic key:
1235      * (xxxTitle, xxxMnemonic)
1236      *
1237      */
1238     private static class TextAndMnemonicHashMap extends HashMap<String, Object> {
1239 
1240         static final String AND_MNEMONIC = "AndMnemonic";
1241         static final String TITLE_SUFFIX = ".titleAndMnemonic";
1242         static final String TEXT_SUFFIX = ".textAndMnemonic";
1243 
1244         @Override
1245         public Object get(Object key) {
1246 
1247             Object value = super.get(key);
1248 
1249             if (value == null) {
1250 
1251                 boolean checkTitle = false;
1252 
1253                 String stringKey = key.toString();
1254                 String compositeKey = null;
1255 
1256                 if (stringKey.endsWith(AND_MNEMONIC)) {
1257                     return null;
1258                 }
1259 
1260                 if (stringKey.endsWith(".mnemonic")) {
1261                     compositeKey = composeKey(stringKey, 9, TEXT_SUFFIX);
1262                 } else if (stringKey.endsWith("NameMnemonic")) {
1263                     compositeKey = composeKey(stringKey, 12, TEXT_SUFFIX);
1264                 } else if (stringKey.endsWith("Mnemonic")) {
1265                     compositeKey = composeKey(stringKey, 8, TEXT_SUFFIX);
1266                     checkTitle = true;
1267                 }
1268 
1269                 if (compositeKey != null) {
1270                     value = super.get(compositeKey);
1271                     if (value == null && checkTitle) {
1272                         compositeKey = composeKey(stringKey, 8, TITLE_SUFFIX);
1273                         value = super.get(compositeKey);
1274                     }
1275 
1276                     return value == null ? null : getMnemonicFromProperty(value.toString());
1277                 }
1278 
1279                 if (stringKey.endsWith("NameText")) {
1280                     compositeKey = composeKey(stringKey, 8, TEXT_SUFFIX);
1281                 } else if (stringKey.endsWith(".nameText")) {
1282                     compositeKey = composeKey(stringKey, 9, TEXT_SUFFIX);
1283                 } else if (stringKey.endsWith("Text")) {
1284                     compositeKey = composeKey(stringKey, 4, TEXT_SUFFIX);
1285                 } else if (stringKey.endsWith("Title")) {
1286                     compositeKey = composeKey(stringKey, 5, TITLE_SUFFIX);
1287                 }
1288 
1289                 if (compositeKey != null) {
1290                     value = super.get(compositeKey);
1291                     return value == null ? null : getTextFromProperty(value.toString());
1292                 }
1293 
1294                 if (stringKey.endsWith("DisplayedMnemonicIndex")) {
1295                     compositeKey = composeKey(stringKey, 22, TEXT_SUFFIX);
1296                     value = super.get(compositeKey);
1297                     if (value == null) {
1298                         compositeKey = composeKey(stringKey, 22, TITLE_SUFFIX);
1299                         value = super.get(compositeKey);
1300                     }
1301                     return value == null ? null : getIndexFromProperty(value.toString());
1302                 }
1303             }
1304 
1305             return value;
1306         }
1307 
1308         String composeKey(String key, int reduce, String sufix) {
1309             return key.substring(0, key.length() - reduce) + sufix;
1310         }
1311 
1312         String getTextFromProperty(String text) {
1313             return text.replace("&", "");
1314         }
1315 
1316         String getMnemonicFromProperty(String text) {
1317             int index = text.indexOf('&');
1318             if (0 <= index && index < text.length() - 1) {
1319                 char c = text.charAt(index + 1);
1320                 return Integer.toString((int) Character.toUpperCase(c));
1321             }
1322             return null;
1323         }
1324 
1325         String getIndexFromProperty(String text) {
1326             int index = text.indexOf('&');
1327             return (index == -1) ? null : Integer.toString(index);
1328         }
1329     }
1330 
1331 }