OSDN Git Service

2006-06-09 Thomas Fitzsimmons <fitzsim@redhat.com>
[pf3gnuchains/gcc-fork.git] / libjava / classpath / javax / swing / UIDefaults.java
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" -&gt; new BasicButtonUI().
63  *
64  * @author Ronald Veldema (rveldema@cs.vu.nl)
65  */
66 public class UIDefaults extends Hashtable
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>&apos;UIDefaults&apos;</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 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 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 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 }