blob: 302dc00b0dda2a64a5d2a1bead989b0d58191d43 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package javax.swing;
27
28
29import javax.swing.plaf.ComponentUI;
30import javax.swing.border.*;
31import javax.swing.event.SwingPropertyChangeSupport;
32
33import java.lang.reflect.*;
34import java.util.HashMap;
35import java.util.Map;
36import java.util.Enumeration;
37import java.util.Hashtable;
38import java.util.ResourceBundle;
39import java.util.ResourceBundle.Control;
40import java.util.Locale;
41import java.util.Vector;
42import java.util.MissingResourceException;
43import java.awt.Font;
44import java.awt.Color;
45import java.awt.Insets;
46import java.awt.Dimension;
47import java.lang.reflect.Method;
48import java.beans.PropertyChangeListener;
49import java.beans.PropertyChangeEvent;
50import java.security.AccessController;
51import java.security.AccessControlContext;
52import java.security.PrivilegedAction;
53
54import sun.reflect.misc.MethodUtil;
55import sun.util.CoreResourceBundleControl;
56
57/**
58 * A table of defaults for Swing components. Applications can set/get
59 * default values via the <code>UIManager</code>.
60 * <p>
61 * <strong>Warning:</strong>
62 * Serialized objects of this class will not be compatible with
63 * future Swing releases. The current serialization support is
64 * appropriate for short term storage or RMI between applications running
65 * the same version of Swing. As of 1.4, support for long term storage
66 * of all JavaBeans<sup><font size="-2">TM</font></sup>
67 * has been added to the <code>java.beans</code> package.
68 * Please see {@link java.beans.XMLEncoder}.
69 *
70 * @see UIManager
71 * @author Hans Muller
72 */
73public class UIDefaults extends Hashtable<Object,Object>
74{
75 private static final Object PENDING = new String("Pending");
76
77 private SwingPropertyChangeSupport changeSupport;
78
79 private Vector resourceBundles;
80
81 private Locale defaultLocale = Locale.getDefault();
82
83 /**
84 * Maps from a Locale to a cached Map of the ResourceBundle. This is done
85 * so as to avoid an exception being thrown when a value is asked for.
86 * Access to this should be done while holding a lock on the
87 * UIDefaults, eg synchronized(this).
88 */
89 private Map resourceCache;
90
91 /**
92 * Creates an empty defaults table.
93 */
94 public UIDefaults() {
95 this(700, .75f);
96 }
97
98 /**
99 * Creates an empty defaults table with the specified initial capacity and
100 * load factor.
101 *
102 * @param initialCapacity the initial capacity of the defaults table
103 * @param loadFactor the load factor of the defaults table
104 * @see java.util.Hashtable
105 * @since 1.6
106 */
107 public UIDefaults(int initialCapacity, float loadFactor) {
108 super(initialCapacity, loadFactor);
109 resourceCache = new HashMap();
110 }
111
112
113 /**
114 * Creates a defaults table initialized with the specified
115 * key/value pairs. For example:
116 * <pre>
117 Object[] uiDefaults = {
118 "Font", new Font("Dialog", Font.BOLD, 12),
119 "Color", Color.red,
120 "five", new Integer(5)
121 }
122 UIDefaults myDefaults = new UIDefaults(uiDefaults);
123 * </pre>
124 * @param keyValueList an array of objects containing the key/value
125 * pairs
126 */
127 public UIDefaults(Object[] keyValueList) {
128 super(keyValueList.length / 2);
129 for(int i = 0; i < keyValueList.length; i += 2) {
130 super.put(keyValueList[i], keyValueList[i + 1]);
131 }
132 }
133
134 /**
135 * Returns the value for key. If the value is a
136 * <code>UIDefaults.LazyValue</code> then the real
137 * value is computed with <code>LazyValue.createValue()</code>,
138 * the table entry is replaced, and the real value is returned.
139 * If the value is an <code>UIDefaults.ActiveValue</code>
140 * the table entry is not replaced - the value is computed
141 * with <code>ActiveValue.createValue()</code> for each
142 * <code>get()</code> call.
143 *
144 * If the key is not found in the table then it is searched for in the list
145 * of resource bundles maintained by this object. The resource bundles are
146 * searched most recently added first using the locale returned by
147 * <code>getDefaultLocale</code>. <code>LazyValues</code> and
148 * <code>ActiveValues</code> are not supported in the resource bundles.
149
150 *
151 * @param key the desired key
152 * @return the value for <code>key</code>
153 * @see LazyValue
154 * @see ActiveValue
155 * @see java.util.Hashtable#get
156 * @see #getDefaultLocale
157 * @see #addResourceBundle
158 * @since 1.4
159 */
160 public Object get(Object key) {
161 Object value = getFromHashtable( key );
162 return (value != null) ? value : getFromResourceBundle(key, null);
163 }
164
165 /**
166 * Looks up up the given key in our Hashtable and resolves LazyValues
167 * or ActiveValues.
168 */
169 private Object getFromHashtable(Object key) {
170 /* Quickly handle the common case, without grabbing
171 * a lock.
172 */
173 Object value = super.get(key);
174 if ((value != PENDING) &&
175 !(value instanceof ActiveValue) &&
176 !(value instanceof LazyValue)) {
177 return value;
178 }
179
180 /* If the LazyValue for key is being constructed by another
181 * thread then wait and then return the new value, otherwise drop
182 * the lock and construct the ActiveValue or the LazyValue.
183 * We use the special value PENDING to mark LazyValues that
184 * are being constructed.
185 */
186 synchronized(this) {
187 value = super.get(key);
188 if (value == PENDING) {
189 do {
190 try {
191 this.wait();
192 }
193 catch (InterruptedException e) {
194 }
195 value = super.get(key);
196 }
197 while(value == PENDING);
198 return value;
199 }
200 else if (value instanceof LazyValue) {
201 super.put(key, PENDING);
202 }
203 else if (!(value instanceof ActiveValue)) {
204 return value;
205 }
206 }
207
208 /* At this point we know that the value of key was
209 * a LazyValue or an ActiveValue.
210 */
211 if (value instanceof LazyValue) {
212 try {
213 /* If an exception is thrown we'll just put the LazyValue
214 * back in the table.
215 */
216 value = ((LazyValue)value).createValue(this);
217 }
218 finally {
219 synchronized(this) {
220 if (value == null) {
221 super.remove(key);
222 }
223 else {
224 super.put(key, value);
225 }
226 this.notifyAll();
227 }
228 }
229 }
230 else {
231 value = ((ActiveValue)value).createValue(this);
232 }
233
234 return value;
235 }
236
237
238 /**
239 * Returns the value for key associated with the given locale.
240 * If the value is a <code>UIDefaults.LazyValue</code> then the real
241 * value is computed with <code>LazyValue.createValue()</code>,
242 * the table entry is replaced, and the real value is returned.
243 * If the value is an <code>UIDefaults.ActiveValue</code>
244 * the table entry is not replaced - the value is computed
245 * with <code>ActiveValue.createValue()</code> for each
246 * <code>get()</code> call.
247 *
248 * If the key is not found in the table then it is searched for in the list
249 * of resource bundles maintained by this object. The resource bundles are
250 * searched most recently added first using the given locale.
251 * <code>LazyValues</code> and <code>ActiveValues</code> are not supported
252 * in the resource bundles.
253 *
254 * @param key the desired key
255 * @param l the desired <code>locale</code>
256 * @return the value for <code>key</code>
257 * @see LazyValue
258 * @see ActiveValue
259 * @see java.util.Hashtable#get
260 * @see #addResourceBundle
261 * @since 1.4
262 */
263 public Object get(Object key, Locale l) {
264 Object value = getFromHashtable( key );
265 return (value != null) ? value : getFromResourceBundle(key, l);
266 }
267
268 /**
269 * Looks up given key in our resource bundles.
270 */
271 private Object getFromResourceBundle(Object key, Locale l) {
272
273 if( resourceBundles == null ||
274 resourceBundles.isEmpty() ||
275 !(key instanceof String) ) {
276 return null;
277 }
278
279 // A null locale means use the default locale.
280 if( l == null ) {
281 if( defaultLocale == null )
282 return null;
283 else
284 l = (Locale)defaultLocale;
285 }
286
287 synchronized(this) {
288 return getResourceCache(l).get((String)key);
289 }
290 }
291
292 /**
293 * Returns a Map of the known resources for the given locale.
294 */
295 private Map getResourceCache(Locale l) {
296 Map values = (Map)resourceCache.get(l);
297
298 if (values == null) {
299 values = new HashMap();
300 for (int i=resourceBundles.size()-1; i >= 0; i--) {
301 String bundleName = (String)resourceBundles.get(i);
302 try {
303 Control c = CoreResourceBundleControl.getRBControlInstance(bundleName);
304 ResourceBundle b;
305 if (c != null) {
306 b = ResourceBundle.getBundle(bundleName, l, c);
307 } else {
308 b = ResourceBundle.getBundle(bundleName, l);
309 }
310 Enumeration keys = b.getKeys();
311
312 while (keys.hasMoreElements()) {
313 String key = (String)keys.nextElement();
314
315 if (values.get(key) == null) {
316 Object value = b.getObject(key);
317
318 values.put(key, value);
319 }
320 }
321 } catch( MissingResourceException mre ) {
322 // Keep looking
323 }
324 }
325 resourceCache.put(l, values);
326 }
327 return values;
328 }
329
330 /**
331 * Sets the value of <code>key</code> to <code>value</code> for all locales.
332 * If <code>key</code> is a string and the new value isn't
333 * equal to the old one, fire a <code>PropertyChangeEvent</code>.
334 * If value is <code>null</code>, the key is removed from the table.
335 *
336 * @param key the unique <code>Object</code> who's value will be used
337 * to retrieve the data value associated with it
338 * @param value the new <code>Object</code> to store as data under
339 * that key
340 * @return the previous <code>Object</code> value, or <code>null</code>
341 * @see #putDefaults
342 * @see java.util.Hashtable#put
343 */
344 public Object put(Object key, Object value) {
345 Object oldValue = (value == null) ? super.remove(key) : super.put(key, value);
346 if (key instanceof String) {
347 firePropertyChange((String)key, oldValue, value);
348 }
349 return oldValue;
350 }
351
352
353 /**
354 * Puts all of the key/value pairs in the database and
355 * unconditionally generates one <code>PropertyChangeEvent</code>.
356 * The events oldValue and newValue will be <code>null</code> and its
357 * <code>propertyName</code> will be "UIDefaults". The key/value pairs are
358 * added for all locales.
359 *
360 * @param keyValueList an array of key/value pairs
361 * @see #put
362 * @see java.util.Hashtable#put
363 */
364 public void putDefaults(Object[] keyValueList) {
365 for(int i = 0, max = keyValueList.length; i < max; i += 2) {
366 Object value = keyValueList[i + 1];
367 if (value == null) {
368 super.remove(keyValueList[i]);
369 }
370 else {
371 super.put(keyValueList[i], value);
372 }
373 }
374 firePropertyChange("UIDefaults", null, null);
375 }
376
377
378 /**
379 * If the value of <code>key</code> is a <code>Font</code> return it,
380 * otherwise return <code>null</code>.
381 * @param key the desired key
382 * @return if the value for <code>key</code> is a <code>Font</code>,
383 * return the <code>Font</code> object; otherwise return
384 * <code>null</code>
385 */
386 public Font getFont(Object key) {
387 Object value = get(key);
388 return (value instanceof Font) ? (Font)value : null;
389 }
390
391
392 /**
393 * If the value of <code>key</code> for the given <code>Locale</code>
394 * is a <code>Font</code> return it, otherwise return <code>null</code>.
395 * @param key the desired key
396 * @param l the desired locale
397 * @return if the value for <code>key</code> and <code>Locale</code>
398 * is a <code>Font</code>,
399 * return the <code>Font</code> object; otherwise return
400 * <code>null</code>
401 * @since 1.4
402 */
403 public Font getFont(Object key, Locale l) {
404 Object value = get(key,l);
405 return (value instanceof Font) ? (Font)value : null;
406 }
407
408 /**
409 * If the value of <code>key</code> is a <code>Color</code> return it,
410 * otherwise return <code>null</code>.
411 * @param key the desired key
412 * @return if the value for <code>key</code> is a <code>Color</code>,
413 * return the <code>Color</code> object; otherwise return
414 * <code>null</code>
415 */
416 public Color getColor(Object key) {
417 Object value = get(key);
418 return (value instanceof Color) ? (Color)value : null;
419 }
420
421
422 /**
423 * If the value of <code>key</code> for the given <code>Locale</code>
424 * is a <code>Color</code> return it, otherwise return <code>null</code>.
425 * @param key the desired key
426 * @param l the desired locale
427 * @return if the value for <code>key</code> and <code>Locale</code>
428 * is a <code>Color</code>,
429 * return the <code>Color</code> object; otherwise return
430 * <code>null</code>
431 * @since 1.4
432 */
433 public Color getColor(Object key, Locale l) {
434 Object value = get(key,l);
435 return (value instanceof Color) ? (Color)value : null;
436 }
437
438
439 /**
440 * If the value of <code>key</code> is an <code>Icon</code> return it,
441 * otherwise return <code>null</code>.
442 * @param key the desired key
443 * @return if the value for <code>key</code> is an <code>Icon</code>,
444 * return the <code>Icon</code> object; otherwise return
445 * <code>null</code>
446 */
447 public Icon getIcon(Object key) {
448 Object value = get(key);
449 return (value instanceof Icon) ? (Icon)value : null;
450 }
451
452
453 /**
454 * If the value of <code>key</code> for the given <code>Locale</code>
455 * is an <code>Icon</code> return it, otherwise return <code>null</code>.
456 * @param key the desired key
457 * @param l the desired locale
458 * @return if the value for <code>key</code> and <code>Locale</code>
459 * is an <code>Icon</code>,
460 * return the <code>Icon</code> object; otherwise return
461 * <code>null</code>
462 * @since 1.4
463 */
464 public Icon getIcon(Object key, Locale l) {
465 Object value = get(key,l);
466 return (value instanceof Icon) ? (Icon)value : null;
467 }
468
469
470 /**
471 * If the value of <code>key</code> is a <code>Border</code> return it,
472 * otherwise return <code>null</code>.
473 * @param key the desired key
474 * @return if the value for <code>key</code> is a <code>Border</code>,
475 * return the <code>Border</code> object; otherwise return
476 * <code>null</code>
477 */
478 public Border getBorder(Object key) {
479 Object value = get(key);
480 return (value instanceof Border) ? (Border)value : null;
481 }
482
483
484 /**
485 * If the value of <code>key</code> for the given <code>Locale</code>
486 * is a <code>Border</code> return it, otherwise return <code>null</code>.
487 * @param key the desired key
488 * @param l the desired locale
489 * @return if the value for <code>key</code> and <code>Locale</code>
490 * is a <code>Border</code>,
491 * return the <code>Border</code> object; otherwise return
492 * <code>null</code>
493 * @since 1.4
494 */
495 public Border getBorder(Object key, Locale l) {
496 Object value = get(key,l);
497 return (value instanceof Border) ? (Border)value : null;
498 }
499
500
501 /**
502 * If the value of <code>key</code> is a <code>String</code> return it,
503 * otherwise return <code>null</code>.
504 * @param key the desired key
505 * @return if the value for <code>key</code> is a <code>String</code>,
506 * return the <code>String</code> object; otherwise return
507 * <code>null</code>
508 */
509 public String getString(Object key) {
510 Object value = get(key);
511 return (value instanceof String) ? (String)value : null;
512 }
513
514 /**
515 * If the value of <code>key</code> for the given <code>Locale</code>
516 * is a <code>String</code> return it, otherwise return <code>null</code>.
517 * @param key the desired key
518 * @param l the desired <code>Locale</code>
519 * @return if the value for <code>key</code> for the given
520 * <code>Locale</code> is a <code>String</code>,
521 * return the <code>String</code> object; otherwise return
522 * <code>null</code>
523 * @since 1.4
524 */
525 public String getString(Object key, Locale l) {
526 Object value = get(key,l);
527 return (value instanceof String) ? (String)value : null;
528 }
529
530 /**
531 * If the value of <code>key</code> is an <code>Integer</code> return its
532 * integer value, otherwise return 0.
533 * @param key the desired key
534 * @return if the value for <code>key</code> is an <code>Integer</code>,
535 * return its value, otherwise return 0
536 */
537 public int getInt(Object key) {
538 Object value = get(key);
539 return (value instanceof Integer) ? ((Integer)value).intValue() : 0;
540 }
541
542
543 /**
544 * If the value of <code>key</code> for the given <code>Locale</code>
545 * is an <code>Integer</code> return its integer value, otherwise return 0.
546 * @param key the desired key
547 * @param l the desired locale
548 * @return if the value for <code>key</code> and <code>Locale</code>
549 * is an <code>Integer</code>,
550 * return its value, otherwise return 0
551 * @since 1.4
552 */
553 public int getInt(Object key, Locale l) {
554 Object value = get(key,l);
555 return (value instanceof Integer) ? ((Integer)value).intValue() : 0;
556 }
557
558
559 /**
560 * If the value of <code>key</code> is boolean, return the
561 * boolean value, otherwise return false.
562 *
563 * @param key an <code>Object</code> specifying the key for the desired boolean value
564 * @return if the value of <code>key</code> is boolean, return the
565 * boolean value, otherwise return false.
566 * @since 1.4
567 */
568 public boolean getBoolean(Object key) {
569 Object value = get(key);
570 return (value instanceof Boolean) ? ((Boolean)value).booleanValue() : false;
571 }
572
573
574 /**
575 * If the value of <code>key</code> for the given <code>Locale</code>
576 * is boolean, return the boolean value, otherwise return false.
577 *
578 * @param key an <code>Object</code> specifying the key for the desired boolean value
579 * @param l the desired locale
580 * @return if the value for <code>key</code> and <code>Locale</code>
581 * is boolean, return the
582 * boolean value, otherwise return false.
583 * @since 1.4
584 */
585 public boolean getBoolean(Object key, Locale l) {
586 Object value = get(key,l);
587 return (value instanceof Boolean) ? ((Boolean)value).booleanValue() : false;
588 }
589
590
591 /**
592 * If the value of <code>key</code> is an <code>Insets</code> return it,
593 * otherwise return <code>null</code>.
594 * @param key the desired key
595 * @return if the value for <code>key</code> is an <code>Insets</code>,
596 * return the <code>Insets</code> object; otherwise return
597 * <code>null</code>
598 */
599 public Insets getInsets(Object key) {
600 Object value = get(key);
601 return (value instanceof Insets) ? (Insets)value : null;
602 }
603
604
605 /**
606 * If the value of <code>key</code> for the given <code>Locale</code>
607 * is an <code>Insets</code> return it, otherwise return <code>null</code>.
608 * @param key the desired key
609 * @param l the desired locale
610 * @return if the value for <code>key</code> and <code>Locale</code>
611 * is an <code>Insets</code>,
612 * return the <code>Insets</code> object; otherwise return
613 * <code>null</code>
614 * @since 1.4
615 */
616 public Insets getInsets(Object key, Locale l) {
617 Object value = get(key,l);
618 return (value instanceof Insets) ? (Insets)value : null;
619 }
620
621
622 /**
623 * If the value of <code>key</code> is a <code>Dimension</code> return it,
624 * otherwise return <code>null</code>.
625 * @param key the desired key
626 * @return if the value for <code>key</code> is a <code>Dimension</code>,
627 * return the <code>Dimension</code> object; otherwise return
628 * <code>null</code>
629 */
630 public Dimension getDimension(Object key) {
631 Object value = get(key);
632 return (value instanceof Dimension) ? (Dimension)value : null;
633 }
634
635
636 /**
637 * If the value of <code>key</code> for the given <code>Locale</code>
638 * is a <code>Dimension</code> return it, otherwise return <code>null</code>.
639 * @param key the desired key
640 * @param l the desired locale
641 * @return if the value for <code>key</code> and <code>Locale</code>
642 * is a <code>Dimension</code>,
643 * return the <code>Dimension</code> object; otherwise return
644 * <code>null</code>
645 * @since 1.4
646 */
647 public Dimension getDimension(Object key, Locale l) {
648 Object value = get(key,l);
649 return (value instanceof Dimension) ? (Dimension)value : null;
650 }
651
652
653 /**
654 * The value of <code>get(uidClassID)</code> must be the
655 * <code>String</code> name of a
656 * class that implements the corresponding <code>ComponentUI</code>
657 * class. If the class hasn't been loaded before, this method looks
658 * up the class with <code>uiClassLoader.loadClass()</code> if a non
659 * <code>null</code>
660 * class loader is provided, <code>classForName()</code> otherwise.
661 * <p>
662 * If a mapping for <code>uiClassID</code> exists or if the specified
663 * class can't be found, return <code>null</code>.
664 * <p>
665 * This method is used by <code>getUI</code>, it's usually
666 * not necessary to call it directly.
667 *
668 * @param uiClassID a string containing the class ID
669 * @param uiClassLoader the object which will load the class
670 * @return the value of <code>Class.forName(get(uidClassID))</code>
671 * @see #getUI
672 */
673 public Class<? extends ComponentUI>
674 getUIClass(String uiClassID, ClassLoader uiClassLoader)
675 {
676 try {
677 String className = (String)get(uiClassID);
678 if (className != null) {
679 Class cls = (Class)get(className);
680 if (cls == null) {
681 if (uiClassLoader == null) {
682 cls = SwingUtilities.loadSystemClass(className);
683 }
684 else {
685 cls = uiClassLoader.loadClass(className);
686 }
687 if (cls != null) {
688 // Save lookup for future use, as forName is slow.
689 put(className, cls);
690 }
691 }
692 return cls;
693 }
694 }
695 catch (ClassNotFoundException e) {
696 return null;
697 }
698 catch (ClassCastException e) {
699 return null;
700 }
701 return null;
702 }
703
704
705 /**
706 * Returns the L&F class that renders this component.
707 *
708 * @param uiClassID a string containing the class ID
709 * @return the Class object returned by
710 * <code>getUIClass(uiClassID, null)</code>
711 */
712 public Class<? extends ComponentUI> getUIClass(String uiClassID) {
713 return getUIClass(uiClassID, null);
714 }
715
716
717 /**
718 * If <code>getUI()</code> fails for any reason,
719 * it calls this method before returning <code>null</code>.
720 * Subclasses may choose to do more or less here.
721 *
722 * @param msg message string to print
723 * @see #getUI
724 */
725 protected void getUIError(String msg) {
726 System.err.println("UIDefaults.getUI() failed: " + msg);
727 try {
728 throw new Error();
729 }
730 catch (Throwable e) {
731 e.printStackTrace();
732 }
733 }
734
735 /**
736 * Creates an <code>ComponentUI</code> implementation for the
737 * specified component. In other words create the look
738 * and feel specific delegate object for <code>target</code>.
739 * This is done in two steps:
740 * <ul>
741 * <li> Look up the name of the <code>ComponentUI</code> implementation
742 * class under the value returned by <code>target.getUIClassID()</code>.
743 * <li> Use the implementation classes static <code>createUI()</code>
744 * method to construct a look and feel delegate.
745 * </ul>
746 * @param target the <code>JComponent</code> which needs a UI
747 * @return the <code>ComponentUI</code> object
748 */
749 public ComponentUI getUI(JComponent target) {
750
751 Object cl = get("ClassLoader");
752 ClassLoader uiClassLoader =
753 (cl != null) ? (ClassLoader)cl : target.getClass().getClassLoader();
754 Class uiClass = getUIClass(target.getUIClassID(), uiClassLoader);
755 Object uiObject = null;
756
757 if (uiClass == null) {
758 getUIError("no ComponentUI class for: " + target);
759 }
760 else {
761 try {
762 Method m = (Method)get(uiClass);
763 if (m == null) {
764 Class acClass = javax.swing.JComponent.class;
765 m = uiClass.getMethod("createUI", new Class[]{acClass});
766 put(uiClass, m);
767 }
768 uiObject = MethodUtil.invoke(m, null, new Object[]{target});
769 }
770 catch (NoSuchMethodException e) {
771 getUIError("static createUI() method not found in " + uiClass);
772 }
773 catch (Exception e) {
774 getUIError("createUI() failed for " + target + " " + e);
775 }
776 }
777
778 return (ComponentUI)uiObject;
779 }
780
781 /**
782 * Adds a <code>PropertyChangeListener</code> to the listener list.
783 * The listener is registered for all properties.
784 * <p>
785 * A <code>PropertyChangeEvent</code> will get fired whenever a default
786 * is changed.
787 *
788 * @param listener the <code>PropertyChangeListener</code> to be added
789 * @see java.beans.PropertyChangeSupport
790 */
791 public synchronized void addPropertyChangeListener(PropertyChangeListener listener) {
792 if (changeSupport == null) {
793 changeSupport = new SwingPropertyChangeSupport(this);
794 }
795 changeSupport.addPropertyChangeListener(listener);
796 }
797
798
799 /**
800 * Removes a <code>PropertyChangeListener</code> from the listener list.
801 * This removes a <code>PropertyChangeListener</code> that was registered
802 * for all properties.
803 *
804 * @param listener the <code>PropertyChangeListener</code> to be removed
805 * @see java.beans.PropertyChangeSupport
806 */
807 public synchronized void removePropertyChangeListener(PropertyChangeListener listener) {
808 if (changeSupport != null) {
809 changeSupport.removePropertyChangeListener(listener);
810 }
811 }
812
813
814 /**
815 * Returns an array of all the <code>PropertyChangeListener</code>s added
816 * to this UIDefaults with addPropertyChangeListener().
817 *
818 * @return all of the <code>PropertyChangeListener</code>s added or an empty
819 * array if no listeners have been added
820 * @since 1.4
821 */
822 public synchronized PropertyChangeListener[] getPropertyChangeListeners() {
823 if (changeSupport == null) {
824 return new PropertyChangeListener[0];
825 }
826 return changeSupport.getPropertyChangeListeners();
827 }
828
829
830 /**
831 * Support for reporting bound property changes. If oldValue and
832 * newValue are not equal and the <code>PropertyChangeEvent</code>x
833 * listener list isn't empty, then fire a
834 * <code>PropertyChange</code> event to each listener.
835 *
836 * @param propertyName the programmatic name of the property
837 * that was changed
838 * @param oldValue the old value of the property
839 * @param newValue the new value of the property
840 * @see java.beans.PropertyChangeSupport
841 */
842 protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
843 if (changeSupport != null) {
844 changeSupport.firePropertyChange(propertyName, oldValue, newValue);
845 }
846 }
847
848
849 /**
850 * Adds a resource bundle to the list of resource bundles that are
851 * searched for localized values. Resource bundles are searched in the
852 * reverse order they were added. In other words, the most recently added
853 * bundle is searched first.
854 *
855 * @param bundleName the base name of the resource bundle to be added
856 * @see java.util.ResourceBundle
857 * @see #removeResourceBundle
858 * @since 1.4
859 */
860 public synchronized void addResourceBundle( String bundleName ) {
861 if( bundleName == null ) {
862 return;
863 }
864 if( resourceBundles == null ) {
865 resourceBundles = new Vector(5);
866 }
867 if (!resourceBundles.contains(bundleName)) {
868 resourceBundles.add( bundleName );
869 resourceCache.clear();
870 }
871 }
872
873
874 /**
875 * Removes a resource bundle from the list of resource bundles that are
876 * searched for localized defaults.
877 *
878 * @param bundleName the base name of the resource bundle to be removed
879 * @see java.util.ResourceBundle
880 * @see #addResourceBundle
881 * @since 1.4
882 */
883 public synchronized void removeResourceBundle( String bundleName ) {
884 if( resourceBundles != null ) {
885 resourceBundles.remove( bundleName );
886 }
887 resourceCache.clear();
888 }
889
890 /**
891 * Sets the default locale. The default locale is used in retrieving
892 * localized values via <code>get</code> methods that do not take a
893 * locale argument. As of release 1.4, Swing UI objects should retrieve
894 * localized values using the locale of their component rather than the
895 * default locale. The default locale exists to provide compatibility with
896 * pre 1.4 behaviour.
897 *
898 * @param l the new default locale
899 * @see #getDefaultLocale
900 * @see #get(Object)
901 * @see #get(Object,Locale)
902 * @since 1.4
903 */
904 public void setDefaultLocale( Locale l ) {
905 defaultLocale = l;
906 }
907
908 /**
909 * Returns the default locale. The default locale is used in retrieving
910 * localized values via <code>get</code> methods that do not take a
911 * locale argument. As of release 1.4, Swing UI objects should retrieve
912 * localized values using the locale of their component rather than the
913 * default locale. The default locale exists to provide compatibility with
914 * pre 1.4 behaviour.
915 *
916 * @return the default locale
917 * @see #setDefaultLocale
918 * @see #get(Object)
919 * @see #get(Object,Locale)
920 * @since 1.4
921 */
922 public Locale getDefaultLocale() {
923 return defaultLocale;
924 }
925
926 /**
927 * This class enables one to store an entry in the defaults
928 * table that isn't constructed until the first time it's
929 * looked up with one of the <code>getXXX(key)</code> methods.
930 * Lazy values are useful for defaults that are expensive
931 * to construct or are seldom retrieved. The first time
932 * a <code>LazyValue</code> is retrieved its "real value" is computed
933 * by calling <code>LazyValue.createValue()</code> and the real
934 * value is used to replace the <code>LazyValue</code> in the
935 * <code>UIDefaults</code>
936 * table. Subsequent lookups for the same key return
937 * the real value. Here's an example of a <code>LazyValue</code>
938 * that constructs a <code>Border</code>:
939 * <pre>
940 * Object borderLazyValue = new UIDefaults.LazyValue() {
941 * public Object createValue(UIDefaults table) {
942 * return new BorderFactory.createLoweredBevelBorder();
943 * }
944 * };
945 *
946 * uiDefaultsTable.put("MyBorder", borderLazyValue);
947 * </pre>
948 *
949 * @see UIDefaults#get
950 */
951 public interface LazyValue {
952 /**
953 * Creates the actual value retrieved from the <code>UIDefaults</code>
954 * table. When an object that implements this interface is
955 * retrieved from the table, this method is used to create
956 * the real value, which is then stored in the table and
957 * returned to the calling method.
958 *
959 * @param table a <code>UIDefaults</code> table
960 * @return the created <code>Object</code>
961 */
962 Object createValue(UIDefaults table);
963 }
964
965
966 /**
967 * This class enables one to store an entry in the defaults
968 * table that's constructed each time it's looked up with one of
969 * the <code>getXXX(key)</code> methods. Here's an example of
970 * an <code>ActiveValue</code> that constructs a
971 * <code>DefaultListCellRenderer</code>:
972 * <pre>
973 * Object cellRendererActiveValue = new UIDefaults.ActiveValue() {
974 * public Object createValue(UIDefaults table) {
975 * return new DefaultListCellRenderer();
976 * }
977 * };
978 *
979 * uiDefaultsTable.put("MyRenderer", cellRendererActiveValue);
980 * </pre>
981 *
982 * @see UIDefaults#get
983 */
984 public interface ActiveValue {
985 /**
986 * Creates the value retrieved from the <code>UIDefaults</code> table.
987 * The object is created each time it is accessed.
988 *
989 * @param table a <code>UIDefaults</code> table
990 * @return the created <code>Object</code>
991 */
992 Object createValue(UIDefaults table);
993 }
994
995 /**
996 * This class provides an implementation of <code>LazyValue</code>
997 * which can be
998 * used to delay loading of the Class for the instance to be created.
999 * It also avoids creation of an anonymous inner class for the
1000 * <code>LazyValue</code>
1001 * subclass. Both of these improve performance at the time that a
1002 * a Look and Feel is loaded, at the cost of a slight performance
1003 * reduction the first time <code>createValue</code> is called
1004 * (since Reflection APIs are used).
1005 * @since 1.3
1006 */
1007 public static class ProxyLazyValue implements LazyValue {
1008 private AccessControlContext acc;
1009 private String className;
1010 private String methodName;
1011 private Object[] args;
1012
1013 /**
1014 * Creates a <code>LazyValue</code> which will construct an instance
1015 * when asked.
1016 *
1017 * @param c a <code>String</code> specifying the classname
1018 * of the instance to be created on demand
1019 */
1020 public ProxyLazyValue(String c) {
1021 this(c, (String)null);
1022 }
1023 /**
1024 * Creates a <code>LazyValue</code> which will construct an instance
1025 * when asked.
1026 *
1027 * @param c a <code>String</code> specifying the classname of
1028 * the class
1029 * containing a static method to be called for
1030 * instance creation
1031 * @param m a <code>String</code> specifying the static
1032 * method to be called on class c
1033 */
1034 public ProxyLazyValue(String c, String m) {
1035 this(c, m, null);
1036 }
1037 /**
1038 * Creates a <code>LazyValue</code> which will construct an instance
1039 * when asked.
1040 *
1041 * @param c a <code>String</code> specifying the classname
1042 * of the instance to be created on demand
1043 * @param o an array of <code>Objects</code> to be passed as
1044 * paramaters to the constructor in class c
1045 */
1046 public ProxyLazyValue(String c, Object[] o) {
1047 this(c, null, o);
1048 }
1049 /**
1050 * Creates a <code>LazyValue</code> which will construct an instance
1051 * when asked.
1052 *
1053 * @param c a <code>String</code> specifying the classname
1054 * of the class
1055 * containing a static method to be called for
1056 * instance creation.
1057 * @param m a <code>String</code> specifying the static method
1058 * to be called on class c
1059 * @param o an array of <code>Objects</code> to be passed as
1060 * paramaters to the static method in class c
1061 */
1062 public ProxyLazyValue(String c, String m, Object[] o) {
1063 acc = AccessController.getContext();
1064 className = c;
1065 methodName = m;
1066 if (o != null) {
1067 args = (Object[])o.clone();
1068 }
1069 }
1070
1071 /**
1072 * Creates the value retrieved from the <code>UIDefaults</code> table.
1073 * The object is created each time it is accessed.
1074 *
1075 * @param table a <code>UIDefaults</code> table
1076 * @return the created <code>Object</code>
1077 */
1078 public Object createValue(final UIDefaults table) {
1079 // In order to pick up the security policy in effect at the
1080 // time of creation we use a doPrivileged with the
1081 // AccessControlContext that was in place when this was created.
1082 return AccessController.doPrivileged(new PrivilegedAction() {
1083 public Object run() {
1084 try {
1085 Class c;
1086 Object cl;
1087 // See if we should use a separate ClassLoader
1088 if (table == null || !((cl = table.get("ClassLoader"))
1089 instanceof ClassLoader)) {
1090 cl = Thread.currentThread().
1091 getContextClassLoader();
1092 if (cl == null) {
1093 // Fallback to the system class loader.
1094 cl = ClassLoader.getSystemClassLoader();
1095 }
1096 }
1097 c = Class.forName(className, true, (ClassLoader)cl);
1098 if (methodName != null) {
1099 Class[] types = getClassArray(args);
1100 Method m = c.getMethod(methodName, types);
1101 return MethodUtil.invoke(m, c, args);
1102 } else {
1103 Class[] types = getClassArray(args);
1104 Constructor constructor = c.getConstructor(types);
1105 return constructor.newInstance(args);
1106 }
1107 } catch(Exception e) {
1108 // Ideally we would throw an exception, unfortunately
1109 // often times there are errors as an initial look and
1110 // feel is loaded before one can be switched. Perhaps a
1111 // flag should be added for debugging, so that if true
1112 // the exception would be thrown.
1113 }
1114 return null;
1115 }
1116 }, acc);
1117 }
1118
1119 /*
1120 * Coerce the array of class types provided into one which
1121 * looks the way the Reflection APIs expect. This is done
1122 * by substituting primitive types for their Object counterparts,
1123 * and superclasses for subclasses used to add the
1124 * <code>UIResource</code> tag.
1125 */
1126 private Class[] getClassArray(Object[] args) {
1127 Class[] types = null;
1128 if (args!=null) {
1129 types = new Class[args.length];
1130 for (int i = 0; i< args.length; i++) {
1131 /* PENDING(ges): At present only the primitive types
1132 used are handled correctly; this should eventually
1133 handle all primitive types */
1134 if (args[i] instanceof java.lang.Integer) {
1135 types[i]=Integer.TYPE;
1136 } else if (args[i] instanceof java.lang.Boolean) {
1137 types[i]=Boolean.TYPE;
1138 } else if (args[i] instanceof javax.swing.plaf.ColorUIResource) {
1139 /* PENDING(ges) Currently the Reflection APIs do not
1140 search superclasses of parameters supplied for
1141 constructor/method lookup. Since we only have
1142 one case where this is needed, we substitute
1143 directly instead of adding a massive amount
1144 of mechanism for this. Eventually this will
1145 probably need to handle the general case as well.
1146 */
1147 types[i]=java.awt.Color.class;
1148 } else {
1149 types[i]=args[i].getClass();
1150 }
1151 }
1152 }
1153 return types;
1154 }
1155
1156 private String printArgs(Object[] array) {
1157 String s = "{";
1158 if (array !=null) {
1159 for (int i = 0 ; i < array.length-1; i++) {
1160 s = s.concat(array[i] + ",");
1161 }
1162 s = s.concat(array[array.length-1] + "}");
1163 } else {
1164 s = s.concat("}");
1165 }
1166 return s;
1167 }
1168 }
1169
1170
1171 /**
1172 * <code>LazyInputMap</code> will create a <code>InputMap</code>
1173 * in its <code>createValue</code>
1174 * method. The bindings are passed in in the constructor.
1175 * The bindings are an array with
1176 * the even number entries being string <code>KeyStrokes</code>
1177 * (eg "alt SPACE") and
1178 * the odd number entries being the value to use in the
1179 * <code>InputMap</code> (and the key in the <code>ActionMap</code>).
1180 * @since 1.3
1181 */
1182 public static class LazyInputMap implements LazyValue {
1183 /** Key bindings are registered under. */
1184 private Object[] bindings;
1185
1186 public LazyInputMap(Object[] bindings) {
1187 this.bindings = bindings;
1188 }
1189
1190 /**
1191 * Creates an <code>InputMap</code> with the bindings that are
1192 * passed in.
1193 *
1194 * @param table a <code>UIDefaults</code> table
1195 * @return the <code>InputMap</code>
1196 */
1197 public Object createValue(UIDefaults table) {
1198 if (bindings != null) {
1199 InputMap km = LookAndFeel.makeInputMap(bindings);
1200 return km;
1201 }
1202 return null;
1203 }
1204 }
1205}