6802944: Nimbus initialization is too slow
authorpeterz
Mon, 31 Aug 2009 13:46:24 +0400
changeset 3741 4021567cd4ca
parent 3740 efb34ff1cef4
child 3742 5e1b8bc3cb1a
6802944: Nimbus initialization is too slow Reviewed-by: jasper
jdk/make/tools/swing-nimbus/classes/org/jdesktop/synthdesigner/generator/DefaultsGenerator.java
jdk/src/share/classes/javax/swing/plaf/nimbus/Defaults.template
jdk/src/share/classes/javax/swing/plaf/nimbus/DerivedColor.java
jdk/src/share/classes/javax/swing/plaf/nimbus/NimbusLookAndFeel.java
jdk/src/share/classes/javax/swing/plaf/nimbus/NimbusStyle.java
--- a/jdk/make/tools/swing-nimbus/classes/org/jdesktop/synthdesigner/generator/DefaultsGenerator.java	Mon Aug 31 14:53:05 2009 +0900
+++ b/jdk/make/tools/swing-nimbus/classes/org/jdesktop/synthdesigner/generator/DefaultsGenerator.java	Mon Aug 31 13:46:24 2009 +0400
@@ -24,6 +24,7 @@
  */
 package org.jdesktop.synthdesigner.generator;
 
+import java.awt.Color;
 import org.jdesktop.swingx.designer.Canvas;
 import org.jdesktop.swingx.designer.font.Typeface;
 import org.jdesktop.swingx.designer.paint.Matte;
@@ -133,11 +134,7 @@
 
     private static void writeColorPalette(StringBuilder uiDefaultInit, List<UIPaint> colors) {
         for (UIPaint color : colors) {
-            uiDefaultInit.append("        d.put(\"")
-                    .append(color.getName())
-                    .append("\",")
-                    .append(convertPaint(color.getValue()))
-                    .append(");\n");
+            writeMatte(color.getName(), (Matte)color.getValue(), uiDefaultInit);
         }
     }
 
@@ -255,12 +252,8 @@
                             .append("));\n");
                     break;
                 case COLOR:
-                    uiDefaultInit.append("        d.put(\"")
-                            .append(prefix)
-                            .append(property.getName())
-                            .append("\", ")
-                            .append(convertPaint((Matte)property.getValue()))
-                            .append(");\n");
+                    writeMatte(prefix + property.getName(),
+                            (Matte) property.getValue(), uiDefaultInit);
                     break;
                 case FONT:
                     writeTypeFace(prefix.replace("\"", "\\\"") + property.getName(),
@@ -300,7 +293,7 @@
 
     private static void writeMatte(String propertyName, Matte matte, StringBuilder uiDefaultInit) {
         if (matte==null) System.err.println("Error matte is NULL for ["+propertyName+"]");
-        uiDefaultInit.append("        d.put(\"")
+        uiDefaultInit.append("        addColor(d, \"")
                     .append(propertyName)
                     .append("\", ")
                     .append(convertPaint(matte))
@@ -605,27 +598,23 @@
         return s.replace("\"", "\\\"");
     }
 
-    private static String convertPaint(PaintModel paint){
-        if (paint instanceof Matte){
-            Matte matte = (Matte)paint;
-            if (matte.isAbsolute()){
-                String colorParams = convert(matte.getColor());
-                if (matte.isUiResource()) {
-                    return "new ColorUIResource(" + colorParams + ")";
-                } else {
-                    return colorParams;
-                }
+    private static String convertPaint(PaintModel paint) {
+        if (paint instanceof Matte) {
+            Matte matte = (Matte) paint;
+            if (matte.isAbsolute()) {
+                Color c = matte.getColor();
+                return c.getRed()  + ", " + c.getGreen() + ", " +
+                       c.getBlue() + ", " + c.getAlpha();
             } else {
-                String s = "getDerivedColor(\"" +
-                            matte.getUiDefaultParentName()+"\","+
-                            matte.getHueOffset()+"f,"+matte.getSaturationOffset()+
-                            "f,"+matte.getBrightnessOffset()+"f,"+
-                            matte.getAlphaOffset();
-                if (matte.isUiResource()) {
-                    return s + ")";
-                } else {
-                    return s + ",false)";
+                String s = "\"" + matte.getUiDefaultParentName() + "\", " +
+                        matte.getHueOffset()        + "f, " +
+                        matte.getSaturationOffset() + "f, " +
+                        matte.getBrightnessOffset() + "f, " +
+                        matte.getAlphaOffset();
+                if (! matte.isUiResource()) {
+                    s += ", false";
                 }
+                return s;
             }
         } else {
             //TODO: What about gradients etc here?
--- a/jdk/src/share/classes/javax/swing/plaf/nimbus/Defaults.template	Mon Aug 31 14:53:05 2009 +0900
+++ b/jdk/src/share/classes/javax/swing/plaf/nimbus/Defaults.template	Mon Aug 31 13:46:24 2009 +0400
@@ -101,14 +101,7 @@
      */
     private FontUIResource defaultFont;
 
-    /**
-     * Map of lists of derived colors keyed by the DerivedColorKeys
-     */
-    private Map<DerivedColorKey, DerivedColor> derivedColorsMap =
-            new HashMap<DerivedColorKey, DerivedColor>();
-
-    /** Tempory key used for fetching from the derivedColorsMap */
-    private final DerivedColorKey tmpDCKey = new DerivedColorKey();
+    private ColorTree colorTree = new ColorTree();
 
     /** Listener for changes to user defaults table */
     private DefaultsListener defaultsListener = new DefaultsListener();
@@ -117,14 +110,14 @@
     void initialize() {
         // add listener for derived colors
         UIManager.addPropertyChangeListener(defaultsListener);
-        UIManager.getDefaults().addPropertyChangeListener(defaultsListener);
+        UIManager.getDefaults().addPropertyChangeListener(colorTree);
     }
 
     /** Called by UIManager when this look and feel is uninstalled. */
     void uninitialize() {
         // remove listener for derived colors
-        UIManager.getDefaults().removePropertyChangeListener(defaultsListener);
         UIManager.removePropertyChangeListener(defaultsListener);
+        UIManager.getDefaults().removePropertyChangeListener(colorTree);
     }
 
     /**
@@ -663,22 +656,23 @@
         }
     }
 
-    /**
-     * Get a derived color, derived colors are shared instances and will be
-     * updated when its parent UIDefault color changes.
-     *
-     * @param uiDefaultParentName The parent UIDefault key
-     * @param hOffset The hue offset
-     * @param sOffset The saturation offset
-     * @param bOffset The brightness offset
-     * @param aOffset The alpha offset
-     * @return The stored derived color
-     */
-    public DerivedColor getDerivedColor(String uiDefaultParentName,
-                                        float hOffset, float sOffset,
-                                        float bOffset, int aOffset){
-        return getDerivedColor(uiDefaultParentName, hOffset, sOffset,
-                               bOffset, aOffset, true);
+    private void addColor(UIDefaults d, String uin, int r, int g, int b, int a) {
+        Color color = new ColorUIResource(new Color(r, g, b, a));
+        colorTree.addColor(uin, color);
+        d.put(uin, color);
+    }
+
+    private void addColor(UIDefaults d, String uin, String parentUin,
+            float hOffset, float sOffset, float bOffset, int aOffset) {
+        addColor(d, uin, parentUin, hOffset, sOffset, bOffset, aOffset, true);
+    }
+
+    private void addColor(UIDefaults d, String uin, String parentUin,
+            float hOffset, float sOffset, float bOffset,
+            int aOffset, boolean uiResource) {
+        Color color = getDerivedColor(uin, parentUin,
+                hOffset, sOffset, bOffset, aOffset, uiResource);
+        d.put(uin, color);
     }
 
     /**
@@ -694,89 +688,110 @@
      *        false if it should not be a UIResource
      * @return The stored derived color
      */
-    public DerivedColor getDerivedColor(String uiDefaultParentName,
+    public DerivedColor getDerivedColor(String parentUin,
                                         float hOffset, float sOffset,
                                         float bOffset, int aOffset,
                                         boolean uiResource){
-        tmpDCKey.set(uiDefaultParentName, hOffset, sOffset, bOffset, aOffset,
-            uiResource);
-        DerivedColor color = derivedColorsMap.get(tmpDCKey);
-        if (color == null){
-            if (uiResource) {
-                color = new DerivedColor.UIResource(uiDefaultParentName,
-                        hOffset, sOffset, bOffset, aOffset);
-            } else {
-                color = new DerivedColor(uiDefaultParentName, hOffset, sOffset,
-                    bOffset, aOffset);
-            }
-            // calculate the initial value
-            color.rederiveColor();
-            // add the listener so that if the color changes we'll propogate it
-            color.addPropertyChangeListener(defaultsListener);
-            // add to the derived colors table
-            derivedColorsMap.put(new DerivedColorKey(uiDefaultParentName,
-                    hOffset, sOffset, bOffset, aOffset, uiResource),color);
+        return getDerivedColor(null, parentUin,
+                hOffset, sOffset, bOffset, aOffset, uiResource);
+    }
+
+    private DerivedColor getDerivedColor(String uin, String parentUin,
+                                        float hOffset, float sOffset,
+                                        float bOffset, int aOffset,
+                                        boolean uiResource) {
+        DerivedColor color;
+        if (uiResource) {
+            color = new DerivedColor.UIResource(parentUin,
+                    hOffset, sOffset, bOffset, aOffset);
+        } else {
+            color = new DerivedColor(parentUin, hOffset, sOffset,
+                bOffset, aOffset);
         }
-        return color;
+
+        if (derivedColors.containsKey(color)) {
+            return derivedColors.get(color);
+        } else {
+            derivedColors.put(color, color);
+            color.rederiveColor(); /// move to ARP.decodeColor() ?
+            colorTree.addColor(uin, color);
+            return color;
+        }
     }
 
-    /**
-     * Key class for derived colors
-     */
-    private class DerivedColorKey {
-        private String uiDefaultParentName;
-        private float hOffset, sOffset, bOffset;
-        private int aOffset;
-        private boolean uiResource;
+    private Map<DerivedColor, DerivedColor> derivedColors =
+            new HashMap<DerivedColor, DerivedColor>();
 
-        DerivedColorKey(){}
+    private class ColorTree implements PropertyChangeListener {
+        private Node root = new Node(null, null);
+        private Map<String, Node> nodes = new HashMap<String, Node>();
 
-        DerivedColorKey(String uiDefaultParentName, float hOffset,
-                        float sOffset, float bOffset, int aOffset,
-                        boolean uiResource) {
-            set(uiDefaultParentName, hOffset, sOffset, bOffset, aOffset, uiResource);
+        public Color getColor(String uin) {
+            return nodes.get(uin).color;
         }
 
-        void set (String uiDefaultParentName, float hOffset,
-                        float sOffset, float bOffset, int aOffset,
-                        boolean uiResource) {
-            this.uiDefaultParentName = uiDefaultParentName;
-            this.hOffset = hOffset;
-            this.sOffset = sOffset;
-            this.bOffset = bOffset;
-            this.aOffset = aOffset;
-            this.uiResource = uiResource;
+        public void addColor(String uin, Color color) {
+            Node parent = getParentNode(color);
+            Node node = new Node(color, parent);
+            parent.children.add(node);
+            if (uin != null) {
+                nodes.put(uin, node);
+            }
+        }
+
+        private Node getParentNode(Color color) {
+            Node parent = root;
+            if (color instanceof DerivedColor) {
+                String parentUin = ((DerivedColor)color).getUiDefaultParentName();
+                Node p = nodes.get(parentUin);
+                if (p != null) {
+                    parent = p;
+                }
+            }
+            return parent;
+        }
+
+        public void update() {
+            root.update();
         }
 
         @Override
-        public boolean equals(Object o) {
-            if (this == o) return true;
-            if (!(o instanceof DerivedColorKey)) return false;
-            DerivedColorKey that = (DerivedColorKey) o;
-            if (aOffset != that.aOffset) return false;
-            if (Float.compare(that.bOffset, bOffset) != 0) return false;
-            if (Float.compare(that.hOffset, hOffset) != 0) return false;
-            if (Float.compare(that.sOffset, sOffset) != 0) return false;
-            if (uiDefaultParentName != null ?
-                !uiDefaultParentName.equals(that.uiDefaultParentName) :
-                that.uiDefaultParentName != null) return false;
-            if (this.uiResource != that.uiResource) return false;
-            return true;
+        public void propertyChange(PropertyChangeEvent ev) {
+            String name = ev.getPropertyName();
+            Node node = nodes.get(name);
+            if (node != null) {
+                // this is a registered color
+                node.parent.children.remove(node);
+                Color color = (Color) ev.getNewValue();
+                Node parent = getParentNode(color);
+                node.set(color, parent);
+                parent.children.add(node);
+                node.update();
+            }
         }
 
-        @Override
-        public int hashCode() {
-            int result = super.hashCode();
-            result = 31 * result + uiDefaultParentName.hashCode();
-            result = 31 * result + hOffset != +0.0f ?
-                    Float.floatToIntBits(hOffset) : 0;
-            result = 31 * result + sOffset != +0.0f ?
-                    Float.floatToIntBits(sOffset) : 0;
-            result = 31 * result + bOffset != +0.0f ?
-                    Float.floatToIntBits(bOffset) : 0;
-            result = 31 * result + aOffset;
-            result = 31 * result + (uiResource ? 1 : 0);
-            return result;
+        class Node {
+            Color color;
+            Node parent;
+            List<Node> children = new LinkedList<Node>();
+
+            Node(Color color, Node parent) {
+                set(color, parent);
+            }
+
+            public void set(Color color, Node parent) {
+                this.color = color;
+                this.parent = parent;
+            }
+
+            public void update() {
+                if (color instanceof DerivedColor) {
+                    ((DerivedColor)color).rederiveColor();
+                }
+                for (Node child: children) {
+                    child.update();
+                }
+            }
         }
     }
 
@@ -786,49 +801,12 @@
     private class DefaultsListener implements PropertyChangeListener {
         @Override
         public void propertyChange(PropertyChangeEvent evt) {
-            Object src = evt.getSource();
-            String key = evt.getPropertyName();
-            if (key.equals("lookAndFeel")){
+            if ("lookAndFeel".equals(evt.getPropertyName())) {
                 // LAF has been installed, this is the first point at which we
                 // can access our defaults table via UIManager so before now
                 // all derived colors will be incorrect.
                 // First we need to update
-                for (DerivedColor color : derivedColorsMap.values()) {
-                    color.rederiveColor();
-                }
-            } else if (src instanceof DerivedColor && key.equals("rgb")) {
-                // derived color that is in UIManager defaults has changed
-                // update all its dependent colors. Don't worry about doing
-                // this recursively since calling rederiveColor will cause
-                // another PCE to be fired, ending up here and essentially
-                // recursing
-                DerivedColor parentColor = (DerivedColor)src;
-                String parentKey = null;
-                Set<Map.Entry<Object,Object>> entries =
-                        UIManager.getDefaults().entrySet();
-                
-                for (Map.Entry entry : entries) {
-                    Object value = entry.getValue();
-                    if (value == parentColor) {
-                        parentKey = entry.getKey().toString();
-                    }
-                }
-                
-                if (parentKey == null) {
-                    //couldn't find the DerivedColor in the UIDefaults map,
-                    //so we just bail.
-                    return;
-                }
-                
-                for (Map.Entry entry : entries) {
-                    Object value = entry.getValue();
-                    if (value instanceof DerivedColor) {
-                        DerivedColor color = (DerivedColor)entry.getValue();
-                        if (parentKey.equals(color.getUiDefaultParentName())) {
-                            color.rederiveColor();
-                        }
-                    }
-                }
+                colorTree.update();
             }
         }
     }
@@ -875,4 +853,3 @@
         }
     }
 }
-
--- a/jdk/src/share/classes/javax/swing/plaf/nimbus/DerivedColor.java	Mon Aug 31 14:53:05 2009 +0900
+++ b/jdk/src/share/classes/javax/swing/plaf/nimbus/DerivedColor.java	Mon Aug 31 13:46:24 2009 +0400
@@ -39,8 +39,6 @@
  * @author Jasper Potts
  */
 class DerivedColor extends Color {
-    private final PropertyChangeSupport changeSupport =
-            new PropertyChangeSupport(this);
     private final String uiDefaultParentName;
     private final float hOffset, sOffset, bOffset;
     private final int aOffset;
@@ -79,7 +77,6 @@
      * Recalculate the derived color from the UIManager parent color and offsets
      */
     public void rederiveColor() {
-        int old = argbValue;
         Color src = UIManager.getColor(uiDefaultParentName);
         if (src != null) {
             float[] tmp = Color.RGBtoHSB(src.getRed(), src.getGreen(), src.getBlue(), null);
@@ -97,7 +94,6 @@
             int alpha = clamp(aOffset);
             argbValue = (Color.HSBtoRGB(tmp[0], tmp[1], tmp[2]) & 0xFFFFFF) | (alpha << 24);
         }
-        changeSupport.firePropertyChange("rgb", old, argbValue);
     }
 
     /**
@@ -141,35 +137,6 @@
         return result;
     }
 
-     /**
-     * Add a PropertyChangeListener to the listener list.
-     * The listener is registered for all properties.
-     * The same listener object may be added more than once, and will be called
-     * as many times as it is added.
-     * If <code>listener</code> is null, no exception is thrown and no action
-     * is taken.
-     *
-     * @param listener  The PropertyChangeListener to be added
-     */
-    public void addPropertyChangeListener(PropertyChangeListener listener) {
-        changeSupport.addPropertyChangeListener(listener);
-    }
-
-    /**
-     * Remove a PropertyChangeListener from the listener list.
-     * This removes a PropertyChangeListener that was registered
-     * for all properties.
-     * If <code>listener</code> was added more than once to the same event
-     * source, it will be notified one less time after being removed.
-     * If <code>listener</code> is null, or was never added, no exception is
-     * thrown and no action is taken.
-     *
-     * @param listener  The PropertyChangeListener to be removed
-     */
-    public void removePropertyChangeListener(PropertyChangeListener listener) {
-        changeSupport.removePropertyChangeListener(listener);
-    }
-
     private float clamp(float value) {
         if (value < 0) {
             value = 0;
@@ -211,5 +178,15 @@
                    float bOffset, int aOffset) {
             super(uiDefaultParentName, hOffset, sOffset, bOffset, aOffset);
         }
+
+        @Override
+        public boolean equals(Object o) {
+            return (o instanceof UIResource) && super.equals(o);
+        }
+
+        @Override
+        public int hashCode() {
+            return super.hashCode() + 7;
+        }
     }
 }
--- a/jdk/src/share/classes/javax/swing/plaf/nimbus/NimbusLookAndFeel.java	Mon Aug 31 14:53:05 2009 +0900
+++ b/jdk/src/share/classes/javax/swing/plaf/nimbus/NimbusLookAndFeel.java	Mon Aug 31 13:46:24 2009 +0400
@@ -40,6 +40,9 @@
 import java.awt.Graphics2D;
 import java.awt.LayoutManager;
 import java.awt.image.BufferedImage;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.*;
 import javax.swing.GrayFilter;
 import javax.swing.Icon;
 import javax.swing.JToolBar;
@@ -87,6 +90,8 @@
      */
     private UIDefaults uiDefaults;
 
+    private DefaultsListener defaultsListener = new DefaultsListener();
+
     /**
      * Create a new NimbusLookAndFeel.
      */
@@ -115,8 +120,7 @@
         defaults.uninitialize();
         // clear all cached images to free memory
         ImageCache.getInstance().flush();
-        // remove the listeners and things installed by NimbusStyle
-        NimbusStyle.uninitialize();
+        UIManager.getDefaults().removePropertyChangeListener(defaultsListener);
     }
 
     /**
@@ -515,4 +519,62 @@
             return obj;
         }
     }
+
+    private Map<String, Map<String, Object>> compiledDefaults = null;
+    private boolean defaultListenerAdded = false;
+
+    static String parsePrefix(String key) {
+        if (key == null) {
+            return null;
+        }
+        boolean inquotes = false;
+        for (int i = 0; i < key.length(); i++) {
+            char c = key.charAt(i);
+            if (c == '"') {
+                inquotes = !inquotes;
+            } else if ((c == '[' || c == '.') && !inquotes) {
+                return key.substring(0, i);
+            }
+        }
+        return null;
+    }
+
+    Map<String, Object> getDefaultsForPrefix(String prefix) {
+        if (compiledDefaults == null) {
+            compiledDefaults = new HashMap<String, Map<String, Object>>();
+            for (Map.Entry<Object, Object> entry: UIManager.getDefaults().entrySet()) {
+                if (entry.getKey() instanceof String) {
+                    addDefault((String) entry.getKey(), entry.getValue());
+                }
+            }
+            if (! defaultListenerAdded) {
+                UIManager.getDefaults().addPropertyChangeListener(defaultsListener);
+                defaultListenerAdded = true;
+            }
+        }
+        return compiledDefaults.get(prefix);
+    }
+
+    private void addDefault(String key, Object value) {
+        String prefix = parsePrefix(key);
+        if (prefix != null) {
+            Map<String, Object> keys = compiledDefaults.get(prefix);
+            if (keys == null) {
+                keys = new HashMap<String, Object>();
+                compiledDefaults.put(prefix, keys);
+            }
+            keys.put(key, value);
+        }
+    }
+
+    private class DefaultsListener implements PropertyChangeListener {
+        @Override public void propertyChange(PropertyChangeEvent ev) {
+            String key = ev.getPropertyName();
+            if ("UIDefaults".equals(key)) {
+                compiledDefaults = null;
+            } else {
+                addDefault(key, ev.getNewValue());
+            }
+        }
+    }
 }
--- a/jdk/src/share/classes/javax/swing/plaf/nimbus/NimbusStyle.java	Mon Aug 31 14:53:05 2009 +0900
+++ b/jdk/src/share/classes/javax/swing/plaf/nimbus/NimbusStyle.java	Mon Aug 31 13:46:24 2009 +0400
@@ -26,7 +26,6 @@
 
 import javax.swing.Painter;
 
-import java.beans.PropertyChangeEvent;
 import javax.swing.JComponent;
 import javax.swing.UIDefaults;
 import javax.swing.UIManager;
@@ -39,16 +38,13 @@
 import java.awt.Color;
 import java.awt.Font;
 import java.awt.Insets;
-import java.beans.PropertyChangeListener;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.TreeMap;
-import sun.awt.AppContext;
 
 /**
  * <p>A SynthStyle implementation used by Nimbus. Each Region that has been
@@ -232,42 +228,6 @@
         super.installDefaults(ctx);
     }
 
-    static String parsePrefix(String key) {
-       if (key == null) return null;
-       boolean inquotes = false;
-       for (int i=0; i<key.length(); i++) {
-           char c = key.charAt(i);
-           if (c == '"') {
-               inquotes = !inquotes;
-           } else if ((c == '[' || c == '.') && !inquotes) {
-               return key.substring(0, i);
-           }
-       }
-       return null;
-    }
-
-    /**
-     * Called by NimbusLookAndFeel when the look and feel is being uninstalled.
-     * Performs general cleanup of any app-context specific data.
-     */
-    static void uninitialize() {
-        // get the appcontext that we've stored data in
-        AppContext ctx = AppContext.getAppContext();
-
-        // get the pcl stored in app context
-        PropertyChangeListener pcl = (PropertyChangeListener)
-                ctx.get("NimbusStyle.defaults.pcl");
-
-        // if the pcl exists, uninstall it from the UIDefaults tables
-        if (pcl != null) {
-            UIManager.getDefaults().removePropertyChangeListener(pcl);
-            UIManager.getLookAndFeelDefaults().removePropertyChangeListener(pcl);
-        }
-
-        // clear out the compiled defaults
-        ctx.put("NimbusStyle.defaults", null);
-    }
-
     /**
      * Pulls data out of UIDefaults, if it has not done so already, and sets
      * up the internal state.
@@ -283,66 +243,9 @@
         // any Nimbus.Overrides)
         values = new Values();
 
-        // the profiler revealed that a great deal of CPU time and useless
-        // garbage was being produced by this method and the init method. One
-        // culprit was the creation and reparsing of the entire UIDefaults
-        // map on each call to this method where "values" was null. It turns
-        // out this was happening a lot.
-        // To remove this bottleneck, we store the compiled TreeMaps of defaults
-        // in the appContext for reuse. It is nulled whenever the UIDefaults
-        // changes and recomputed when necessary.
-        final AppContext ctx = AppContext.getAppContext();
-
-        // fetch the defaults from the app context. If null, then create and
-        // store the compiled defaults
-        Map<String, TreeMap<String, Object>> compiledDefaults =
-                (Map<String, TreeMap<String, Object>>)
-                    ctx.get("NimbusStyle.defaults");
-
-        if (compiledDefaults == null) {
-            // the entire UIDefaults tables are parsed and compiled into
-            // this map of maps. The key of the compiledDefaults is the
-            // prefix for each style, while the value is a map of
-            // keys->values for that prefix.
-            compiledDefaults = new HashMap<String, TreeMap<String, Object>>();
-
-            // get all the defaults from UIManager.getDefaults() and put them
-            // into the compiledDefaults
-            compileDefaults(compiledDefaults, UIManager.getDefaults());
-
-            // This second statement pulls defaults from the laf defaults
-            UIDefaults lafDefaults = UIManager.getLookAndFeelDefaults();
-            compileDefaults(compiledDefaults, lafDefaults);
-
-            // if it has not already been done, add a listener to both
-            // UIManager.getDefaults() and UIManager.getLookAndFeelDefaults().
-            PropertyChangeListener pcl = (PropertyChangeListener)
-                    ctx.get("NimbusStyle.defaults.pcl");
-
-            // if pcl is null, then it has not yet been registered with
-            // the UIManager defaults for this app context
-            if (pcl == null) {
-                // create a PCL which will simply clear out the compiled
-                // defaults from the app context, causing it to be recomputed
-                // on subsequent passes
-                pcl = new DefaultsListener();
-                // add the PCL to both defaults tables that we pay attention
-                // to, so that if the UIDefaults are updated, then the
-                // precompiled defaults will be cleared from the app context
-                // and recomputed on subsequent passes
-                UIManager.getDefaults().addPropertyChangeListener(pcl);
-                UIManager.getLookAndFeelDefaults().addPropertyChangeListener(pcl);
-                // save the PCL to the app context as a marker indicating
-                // that the PCL has been registered so we don't end up adding
-                // more than one listener to the UIDefaults tables.
-                ctx.put("NimbusStyle.defaults.pcl", pcl);
-            }
-
-            // store the defaults for reuse
-            ctx.put("NimbusStyle.defaults", compiledDefaults);
-        }
-
-        TreeMap<String, Object> defaults = compiledDefaults.get(prefix);
+        Map<String, Object> defaults =
+                ((NimbusLookAndFeel) UIManager.getLookAndFeel()).
+                        getDefaultsForPrefix(prefix);
 
         // inspect the client properties for the key "Nimbus.Overrides". If the
         // value is an instance of UIDefaults, then these defaults are used
@@ -371,52 +274,6 @@
             }
         }
 
-        // Now that I've accumulated all the defaults pertaining to this
-        // style, call init which will read these defaults and configure
-        // the default "values".
-        init(values, defaults);
-    }
-
-    /**
-     * Iterates over all the keys in the specified UIDefaults and compiles
-     * those keys into the comiledDefaults data structure. It relies on
-     * parsing the "prefix" out of the key. If the key is not a String or is
-     * null then it is ignored. In all other cases a prefix is parsed out
-     * (even if that prefix is the empty String or is a "fake" prefix. That
-     * is, suppose you had a key Foo~~MySpecial.KeyThing~~. In this case this
-     * is not a Nimbus formatted key, but we don't care, we treat it as if it
-     * is. This doesn't pose any harm, it will simply never be used).
-     *
-     * @param compiledDefaults
-     * @param d
-     */
-    private void compileDefaults(
-            Map<String, TreeMap<String,Object>> compiledDefaults,
-            UIDefaults d) {
-        for (Object obj : new HashSet(d.keySet())) {
-            if (obj instanceof String) {
-                String key = (String)obj;
-                String kp = parsePrefix(key);
-                if (kp == null) continue;
-                TreeMap<String,Object> map = compiledDefaults.get(kp);
-                if (map == null) {
-                    map = new TreeMap<String,Object>();
-                    compiledDefaults.put(kp, map);
-                }
-                map.put(key, d.get(key));
-            }
-        }
-    }
-
-    /**
-     * Initializes the given <code>Values</code> object with the defaults
-     * contained in the given TreeMap.
-     *
-     * @param v The Values object to be initialized
-     * @param myDefaults a map of UIDefaults to use in initializing the Values.
-     *        This map must contain only keys associated with this Style.
-     */
-    private void init(Values v, TreeMap<String, Object> myDefaults) {
         //a list of the different types of states used by this style. This
         //list may contain only "standard" states (those defined by Synth),
         //or it may contain custom states, or it may contain only "standard"
@@ -433,7 +290,7 @@
         //"values" stateTypes to be a non-null array.
         //Otherwise, let the "values" stateTypes be null to indicate that
         //there are no custom states or custom state ordering
-        String statesString = (String)myDefaults.get(prefix + ".States");
+        String statesString = (String)defaults.get(prefix + ".States");
         if (statesString != null) {
             String s[] = statesString.split(",");
             for (int i=0; i<s.length; i++) {
@@ -442,7 +299,7 @@
                     //this is a non-standard state name, so look for the
                     //custom state associated with it
                     String stateName = prefix + "." + s[i];
-                    State customState = (State)myDefaults.get(stateName);
+                    State customState = (State)defaults.get(stateName);
                     if (customState != null) {
                         states.add(customState);
                     }
@@ -455,7 +312,7 @@
             //to be non-null. Otherwise, leave it null (meaning, use the
             //standard synth states).
             if (states.size() > 0) {
-                v.stateTypes = states.toArray(new State[states.size()]);
+                values.stateTypes = states.toArray(new State[states.size()]);
             }
 
             //assign codes for each of the state types
@@ -490,7 +347,7 @@
         }
 
         //Now iterate over all the keys in the defaults table
-        for (String key : myDefaults.keySet()) {
+        for (String key : defaults.keySet()) {
             //The key is something like JButton.Enabled.backgroundPainter,
             //or JButton.States, or JButton.background.
             //Remove the "JButton." portion of the key
@@ -528,11 +385,11 @@
                 //otherwise, assume it is a property and install it on the
                 //values object
                 if ("contentMargins".equals(property)) {
-                    v.contentMargins = (Insets)myDefaults.get(key);
+                    values.contentMargins = (Insets)defaults.get(key);
                 } else if ("States".equals(property)) {
                     //ignore
                 } else {
-                    v.defaults.put(property, myDefaults.get(key));
+                    values.defaults.put(property, defaults.get(key));
                 }
             } else {
                 //it is possible that the developer has a malformed UIDefaults
@@ -582,13 +439,13 @@
                 //so put it in the UIDefaults associated with that runtime
                 //state
                 if ("backgroundPainter".equals(property)) {
-                    rs.backgroundPainter = (Painter)myDefaults.get(key);
+                    rs.backgroundPainter = getPainter(defaults, key);
                 } else if ("foregroundPainter".equals(property)) {
-                    rs.foregroundPainter = (Painter) myDefaults.get(key);
+                    rs.foregroundPainter = getPainter(defaults, key);
                 } else if ("borderPainter".equals(property)) {
-                    rs.borderPainter = (Painter) myDefaults.get(key);
+                    rs.borderPainter = getPainter(defaults, key);
                 } else {
-                    rs.defaults.put(property, myDefaults.get(key));
+                    rs.defaults.put(property, defaults.get(key));
                 }
             }
         }
@@ -598,7 +455,15 @@
         Collections.sort(runtimeStates, STATE_COMPARATOR);
 
         //finally, set the array of runtime states on the values object
-        v.states = runtimeStates.toArray(new RuntimeState[runtimeStates.size()]);
+        values.states = runtimeStates.toArray(new RuntimeState[runtimeStates.size()]);
+    }
+
+    private Painter getPainter(Map<String, Object> defaults, String key) {
+        Object p = defaults.get(key);
+        if (p instanceof UIDefaults.LazyValue) {
+            p = ((UIDefaults.LazyValue)p).createValue(UIManager.getDefaults());
+        }
+        return (p instanceof Painter ? (Painter)p : null);
     }
 
     /**
@@ -1245,15 +1110,4 @@
             return hash;
         }
     }
-
-    /**
-     * This listener is used to listen to the UIDefaults tables and clear out
-     * the cached-precompiled map of defaults in that case.
-     */
-    private static final class DefaultsListener implements PropertyChangeListener {
-        @Override
-        public void propertyChange(PropertyChangeEvent evt) {
-            AppContext.getAppContext().put("NimbusStyle.defaults", null);
-        }
-    }
 }