jdk/src/share/classes/javax/swing/UIDefaults.java
changeset 2 90ce3da70b43
child 1301 15e81207e1f2
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/javax/swing/UIDefaults.java	Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,1205 @@
+/*
+ * Copyright 1997-2006 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package javax.swing;
+
+
+import javax.swing.plaf.ComponentUI;
+import javax.swing.border.*;
+import javax.swing.event.SwingPropertyChangeSupport;
+
+import java.lang.reflect.*;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.ResourceBundle;
+import java.util.ResourceBundle.Control;
+import java.util.Locale;
+import java.util.Vector;
+import java.util.MissingResourceException;
+import java.awt.Font;
+import java.awt.Color;
+import java.awt.Insets;
+import java.awt.Dimension;
+import java.lang.reflect.Method;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeEvent;
+import java.security.AccessController;
+import java.security.AccessControlContext;
+import java.security.PrivilegedAction;
+
+import sun.reflect.misc.MethodUtil;
+import sun.util.CoreResourceBundleControl;
+
+/**
+ * A table of defaults for Swing components.  Applications can set/get
+ * default values via the <code>UIManager</code>.
+ * <p>
+ * <strong>Warning:</strong>
+ * Serialized objects of this class will not be compatible with
+ * future Swing releases. The current serialization support is
+ * appropriate for short term storage or RMI between applications running
+ * the same version of Swing.  As of 1.4, support for long term storage
+ * of all JavaBeans<sup><font size="-2">TM</font></sup>
+ * has been added to the <code>java.beans</code> package.
+ * Please see {@link java.beans.XMLEncoder}.
+ *
+ * @see UIManager
+ * @author Hans Muller
+ */
+public class UIDefaults extends Hashtable<Object,Object>
+{
+    private static final Object PENDING = new String("Pending");
+
+    private SwingPropertyChangeSupport changeSupport;
+
+    private Vector resourceBundles;
+
+    private Locale defaultLocale = Locale.getDefault();
+
+    /**
+     * Maps from a Locale to a cached Map of the ResourceBundle. This is done
+     * so as to avoid an exception being thrown when a value is asked for.
+     * Access to this should be done while holding a lock on the
+     * UIDefaults, eg synchronized(this).
+     */
+    private Map resourceCache;
+
+    /**
+     * Creates an empty defaults table.
+     */
+    public UIDefaults() {
+        this(700, .75f);
+    }
+
+    /**
+     * Creates an empty defaults table with the specified initial capacity and
+     * load factor.
+     *
+     * @param initialCapacity   the initial capacity of the defaults table
+     * @param loadFactor        the load factor of the defaults table
+     * @see java.util.Hashtable
+     * @since 1.6
+     */
+    public UIDefaults(int initialCapacity, float loadFactor) {
+        super(initialCapacity, loadFactor);
+        resourceCache = new HashMap();
+    }
+
+
+    /**
+     * Creates a defaults table initialized with the specified
+     * key/value pairs.  For example:
+     * <pre>
+        Object[] uiDefaults = {
+             "Font", new Font("Dialog", Font.BOLD, 12),
+            "Color", Color.red,
+             "five", new Integer(5)
+        }
+        UIDefaults myDefaults = new UIDefaults(uiDefaults);
+     * </pre>
+     * @param keyValueList  an array of objects containing the key/value
+     *          pairs
+     */
+    public UIDefaults(Object[] keyValueList) {
+        super(keyValueList.length / 2);
+        for(int i = 0; i < keyValueList.length; i += 2) {
+            super.put(keyValueList[i], keyValueList[i + 1]);
+        }
+    }
+
+    /**
+     * Returns the value for key.  If the value is a
+     * <code>UIDefaults.LazyValue</code> then the real
+     * value is computed with <code>LazyValue.createValue()</code>,
+     * the table entry is replaced, and the real value is returned.
+     * If the value is an <code>UIDefaults.ActiveValue</code>
+     * the table entry is not replaced - the value is computed
+     * with <code>ActiveValue.createValue()</code> for each
+     * <code>get()</code> call.
+     *
+     * If the key is not found in the table then it is searched for in the list
+     * of resource bundles maintained by this object.  The resource bundles are
+     * searched most recently added first using the locale returned by
+     * <code>getDefaultLocale</code>.  <code>LazyValues</code> and
+     * <code>ActiveValues</code> are not supported in the resource bundles.
+
+     *
+     * @param key the desired key
+     * @return the value for <code>key</code>
+     * @see LazyValue
+     * @see ActiveValue
+     * @see java.util.Hashtable#get
+     * @see #getDefaultLocale
+     * @see #addResourceBundle
+     * @since 1.4
+     */
+    public Object get(Object key) {
+        Object value = getFromHashtable( key );
+        return (value != null) ? value : getFromResourceBundle(key, null);
+    }
+
+    /**
+     * Looks up up the given key in our Hashtable and resolves LazyValues
+     * or ActiveValues.
+     */
+    private Object getFromHashtable(Object key) {
+        /* Quickly handle the common case, without grabbing
+         * a lock.
+         */
+        Object value = super.get(key);
+        if ((value != PENDING) &&
+            !(value instanceof ActiveValue) &&
+            !(value instanceof LazyValue)) {
+            return value;
+        }
+
+        /* If the LazyValue for key is being constructed by another
+         * thread then wait and then return the new value, otherwise drop
+         * the lock and construct the ActiveValue or the LazyValue.
+         * We use the special value PENDING to mark LazyValues that
+         * are being constructed.
+         */
+        synchronized(this) {
+            value = super.get(key);
+            if (value == PENDING) {
+                do {
+                    try {
+                        this.wait();
+                    }
+                    catch (InterruptedException e) {
+                    }
+                    value = super.get(key);
+                }
+                while(value == PENDING);
+                return value;
+            }
+            else if (value instanceof LazyValue) {
+                super.put(key, PENDING);
+            }
+            else if (!(value instanceof ActiveValue)) {
+                return value;
+            }
+        }
+
+        /* At this point we know that the value of key was
+         * a LazyValue or an ActiveValue.
+         */
+        if (value instanceof LazyValue) {
+            try {
+                /* If an exception is thrown we'll just put the LazyValue
+                 * back in the table.
+                 */
+                value = ((LazyValue)value).createValue(this);
+            }
+            finally {
+                synchronized(this) {
+                    if (value == null) {
+                        super.remove(key);
+                    }
+                    else {
+                        super.put(key, value);
+                    }
+                    this.notifyAll();
+                }
+            }
+        }
+        else {
+            value = ((ActiveValue)value).createValue(this);
+        }
+
+        return value;
+    }
+
+
+    /**
+     * Returns the value for key associated with the given locale.
+     * If the value is a <code>UIDefaults.LazyValue</code> then the real
+     * value is computed with <code>LazyValue.createValue()</code>,
+     * the table entry is replaced, and the real value is returned.
+     * If the value is an <code>UIDefaults.ActiveValue</code>
+     * the table entry is not replaced - the value is computed
+     * with <code>ActiveValue.createValue()</code> for each
+     * <code>get()</code> call.
+     *
+     * If the key is not found in the table then it is searched for in the list
+     * of resource bundles maintained by this object.  The resource bundles are
+     * searched most recently added first using the given locale.
+     * <code>LazyValues</code> and <code>ActiveValues</code> are not supported
+     * in the resource bundles.
+     *
+     * @param key the desired key
+     * @param l the desired <code>locale</code>
+     * @return the value for <code>key</code>
+     * @see LazyValue
+     * @see ActiveValue
+     * @see java.util.Hashtable#get
+     * @see #addResourceBundle
+     * @since 1.4
+     */
+    public Object get(Object key, Locale l) {
+        Object value = getFromHashtable( key );
+        return (value != null) ? value : getFromResourceBundle(key, l);
+    }
+
+    /**
+     * Looks up given key in our resource bundles.
+     */
+    private Object getFromResourceBundle(Object key, Locale l) {
+
+        if( resourceBundles == null ||
+            resourceBundles.isEmpty() ||
+            !(key instanceof String) ) {
+            return null;
+        }
+
+        // A null locale means use the default locale.
+        if( l == null ) {
+            if( defaultLocale == null )
+                return null;
+            else
+                l = (Locale)defaultLocale;
+        }
+
+        synchronized(this) {
+            return getResourceCache(l).get((String)key);
+        }
+    }
+
+    /**
+     * Returns a Map of the known resources for the given locale.
+     */
+    private Map getResourceCache(Locale l) {
+        Map values = (Map)resourceCache.get(l);
+
+        if (values == null) {
+            values = new HashMap();
+            for (int i=resourceBundles.size()-1; i >= 0; i--) {
+                String bundleName = (String)resourceBundles.get(i);
+                try {
+                    Control c = CoreResourceBundleControl.getRBControlInstance(bundleName);
+                    ResourceBundle b;
+                    if (c != null) {
+                        b = ResourceBundle.getBundle(bundleName, l, c);
+                    } else {
+                        b = ResourceBundle.getBundle(bundleName, l);
+                    }
+                    Enumeration keys = b.getKeys();
+
+                    while (keys.hasMoreElements()) {
+                        String key = (String)keys.nextElement();
+
+                        if (values.get(key) == null) {
+                            Object value = b.getObject(key);
+
+                            values.put(key, value);
+                        }
+                    }
+                } catch( MissingResourceException mre ) {
+                    // Keep looking
+                }
+            }
+            resourceCache.put(l, values);
+        }
+        return values;
+    }
+
+    /**
+     * Sets the value of <code>key</code> to <code>value</code> for all locales.
+     * If <code>key</code> is a string and the new value isn't
+     * equal to the old one, fire a <code>PropertyChangeEvent</code>.
+     * If value is <code>null</code>, the key is removed from the table.
+     *
+     * @param key    the unique <code>Object</code> who's value will be used
+     *          to retrieve the data value associated with it
+     * @param value  the new <code>Object</code> to store as data under
+     *          that key
+     * @return the previous <code>Object</code> value, or <code>null</code>
+     * @see #putDefaults
+     * @see java.util.Hashtable#put
+     */
+    public Object put(Object key, Object value) {
+        Object oldValue = (value == null) ? super.remove(key) : super.put(key, value);
+        if (key instanceof String) {
+            firePropertyChange((String)key, oldValue, value);
+        }
+        return oldValue;
+    }
+
+
+    /**
+     * Puts all of the key/value pairs in the database and
+     * unconditionally generates one <code>PropertyChangeEvent</code>.
+     * The events oldValue and newValue will be <code>null</code> and its
+     * <code>propertyName</code> will be "UIDefaults".  The key/value pairs are
+     * added for all locales.
+     *
+     * @param keyValueList  an array of key/value pairs
+     * @see #put
+     * @see java.util.Hashtable#put
+     */
+    public void putDefaults(Object[] keyValueList) {
+        for(int i = 0, max = keyValueList.length; i < max; i += 2) {
+            Object value = keyValueList[i + 1];
+            if (value == null) {
+                super.remove(keyValueList[i]);
+            }
+            else {
+                super.put(keyValueList[i], value);
+            }
+        }
+        firePropertyChange("UIDefaults", null, null);
+    }
+
+
+    /**
+     * If the value of <code>key</code> is a <code>Font</code> return it,
+     * otherwise return <code>null</code>.
+     * @param key the desired key
+     * @return if the value for <code>key</code> is a <code>Font</code>,
+     *          return the <code>Font</code> object; otherwise return
+     *          <code>null</code>
+     */
+    public Font getFont(Object key) {
+        Object value = get(key);
+        return (value instanceof Font) ? (Font)value : null;
+    }
+
+
+    /**
+     * If the value of <code>key</code> for the given <code>Locale</code>
+     * is a <code>Font</code> return it, otherwise return <code>null</code>.
+     * @param key the desired key
+     * @param l the desired locale
+     * @return if the value for <code>key</code> and <code>Locale</code>
+     *          is a <code>Font</code>,
+     *          return the <code>Font</code> object; otherwise return
+     *          <code>null</code>
+     * @since 1.4
+     */
+    public Font getFont(Object key, Locale l) {
+        Object value = get(key,l);
+        return (value instanceof Font) ? (Font)value : null;
+    }
+
+    /**
+     * If the value of <code>key</code> is a <code>Color</code> return it,
+     * otherwise return <code>null</code>.
+     * @param key the desired key
+     * @return if the value for <code>key</code> is a <code>Color</code>,
+     *          return the <code>Color</code> object; otherwise return
+     *          <code>null</code>
+     */
+    public Color getColor(Object key) {
+        Object value = get(key);
+        return (value instanceof Color) ? (Color)value : null;
+    }
+
+
+    /**
+     * If the value of <code>key</code> for the given <code>Locale</code>
+     * is a <code>Color</code> return it, otherwise return <code>null</code>.
+     * @param key the desired key
+     * @param l the desired locale
+     * @return if the value for <code>key</code> and <code>Locale</code>
+     *          is a <code>Color</code>,
+     *          return the <code>Color</code> object; otherwise return
+     *          <code>null</code>
+     * @since 1.4
+     */
+    public Color getColor(Object key, Locale l) {
+        Object value = get(key,l);
+        return (value instanceof Color) ? (Color)value : null;
+    }
+
+
+    /**
+     * If the value of <code>key</code> is an <code>Icon</code> return it,
+     * otherwise return <code>null</code>.
+     * @param key the desired key
+     * @return if the value for <code>key</code> is an <code>Icon</code>,
+     *          return the <code>Icon</code> object; otherwise return
+     *          <code>null</code>
+     */
+    public Icon getIcon(Object key) {
+        Object value = get(key);
+        return (value instanceof Icon) ? (Icon)value : null;
+    }
+
+
+    /**
+     * If the value of <code>key</code> for the given <code>Locale</code>
+     * is an <code>Icon</code> return it, otherwise return <code>null</code>.
+     * @param key the desired key
+     * @param l the desired locale
+     * @return if the value for <code>key</code> and <code>Locale</code>
+     *          is an <code>Icon</code>,
+     *          return the <code>Icon</code> object; otherwise return
+     *          <code>null</code>
+     * @since 1.4
+     */
+    public Icon getIcon(Object key, Locale l) {
+        Object value = get(key,l);
+        return (value instanceof Icon) ? (Icon)value : null;
+    }
+
+
+    /**
+     * If the value of <code>key</code> is a <code>Border</code> return it,
+     * otherwise return <code>null</code>.
+     * @param key the desired key
+     * @return if the value for <code>key</code> is a <code>Border</code>,
+     *          return the <code>Border</code> object; otherwise return
+     *          <code>null</code>
+     */
+    public Border getBorder(Object key) {
+        Object value = get(key);
+        return (value instanceof Border) ? (Border)value : null;
+    }
+
+
+    /**
+     * If the value of <code>key</code> for the given <code>Locale</code>
+     * is a <code>Border</code> return it, otherwise return <code>null</code>.
+     * @param key the desired key
+     * @param l the desired locale
+     * @return if the value for <code>key</code> and <code>Locale</code>
+     *          is a <code>Border</code>,
+     *          return the <code>Border</code> object; otherwise return
+     *          <code>null</code>
+     * @since 1.4
+     */
+    public Border getBorder(Object key, Locale l)  {
+        Object value = get(key,l);
+        return (value instanceof Border) ? (Border)value : null;
+    }
+
+
+    /**
+     * If the value of <code>key</code> is a <code>String</code> return it,
+     * otherwise return <code>null</code>.
+     * @param key the desired key
+     * @return if the value for <code>key</code> is a <code>String</code>,
+     *          return the <code>String</code> object; otherwise return
+     *          <code>null</code>
+     */
+    public String getString(Object key) {
+        Object value = get(key);
+        return (value instanceof String) ? (String)value : null;
+    }
+
+    /**
+     * If the value of <code>key</code> for the given <code>Locale</code>
+     * is a <code>String</code> return it, otherwise return <code>null</code>.
+     * @param key the desired key
+     * @param l the desired <code>Locale</code>
+     * @return if the value for <code>key</code> for the given
+     *          <code>Locale</code> is a <code>String</code>,
+     *          return the <code>String</code> object; otherwise return
+     *          <code>null</code>
+     * @since 1.4
+     */
+    public String getString(Object key, Locale l) {
+        Object value = get(key,l);
+        return (value instanceof String) ? (String)value : null;
+    }
+
+    /**
+     * If the value of <code>key</code> is an <code>Integer</code> return its
+     * integer value, otherwise return 0.
+     * @param key the desired key
+     * @return if the value for <code>key</code> is an <code>Integer</code>,
+     *          return its value, otherwise return 0
+     */
+    public int getInt(Object key) {
+        Object value = get(key);
+        return (value instanceof Integer) ? ((Integer)value).intValue() : 0;
+    }
+
+
+    /**
+     * If the value of <code>key</code> for the given <code>Locale</code>
+     * is an <code>Integer</code> return its integer value, otherwise return 0.
+     * @param key the desired key
+     * @param l the desired locale
+     * @return if the value for <code>key</code> and <code>Locale</code>
+     *          is an <code>Integer</code>,
+     *          return its value, otherwise return 0
+     * @since 1.4
+     */
+    public int getInt(Object key, Locale l) {
+        Object value = get(key,l);
+        return (value instanceof Integer) ? ((Integer)value).intValue() : 0;
+    }
+
+
+    /**
+     * If the value of <code>key</code> is boolean, return the
+     * boolean value, otherwise return false.
+     *
+     * @param key an <code>Object</code> specifying the key for the desired boolean value
+     * @return if the value of <code>key</code> is boolean, return the
+     *         boolean value, otherwise return false.
+     * @since 1.4
+     */
+    public boolean getBoolean(Object key) {
+        Object value = get(key);
+        return (value instanceof Boolean) ? ((Boolean)value).booleanValue() : false;
+    }
+
+
+    /**
+     * If the value of <code>key</code> for the given <code>Locale</code>
+     * is boolean, return the boolean value, otherwise return false.
+     *
+     * @param key an <code>Object</code> specifying the key for the desired boolean value
+     * @param l the desired locale
+     * @return if the value for <code>key</code> and <code>Locale</code>
+     *         is boolean, return the
+     *         boolean value, otherwise return false.
+     * @since 1.4
+     */
+    public boolean getBoolean(Object key, Locale l) {
+        Object value = get(key,l);
+        return (value instanceof Boolean) ? ((Boolean)value).booleanValue() : false;
+    }
+
+
+    /**
+     * If the value of <code>key</code> is an <code>Insets</code> return it,
+     * otherwise return <code>null</code>.
+     * @param key the desired key
+     * @return if the value for <code>key</code> is an <code>Insets</code>,
+     *          return the <code>Insets</code> object; otherwise return
+     *          <code>null</code>
+     */
+    public Insets getInsets(Object key) {
+        Object value = get(key);
+        return (value instanceof Insets) ? (Insets)value : null;
+    }
+
+
+    /**
+     * If the value of <code>key</code> for the given <code>Locale</code>
+     * is an <code>Insets</code> return it, otherwise return <code>null</code>.
+     * @param key the desired key
+     * @param l the desired locale
+     * @return if the value for <code>key</code> and <code>Locale</code>
+     *          is an <code>Insets</code>,
+     *          return the <code>Insets</code> object; otherwise return
+     *          <code>null</code>
+     * @since 1.4
+     */
+    public Insets getInsets(Object key, Locale l) {
+        Object value = get(key,l);
+        return (value instanceof Insets) ? (Insets)value : null;
+    }
+
+
+    /**
+     * If the value of <code>key</code> is a <code>Dimension</code> return it,
+     * otherwise return <code>null</code>.
+     * @param key the desired key
+     * @return if the value for <code>key</code> is a <code>Dimension</code>,
+     *          return the <code>Dimension</code> object; otherwise return
+     *          <code>null</code>
+     */
+    public Dimension getDimension(Object key) {
+        Object value = get(key);
+        return (value instanceof Dimension) ? (Dimension)value : null;
+    }
+
+
+    /**
+     * If the value of <code>key</code> for the given <code>Locale</code>
+     * is a <code>Dimension</code> return it, otherwise return <code>null</code>.
+     * @param key the desired key
+     * @param l the desired locale
+     * @return if the value for <code>key</code> and <code>Locale</code>
+     *          is a <code>Dimension</code>,
+     *          return the <code>Dimension</code> object; otherwise return
+     *          <code>null</code>
+     * @since 1.4
+     */
+    public Dimension getDimension(Object key, Locale l) {
+        Object value = get(key,l);
+        return (value instanceof Dimension) ? (Dimension)value : null;
+    }
+
+
+    /**
+     * The value of <code>get(uidClassID)</code> must be the
+     * <code>String</code> name of a
+     * class that implements the corresponding <code>ComponentUI</code>
+     * class.  If the class hasn't been loaded before, this method looks
+     * up the class with <code>uiClassLoader.loadClass()</code> if a non
+     * <code>null</code>
+     * class loader is provided, <code>classForName()</code> otherwise.
+     * <p>
+     * If a mapping for <code>uiClassID</code> exists or if the specified
+     * class can't be found, return <code>null</code>.
+     * <p>
+     * This method is used by <code>getUI</code>, it's usually
+     * not necessary to call it directly.
+     *
+     * @param uiClassID  a string containing the class ID
+     * @param uiClassLoader the object which will load the class
+     * @return the value of <code>Class.forName(get(uidClassID))</code>
+     * @see #getUI
+     */
+    public Class<? extends ComponentUI>
+        getUIClass(String uiClassID, ClassLoader uiClassLoader)
+    {
+        try {
+            String className = (String)get(uiClassID);
+            if (className != null) {
+                Class cls = (Class)get(className);
+                if (cls == null) {
+                    if (uiClassLoader == null) {
+                        cls = SwingUtilities.loadSystemClass(className);
+                    }
+                    else {
+                        cls = uiClassLoader.loadClass(className);
+                    }
+                    if (cls != null) {
+                        // Save lookup for future use, as forName is slow.
+                        put(className, cls);
+                    }
+                }
+                return cls;
+            }
+        }
+        catch (ClassNotFoundException e) {
+            return null;
+        }
+        catch (ClassCastException e) {
+            return null;
+        }
+        return null;
+    }
+
+
+    /**
+     * Returns the L&F class that renders this component.
+     *
+     * @param uiClassID a string containing the class ID
+     * @return the Class object returned by
+     *          <code>getUIClass(uiClassID, null)</code>
+     */
+    public Class<? extends ComponentUI> getUIClass(String uiClassID) {
+        return getUIClass(uiClassID, null);
+    }
+
+
+    /**
+     * If <code>getUI()</code> fails for any reason,
+     * it calls this method before returning <code>null</code>.
+     * Subclasses may choose to do more or less here.
+     *
+     * @param msg message string to print
+     * @see #getUI
+     */
+    protected void getUIError(String msg) {
+        System.err.println("UIDefaults.getUI() failed: " + msg);
+        try {
+            throw new Error();
+        }
+        catch (Throwable e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * Creates an <code>ComponentUI</code> implementation for the
+     * specified component.  In other words create the look
+     * and feel specific delegate object for <code>target</code>.
+     * This is done in two steps:
+     * <ul>
+     * <li> Look up the name of the <code>ComponentUI</code> implementation
+     * class under the value returned by <code>target.getUIClassID()</code>.
+     * <li> Use the implementation classes static <code>createUI()</code>
+     * method to construct a look and feel delegate.
+     * </ul>
+     * @param target  the <code>JComponent</code> which needs a UI
+     * @return the <code>ComponentUI</code> object
+     */
+    public ComponentUI getUI(JComponent target) {
+
+        Object cl = get("ClassLoader");
+        ClassLoader uiClassLoader =
+            (cl != null) ? (ClassLoader)cl : target.getClass().getClassLoader();
+        Class uiClass = getUIClass(target.getUIClassID(), uiClassLoader);
+        Object uiObject = null;
+
+        if (uiClass == null) {
+            getUIError("no ComponentUI class for: " + target);
+        }
+        else {
+            try {
+                Method m = (Method)get(uiClass);
+                if (m == null) {
+                    Class acClass = javax.swing.JComponent.class;
+                    m = uiClass.getMethod("createUI", new Class[]{acClass});
+                    put(uiClass, m);
+                }
+                uiObject = MethodUtil.invoke(m, null, new Object[]{target});
+            }
+            catch (NoSuchMethodException e) {
+                getUIError("static createUI() method not found in " + uiClass);
+            }
+            catch (Exception e) {
+                getUIError("createUI() failed for " + target + " " + e);
+            }
+        }
+
+        return (ComponentUI)uiObject;
+    }
+
+    /**
+     * Adds a <code>PropertyChangeListener</code> to the listener list.
+     * The listener is registered for all properties.
+     * <p>
+     * A <code>PropertyChangeEvent</code> will get fired whenever a default
+     * is changed.
+     *
+     * @param listener  the <code>PropertyChangeListener</code> to be added
+     * @see java.beans.PropertyChangeSupport
+     */
+    public synchronized void addPropertyChangeListener(PropertyChangeListener listener) {
+        if (changeSupport == null) {
+            changeSupport = new SwingPropertyChangeSupport(this);
+        }
+        changeSupport.addPropertyChangeListener(listener);
+    }
+
+
+    /**
+     * Removes a <code>PropertyChangeListener</code> from the listener list.
+     * This removes a <code>PropertyChangeListener</code> that was registered
+     * for all properties.
+     *
+     * @param listener  the <code>PropertyChangeListener</code> to be removed
+     * @see java.beans.PropertyChangeSupport
+     */
+    public synchronized void removePropertyChangeListener(PropertyChangeListener listener) {
+        if (changeSupport != null) {
+            changeSupport.removePropertyChangeListener(listener);
+        }
+    }
+
+
+    /**
+     * Returns an array of all the <code>PropertyChangeListener</code>s added
+     * to this UIDefaults with addPropertyChangeListener().
+     *
+     * @return all of the <code>PropertyChangeListener</code>s added or an empty
+     *         array if no listeners have been added
+     * @since 1.4
+     */
+    public synchronized PropertyChangeListener[] getPropertyChangeListeners() {
+        if (changeSupport == null) {
+            return new PropertyChangeListener[0];
+        }
+        return changeSupport.getPropertyChangeListeners();
+    }
+
+
+    /**
+     * Support for reporting bound property changes.  If oldValue and
+     * newValue are not equal and the <code>PropertyChangeEvent</code>x
+     * listener list isn't empty, then fire a
+     * <code>PropertyChange</code> event to each listener.
+     *
+     * @param propertyName  the programmatic name of the property
+     *          that was changed
+     * @param oldValue  the old value of the property
+     * @param newValue  the new value of the property
+     * @see java.beans.PropertyChangeSupport
+     */
+    protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
+        if (changeSupport != null) {
+            changeSupport.firePropertyChange(propertyName, oldValue, newValue);
+        }
+    }
+
+
+    /**
+     * Adds a resource bundle to the list of resource bundles that are
+     * searched for localized values.  Resource bundles are searched in the
+     * reverse order they were added.  In other words, the most recently added
+     * bundle is searched first.
+     *
+     * @param bundleName  the base name of the resource bundle to be added
+     * @see java.util.ResourceBundle
+     * @see #removeResourceBundle
+     * @since 1.4
+     */
+    public synchronized void addResourceBundle( String bundleName ) {
+        if( bundleName == null ) {
+            return;
+        }
+        if( resourceBundles == null ) {
+            resourceBundles = new Vector(5);
+        }
+        if (!resourceBundles.contains(bundleName)) {
+            resourceBundles.add( bundleName );
+            resourceCache.clear();
+        }
+    }
+
+
+    /**
+     * Removes a resource bundle from the list of resource bundles that are
+     * searched for localized defaults.
+     *
+     * @param bundleName  the base name of the resource bundle to be removed
+     * @see java.util.ResourceBundle
+     * @see #addResourceBundle
+     * @since 1.4
+     */
+    public synchronized void removeResourceBundle( String bundleName ) {
+        if( resourceBundles != null ) {
+            resourceBundles.remove( bundleName );
+        }
+        resourceCache.clear();
+    }
+
+    /**
+     * Sets the default locale.  The default locale is used in retrieving
+     * localized values via <code>get</code> methods that do not take a
+     * locale argument.  As of release 1.4, Swing UI objects should retrieve
+     * localized values using the locale of their component rather than the
+     * default locale.  The default locale exists to provide compatibility with
+     * pre 1.4 behaviour.
+     *
+     * @param l the new default locale
+     * @see #getDefaultLocale
+     * @see #get(Object)
+     * @see #get(Object,Locale)
+     * @since 1.4
+     */
+    public void setDefaultLocale( Locale l ) {
+        defaultLocale = l;
+    }
+
+    /**
+     * Returns the default locale.  The default locale is used in retrieving
+     * localized values via <code>get</code> methods that do not take a
+     * locale argument.  As of release 1.4, Swing UI objects should retrieve
+     * localized values using the locale of their component rather than the
+     * default locale.  The default locale exists to provide compatibility with
+     * pre 1.4 behaviour.
+     *
+     * @return the default locale
+     * @see #setDefaultLocale
+     * @see #get(Object)
+     * @see #get(Object,Locale)
+     * @since 1.4
+     */
+    public Locale getDefaultLocale() {
+        return defaultLocale;
+    }
+
+    /**
+     * This class enables one to store an entry in the defaults
+     * table that isn't constructed until the first time it's
+     * looked up with one of the <code>getXXX(key)</code> methods.
+     * Lazy values are useful for defaults that are expensive
+     * to construct or are seldom retrieved.  The first time
+     * a <code>LazyValue</code> is retrieved its "real value" is computed
+     * by calling <code>LazyValue.createValue()</code> and the real
+     * value is used to replace the <code>LazyValue</code> in the
+     * <code>UIDefaults</code>
+     * table.  Subsequent lookups for the same key return
+     * the real value.  Here's an example of a <code>LazyValue</code>
+     * that constructs a <code>Border</code>:
+     * <pre>
+     *  Object borderLazyValue = new UIDefaults.LazyValue() {
+     *      public Object createValue(UIDefaults table) {
+     *          return new BorderFactory.createLoweredBevelBorder();
+     *      }
+     *  };
+     *
+     *  uiDefaultsTable.put("MyBorder", borderLazyValue);
+     * </pre>
+     *
+     * @see UIDefaults#get
+     */
+    public interface LazyValue {
+        /**
+         * Creates the actual value retrieved from the <code>UIDefaults</code>
+         * table. When an object that implements this interface is
+         * retrieved from the table, this method is used to create
+         * the real value, which is then stored in the table and
+         * returned to the calling method.
+         *
+         * @param table  a <code>UIDefaults</code> table
+         * @return the created <code>Object</code>
+         */
+        Object createValue(UIDefaults table);
+    }
+
+
+    /**
+     * This class enables one to store an entry in the defaults
+     * table that's constructed each time it's looked up with one of
+     * the <code>getXXX(key)</code> methods. Here's an example of
+     * an <code>ActiveValue</code> that constructs a
+     * <code>DefaultListCellRenderer</code>:
+     * <pre>
+     *  Object cellRendererActiveValue = new UIDefaults.ActiveValue() {
+     *      public Object createValue(UIDefaults table) {
+     *          return new DefaultListCellRenderer();
+     *      }
+     *  };
+     *
+     *  uiDefaultsTable.put("MyRenderer", cellRendererActiveValue);
+     * </pre>
+     *
+     * @see UIDefaults#get
+     */
+    public interface ActiveValue {
+        /**
+         * Creates the value retrieved from the <code>UIDefaults</code> table.
+         * The object is created each time it is accessed.
+         *
+         * @param table  a <code>UIDefaults</code> table
+         * @return the created <code>Object</code>
+         */
+        Object createValue(UIDefaults table);
+    }
+
+    /**
+     * This class provides an implementation of <code>LazyValue</code>
+     * which can be
+     * used to delay loading of the Class for the instance to be created.
+     * It also avoids creation of an anonymous inner class for the
+     * <code>LazyValue</code>
+     * subclass.  Both of these improve performance at the time that a
+     * a Look and Feel is loaded, at the cost of a slight performance
+     * reduction the first time <code>createValue</code> is called
+     * (since Reflection APIs are used).
+     * @since 1.3
+     */
+    public static class ProxyLazyValue implements LazyValue {
+        private AccessControlContext acc;
+        private String className;
+        private String methodName;
+        private Object[] args;
+
+        /**
+         * Creates a <code>LazyValue</code> which will construct an instance
+         * when asked.
+         *
+         * @param c    a <code>String</code> specifying the classname
+         *             of the instance to be created on demand
+         */
+        public ProxyLazyValue(String c) {
+            this(c, (String)null);
+        }
+        /**
+         * Creates a <code>LazyValue</code> which will construct an instance
+         * when asked.
+         *
+         * @param c    a <code>String</code> specifying the classname of
+         *              the class
+         *              containing a static method to be called for
+         *              instance creation
+         * @param m    a <code>String</code> specifying the static
+         *              method to be called on class c
+         */
+        public ProxyLazyValue(String c, String m) {
+            this(c, m, null);
+        }
+        /**
+         * Creates a <code>LazyValue</code> which will construct an instance
+         * when asked.
+         *
+         * @param c    a <code>String</code> specifying the classname
+         *              of the instance to be created on demand
+         * @param o    an array of <code>Objects</code> to be passed as
+         *              paramaters to the constructor in class c
+         */
+        public ProxyLazyValue(String c, Object[] o) {
+            this(c, null, o);
+        }
+        /**
+         * Creates a <code>LazyValue</code> which will construct an instance
+         * when asked.
+         *
+         * @param c    a <code>String</code> specifying the classname
+         *              of the class
+         *              containing a static method to be called for
+         *              instance creation.
+         * @param m    a <code>String</code> specifying the static method
+         *              to be called on class c
+         * @param o    an array of <code>Objects</code> to be passed as
+         *              paramaters to the static method in class c
+         */
+        public ProxyLazyValue(String c, String m, Object[] o) {
+            acc = AccessController.getContext();
+            className = c;
+            methodName = m;
+            if (o != null) {
+                args = (Object[])o.clone();
+            }
+        }
+
+        /**
+         * Creates the value retrieved from the <code>UIDefaults</code> table.
+         * The object is created each time it is accessed.
+         *
+         * @param table  a <code>UIDefaults</code> table
+         * @return the created <code>Object</code>
+         */
+        public Object createValue(final UIDefaults table) {
+            // In order to pick up the security policy in effect at the
+            // time of creation we use a doPrivileged with the
+            // AccessControlContext that was in place when this was created.
+            return AccessController.doPrivileged(new PrivilegedAction() {
+                public Object run() {
+                    try {
+                        Class c;
+                        Object cl;
+                        // See if we should use a separate ClassLoader
+                        if (table == null || !((cl = table.get("ClassLoader"))
+                                               instanceof ClassLoader)) {
+                            cl = Thread.currentThread().
+                                        getContextClassLoader();
+                            if (cl == null) {
+                                // Fallback to the system class loader.
+                                cl = ClassLoader.getSystemClassLoader();
+                            }
+                        }
+                        c = Class.forName(className, true, (ClassLoader)cl);
+                        if (methodName != null) {
+                            Class[] types = getClassArray(args);
+                            Method m = c.getMethod(methodName, types);
+                            return MethodUtil.invoke(m, c, args);
+                        } else {
+                            Class[] types = getClassArray(args);
+                            Constructor constructor = c.getConstructor(types);
+                            return constructor.newInstance(args);
+                        }
+                    } catch(Exception e) {
+                        // Ideally we would throw an exception, unfortunately
+                        // often times there are errors as an initial look and
+                        // feel is loaded before one can be switched. Perhaps a
+                        // flag should be added for debugging, so that if true
+                        // the exception would be thrown.
+                    }
+                    return null;
+                }
+            }, acc);
+        }
+
+        /*
+         * Coerce the array of class types provided into one which
+         * looks the way the Reflection APIs expect.  This is done
+         * by substituting primitive types for their Object counterparts,
+         * and superclasses for subclasses used to add the
+         * <code>UIResource</code> tag.
+         */
+        private Class[] getClassArray(Object[] args) {
+            Class[] types = null;
+            if (args!=null) {
+                types = new Class[args.length];
+                for (int i = 0; i< args.length; i++) {
+                    /* PENDING(ges): At present only the primitive types
+                       used are handled correctly; this should eventually
+                       handle all primitive types */
+                    if (args[i] instanceof java.lang.Integer) {
+                        types[i]=Integer.TYPE;
+                    } else if (args[i] instanceof java.lang.Boolean) {
+                        types[i]=Boolean.TYPE;
+                    } else if (args[i] instanceof javax.swing.plaf.ColorUIResource) {
+                        /* PENDING(ges) Currently the Reflection APIs do not
+                           search superclasses of parameters supplied for
+                           constructor/method lookup.  Since we only have
+                           one case where this is needed, we substitute
+                           directly instead of adding a massive amount
+                           of mechanism for this.  Eventually this will
+                           probably need to handle the general case as well.
+                           */
+                        types[i]=java.awt.Color.class;
+                    } else {
+                        types[i]=args[i].getClass();
+                    }
+                }
+            }
+            return types;
+        }
+
+        private String printArgs(Object[] array) {
+            String s = "{";
+            if (array !=null) {
+                for (int i = 0 ; i < array.length-1; i++) {
+                    s = s.concat(array[i] + ",");
+                }
+                s = s.concat(array[array.length-1] + "}");
+            } else {
+                s = s.concat("}");
+            }
+            return s;
+        }
+    }
+
+
+    /**
+     * <code>LazyInputMap</code> will create a <code>InputMap</code>
+     * in its <code>createValue</code>
+     * method. The bindings are passed in in the constructor.
+     * The bindings are an array with
+     * the even number entries being string <code>KeyStrokes</code>
+     * (eg "alt SPACE") and
+     * the odd number entries being the value to use in the
+     * <code>InputMap</code> (and the key in the <code>ActionMap</code>).
+     * @since 1.3
+     */
+    public static class LazyInputMap implements LazyValue {
+        /** Key bindings are registered under. */
+        private Object[] bindings;
+
+        public LazyInputMap(Object[] bindings) {
+            this.bindings = bindings;
+        }
+
+        /**
+         * Creates an <code>InputMap</code> with the bindings that are
+         * passed in.
+         *
+         * @param table a <code>UIDefaults</code> table
+         * @return the <code>InputMap</code>
+         */
+        public Object createValue(UIDefaults table) {
+            if (bindings != null) {
+                InputMap km = LookAndFeel.makeInputMap(bindings);
+                return km;
+            }
+            return null;
+        }
+    }
+}