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.beans.PropertyChangeListener;
48 import java.security.AccessController;
49 import java.security.AccessControlContext;
50 import java.security.PrivilegedAction;
51
52 import sun.reflect.misc.MethodUtil;
53 import sun.reflect.misc.ReflectUtil;
54 import sun.swing.SwingUtilities2;
55 import 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™
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 * @since 1.2
73 */
74 @SuppressWarnings("serial") // Same-version serialization only
75 public class UIDefaults extends Hashtable<Object,Object>
76 {
77 private static final Object PENDING = new Object();
78
79 private SwingPropertyChangeSupport changeSupport;
80
81 private Vector<String> resourceBundles;
82
83 private Locale defaultLocale = Locale.getDefault();
84
85 /**
86 * Maps from a Locale to a cached Map of the ResourceBundle. This is done
87 * so as to avoid an exception being thrown when a value is asked for.
118 * <pre>
119 Object[] uiDefaults = {
120 "Font", new Font("Dialog", Font.BOLD, 12),
121 "Color", Color.red,
122 "five", new Integer(5)
123 }
124 UIDefaults myDefaults = new UIDefaults(uiDefaults);
125 * </pre>
126 * @param keyValueList an array of objects containing the key/value
127 * pairs
128 */
129 public UIDefaults(Object[] keyValueList) {
130 super(keyValueList.length / 2);
131 for(int i = 0; i < keyValueList.length; i += 2) {
132 super.put(keyValueList[i], keyValueList[i + 1]);
133 }
134 }
135
136 /**
137 * Returns the value for key. If the value is a
138 * <code>UIDefaults.LazyValue</code> then the real
139 * value is computed with <code>LazyValue.createValue()</code>,
140 * the table entry is replaced, and the real value is returned.
141 * If the value is an <code>UIDefaults.ActiveValue</code>
142 * the table entry is not replaced - the value is computed
143 * with <code>ActiveValue.createValue()</code> for each
144 * <code>get()</code> call.
145 *
146 * If the key is not found in the table then it is searched for in the list
147 * of resource bundles maintained by this object. The resource bundles are
148 * searched most recently added first using the locale returned by
149 * <code>getDefaultLocale</code>. <code>LazyValues</code> and
150 * <code>ActiveValues</code> are not supported in the resource bundles.
151
152 *
153 * @param key the desired key
154 * @return the value for <code>key</code>
155 * @see LazyValue
156 * @see ActiveValue
157 * @see java.util.Hashtable#get
158 * @see #getDefaultLocale
159 * @see #addResourceBundle
160 * @since 1.4
161 */
162 public Object get(Object key) {
163 Object value = getFromHashtable( key );
164 return (value != null) ? value : getFromResourceBundle(key, null);
165 }
166
167 /**
168 * Looks up the given key in our Hashtable and resolves LazyValues
169 * or ActiveValues.
170 */
171 private Object getFromHashtable(final Object key) {
172 /* Quickly handle the common case, without grabbing
173 * a lock.
174 */
222 if (value == null) {
223 super.remove(key);
224 }
225 else {
226 super.put(key, value);
227 }
228 this.notifyAll();
229 }
230 }
231 }
232 else {
233 value = ((ActiveValue)value).createValue(this);
234 }
235
236 return value;
237 }
238
239
240 /**
241 * Returns the value for key associated with the given locale.
242 * If the value is a <code>UIDefaults.LazyValue</code> then the real
243 * value is computed with <code>LazyValue.createValue()</code>,
244 * the table entry is replaced, and the real value is returned.
245 * If the value is an <code>UIDefaults.ActiveValue</code>
246 * the table entry is not replaced - the value is computed
247 * with <code>ActiveValue.createValue()</code> for each
248 * <code>get()</code> call.
249 *
250 * If the key is not found in the table then it is searched for in the list
251 * of resource bundles maintained by this object. The resource bundles are
252 * searched most recently added first using the given locale.
253 * <code>LazyValues</code> and <code>ActiveValues</code> are not supported
254 * in the resource bundles.
255 *
256 * @param key the desired key
257 * @param l the desired <code>locale</code>
258 * @return the value for <code>key</code>
259 * @see LazyValue
260 * @see ActiveValue
261 * @see java.util.Hashtable#get
262 * @see #addResourceBundle
263 * @since 1.4
264 */
265 public Object get(Object key, Locale l) {
266 Object value = getFromHashtable( key );
267 return (value != null) ? value : getFromResourceBundle(key, l);
268 }
269
270 /**
271 * Looks up given key in our resource bundles.
272 */
273 private Object getFromResourceBundle(Object key, Locale l) {
274
275 if( resourceBundles == null ||
276 resourceBundles.isEmpty() ||
277 !(key instanceof String) ) {
278 return null;
313
314 while (keys.hasMoreElements()) {
315 String key = keys.nextElement();
316
317 if (values.get(key) == null) {
318 Object value = b.getObject(key);
319
320 values.put(key, value);
321 }
322 }
323 } catch( MissingResourceException mre ) {
324 // Keep looking
325 }
326 }
327 resourceCache.put(l, values);
328 }
329 return values;
330 }
331
332 /**
333 * Sets the value of <code>key</code> to <code>value</code> for all locales.
334 * If <code>key</code> is a string and the new value isn't
335 * equal to the old one, fire a <code>PropertyChangeEvent</code>.
336 * If value is <code>null</code>, the key is removed from the table.
337 *
338 * @param key the unique <code>Object</code> who's value will be used
339 * to retrieve the data value associated with it
340 * @param value the new <code>Object</code> to store as data under
341 * that key
342 * @return the previous <code>Object</code> value, or <code>null</code>
343 * @see #putDefaults
344 * @see java.util.Hashtable#put
345 */
346 public Object put(Object key, Object value) {
347 Object oldValue = (value == null) ? super.remove(key) : super.put(key, value);
348 if (key instanceof String) {
349 firePropertyChange((String)key, oldValue, value);
350 }
351 return oldValue;
352 }
353
354
355 /**
356 * Puts all of the key/value pairs in the database and
357 * unconditionally generates one <code>PropertyChangeEvent</code>.
358 * The events oldValue and newValue will be <code>null</code> and its
359 * <code>propertyName</code> will be "UIDefaults". The key/value pairs are
360 * added for all locales.
361 *
362 * @param keyValueList an array of key/value pairs
363 * @see #put
364 * @see java.util.Hashtable#put
365 */
366 public void putDefaults(Object[] keyValueList) {
367 for(int i = 0, max = keyValueList.length; i < max; i += 2) {
368 Object value = keyValueList[i + 1];
369 if (value == null) {
370 super.remove(keyValueList[i]);
371 }
372 else {
373 super.put(keyValueList[i], value);
374 }
375 }
376 firePropertyChange("UIDefaults", null, null);
377 }
378
379
380 /**
381 * If the value of <code>key</code> is a <code>Font</code> return it,
382 * otherwise return <code>null</code>.
383 * @param key the desired key
384 * @return if the value for <code>key</code> is a <code>Font</code>,
385 * return the <code>Font</code> object; otherwise return
386 * <code>null</code>
387 */
388 public Font getFont(Object key) {
389 Object value = get(key);
390 return (value instanceof Font) ? (Font)value : null;
391 }
392
393
394 /**
395 * If the value of <code>key</code> for the given <code>Locale</code>
396 * is a <code>Font</code> return it, otherwise return <code>null</code>.
397 * @param key the desired key
398 * @param l the desired locale
399 * @return if the value for <code>key</code> and <code>Locale</code>
400 * is a <code>Font</code>,
401 * return the <code>Font</code> object; otherwise return
402 * <code>null</code>
403 * @since 1.4
404 */
405 public Font getFont(Object key, Locale l) {
406 Object value = get(key,l);
407 return (value instanceof Font) ? (Font)value : null;
408 }
409
410 /**
411 * If the value of <code>key</code> is a <code>Color</code> return it,
412 * otherwise return <code>null</code>.
413 * @param key the desired key
414 * @return if the value for <code>key</code> is a <code>Color</code>,
415 * return the <code>Color</code> object; otherwise return
416 * <code>null</code>
417 */
418 public Color getColor(Object key) {
419 Object value = get(key);
420 return (value instanceof Color) ? (Color)value : null;
421 }
422
423
424 /**
425 * If the value of <code>key</code> for the given <code>Locale</code>
426 * is a <code>Color</code> return it, otherwise return <code>null</code>.
427 * @param key the desired key
428 * @param l the desired locale
429 * @return if the value for <code>key</code> and <code>Locale</code>
430 * is a <code>Color</code>,
431 * return the <code>Color</code> object; otherwise return
432 * <code>null</code>
433 * @since 1.4
434 */
435 public Color getColor(Object key, Locale l) {
436 Object value = get(key,l);
437 return (value instanceof Color) ? (Color)value : null;
438 }
439
440
441 /**
442 * If the value of <code>key</code> is an <code>Icon</code> return it,
443 * otherwise return <code>null</code>.
444 * @param key the desired key
445 * @return if the value for <code>key</code> is an <code>Icon</code>,
446 * return the <code>Icon</code> object; otherwise return
447 * <code>null</code>
448 */
449 public Icon getIcon(Object key) {
450 Object value = get(key);
451 return (value instanceof Icon) ? (Icon)value : null;
452 }
453
454
455 /**
456 * If the value of <code>key</code> for the given <code>Locale</code>
457 * is an <code>Icon</code> return it, otherwise return <code>null</code>.
458 * @param key the desired key
459 * @param l the desired locale
460 * @return if the value for <code>key</code> and <code>Locale</code>
461 * is an <code>Icon</code>,
462 * return the <code>Icon</code> object; otherwise return
463 * <code>null</code>
464 * @since 1.4
465 */
466 public Icon getIcon(Object key, Locale l) {
467 Object value = get(key,l);
468 return (value instanceof Icon) ? (Icon)value : null;
469 }
470
471
472 /**
473 * If the value of <code>key</code> is a <code>Border</code> return it,
474 * otherwise return <code>null</code>.
475 * @param key the desired key
476 * @return if the value for <code>key</code> is a <code>Border</code>,
477 * return the <code>Border</code> object; otherwise return
478 * <code>null</code>
479 */
480 public Border getBorder(Object key) {
481 Object value = get(key);
482 return (value instanceof Border) ? (Border)value : null;
483 }
484
485
486 /**
487 * If the value of <code>key</code> for the given <code>Locale</code>
488 * is a <code>Border</code> return it, otherwise return <code>null</code>.
489 * @param key the desired key
490 * @param l the desired locale
491 * @return if the value for <code>key</code> and <code>Locale</code>
492 * is a <code>Border</code>,
493 * return the <code>Border</code> object; otherwise return
494 * <code>null</code>
495 * @since 1.4
496 */
497 public Border getBorder(Object key, Locale l) {
498 Object value = get(key,l);
499 return (value instanceof Border) ? (Border)value : null;
500 }
501
502
503 /**
504 * If the value of <code>key</code> is a <code>String</code> return it,
505 * otherwise return <code>null</code>.
506 * @param key the desired key
507 * @return if the value for <code>key</code> is a <code>String</code>,
508 * return the <code>String</code> object; otherwise return
509 * <code>null</code>
510 */
511 public String getString(Object key) {
512 Object value = get(key);
513 return (value instanceof String) ? (String)value : null;
514 }
515
516 /**
517 * If the value of <code>key</code> for the given <code>Locale</code>
518 * is a <code>String</code> return it, otherwise return <code>null</code>.
519 * @param key the desired key
520 * @param l the desired <code>Locale</code>
521 * @return if the value for <code>key</code> for the given
522 * <code>Locale</code> is a <code>String</code>,
523 * return the <code>String</code> object; otherwise return
524 * <code>null</code>
525 * @since 1.4
526 */
527 public String getString(Object key, Locale l) {
528 Object value = get(key,l);
529 return (value instanceof String) ? (String)value : null;
530 }
531
532 /**
533 * If the value of <code>key</code> is an <code>Integer</code> return its
534 * integer value, otherwise return 0.
535 * @param key the desired key
536 * @return if the value for <code>key</code> is an <code>Integer</code>,
537 * return its value, otherwise return 0
538 */
539 public int getInt(Object key) {
540 Object value = get(key);
541 return (value instanceof Integer) ? ((Integer)value).intValue() : 0;
542 }
543
544
545 /**
546 * If the value of <code>key</code> for the given <code>Locale</code>
547 * is an <code>Integer</code> return its integer value, otherwise return 0.
548 * @param key the desired key
549 * @param l the desired locale
550 * @return if the value for <code>key</code> and <code>Locale</code>
551 * is an <code>Integer</code>,
552 * return its value, otherwise return 0
553 * @since 1.4
554 */
555 public int getInt(Object key, Locale l) {
556 Object value = get(key,l);
557 return (value instanceof Integer) ? ((Integer)value).intValue() : 0;
558 }
559
560
561 /**
562 * If the value of <code>key</code> is boolean, return the
563 * boolean value, otherwise return false.
564 *
565 * @param key an <code>Object</code> specifying the key for the desired boolean value
566 * @return if the value of <code>key</code> is boolean, return the
567 * boolean value, otherwise return false.
568 * @since 1.4
569 */
570 public boolean getBoolean(Object key) {
571 Object value = get(key);
572 return (value instanceof Boolean) ? ((Boolean)value).booleanValue() : false;
573 }
574
575
576 /**
577 * If the value of <code>key</code> for the given <code>Locale</code>
578 * is boolean, return the boolean value, otherwise return false.
579 *
580 * @param key an <code>Object</code> specifying the key for the desired boolean value
581 * @param l the desired locale
582 * @return if the value for <code>key</code> and <code>Locale</code>
583 * is boolean, return the
584 * boolean value, otherwise return false.
585 * @since 1.4
586 */
587 public boolean getBoolean(Object key, Locale l) {
588 Object value = get(key,l);
589 return (value instanceof Boolean) ? ((Boolean)value).booleanValue() : false;
590 }
591
592
593 /**
594 * If the value of <code>key</code> is an <code>Insets</code> return it,
595 * otherwise return <code>null</code>.
596 * @param key the desired key
597 * @return if the value for <code>key</code> is an <code>Insets</code>,
598 * return the <code>Insets</code> object; otherwise return
599 * <code>null</code>
600 */
601 public Insets getInsets(Object key) {
602 Object value = get(key);
603 return (value instanceof Insets) ? (Insets)value : null;
604 }
605
606
607 /**
608 * If the value of <code>key</code> for the given <code>Locale</code>
609 * is an <code>Insets</code> return it, otherwise return <code>null</code>.
610 * @param key the desired key
611 * @param l the desired locale
612 * @return if the value for <code>key</code> and <code>Locale</code>
613 * is an <code>Insets</code>,
614 * return the <code>Insets</code> object; otherwise return
615 * <code>null</code>
616 * @since 1.4
617 */
618 public Insets getInsets(Object key, Locale l) {
619 Object value = get(key,l);
620 return (value instanceof Insets) ? (Insets)value : null;
621 }
622
623
624 /**
625 * If the value of <code>key</code> is a <code>Dimension</code> return it,
626 * otherwise return <code>null</code>.
627 * @param key the desired key
628 * @return if the value for <code>key</code> is a <code>Dimension</code>,
629 * return the <code>Dimension</code> object; otherwise return
630 * <code>null</code>
631 */
632 public Dimension getDimension(Object key) {
633 Object value = get(key);
634 return (value instanceof Dimension) ? (Dimension)value : null;
635 }
636
637
638 /**
639 * If the value of <code>key</code> for the given <code>Locale</code>
640 * is a <code>Dimension</code> return it, otherwise return <code>null</code>.
641 * @param key the desired key
642 * @param l the desired locale
643 * @return if the value for <code>key</code> and <code>Locale</code>
644 * is a <code>Dimension</code>,
645 * return the <code>Dimension</code> object; otherwise return
646 * <code>null</code>
647 * @since 1.4
648 */
649 public Dimension getDimension(Object key, Locale l) {
650 Object value = get(key,l);
651 return (value instanceof Dimension) ? (Dimension)value : null;
652 }
653
654
655 /**
656 * The value of <code>get(uidClassID)</code> must be the
657 * <code>String</code> name of a
658 * class that implements the corresponding <code>ComponentUI</code>
659 * class. If the class hasn't been loaded before, this method looks
660 * up the class with <code>uiClassLoader.loadClass()</code> if a non
661 * <code>null</code>
662 * class loader is provided, <code>classForName()</code> otherwise.
663 * <p>
664 * If a mapping for <code>uiClassID</code> exists or if the specified
665 * class can't be found, return <code>null</code>.
666 * <p>
667 * This method is used by <code>getUI</code>, it's usually
668 * not necessary to call it directly.
669 *
670 * @param uiClassID a string containing the class ID
671 * @param uiClassLoader the object which will load the class
672 * @return the value of <code>Class.forName(get(uidClassID))</code>
673 * @see #getUI
674 */
675 public Class<? extends ComponentUI>
676 getUIClass(String uiClassID, ClassLoader uiClassLoader)
677 {
678 try {
679 String className = (String)get(uiClassID);
680 if (className != null) {
681 ReflectUtil.checkPackageAccess(className);
682
683 Class<?> cls = (Class)get(className);
684 if (cls == null) {
685 if (uiClassLoader == null) {
686 cls = SwingUtilities.loadSystemClass(className);
687 }
688 else {
689 cls = uiClassLoader.loadClass(className);
690 }
691 if (cls != null) {
692 // Save lookup for future use, as forName is slow.
693 put(className, cls);
694 }
695 }
696 @SuppressWarnings("unchecked")
697 Class<? extends ComponentUI> tmp = (Class<? extends ComponentUI>)cls;
698 return tmp;
699 }
700 }
701 catch (ClassNotFoundException | ClassCastException e) {
702 return null;
703 }
704 return null;
705 }
706
707
708 /**
709 * Returns the L&F class that renders this component.
710 *
711 * @param uiClassID a string containing the class ID
712 * @return the Class object returned by
713 * <code>getUIClass(uiClassID, null)</code>
714 */
715 public Class<? extends ComponentUI> getUIClass(String uiClassID) {
716 return getUIClass(uiClassID, null);
717 }
718
719
720 /**
721 * If <code>getUI()</code> fails for any reason,
722 * it calls this method before returning <code>null</code>.
723 * Subclasses may choose to do more or less here.
724 *
725 * @param msg message string to print
726 * @see #getUI
727 */
728 protected void getUIError(String msg) {
729 System.err.println("UIDefaults.getUI() failed: " + msg);
730 try {
731 throw new Error();
732 }
733 catch (Throwable e) {
734 e.printStackTrace();
735 }
736 }
737
738 /**
739 * Creates an <code>ComponentUI</code> implementation for the
740 * specified component. In other words create the look
741 * and feel specific delegate object for <code>target</code>.
742 * This is done in two steps:
743 * <ul>
744 * <li> Look up the name of the <code>ComponentUI</code> implementation
745 * class under the value returned by <code>target.getUIClassID()</code>.
746 * <li> Use the implementation classes static <code>createUI()</code>
747 * method to construct a look and feel delegate.
748 * </ul>
749 * @param target the <code>JComponent</code> which needs a UI
750 * @return the <code>ComponentUI</code> object
751 */
752 public ComponentUI getUI(JComponent target) {
753
754 Object cl = get("ClassLoader");
755 ClassLoader uiClassLoader =
756 (cl != null) ? (ClassLoader)cl : target.getClass().getClassLoader();
757 Class<? extends ComponentUI> uiClass = getUIClass(target.getUIClassID(), uiClassLoader);
758 Object uiObject = null;
759
760 if (uiClass == null) {
761 getUIError("no ComponentUI class for: " + target);
762 }
763 else {
764 try {
765 Method m = (Method)get(uiClass);
766 if (m == null) {
767 m = uiClass.getMethod("createUI", new Class<?>[]{JComponent.class});
768 put(uiClass, m);
769 }
770 uiObject = MethodUtil.invoke(m, null, new Object[]{target});
771 }
772 catch (NoSuchMethodException e) {
773 getUIError("static createUI() method not found in " + uiClass);
774 }
775 catch (Exception e) {
776 getUIError("createUI() failed for " + target + " " + e);
777 }
778 }
779
780 return (ComponentUI)uiObject;
781 }
782
783 /**
784 * Adds a <code>PropertyChangeListener</code> to the listener list.
785 * The listener is registered for all properties.
786 * <p>
787 * A <code>PropertyChangeEvent</code> will get fired whenever a default
788 * is changed.
789 *
790 * @param listener the <code>PropertyChangeListener</code> to be added
791 * @see java.beans.PropertyChangeSupport
792 */
793 public synchronized void addPropertyChangeListener(PropertyChangeListener listener) {
794 if (changeSupport == null) {
795 changeSupport = new SwingPropertyChangeSupport(this);
796 }
797 changeSupport.addPropertyChangeListener(listener);
798 }
799
800
801 /**
802 * Removes a <code>PropertyChangeListener</code> from the listener list.
803 * This removes a <code>PropertyChangeListener</code> that was registered
804 * for all properties.
805 *
806 * @param listener the <code>PropertyChangeListener</code> to be removed
807 * @see java.beans.PropertyChangeSupport
808 */
809 public synchronized void removePropertyChangeListener(PropertyChangeListener listener) {
810 if (changeSupport != null) {
811 changeSupport.removePropertyChangeListener(listener);
812 }
813 }
814
815
816 /**
817 * Returns an array of all the <code>PropertyChangeListener</code>s added
818 * to this UIDefaults with addPropertyChangeListener().
819 *
820 * @return all of the <code>PropertyChangeListener</code>s added or an empty
821 * array if no listeners have been added
822 * @since 1.4
823 */
824 public synchronized PropertyChangeListener[] getPropertyChangeListeners() {
825 if (changeSupport == null) {
826 return new PropertyChangeListener[0];
827 }
828 return changeSupport.getPropertyChangeListeners();
829 }
830
831
832 /**
833 * Support for reporting bound property changes. If oldValue and
834 * newValue are not equal and the <code>PropertyChangeEvent</code>x
835 * listener list isn't empty, then fire a
836 * <code>PropertyChange</code> event to each listener.
837 *
838 * @param propertyName the programmatic name of the property
839 * that was changed
840 * @param oldValue the old value of the property
841 * @param newValue the new value of the property
842 * @see java.beans.PropertyChangeSupport
843 */
844 protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
845 if (changeSupport != null) {
846 changeSupport.firePropertyChange(propertyName, oldValue, newValue);
847 }
848 }
849
850
851 /**
852 * Adds a resource bundle to the list of resource bundles that are
853 * searched for localized values. Resource bundles are searched in the
854 * reverse order they were added. In other words, the most recently added
855 * bundle is searched first.
856 *
874
875
876 /**
877 * Removes a resource bundle from the list of resource bundles that are
878 * searched for localized defaults.
879 *
880 * @param bundleName the base name of the resource bundle to be removed
881 * @see java.util.ResourceBundle
882 * @see #addResourceBundle
883 * @since 1.4
884 */
885 public synchronized void removeResourceBundle( String bundleName ) {
886 if( resourceBundles != null ) {
887 resourceBundles.remove( bundleName );
888 }
889 resourceCache.clear();
890 }
891
892 /**
893 * Sets the default locale. The default locale is used in retrieving
894 * localized values via <code>get</code> methods that do not take a
895 * locale argument. As of release 1.4, Swing UI objects should retrieve
896 * localized values using the locale of their component rather than the
897 * default locale. The default locale exists to provide compatibility with
898 * pre 1.4 behaviour.
899 *
900 * @param l the new default locale
901 * @see #getDefaultLocale
902 * @see #get(Object)
903 * @see #get(Object,Locale)
904 * @since 1.4
905 */
906 public void setDefaultLocale( Locale l ) {
907 defaultLocale = l;
908 }
909
910 /**
911 * Returns the default locale. The default locale is used in retrieving
912 * localized values via <code>get</code> methods that do not take a
913 * locale argument. As of release 1.4, Swing UI objects should retrieve
914 * localized values using the locale of their component rather than the
915 * default locale. The default locale exists to provide compatibility with
916 * pre 1.4 behaviour.
917 *
918 * @return the default locale
919 * @see #setDefaultLocale
920 * @see #get(Object)
921 * @see #get(Object,Locale)
922 * @since 1.4
923 */
924 public Locale getDefaultLocale() {
925 return defaultLocale;
926 }
927
928 /**
929 * This class enables one to store an entry in the defaults
930 * table that isn't constructed until the first time it's
931 * looked up with one of the <code>getXXX(key)</code> methods.
932 * Lazy values are useful for defaults that are expensive
933 * to construct or are seldom retrieved. The first time
934 * a <code>LazyValue</code> is retrieved its "real value" is computed
935 * by calling <code>LazyValue.createValue()</code> and the real
936 * value is used to replace the <code>LazyValue</code> in the
937 * <code>UIDefaults</code>
938 * table. Subsequent lookups for the same key return
939 * the real value. Here's an example of a <code>LazyValue</code>
940 * that constructs a <code>Border</code>:
941 * <pre>
942 * Object borderLazyValue = new UIDefaults.LazyValue() {
943 * public Object createValue(UIDefaults table) {
944 * return new BorderFactory.createLoweredBevelBorder();
945 * }
946 * };
947 *
948 * uiDefaultsTable.put("MyBorder", borderLazyValue);
949 * </pre>
950 *
951 * @see UIDefaults#get
952 */
953 public interface LazyValue {
954 /**
955 * Creates the actual value retrieved from the <code>UIDefaults</code>
956 * table. When an object that implements this interface is
957 * retrieved from the table, this method is used to create
958 * the real value, which is then stored in the table and
959 * returned to the calling method.
960 *
961 * @param table a <code>UIDefaults</code> table
962 * @return the created <code>Object</code>
963 */
964 Object createValue(UIDefaults table);
965 }
966
967
968 /**
969 * This class enables one to store an entry in the defaults
970 * table that's constructed each time it's looked up with one of
971 * the <code>getXXX(key)</code> methods. Here's an example of
972 * an <code>ActiveValue</code> that constructs a
973 * <code>DefaultListCellRenderer</code>:
974 * <pre>
975 * Object cellRendererActiveValue = new UIDefaults.ActiveValue() {
976 * public Object createValue(UIDefaults table) {
977 * return new DefaultListCellRenderer();
978 * }
979 * };
980 *
981 * uiDefaultsTable.put("MyRenderer", cellRendererActiveValue);
982 * </pre>
983 *
984 * @see UIDefaults#get
985 */
986 public interface ActiveValue {
987 /**
988 * Creates the value retrieved from the <code>UIDefaults</code> table.
989 * The object is created each time it is accessed.
990 *
991 * @param table a <code>UIDefaults</code> table
992 * @return the created <code>Object</code>
993 */
994 Object createValue(UIDefaults table);
995 }
996
997 /**
998 * This class provides an implementation of <code>LazyValue</code>
999 * which can be
1000 * used to delay loading of the Class for the instance to be created.
1001 * It also avoids creation of an anonymous inner class for the
1002 * <code>LazyValue</code>
1003 * subclass. Both of these improve performance at the time that a
1004 * a Look and Feel is loaded, at the cost of a slight performance
1005 * reduction the first time <code>createValue</code> is called
1006 * (since Reflection APIs are used).
1007 * @since 1.3
1008 */
1009 public static class ProxyLazyValue implements LazyValue {
1010 private AccessControlContext acc;
1011 private String className;
1012 private String methodName;
1013 private Object[] args;
1014
1015 /**
1016 * Creates a <code>LazyValue</code> which will construct an instance
1017 * when asked.
1018 *
1019 * @param c a <code>String</code> specifying the classname
1020 * of the instance to be created on demand
1021 */
1022 public ProxyLazyValue(String c) {
1023 this(c, (String)null);
1024 }
1025 /**
1026 * Creates a <code>LazyValue</code> which will construct an instance
1027 * when asked.
1028 *
1029 * @param c a <code>String</code> specifying the classname of
1030 * the class
1031 * containing a static method to be called for
1032 * instance creation
1033 * @param m a <code>String</code> specifying the static
1034 * method to be called on class c
1035 */
1036 public ProxyLazyValue(String c, String m) {
1037 this(c, m, null);
1038 }
1039 /**
1040 * Creates a <code>LazyValue</code> which will construct an instance
1041 * when asked.
1042 *
1043 * @param c a <code>String</code> specifying the classname
1044 * of the instance to be created on demand
1045 * @param o an array of <code>Objects</code> to be passed as
1046 * paramaters to the constructor in class c
1047 */
1048 public ProxyLazyValue(String c, Object[] o) {
1049 this(c, null, o);
1050 }
1051 /**
1052 * Creates a <code>LazyValue</code> which will construct an instance
1053 * when asked.
1054 *
1055 * @param c a <code>String</code> specifying the classname
1056 * of the class
1057 * containing a static method to be called for
1058 * instance creation.
1059 * @param m a <code>String</code> specifying the static method
1060 * to be called on class c
1061 * @param o an array of <code>Objects</code> to be passed as
1062 * paramaters to the static method in class c
1063 */
1064 public ProxyLazyValue(String c, String m, Object[] o) {
1065 acc = AccessController.getContext();
1066 className = c;
1067 methodName = m;
1068 if (o != null) {
1069 args = o.clone();
1070 }
1071 }
1072
1073 /**
1074 * Creates the value retrieved from the <code>UIDefaults</code> table.
1075 * The object is created each time it is accessed.
1076 *
1077 * @param table a <code>UIDefaults</code> table
1078 * @return the created <code>Object</code>
1079 */
1080 public Object createValue(final UIDefaults table) {
1081 // In order to pick up the security policy in effect at the
1082 // time of creation we use a doPrivileged with the
1083 // AccessControlContext that was in place when this was created.
1084 if (acc == null && System.getSecurityManager() != null) {
1085 throw new SecurityException("null AccessControlContext");
1086 }
1087 return AccessController.doPrivileged(new PrivilegedAction<Object>() {
1088 public Object run() {
1089 try {
1090 Class<?> c;
1091 Object cl;
1092 // See if we should use a separate ClassLoader
1093 if (table == null || !((cl = table.get("ClassLoader"))
1094 instanceof ClassLoader)) {
1095 cl = Thread.currentThread().
1096 getContextClassLoader();
1097 if (cl == null) {
1098 // Fallback to the system class loader.
1112 SwingUtilities2.checkAccess(constructor.getModifiers());
1113 return constructor.newInstance(args);
1114 }
1115 } catch(Exception e) {
1116 // Ideally we would throw an exception, unfortunately
1117 // often times there are errors as an initial look and
1118 // feel is loaded before one can be switched. Perhaps a
1119 // flag should be added for debugging, so that if true
1120 // the exception would be thrown.
1121 }
1122 return null;
1123 }
1124 }, acc);
1125 }
1126
1127 /*
1128 * Coerce the array of class types provided into one which
1129 * looks the way the Reflection APIs expect. This is done
1130 * by substituting primitive types for their Object counterparts,
1131 * and superclasses for subclasses used to add the
1132 * <code>UIResource</code> tag.
1133 */
1134 private Class<?>[] getClassArray(Object[] args) {
1135 Class<?>[] types = null;
1136 if (args!=null) {
1137 types = new Class<?>[args.length];
1138 for (int i = 0; i< args.length; i++) {
1139 /* PENDING(ges): At present only the primitive types
1140 used are handled correctly; this should eventually
1141 handle all primitive types */
1142 if (args[i] instanceof java.lang.Integer) {
1143 types[i]=Integer.TYPE;
1144 } else if (args[i] instanceof java.lang.Boolean) {
1145 types[i]=Boolean.TYPE;
1146 } else if (args[i] instanceof javax.swing.plaf.ColorUIResource) {
1147 /* PENDING(ges) Currently the Reflection APIs do not
1148 search superclasses of parameters supplied for
1149 constructor/method lookup. Since we only have
1150 one case where this is needed, we substitute
1151 directly instead of adding a massive amount
1152 of mechanism for this. Eventually this will
1160 }
1161 return types;
1162 }
1163
1164 private String printArgs(Object[] array) {
1165 String s = "{";
1166 if (array !=null) {
1167 for (int i = 0 ; i < array.length-1; i++) {
1168 s = s.concat(array[i] + ",");
1169 }
1170 s = s.concat(array[array.length-1] + "}");
1171 } else {
1172 s = s.concat("}");
1173 }
1174 return s;
1175 }
1176 }
1177
1178
1179 /**
1180 * <code>LazyInputMap</code> will create a <code>InputMap</code>
1181 * in its <code>createValue</code>
1182 * method. The bindings are passed in the constructor.
1183 * The bindings are an array with
1184 * the even number entries being string <code>KeyStrokes</code>
1185 * (eg "alt SPACE") and
1186 * the odd number entries being the value to use in the
1187 * <code>InputMap</code> (and the key in the <code>ActionMap</code>).
1188 * @since 1.3
1189 */
1190 public static class LazyInputMap implements LazyValue {
1191 /** Key bindings are registered under. */
1192 private Object[] bindings;
1193
1194 /**
1195 * Constructs a {@code LazyInputMap}.
1196 * @param bindings the bindings
1197 */
1198 public LazyInputMap(Object[] bindings) {
1199 this.bindings = bindings;
1200 }
1201
1202 /**
1203 * Creates an <code>InputMap</code> with the bindings that are
1204 * passed in.
1205 *
1206 * @param table a <code>UIDefaults</code> table
1207 * @return the <code>InputMap</code>
1208 */
1209 public Object createValue(UIDefaults table) {
1210 if (bindings != null) {
1211 InputMap km = LookAndFeel.makeInputMap(bindings);
1212 return km;
1213 }
1214 return null;
1215 }
1216 }
1217
1218 /**
1219 * <code>TextAndMnemonicHashMap</code> stores swing resource strings. Many of strings
1220 * can have a mnemonic. For example:
1221 * FileChooser.saveButton.textAndMnemonic=&Save
1222 * For this case method get returns "Save" for the key "FileChooser.saveButtonText" and
1223 * mnemonic "S" for the key "FileChooser.saveButtonMnemonic"
1224 *
1225 * There are several patterns for the text and mnemonic suffixes which are checked by the
1226 * <code>TextAndMnemonicHashMap</code> class.
1227 * Patterns which are converted to the xxx.textAndMnemonic key:
1228 * (xxxNameText, xxxNameMnemonic)
1229 * (xxxNameText, xxxMnemonic)
1230 * (xxx.nameText, xxx.mnemonic)
1231 * (xxxText, xxxMnemonic)
1232 *
1233 * These patterns can have a mnemonic index in format
1234 * (xxxDisplayedMnemonicIndex)
1235 *
1236 * Pattern which is converted to the xxx.titleAndMnemonic key:
1237 * (xxxTitle, xxxMnemonic)
1238 *
1239 */
1240 private static class TextAndMnemonicHashMap extends HashMap<String, Object> {
1241
1242 static final String AND_MNEMONIC = "AndMnemonic";
1243 static final String TITLE_SUFFIX = ".titleAndMnemonic";
1244 static final String TEXT_SUFFIX = ".textAndMnemonic";
1245
1246 @Override
|
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.beans.PropertyChangeListener;
48 import java.security.AccessController;
49 import java.security.AccessControlContext;
50 import java.security.PrivilegedAction;
51
52 import sun.reflect.misc.MethodUtil;
53 import sun.reflect.misc.ReflectUtil;
54 import sun.swing.SwingUtilities2;
55 import sun.util.CoreResourceBundleControl;
56
57 /**
58 * A table of defaults for Swing components. Applications can set/get
59 * default values via the {@code UIManager}.
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™
67 * has been added to the {@code java.beans} package.
68 * Please see {@link java.beans.XMLEncoder}.
69 *
70 * @see UIManager
71 * @author Hans Muller
72 * @since 1.2
73 */
74 @SuppressWarnings("serial") // Same-version serialization only
75 public class UIDefaults extends Hashtable<Object,Object>
76 {
77 private static final Object PENDING = new Object();
78
79 private SwingPropertyChangeSupport changeSupport;
80
81 private Vector<String> resourceBundles;
82
83 private Locale defaultLocale = Locale.getDefault();
84
85 /**
86 * Maps from a Locale to a cached Map of the ResourceBundle. This is done
87 * so as to avoid an exception being thrown when a value is asked for.
118 * <pre>
119 Object[] uiDefaults = {
120 "Font", new Font("Dialog", Font.BOLD, 12),
121 "Color", Color.red,
122 "five", new Integer(5)
123 }
124 UIDefaults myDefaults = new UIDefaults(uiDefaults);
125 * </pre>
126 * @param keyValueList an array of objects containing the key/value
127 * pairs
128 */
129 public UIDefaults(Object[] keyValueList) {
130 super(keyValueList.length / 2);
131 for(int i = 0; i < keyValueList.length; i += 2) {
132 super.put(keyValueList[i], keyValueList[i + 1]);
133 }
134 }
135
136 /**
137 * Returns the value for key. If the value is a
138 * {@code UIDefaults.LazyValue} then the real
139 * value is computed with {@code LazyValue.createValue()},
140 * the table entry is replaced, and the real value is returned.
141 * If the value is an {@code UIDefaults.ActiveValue}
142 * the table entry is not replaced - the value is computed
143 * with {@code ActiveValue.createValue()} for each
144 * {@code get()} call.
145 *
146 * If the key is not found in the table then it is searched for in the list
147 * of resource bundles maintained by this object. The resource bundles are
148 * searched most recently added first using the locale returned by
149 * {@code getDefaultLocale}. {@code LazyValues} and
150 * {@code ActiveValues} are not supported in the resource bundles.
151
152 *
153 * @param key the desired key
154 * @return the value for {@code key}
155 * @see LazyValue
156 * @see ActiveValue
157 * @see java.util.Hashtable#get
158 * @see #getDefaultLocale
159 * @see #addResourceBundle
160 * @since 1.4
161 */
162 public Object get(Object key) {
163 Object value = getFromHashtable( key );
164 return (value != null) ? value : getFromResourceBundle(key, null);
165 }
166
167 /**
168 * Looks up the given key in our Hashtable and resolves LazyValues
169 * or ActiveValues.
170 */
171 private Object getFromHashtable(final Object key) {
172 /* Quickly handle the common case, without grabbing
173 * a lock.
174 */
222 if (value == null) {
223 super.remove(key);
224 }
225 else {
226 super.put(key, value);
227 }
228 this.notifyAll();
229 }
230 }
231 }
232 else {
233 value = ((ActiveValue)value).createValue(this);
234 }
235
236 return value;
237 }
238
239
240 /**
241 * Returns the value for key associated with the given locale.
242 * If the value is a {@code UIDefaults.LazyValue} then the real
243 * value is computed with {@code LazyValue.createValue()},
244 * the table entry is replaced, and the real value is returned.
245 * If the value is an {@code UIDefaults.ActiveValue}
246 * the table entry is not replaced - the value is computed
247 * with {@code ActiveValue.createValue()} for each
248 * {@code get()} call.
249 *
250 * If the key is not found in the table then it is searched for in the list
251 * of resource bundles maintained by this object. The resource bundles are
252 * searched most recently added first using the given locale.
253 * {@code LazyValues} and {@code ActiveValues} are not supported
254 * in the resource bundles.
255 *
256 * @param key the desired key
257 * @param l the desired {@code locale}
258 * @return the value for {@code key}
259 * @see LazyValue
260 * @see ActiveValue
261 * @see java.util.Hashtable#get
262 * @see #addResourceBundle
263 * @since 1.4
264 */
265 public Object get(Object key, Locale l) {
266 Object value = getFromHashtable( key );
267 return (value != null) ? value : getFromResourceBundle(key, l);
268 }
269
270 /**
271 * Looks up given key in our resource bundles.
272 */
273 private Object getFromResourceBundle(Object key, Locale l) {
274
275 if( resourceBundles == null ||
276 resourceBundles.isEmpty() ||
277 !(key instanceof String) ) {
278 return null;
313
314 while (keys.hasMoreElements()) {
315 String key = keys.nextElement();
316
317 if (values.get(key) == null) {
318 Object value = b.getObject(key);
319
320 values.put(key, value);
321 }
322 }
323 } catch( MissingResourceException mre ) {
324 // Keep looking
325 }
326 }
327 resourceCache.put(l, values);
328 }
329 return values;
330 }
331
332 /**
333 * Sets the value of {@code key} to {@code value} for all locales.
334 * If {@code key} is a string and the new value isn't
335 * equal to the old one, fire a {@code PropertyChangeEvent}.
336 * If value is {@code null}, the key is removed from the table.
337 *
338 * @param key the unique {@code Object} who's value will be used
339 * to retrieve the data value associated with it
340 * @param value the new {@code Object} to store as data under
341 * that key
342 * @return the previous {@code Object} value, or {@code null}
343 * @see #putDefaults
344 * @see java.util.Hashtable#put
345 */
346 public Object put(Object key, Object value) {
347 Object oldValue = (value == null) ? super.remove(key) : super.put(key, value);
348 if (key instanceof String) {
349 firePropertyChange((String)key, oldValue, value);
350 }
351 return oldValue;
352 }
353
354
355 /**
356 * Puts all of the key/value pairs in the database and
357 * unconditionally generates one {@code PropertyChangeEvent}.
358 * The events oldValue and newValue will be {@code null} and its
359 * {@code propertyName} will be "UIDefaults". The key/value pairs are
360 * added for all locales.
361 *
362 * @param keyValueList an array of key/value pairs
363 * @see #put
364 * @see java.util.Hashtable#put
365 */
366 public void putDefaults(Object[] keyValueList) {
367 for(int i = 0, max = keyValueList.length; i < max; i += 2) {
368 Object value = keyValueList[i + 1];
369 if (value == null) {
370 super.remove(keyValueList[i]);
371 }
372 else {
373 super.put(keyValueList[i], value);
374 }
375 }
376 firePropertyChange("UIDefaults", null, null);
377 }
378
379
380 /**
381 * If the value of {@code key} is a {@code Font} return it,
382 * otherwise return {@code null}.
383 * @param key the desired key
384 * @return if the value for {@code key} is a {@code Font},
385 * return the {@code Font} object; otherwise return
386 * {@code null}
387 */
388 public Font getFont(Object key) {
389 Object value = get(key);
390 return (value instanceof Font) ? (Font)value : null;
391 }
392
393
394 /**
395 * If the value of {@code key} for the given {@code Locale}
396 * is a {@code Font} return it, otherwise return {@code null}.
397 * @param key the desired key
398 * @param l the desired locale
399 * @return if the value for {@code key} and {@code Locale}
400 * is a {@code Font},
401 * return the {@code Font} object; otherwise return
402 * {@code null}
403 * @since 1.4
404 */
405 public Font getFont(Object key, Locale l) {
406 Object value = get(key,l);
407 return (value instanceof Font) ? (Font)value : null;
408 }
409
410 /**
411 * If the value of {@code key} is a {@code Color} return it,
412 * otherwise return {@code null}.
413 * @param key the desired key
414 * @return if the value for {@code key} is a {@code Color},
415 * return the {@code Color} object; otherwise return
416 * {@code null}
417 */
418 public Color getColor(Object key) {
419 Object value = get(key);
420 return (value instanceof Color) ? (Color)value : null;
421 }
422
423
424 /**
425 * If the value of {@code key} for the given {@code Locale}
426 * is a {@code Color} return it, otherwise return {@code null}.
427 * @param key the desired key
428 * @param l the desired locale
429 * @return if the value for {@code key} and {@code Locale}
430 * is a {@code Color},
431 * return the {@code Color} object; otherwise return
432 * {@code null}
433 * @since 1.4
434 */
435 public Color getColor(Object key, Locale l) {
436 Object value = get(key,l);
437 return (value instanceof Color) ? (Color)value : null;
438 }
439
440
441 /**
442 * If the value of {@code key} is an {@code Icon} return it,
443 * otherwise return {@code null}.
444 * @param key the desired key
445 * @return if the value for {@code key} is an {@code Icon},
446 * return the {@code Icon} object; otherwise return
447 * {@code null}
448 */
449 public Icon getIcon(Object key) {
450 Object value = get(key);
451 return (value instanceof Icon) ? (Icon)value : null;
452 }
453
454
455 /**
456 * If the value of {@code key} for the given {@code Locale}
457 * is an {@code Icon} return it, otherwise return {@code null}.
458 * @param key the desired key
459 * @param l the desired locale
460 * @return if the value for {@code key} and {@code Locale}
461 * is an {@code Icon},
462 * return the {@code Icon} object; otherwise return
463 * {@code null}
464 * @since 1.4
465 */
466 public Icon getIcon(Object key, Locale l) {
467 Object value = get(key,l);
468 return (value instanceof Icon) ? (Icon)value : null;
469 }
470
471
472 /**
473 * If the value of {@code key} is a {@code Border} return it,
474 * otherwise return {@code null}.
475 * @param key the desired key
476 * @return if the value for {@code key} is a {@code Border},
477 * return the {@code Border} object; otherwise return
478 * {@code null}
479 */
480 public Border getBorder(Object key) {
481 Object value = get(key);
482 return (value instanceof Border) ? (Border)value : null;
483 }
484
485
486 /**
487 * If the value of {@code key} for the given {@code Locale}
488 * is a {@code Border} return it, otherwise return {@code null}.
489 * @param key the desired key
490 * @param l the desired locale
491 * @return if the value for {@code key} and {@code Locale}
492 * is a {@code Border},
493 * return the {@code Border} object; otherwise return
494 * {@code null}
495 * @since 1.4
496 */
497 public Border getBorder(Object key, Locale l) {
498 Object value = get(key,l);
499 return (value instanceof Border) ? (Border)value : null;
500 }
501
502
503 /**
504 * If the value of {@code key} is a {@code String} return it,
505 * otherwise return {@code null}.
506 * @param key the desired key
507 * @return if the value for {@code key} is a {@code String},
508 * return the {@code String} object; otherwise return
509 * {@code null}
510 */
511 public String getString(Object key) {
512 Object value = get(key);
513 return (value instanceof String) ? (String)value : null;
514 }
515
516 /**
517 * If the value of {@code key} for the given {@code Locale}
518 * is a {@code String} return it, otherwise return {@code null}.
519 * @param key the desired key
520 * @param l the desired {@code Locale}
521 * @return if the value for {@code key} for the given
522 * {@code Locale} is a {@code String},
523 * return the {@code String} object; otherwise return
524 * {@code null}
525 * @since 1.4
526 */
527 public String getString(Object key, Locale l) {
528 Object value = get(key,l);
529 return (value instanceof String) ? (String)value : null;
530 }
531
532 /**
533 * If the value of {@code key} is an {@code Integer} return its
534 * integer value, otherwise return 0.
535 * @param key the desired key
536 * @return if the value for {@code key} is an {@code Integer},
537 * return its value, otherwise return 0
538 */
539 public int getInt(Object key) {
540 Object value = get(key);
541 return (value instanceof Integer) ? ((Integer)value).intValue() : 0;
542 }
543
544
545 /**
546 * If the value of {@code key} for the given {@code Locale}
547 * is an {@code Integer} return its integer value, otherwise return 0.
548 * @param key the desired key
549 * @param l the desired locale
550 * @return if the value for {@code key} and {@code Locale}
551 * is an {@code Integer},
552 * return its value, otherwise return 0
553 * @since 1.4
554 */
555 public int getInt(Object key, Locale l) {
556 Object value = get(key,l);
557 return (value instanceof Integer) ? ((Integer)value).intValue() : 0;
558 }
559
560
561 /**
562 * If the value of {@code key} is boolean, return the
563 * boolean value, otherwise return false.
564 *
565 * @param key an {@code Object} specifying the key for the desired boolean value
566 * @return if the value of {@code key} is boolean, return the
567 * boolean value, otherwise return false.
568 * @since 1.4
569 */
570 public boolean getBoolean(Object key) {
571 Object value = get(key);
572 return (value instanceof Boolean) ? ((Boolean)value).booleanValue() : false;
573 }
574
575
576 /**
577 * If the value of {@code key} for the given {@code Locale}
578 * is boolean, return the boolean value, otherwise return false.
579 *
580 * @param key an {@code Object} specifying the key for the desired boolean value
581 * @param l the desired locale
582 * @return if the value for {@code key} and {@code Locale}
583 * is boolean, return the
584 * boolean value, otherwise return false.
585 * @since 1.4
586 */
587 public boolean getBoolean(Object key, Locale l) {
588 Object value = get(key,l);
589 return (value instanceof Boolean) ? ((Boolean)value).booleanValue() : false;
590 }
591
592
593 /**
594 * If the value of {@code key} is an {@code Insets} return it,
595 * otherwise return {@code null}.
596 * @param key the desired key
597 * @return if the value for {@code key} is an {@code Insets},
598 * return the {@code Insets} object; otherwise return
599 * {@code null}
600 */
601 public Insets getInsets(Object key) {
602 Object value = get(key);
603 return (value instanceof Insets) ? (Insets)value : null;
604 }
605
606
607 /**
608 * If the value of {@code key} for the given {@code Locale}
609 * is an {@code Insets} return it, otherwise return {@code null}.
610 * @param key the desired key
611 * @param l the desired locale
612 * @return if the value for {@code key} and {@code Locale}
613 * is an {@code Insets},
614 * return the {@code Insets} object; otherwise return
615 * {@code null}
616 * @since 1.4
617 */
618 public Insets getInsets(Object key, Locale l) {
619 Object value = get(key,l);
620 return (value instanceof Insets) ? (Insets)value : null;
621 }
622
623
624 /**
625 * If the value of {@code key} is a {@code Dimension} return it,
626 * otherwise return {@code null}.
627 * @param key the desired key
628 * @return if the value for {@code key} is a {@code Dimension},
629 * return the {@code Dimension} object; otherwise return
630 * {@code null}
631 */
632 public Dimension getDimension(Object key) {
633 Object value = get(key);
634 return (value instanceof Dimension) ? (Dimension)value : null;
635 }
636
637
638 /**
639 * If the value of {@code key} for the given {@code Locale}
640 * is a {@code Dimension} return it, otherwise return {@code null}.
641 * @param key the desired key
642 * @param l the desired locale
643 * @return if the value for {@code key} and {@code Locale}
644 * is a {@code Dimension},
645 * return the {@code Dimension} object; otherwise return
646 * {@code null}
647 * @since 1.4
648 */
649 public Dimension getDimension(Object key, Locale l) {
650 Object value = get(key,l);
651 return (value instanceof Dimension) ? (Dimension)value : null;
652 }
653
654
655 /**
656 * The value of {@code get(uidClassID)} must be the
657 * {@code String} name of a
658 * class that implements the corresponding {@code ComponentUI}
659 * class. If the class hasn't been loaded before, this method looks
660 * up the class with {@code uiClassLoader.loadClass()} if a non
661 * {@code null}
662 * class loader is provided, {@code classForName()} otherwise.
663 * <p>
664 * If a mapping for {@code uiClassID} exists or if the specified
665 * class can't be found, return {@code null}.
666 * <p>
667 * This method is used by {@code getUI}, it's usually
668 * not necessary to call it directly.
669 *
670 * @param uiClassID a string containing the class ID
671 * @param uiClassLoader the object which will load the class
672 * @return the value of {@code Class.forName(get(uidClassID))}
673 * @see #getUI
674 */
675 public Class<? extends ComponentUI>
676 getUIClass(String uiClassID, ClassLoader uiClassLoader)
677 {
678 try {
679 String className = (String)get(uiClassID);
680 if (className != null) {
681 ReflectUtil.checkPackageAccess(className);
682
683 Class<?> cls = (Class)get(className);
684 if (cls == null) {
685 if (uiClassLoader == null) {
686 cls = SwingUtilities.loadSystemClass(className);
687 }
688 else {
689 cls = uiClassLoader.loadClass(className);
690 }
691 if (cls != null) {
692 // Save lookup for future use, as forName is slow.
693 put(className, cls);
694 }
695 }
696 @SuppressWarnings("unchecked")
697 Class<? extends ComponentUI> tmp = (Class<? extends ComponentUI>)cls;
698 return tmp;
699 }
700 }
701 catch (ClassNotFoundException | ClassCastException e) {
702 return null;
703 }
704 return null;
705 }
706
707
708 /**
709 * Returns the L&F class that renders this component.
710 *
711 * @param uiClassID a string containing the class ID
712 * @return the Class object returned by
713 * {@code getUIClass(uiClassID, null)}
714 */
715 public Class<? extends ComponentUI> getUIClass(String uiClassID) {
716 return getUIClass(uiClassID, null);
717 }
718
719
720 /**
721 * If {@code getUI()} fails for any reason,
722 * it calls this method before returning {@code null}.
723 * Subclasses may choose to do more or less here.
724 *
725 * @param msg message string to print
726 * @see #getUI
727 */
728 protected void getUIError(String msg) {
729 System.err.println("UIDefaults.getUI() failed: " + msg);
730 try {
731 throw new Error();
732 }
733 catch (Throwable e) {
734 e.printStackTrace();
735 }
736 }
737
738 /**
739 * Creates an {@code ComponentUI} implementation for the
740 * specified component. In other words create the look
741 * and feel specific delegate object for {@code target}.
742 * This is done in two steps:
743 * <ul>
744 * <li> Look up the name of the {@code ComponentUI} implementation
745 * class under the value returned by {@code target.getUIClassID()}.
746 * <li> Use the implementation classes static {@code createUI()}
747 * method to construct a look and feel delegate.
748 * </ul>
749 * @param target the {@code JComponent} which needs a UI
750 * @return the {@code ComponentUI} object
751 */
752 public ComponentUI getUI(JComponent target) {
753
754 Object cl = get("ClassLoader");
755 ClassLoader uiClassLoader =
756 (cl != null) ? (ClassLoader)cl : target.getClass().getClassLoader();
757 Class<? extends ComponentUI> uiClass = getUIClass(target.getUIClassID(), uiClassLoader);
758 Object uiObject = null;
759
760 if (uiClass == null) {
761 getUIError("no ComponentUI class for: " + target);
762 }
763 else {
764 try {
765 Method m = (Method)get(uiClass);
766 if (m == null) {
767 m = uiClass.getMethod("createUI", new Class<?>[]{JComponent.class});
768 put(uiClass, m);
769 }
770 uiObject = MethodUtil.invoke(m, null, new Object[]{target});
771 }
772 catch (NoSuchMethodException e) {
773 getUIError("static createUI() method not found in " + uiClass);
774 }
775 catch (Exception e) {
776 getUIError("createUI() failed for " + target + " " + e);
777 }
778 }
779
780 return (ComponentUI)uiObject;
781 }
782
783 /**
784 * Adds a {@code PropertyChangeListener} to the listener list.
785 * The listener is registered for all properties.
786 * <p>
787 * A {@code PropertyChangeEvent} will get fired whenever a default
788 * is changed.
789 *
790 * @param listener the {@code PropertyChangeListener} to be added
791 * @see java.beans.PropertyChangeSupport
792 */
793 public synchronized void addPropertyChangeListener(PropertyChangeListener listener) {
794 if (changeSupport == null) {
795 changeSupport = new SwingPropertyChangeSupport(this);
796 }
797 changeSupport.addPropertyChangeListener(listener);
798 }
799
800
801 /**
802 * Removes a {@code PropertyChangeListener} from the listener list.
803 * This removes a {@code PropertyChangeListener} that was registered
804 * for all properties.
805 *
806 * @param listener the {@code PropertyChangeListener} to be removed
807 * @see java.beans.PropertyChangeSupport
808 */
809 public synchronized void removePropertyChangeListener(PropertyChangeListener listener) {
810 if (changeSupport != null) {
811 changeSupport.removePropertyChangeListener(listener);
812 }
813 }
814
815
816 /**
817 * Returns an array of all the {@code PropertyChangeListener}s added
818 * to this UIDefaults with addPropertyChangeListener().
819 *
820 * @return all of the {@code PropertyChangeListener}s added or an empty
821 * array if no listeners have been added
822 * @since 1.4
823 */
824 public synchronized PropertyChangeListener[] getPropertyChangeListeners() {
825 if (changeSupport == null) {
826 return new PropertyChangeListener[0];
827 }
828 return changeSupport.getPropertyChangeListeners();
829 }
830
831
832 /**
833 * Support for reporting bound property changes. If oldValue and
834 * newValue are not equal and the {@code PropertyChangeEvent}x
835 * listener list isn't empty, then fire a
836 * {@code PropertyChange} event to each listener.
837 *
838 * @param propertyName the programmatic name of the property
839 * that was changed
840 * @param oldValue the old value of the property
841 * @param newValue the new value of the property
842 * @see java.beans.PropertyChangeSupport
843 */
844 protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
845 if (changeSupport != null) {
846 changeSupport.firePropertyChange(propertyName, oldValue, newValue);
847 }
848 }
849
850
851 /**
852 * Adds a resource bundle to the list of resource bundles that are
853 * searched for localized values. Resource bundles are searched in the
854 * reverse order they were added. In other words, the most recently added
855 * bundle is searched first.
856 *
874
875
876 /**
877 * Removes a resource bundle from the list of resource bundles that are
878 * searched for localized defaults.
879 *
880 * @param bundleName the base name of the resource bundle to be removed
881 * @see java.util.ResourceBundle
882 * @see #addResourceBundle
883 * @since 1.4
884 */
885 public synchronized void removeResourceBundle( String bundleName ) {
886 if( resourceBundles != null ) {
887 resourceBundles.remove( bundleName );
888 }
889 resourceCache.clear();
890 }
891
892 /**
893 * Sets the default locale. The default locale is used in retrieving
894 * localized values via {@code get} methods that do not take a
895 * locale argument. As of release 1.4, Swing UI objects should retrieve
896 * localized values using the locale of their component rather than the
897 * default locale. The default locale exists to provide compatibility with
898 * pre 1.4 behaviour.
899 *
900 * @param l the new default locale
901 * @see #getDefaultLocale
902 * @see #get(Object)
903 * @see #get(Object,Locale)
904 * @since 1.4
905 */
906 public void setDefaultLocale( Locale l ) {
907 defaultLocale = l;
908 }
909
910 /**
911 * Returns the default locale. The default locale is used in retrieving
912 * localized values via {@code get} methods that do not take a
913 * locale argument. As of release 1.4, Swing UI objects should retrieve
914 * localized values using the locale of their component rather than the
915 * default locale. The default locale exists to provide compatibility with
916 * pre 1.4 behaviour.
917 *
918 * @return the default locale
919 * @see #setDefaultLocale
920 * @see #get(Object)
921 * @see #get(Object,Locale)
922 * @since 1.4
923 */
924 public Locale getDefaultLocale() {
925 return defaultLocale;
926 }
927
928 /**
929 * This class enables one to store an entry in the defaults
930 * table that isn't constructed until the first time it's
931 * looked up with one of the {@code getXXX(key)} methods.
932 * Lazy values are useful for defaults that are expensive
933 * to construct or are seldom retrieved. The first time
934 * a {@code LazyValue} is retrieved its "real value" is computed
935 * by calling {@code LazyValue.createValue()} and the real
936 * value is used to replace the {@code LazyValue} in the
937 * {@code UIDefaults}
938 * table. Subsequent lookups for the same key return
939 * the real value. Here's an example of a {@code LazyValue}
940 * that constructs a {@code Border}:
941 * <pre>
942 * Object borderLazyValue = new UIDefaults.LazyValue() {
943 * public Object createValue(UIDefaults table) {
944 * return new BorderFactory.createLoweredBevelBorder();
945 * }
946 * };
947 *
948 * uiDefaultsTable.put("MyBorder", borderLazyValue);
949 * </pre>
950 *
951 * @see UIDefaults#get
952 */
953 public interface LazyValue {
954 /**
955 * Creates the actual value retrieved from the {@code UIDefaults}
956 * table. When an object that implements this interface is
957 * retrieved from the table, this method is used to create
958 * the real value, which is then stored in the table and
959 * returned to the calling method.
960 *
961 * @param table a {@code UIDefaults} table
962 * @return the created {@code Object}
963 */
964 Object createValue(UIDefaults table);
965 }
966
967
968 /**
969 * This class enables one to store an entry in the defaults
970 * table that's constructed each time it's looked up with one of
971 * the {@code getXXX(key)} methods. Here's an example of
972 * an {@code ActiveValue} that constructs a
973 * {@code DefaultListCellRenderer}:
974 * <pre>
975 * Object cellRendererActiveValue = new UIDefaults.ActiveValue() {
976 * public Object createValue(UIDefaults table) {
977 * return new DefaultListCellRenderer();
978 * }
979 * };
980 *
981 * uiDefaultsTable.put("MyRenderer", cellRendererActiveValue);
982 * </pre>
983 *
984 * @see UIDefaults#get
985 */
986 public interface ActiveValue {
987 /**
988 * Creates the value retrieved from the {@code UIDefaults} table.
989 * The object is created each time it is accessed.
990 *
991 * @param table a {@code UIDefaults} table
992 * @return the created {@code Object}
993 */
994 Object createValue(UIDefaults table);
995 }
996
997 /**
998 * This class provides an implementation of {@code LazyValue}
999 * which can be
1000 * used to delay loading of the Class for the instance to be created.
1001 * It also avoids creation of an anonymous inner class for the
1002 * {@code LazyValue}
1003 * subclass. Both of these improve performance at the time that a
1004 * a Look and Feel is loaded, at the cost of a slight performance
1005 * reduction the first time {@code createValue} is called
1006 * (since Reflection APIs are used).
1007 * @since 1.3
1008 */
1009 public static class ProxyLazyValue implements LazyValue {
1010 private AccessControlContext acc;
1011 private String className;
1012 private String methodName;
1013 private Object[] args;
1014
1015 /**
1016 * Creates a {@code LazyValue} which will construct an instance
1017 * when asked.
1018 *
1019 * @param c a {@code String} specifying the classname
1020 * of the instance to be created on demand
1021 */
1022 public ProxyLazyValue(String c) {
1023 this(c, (String)null);
1024 }
1025 /**
1026 * Creates a {@code LazyValue} which will construct an instance
1027 * when asked.
1028 *
1029 * @param c a {@code String} specifying the classname of
1030 * the class
1031 * containing a static method to be called for
1032 * instance creation
1033 * @param m a {@code String} specifying the static
1034 * method to be called on class c
1035 */
1036 public ProxyLazyValue(String c, String m) {
1037 this(c, m, null);
1038 }
1039 /**
1040 * Creates a {@code LazyValue} which will construct an instance
1041 * when asked.
1042 *
1043 * @param c a {@code String} specifying the classname
1044 * of the instance to be created on demand
1045 * @param o an array of {@code Objects} to be passed as
1046 * paramaters to the constructor in class c
1047 */
1048 public ProxyLazyValue(String c, Object[] o) {
1049 this(c, null, o);
1050 }
1051 /**
1052 * Creates a {@code LazyValue} which will construct an instance
1053 * when asked.
1054 *
1055 * @param c a {@code String} specifying the classname
1056 * of the class
1057 * containing a static method to be called for
1058 * instance creation.
1059 * @param m a {@code String} specifying the static method
1060 * to be called on class c
1061 * @param o an array of {@code Objects} to be passed as
1062 * paramaters to the static method in class c
1063 */
1064 public ProxyLazyValue(String c, String m, Object[] o) {
1065 acc = AccessController.getContext();
1066 className = c;
1067 methodName = m;
1068 if (o != null) {
1069 args = o.clone();
1070 }
1071 }
1072
1073 /**
1074 * Creates the value retrieved from the {@code UIDefaults} table.
1075 * The object is created each time it is accessed.
1076 *
1077 * @param table a {@code UIDefaults} table
1078 * @return the created {@code Object}
1079 */
1080 public Object createValue(final UIDefaults table) {
1081 // In order to pick up the security policy in effect at the
1082 // time of creation we use a doPrivileged with the
1083 // AccessControlContext that was in place when this was created.
1084 if (acc == null && System.getSecurityManager() != null) {
1085 throw new SecurityException("null AccessControlContext");
1086 }
1087 return AccessController.doPrivileged(new PrivilegedAction<Object>() {
1088 public Object run() {
1089 try {
1090 Class<?> c;
1091 Object cl;
1092 // See if we should use a separate ClassLoader
1093 if (table == null || !((cl = table.get("ClassLoader"))
1094 instanceof ClassLoader)) {
1095 cl = Thread.currentThread().
1096 getContextClassLoader();
1097 if (cl == null) {
1098 // Fallback to the system class loader.
1112 SwingUtilities2.checkAccess(constructor.getModifiers());
1113 return constructor.newInstance(args);
1114 }
1115 } catch(Exception e) {
1116 // Ideally we would throw an exception, unfortunately
1117 // often times there are errors as an initial look and
1118 // feel is loaded before one can be switched. Perhaps a
1119 // flag should be added for debugging, so that if true
1120 // the exception would be thrown.
1121 }
1122 return null;
1123 }
1124 }, acc);
1125 }
1126
1127 /*
1128 * Coerce the array of class types provided into one which
1129 * looks the way the Reflection APIs expect. This is done
1130 * by substituting primitive types for their Object counterparts,
1131 * and superclasses for subclasses used to add the
1132 * {@code UIResource} tag.
1133 */
1134 private Class<?>[] getClassArray(Object[] args) {
1135 Class<?>[] types = null;
1136 if (args!=null) {
1137 types = new Class<?>[args.length];
1138 for (int i = 0; i< args.length; i++) {
1139 /* PENDING(ges): At present only the primitive types
1140 used are handled correctly; this should eventually
1141 handle all primitive types */
1142 if (args[i] instanceof java.lang.Integer) {
1143 types[i]=Integer.TYPE;
1144 } else if (args[i] instanceof java.lang.Boolean) {
1145 types[i]=Boolean.TYPE;
1146 } else if (args[i] instanceof javax.swing.plaf.ColorUIResource) {
1147 /* PENDING(ges) Currently the Reflection APIs do not
1148 search superclasses of parameters supplied for
1149 constructor/method lookup. Since we only have
1150 one case where this is needed, we substitute
1151 directly instead of adding a massive amount
1152 of mechanism for this. Eventually this will
1160 }
1161 return types;
1162 }
1163
1164 private String printArgs(Object[] array) {
1165 String s = "{";
1166 if (array !=null) {
1167 for (int i = 0 ; i < array.length-1; i++) {
1168 s = s.concat(array[i] + ",");
1169 }
1170 s = s.concat(array[array.length-1] + "}");
1171 } else {
1172 s = s.concat("}");
1173 }
1174 return s;
1175 }
1176 }
1177
1178
1179 /**
1180 * {@code LazyInputMap} will create a {@code InputMap}
1181 * in its {@code createValue}
1182 * method. The bindings are passed in the constructor.
1183 * The bindings are an array with
1184 * the even number entries being string {@code KeyStrokes}
1185 * (eg "alt SPACE") and
1186 * the odd number entries being the value to use in the
1187 * {@code InputMap} (and the key in the {@code ActionMap}).
1188 * @since 1.3
1189 */
1190 public static class LazyInputMap implements LazyValue {
1191 /** Key bindings are registered under. */
1192 private Object[] bindings;
1193
1194 /**
1195 * Constructs a {@code LazyInputMap}.
1196 * @param bindings the bindings
1197 */
1198 public LazyInputMap(Object[] bindings) {
1199 this.bindings = bindings;
1200 }
1201
1202 /**
1203 * Creates an {@code InputMap} with the bindings that are
1204 * passed in.
1205 *
1206 * @param table a {@code UIDefaults} table
1207 * @return the {@code InputMap}
1208 */
1209 public Object createValue(UIDefaults table) {
1210 if (bindings != null) {
1211 InputMap km = LookAndFeel.makeInputMap(bindings);
1212 return km;
1213 }
1214 return null;
1215 }
1216 }
1217
1218 /**
1219 * {@code TextAndMnemonicHashMap} stores swing resource strings. Many of strings
1220 * can have a mnemonic. For example:
1221 * FileChooser.saveButton.textAndMnemonic=&Save
1222 * For this case method get returns "Save" for the key "FileChooser.saveButtonText" and
1223 * mnemonic "S" for the key "FileChooser.saveButtonMnemonic"
1224 *
1225 * There are several patterns for the text and mnemonic suffixes which are checked by the
1226 * {@code TextAndMnemonicHashMap} class.
1227 * Patterns which are converted to the xxx.textAndMnemonic key:
1228 * (xxxNameText, xxxNameMnemonic)
1229 * (xxxNameText, xxxMnemonic)
1230 * (xxx.nameText, xxx.mnemonic)
1231 * (xxxText, xxxMnemonic)
1232 *
1233 * These patterns can have a mnemonic index in format
1234 * (xxxDisplayedMnemonicIndex)
1235 *
1236 * Pattern which is converted to the xxx.titleAndMnemonic key:
1237 * (xxxTitle, xxxMnemonic)
1238 *
1239 */
1240 private static class TextAndMnemonicHashMap extends HashMap<String, Object> {
1241
1242 static final String AND_MNEMONIC = "AndMnemonic";
1243 static final String TITLE_SUFFIX = ".titleAndMnemonic";
1244 static final String TEXT_SUFFIX = ".textAndMnemonic";
1245
1246 @Override
|