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