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+
26+package javax.swing;
27+
28+
29+import javax.swing.plaf.ComponentUI;
30+import javax.swing.border.*;
31+import javax.swing.event.SwingPropertyChangeSupport;
32+
33+import java.lang.reflect.*;
34+import java.util.HashMap;
35+import java.util.Map;
36+import java.util.Enumeration;
37+import java.util.Hashtable;
38+import java.util.ResourceBundle;
39+import java.util.ResourceBundle.Control;
40+import java.util.Locale;
41+import java.util.Vector;
42+import java.util.MissingResourceException;
43+import java.awt.Font;
44+import java.awt.Color;
45+import java.awt.Insets;
46+import java.awt.Dimension;
47+import java.lang.reflect.Method;
48+import java.beans.PropertyChangeListener;
49+import java.beans.PropertyChangeEvent;
50+import java.security.AccessController;
51+import java.security.AccessControlContext;
52+import java.security.PrivilegedAction;
53+
54+import sun.reflect.misc.MethodUtil;
55+import sun.reflect.misc.ReflectUtil;
56+import 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+ */
74+public 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+}