| /* UIDefaults.java -- database for all settings and interface bindings. |
| Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. |
| |
| This file is part of GNU Classpath. |
| |
| GNU Classpath is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 2, or (at your option) |
| any later version. |
| |
| GNU Classpath 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 for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GNU Classpath; see the file COPYING. If not, write to the |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
| 02110-1301 USA. |
| |
| Linking this library statically or dynamically with other modules is |
| making a combined work based on this library. Thus, the terms and |
| conditions of the GNU General Public License cover the whole |
| combination. |
| |
| As a special exception, the copyright holders of this library give you |
| permission to link this library with independent modules to produce an |
| executable, regardless of the license terms of these independent |
| modules, and to copy and distribute the resulting executable under |
| terms of your choice, provided that you also meet, for each linked |
| independent module, the terms and conditions of the license of that |
| module. An independent module is a module which is not derived from |
| or based on this library. If you modify this library, you may extend |
| this exception to your version of the library, but you are not |
| obligated to do so. If you do not wish to do so, delete this |
| exception statement from your version. */ |
| |
| |
| package javax.swing; |
| |
| import java.awt.Color; |
| import java.awt.Dimension; |
| import java.awt.Font; |
| import java.awt.Insets; |
| import java.beans.PropertyChangeListener; |
| import java.beans.PropertyChangeSupport; |
| import java.lang.reflect.Method; |
| import java.util.Hashtable; |
| import java.util.LinkedList; |
| import java.util.ListIterator; |
| import java.util.Locale; |
| import java.util.MissingResourceException; |
| import java.util.ResourceBundle; |
| |
| import javax.swing.border.Border; |
| import javax.swing.plaf.ComponentUI; |
| import javax.swing.plaf.InputMapUIResource; |
| |
| /** |
| * UIDefaults is a database where all settings and interface bindings are |
| * stored into. A PLAF implementation fills one of these (see for example |
| * plaf/basic/BasicLookAndFeel.java) with "ButtonUI" -> new BasicButtonUI(). |
| * |
| * @author Ronald Veldema (rveldema@cs.vu.nl) |
| */ |
| public class UIDefaults extends Hashtable |
| { |
| |
| /** Our ResourceBundles. */ |
| private LinkedList bundles; |
| |
| /** The default locale. */ |
| private Locale defaultLocale; |
| |
| /** We use this for firing PropertyChangeEvents. */ |
| private PropertyChangeSupport propertyChangeSupport; |
| |
| /** |
| * Used for lazy instantiation of UIDefaults values so that they are not |
| * all loaded when a Swing application starts up, but only the values that |
| * are really needed. An <code>ActiveValue</code> is newly instantiated |
| * every time when the value is requested, as opposed to the normal |
| * {@link LazyValue} that is only instantiated once. |
| */ |
| public static interface ActiveValue |
| { |
| Object createValue(UIDefaults table); |
| } |
| |
| public static class LazyInputMap implements LazyValue |
| { |
| Object[] bind; |
| public LazyInputMap(Object[] bindings) |
| { |
| bind = bindings; |
| } |
| public Object createValue(UIDefaults table) |
| { |
| InputMapUIResource im = new InputMapUIResource(); |
| for (int i = 0; 2 * i + 1 < bind.length; ++i) |
| { |
| Object curr = bind[2 * i]; |
| if (curr instanceof KeyStroke) |
| im.put((KeyStroke) curr, bind[2 * i + 1]); |
| else |
| im.put(KeyStroke.getKeyStroke((String) curr), |
| bind[2 * i + 1]); |
| } |
| return im; |
| } |
| } |
| |
| /** |
| * Used for lazy instantiation of UIDefaults values so that they are not |
| * all loaded when a Swing application starts up, but only the values that |
| * are really needed. A <code>LazyValue</code> is only instantiated once, |
| * as opposed to the {@link ActiveValue} that is newly created every time |
| * it is requested. |
| */ |
| public static interface LazyValue |
| { |
| Object createValue(UIDefaults table); |
| } |
| |
| public static class ProxyLazyValue implements LazyValue |
| { |
| LazyValue inner; |
| public ProxyLazyValue(String s) |
| { |
| final String className = s; |
| inner = new LazyValue() |
| { |
| public Object createValue(UIDefaults table) |
| { |
| try |
| { |
| return Class |
| .forName(className) |
| .getConstructor(new Class[] {}) |
| .newInstance(new Object[] {}); |
| } |
| catch (Exception e) |
| { |
| return null; |
| } |
| } |
| }; |
| } |
| |
| public ProxyLazyValue(String c, String m) |
| { |
| final String className = c; |
| final String methodName = m; |
| inner = new LazyValue() |
| { |
| public Object createValue(UIDefaults table) |
| { |
| try |
| { |
| return Class |
| .forName(className) |
| .getMethod(methodName, new Class[] {}) |
| .invoke(null, new Object[] {}); |
| } |
| catch (Exception e) |
| { |
| return null; |
| } |
| } |
| }; |
| } |
| |
| public ProxyLazyValue(String c, Object[] os) |
| { |
| final String className = c; |
| final Object[] objs = os; |
| final Class[] clss = new Class[objs.length]; |
| for (int i = 0; i < objs.length; ++i) |
| { |
| clss[i] = objs[i].getClass(); |
| } |
| inner = new LazyValue() |
| { |
| public Object createValue(UIDefaults table) |
| { |
| try |
| { |
| return Class |
| .forName(className) |
| .getConstructor(clss) |
| .newInstance(objs); |
| } |
| catch (Exception e) |
| { |
| return null; |
| } |
| } |
| }; |
| } |
| |
| public ProxyLazyValue(String c, String m, Object[] os) |
| { |
| final String className = c; |
| final String methodName = m; |
| final Object[] objs = os; |
| final Class[] clss = new Class[objs.length]; |
| for (int i = 0; i < objs.length; ++i) |
| { |
| clss[i] = objs[i].getClass(); |
| } |
| inner = new LazyValue() |
| { |
| public Object createValue(UIDefaults table) |
| { |
| try |
| { |
| return Class |
| .forName(className) |
| .getMethod(methodName, clss) |
| .invoke(null, objs); |
| } |
| catch (Exception e) |
| { |
| return null; |
| } |
| } |
| }; |
| } |
| |
| public Object createValue(UIDefaults table) |
| { |
| return inner.createValue(table); |
| } |
| } |
| |
| /** Our serialVersionUID for serialization. */ |
| private static final long serialVersionUID = 7341222528856548117L; |
| |
| /** |
| * Constructs a new empty UIDefaults instance. |
| */ |
| public UIDefaults() |
| { |
| bundles = new LinkedList(); |
| defaultLocale = Locale.getDefault(); |
| propertyChangeSupport = new PropertyChangeSupport(this); |
| } |
| |
| /** |
| * Constructs a new UIDefaults instance and loads the specified entries. |
| * The entries are expected to come in pairs, that means |
| * <code>entries[0]</code> is a key, <code>entries[1]</code> is a value, |
| * <code>entries[2]</code> a key and so forth. |
| * |
| * @param entries the entries to initialize the UIDefaults instance with |
| */ |
| public UIDefaults(Object[] entries) |
| { |
| this(); |
| |
| for (int i = 0; (2 * i + 1) < entries.length; ++i) |
| put(entries[2 * i], entries[2 * i + 1]); |
| } |
| |
| /** |
| * Returns the entry for the specified <code>key</code> in the default |
| * locale. |
| * |
| * @return the entry for the specified <code>key</code> |
| */ |
| public Object get(Object key) |
| { |
| return this.get(key, getDefaultLocale()); |
| } |
| |
| /** |
| * Returns the entry for the specified <code>key</code> in the Locale |
| * <code>loc</code>. |
| * |
| * @param key the key for which we return the value |
| * @param loc the locale |
| */ |
| public Object get(Object key, Locale loc) |
| { |
| Object obj = null; |
| |
| if (super.containsKey(key)) |
| { |
| obj = super.get(key); |
| } |
| else if (key instanceof String) |
| { |
| String keyString = (String) key; |
| ListIterator i = bundles.listIterator(0); |
| while (i.hasNext()) |
| { |
| String bundle_name = (String) i.next(); |
| ResourceBundle res = |
| ResourceBundle.getBundle(bundle_name, loc); |
| if (res != null) |
| { |
| try |
| { |
| obj = res.getObject(keyString); |
| break; |
| } |
| catch (MissingResourceException me) |
| { |
| // continue, this bundle has no such key |
| } |
| } |
| } |
| } |
| |
| // now we've found the object, resolve it. |
| // nb: LazyValues aren't supported in resource bundles, so it's correct |
| // to insert their results in the locale-less hashtable. |
| |
| if (obj == null) |
| return null; |
| |
| if (obj instanceof LazyValue) |
| { |
| Object resolved = ((LazyValue) obj).createValue(this); |
| super.remove(key); |
| super.put(key, resolved); |
| return resolved; |
| } |
| else if (obj instanceof ActiveValue) |
| { |
| return ((ActiveValue) obj).createValue(this); |
| } |
| |
| return obj; |
| } |
| |
| /** |
| * Puts a key and value into this UIDefaults object.<br> |
| * In contrast to |
| * {@link java.util.Hashtable}s <code>null</code>-values are accepted |
| * here and treated like #remove(key). |
| * <br> |
| * This fires a PropertyChangeEvent with key as name and the old and new |
| * values. |
| * |
| * @param key the key to put into the map |
| * @param value the value to put into the map |
| * |
| * @return the old value for key or <code>null</code> if <code>key</code> |
| * had no value assigned |
| */ |
| public Object put(Object key, Object value) |
| { |
| Object old = checkAndPut(key, value); |
| |
| if (key instanceof String && old != value) |
| firePropertyChange((String) key, old, value); |
| return old; |
| } |
| |
| /** |
| * Puts a set of key-value pairs into the map. |
| * The entries are expected to come in pairs, that means |
| * <code>entries[0]</code> is a key, <code>entries[1]</code> is a value, |
| * <code>entries[2]</code> a key and so forth. |
| * <br> |
| * If a value is <code>null</code> it is treated like #remove(key). |
| * <br> |
| * This unconditionally fires a PropertyChangeEvent with |
| * <code>'UIDefaults'</code> as name and <code>null</code> for |
| * old and new value. |
| * |
| * @param entries the entries to be put into the map |
| */ |
| public void putDefaults(Object[] entries) |
| { |
| for (int i = 0; (2 * i + 1) < entries.length; ++i) |
| { |
| checkAndPut(entries[2 * i], entries[2 * i + 1]); |
| } |
| firePropertyChange("UIDefaults", null, null); |
| } |
| |
| /** |
| * Checks the value for <code>null</code> and put it into the Hashtable, if |
| * it is not <code>null</code>. If the value is <code>null</code> then |
| * remove the corresponding key. |
| * |
| * @param key the key to put into this UIDefauls table |
| * @param value the value to put into this UIDefaults table |
| * |
| * @return the old value for <code>key</code> |
| */ |
| private Object checkAndPut(Object key, Object value) |
| { |
| Object old; |
| |
| if (value != null) |
| old = super.put(key, value); |
| else |
| old = super.remove(key); |
| |
| return old; |
| } |
| |
| /** |
| * Returns a font entry for the default locale. |
| * |
| * @param key the key to the requested entry |
| * |
| * @return the font entry for <code>key</code> or null if no such entry |
| * exists |
| */ |
| public Font getFont(Object key) |
| { |
| Object o = get(key); |
| return o instanceof Font ? (Font) o : null; |
| } |
| |
| /** |
| * Returns a font entry for a specic locale. |
| * |
| * @param key the key to the requested entry |
| * @param locale the locale to the requested entry |
| * |
| * @return the font entry for <code>key</code> or null if no such entry |
| * exists |
| */ |
| public Font getFont(Object key, Locale locale) |
| { |
| Object o = get(key, locale); |
| return o instanceof Font ? (Font) o : null; |
| } |
| |
| /** |
| * Returns a color entry for the default locale. |
| * |
| * @param key the key to the requested entry |
| * |
| * @return the color entry for <code>key</code> or null if no such entry |
| * exists |
| */ |
| public Color getColor(Object key) |
| { |
| Object o = get(key); |
| return o instanceof Color ? (Color) o : null; |
| } |
| |
| /** |
| * Returns a color entry for a specic locale. |
| * |
| * @param key the key to the requested entry |
| * @param locale the locale to the requested entry |
| * |
| * @return the color entry for <code>key</code> or null if no such entry |
| * exists |
| */ |
| public Color getColor(Object key, Locale locale) |
| { |
| Object o = get(key, locale); |
| return o instanceof Color ? (Color) o : null; |
| } |
| |
| /** |
| * Returns an icon entry for the default locale. |
| * |
| * @param key the key to the requested entry |
| * |
| * @return the icon entry for <code>key</code> or null if no such entry |
| * exists |
| */ |
| public Icon getIcon(Object key) |
| { |
| Object o = get(key); |
| return o instanceof Icon ? (Icon) o : null; |
| } |
| |
| /** |
| * Returns an icon entry for a specic locale. |
| * |
| * @param key the key to the requested entry |
| * @param locale the locale to the requested entry |
| * |
| * @return the icon entry for <code>key</code> or null if no such entry |
| * exists |
| */ |
| public Icon getIcon(Object key, Locale locale) |
| { |
| Object o = get(key, locale); |
| return o instanceof Icon ? (Icon) o : null; |
| } |
| |
| /** |
| * Returns a border entry for the default locale. |
| * |
| * @param key the key to the requested entry |
| * |
| * @return the border entry for <code>key</code> or null if no such entry |
| * exists |
| */ |
| public Border getBorder(Object key) |
| { |
| Object o = get(key); |
| return o instanceof Border ? (Border) o : null; |
| } |
| |
| /** |
| * Returns a border entry for a specic locale. |
| * |
| * @param key the key to the requested entry |
| * @param locale the locale to the requested entry |
| * |
| * @return the border entry for <code>key</code> or null if no such entry |
| * exists |
| */ |
| public Border getBorder(Object key, Locale locale) |
| { |
| Object o = get(key, locale); |
| return o instanceof Border ? (Border) o : null; |
| } |
| |
| /** |
| * Returns a string entry for the default locale. |
| * |
| * @param key the key to the requested entry |
| * |
| * @return the string entry for <code>key</code> or null if no such entry |
| * exists |
| */ |
| public String getString(Object key) |
| { |
| Object o = get(key); |
| return o instanceof String ? (String) o : null; |
| } |
| |
| /** |
| * Returns a string entry for a specic locale. |
| * |
| * @param key the key to the requested entry |
| * @param locale the locale to the requested entry |
| * |
| * @return the string entry for <code>key</code> or null if no such entry |
| * exists |
| */ |
| public String getString(Object key, Locale locale) |
| { |
| Object o = get(key, locale); |
| return o instanceof String ? (String) o : null; |
| } |
| |
| /** |
| * Returns an integer entry for the default locale. |
| * |
| * @param key the key to the requested entry |
| * |
| * @return the integer entry for <code>key</code> or null if no such entry |
| * exists |
| */ |
| public int getInt(Object key) |
| { |
| Object o = get(key); |
| return o instanceof Integer ? ((Integer) o).intValue() : 0; |
| } |
| |
| /** |
| * Returns an integer entry for a specic locale. |
| * |
| * @param key the key to the requested entry |
| * @param locale the locale to the requested entry |
| * |
| * @return the integer entry for <code>key</code> or null if no such entry |
| * exists |
| */ |
| public int getInt(Object key, Locale locale) |
| { |
| Object o = get(key, locale); |
| return o instanceof Integer ? ((Integer) o).intValue() : 0; |
| } |
| |
| /** |
| * Returns a boolean entry for the default locale. |
| * |
| * @param key the key to the requested entry |
| * |
| * @return The boolean entry for <code>key</code> or <code>false</code> if no |
| * such entry exists. |
| */ |
| public boolean getBoolean(Object key) |
| { |
| return Boolean.TRUE.equals(get(key)); |
| } |
| |
| /** |
| * Returns a boolean entry for a specic locale. |
| * |
| * @param key the key to the requested entry |
| * @param locale the locale to the requested entry |
| * |
| * @return the boolean entry for <code>key</code> or null if no such entry |
| * exists |
| */ |
| public boolean getBoolean(Object key, Locale locale) |
| { |
| return Boolean.TRUE.equals(get(key, locale)); |
| } |
| |
| /** |
| * Returns an insets entry for the default locale. |
| * |
| * @param key the key to the requested entry |
| * |
| * @return the insets entry for <code>key</code> or null if no such entry |
| * exists |
| */ |
| public Insets getInsets(Object key) |
| { |
| Object o = get(key); |
| return o instanceof Insets ? (Insets) o : null; |
| } |
| |
| /** |
| * Returns an insets entry for a specic locale. |
| * |
| * @param key the key to the requested entry |
| * @param locale the locale to the requested entry |
| * |
| * @return the boolean entry for <code>key</code> or null if no such entry |
| * exists |
| */ |
| public Insets getInsets(Object key, Locale locale) |
| { |
| Object o = get(key, locale); |
| return o instanceof Insets ? (Insets) o : null; |
| } |
| |
| /** |
| * Returns a dimension entry for the default locale. |
| * |
| * @param key the key to the requested entry |
| * |
| * @return the dimension entry for <code>key</code> or null if no such entry |
| * exists |
| */ |
| public Dimension getDimension(Object key) |
| { |
| Object o = get(key); |
| return o instanceof Dimension ? (Dimension) o : null; |
| } |
| |
| /** |
| * Returns a dimension entry for a specic locale. |
| * |
| * @param key the key to the requested entry |
| * @param locale the locale to the requested entry |
| * |
| * @return the boolean entry for <code>key</code> or null if no such entry |
| * exists |
| */ |
| public Dimension getDimension(Object key, Locale locale) |
| { |
| Object o = get(key, locale); |
| return o instanceof Dimension ? (Dimension) o : null; |
| } |
| |
| /** |
| * Returns the ComponentUI class that renders a component. <code>id</code> |
| * is the ID for which the String value of the classname is stored in |
| * this UIDefaults map. |
| * |
| * @param id the ID of the UI class |
| * @param loader the ClassLoader to use |
| * |
| * @return the UI class for <code>id</code> |
| */ |
| public Class getUIClass(String id, ClassLoader loader) |
| { |
| String className = (String) get(id); |
| if (className == null) |
| return null; |
| try |
| { |
| if (loader == null) |
| loader = ClassLoader.getSystemClassLoader(); |
| return loader.loadClass (className); |
| } |
| catch (Exception e) |
| { |
| return null; |
| } |
| } |
| |
| /** |
| * Returns the ComponentUI class that renders a component. <code>id</code> |
| * is the ID for which the String value of the classname is stored in |
| * this UIDefaults map. |
| * |
| * @param id the ID of the UI class |
| * |
| * @return the UI class for <code>id</code> |
| */ |
| public Class getUIClass(String id) |
| { |
| return getUIClass (id, null); |
| } |
| |
| /** |
| * If a key is requested in #get(key) that has no value, this method |
| * is called before returning <code>null</code>. |
| * |
| * @param msg the error message |
| */ |
| protected void getUIError(String msg) |
| { |
| System.err.println ("UIDefaults.getUIError: " + msg); |
| } |
| |
| /** |
| * Returns the {@link ComponentUI} for the specified {@link JComponent}. |
| * |
| * @param target the component for which the ComponentUI is requested |
| * |
| * @return the {@link ComponentUI} for the specified {@link JComponent} |
| */ |
| public ComponentUI getUI(JComponent target) |
| { |
| String classId = target.getUIClassID (); |
| Class cls = getUIClass (classId); |
| if (cls == null) |
| { |
| getUIError ("failed to locate UI class:" + classId); |
| return null; |
| } |
| |
| Method factory; |
| |
| try |
| { |
| factory = cls.getMethod ("createUI", new Class[] { JComponent.class } ); |
| } |
| catch (NoSuchMethodException nme) |
| { |
| getUIError ("failed to locate createUI method on " + cls.toString ()); |
| return null; |
| } |
| |
| try |
| { |
| return (ComponentUI) factory.invoke (null, new Object[] { target }); |
| } |
| catch (java.lang.reflect.InvocationTargetException ite) |
| { |
| getUIError ("InvocationTargetException ("+ ite.getTargetException() |
| +") calling createUI(...) on " + cls.toString ()); |
| return null; |
| } |
| catch (Exception e) |
| { |
| getUIError ("exception calling createUI(...) on " + cls.toString ()); |
| return null; |
| } |
| } |
| |
| /** |
| * Adds a {@link PropertyChangeListener} to this UIDefaults map. |
| * Registered PropertyChangeListener are notified when values |
| * are beeing put into this UIDefaults map. |
| * |
| * @param listener the PropertyChangeListener to add |
| */ |
| public void addPropertyChangeListener(PropertyChangeListener listener) |
| { |
| propertyChangeSupport.addPropertyChangeListener(listener); |
| } |
| |
| /** |
| * Removes a PropertyChangeListener from this UIDefaults map. |
| * |
| * @param listener the PropertyChangeListener to remove |
| */ |
| public void removePropertyChangeListener(PropertyChangeListener listener) |
| { |
| propertyChangeSupport.removePropertyChangeListener(listener); |
| } |
| |
| /** |
| * Returns an array of all registered PropertyChangeListeners. |
| * |
| * @return all registered PropertyChangeListeners |
| */ |
| public PropertyChangeListener[] getPropertyChangeListeners() |
| { |
| return propertyChangeSupport.getPropertyChangeListeners(); |
| } |
| |
| /** |
| * Fires a PropertyChangeEvent. |
| * |
| * @param property the property name |
| * @param oldValue the old value |
| * @param newValue the new value |
| */ |
| protected void firePropertyChange(String property, |
| Object oldValue, Object newValue) |
| { |
| propertyChangeSupport.firePropertyChange(property, oldValue, newValue); |
| } |
| |
| /** |
| * Adds a ResourceBundle for localized values. |
| * |
| * @param name the name of the ResourceBundle to add |
| */ |
| public void addResourceBundle(String name) |
| { |
| bundles.addFirst(name); |
| } |
| |
| /** |
| * Removes a ResourceBundle. |
| * |
| * @param name the name of the ResourceBundle to remove |
| */ |
| public void removeResourceBundle(String name) |
| { |
| bundles.remove(name); |
| } |
| |
| /** |
| * Sets the current locale to <code>loc</code>. |
| * |
| * @param loc the Locale to be set |
| */ |
| public void setDefaultLocale(Locale loc) |
| { |
| defaultLocale = loc; |
| } |
| |
| /** |
| * Returns the current default locale. |
| * |
| * @return the current default locale |
| */ |
| public Locale getDefaultLocale() |
| { |
| return defaultLocale; |
| } |
| } |