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