History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/javax/swing/UIDefaults.java (1 of 18352 files). Show all

Unified · Split

java/pkg/javax/swing/UIDefaults.java+1218−0
@@ -0,0 +1,1218 @@
1/*
2 * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package javax.swing;
27
28
29import javax.swing.plaf.ComponentUI;
30import javax.swing.border.*;
31import javax.swing.event.SwingPropertyChangeSupport;
32
33import java.lang.reflect.*;
34import java.util.HashMap;
35import java.util.Map;
36import java.util.Enumeration;
37import java.util.Hashtable;
38import java.util.ResourceBundle;
39import java.util.ResourceBundle.Control;
40import java.util.Locale;
41import java.util.Vector;
42import java.util.MissingResourceException;
43import java.awt.Font;
44import java.awt.Color;
45import java.awt.Insets;
46import java.awt.Dimension;
47import java.lang.reflect.Method;
48import java.beans.PropertyChangeListener;
49import java.beans.PropertyChangeEvent;
50import java.security.AccessController;
51import java.security.AccessControlContext;
52import java.security.PrivilegedAction;
53
54import sun.reflect.misc.MethodUtil;
55import sun.reflect.misc.ReflectUtil;
56import sun.util.CoreResourceBundleControl;
57
58/**
59 * A table of defaults for Swing components. Applications can set/get
60 * default values via the <code>UIManager</code>.
61 * <p>
62 * <strong>Warning:</strong>
63 * Serialized objects of this class will not be compatible with
64 * future Swing releases. The current serialization support is
65 * appropriate for short term storage or RMI between applications running
66 * the same version of Swing. As of 1.4, support for long term storage
67 * of all JavaBeans<sup><font size="-2">TM</font></sup>
68 * has been added to the <code>java.beans</code> package.
69 * Please see {@link java.beans.XMLEncoder}.
70 *
71 * @see UIManager
72 * @author Hans Muller
73 */
74public class UIDefaults extends Hashtable<Object,Object>
75{
76 private static final Object PENDING = "Pending";
77
78 private SwingPropertyChangeSupport changeSupport;
79
80 private Vector<String> resourceBundles;
81
82 private Locale defaultLocale = Locale.getDefault();
83
84 /**
85 * Maps from a Locale to a cached Map of the ResourceBundle. This is done
86 * so as to avoid an exception being thrown when a value is asked for.
87 * Access to this should be done while holding a lock on the
88 * UIDefaults, eg synchronized(this).
89 */
90 private Map<Locale, Map<String, Object>> resourceCache;
91
92 /**
93 * Creates an empty defaults table.
94 */
95 public UIDefaults() {
96 this(700, .75f);
97 }
98
99 /**
100 * Creates an empty defaults table with the specified initial capacity and
101 * load factor.
102 *
103 * @param initialCapacity the initial capacity of the defaults table
104 * @param loadFactor the load factor of the defaults table
105 * @see java.util.Hashtable
106 * @since 1.6
107 */
108 public UIDefaults(int initialCapacity, float loadFactor) {
109 super(initialCapacity, loadFactor);
110 resourceCache = new HashMap<Locale, Map<String, Object>>();
111 }
112
113
114 /**
115 * Creates a defaults table initialized with the specified
116 * key/value pairs. For example:
117 * <pre>
118 Object[] uiDefaults = {
119 "Font", new Font("Dialog", Font.BOLD, 12),
120 "Color", Color.red,
121 "five", new Integer(5)
122 }
123 UIDefaults myDefaults = new UIDefaults(uiDefaults);
124 * </pre>
125 * @param keyValueList an array of objects containing the key/value
126 * pairs
127 */
128 public UIDefaults(Object[] keyValueList) {
129 super(keyValueList.length / 2);
130 for(int i = 0; i < keyValueList.length; i += 2) {
131 super.put(keyValueList[i], keyValueList[i + 1]);
132 }
133 }
134
135 /**
136 * Returns the value for key. If the value is a
137 * <code>UIDefaults.LazyValue</code> then the real
138 * value is computed with <code>LazyValue.createValue()</code>,
139 * the table entry is replaced, and the real value is returned.
140 * If the value is an <code>UIDefaults.ActiveValue</code>
141 * the table entry is not replaced - the value is computed
142 * with <code>ActiveValue.createValue()</code> for each
143 * <code>get()</code> call.
144 *
145 * If the key is not found in the table then it is searched for in the list
146 * of resource bundles maintained by this object. The resource bundles are
147 * searched most recently added first using the locale returned by
148 * <code>getDefaultLocale</code>. <code>LazyValues</code> and
149 * <code>ActiveValues</code> are not supported in the resource bundles.
150
151 *
152 * @param key the desired key
153 * @return the value for <code>key</code>
154 * @see LazyValue
155 * @see ActiveValue
156 * @see java.util.Hashtable#get
157 * @see #getDefaultLocale
158 * @see #addResourceBundle
159 * @since 1.4
160 */
161 public Object get(Object key) {
162 Object value = getFromHashtable( key );
163 return (value != null) ? value : getFromResourceBundle(key, null);
164 }
165
166 /**
167 * Looks up up the given key in our Hashtable and resolves LazyValues
168 * or ActiveValues.
169 */
170 private Object getFromHashtable(Object key) {
171 /* Quickly handle the common case, without grabbing
172 * a lock.
173 */
174 Object value = super.get(key);
175 if ((value != PENDING) &&
176 !(value instanceof ActiveValue) &&
177 !(value instanceof LazyValue)) {
178 return value;
179 }
180
181 /* If the LazyValue for key is being constructed by another
182 * thread then wait and then return the new value, otherwise drop
183 * the lock and construct the ActiveValue or the LazyValue.
184 * We use the special value PENDING to mark LazyValues that
185 * are being constructed.
186 */
187 synchronized(this) {
188 value = super.get(key);
189 if (value == PENDING) {
190 do {
191 try {
192 this.wait();
193 }
194 catch (InterruptedException e) {
195 }
196 value = super.get(key);
197 }
198 while(value == PENDING);
199 return value;
200 }
201 else if (value instanceof LazyValue) {
202 super.put(key, PENDING);
203 }
204 else if (!(value instanceof ActiveValue)) {
205 return value;
206 }
207 }
208
209 /* At this point we know that the value of key was
210 * a LazyValue or an ActiveValue.
211 */
212 if (value instanceof LazyValue) {
213 try {
214 /* If an exception is thrown we'll just put the LazyValue
215 * back in the table.
216 */
217 value = ((LazyValue)value).createValue(this);
218 }
219 finally {
220 synchronized(this) {
221 if (value == null) {
222 super.remove(key);
223 }
224 else {
225 super.put(key, value);
226 }
227 this.notifyAll();
228 }
229 }
230 }
231 else {
232 value = ((ActiveValue)value).createValue(this);
233 }
234
235 return value;
236 }
237
238
239 /**
240 * Returns the value for key associated with the given locale.
241 * If the value is a <code>UIDefaults.LazyValue</code> then the real
242 * value is computed with <code>LazyValue.createValue()</code>,
243 * the table entry is replaced, and the real value is returned.
244 * If the value is an <code>UIDefaults.ActiveValue</code>
245 * the table entry is not replaced - the value is computed
246 * with <code>ActiveValue.createValue()</code> for each
247 * <code>get()</code> call.
248 *
249 * If the key is not found in the table then it is searched for in the list
250 * of resource bundles maintained by this object. The resource bundles are
251 * searched most recently added first using the given locale.
252 * <code>LazyValues</code> and <code>ActiveValues</code> are not supported
253 * in the resource bundles.
254 *
255 * @param key the desired key
256 * @param l the desired <code>locale</code>
257 * @return the value for <code>key</code>
258 * @see LazyValue
259 * @see ActiveValue
260 * @see java.util.Hashtable#get
261 * @see #addResourceBundle
262 * @since 1.4
263 */
264 public Object get(Object key, Locale l) {
265 Object value = getFromHashtable( key );
266 return (value != null) ? value : getFromResourceBundle(key, l);
267 }
268
269 /**
270 * Looks up given key in our resource bundles.
271 */
272 private Object getFromResourceBundle(Object key, Locale l) {
273
274 if( resourceBundles == null ||
275 resourceBundles.isEmpty() ||
276 !(key instanceof String) ) {
277 return null;
278 }
279
280 // A null locale means use the default locale.
281 if( l == null ) {
282 if( defaultLocale == null )
283 return null;
284 else
285 l = defaultLocale;
286 }
287
288 synchronized(this) {
289 return getResourceCache(l).get(key);
290 }
291 }
292
293 /**
294 * Returns a Map of the known resources for the given locale.
295 */
296 private Map<String, Object> getResourceCache(Locale l) {
297 Map<String, Object> values = resourceCache.get(l);
298
299 if (values == null) {
300 values = new HashMap<String, Object>();
301 for (int i=resourceBundles.size()-1; i >= 0; i--) {
302 String bundleName = resourceBundles.get(i);
303 try {
304 Control c = CoreResourceBundleControl.getRBControlInstance(bundleName);
305 ResourceBundle b;
306 if (c != null) {
307 b = ResourceBundle.getBundle(bundleName, l, c);
308 } else {
309 b = ResourceBundle.getBundle(bundleName, l);
310 }
311 Enumeration keys = b.getKeys();
312
313 while (keys.hasMoreElements()) {
314 String key = (String)keys.nextElement();
315
316 if (values.get(key) == null) {
317 Object value = b.getObject(key);
318
319 values.put(key, value);
320 }
321 }
322 } catch( MissingResourceException mre ) {
323 // Keep looking
324 }
325 }
326 resourceCache.put(l, values);
327 }
328 return values;
329 }
330
331 /**
332 * Sets the value of <code>key</code> to <code>value</code> for all locales.
333 * If <code>key</code> is a string and the new value isn't
334 * equal to the old one, fire a <code>PropertyChangeEvent</code>.
335 * If value is <code>null</code>, the key is removed from the table.
336 *
337 * @param key the unique <code>Object</code> who's value will be used
338 * to retrieve the data value associated with it
339 * @param value the new <code>Object</code> to store as data under
340 * that key
341 * @return the previous <code>Object</code> value, or <code>null</code>
342 * @see #putDefaults
343 * @see java.util.Hashtable#put
344 */
345 public Object put(Object key, Object value) {
346 Object oldValue = (value == null) ? super.remove(key) : super.put(key, value);
347 if (key instanceof String) {
348 firePropertyChange((String)key, oldValue, value);
349 }
350 return oldValue;
351 }
352
353
354 /**
355 * Puts all of the key/value pairs in the database and
356 * unconditionally generates one <code>PropertyChangeEvent</code>.
357 * The events oldValue and newValue will be <code>null</code> and its
358 * <code>propertyName</code> will be "UIDefaults". The key/value pairs are
359 * added for all locales.
360 *
361 * @param keyValueList an array of key/value pairs
362 * @see #put
363 * @see java.util.Hashtable#put
364 */
365 public void putDefaults(Object[] keyValueList) {
366 for(int i = 0, max = keyValueList.length; i < max; i += 2) {
367 Object value = keyValueList[i + 1];
368 if (value == null) {
369 super.remove(keyValueList[i]);
370 }
371 else {
372 super.put(keyValueList[i], value);
373 }
374 }
375 firePropertyChange("UIDefaults", null, null);
376 }
377
378
379 /**
380 * If the value of <code>key</code> is a <code>Font</code> return it,
381 * otherwise return <code>null</code>.
382 * @param key the desired key
383 * @return if the value for <code>key</code> is a <code>Font</code>,
384 * return the <code>Font</code> object; otherwise return
385 * <code>null</code>
386 */
387 public Font getFont(Object key) {
388 Object value = get(key);
389 return (value instanceof Font) ? (Font)value : null;
390 }
391
392
393 /**
394 * If the value of <code>key</code> for the given <code>Locale</code>
395 * is a <code>Font</code> return it, otherwise return <code>null</code>.
396 * @param key the desired key
397 * @param l the desired locale
398 * @return if the value for <code>key</code> and <code>Locale</code>
399 * is a <code>Font</code>,
400 * return the <code>Font</code> object; otherwise return
401 * <code>null</code>
402 * @since 1.4
403 */
404 public Font getFont(Object key, Locale l) {
405 Object value = get(key,l);
406 return (value instanceof Font) ? (Font)value : null;
407 }
408
409 /**
410 * If the value of <code>key</code> is a <code>Color</code> return it,
411 * otherwise return <code>null</code>.
412 * @param key the desired key
413 * @return if the value for <code>key</code> is a <code>Color</code>,
414 * return the <code>Color</code> object; otherwise return
415 * <code>null</code>
416 */
417 public Color getColor(Object key) {
418 Object value = get(key);
419 return (value instanceof Color) ? (Color)value : null;
420 }
421
422
423 /**
424 * If the value of <code>key</code> for the given <code>Locale</code>
425 * is a <code>Color</code> return it, otherwise return <code>null</code>.
426 * @param key the desired key
427 * @param l the desired locale
428 * @return if the value for <code>key</code> and <code>Locale</code>
429 * is a <code>Color</code>,
430 * return the <code>Color</code> object; otherwise return
431 * <code>null</code>
432 * @since 1.4
433 */
434 public Color getColor(Object key, Locale l) {
435 Object value = get(key,l);
436 return (value instanceof Color) ? (Color)value : null;
437 }
438
439
440 /**
441 * If the value of <code>key</code> is an <code>Icon</code> return it,
442 * otherwise return <code>null</code>.
443 * @param key the desired key
444 * @return if the value for <code>key</code> is an <code>Icon</code>,
445 * return the <code>Icon</code> object; otherwise return
446 * <code>null</code>
447 */
448 public Icon getIcon(Object key) {
449 Object value = get(key);
450 return (value instanceof Icon) ? (Icon)value : null;
451 }
452
453
454 /**
455 * If the value of <code>key</code> for the given <code>Locale</code>
456 * is an <code>Icon</code> return it, otherwise return <code>null</code>.
457 * @param key the desired key
458 * @param l the desired locale
459 * @return if the value for <code>key</code> and <code>Locale</code>
460 * is an <code>Icon</code>,
461 * return the <code>Icon</code> object; otherwise return
462 * <code>null</code>
463 * @since 1.4
464 */
465 public Icon getIcon(Object key, Locale l) {
466 Object value = get(key,l);
467 return (value instanceof Icon) ? (Icon)value : null;
468 }
469
470
471 /**
472 * If the value of <code>key</code> is a <code>Border</code> return it,
473 * otherwise return <code>null</code>.
474 * @param key the desired key
475 * @return if the value for <code>key</code> is a <code>Border</code>,
476 * return the <code>Border</code> object; otherwise return
477 * <code>null</code>
478 */
479 public Border getBorder(Object key) {
480 Object value = get(key);
481 return (value instanceof Border) ? (Border)value : null;
482 }
483
484
485 /**
486 * If the value of <code>key</code> for the given <code>Locale</code>
487 * is a <code>Border</code> return it, otherwise return <code>null</code>.
488 * @param key the desired key
489 * @param l the desired locale
490 * @return if the value for <code>key</code> and <code>Locale</code>
491 * is a <code>Border</code>,
492 * return the <code>Border</code> object; otherwise return
493 * <code>null</code>
494 * @since 1.4
495 */
496 public Border getBorder(Object key, Locale l) {
497 Object value = get(key,l);
498 return (value instanceof Border) ? (Border)value : null;
499 }
500
501
502 /**
503 * If the value of <code>key</code> is a <code>String</code> return it,
504 * otherwise return <code>null</code>.
505 * @param key the desired key
506 * @return if the value for <code>key</code> is a <code>String</code>,
507 * return the <code>String</code> object; otherwise return
508 * <code>null</code>
509 */
510 public String getString(Object key) {
511 Object value = get(key);
512 return (value instanceof String) ? (String)value : null;
513 }
514
515 /**
516 * If the value of <code>key</code> for the given <code>Locale</code>
517 * is a <code>String</code> return it, otherwise return <code>null</code>.
518 * @param key the desired key
519 * @param l the desired <code>Locale</code>
520 * @return if the value for <code>key</code> for the given
521 * <code>Locale</code> is a <code>String</code>,
522 * return the <code>String</code> object; otherwise return
523 * <code>null</code>
524 * @since 1.4
525 */
526 public String getString(Object key, Locale l) {
527 Object value = get(key,l);
528 return (value instanceof String) ? (String)value : null;
529 }
530
531 /**
532 * If the value of <code>key</code> is an <code>Integer</code> return its
533 * integer value, otherwise return 0.
534 * @param key the desired key
535 * @return if the value for <code>key</code> is an <code>Integer</code>,
536 * return its value, otherwise return 0
537 */
538 public int getInt(Object key) {
539 Object value = get(key);
540 return (value instanceof Integer) ? ((Integer)value).intValue() : 0;
541 }
542
543
544 /**
545 * If the value of <code>key</code> for the given <code>Locale</code>
546 * is an <code>Integer</code> return its integer value, otherwise return 0.
547 * @param key the desired key
548 * @param l the desired locale
549 * @return if the value for <code>key</code> and <code>Locale</code>
550 * is an <code>Integer</code>,
551 * return its value, otherwise return 0
552 * @since 1.4
553 */
554 public int getInt(Object key, Locale l) {
555 Object value = get(key,l);
556 return (value instanceof Integer) ? ((Integer)value).intValue() : 0;
557 }
558
559
560 /**
561 * If the value of <code>key</code> is boolean, return the
562 * boolean value, otherwise return false.
563 *
564 * @param key an <code>Object</code> specifying the key for the desired boolean value
565 * @return if the value of <code>key</code> is boolean, return the
566 * boolean value, otherwise return false.
567 * @since 1.4
568 */
569 public boolean getBoolean(Object key) {
570 Object value = get(key);
571 return (value instanceof Boolean) ? ((Boolean)value).booleanValue() : false;
572 }
573
574
575 /**
576 * If the value of <code>key</code> for the given <code>Locale</code>
577 * is boolean, return the boolean value, otherwise return false.
578 *
579 * @param key an <code>Object</code> specifying the key for the desired boolean value
580 * @param l the desired locale
581 * @return if the value for <code>key</code> and <code>Locale</code>
582 * is boolean, return the
583 * boolean value, otherwise return false.
584 * @since 1.4
585 */
586 public boolean getBoolean(Object key, Locale l) {
587 Object value = get(key,l);
588 return (value instanceof Boolean) ? ((Boolean)value).booleanValue() : false;
589 }
590
591
592 /**
593 * If the value of <code>key</code> is an <code>Insets</code> return it,
594 * otherwise return <code>null</code>.
595 * @param key the desired key
596 * @return if the value for <code>key</code> is an <code>Insets</code>,
597 * return the <code>Insets</code> object; otherwise return
598 * <code>null</code>
599 */
600 public Insets getInsets(Object key) {
601 Object value = get(key);
602 return (value instanceof Insets) ? (Insets)value : null;
603 }
604
605
606 /**
607 * If the value of <code>key</code> for the given <code>Locale</code>
608 * is an <code>Insets</code> return it, otherwise return <code>null</code>.
609 * @param key the desired key
610 * @param l the desired locale
611 * @return if the value for <code>key</code> and <code>Locale</code>
612 * is an <code>Insets</code>,
613 * return the <code>Insets</code> object; otherwise return
614 * <code>null</code>
615 * @since 1.4
616 */
617 public Insets getInsets(Object key, Locale l) {
618 Object value = get(key,l);
619 return (value instanceof Insets) ? (Insets)value : null;
620 }
621
622
623 /**
624 * If the value of <code>key</code> is a <code>Dimension</code> return it,
625 * otherwise return <code>null</code>.
626 * @param key the desired key
627 * @return if the value for <code>key</code> is a <code>Dimension</code>,
628 * return the <code>Dimension</code> object; otherwise return
629 * <code>null</code>
630 */
631 public Dimension getDimension(Object key) {
632 Object value = get(key);
633 return (value instanceof Dimension) ? (Dimension)value : null;
634 }
635
636
637 /**
638 * If the value of <code>key</code> for the given <code>Locale</code>
639 * is a <code>Dimension</code> return it, otherwise return <code>null</code>.
640 * @param key the desired key
641 * @param l the desired locale
642 * @return if the value for <code>key</code> and <code>Locale</code>
643 * is a <code>Dimension</code>,
644 * return the <code>Dimension</code> object; otherwise return
645 * <code>null</code>
646 * @since 1.4
647 */
648 public Dimension getDimension(Object key, Locale l) {
649 Object value = get(key,l);
650 return (value instanceof Dimension) ? (Dimension)value : null;
651 }
652
653
654 /**
655 * The value of <code>get(uidClassID)</code> must be the
656 * <code>String</code> name of a
657 * class that implements the corresponding <code>ComponentUI</code>
658 * class. If the class hasn't been loaded before, this method looks
659 * up the class with <code>uiClassLoader.loadClass()</code> if a non
660 * <code>null</code>
661 * class loader is provided, <code>classForName()</code> otherwise.
662 * <p>
663 * If a mapping for <code>uiClassID</code> exists or if the specified
664 * class can't be found, return <code>null</code>.
665 * <p>
666 * This method is used by <code>getUI</code>, it's usually
667 * not necessary to call it directly.
668 *
669 * @param uiClassID a string containing the class ID
670 * @param uiClassLoader the object which will load the class
671 * @return the value of <code>Class.forName(get(uidClassID))</code>
672 * @see #getUI
673 */
674 public Class<? extends ComponentUI>
675 getUIClass(String uiClassID, ClassLoader uiClassLoader)
676 {
677 try {
678 String className = (String)get(uiClassID);
679 if (className != null) {
680 Class cls = (Class)get(className);
681 if (cls == null) {
682 if (uiClassLoader == null) {
683 cls = SwingUtilities.loadSystemClass(className);
684 }
685 else {
686 cls = uiClassLoader.loadClass(className);
687 }
688 if (cls != null) {
689 // Save lookup for future use, as forName is slow.
690 put(className, cls);
691 }
692 }
693 return cls;
694 }
695 }
696 catch (ClassNotFoundException e) {
697 return null;
698 }
699 catch (ClassCastException e) {
700 return null;
701 }
702 return null;
703 }
704
705
706 /**
707 * Returns the L&F class that renders this component.
708 *
709 * @param uiClassID a string containing the class ID
710 * @return the Class object returned by
711 * <code>getUIClass(uiClassID, null)</code>
712 */
713 public Class<? extends ComponentUI> getUIClass(String uiClassID) {
714 return getUIClass(uiClassID, null);
715 }
716
717
718 /**
719 * If <code>getUI()</code> fails for any reason,
720 * it calls this method before returning <code>null</code>.
721 * Subclasses may choose to do more or less here.
722 *
723 * @param msg message string to print
724 * @see #getUI
725 */
726 protected void getUIError(String msg) {
727 System.err.println("UIDefaults.getUI() failed: " + msg);
728 try {
729 throw new Error();
730 }
731 catch (Throwable e) {
732 e.printStackTrace();
733 }
734 }
735
736 /**
737 * Creates an <code>ComponentUI</code> implementation for the
738 * specified component. In other words create the look
739 * and feel specific delegate object for <code>target</code>.
740 * This is done in two steps:
741 * <ul>
742 * <li> Look up the name of the <code>ComponentUI</code> implementation
743 * class under the value returned by <code>target.getUIClassID()</code>.
744 * <li> Use the implementation classes static <code>createUI()</code>
745 * method to construct a look and feel delegate.
746 * </ul>
747 * @param target the <code>JComponent</code> which needs a UI
748 * @return the <code>ComponentUI</code> object
749 */
750 public ComponentUI getUI(JComponent target) {
751
752 Object cl = get("ClassLoader");
753 ClassLoader uiClassLoader =
754 (cl != null) ? (ClassLoader)cl : target.getClass().getClassLoader();
755 Class<? extends ComponentUI> uiClass = getUIClass(target.getUIClassID(), uiClassLoader);
756 Object uiObject = null;
757
758 if (uiClass == null) {
759 getUIError("no ComponentUI class for: " + target);
760 }
761 else {
762 try {
763 Method m = (Method)get(uiClass);
764 if (m == null) {
765 m = uiClass.getMethod("createUI", new Class[]{JComponent.class});
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<String>(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 = 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 if (acc == null && System.getSecurityManager() != null) {
1083 throw new SecurityException("null AccessControlContext");
1084 }
1085 return AccessController.doPrivileged(new PrivilegedAction<Object>() {
1086 public Object run() {
1087 try {
1088 Class<?> c;
1089 Object cl;
1090 // See if we should use a separate ClassLoader
1091 if (table == null || !((cl = table.get("ClassLoader"))
1092 instanceof ClassLoader)) {
1093 cl = Thread.currentThread().
1094 getContextClassLoader();
1095 if (cl == null) {
1096 // Fallback to the system class loader.
1097 cl = ClassLoader.getSystemClassLoader();
1098 }
1099 }
1100 ReflectUtil.checkPackageAccess(className);
1101 c = Class.forName(className, true, (ClassLoader)cl);
1102 checkAccess(c.getModifiers());
1103 if (methodName != null) {
1104 Class[] types = getClassArray(args);
1105 Method m = c.getMethod(methodName, types);
1106 return MethodUtil.invoke(m, c, args);
1107 } else {
1108 Class[] types = getClassArray(args);
1109 Constructor constructor = c.getConstructor(types);
1110 checkAccess(constructor.getModifiers());
1111 return constructor.newInstance(args);
1112 }
1113 } catch(Exception e) {
1114 // Ideally we would throw an exception, unfortunately
1115 // often times there are errors as an initial look and
1116 // feel is loaded before one can be switched. Perhaps a
1117 // flag should be added for debugging, so that if true
1118 // the exception would be thrown.
1119 }
1120 return null;
1121 }
1122 }, acc);
1123 }
1124
1125 private void checkAccess(int modifiers) {
1126 if(System.getSecurityManager() != null &&
1127 !Modifier.isPublic(modifiers)) {
1128 throw new SecurityException("Resource is not accessible");
1129 }
1130 }
1131
1132 /*
1133 * Coerce the array of class types provided into one which
1134 * looks the way the Reflection APIs expect. This is done
1135 * by substituting primitive types for their Object counterparts,
1136 * and superclasses for subclasses used to add the
1137 * <code>UIResource</code> tag.
1138 */
1139 private Class[] getClassArray(Object[] args) {
1140 Class[] types = null;
1141 if (args!=null) {
1142 types = new Class[args.length];
1143 for (int i = 0; i< args.length; i++) {
1144 /* PENDING(ges): At present only the primitive types
1145 used are handled correctly; this should eventually
1146 handle all primitive types */
1147 if (args[i] instanceof java.lang.Integer) {
1148 types[i]=Integer.TYPE;
1149 } else if (args[i] instanceof java.lang.Boolean) {
1150 types[i]=Boolean.TYPE;
1151 } else if (args[i] instanceof javax.swing.plaf.ColorUIResource) {
1152 /* PENDING(ges) Currently the Reflection APIs do not
1153 search superclasses of parameters supplied for
1154 constructor/method lookup. Since we only have
1155 one case where this is needed, we substitute
1156 directly instead of adding a massive amount
1157 of mechanism for this. Eventually this will
1158 probably need to handle the general case as well.
1159 */
1160 types[i]=java.awt.Color.class;
1161 } else {
1162 types[i]=args[i].getClass();
1163 }
1164 }
1165 }
1166 return types;
1167 }
1168
1169 private String printArgs(Object[] array) {
1170 String s = "{";
1171 if (array !=null) {
1172 for (int i = 0 ; i < array.length-1; i++) {
1173 s = s.concat(array[i] + ",");
1174 }
1175 s = s.concat(array[array.length-1] + "}");
1176 } else {
1177 s = s.concat("}");
1178 }
1179 return s;
1180 }
1181 }
1182
1183
1184 /**
1185 * <code>LazyInputMap</code> will create a <code>InputMap</code>
1186 * in its <code>createValue</code>
1187 * method. The bindings are passed in in the constructor.
1188 * The bindings are an array with
1189 * the even number entries being string <code>KeyStrokes</code>
1190 * (eg "alt SPACE") and
1191 * the odd number entries being the value to use in the
1192 * <code>InputMap</code> (and the key in the <code>ActionMap</code>).
1193 * @since 1.3
1194 */
1195 public static class LazyInputMap implements LazyValue {
1196 /** Key bindings are registered under. */
1197 private Object[] bindings;
1198
1199 public LazyInputMap(Object[] bindings) {
1200 this.bindings = bindings;
1201 }
1202
1203 /**
1204 * Creates an <code>InputMap</code> with the bindings that are
1205 * passed in.
1206 *
1207 * @param table a <code>UIDefaults</code> table
1208 * @return the <code>InputMap</code>
1209 */
1210 public Object createValue(UIDefaults table) {
1211 if (bindings != null) {
1212 InputMap km = LookAndFeel.makeInputMap(bindings);
1213 return km;
1214 }
1215 return null;
1216 }
1217 }
1218}