0001 /*
0002 * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved.
0003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004 *
0005 * This code is free software; you can redistribute it and/or modify it
0006 * under the terms of the GNU General Public License version 2 only, as
0007 * published by the Free Software Foundation. Sun designates this
0008 * particular file as subject to the "Classpath" exception as provided
0009 * by Sun in the LICENSE file that accompanied this code.
0010 *
0011 * This code is distributed in the hope that it will be useful, but WITHOUT
0012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0014 * version 2 for more details (a copy is included in the LICENSE file that
0015 * accompanied this code).
0016 *
0017 * You should have received a copy of the GNU General Public License version
0018 * 2 along with this work; if not, write to the Free Software Foundation,
0019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020 *
0021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022 * CA 95054 USA or visit www.sun.com if you need additional information or
0023 * have any questions.
0024 */
0025
0026 package javax.swing;
0027
0028 import javax.swing.plaf.ComponentUI;
0029 import javax.swing.border.*;
0030 import javax.swing.event.SwingPropertyChangeSupport;
0031
0032 import java.lang.reflect.*;
0033 import java.util.HashMap;
0034 import java.util.Map;
0035 import java.util.Enumeration;
0036 import java.util.Hashtable;
0037 import java.util.ResourceBundle;
0038 import java.util.ResourceBundle.Control;
0039 import java.util.Locale;
0040 import java.util.Vector;
0041 import java.util.MissingResourceException;
0042 import java.awt.Font;
0043 import java.awt.Color;
0044 import java.awt.Insets;
0045 import java.awt.Dimension;
0046 import java.lang.reflect.Method;
0047 import java.beans.PropertyChangeListener;
0048 import java.beans.PropertyChangeEvent;
0049 import java.security.AccessController;
0050 import java.security.AccessControlContext;
0051 import java.security.PrivilegedAction;
0052
0053 import sun.reflect.misc.MethodUtil;
0054 import sun.util.CoreResourceBundleControl;
0055
0056 /**
0057 * A table of defaults for Swing components. Applications can set/get
0058 * default values via the <code>UIManager</code>.
0059 * <p>
0060 * <strong>Warning:</strong>
0061 * Serialized objects of this class will not be compatible with
0062 * future Swing releases. The current serialization support is
0063 * appropriate for short term storage or RMI between applications running
0064 * the same version of Swing. As of 1.4, support for long term storage
0065 * of all JavaBeans<sup><font size="-2">TM</font></sup>
0066 * has been added to the <code>java.beans</code> package.
0067 * Please see {@link java.beans.XMLEncoder}.
0068 *
0069 * @see UIManager
0070 * @version 1.70 05/10/07
0071 * @author Hans Muller
0072 */
0073 public class UIDefaults extends Hashtable<Object, Object> {
0074 private static final Object PENDING = new String("Pending");
0075
0076 private SwingPropertyChangeSupport changeSupport;
0077
0078 private Vector resourceBundles;
0079
0080 private Locale defaultLocale = Locale.getDefault();
0081
0082 /**
0083 * Maps from a Locale to a cached Map of the ResourceBundle. This is done
0084 * so as to avoid an exception being thrown when a value is asked for.
0085 * Access to this should be done while holding a lock on the
0086 * UIDefaults, eg synchronized(this).
0087 */
0088 private Map resourceCache;
0089
0090 /**
0091 * Creates an empty defaults table.
0092 */
0093 public UIDefaults() {
0094 this (700, .75f);
0095 }
0096
0097 /**
0098 * Creates an empty defaults table with the specified initial capacity and
0099 * load factor.
0100 *
0101 * @param initialCapacity the initial capacity of the defaults table
0102 * @param loadFactor the load factor of the defaults table
0103 * @see java.util.Hashtable
0104 * @since 1.6
0105 */
0106 public UIDefaults(int initialCapacity, float loadFactor) {
0107 super (initialCapacity, loadFactor);
0108 resourceCache = new HashMap();
0109 }
0110
0111 /**
0112 * Creates a defaults table initialized with the specified
0113 * key/value pairs. For example:
0114 * <pre>
0115 Object[] uiDefaults = {
0116 "Font", new Font("Dialog", Font.BOLD, 12),
0117 "Color", Color.red,
0118 "five", new Integer(5)
0119 }
0120 UIDefaults myDefaults = new UIDefaults(uiDefaults);
0121 * </pre>
0122 * @param keyValueList an array of objects containing the key/value
0123 * pairs
0124 */
0125 public UIDefaults(Object[] keyValueList) {
0126 super (keyValueList.length / 2);
0127 for (int i = 0; i < keyValueList.length; i += 2) {
0128 super .put(keyValueList[i], keyValueList[i + 1]);
0129 }
0130 }
0131
0132 /**
0133 * Returns the value for key. If the value is a
0134 * <code>UIDefaults.LazyValue</code> then the real
0135 * value is computed with <code>LazyValue.createValue()</code>,
0136 * the table entry is replaced, and the real value is returned.
0137 * If the value is an <code>UIDefaults.ActiveValue</code>
0138 * the table entry is not replaced - the value is computed
0139 * with <code>ActiveValue.createValue()</code> for each
0140 * <code>get()</code> call.
0141 *
0142 * If the key is not found in the table then it is searched for in the list
0143 * of resource bundles maintained by this object. The resource bundles are
0144 * searched most recently added first using the locale returned by
0145 * <code>getDefaultLocale</code>. <code>LazyValues</code> and
0146 * <code>ActiveValues</code> are not supported in the resource bundles.
0147
0148 *
0149 * @param key the desired key
0150 * @return the value for <code>key</code>
0151 * @see LazyValue
0152 * @see ActiveValue
0153 * @see java.util.Hashtable#get
0154 * @see #getDefaultLocale
0155 * @see #addResourceBundle
0156 * @since 1.4
0157 */
0158 public Object get(Object key) {
0159 Object value = getFromHashtable(key);
0160 return (value != null) ? value : getFromResourceBundle(key,
0161 null);
0162 }
0163
0164 /**
0165 * Looks up up the given key in our Hashtable and resolves LazyValues
0166 * or ActiveValues.
0167 */
0168 private Object getFromHashtable(Object key) {
0169 /* Quickly handle the common case, without grabbing
0170 * a lock.
0171 */
0172 Object value = super .get(key);
0173 if ((value != PENDING) && !(value instanceof ActiveValue)
0174 && !(value instanceof LazyValue)) {
0175 return value;
0176 }
0177
0178 /* If the LazyValue for key is being constructed by another
0179 * thread then wait and then return the new value, otherwise drop
0180 * the lock and construct the ActiveValue or the LazyValue.
0181 * We use the special value PENDING to mark LazyValues that
0182 * are being constructed.
0183 */
0184 synchronized (this ) {
0185 value = super .get(key);
0186 if (value == PENDING) {
0187 do {
0188 try {
0189 this .wait();
0190 } catch (InterruptedException e) {
0191 }
0192 value = super .get(key);
0193 } while (value == PENDING);
0194 return value;
0195 } else if (value instanceof LazyValue) {
0196 super .put(key, PENDING);
0197 } else if (!(value instanceof ActiveValue)) {
0198 return value;
0199 }
0200 }
0201
0202 /* At this point we know that the value of key was
0203 * a LazyValue or an ActiveValue.
0204 */
0205 if (value instanceof LazyValue) {
0206 try {
0207 /* If an exception is thrown we'll just put the LazyValue
0208 * back in the table.
0209 */
0210 value = ((LazyValue) value).createValue(this );
0211 } finally {
0212 synchronized (this ) {
0213 if (value == null) {
0214 super .remove(key);
0215 } else {
0216 super .put(key, value);
0217 }
0218 this .notifyAll();
0219 }
0220 }
0221 } else {
0222 value = ((ActiveValue) value).createValue(this );
0223 }
0224
0225 return value;
0226 }
0227
0228 /**
0229 * Returns the value for key associated with the given locale.
0230 * If the value is a <code>UIDefaults.LazyValue</code> then the real
0231 * value is computed with <code>LazyValue.createValue()</code>,
0232 * the table entry is replaced, and the real value is returned.
0233 * If the value is an <code>UIDefaults.ActiveValue</code>
0234 * the table entry is not replaced - the value is computed
0235 * with <code>ActiveValue.createValue()</code> for each
0236 * <code>get()</code> call.
0237 *
0238 * If the key is not found in the table then it is searched for in the list
0239 * of resource bundles maintained by this object. The resource bundles are
0240 * searched most recently added first using the given locale.
0241 * <code>LazyValues</code> and <code>ActiveValues</code> are not supported
0242 * in the resource bundles.
0243 *
0244 * @param key the desired key
0245 * @param l the desired <code>locale</code>
0246 * @return the value for <code>key</code>
0247 * @see LazyValue
0248 * @see ActiveValue
0249 * @see java.util.Hashtable#get
0250 * @see #addResourceBundle
0251 * @since 1.4
0252 */
0253 public Object get(Object key, Locale l) {
0254 Object value = getFromHashtable(key);
0255 return (value != null) ? value : getFromResourceBundle(key, l);
0256 }
0257
0258 /**
0259 * Looks up given key in our resource bundles.
0260 */
0261 private Object getFromResourceBundle(Object key, Locale l) {
0262
0263 if (resourceBundles == null || resourceBundles.isEmpty()
0264 || !(key instanceof String)) {
0265 return null;
0266 }
0267
0268 // A null locale means use the default locale.
0269 if (l == null) {
0270 if (defaultLocale == null)
0271 return null;
0272 else
0273 l = (Locale) defaultLocale;
0274 }
0275
0276 synchronized (this ) {
0277 return getResourceCache(l).get((String) key);
0278 }
0279 }
0280
0281 /**
0282 * Returns a Map of the known resources for the given locale.
0283 */
0284 private Map getResourceCache(Locale l) {
0285 Map values = (Map) resourceCache.get(l);
0286
0287 if (values == null) {
0288 values = new HashMap();
0289 for (int i = resourceBundles.size() - 1; i >= 0; i--) {
0290 String bundleName = (String) resourceBundles.get(i);
0291 try {
0292 Control c = CoreResourceBundleControl
0293 .getRBControlInstance(bundleName);
0294 ResourceBundle b;
0295 if (c != null) {
0296 b = ResourceBundle.getBundle(bundleName, l, c);
0297 } else {
0298 b = ResourceBundle.getBundle(bundleName, l);
0299 }
0300 Enumeration keys = b.getKeys();
0301
0302 while (keys.hasMoreElements()) {
0303 String key = (String) keys.nextElement();
0304
0305 if (values.get(key) == null) {
0306 Object value = b.getObject(key);
0307
0308 values.put(key, value);
0309 }
0310 }
0311 } catch (MissingResourceException mre) {
0312 // Keep looking
0313 }
0314 }
0315 resourceCache.put(l, values);
0316 }
0317 return values;
0318 }
0319
0320 /**
0321 * Sets the value of <code>key</code> to <code>value</code> for all locales.
0322 * If <code>key</code> is a string and the new value isn't
0323 * equal to the old one, fire a <code>PropertyChangeEvent</code>.
0324 * If value is <code>null</code>, the key is removed from the table.
0325 *
0326 * @param key the unique <code>Object</code> who's value will be used
0327 * to retrieve the data value associated with it
0328 * @param value the new <code>Object</code> to store as data under
0329 * that key
0330 * @return the previous <code>Object</code> value, or <code>null</code>
0331 * @see #putDefaults
0332 * @see java.util.Hashtable#put
0333 */
0334 public Object put(Object key, Object value) {
0335 Object oldValue = (value == null) ? super .remove(key) : super
0336 .put(key, value);
0337 if (key instanceof String) {
0338 firePropertyChange((String) key, oldValue, value);
0339 }
0340 return oldValue;
0341 }
0342
0343 /**
0344 * Puts all of the key/value pairs in the database and
0345 * unconditionally generates one <code>PropertyChangeEvent</code>.
0346 * The events oldValue and newValue will be <code>null</code> and its
0347 * <code>propertyName</code> will be "UIDefaults". The key/value pairs are
0348 * added for all locales.
0349 *
0350 * @param keyValueList an array of key/value pairs
0351 * @see #put
0352 * @see java.util.Hashtable#put
0353 */
0354 public void putDefaults(Object[] keyValueList) {
0355 for (int i = 0, max = keyValueList.length; i < max; i += 2) {
0356 Object value = keyValueList[i + 1];
0357 if (value == null) {
0358 super .remove(keyValueList[i]);
0359 } else {
0360 super .put(keyValueList[i], value);
0361 }
0362 }
0363 firePropertyChange("UIDefaults", null, null);
0364 }
0365
0366 /**
0367 * If the value of <code>key</code> is a <code>Font</code> return it,
0368 * otherwise return <code>null</code>.
0369 * @param key the desired key
0370 * @return if the value for <code>key</code> is a <code>Font</code>,
0371 * return the <code>Font</code> object; otherwise return
0372 * <code>null</code>
0373 */
0374 public Font getFont(Object key) {
0375 Object value = get(key);
0376 return (value instanceof Font) ? (Font) value : null;
0377 }
0378
0379 /**
0380 * If the value of <code>key</code> for the given <code>Locale</code>
0381 * is a <code>Font</code> return it, otherwise return <code>null</code>.
0382 * @param key the desired key
0383 * @param l the desired locale
0384 * @return if the value for <code>key</code> and <code>Locale</code>
0385 * is a <code>Font</code>,
0386 * return the <code>Font</code> object; otherwise return
0387 * <code>null</code>
0388 * @since 1.4
0389 */
0390 public Font getFont(Object key, Locale l) {
0391 Object value = get(key, l);
0392 return (value instanceof Font) ? (Font) value : null;
0393 }
0394
0395 /**
0396 * If the value of <code>key</code> is a <code>Color</code> return it,
0397 * otherwise return <code>null</code>.
0398 * @param key the desired key
0399 * @return if the value for <code>key</code> is a <code>Color</code>,
0400 * return the <code>Color</code> object; otherwise return
0401 * <code>null</code>
0402 */
0403 public Color getColor(Object key) {
0404 Object value = get(key);
0405 return (value instanceof Color) ? (Color) value : null;
0406 }
0407
0408 /**
0409 * If the value of <code>key</code> for the given <code>Locale</code>
0410 * is a <code>Color</code> return it, otherwise return <code>null</code>.
0411 * @param key the desired key
0412 * @param l the desired locale
0413 * @return if the value for <code>key</code> and <code>Locale</code>
0414 * is a <code>Color</code>,
0415 * return the <code>Color</code> object; otherwise return
0416 * <code>null</code>
0417 * @since 1.4
0418 */
0419 public Color getColor(Object key, Locale l) {
0420 Object value = get(key, l);
0421 return (value instanceof Color) ? (Color) value : null;
0422 }
0423
0424 /**
0425 * If the value of <code>key</code> is an <code>Icon</code> return it,
0426 * otherwise return <code>null</code>.
0427 * @param key the desired key
0428 * @return if the value for <code>key</code> is an <code>Icon</code>,
0429 * return the <code>Icon</code> object; otherwise return
0430 * <code>null</code>
0431 */
0432 public Icon getIcon(Object key) {
0433 Object value = get(key);
0434 return (value instanceof Icon) ? (Icon) value : null;
0435 }
0436
0437 /**
0438 * If the value of <code>key</code> for the given <code>Locale</code>
0439 * is an <code>Icon</code> return it, otherwise return <code>null</code>.
0440 * @param key the desired key
0441 * @param l the desired locale
0442 * @return if the value for <code>key</code> and <code>Locale</code>
0443 * is an <code>Icon</code>,
0444 * return the <code>Icon</code> object; otherwise return
0445 * <code>null</code>
0446 * @since 1.4
0447 */
0448 public Icon getIcon(Object key, Locale l) {
0449 Object value = get(key, l);
0450 return (value instanceof Icon) ? (Icon) value : null;
0451 }
0452
0453 /**
0454 * If the value of <code>key</code> is a <code>Border</code> return it,
0455 * otherwise return <code>null</code>.
0456 * @param key the desired key
0457 * @return if the value for <code>key</code> is a <code>Border</code>,
0458 * return the <code>Border</code> object; otherwise return
0459 * <code>null</code>
0460 */
0461 public Border getBorder(Object key) {
0462 Object value = get(key);
0463 return (value instanceof Border) ? (Border) value : null;
0464 }
0465
0466 /**
0467 * If the value of <code>key</code> for the given <code>Locale</code>
0468 * is a <code>Border</code> return it, otherwise return <code>null</code>.
0469 * @param key the desired key
0470 * @param l the desired locale
0471 * @return if the value for <code>key</code> and <code>Locale</code>
0472 * is a <code>Border</code>,
0473 * return the <code>Border</code> object; otherwise return
0474 * <code>null</code>
0475 * @since 1.4
0476 */
0477 public Border getBorder(Object key, Locale l) {
0478 Object value = get(key, l);
0479 return (value instanceof Border) ? (Border) value : null;
0480 }
0481
0482 /**
0483 * If the value of <code>key</code> is a <code>String</code> return it,
0484 * otherwise return <code>null</code>.
0485 * @param key the desired key
0486 * @return if the value for <code>key</code> is a <code>String</code>,
0487 * return the <code>String</code> object; otherwise return
0488 * <code>null</code>
0489 */
0490 public String getString(Object key) {
0491 Object value = get(key);
0492 return (value instanceof String) ? (String) value : null;
0493 }
0494
0495 /**
0496 * If the value of <code>key</code> for the given <code>Locale</code>
0497 * is a <code>String</code> return it, otherwise return <code>null</code>.
0498 * @param key the desired key
0499 * @param l the desired <code>Locale</code>
0500 * @return if the value for <code>key</code> for the given
0501 * <code>Locale</code> is a <code>String</code>,
0502 * return the <code>String</code> object; otherwise return
0503 * <code>null</code>
0504 * @since 1.4
0505 */
0506 public String getString(Object key, Locale l) {
0507 Object value = get(key, l);
0508 return (value instanceof String) ? (String) value : null;
0509 }
0510
0511 /**
0512 * If the value of <code>key</code> is an <code>Integer</code> return its
0513 * integer value, otherwise return 0.
0514 * @param key the desired key
0515 * @return if the value for <code>key</code> is an <code>Integer</code>,
0516 * return its value, otherwise return 0
0517 */
0518 public int getInt(Object key) {
0519 Object value = get(key);
0520 return (value instanceof Integer) ? ((Integer) value)
0521 .intValue() : 0;
0522 }
0523
0524 /**
0525 * If the value of <code>key</code> for the given <code>Locale</code>
0526 * is an <code>Integer</code> return its integer value, otherwise return 0.
0527 * @param key the desired key
0528 * @param l the desired locale
0529 * @return if the value for <code>key</code> and <code>Locale</code>
0530 * is an <code>Integer</code>,
0531 * return its value, otherwise return 0
0532 * @since 1.4
0533 */
0534 public int getInt(Object key, Locale l) {
0535 Object value = get(key, l);
0536 return (value instanceof Integer) ? ((Integer) value)
0537 .intValue() : 0;
0538 }
0539
0540 /**
0541 * If the value of <code>key</code> is boolean, return the
0542 * boolean value, otherwise return false.
0543 *
0544 * @param key an <code>Object</code> specifying the key for the desired boolean value
0545 * @return if the value of <code>key</code> is boolean, return the
0546 * boolean value, otherwise return false.
0547 * @since 1.4
0548 */
0549 public boolean getBoolean(Object key) {
0550 Object value = get(key);
0551 return (value instanceof Boolean) ? ((Boolean) value)
0552 .booleanValue() : false;
0553 }
0554
0555 /**
0556 * If the value of <code>key</code> for the given <code>Locale</code>
0557 * is boolean, return the boolean value, otherwise return false.
0558 *
0559 * @param key an <code>Object</code> specifying the key for the desired boolean value
0560 * @param l the desired locale
0561 * @return if the value for <code>key</code> and <code>Locale</code>
0562 * is boolean, return the
0563 * boolean value, otherwise return false.
0564 * @since 1.4
0565 */
0566 public boolean getBoolean(Object key, Locale l) {
0567 Object value = get(key, l);
0568 return (value instanceof Boolean) ? ((Boolean) value)
0569 .booleanValue() : false;
0570 }
0571
0572 /**
0573 * If the value of <code>key</code> is an <code>Insets</code> return it,
0574 * otherwise return <code>null</code>.
0575 * @param key the desired key
0576 * @return if the value for <code>key</code> is an <code>Insets</code>,
0577 * return the <code>Insets</code> object; otherwise return
0578 * <code>null</code>
0579 */
0580 public Insets getInsets(Object key) {
0581 Object value = get(key);
0582 return (value instanceof Insets) ? (Insets) value : null;
0583 }
0584
0585 /**
0586 * If the value of <code>key</code> for the given <code>Locale</code>
0587 * is an <code>Insets</code> return it, otherwise return <code>null</code>.
0588 * @param key the desired key
0589 * @param l the desired locale
0590 * @return if the value for <code>key</code> and <code>Locale</code>
0591 * is an <code>Insets</code>,
0592 * return the <code>Insets</code> object; otherwise return
0593 * <code>null</code>
0594 * @since 1.4
0595 */
0596 public Insets getInsets(Object key, Locale l) {
0597 Object value = get(key, l);
0598 return (value instanceof Insets) ? (Insets) value : null;
0599 }
0600
0601 /**
0602 * If the value of <code>key</code> is a <code>Dimension</code> return it,
0603 * otherwise return <code>null</code>.
0604 * @param key the desired key
0605 * @return if the value for <code>key</code> is a <code>Dimension</code>,
0606 * return the <code>Dimension</code> object; otherwise return
0607 * <code>null</code>
0608 */
0609 public Dimension getDimension(Object key) {
0610 Object value = get(key);
0611 return (value instanceof Dimension) ? (Dimension) value : null;
0612 }
0613
0614 /**
0615 * If the value of <code>key</code> for the given <code>Locale</code>
0616 * is a <code>Dimension</code> return it, otherwise return <code>null</code>.
0617 * @param key the desired key
0618 * @param l the desired locale
0619 * @return if the value for <code>key</code> and <code>Locale</code>
0620 * is a <code>Dimension</code>,
0621 * return the <code>Dimension</code> object; otherwise return
0622 * <code>null</code>
0623 * @since 1.4
0624 */
0625 public Dimension getDimension(Object key, Locale l) {
0626 Object value = get(key, l);
0627 return (value instanceof Dimension) ? (Dimension) value : null;
0628 }
0629
0630 /**
0631 * The value of <code>get(uidClassID)</code> must be the
0632 * <code>String</code> name of a
0633 * class that implements the corresponding <code>ComponentUI</code>
0634 * class. If the class hasn't been loaded before, this method looks
0635 * up the class with <code>uiClassLoader.loadClass()</code> if a non
0636 * <code>null</code>
0637 * class loader is provided, <code>classForName()</code> otherwise.
0638 * <p>
0639 * If a mapping for <code>uiClassID</code> exists or if the specified
0640 * class can't be found, return <code>null</code>.
0641 * <p>
0642 * This method is used by <code>getUI</code>, it's usually
0643 * not necessary to call it directly.
0644 *
0645 * @param uiClassID a string containing the class ID
0646 * @param uiClassLoader the object which will load the class
0647 * @return the value of <code>Class.forName(get(uidClassID))</code>
0648 * @see #getUI
0649 */
0650 public Class<? extends ComponentUI> getUIClass(String uiClassID,
0651 ClassLoader uiClassLoader) {
0652 try {
0653 String className = (String) get(uiClassID);
0654 if (className != null) {
0655 Class cls = (Class) get(className);
0656 if (cls == null) {
0657 if (uiClassLoader == null) {
0658 cls = SwingUtilities.loadSystemClass(className);
0659 } else {
0660 cls = uiClassLoader.loadClass(className);
0661 }
0662 if (cls != null) {
0663 // Save lookup for future use, as forName is slow.
0664 put(className, cls);
0665 }
0666 }
0667 return cls;
0668 }
0669 } catch (ClassNotFoundException e) {
0670 return null;
0671 } catch (ClassCastException e) {
0672 return null;
0673 }
0674 return null;
0675 }
0676
0677 /**
0678 * Returns the L&F class that renders this component.
0679 *
0680 * @param uiClassID a string containing the class ID
0681 * @return the Class object returned by
0682 * <code>getUIClass(uiClassID, null)</code>
0683 */
0684 public Class<? extends ComponentUI> getUIClass(String uiClassID) {
0685 return getUIClass(uiClassID, null);
0686 }
0687
0688 /**
0689 * If <code>getUI()</code> fails for any reason,
0690 * it calls this method before returning <code>null</code>.
0691 * Subclasses may choose to do more or less here.
0692 *
0693 * @param msg message string to print
0694 * @see #getUI
0695 */
0696 protected void getUIError(String msg) {
0697 System.err.println("UIDefaults.getUI() failed: " + msg);
0698 try {
0699 throw new Error();
0700 } catch (Throwable e) {
0701 e.printStackTrace();
0702 }
0703 }
0704
0705 /**
0706 * Creates an <code>ComponentUI</code> implementation for the
0707 * specified component. In other words create the look
0708 * and feel specific delegate object for <code>target</code>.
0709 * This is done in two steps:
0710 * <ul>
0711 * <li> Look up the name of the <code>ComponentUI</code> implementation
0712 * class under the value returned by <code>target.getUIClassID()</code>.
0713 * <li> Use the implementation classes static <code>createUI()</code>
0714 * method to construct a look and feel delegate.
0715 * </ul>
0716 * @param target the <code>JComponent</code> which needs a UI
0717 * @return the <code>ComponentUI</code> object
0718 */
0719 public ComponentUI getUI(JComponent target) {
0720
0721 Object cl = get("ClassLoader");
0722 ClassLoader uiClassLoader = (cl != null) ? (ClassLoader) cl
0723 : target.getClass().getClassLoader();
0724 Class uiClass = getUIClass(target.getUIClassID(), uiClassLoader);
0725 Object uiObject = null;
0726
0727 if (uiClass == null) {
0728 getUIError("no ComponentUI class for: " + target);
0729 } else {
0730 try {
0731 Method m = (Method) get(uiClass);
0732 if (m == null) {
0733 Class acClass = javax.swing.JComponent.class;
0734 m = uiClass.getMethod("createUI",
0735 new Class[] { acClass });
0736 put(uiClass, m);
0737 }
0738 uiObject = MethodUtil.invoke(m, null,
0739 new Object[] { target });
0740 } catch (NoSuchMethodException e) {
0741 getUIError("static createUI() method not found in "
0742 + uiClass);
0743 } catch (Exception e) {
0744 getUIError("createUI() failed for " + target + " " + e);
0745 }
0746 }
0747
0748 return (ComponentUI) uiObject;
0749 }
0750
0751 /**
0752 * Adds a <code>PropertyChangeListener</code> to the listener list.
0753 * The listener is registered for all properties.
0754 * <p>
0755 * A <code>PropertyChangeEvent</code> will get fired whenever a default
0756 * is changed.
0757 *
0758 * @param listener the <code>PropertyChangeListener</code> to be added
0759 * @see java.beans.PropertyChangeSupport
0760 */
0761 public synchronized void addPropertyChangeListener(
0762 PropertyChangeListener listener) {
0763 if (changeSupport == null) {
0764 changeSupport = new SwingPropertyChangeSupport(this );
0765 }
0766 changeSupport.addPropertyChangeListener(listener);
0767 }
0768
0769 /**
0770 * Removes a <code>PropertyChangeListener</code> from the listener list.
0771 * This removes a <code>PropertyChangeListener</code> that was registered
0772 * for all properties.
0773 *
0774 * @param listener the <code>PropertyChangeListener</code> to be removed
0775 * @see java.beans.PropertyChangeSupport
0776 */
0777 public synchronized void removePropertyChangeListener(
0778 PropertyChangeListener listener) {
0779 if (changeSupport != null) {
0780 changeSupport.removePropertyChangeListener(listener);
0781 }
0782 }
0783
0784 /**
0785 * Returns an array of all the <code>PropertyChangeListener</code>s added
0786 * to this UIDefaults with addPropertyChangeListener().
0787 *
0788 * @return all of the <code>PropertyChangeListener</code>s added or an empty
0789 * array if no listeners have been added
0790 * @since 1.4
0791 */
0792 public synchronized PropertyChangeListener[] getPropertyChangeListeners() {
0793 if (changeSupport == null) {
0794 return new PropertyChangeListener[0];
0795 }
0796 return changeSupport.getPropertyChangeListeners();
0797 }
0798
0799 /**
0800 * Support for reporting bound property changes. If oldValue and
0801 * newValue are not equal and the <code>PropertyChangeEvent</code>x
0802 * listener list isn't empty, then fire a
0803 * <code>PropertyChange</code> event to each listener.
0804 *
0805 * @param propertyName the programmatic name of the property
0806 * that was changed
0807 * @param oldValue the old value of the property
0808 * @param newValue the new value of the property
0809 * @see java.beans.PropertyChangeSupport
0810 */
0811 protected void firePropertyChange(String propertyName,
0812 Object oldValue, Object newValue) {
0813 if (changeSupport != null) {
0814 changeSupport.firePropertyChange(propertyName, oldValue,
0815 newValue);
0816 }
0817 }
0818
0819 /**
0820 * Adds a resource bundle to the list of resource bundles that are
0821 * searched for localized values. Resource bundles are searched in the
0822 * reverse order they were added. In other words, the most recently added
0823 * bundle is searched first.
0824 *
0825 * @param bundleName the base name of the resource bundle to be added
0826 * @see java.util.ResourceBundle
0827 * @see #removeResourceBundle
0828 * @since 1.4
0829 */
0830 public synchronized void addResourceBundle(String bundleName) {
0831 if (bundleName == null) {
0832 return;
0833 }
0834 if (resourceBundles == null) {
0835 resourceBundles = new Vector(5);
0836 }
0837 if (!resourceBundles.contains(bundleName)) {
0838 resourceBundles.add(bundleName);
0839 resourceCache.clear();
0840 }
0841 }
0842
0843 /**
0844 * Removes a resource bundle from the list of resource bundles that are
0845 * searched for localized defaults.
0846 *
0847 * @param bundleName the base name of the resource bundle to be removed
0848 * @see java.util.ResourceBundle
0849 * @see #addResourceBundle
0850 * @since 1.4
0851 */
0852 public synchronized void removeResourceBundle(String bundleName) {
0853 if (resourceBundles != null) {
0854 resourceBundles.remove(bundleName);
0855 }
0856 resourceCache.clear();
0857 }
0858
0859 /**
0860 * Sets the default locale. The default locale is used in retrieving
0861 * localized values via <code>get</code> methods that do not take a
0862 * locale argument. As of release 1.4, Swing UI objects should retrieve
0863 * localized values using the locale of their component rather than the
0864 * default locale. The default locale exists to provide compatibility with
0865 * pre 1.4 behaviour.
0866 *
0867 * @param l the new default locale
0868 * @see #getDefaultLocale
0869 * @see #get(Object)
0870 * @see #get(Object,Locale)
0871 * @since 1.4
0872 */
0873 public void setDefaultLocale(Locale l) {
0874 defaultLocale = l;
0875 }
0876
0877 /**
0878 * Returns the default locale. The default locale is used in retrieving
0879 * localized values via <code>get</code> methods that do not take a
0880 * locale argument. As of release 1.4, Swing UI objects should retrieve
0881 * localized values using the locale of their component rather than the
0882 * default locale. The default locale exists to provide compatibility with
0883 * pre 1.4 behaviour.
0884 *
0885 * @return the default locale
0886 * @see #setDefaultLocale
0887 * @see #get(Object)
0888 * @see #get(Object,Locale)
0889 * @since 1.4
0890 */
0891 public Locale getDefaultLocale() {
0892 return defaultLocale;
0893 }
0894
0895 /**
0896 * This class enables one to store an entry in the defaults
0897 * table that isn't constructed until the first time it's
0898 * looked up with one of the <code>getXXX(key)</code> methods.
0899 * Lazy values are useful for defaults that are expensive
0900 * to construct or are seldom retrieved. The first time
0901 * a <code>LazyValue</code> is retrieved its "real value" is computed
0902 * by calling <code>LazyValue.createValue()</code> and the real
0903 * value is used to replace the <code>LazyValue</code> in the
0904 * <code>UIDefaults</code>
0905 * table. Subsequent lookups for the same key return
0906 * the real value. Here's an example of a <code>LazyValue</code>
0907 * that constructs a <code>Border</code>:
0908 * <pre>
0909 * Object borderLazyValue = new UIDefaults.LazyValue() {
0910 * public Object createValue(UIDefaults table) {
0911 * return new BorderFactory.createLoweredBevelBorder();
0912 * }
0913 * };
0914 *
0915 * uiDefaultsTable.put("MyBorder", borderLazyValue);
0916 * </pre>
0917 *
0918 * @see UIDefaults#get
0919 */
0920 public interface LazyValue {
0921 /**
0922 * Creates the actual value retrieved from the <code>UIDefaults</code>
0923 * table. When an object that implements this interface is
0924 * retrieved from the table, this method is used to create
0925 * the real value, which is then stored in the table and
0926 * returned to the calling method.
0927 *
0928 * @param table a <code>UIDefaults</code> table
0929 * @return the created <code>Object</code>
0930 */
0931 Object createValue(UIDefaults table);
0932 }
0933
0934 /**
0935 * This class enables one to store an entry in the defaults
0936 * table that's constructed each time it's looked up with one of
0937 * the <code>getXXX(key)</code> methods. Here's an example of
0938 * an <code>ActiveValue</code> that constructs a
0939 * <code>DefaultListCellRenderer</code>:
0940 * <pre>
0941 * Object cellRendererActiveValue = new UIDefaults.ActiveValue() {
0942 * public Object createValue(UIDefaults table) {
0943 * return new DefaultListCellRenderer();
0944 * }
0945 * };
0946 *
0947 * uiDefaultsTable.put("MyRenderer", cellRendererActiveValue);
0948 * </pre>
0949 *
0950 * @see UIDefaults#get
0951 */
0952 public interface ActiveValue {
0953 /**
0954 * Creates the value retrieved from the <code>UIDefaults</code> table.
0955 * The object is created each time it is accessed.
0956 *
0957 * @param table a <code>UIDefaults</code> table
0958 * @return the created <code>Object</code>
0959 */
0960 Object createValue(UIDefaults table);
0961 }
0962
0963 /**
0964 * This class provides an implementation of <code>LazyValue</code>
0965 * which can be
0966 * used to delay loading of the Class for the instance to be created.
0967 * It also avoids creation of an anonymous inner class for the
0968 * <code>LazyValue</code>
0969 * subclass. Both of these improve performance at the time that a
0970 * a Look and Feel is loaded, at the cost of a slight performance
0971 * reduction the first time <code>createValue</code> is called
0972 * (since Reflection APIs are used).
0973 * @since 1.3
0974 */
0975 public static class ProxyLazyValue implements LazyValue {
0976 private AccessControlContext acc;
0977 private String className;
0978 private String methodName;
0979 private Object[] args;
0980
0981 /**
0982 * Creates a <code>LazyValue</code> which will construct an instance
0983 * when asked.
0984 *
0985 * @param c a <code>String</code> specifying the classname
0986 * of the instance to be created on demand
0987 */
0988 public ProxyLazyValue(String c) {
0989 this (c, (String) null);
0990 }
0991
0992 /**
0993 * Creates a <code>LazyValue</code> which will construct an instance
0994 * when asked.
0995 *
0996 * @param c a <code>String</code> specifying the classname of
0997 * the class
0998 * containing a static method to be called for
0999 * instance creation
1000 * @param m a <code>String</code> specifying the static
1001 * method to be called on class c
1002 */
1003 public ProxyLazyValue(String c, String m) {
1004 this (c, m, null);
1005 }
1006
1007 /**
1008 * Creates a <code>LazyValue</code> which will construct an instance
1009 * when asked.
1010 *
1011 * @param c a <code>String</code> specifying the classname
1012 * of the instance to be created on demand
1013 * @param o an array of <code>Objects</code> to be passed as
1014 * paramaters to the constructor in class c
1015 */
1016 public ProxyLazyValue(String c, Object[] o) {
1017 this (c, null, o);
1018 }
1019
1020 /**
1021 * Creates a <code>LazyValue</code> which will construct an instance
1022 * when asked.
1023 *
1024 * @param c a <code>String</code> specifying the classname
1025 * of the class
1026 * containing a static method to be called for
1027 * instance creation.
1028 * @param m a <code>String</code> specifying the static method
1029 * to be called on class c
1030 * @param o an array of <code>Objects</code> to be passed as
1031 * paramaters to the static method in class c
1032 */
1033 public ProxyLazyValue(String c, String m, Object[] o) {
1034 acc = AccessController.getContext();
1035 className = c;
1036 methodName = m;
1037 if (o != null) {
1038 args = (Object[]) o.clone();
1039 }
1040 }
1041
1042 /**
1043 * Creates the value retrieved from the <code>UIDefaults</code> table.
1044 * The object is created each time it is accessed.
1045 *
1046 * @param table a <code>UIDefaults</code> table
1047 * @return the created <code>Object</code>
1048 */
1049 public Object createValue(final UIDefaults table) {
1050 // In order to pick up the security policy in effect at the
1051 // time of creation we use a doPrivileged with the
1052 // AccessControlContext that was in place when this was created.
1053 return AccessController.doPrivileged(
1054 new PrivilegedAction() {
1055 public Object run() {
1056 try {
1057 Class c;
1058 Object cl;
1059 // See if we should use a separate ClassLoader
1060 if (table == null
1061 || !((cl = table
1062 .get("ClassLoader")) instanceof ClassLoader)) {
1063 cl = Thread.currentThread()
1064 .getContextClassLoader();
1065 if (cl == null) {
1066 // Fallback to the system class loader.
1067 cl = ClassLoader
1068 .getSystemClassLoader();
1069 }
1070 }
1071 c = Class.forName(className, true,
1072 (ClassLoader) cl);
1073 if (methodName != null) {
1074 Class[] types = getClassArray(args);
1075 Method m = c.getMethod(methodName,
1076 types);
1077 return MethodUtil
1078 .invoke(m, c, args);
1079 } else {
1080 Class[] types = getClassArray(args);
1081 Constructor constructor = c
1082 .getConstructor(types);
1083 return constructor
1084 .newInstance(args);
1085 }
1086 } catch (Exception e) {
1087 // Ideally we would throw an exception, unfortunately
1088 // often times there are errors as an initial look and
1089 // feel is loaded before one can be switched. Perhaps a
1090 // flag should be added for debugging, so that if true
1091 // the exception would be thrown.
1092 }
1093 return null;
1094 }
1095 }, acc);
1096 }
1097
1098 /*
1099 * Coerce the array of class types provided into one which
1100 * looks the way the Reflection APIs expect. This is done
1101 * by substituting primitive types for their Object counterparts,
1102 * and superclasses for subclasses used to add the
1103 * <code>UIResource</code> tag.
1104 */
1105 private Class[] getClassArray(Object[] args) {
1106 Class[] types = null;
1107 if (args != null) {
1108 types = new Class[args.length];
1109 for (int i = 0; i < args.length; i++) {
1110 /* PENDING(ges): At present only the primitive types
1111 used are handled correctly; this should eventually
1112 handle all primitive types */
1113 if (args[i] instanceof java.lang.Integer) {
1114 types[i] = Integer.TYPE;
1115 } else if (args[i] instanceof java.lang.Boolean) {
1116 types[i] = Boolean.TYPE;
1117 } else if (args[i] instanceof javax.swing.plaf.ColorUIResource) {
1118 /* PENDING(ges) Currently the Reflection APIs do not
1119 search superclasses of parameters supplied for
1120 constructor/method lookup. Since we only have
1121 one case where this is needed, we substitute
1122 directly instead of adding a massive amount
1123 of mechanism for this. Eventually this will
1124 probably need to handle the general case as well.
1125 */
1126 types[i] = java.awt.Color.class;
1127 } else {
1128 types[i] = args[i].getClass();
1129 }
1130 }
1131 }
1132 return types;
1133 }
1134
1135 private String printArgs(Object[] array) {
1136 String s = "{";
1137 if (array != null) {
1138 for (int i = 0; i < array.length - 1; i++) {
1139 s = s.concat(array[i] + ",");
1140 }
1141 s = s.concat(array[array.length - 1] + "}");
1142 } else {
1143 s = s.concat("}");
1144 }
1145 return s;
1146 }
1147 }
1148
1149 /**
1150 * <code>LazyInputMap</code> will create a <code>InputMap</code>
1151 * in its <code>createValue</code>
1152 * method. The bindings are passed in in the constructor.
1153 * The bindings are an array with
1154 * the even number entries being string <code>KeyStrokes</code>
1155 * (eg "alt SPACE") and
1156 * the odd number entries being the value to use in the
1157 * <code>InputMap</code> (and the key in the <code>ActionMap</code>).
1158 * @since 1.3
1159 */
1160 public static class LazyInputMap implements LazyValue {
1161 /** Key bindings are registered under. */
1162 private Object[] bindings;
1163
1164 public LazyInputMap(Object[] bindings) {
1165 this .bindings = bindings;
1166 }
1167
1168 /**
1169 * Creates an <code>InputMap</code> with the bindings that are
1170 * passed in.
1171 *
1172 * @param table a <code>UIDefaults</code> table
1173 * @return the <code>InputMap</code>
1174 */
1175 public Object createValue(UIDefaults table) {
1176 if (bindings != null) {
1177 InputMap km = LookAndFeel.makeInputMap(bindings);
1178 return km;
1179 }
1180 return null;
1181 }
1182 }
1183 }
|