While playing with some custom L&Fs I noticed some flaws in our
UIManager impl. It seems like the UIManager should not only manage 1
UIDefaults instance (from the L&F), but another one, to which
applications can make modifications. The latter one overloads the L&F
defaults. The lookup has to first check the 'user' defaults, if the key
isn't there, it falls back to the L&F defaults. So, calling
UIManager.put("blah", null) doesn't necessarily lead to
UIManager.get("blah") == null, when that key is still in the L&F
defaults, then the value from there is returned.

I implemented this with a special subclass of UIDefaults, which does
exactly this kind of multiplexing. This brings us a little forward with
custom L&Fs. Unfortunately some of them are still not usable due to
missing Font.create().


2006-06-21  Roman Kennke  <[EMAIL PROTECTED]>

        * javax/swing/UIManager.java
        (MultiplexUIDefaults): New inner class.
        (currentUIDefaults): Changed type to be MultiplexUIDefaults.
        (userUIDefaults): Changed name to be lookAndFeelDefaults.
        (<cinit>): Call setLookAndFeel(String) instead of trying to load
        directly. Print stacktrace if something goes wrong.
        (get): Delegate call to currentUIDefaults.
        (getDefaults): If currentUIDefaults is null, then lazily
instantiate
        it.
        (getUI): Delegate call to currentUIDefaults.
        (put): Delegate call to currentUIDefaults.
        (setLookAndFeel): Initialize currentUIDefaults with
        MultiplexUIDefaults. Set lookAndFeelDefaults.
        (setLookAndFeel): Use current thread's context classloader for
        loading the L&F.

/Roman

-- 
“Improvement makes straight roads, but the crooked roads, without
Improvement, are roads of Genius.” - William Blake
Index: javax/swing/UIManager.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/UIManager.java,v
retrieving revision 1.35
diff -u -1 -0 -r1.35 UIManager.java
--- javax/swing/UIManager.java	1 Jun 2006 04:38:49 -0000	1.35
+++ javax/swing/UIManager.java	21 Jun 2006 09:56:39 -0000
@@ -38,20 +38,21 @@
 
 package javax.swing;
 
 import java.awt.Color;
 import java.awt.Dimension;
 import java.awt.Font;
 import java.awt.Insets;
 import java.beans.PropertyChangeListener;
 import java.beans.PropertyChangeSupport;
 import java.io.Serializable;
+import java.util.Enumeration;
 import java.util.Locale;
 
 import javax.swing.border.Border;
 import javax.swing.plaf.ComponentUI;
 import javax.swing.plaf.metal.MetalLookAndFeel;
 
 /**
  * Manages the current [EMAIL PROTECTED] LookAndFeel} and any auxiliary [EMAIL PROTECTED] LookAndFeel}
  * instances.
  */
@@ -110,65 +111,148 @@
       s.append(getClass().getName());
       s.append('[');
       s.append(getName());
       s.append(' ');
       s.append(getClassName());
       s.append(']');
       return s.toString();
     }
   }
 
+  /**
+   * A UIDefaults subclass that multiplexes between itself and a 'fallback'
+   * UIDefaults instance. This is used to protect the L&F UIDefaults from beeing
+   * overwritten by applications.
+   */
+  private static class MultiplexUIDefaults
+    extends UIDefaults
+  {
+    private class MultiplexEnumeration
+      implements Enumeration
+    {
+      Enumeration[] enums;
+      int i;
+      MultiplexEnumeration(Enumeration e1, Enumeration e2)
+      {
+        enums = new Enumeration[]{ e1, e2 };
+        i = 0;
+      }
+
+      public boolean hasMoreElements()
+      {
+        return enums[i].hasMoreElements() || i < enums.length - 1;
+      }
+
+      public Object nextElement()
+      {
+        Object val = enums[i].nextElement();
+        if (! enums[i].hasMoreElements() && i < enums.length - 1)
+          i++;
+        return val;
+      }
+        
+    }
+
+    UIDefaults fallback;
+
+    MultiplexUIDefaults(UIDefaults d)
+    {
+      fallback = d;
+    }
+
+    public Object get(Object key)
+    {
+      Object val = super.get(key);
+      if (val == null)
+        val = fallback.get(key);
+      return val;
+    }
+
+    public Object get(Object key, Locale l)
+    {
+      Object val = super.get(key, l);
+      if (val == null)
+        val = fallback.get(key, l);
+      return val;
+    }
+
+    public Object remove(Object key)
+    {
+      Object val = super.remove(key);
+      if (val == null)
+        val = fallback.remove(key);
+      return val;
+    }
+
+    public void clear()
+    {
+      super.clear();
+      fallback.clear();
+    }
+
+    public int size()
+    {
+      return super.size() + fallback.size();
+    }
+
+    public Enumeration keys()
+    {
+      return new MultiplexEnumeration(super.keys(), fallback.keys());
+    }
+
+    public Enumeration elements()
+    {
+      return new MultiplexEnumeration(super.elements(), fallback.elements());
+    }
+  }
+
   private static final long serialVersionUID = -5547433830339189365L;
 
   /** The installed look and feel(s). */
   static LookAndFeelInfo [] installed = {
     new LookAndFeelInfo("Metal", "javax.swing.plaf.metal.MetalLookAndFeel"),
     new LookAndFeelInfo("GNU", "gnu.javax.swing.plaf.gnu.GNULookAndFeel")
   };
 
   /** The installed auxiliary look and feels. */
   static LookAndFeel[] auxLookAndFeels;
   
   /** The current look and feel. */
   static LookAndFeel currentLookAndFeel;
   
-  static UIDefaults currentUIDefaults;
+  static MultiplexUIDefaults currentUIDefaults;
 
-  /**
-   * UIDefaults set by the user.
-   */
-  static UIDefaults userUIDefaults;
+  static UIDefaults lookAndFeelDefaults;
 
   /** Property change listener mechanism. */
   static PropertyChangeSupport listeners
       = new PropertyChangeSupport(UIManager.class);
 
   static
   {
     String defaultlaf = System.getProperty("swing.defaultlaf");
     try 
       {
         if (defaultlaf != null)
           {
-            Class lafClass = Class.forName(defaultlaf);
-            LookAndFeel laf = (LookAndFeel) lafClass.newInstance();
-            setLookAndFeel(laf);
+            setLookAndFeel(defaultlaf);
           }
         else
           {
             setLookAndFeel(new MetalLookAndFeel());
           }
       }
     catch (Exception ex)
       {
         System.err.println("cannot initialize Look and Feel: " + defaultlaf);
         System.err.println("error: " + ex.toString());
+        ex.printStackTrace();
         System.err.println("falling back to Metal Look and Feel");
         try
           {
             setLookAndFeel(new MetalLookAndFeel());
           }
         catch (Exception ex2)
         {
           throw (Error) new AssertionError("There must be no problem installing"
                                            + " the MetalLookAndFeel.")
                                            .initCause(ex2);
@@ -305,44 +389,34 @@
   /**
    * Returns an object from the [EMAIL PROTECTED] UIDefaults} table for the current
    * [EMAIL PROTECTED] LookAndFeel}.
    * 
    * @param key  the key.
    * 
    * @return The object.
    */
   public static Object get(Object key)
   {
-    Object val = null;
-    if (userUIDefaults != null)
-      val = userUIDefaults.get(key);
-    if (val == null)
-      val = getLookAndFeelDefaults().get(key);
-    return val;
+    return getDefaults().get(key);
   }
 
   /**
    * Returns an object from the [EMAIL PROTECTED] UIDefaults} table for the current
    * [EMAIL PROTECTED] LookAndFeel}.
    * 
    * @param key  the key.
    * 
    * @return The object.
    */
   public static Object get(Object key, Locale locale)
   {
-    Object val = null;
-    if (userUIDefaults != null)
-      val = userUIDefaults.get(key, locale);
-    if (val == null)
-      val = getLookAndFeelDefaults().get(key, locale);
-    return val;
+    return getDefaults().get(key, locale);
   }
 
   /**
    * Returns a boolean value from the defaults table,
    * <code>false</code> if key is not present.
    *
    * @since 1.4
    */
   public static boolean getBoolean(Object key)
   {
@@ -407,20 +481,22 @@
     return "javax.swing.plaf.metal.MetalLookAndFeel";
   }
 
   /**
    * Returns the default values for this look and feel. 
    * 
    * @return The [EMAIL PROTECTED] UIDefaults} for the current [EMAIL PROTECTED] LookAndFeel}.
    */
   public static UIDefaults getDefaults()
   {
+    if (currentUIDefaults == null)
+      currentUIDefaults = new MultiplexUIDefaults(null);
     return currentUIDefaults;
   }
 
   /**
    * Returns a dimension from the defaults table. 
    */
   public static Dimension getDimension(Object key)
   {
     return (Dimension) get(key);
   }
@@ -580,27 +656,21 @@
   }
 
   /**
    * Returns UI delegate from the current [EMAIL PROTECTED] LookAndFeel} that renders the 
    * target component.
    * 
    * @param target  the target component.
    */
   public static ComponentUI getUI(JComponent target)
   {
-    ComponentUI ui = null;
-    if (userUIDefaults != null
-        && userUIDefaults.get(target.getUIClassID()) != null)
-      ui = userUIDefaults.getUI(target);
-    if (ui == null)
-      ui = currentUIDefaults.getUI(target);
-    return ui;
+    return getDefaults().getUI(target);
   }
 
   /**
    * Creates a new look and feel and adds it to the current array.
    * 
    * @param name  the look and feel name.
    * @param className  the fully qualified name of the class that implements the
    *                   look and feel.
    */
   public static void installLookAndFeel(String name, String className)
@@ -618,25 +688,21 @@
     System.arraycopy(installed, 0, newInstalled, 0, installed.length);
     newInstalled[newInstalled.length - 1] = info;
     setInstalledLookAndFeels(newInstalled);
   }
 
   /**
    * Stores an object in the defaults table.
    */
   public static Object put(Object key, Object value)
   {
-    Object old = get(key);
-    if (userUIDefaults == null)
-      userUIDefaults = new UIDefaults();
-    userUIDefaults.put(key, value);
-    return old;
+    return getDefaults().put(key, value);
   }
 
   /**
    * Replaces the current array of installed LookAndFeelInfos.
    */
   public static void setInstalledLookAndFeels(UIManager.LookAndFeelInfo[] infos)
   {
     installed = infos;
   }
   
@@ -657,21 +723,26 @@
       throw new UnsupportedLookAndFeelException(newLookAndFeel.getName());
     LookAndFeel oldLookAndFeel = currentLookAndFeel;
     if (oldLookAndFeel != null)
       oldLookAndFeel.uninitialize();
 
     // Set the current default look and feel using a LookAndFeel object. 
     currentLookAndFeel = newLookAndFeel;
     if (newLookAndFeel != null)
       {
         newLookAndFeel.initialize();
-        currentUIDefaults = newLookAndFeel.getDefaults();
+        lookAndFeelDefaults = newLookAndFeel.getDefaults();
+        if (currentUIDefaults == null)
+          currentUIDefaults =
+            new MultiplexUIDefaults(lookAndFeelDefaults);
+        else
+          currentUIDefaults.fallback = lookAndFeelDefaults;
       }
     else
       {
         currentUIDefaults = null;    
       }
     listeners.firePropertyChange("lookAndFeel", oldLookAndFeel, newLookAndFeel);
     //revalidate();
     //repaint();
   }
 
@@ -682,15 +753,16 @@
    * 
    * @throws UnsupportedLookAndFeelException if the look and feel is not 
    *         supported on the current platform.
    * 
    * @see LookAndFeel#isSupportedLookAndFeel()
    */
   public static void setLookAndFeel(String className)
     throws ClassNotFoundException, InstantiationException, IllegalAccessException,
     UnsupportedLookAndFeelException
   {
-    Class c = Class.forName(className);
+    Class c = Class.forName(className, true,
+                            Thread.currentThread().getContextClassLoader());
     LookAndFeel a = (LookAndFeel) c.newInstance(); // throws class-cast-exception
     setLookAndFeel(a);
   }
 }

Attachment: signature.asc
Description: Dies ist ein digital signierter Nachrichtenteil

Reply via email to