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