Frames | No Frames |
1: /* UIDefaults.java -- database for all settings and interface bindings. 2: Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. 3: 4: This file is part of GNU Classpath. 5: 6: GNU Classpath is free software; you can redistribute it and/or modify 7: it under the terms of the GNU General Public License as published by 8: the Free Software Foundation; either version 2, or (at your option) 9: any later version. 10: 11: GNU Classpath is distributed in the hope that it will be useful, but 12: WITHOUT ANY WARRANTY; without even the implied warranty of 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14: General Public License for more details. 15: 16: You should have received a copy of the GNU General Public License 17: along with GNU Classpath; see the file COPYING. If not, write to the 18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19: 02110-1301 USA. 20: 21: Linking this library statically or dynamically with other modules is 22: making a combined work based on this library. Thus, the terms and 23: conditions of the GNU General Public License cover the whole 24: combination. 25: 26: As a special exception, the copyright holders of this library give you 27: permission to link this library with independent modules to produce an 28: executable, regardless of the license terms of these independent 29: modules, and to copy and distribute the resulting executable under 30: terms of your choice, provided that you also meet, for each linked 31: independent module, the terms and conditions of the license of that 32: module. An independent module is a module which is not derived from 33: or based on this library. If you modify this library, you may extend 34: this exception to your version of the library, but you are not 35: obligated to do so. If you do not wish to do so, delete this 36: exception statement from your version. */ 37: 38: 39: package javax.swing; 40: 41: import java.awt.Color; 42: import java.awt.Dimension; 43: import java.awt.Font; 44: import java.awt.Insets; 45: import java.beans.PropertyChangeListener; 46: import java.beans.PropertyChangeSupport; 47: import java.lang.reflect.Method; 48: import java.util.Hashtable; 49: import java.util.LinkedList; 50: import java.util.ListIterator; 51: import java.util.Locale; 52: import java.util.MissingResourceException; 53: import java.util.ResourceBundle; 54: 55: import javax.swing.border.Border; 56: import javax.swing.plaf.ComponentUI; 57: import javax.swing.plaf.InputMapUIResource; 58: 59: /** 60: * UIDefaults is a database where all settings and interface bindings are 61: * stored into. A PLAF implementation fills one of these (see for example 62: * plaf/basic/BasicLookAndFeel.java) with "ButtonUI" -> new BasicButtonUI(). 63: * 64: * @author Ronald Veldema (rveldema@cs.vu.nl) 65: */ 66: public class UIDefaults extends Hashtable<Object, Object> 67: { 68: 69: /** Our ResourceBundles. */ 70: private LinkedList bundles; 71: 72: /** The default locale. */ 73: private Locale defaultLocale; 74: 75: /** We use this for firing PropertyChangeEvents. */ 76: private PropertyChangeSupport propertyChangeSupport; 77: 78: /** 79: * Used for lazy instantiation of UIDefaults values so that they are not 80: * all loaded when a Swing application starts up, but only the values that 81: * are really needed. An <code>ActiveValue</code> is newly instantiated 82: * every time when the value is requested, as opposed to the normal 83: * {@link LazyValue} that is only instantiated once. 84: */ 85: public static interface ActiveValue 86: { 87: Object createValue(UIDefaults table); 88: } 89: 90: public static class LazyInputMap implements LazyValue 91: { 92: Object[] bind; 93: public LazyInputMap(Object[] bindings) 94: { 95: bind = bindings; 96: } 97: public Object createValue(UIDefaults table) 98: { 99: InputMapUIResource im = new InputMapUIResource(); 100: for (int i = 0; 2 * i + 1 < bind.length; ++i) 101: { 102: Object curr = bind[2 * i]; 103: if (curr instanceof KeyStroke) 104: im.put((KeyStroke) curr, bind[2 * i + 1]); 105: else 106: im.put(KeyStroke.getKeyStroke((String) curr), 107: bind[2 * i + 1]); 108: } 109: return im; 110: } 111: } 112: 113: /** 114: * Used for lazy instantiation of UIDefaults values so that they are not 115: * all loaded when a Swing application starts up, but only the values that 116: * are really needed. A <code>LazyValue</code> is only instantiated once, 117: * as opposed to the {@link ActiveValue} that is newly created every time 118: * it is requested. 119: */ 120: public static interface LazyValue 121: { 122: Object createValue(UIDefaults table); 123: } 124: 125: public static class ProxyLazyValue implements LazyValue 126: { 127: LazyValue inner; 128: public ProxyLazyValue(String s) 129: { 130: final String className = s; 131: inner = new LazyValue() 132: { 133: public Object createValue(UIDefaults table) 134: { 135: try 136: { 137: return Class 138: .forName(className) 139: .getConstructor(new Class[] {}) 140: .newInstance(new Object[] {}); 141: } 142: catch (Exception e) 143: { 144: return null; 145: } 146: } 147: }; 148: } 149: 150: public ProxyLazyValue(String c, String m) 151: { 152: final String className = c; 153: final String methodName = m; 154: inner = new LazyValue() 155: { 156: public Object createValue(UIDefaults table) 157: { 158: try 159: { 160: return Class 161: .forName(className) 162: .getMethod(methodName, new Class[] {}) 163: .invoke(null, new Object[] {}); 164: } 165: catch (Exception e) 166: { 167: return null; 168: } 169: } 170: }; 171: } 172: 173: public ProxyLazyValue(String c, Object[] os) 174: { 175: final String className = c; 176: final Object[] objs = os; 177: final Class[] clss = new Class[objs.length]; 178: for (int i = 0; i < objs.length; ++i) 179: { 180: clss[i] = objs[i].getClass(); 181: } 182: inner = new LazyValue() 183: { 184: public Object createValue(UIDefaults table) 185: { 186: try 187: { 188: return Class 189: .forName(className) 190: .getConstructor(clss) 191: .newInstance(objs); 192: } 193: catch (Exception e) 194: { 195: return null; 196: } 197: } 198: }; 199: } 200: 201: public ProxyLazyValue(String c, String m, Object[] os) 202: { 203: final String className = c; 204: final String methodName = m; 205: final Object[] objs = os; 206: final Class[] clss = new Class[objs.length]; 207: for (int i = 0; i < objs.length; ++i) 208: { 209: clss[i] = objs[i].getClass(); 210: } 211: inner = new LazyValue() 212: { 213: public Object createValue(UIDefaults table) 214: { 215: try 216: { 217: return Class 218: .forName(className) 219: .getMethod(methodName, clss) 220: .invoke(null, objs); 221: } 222: catch (Exception e) 223: { 224: return null; 225: } 226: } 227: }; 228: } 229: 230: public Object createValue(UIDefaults table) 231: { 232: return inner.createValue(table); 233: } 234: } 235: 236: /** Our serialVersionUID for serialization. */ 237: private static final long serialVersionUID = 7341222528856548117L; 238: 239: /** 240: * Constructs a new empty UIDefaults instance. 241: */ 242: public UIDefaults() 243: { 244: bundles = new LinkedList(); 245: defaultLocale = Locale.getDefault(); 246: propertyChangeSupport = new PropertyChangeSupport(this); 247: } 248: 249: /** 250: * Constructs a new UIDefaults instance and loads the specified entries. 251: * The entries are expected to come in pairs, that means 252: * <code>entries[0]</code> is a key, <code>entries[1]</code> is a value, 253: * <code>entries[2]</code> a key and so forth. 254: * 255: * @param entries the entries to initialize the UIDefaults instance with 256: */ 257: public UIDefaults(Object[] entries) 258: { 259: this(); 260: 261: for (int i = 0; (2 * i + 1) < entries.length; ++i) 262: put(entries[2 * i], entries[2 * i + 1]); 263: } 264: 265: /** 266: * Returns the entry for the specified <code>key</code> in the default 267: * locale. 268: * 269: * @return the entry for the specified <code>key</code> 270: */ 271: public Object get(Object key) 272: { 273: return this.get(key, getDefaultLocale()); 274: } 275: 276: /** 277: * Returns the entry for the specified <code>key</code> in the Locale 278: * <code>loc</code>. 279: * 280: * @param key the key for which we return the value 281: * @param loc the locale 282: */ 283: public Object get(Object key, Locale loc) 284: { 285: Object obj = null; 286: 287: if (super.containsKey(key)) 288: { 289: obj = super.get(key); 290: } 291: else if (key instanceof String) 292: { 293: String keyString = (String) key; 294: ListIterator i = bundles.listIterator(0); 295: while (i.hasNext()) 296: { 297: String bundle_name = (String) i.next(); 298: ResourceBundle res = 299: ResourceBundle.getBundle(bundle_name, loc); 300: if (res != null) 301: { 302: try 303: { 304: obj = res.getObject(keyString); 305: break; 306: } 307: catch (MissingResourceException me) 308: { 309: // continue, this bundle has no such key 310: } 311: } 312: } 313: } 314: 315: // now we've found the object, resolve it. 316: // nb: LazyValues aren't supported in resource bundles, so it's correct 317: // to insert their results in the locale-less hashtable. 318: 319: if (obj == null) 320: return null; 321: 322: if (obj instanceof LazyValue) 323: { 324: Object resolved = ((LazyValue) obj).createValue(this); 325: super.remove(key); 326: super.put(key, resolved); 327: return resolved; 328: } 329: else if (obj instanceof ActiveValue) 330: { 331: return ((ActiveValue) obj).createValue(this); 332: } 333: 334: return obj; 335: } 336: 337: /** 338: * Puts a key and value into this UIDefaults object.<br> 339: * In contrast to 340: * {@link java.util.Hashtable}s <code>null</code>-values are accepted 341: * here and treated like #remove(key). 342: * <br> 343: * This fires a PropertyChangeEvent with key as name and the old and new 344: * values. 345: * 346: * @param key the key to put into the map 347: * @param value the value to put into the map 348: * 349: * @return the old value for key or <code>null</code> if <code>key</code> 350: * had no value assigned 351: */ 352: public Object put(Object key, Object value) 353: { 354: Object old = checkAndPut(key, value); 355: 356: if (key instanceof String && old != value) 357: firePropertyChange((String) key, old, value); 358: return old; 359: } 360: 361: /** 362: * Puts a set of key-value pairs into the map. 363: * The entries are expected to come in pairs, that means 364: * <code>entries[0]</code> is a key, <code>entries[1]</code> is a value, 365: * <code>entries[2]</code> a key and so forth. 366: * <br> 367: * If a value is <code>null</code> it is treated like #remove(key). 368: * <br> 369: * This unconditionally fires a PropertyChangeEvent with 370: * <code>'UIDefaults'</code> as name and <code>null</code> for 371: * old and new value. 372: * 373: * @param entries the entries to be put into the map 374: */ 375: public void putDefaults(Object[] entries) 376: { 377: for (int i = 0; (2 * i + 1) < entries.length; ++i) 378: { 379: checkAndPut(entries[2 * i], entries[2 * i + 1]); 380: } 381: firePropertyChange("UIDefaults", null, null); 382: } 383: 384: /** 385: * Checks the value for <code>null</code> and put it into the Hashtable, if 386: * it is not <code>null</code>. If the value is <code>null</code> then 387: * remove the corresponding key. 388: * 389: * @param key the key to put into this UIDefauls table 390: * @param value the value to put into this UIDefaults table 391: * 392: * @return the old value for <code>key</code> 393: */ 394: private Object checkAndPut(Object key, Object value) 395: { 396: Object old; 397: 398: if (value != null) 399: old = super.put(key, value); 400: else 401: old = super.remove(key); 402: 403: return old; 404: } 405: 406: /** 407: * Returns a font entry for the default locale. 408: * 409: * @param key the key to the requested entry 410: * 411: * @return the font entry for <code>key</code> or null if no such entry 412: * exists 413: */ 414: public Font getFont(Object key) 415: { 416: Object o = get(key); 417: return o instanceof Font ? (Font) o : null; 418: } 419: 420: /** 421: * Returns a font entry for a specic locale. 422: * 423: * @param key the key to the requested entry 424: * @param locale the locale to the requested entry 425: * 426: * @return the font entry for <code>key</code> or null if no such entry 427: * exists 428: */ 429: public Font getFont(Object key, Locale locale) 430: { 431: Object o = get(key, locale); 432: return o instanceof Font ? (Font) o : null; 433: } 434: 435: /** 436: * Returns a color entry for the default locale. 437: * 438: * @param key the key to the requested entry 439: * 440: * @return the color entry for <code>key</code> or null if no such entry 441: * exists 442: */ 443: public Color getColor(Object key) 444: { 445: Object o = get(key); 446: return o instanceof Color ? (Color) o : null; 447: } 448: 449: /** 450: * Returns a color entry for a specic locale. 451: * 452: * @param key the key to the requested entry 453: * @param locale the locale to the requested entry 454: * 455: * @return the color entry for <code>key</code> or null if no such entry 456: * exists 457: */ 458: public Color getColor(Object key, Locale locale) 459: { 460: Object o = get(key, locale); 461: return o instanceof Color ? (Color) o : null; 462: } 463: 464: /** 465: * Returns an icon entry for the default locale. 466: * 467: * @param key the key to the requested entry 468: * 469: * @return the icon entry for <code>key</code> or null if no such entry 470: * exists 471: */ 472: public Icon getIcon(Object key) 473: { 474: Object o = get(key); 475: return o instanceof Icon ? (Icon) o : null; 476: } 477: 478: /** 479: * Returns an icon entry for a specic locale. 480: * 481: * @param key the key to the requested entry 482: * @param locale the locale to the requested entry 483: * 484: * @return the icon entry for <code>key</code> or null if no such entry 485: * exists 486: */ 487: public Icon getIcon(Object key, Locale locale) 488: { 489: Object o = get(key, locale); 490: return o instanceof Icon ? (Icon) o : null; 491: } 492: 493: /** 494: * Returns a border entry for the default locale. 495: * 496: * @param key the key to the requested entry 497: * 498: * @return the border entry for <code>key</code> or null if no such entry 499: * exists 500: */ 501: public Border getBorder(Object key) 502: { 503: Object o = get(key); 504: return o instanceof Border ? (Border) o : null; 505: } 506: 507: /** 508: * Returns a border entry for a specic locale. 509: * 510: * @param key the key to the requested entry 511: * @param locale the locale to the requested entry 512: * 513: * @return the border entry for <code>key</code> or null if no such entry 514: * exists 515: */ 516: public Border getBorder(Object key, Locale locale) 517: { 518: Object o = get(key, locale); 519: return o instanceof Border ? (Border) o : null; 520: } 521: 522: /** 523: * Returns a string entry for the default locale. 524: * 525: * @param key the key to the requested entry 526: * 527: * @return the string entry for <code>key</code> or null if no such entry 528: * exists 529: */ 530: public String getString(Object key) 531: { 532: Object o = get(key); 533: return o instanceof String ? (String) o : null; 534: } 535: 536: /** 537: * Returns a string entry for a specic locale. 538: * 539: * @param key the key to the requested entry 540: * @param locale the locale to the requested entry 541: * 542: * @return the string entry for <code>key</code> or null if no such entry 543: * exists 544: */ 545: public String getString(Object key, Locale locale) 546: { 547: Object o = get(key, locale); 548: return o instanceof String ? (String) o : null; 549: } 550: 551: /** 552: * Returns an integer entry for the default locale. 553: * 554: * @param key the key to the requested entry 555: * 556: * @return the integer entry for <code>key</code> or null if no such entry 557: * exists 558: */ 559: public int getInt(Object key) 560: { 561: Object o = get(key); 562: return o instanceof Integer ? ((Integer) o).intValue() : 0; 563: } 564: 565: /** 566: * Returns an integer entry for a specic locale. 567: * 568: * @param key the key to the requested entry 569: * @param locale the locale to the requested entry 570: * 571: * @return the integer entry for <code>key</code> or null if no such entry 572: * exists 573: */ 574: public int getInt(Object key, Locale locale) 575: { 576: Object o = get(key, locale); 577: return o instanceof Integer ? ((Integer) o).intValue() : 0; 578: } 579: 580: /** 581: * Returns a boolean entry for the default locale. 582: * 583: * @param key the key to the requested entry 584: * 585: * @return The boolean entry for <code>key</code> or <code>false</code> if no 586: * such entry exists. 587: */ 588: public boolean getBoolean(Object key) 589: { 590: return Boolean.TRUE.equals(get(key)); 591: } 592: 593: /** 594: * Returns a boolean entry for a specic locale. 595: * 596: * @param key the key to the requested entry 597: * @param locale the locale to the requested entry 598: * 599: * @return the boolean entry for <code>key</code> or null if no such entry 600: * exists 601: */ 602: public boolean getBoolean(Object key, Locale locale) 603: { 604: return Boolean.TRUE.equals(get(key, locale)); 605: } 606: 607: /** 608: * Returns an insets entry for the default locale. 609: * 610: * @param key the key to the requested entry 611: * 612: * @return the insets entry for <code>key</code> or null if no such entry 613: * exists 614: */ 615: public Insets getInsets(Object key) 616: { 617: Object o = get(key); 618: return o instanceof Insets ? (Insets) o : null; 619: } 620: 621: /** 622: * Returns an insets entry for a specic locale. 623: * 624: * @param key the key to the requested entry 625: * @param locale the locale to the requested entry 626: * 627: * @return the boolean entry for <code>key</code> or null if no such entry 628: * exists 629: */ 630: public Insets getInsets(Object key, Locale locale) 631: { 632: Object o = get(key, locale); 633: return o instanceof Insets ? (Insets) o : null; 634: } 635: 636: /** 637: * Returns a dimension entry for the default locale. 638: * 639: * @param key the key to the requested entry 640: * 641: * @return the dimension entry for <code>key</code> or null if no such entry 642: * exists 643: */ 644: public Dimension getDimension(Object key) 645: { 646: Object o = get(key); 647: return o instanceof Dimension ? (Dimension) o : null; 648: } 649: 650: /** 651: * Returns a dimension entry for a specic locale. 652: * 653: * @param key the key to the requested entry 654: * @param locale the locale to the requested entry 655: * 656: * @return the boolean entry for <code>key</code> or null if no such entry 657: * exists 658: */ 659: public Dimension getDimension(Object key, Locale locale) 660: { 661: Object o = get(key, locale); 662: return o instanceof Dimension ? (Dimension) o : null; 663: } 664: 665: /** 666: * Returns the ComponentUI class that renders a component. <code>id</code> 667: * is the ID for which the String value of the classname is stored in 668: * this UIDefaults map. 669: * 670: * @param id the ID of the UI class 671: * @param loader the ClassLoader to use 672: * 673: * @return the UI class for <code>id</code> 674: */ 675: public Class<? extends ComponentUI> getUIClass(String id, ClassLoader loader) 676: { 677: String className = (String) get(id); 678: if (className == null) 679: return null; 680: try 681: { 682: if (loader == null) 683: loader = ClassLoader.getSystemClassLoader(); 684: return (Class<? extends ComponentUI>) loader.loadClass (className); 685: } 686: catch (Exception e) 687: { 688: return null; 689: } 690: } 691: 692: /** 693: * Returns the ComponentUI class that renders a component. <code>id</code> 694: * is the ID for which the String value of the classname is stored in 695: * this UIDefaults map. 696: * 697: * @param id the ID of the UI class 698: * 699: * @return the UI class for <code>id</code> 700: */ 701: public Class<? extends ComponentUI> getUIClass(String id) 702: { 703: return getUIClass (id, null); 704: } 705: 706: /** 707: * If a key is requested in #get(key) that has no value, this method 708: * is called before returning <code>null</code>. 709: * 710: * @param msg the error message 711: */ 712: protected void getUIError(String msg) 713: { 714: System.err.println ("UIDefaults.getUIError: " + msg); 715: } 716: 717: /** 718: * Returns the {@link ComponentUI} for the specified {@link JComponent}. 719: * 720: * @param target the component for which the ComponentUI is requested 721: * 722: * @return the {@link ComponentUI} for the specified {@link JComponent} 723: */ 724: public ComponentUI getUI(JComponent target) 725: { 726: String classId = target.getUIClassID (); 727: Class cls = getUIClass (classId); 728: if (cls == null) 729: { 730: getUIError ("failed to locate UI class:" + classId); 731: return null; 732: } 733: 734: Method factory; 735: 736: try 737: { 738: factory = cls.getMethod ("createUI", new Class[] { JComponent.class } ); 739: } 740: catch (NoSuchMethodException nme) 741: { 742: getUIError ("failed to locate createUI method on " + cls.toString ()); 743: return null; 744: } 745: 746: try 747: { 748: return (ComponentUI) factory.invoke (null, new Object[] { target }); 749: } 750: catch (java.lang.reflect.InvocationTargetException ite) 751: { 752: getUIError ("InvocationTargetException ("+ ite.getTargetException() 753: +") calling createUI(...) on " + cls.toString ()); 754: return null; 755: } 756: catch (Exception e) 757: { 758: getUIError ("exception calling createUI(...) on " + cls.toString ()); 759: return null; 760: } 761: } 762: 763: /** 764: * Adds a {@link PropertyChangeListener} to this UIDefaults map. 765: * Registered PropertyChangeListener are notified when values 766: * are beeing put into this UIDefaults map. 767: * 768: * @param listener the PropertyChangeListener to add 769: */ 770: public void addPropertyChangeListener(PropertyChangeListener listener) 771: { 772: propertyChangeSupport.addPropertyChangeListener(listener); 773: } 774: 775: /** 776: * Removes a PropertyChangeListener from this UIDefaults map. 777: * 778: * @param listener the PropertyChangeListener to remove 779: */ 780: public void removePropertyChangeListener(PropertyChangeListener listener) 781: { 782: propertyChangeSupport.removePropertyChangeListener(listener); 783: } 784: 785: /** 786: * Returns an array of all registered PropertyChangeListeners. 787: * 788: * @return all registered PropertyChangeListeners 789: */ 790: public PropertyChangeListener[] getPropertyChangeListeners() 791: { 792: return propertyChangeSupport.getPropertyChangeListeners(); 793: } 794: 795: /** 796: * Fires a PropertyChangeEvent. 797: * 798: * @param property the property name 799: * @param oldValue the old value 800: * @param newValue the new value 801: */ 802: protected void firePropertyChange(String property, 803: Object oldValue, Object newValue) 804: { 805: propertyChangeSupport.firePropertyChange(property, oldValue, newValue); 806: } 807: 808: /** 809: * Adds a ResourceBundle for localized values. 810: * 811: * @param name the name of the ResourceBundle to add 812: */ 813: public void addResourceBundle(String name) 814: { 815: bundles.addFirst(name); 816: } 817: 818: /** 819: * Removes a ResourceBundle. 820: * 821: * @param name the name of the ResourceBundle to remove 822: */ 823: public void removeResourceBundle(String name) 824: { 825: bundles.remove(name); 826: } 827: 828: /** 829: * Sets the current locale to <code>loc</code>. 830: * 831: * @param loc the Locale to be set 832: */ 833: public void setDefaultLocale(Locale loc) 834: { 835: defaultLocale = loc; 836: } 837: 838: /** 839: * Returns the current default locale. 840: * 841: * @return the current default locale 842: */ 843: public Locale getDefaultLocale() 844: { 845: return defaultLocale; 846: } 847: }