UIDefaults.java revision 11099:678faa7d1a6a
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
26package javax.swing;
27
28
29import javax.swing.plaf.ComponentUI;
30import javax.swing.border.*;
31import javax.swing.event.SwingPropertyChangeSupport;
32
33import java.lang.reflect.*;
34import java.util.HashMap;
35import java.util.Map;
36import java.util.Enumeration;
37import java.util.Hashtable;
38import java.util.ResourceBundle;
39import java.util.ResourceBundle.Control;
40import java.util.Locale;
41import java.util.Vector;
42import java.util.MissingResourceException;
43import java.awt.Font;
44import java.awt.Color;
45import java.awt.Insets;
46import java.awt.Dimension;
47import java.lang.reflect.Method;
48import java.beans.PropertyChangeListener;
49import java.beans.PropertyChangeEvent;
50import java.security.AccessController;
51import java.security.AccessControlContext;
52import java.security.PrivilegedAction;
53
54import sun.reflect.misc.MethodUtil;
55import sun.reflect.misc.ReflectUtil;
56import sun.swing.SwingUtilities2;
57import 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
77public 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 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 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}
1332