Index: trunk/src/org/openstreetmap/josm/data/Preferences.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/Preferences.java	(revision 3847)
+++ trunk/src/org/openstreetmap/josm/data/Preferences.java	(revision 3848)
@@ -31,4 +31,5 @@
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.tools.ColorHelper;
+import org.openstreetmap.josm.tools.Utils;
 
 /**
@@ -668,9 +669,9 @@
 
     synchronized public boolean putCollection(String key, Collection<String> val) {
-        return put(key, join("\u001e", val));
+        return put(key, Utils.join("\u001e", val));
     }
 
     synchronized private void putCollectionDefault(String key, Collection<String> val) {
-        putDefault(key, join("\u001e", val));
+        putDefault(key, Utils.join("\u001e", val));
     }
 
@@ -767,31 +768,3 @@
         putCollection("pluginmanager.sites", sites);
     }
-
-    /**
-     * Joins a collection of strings into a single string with fields
-     * separated by the value of sep.
-     * @param sep the separator
-     * @param values collection of strings, null strings are converted to the
-     *  empty string
-     * @return null if values is null. The joined string otherwise.
-     */
-    public static String join(String sep, Collection<?> values) {
-        if (values == null)
-            return null;
-        if (values.isEmpty())
-            return "";
-        StringBuilder s = null;
-        for (Object a : values) {
-            if (a == null) {
-                a = "";
-            }
-            if(s != null) {
-                s.append(sep).append(a.toString());
-            } else {
-                s = new StringBuilder(a.toString());
-            }
-        }
-        return s.toString();
-    }
-
 }
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/InspectPrimitiveDialog.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/InspectPrimitiveDialog.java	(revision 3847)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/InspectPrimitiveDialog.java	(revision 3848)
@@ -10,7 +10,11 @@
 import java.util.List;
 
+import javax.swing.event.ChangeEvent;
 import javax.swing.JPanel;
 import javax.swing.JScrollPane;
+import javax.swing.JTabbedPane;
 import javax.swing.JTextArea;
+import javax.swing.SingleSelectionModel;
+import javax.swing.event.ChangeListener;
 
 import org.openstreetmap.josm.Main;
@@ -24,4 +28,14 @@
 import org.openstreetmap.josm.data.osm.User;
 import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.gui.DefaultNameFormatter;
+import org.openstreetmap.josm.gui.NavigatableComponent;
+import org.openstreetmap.josm.gui.mappaint.ElemStyle;
+import org.openstreetmap.josm.gui.mappaint.ElemStyles;
+import org.openstreetmap.josm.gui.mappaint.MapPaintStyles;
+import org.openstreetmap.josm.gui.mappaint.MultiCascade;
+import org.openstreetmap.josm.gui.mappaint.StyleCache.StyleList;
+import org.openstreetmap.josm.gui.mappaint.StyleSource;
+import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSStyleSource;
+import org.openstreetmap.josm.gui.mappaint.xml.XmlStyleSource;
 import org.openstreetmap.josm.tools.DateUtils;
 import org.openstreetmap.josm.tools.GBC;
@@ -38,24 +52,41 @@
 public class InspectPrimitiveDialog extends ExtendedDialog {
     protected Collection<OsmPrimitive> primitives;
-    protected JTextArea textArea;
+    private JTextArea txtData;
+    private JTextArea txtMappaint;
+    boolean mappaintTabLoaded;
 
     public InspectPrimitiveDialog(Collection<OsmPrimitive> primitives) {
         super(Main.parent, tr("Advanced object info"), new String[] {"Close"});
         this.primitives = primitives;
-        setPreferredSize(new Dimension(450, 350));
+        setPreferredSize(new Dimension(750, 550));
 
         setButtonIcons(new String[] {"ok.png"});
-        JPanel p = buildPanel();
-        textArea.setText(buildText());
-        setContent(p, false);
-    }
-
-    protected JPanel buildPanel() {
+        final JTabbedPane tabs = new JTabbedPane();
+        JPanel pData = buildDataPanel();
+        tabs.addTab(tr("data"), pData);
+        final JPanel pMapPaint = new JPanel();
+        tabs.addTab(tr("map style"), pMapPaint);
+        tabs.getModel().addChangeListener(new ChangeListener() {
+
+            @Override
+            public void stateChanged(ChangeEvent e) {
+                if (!mappaintTabLoaded && ((SingleSelectionModel) e.getSource()).getSelectedIndex() == 1) {
+                    mappaintTabLoaded = true;
+                    buildMapPaintPanel(pMapPaint);
+                    createMapPaintText();
+                }
+            }
+        });
+        txtData.setText(buildDataText());
+        setContent(tabs, false);
+    }
+
+    protected JPanel buildDataPanel() {
         JPanel p = new JPanel(new GridBagLayout());
-        textArea = new JTextArea();
-        textArea.setFont(new Font("Monospaced", textArea.getFont().getStyle(), textArea.getFont().getSize()));
-        textArea.setEditable(false);
-
-        JScrollPane scroll = new JScrollPane(textArea);
+        txtData = new JTextArea();
+        txtData.setFont(new Font("Monospaced", txtData.getFont().getStyle(), txtData.getFont().getSize()));
+        txtData.setEditable(false);
+
+        JScrollPane scroll = new JScrollPane(txtData);
 
         p.add(scroll, GBC.std().fill());
@@ -63,5 +94,5 @@
     }
 
-    protected String buildText() {
+    protected String buildDataText() {
         StringBuilder s = new StringBuilder();
         for (Node n : new SubclassFilteredCollection<OsmPrimitive, Node>(primitives, OsmPrimitive.nodePredicate)) {
@@ -229,3 +260,53 @@
         return us.toString();
     }
+
+    protected void buildMapPaintPanel(JPanel p) {
+        p.setLayout(new GridBagLayout());
+        txtMappaint = new JTextArea();
+        txtMappaint.setFont(new Font("Monospaced", txtMappaint.getFont().getStyle(), txtMappaint.getFont().getSize()));
+        txtMappaint.setEditable(false);
+
+        p.add(new JScrollPane(txtMappaint), GBC.std().fill());
+    }
+
+    protected void createMapPaintText() {
+        final Collection<OsmPrimitive> sel = Main.main.getCurrentDataSet().getSelected();
+        ElemStyles elemstyles = MapPaintStyles.getStyles();
+        NavigatableComponent nc = Main.map.mapView;
+        double scale = nc.getDist100Pixel();
+
+        for (OsmPrimitive osm : sel) {
+            txtMappaint.append("Styles Cache for \""+osm.getDisplayName(DefaultNameFormatter.getInstance())+"\":");
+
+            MultiCascade mc = new MultiCascade();
+
+            for (StyleSource s : elemstyles.getStyleSources()) {
+                if (s.active) {
+                    txtMappaint.append("\n\n> applying "+getSort(s)+" style \""+s.getDisplayString()+"\n");
+                    s.apply(mc, osm, scale, null, false);
+                    txtMappaint.append("\nRange:"+mc.range);
+                    for (String key : mc.keySet()) {
+                        txtMappaint.append("\n "+key+": \n"+mc.get(key));
+                    }
+                } else {
+                    txtMappaint.append("\n\n> skipping \""+s.getDisplayString()+"\" (not active)");
+                }
+            }
+            txtMappaint.append("\n\nList of generated Styles:\n");
+            StyleList sl = elemstyles.get(osm, scale, nc);
+            for (ElemStyle s : sl) {
+                txtMappaint.append(" * "+s+"\n");
+            }
+            txtMappaint.append("\n\n");
+        }
+    }
+
+    private String getSort(StyleSource s) {
+        if (s instanceof XmlStyleSource)
+            return "xml";
+        if (s instanceof MapCSSStyleSource)
+            return "mapcss";
+        return "unkown";
+    }
+
 }
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/MapPaintDialog.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/MapPaintDialog.java	(revision 3847)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/MapPaintDialog.java	(revision 3848)
@@ -7,5 +7,4 @@
 import java.awt.Dimension;
 import java.awt.Point;
-import java.awt.Rectangle;
 import java.awt.event.ActionEvent;
 import java.awt.event.KeyEvent;
@@ -22,4 +21,5 @@
 import javax.swing.JTable;
 import javax.swing.ListSelectionModel;
+import javax.swing.SwingUtilities;
 import javax.swing.UIManager;
 import javax.swing.event.ListSelectionEvent;
@@ -29,6 +29,6 @@
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.gui.SideButton;
-import org.openstreetmap.josm.gui.mappaint.ElemStyles;
 import org.openstreetmap.josm.gui.mappaint.MapPaintStyles;
+import org.openstreetmap.josm.gui.mappaint.MapPaintStyles.MapPaintStyleLoader;
 import org.openstreetmap.josm.gui.mappaint.StyleSource;
 import org.openstreetmap.josm.gui.widgets.PopupMenuLauncher;
@@ -151,5 +151,5 @@
                 model.fireTableDataChanged();
             }
-            ElemStyles.cacheIdx++;
+            MapPaintStyles.getStyles().clearCached();
             Main.map.mapView.preferenceChanged(null);
             Main.map.mapView.repaint();
@@ -207,15 +207,32 @@
         @Override
         public void actionPerformed(ActionEvent e) {
-            int[] pos = tblStyles.getSelectedRows();
-            for (int p : pos) {
-                StyleSource s = model.data.get(p);
-                s.loadStyleSource();
-            }
-            ElemStyles.cacheIdx++;
-            Main.map.mapView.preferenceChanged(null);
-            Main.map.mapView.repaint();
-        }
-    }
-
+
+            final int[] rows = tblStyles.getSelectedRows();
+            List<StyleSource> sources = new ArrayList<StyleSource>();
+            for (int p : rows) {
+                sources.add(model.data.get(p));
+            }
+            Main.worker.submit(new MapPaintStyleLoader(sources));
+            Main.worker.submit(new Runnable() {
+                @Override
+                public void run() {
+                    SwingUtilities.invokeLater(new Runnable() {
+                        @Override
+                        public void run() {
+                            if (rows.length == 1) {
+                                model.fireTableCellUpdated(rows[0], 1);
+                            } else {
+                                model.fireTableDataChanged();
+                            }
+                            MapPaintStyles.getStyles().clearCached();
+                            Main.map.mapView.preferenceChanged(null);
+                            Main.map.mapView.repaint();
+                        }
+                    });
+                }
+            });
+        }
+    }
+    
     class PopupMenuHandler extends PopupMenuLauncher {
         @Override
Index: trunk/src/org/openstreetmap/josm/gui/mappaint/AreaElemStyle.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/mappaint/AreaElemStyle.java	(revision 3847)
+++ trunk/src/org/openstreetmap/josm/gui/mappaint/AreaElemStyle.java	(revision 3848)
@@ -21,5 +21,5 @@
 
     public static AreaElemStyle create(Cascade c) {
-        Color color = c.get("fill-color", null, Color.class);
+        Color color = c.getColor("fill-color", null);
         if (color == null)
             return null;
Index: trunk/src/org/openstreetmap/josm/gui/mappaint/Cascade.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/mappaint/Cascade.java	(revision 3847)
+++ trunk/src/org/openstreetmap/josm/gui/mappaint/Cascade.java	(revision 3848)
@@ -2,11 +2,15 @@
 package org.openstreetmap.josm.gui.mappaint;
 
+import java.awt.Color;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Map;
+
+import org.openstreetmap.josm.gui.mappaint.mapcss.CSSColors;
 
 /**
  * Simple map of properties with dynamic typing.
  */
-public class Cascade {
+public class Cascade implements Cloneable {
     
     public static final Cascade EMPTY_CASCADE = new Cascade();
@@ -32,5 +36,36 @@
             return res;
         }
-        System.err.println(String.format("Warning: wrong type for mappaint property %s: %s expected, but %s found!", key, klass, o.getClass()));
+        System.err.println(String.format("Warning: wrong type for mappaint property %s: %s expected, but %s of type %s found!", key, klass, o, o.getClass()));
+        return def;
+    }
+
+    public Object get(String key) {
+        return prop.get(key);
+    }
+
+    public Float getFloat(String key, Float def) {
+        Object o = prop.get(key);
+        if (o == null)
+            return def;
+        if (o instanceof Float)
+            return (Float) o;
+        if (o instanceof Integer)
+            return new Float((Integer) o);
+        return def;
+    }
+
+    public Color getColor(String key, Color def) {
+        Object o = prop.get(key);
+        if (o == null)
+            return def;
+        if (o instanceof Color)
+            return (Color) o;
+        if (o instanceof String) {
+            Color clr = CSSColors.get((String) o);
+            if (clr != null)
+                return clr;
+            else
+                return def;
+        }
         return def;
     }
@@ -51,3 +86,28 @@
         prop.remove(key);
     }
+
+    @Override
+    public Cascade clone() {
+        @SuppressWarnings("unchecked") 
+        HashMap<String, Object> clonedProp = (HashMap) ((HashMap) this.prop).clone();
+        Cascade c = new Cascade();
+        c.prop = clonedProp;
+        return c;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder res = new StringBuilder("Cascade{ ");
+        for (String key : prop.keySet()) {
+            res.append(key+":");
+            Object val = prop.get(key);
+            if (val instanceof float[]) {
+                res.append(Arrays.toString((float[]) val));
+            } else {
+                res.append(val+"");
+            }
+            res.append("; ");
+        }
+        return res.append("}").toString();
+    }
 }
Index: trunk/src/org/openstreetmap/josm/gui/mappaint/ElemStyle.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/mappaint/ElemStyle.java	(revision 3847)
+++ trunk/src/org/openstreetmap/josm/gui/mappaint/ElemStyle.java	(revision 3848)
@@ -17,6 +17,6 @@
 
     protected ElemStyle(Cascade c) {
-        z_index = c.get("z-index", 0f, Float.class);
-        object_z_index = c.get("object-z-index", 0f, Float.class);
+        z_index = c.getFloat("z-index", 0f);
+        object_z_index = c.getFloat("object-z-index", 0f);
     }
 
Index: trunk/src/org/openstreetmap/josm/gui/mappaint/ElemStyles.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/mappaint/ElemStyles.java	(revision 3847)
+++ trunk/src/org/openstreetmap/josm/gui/mappaint/ElemStyles.java	(revision 3848)
@@ -28,9 +28,13 @@
     private boolean drawMultipolygon;
 
-    public static int cacheIdx;
+    private int cacheIdx;
 
     public ElemStyles()
     {
         styleSources = new ArrayList<StyleSource>();
+    }
+
+    public void clearCached() {
+        cacheIdx++;
     }
 
Index: trunk/src/org/openstreetmap/josm/gui/mappaint/LineElemStyle.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/mappaint/LineElemStyle.java	(revision 3847)
+++ trunk/src/org/openstreetmap/josm/gui/mappaint/LineElemStyle.java	(revision 3848)
@@ -40,22 +40,26 @@
 
     public static LineElemStyle createCasing(Cascade c) {
-        return createImpl(c, "casing-");
+        LineElemStyle casing =  createImpl(c, "casing-");
+        if (casing != null) {
+            casing.object_z_index = -1;
+        }
+        return casing;
     }
 
     private static LineElemStyle createImpl(Cascade c, String prefix) {
-        Float width = c.get(prefix + "width", null, Float.class);
+        Float width = c.getFloat(prefix + "width", null);
         if (width == null)
             return null;
 
-        float realWidth = c.get(prefix + "real-width", 0f, Float.class);
-        Color color = c.get(prefix + "color", null, Color.class);
+        float realWidth = c.getFloat(prefix + "real-width", 0f);
+        Color color = c.getColor(prefix + "color", null);
         if (color == null) {
-            color = c.get(prefix + "fill-color", null, Color.class);
+            color = c.getColor(prefix + "fill-color", null);
         }
         if (color == null) {
-            color = PaintColors.UNTAGGED_WAY.get();
+            color = PaintColors.UNTAGGED.get();
         }
         float[] dashes = c.get(prefix + "dashes", null, float[].class);
-        Color dashesBackground = c.get(prefix + "dashes-background-color", null, Color.class);
+        Color dashesBackground = c.getColor(prefix + "dashes-background-color", null);
 
         return new LineElemStyle(c, width, realWidth, color, dashes, dashesBackground);
Index: trunk/src/org/openstreetmap/josm/gui/mappaint/MapPaintStyles.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/mappaint/MapPaintStyles.java	(revision 3847)
+++ trunk/src/org/openstreetmap/josm/gui/mappaint/MapPaintStyles.java	(revision 3848)
@@ -14,7 +14,10 @@
 
 import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.gui.PleaseWaitRunnable;
+import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSStyleSource;
 import org.openstreetmap.josm.gui.mappaint.xml.XmlStyleSource;
 import org.openstreetmap.josm.gui.preferences.SourceEntry;
 import org.openstreetmap.josm.gui.preferences.MapPaintPreference.MapPaintPrefMigration;
+import org.openstreetmap.josm.gui.progress.ProgressMonitor;
 import org.openstreetmap.josm.io.MirroredInputStream;
 import org.openstreetmap.josm.tools.ImageProvider;
@@ -83,17 +86,32 @@
 
         for (SourceEntry entry : sourceEntries) {
-            StyleSource style = new XmlStyleSource(entry);
+            StyleSource style = null;
             try {
                 MirroredInputStream in = new MirroredInputStream(entry.url);
                 InputStream zip = in.getZipEntry("xml","style");
                 if (zip != null) {
-                    style.zipIcons = in.getFile();
-                } 
+                    style = new XmlStyleSource(entry);
+                    continue;
+                }
+                zip = in.getZipEntry("mapcss","style");
+                if (zip != null) {
+                    style = new MapCSSStyleSource(entry);
+                    continue;
+                }
+                if (entry.url.toLowerCase().endsWith(".mapcss")) {
+                    style = new MapCSSStyleSource(entry);
+                } else {
+                    style = new XmlStyleSource(entry);
+                }
             } catch(IOException e) {
                 System.err.println(tr("Warning: failed to load Mappaint styles from ''{0}''. Exception was: {1}", entry.url, e.toString()));
                 e.printStackTrace();
-                style.hasError = true;
+                if (style != null) {
+                    style.hasError = true;
+                }
             }
-            styles.add(style);
+            if (style != null) {
+                styles.add(style);
+            }
         }
         for (StyleSource s : styles.getStyleSources()) {
@@ -101,3 +119,36 @@
         }
     }
+
+    public static class MapPaintStyleLoader extends PleaseWaitRunnable {
+        private boolean canceled;
+        private List<StyleSource> sources;
+
+        public MapPaintStyleLoader(List<StyleSource> sources) {
+            super(tr("Reloading style sources"));
+            this.sources = sources;
+        }
+
+        @Override
+        protected void cancel() {
+            canceled = true;
+        }
+
+        @Override
+        protected void finish() {
+        }
+
+        @Override
+        protected void realRun() {
+            ProgressMonitor monitor = getProgressMonitor();
+            monitor.setTicksCount(sources.size());
+            for (StyleSource s : sources) {
+                if (canceled)
+                    return;
+                monitor.subTask(tr("loading style ''{0}''...", s.getDisplayString()));
+                s.loadStyleSource();
+                monitor.worked(1);
+            }
+        }
+    }
+
 }
Index: trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/CSSColors.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/CSSColors.java	(revision 3848)
+++ trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/CSSColors.java	(revision 3848)
@@ -0,0 +1,168 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.mappaint.mapcss;
+
+import java.awt.Color;
+import java.util.HashMap;
+import java.util.Map;
+
+public class CSSColors {
+    private final static Map<String, Color> CSS_COLORS = new HashMap<String, Color>();
+    static {
+        Object[][] CSSCOLORS_INIT = new Object[][] {
+            {"aliceblue", 0xf0f8ff},
+            {"antiquewhite", 0xfaebd7},
+            {"aqua", 0x00ffff},
+            {"aquamarine", 0x7fffd4},
+            {"azure", 0xf0ffff},
+            {"beige", 0xf5f5dc},
+            {"bisque", 0xffe4c4},
+            {"black", 0x000000},
+            {"blanchedalmond", 0xffebcd},
+            {"blue", 0x0000ff},
+            {"blueviolet", 0x8a2be2},
+            {"brown", 0xa52a2a},
+            {"burlywood", 0xdeb887},
+            {"cadetblue", 0x5f9ea0},
+            {"chartreuse", 0x7fff00},
+            {"chocolate", 0xd2691e},
+            {"coral", 0xff7f50},
+            {"cornflowerblue", 0x6495ed},
+            {"cornsilk", 0xfff8dc},
+            {"crimson", 0xdc143c},
+            {"cyan", 0x00ffff},
+            {"darkblue", 0x00008b},
+            {"darkcyan", 0x008b8b},
+            {"darkgoldenrod", 0xb8860b},
+            {"darkgray", 0xa9a9a9},
+            {"darkgreen", 0x006400},
+            {"darkgrey", 0xa9a9a9},
+            {"darkkhaki", 0xbdb76b},
+            {"darkmagenta", 0x8b008b},
+            {"darkolivegreen", 0x556b2f},
+            {"darkorange", 0xff8c00},
+            {"darkorchid", 0x9932cc},
+            {"darkred", 0x8b0000},
+            {"darksalmon", 0xe9967a},
+            {"darkseagreen", 0x8fbc8f},
+            {"darkslateblue", 0x483d8b},
+            {"darkslategray", 0x2f4f4f},
+            {"darkslategrey", 0x2f4f4f},
+            {"darkturquoise", 0x00ced1},
+            {"darkviolet", 0x9400d3},
+            {"deeppink", 0xff1493},
+            {"deepskyblue", 0x00bfff},
+            {"dimgray", 0x696969},
+            {"dimgrey", 0x696969},
+            {"dodgerblue", 0x1e90ff},
+            {"firebrick", 0xb22222},
+            {"floralwhite", 0xfffaf0},
+            {"forestgreen", 0x228b22},
+            {"fuchsia", 0xff00ff},
+            {"gainsboro", 0xdcdcdc},
+            {"ghostwhite", 0xf8f8ff},
+            {"gold", 0xffd700},
+            {"goldenrod", 0xdaa520},
+            {"gray", 0x808080},
+            {"green", 0x008000},
+            {"greenyellow", 0xadff2f},
+            {"grey", 0x808080},
+            {"honeydew", 0xf0fff0},
+            {"hotpink", 0xff69b4},
+            {"indianred", 0xcd5c5c},
+            {"indigo", 0x4b0082},
+            {"ivory", 0xfffff0},
+            {"khaki", 0xf0e68c},
+            {"lavender", 0xe6e6fa},
+            {"lavenderblush", 0xfff0f5},
+            {"lawngreen", 0x7cfc00},
+            {"lemonchiffon", 0xfffacd},
+            {"lightblue", 0xadd8e6},
+            {"lightcoral", 0xf08080},
+            {"lightcyan", 0xe0ffff},
+            {"lightgoldenrodyellow", 0xfafad2},
+            {"lightgray", 0xd3d3d3},
+            {"lightgreen", 0x90ee90},
+            {"lightgrey", 0xd3d3d3},
+            {"lightpink", 0xffb6c1},
+            {"lightsalmon", 0xffa07a},
+            {"lightseagreen", 0x20b2aa},
+            {"lightskyblue", 0x87cefa},
+            {"lightslategray", 0x778899},
+            {"lightslategrey", 0x778899},
+            {"lightsteelblue", 0xb0c4de},
+            {"lightyellow", 0xffffe0},
+            {"lime", 0x00ff00},
+            {"limegreen", 0x32cd32},
+            {"linen", 0xfaf0e6},
+            {"magenta", 0xff00ff},
+            {"maroon", 0x800000},
+            {"mediumaquamarine", 0x66cdaa},
+            {"mediumblue", 0x0000cd},
+            {"mediumorchid", 0xba55d3},
+            {"mediumpurple", 0x9370db},
+            {"mediumseagreen", 0x3cb371},
+            {"mediumslateblue", 0x7b68ee},
+            {"mediumspringgreen", 0x00fa9a},
+            {"mediumturquoise", 0x48d1cc},
+            {"mediumvioletred", 0xc71585},
+            {"midnightblue", 0x191970},
+            {"mintcream", 0xf5fffa},
+            {"mistyrose", 0xffe4e1},
+            {"moccasin", 0xffe4b5},
+            {"navajowhite", 0xffdead},
+            {"navy", 0x000080},
+            {"oldlace", 0xfdf5e6},
+            {"olive", 0x808000},
+            {"olivedrab", 0x6b8e23},
+            {"orange", 0xffa500},
+            {"orangered", 0xff4500},
+            {"orchid", 0xda70d6},
+            {"palegoldenrod", 0xeee8aa},
+            {"palegreen", 0x98fb98},
+            {"paleturquoise", 0xafeeee},
+            {"palevioletred", 0xdb7093},
+            {"papayawhip", 0xffefd5},
+            {"peachpuff", 0xffdab9},
+            {"peru", 0xcd853f},
+            {"pink", 0xffc0cb},
+            {"plum", 0xdda0dd},
+            {"powderblue", 0xb0e0e6},
+            {"purple", 0x800080},
+            {"red", 0xff0000},
+            {"rosybrown", 0xbc8f8f},
+            {"royalblue", 0x4169e1},
+            {"saddlebrown", 0x8b4513},
+            {"salmon", 0xfa8072},
+            {"sandybrown", 0xf4a460},
+            {"seagreen", 0x2e8b57},
+            {"seashell", 0xfff5ee},
+            {"sienna", 0xa0522d},
+            {"silver", 0xc0c0c0},
+            {"skyblue", 0x87ceeb},
+            {"slateblue", 0x6a5acd},
+            {"slategray", 0x708090},
+            {"slategrey", 0x708090},
+            {"snow", 0xfffafa},
+            {"springgreen", 0x00ff7f},
+            {"steelblue", 0x4682b4},
+            {"tan", 0xd2b48c},
+            {"teal", 0x008080},
+            {"thistle", 0xd8bfd8},
+            {"tomato", 0xff6347},
+            {"turquoise", 0x40e0d0},
+            {"violet", 0xee82ee},
+            {"wheat", 0xf5deb3},
+            {"white", 0xffffff},
+            {"whitesmoke", 0xf5f5f5},
+            {"yellow", 0xffff00},
+            {"yellowgreen", 0x9acd32}
+        };
+        for (Object[] pair : CSSCOLORS_INIT) {
+            CSS_COLORS.put((String) pair[0], new Color((Integer) pair[1]));
+        }
+    }
+
+    public static Color get(String key) {
+        return CSS_COLORS.get(key);
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/Condition.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/Condition.java	(revision 3848)
+++ trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/Condition.java	(revision 3848)
@@ -0,0 +1,95 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.mappaint.mapcss;
+
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.Relation;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.tools.Utils;
+
+abstract public class Condition {
+
+    abstract public boolean applies(OsmPrimitive osm);
+
+    public static enum Op {EQ, NEQ}
+
+    public static class KeyValueCondition extends Condition {
+
+        public String k;
+        public String v;
+        public Op op;
+
+        public KeyValueCondition(String k, String v, Op op) {
+
+            this.k = k;
+            this.v = v;
+            this.op = op;
+        }
+
+        @Override
+        public boolean applies(OsmPrimitive osm) {
+            switch (op) {
+                case EQ:
+                    return Utils.equal(osm.get(k), v);
+                case NEQ:
+                    return !Utils.equal(osm.get(k), v);
+                default:
+                    throw new AssertionError();
+            }
+        }
+
+        @Override
+        public String toString() {
+            return "[" + k + (op == Op.EQ ? "=" : "!=") + v + "]";
+        }
+    }
+
+    public static class KeyCondition extends Condition {
+
+        private String k;
+        private boolean not;
+
+        public KeyCondition(String k, boolean not) {
+            this.k = k;
+            this.not = not;
+        }
+
+        @Override
+        public boolean applies(OsmPrimitive osm) {
+            return osm.hasKey(k) ^ not;
+        }
+
+        @Override
+        public String toString() {
+            return "[" + (not ? "!" : "") + k + "]";
+        }
+    }
+
+    public static class PseudoClassCondition extends Condition {
+        
+        String id;
+        boolean not;
+
+        public PseudoClassCondition(String id, boolean not) {
+            this.id = id;
+            this.not = not;
+        }
+
+        @Override
+        public boolean applies(OsmPrimitive osm) {
+            if ("closed".equals(id)) {
+                if (osm instanceof Way && ((Way) osm).isClosed())
+                    return true;
+                if (osm instanceof Relation && "multipolygon".equals(osm.get("type")))
+                    return true;
+                return false;
+            }
+            return true;
+        }
+
+        @Override
+        public String toString() {
+            return ":" + (not ? "!" : "") + id;
+        }
+    }
+
+}
Index: trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/Environment.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/Environment.java	(revision 3848)
+++ trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/Environment.java	(revision 3848)
@@ -0,0 +1,23 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.mappaint.mapcss;
+
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.gui.mappaint.Cascade;
+import org.openstreetmap.josm.gui.mappaint.MultiCascade;
+
+public class Environment {
+    
+    OsmPrimitive osm;
+    MultiCascade mc;
+    String layer;
+
+    public Environment(OsmPrimitive osm, MultiCascade mc, String layer) {
+        this.osm = osm;
+        this.mc = mc;
+        this.layer = layer;
+    }
+
+    public Cascade getCascade() {
+        return mc.getCascade(layer);
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/Expression.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/Expression.java	(revision 3848)
+++ trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/Expression.java	(revision 3848)
@@ -0,0 +1,111 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.mappaint.mapcss;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.openstreetmap.josm.gui.mappaint.Cascade;
+import org.openstreetmap.josm.tools.Utils;
+
+public interface Expression {
+
+    public Object evaluate(Environment env);
+
+    public static class LiteralExpression implements Expression {
+        Object literal;
+
+        public LiteralExpression(Object lit) {
+            this.literal = lit;
+        }
+
+        @Override
+        public Object evaluate(Environment env) {
+            return literal;
+        }
+
+        @Override
+        public String toString() {
+            if (literal == null)
+                return "Lit{<null>}";
+            if (literal instanceof float[])
+                return Arrays.toString((float[]) literal);
+            return "<"+literal.toString()+">";
+        }
+    }
+
+    public static class FunctionExpression implements Expression {
+        String name;
+        List<Expression> args;
+
+        public FunctionExpression(String name, List<Expression> args) {
+            this.name = name;
+            this.args = args;
+        }
+
+        @Override
+        public Object evaluate(Environment env) {
+            if (name.equals("eval")) {
+                if (args.size() != 1) {
+                    return null;
+                }
+                return args.get(0).evaluate(env);
+            }
+            
+            if (name.equals("prop")) {
+                if (!(args.size() == 1 || args.size() == 2))
+                    return null;
+                Object pr = args.get(0).evaluate(env);
+                if (!(pr instanceof String))
+                    return null;
+                Cascade c;
+                if (args.size() == 1) {
+                    c = env.getCascade();
+                } else {
+                    Object layer = args.get(1).evaluate(env);
+                    if (!(layer instanceof String))
+                        return null;
+                    c = env.mc.getCascade((String) layer);
+                }
+                return c.get((String) pr);
+            }
+            if (name.equals("+") || name.equals("*")) {
+                float result = name.equals("+") ? 0f : 1f;
+                for (Expression exp : args) {
+                    Float f = getFloat(exp.evaluate(env));
+                    if (f == null)
+                        return null;
+                    if (name.equals("+")) {
+                        result += f;
+                    } else {
+                        result *= f;
+                    }
+                }
+                return result;
+            }
+            if (name.equals("-")) {
+                if (args.size() != 2) {
+                    return null;
+                }
+                Float fst = getFloat(args.get(0).evaluate(env));
+                Float snd = getFloat(args.get(1).evaluate(env));
+                if (fst == null || snd == null)
+                    return null;
+                return fst - snd;
+            }
+            return null;
+        }
+
+        @Override
+        public String toString() {
+            return name + "(" + Utils.join(", ", args) + ")";
+        }
+
+        static Float getFloat(Object o) {
+            if (o instanceof Float)
+                return (Float) o;
+            if (o instanceof Integer)
+                return new Float((Integer) o);
+            return null;
+        }
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/Instruction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/Instruction.java	(revision 3848)
+++ trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/Instruction.java	(revision 3848)
@@ -0,0 +1,46 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.mappaint.mapcss;
+
+import java.util.Arrays;
+
+abstract public class Instruction {
+
+    public abstract void execute(Environment env);
+
+    public static class RelativeFloat {
+        public float val;
+
+        public RelativeFloat(float val) {
+            this.val = val;
+        }
+
+        @Override
+        public String toString() {
+            return "RelativeFloat{" + "val=" + val + '}';
+        }
+    }
+
+    public static class AssignmentInstruction extends Instruction {
+        String key;
+        Object val;
+
+        public AssignmentInstruction(String key, Object val) {
+            this.key = key;
+            this.val = val;
+        }
+
+        @Override
+        public void execute(Environment env) {
+            if (val instanceof Expression) {
+                env.getCascade().putOrClear(key, ((Expression) val).evaluate(env));
+            } else {
+                env.getCascade().putOrClear(key, val);
+            }
+        }
+
+        @Override
+        public String toString() {
+            return key + ':' + (val instanceof float[] ? Arrays.toString((float[]) val) : val) + ';';
+        }
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSRule.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSRule.java	(revision 3848)
+++ trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSRule.java	(revision 3848)
@@ -0,0 +1,23 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.mappaint.mapcss;
+
+import java.util.List;
+
+import org.openstreetmap.josm.tools.Utils;
+
+public class MapCSSRule {
+    
+    public List<Selector> selectors;
+    public List<Instruction> declaration;
+
+    public MapCSSRule(List<Selector> selectors, List<Instruction> declaration) {
+        this.selectors = selectors;
+        this.declaration = declaration;
+    }
+
+    @Override
+    public String toString() {
+        return Utils.join(",", selectors) + " {\n  " + Utils.join(";\n  ", declaration) + "\n}";
+    }
+}
+
Index: trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSStyleSource.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSStyleSource.java	(revision 3848)
+++ trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSStyleSource.java	(revision 3848)
@@ -0,0 +1,153 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.mappaint.mapcss;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map.Entry;
+
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.gui.mappaint.Cascade;
+import org.openstreetmap.josm.gui.mappaint.MultiCascade;
+import org.openstreetmap.josm.gui.mappaint.Range;
+import org.openstreetmap.josm.gui.mappaint.StyleSource;
+import org.openstreetmap.josm.gui.mappaint.mapcss.parser.MapCSSParser;
+import org.openstreetmap.josm.gui.mappaint.mapcss.parser.ParseException;
+import org.openstreetmap.josm.gui.mappaint.mapcss.parser.TokenMgrError;
+import org.openstreetmap.josm.gui.preferences.SourceEntry;
+import org.openstreetmap.josm.io.MirroredInputStream;
+import org.openstreetmap.josm.tools.LanguageInfo;
+import org.openstreetmap.josm.tools.Utils;
+
+public class MapCSSStyleSource extends StyleSource {
+    
+    final public List<MapCSSRule> rules;
+
+    public MapCSSStyleSource(String url, String name, String shortdescription) {
+        super(url, name, shortdescription);
+        rules = new ArrayList<MapCSSRule>();
+    }
+
+    public MapCSSStyleSource(SourceEntry entry) {
+        super(entry);
+        rules = new ArrayList<MapCSSRule>();
+    }
+
+    @Override
+    public void loadStyleSource() {
+        rules.clear();
+        try {
+            MirroredInputStream in = new MirroredInputStream(url);
+            InputStream zip = in.getZipEntry("mapcss", "style");
+            InputStream input;
+            if (zip != null) {
+                input = zip;
+                zipIcons = in.getFile();
+            } else {
+                input = in;
+                zipIcons = null;
+            }
+            MapCSSParser parser = new MapCSSParser(input, "UTF-8");
+            parser.sheet(this);
+            loadMeta();
+        } catch(IOException e) {
+            System.err.println(tr("Warning: failed to load Mappaint styles from ''{0}''. Exception was: {1}", url, e.toString()));
+            e.printStackTrace();
+            hasError = true;
+        } catch (TokenMgrError e) {
+            System.err.println(tr("Warning: failed to parse Mappaint styles from ''{0}''. Error was: {1}", url, e.getMessage()));
+            e.printStackTrace();
+            hasError = true;
+        } catch (ParseException e) {
+            System.err.println(tr("Warning: failed to parse Mappaint styles from ''{0}''. Error was: {1}", url, e.getMessage()));
+            e.printStackTrace();
+            hasError = true;
+        }
+    }
+
+    /**
+     * load meta info from a selector "meta"
+     */
+    private void loadMeta() {
+        MultiCascade mc = new MultiCascade();
+        Node n = new Node();
+        String code = LanguageInfo.getJOSMLocaleCode();
+        n.put("lang", code);
+        // create a fake environment to read the meta data block
+        Environment env = new Environment(n, mc, "default");
+
+        NEXT_RULE:
+        for (MapCSSRule r : rules) {
+            for (Selector s : r.selectors) {
+                if (s.base.equals("meta")) {
+                    for (Condition cnd : s.conds) {
+                        if (!cnd.applies(n))
+                            continue NEXT_RULE;
+                    }
+                    for (Instruction i : r.declaration) {
+                        i.execute(env);
+                    }
+                }
+            }
+        }
+        Cascade c = mc.getCascade("default");
+        String sd = c.get("title", null, String.class);
+        if (sd != null) {
+            this.shortdescription = sd;
+        }
+    }
+
+    @Override
+    public void apply(MultiCascade mc, OsmPrimitive osm, double scale, OsmPrimitive multipolyOuterWay, boolean pretendWayIsClosed) {
+        for (MapCSSRule r : rules) {
+            for (Selector s : r.selectors) {
+                if (s.applies(osm)) {
+                    if (s.range.contains(scale)) {
+                        mc.range = Range.cut(mc.range, s.range);
+                    } else {
+                        mc.range = mc.range.reduceAround(scale, s.range);
+                        continue;
+                    }
+
+                    String sub = s.subpart;
+                    if (sub == null) {
+                        sub = "default";
+                    }
+
+                    Cascade c = mc.get(sub);
+                    if (c == null) {
+                        if (mc.containsKey("*")) {
+                            c = mc.get("*").clone();
+                        } else {
+                            c = new Cascade();
+                        }
+                        mc.put(sub, c);
+                    }
+
+                    if (sub.equals("*")) {
+                        for (Entry<String, Cascade> entry : mc.entrySet()) {
+                            Environment env = new Environment(osm, mc, entry.getKey());
+                            for (Instruction i : r.declaration) {
+                                i.execute(env);
+                            }
+                        }
+                    } else {
+                        Environment env = new Environment(osm, mc, sub);
+                        for (Instruction i : r.declaration) {
+                            i.execute(env);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    @Override
+    public String toString() {
+        return Utils.join("\n", rules);
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/Selector.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/Selector.java	(revision 3848)
+++ trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/Selector.java	(revision 3848)
@@ -0,0 +1,89 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.mappaint.mapcss;
+
+import java.util.List;
+
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
+import org.openstreetmap.josm.data.osm.Relation;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.gui.mappaint.Range;
+import org.openstreetmap.josm.tools.Pair;
+import org.openstreetmap.josm.tools.Utils;
+
+public class Selector {
+    public String base;
+    public Range range;
+    protected List<Condition> conds;
+    public String subpart;
+
+    public Selector(String base, Pair<Integer, Integer> zoom, List<Condition> conds, String subpart) {
+        this.base = base;
+        if (zoom != null) {
+            int a = zoom.a == null ? 0 : zoom.a;
+            int b = zoom.b == null ? Integer.MAX_VALUE : zoom.b;
+            if (a <= b) {
+                range = fromLevel(a, b);
+            }
+        }
+        if (range == null) {
+            range = new Range();
+        }
+        this.conds = conds;
+        this.subpart = subpart;
+    }
+
+    public boolean applies(OsmPrimitive osm) {
+        if (!baseApplies(osm))
+            return false;
+        for (Condition c : conds) {
+            if (!c.applies(osm))
+                return false;
+        }
+        return true;
+    }
+
+    private boolean baseApplies(OsmPrimitive osm) {
+        if (base.equals("*"))
+            return true;
+        if (base.equals("area")) {
+            if (osm instanceof Way && ((Way)osm).isClosed())
+                return true;
+            if (osm instanceof Relation && "multipolygon".equals(osm.get("type")))
+                return true;
+        }
+        if (base.equals(OsmPrimitiveType.from(osm).getAPIName()))
+            return true;
+        return false;
+    }
+
+    public static Range fromLevel(int a, int b) {
+        if (a > b)
+            throw new AssertionError();
+        double lower = 0;
+        double upper = Double.POSITIVE_INFINITY;
+        if (b != Integer.MAX_VALUE) {
+            lower = level2scale(b + 1);
+        }
+        if (a != 0) {
+            upper = level2scale(a);
+        }
+        return new Range(lower, upper);
+    }
+
+    final static double R = 6378135;
+
+    public static double level2scale(int lvl) {
+        if (lvl < 0)
+            throw new IllegalArgumentException();
+        // preliminary formula - map such that mapnik imagery tiles of the same
+        // or similar level are displayed at the given scale
+        return 2.0 * Math.PI * R / Math.pow(2.0, lvl) / 2.56;
+    }
+
+    @Override
+    public String toString() {
+        return base + (range == null ? "" : range) + Utils.join("", conds) + (subpart != null ? ("::" + subpart) : "");
+    }
+
+}
Index: trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/parser/MapCSSParser.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/parser/MapCSSParser.java	(revision 3848)
+++ trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/parser/MapCSSParser.java	(revision 3848)
@@ -0,0 +1,1052 @@
+/* Generated By:JavaCC: Do not edit this line. MapCSSParser.java */
+package org.openstreetmap.josm.gui.mappaint.mapcss.parser;
+
+import java.awt.Color;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.openstreetmap.josm.gui.mappaint.mapcss.Condition;
+import org.openstreetmap.josm.gui.mappaint.mapcss.Expression;
+import org.openstreetmap.josm.gui.mappaint.mapcss.Instruction;
+import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSRule;
+import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSStyleSource;
+import org.openstreetmap.josm.gui.mappaint.mapcss.Selector;
+import org.openstreetmap.josm.gui.mappaint.mapcss.Expression.FunctionExpression;
+import org.openstreetmap.josm.gui.mappaint.mapcss.Expression.LiteralExpression;
+import org.openstreetmap.josm.tools.Pair;
+
+public class MapCSSParser implements MapCSSParserConstants {
+
+/*************
+ * Parser definitions
+ *
+ *                       rule
+ *  _______________________|______________________________
+ * |                                                      |
+ *        selector                      declaration
+ *  _________|___________________   _________|____________
+ * |                             | |                      |
+ *
+ * way|z11-12[highway=residential] { color: red; width: 3 }
+ *
+ *    |_____||___________________|   |_________|
+ *       |            |                   |
+ *     zoom       condition          instruction
+ *     
+ * more general:
+ * 
+ * way|z13-[a=b][c=d]::subpart, way|z-3[u=v]:closed::subpart2 { p1 : val; p2 : val; }
+ *
+ * 'val' can be a literal, or an expression like "prop(width, default) + 0.8".
+ *
+ */
+  final public int uint() throws ParseException {
+    Token i;
+    i = jj_consume_token(UINT);
+               {if (true) return Integer.parseInt(i.image);}
+    throw new Error("Missing return statement in function");
+  }
+
+  final public float ufloat() throws ParseException {
+    Token f;
+    switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+    case UFLOAT:
+      f = jj_consume_token(UFLOAT);
+      break;
+    case UINT:
+      f = jj_consume_token(UINT);
+      break;
+    default:
+      jj_la1[0] = jj_gen;
+      jj_consume_token(-1);
+      throw new ParseException();
+    }
+      {if (true) return Float.parseFloat(f.image);}
+    throw new Error("Missing return statement in function");
+  }
+
+  final public String string() throws ParseException {
+    Token t;
+    t = jj_consume_token(STRING);
+        {if (true) return t.image.substring(1, t.image.length() - 1).replace("\\\"", "\"").replace("\\\\", "\\");}
+    throw new Error("Missing return statement in function");
+  }
+
+  final public String string_or_ident() throws ParseException {
+    Token t;
+    String s;
+    switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+    case IDENT:
+      t = jj_consume_token(IDENT);
+                {if (true) return t.image;}
+      break;
+    case STRING:
+      s = string();
+                                                 {if (true) return s;}
+      break;
+    default:
+      jj_la1[1] = jj_gen;
+      jj_consume_token(-1);
+      throw new ParseException();
+    }
+    throw new Error("Missing return statement in function");
+  }
+
+/**
+ * white-space
+ */
+  final public void s() throws ParseException {
+    switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+    case S:
+      jj_consume_token(S);
+      break;
+    default:
+      jj_la1[2] = jj_gen;
+      ;
+    }
+  }
+
+/**
+ * mix of white-space and comments
+ */
+  final public void w() throws ParseException {
+    label_1:
+    while (true) {
+      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+      case S:
+      case COMMENT_START:
+        ;
+        break;
+      default:
+        jj_la1[3] = jj_gen;
+        break label_1;
+      }
+      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+      case S:
+        jj_consume_token(S);
+        break;
+      case COMMENT_START:
+        jj_consume_token(COMMENT_START);
+        jj_consume_token(COMMENT_END);
+        break;
+      default:
+        jj_la1[4] = jj_gen;
+        jj_consume_token(-1);
+        throw new ParseException();
+      }
+    }
+  }
+
+/**
+ * comma delimited list of floats (at least 2, all >= 0)
+ */
+  final public float[] float_array() throws ParseException {
+    float f;
+    List<Float> fs = new ArrayList<Float>();
+    f = ufloat();
+                 fs.add(f);
+    label_2:
+    while (true) {
+      jj_consume_token(COMMA);
+      s();
+      f = ufloat();
+                     fs.add(f);
+      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+      case COMMA:
+        ;
+        break;
+      default:
+        jj_la1[5] = jj_gen;
+        break label_2;
+      }
+    }
+        float[] a = new float[fs.size()];
+        for (int i=0; i<fs.size(); ++i) {
+            a[i] = fs.get(i);
+        }
+        {if (true) return a;}
+    throw new Error("Missing return statement in function");
+  }
+
+/**
+ * root
+ */
+  final public void sheet(MapCSSStyleSource sheet) throws ParseException {
+    MapCSSRule r;
+    Token com = null;
+    w();
+    label_3:
+    while (true) {
+      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+      case IDENT:
+      case STAR:
+        ;
+        break;
+      default:
+        jj_la1[6] = jj_gen;
+        break label_3;
+      }
+      r = rule();
+                 sheet.rules.add(r);
+      w();
+    }
+    jj_consume_token(0);
+  }
+
+  final public MapCSSRule rule() throws ParseException {
+    List<Selector> selectors = new ArrayList<Selector>();
+    Selector sel;
+    List<Instruction> decl;
+    sel = selector();
+                     selectors.add(sel);
+    w();
+    label_4:
+    while (true) {
+      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+      case COMMA:
+        ;
+        break;
+      default:
+        jj_la1[7] = jj_gen;
+        break label_4;
+      }
+      jj_consume_token(COMMA);
+      w();
+      sel = selector();
+                         selectors.add(sel);
+      w();
+    }
+    decl = declaration();
+      {if (true) return new MapCSSRule(selectors, decl);}
+    throw new Error("Missing return statement in function");
+  }
+
+  final public Selector selector() throws ParseException {
+    Token base;
+    Condition c;
+    Pair<Integer, Integer> r = null;
+    List<Condition> conditions = new ArrayList<Condition>();
+    String sub = null;
+    switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+    case IDENT:
+      base = jj_consume_token(IDENT);
+      break;
+    case STAR:
+      base = jj_consume_token(STAR);
+      break;
+    default:
+      jj_la1[8] = jj_gen;
+      jj_consume_token(-1);
+      throw new ParseException();
+    }
+    switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+    case PIPE_Z:
+      r = zoom();
+      break;
+    default:
+      jj_la1[9] = jj_gen;
+      ;
+    }
+    label_5:
+    while (true) {
+      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+      case LSQUARE:
+      case COLON:
+        ;
+        break;
+      default:
+        jj_la1[10] = jj_gen;
+        break label_5;
+      }
+      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+      case LSQUARE:
+        c = condition();
+        break;
+      case COLON:
+        c = pseudoclass();
+        break;
+      default:
+        jj_la1[11] = jj_gen;
+        jj_consume_token(-1);
+        throw new ParseException();
+      }
+                                            conditions.add(c);
+    }
+    switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+    case DCOLON:
+      sub = subpart();
+      break;
+    default:
+      jj_la1[12] = jj_gen;
+      ;
+    }
+      {if (true) return new Selector(base.image, r, conditions, sub);}
+    throw new Error("Missing return statement in function");
+  }
+
+  final public Pair<Integer, Integer> zoom() throws ParseException {
+    Integer min = 0;
+    Integer max = Integer.MAX_VALUE;
+    jj_consume_token(PIPE_Z);
+    switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+    case MINUS:
+      jj_consume_token(MINUS);
+      max = uint();
+      break;
+    case UINT:
+      min = uint();
+      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+      case MINUS:
+        jj_consume_token(MINUS);
+        switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+        case UINT:
+          max = uint();
+          break;
+        default:
+          jj_la1[13] = jj_gen;
+          ;
+        }
+        break;
+      default:
+        jj_la1[14] = jj_gen;
+        ;
+      }
+      break;
+    default:
+      jj_la1[15] = jj_gen;
+      jj_consume_token(-1);
+      throw new ParseException();
+    }
+      {if (true) return new Pair<Integer, Integer>(min, max);}
+    throw new Error("Missing return statement in function");
+  }
+
+  final public Condition condition() throws ParseException {
+    boolean not = false;
+    String key;
+    String val;
+    jj_consume_token(LSQUARE);
+    switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+    case EXCLAMATION:
+      jj_consume_token(EXCLAMATION);
+                      not = true;
+      break;
+    default:
+      jj_la1[16] = jj_gen;
+      ;
+    }
+    key = string_or_ident();
+    switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+    case EQUAL:
+    case EXCLAMATION_EQUAL:
+      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+      case EXCLAMATION_EQUAL:
+        jj_consume_token(EXCLAMATION_EQUAL);
+                                not = true;
+        break;
+      case EQUAL:
+        jj_consume_token(EQUAL);
+        break;
+      default:
+        jj_la1[17] = jj_gen;
+        jj_consume_token(-1);
+        throw new ParseException();
+      }
+      val = string_or_ident();
+      jj_consume_token(RSQUARE);
+          {if (true) return new Condition.KeyValueCondition(key, val, not ? Condition.Op.NEQ : Condition.Op.EQ);}
+      break;
+    default:
+      jj_la1[18] = jj_gen;
+      ;
+    }
+    jj_consume_token(RSQUARE);
+      {if (true) return new Condition.KeyCondition(key, not);}
+    throw new Error("Missing return statement in function");
+  }
+
+  final public Condition pseudoclass() throws ParseException {
+    Token t;
+    boolean not = false;
+    jj_consume_token(COLON);
+    switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+    case EXCLAMATION:
+      jj_consume_token(EXCLAMATION);
+                              not = true;
+      break;
+    default:
+      jj_la1[19] = jj_gen;
+      ;
+    }
+    t = jj_consume_token(IDENT);
+      {if (true) return new Condition.PseudoClassCondition(t.image, not);}
+    throw new Error("Missing return statement in function");
+  }
+
+  final public String subpart() throws ParseException {
+    Token t;
+    jj_consume_token(DCOLON);
+    switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+    case IDENT:
+      t = jj_consume_token(IDENT);
+      break;
+    case STAR:
+      t = jj_consume_token(STAR);
+      break;
+    default:
+      jj_la1[20] = jj_gen;
+      jj_consume_token(-1);
+      throw new ParseException();
+    }
+      {if (true) return t.image;}
+    throw new Error("Missing return statement in function");
+  }
+
+  final public List<Instruction> declaration() throws ParseException {
+    List<Instruction> ins = new ArrayList<Instruction>();
+    Instruction i;
+    jj_consume_token(LBRACE);
+    w();
+    switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+    case RBRACE:
+      jj_consume_token(RBRACE);
+                 {if (true) return ins;}
+      break;
+    default:
+      jj_la1[21] = jj_gen;
+      ;
+    }
+    try {
+      i = instruction();
+                          if (i != null) ins.add(i);
+      label_6:
+      while (true) {
+        switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+        case SEMICOLON:
+          ;
+          break;
+        default:
+          jj_la1[22] = jj_gen;
+          break label_6;
+        }
+        jj_consume_token(SEMICOLON);
+        w();
+        switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+        case RBRACE:
+          jj_consume_token(RBRACE);
+                         {if (true) return ins;}
+          break;
+        default:
+          jj_la1[23] = jj_gen;
+          ;
+        }
+        i = instruction();
+                              if (i != null) ins.add(i);
+      }
+      jj_consume_token(RBRACE);
+                   {if (true) return ins;}
+    } catch (ParseException ex) {
+        error_skipto(RBRACE);
+        {if (true) return ins;}
+    }
+    throw new Error("Missing return statement in function");
+  }
+
+  final public Instruction instruction() throws ParseException {
+    Token key;
+    Object val;
+    key = jj_consume_token(IDENT);
+    w();
+    jj_consume_token(COLON);
+    w();
+    if (jj_2_1(2)) {
+      val = float_array();
+      w();
+    } else {
+      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+      case IDENT:
+      case UINT:
+      case UFLOAT:
+      case STRING:
+      case HEXCOLOR:
+      case LPAR:
+      case PLUS:
+      case MINUS:
+        val = expression();
+        break;
+      default:
+        jj_la1[24] = jj_gen;
+        jj_consume_token(-1);
+        throw new ParseException();
+      }
+    }
+        if (val instanceof LiteralExpression)
+            {if (true) return new Instruction.AssignmentInstruction(key.image, ((LiteralExpression) val).evaluate(null));}
+        else
+            {if (true) return new Instruction.AssignmentInstruction(key.image, val);}
+    throw new Error("Missing return statement in function");
+  }
+
+  final public Expression expression() throws ParseException {
+    List<Expression> args = new ArrayList<Expression>();
+    Expression e;
+    String op = null;
+    e = primary();
+                  args.add(e);
+    w();
+    switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+    case STAR:
+    case SLASH:
+    case PLUS:
+    case MINUS:
+      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+      case PLUS:
+        label_7:
+        while (true) {
+          jj_consume_token(PLUS);
+                       op = "+";
+          w();
+          e = primary();
+                                                     args.add(e);
+          w();
+          switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+          case PLUS:
+            ;
+            break;
+          default:
+            jj_la1[25] = jj_gen;
+            break label_7;
+          }
+        }
+        break;
+      case STAR:
+        label_8:
+        while (true) {
+          jj_consume_token(STAR);
+                       op = "*";
+          w();
+          e = primary();
+                                                     args.add(e);
+          w();
+          switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+          case STAR:
+            ;
+            break;
+          default:
+            jj_la1[26] = jj_gen;
+            break label_8;
+          }
+        }
+        break;
+      case MINUS:
+        jj_consume_token(MINUS);
+                        op = "-";
+        w();
+        e = primary();
+                                                      args.add(e);
+        w();
+        break;
+      case SLASH:
+        jj_consume_token(SLASH);
+                        op = "/";
+        w();
+        e = primary();
+                                                      args.add(e);
+        w();
+        break;
+      default:
+        jj_la1[27] = jj_gen;
+        jj_consume_token(-1);
+        throw new ParseException();
+      }
+      break;
+    default:
+      jj_la1[28] = jj_gen;
+      ;
+    }
+        if (args.size() == 1)
+            {if (true) return args.get(0);}
+        {if (true) return new FunctionExpression(op, args);}
+    throw new Error("Missing return statement in function");
+  }
+
+  final public Expression primary() throws ParseException {
+    Expression nested;
+    FunctionExpression fn;
+    Object lit;
+    if (jj_2_2(2)) {
+      // both function and identifier start with an identifier
+              fn = function();
+                        {if (true) return fn;}
+    } else {
+      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+      case IDENT:
+      case UINT:
+      case UFLOAT:
+      case STRING:
+      case HEXCOLOR:
+      case PLUS:
+      case MINUS:
+        lit = literal();
+                        {if (true) return new LiteralExpression(lit);}
+        break;
+      case LPAR:
+        jj_consume_token(LPAR);
+        w();
+        nested = expression();
+        jj_consume_token(RPAR);
+                                                    {if (true) return nested;}
+        break;
+      default:
+        jj_la1[29] = jj_gen;
+        jj_consume_token(-1);
+        throw new ParseException();
+      }
+    }
+    throw new Error("Missing return statement in function");
+  }
+
+  final public FunctionExpression function() throws ParseException {
+    Token tmp;
+    Expression arg;
+    String name;
+    List<Expression> args = new ArrayList<Expression>();
+    tmp = jj_consume_token(IDENT);
+                  name = tmp.image;
+    w();
+    jj_consume_token(LPAR);
+    w();
+    arg = expression();
+                       args.add(arg);
+    label_9:
+    while (true) {
+      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+      case COMMA:
+        ;
+        break;
+      default:
+        jj_la1[30] = jj_gen;
+        break label_9;
+      }
+      jj_consume_token(COMMA);
+      w();
+      arg = expression();
+                                     args.add(arg);
+    }
+    jj_consume_token(RPAR);
+      {if (true) return new FunctionExpression(name, args);}
+    throw new Error("Missing return statement in function");
+  }
+
+  final public Object literal() throws ParseException {
+    Object val;
+    Token t;
+    float f;
+    switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+    case IDENT:
+    case STRING:
+      val = string_or_ident();
+                                {if (true) return val;}
+      break;
+    case PLUS:
+      jj_consume_token(PLUS);
+      f = ufloat();
+                                {if (true) return new Instruction.RelativeFloat(f);}
+      break;
+    case MINUS:
+      jj_consume_token(MINUS);
+      f = ufloat();
+                                 {if (true) return -f;}
+      break;
+    case UINT:
+    case UFLOAT:
+      f = ufloat();
+                     {if (true) return f;}
+      break;
+    case HEXCOLOR:
+      t = jj_consume_token(HEXCOLOR);
+                String clr = t.image.substring(1);
+                if (clr.length() == 3) {
+                    clr = new String(new char[] {clr.charAt(0),clr.charAt(0),clr.charAt(1),clr.charAt(1),clr.charAt(2),clr.charAt(2)});
+                }
+                if (clr.length() != 6)
+                    {if (true) throw new AssertionError();}
+                {if (true) return new Color(Integer.parseInt(clr, 16));}
+      break;
+    default:
+      jj_la1[31] = jj_gen;
+      jj_consume_token(-1);
+      throw new ParseException();
+    }
+    throw new Error("Missing return statement in function");
+  }
+
+  void error_skipto(int kind) throws ParseException {
+  ParseException e = generateParseException();
+  System.err.println(e);
+  Token t;
+  do {
+    t = getNextToken();
+  } while (t.kind != kind);
+  }
+
+  private boolean jj_2_1(int xla) {
+    jj_la = xla; jj_lastpos = jj_scanpos = token;
+    try { return !jj_3_1(); }
+    catch(LookaheadSuccess ls) { return true; }
+    finally { jj_save(0, xla); }
+  }
+
+  private boolean jj_2_2(int xla) {
+    jj_la = xla; jj_lastpos = jj_scanpos = token;
+    try { return !jj_3_2(); }
+    catch(LookaheadSuccess ls) { return true; }
+    finally { jj_save(1, xla); }
+  }
+
+  private boolean jj_3R_15() {
+    Token xsp;
+    xsp = jj_scanpos;
+    if (jj_scan_token(7)) {
+    jj_scanpos = xsp;
+    if (jj_3R_16()) return true;
+    }
+    return false;
+  }
+
+  private boolean jj_3R_14() {
+    Token xsp;
+    while (true) {
+      xsp = jj_scanpos;
+      if (jj_3R_15()) { jj_scanpos = xsp; break; }
+    }
+    return false;
+  }
+
+  private boolean jj_3_2() {
+    if (jj_3R_11()) return true;
+    return false;
+  }
+
+  private boolean jj_3R_13() {
+    if (jj_scan_token(COMMA)) return true;
+    return false;
+  }
+
+  private boolean jj_3R_10() {
+    if (jj_3R_12()) return true;
+    Token xsp;
+    if (jj_3R_13()) return true;
+    while (true) {
+      xsp = jj_scanpos;
+      if (jj_3R_13()) { jj_scanpos = xsp; break; }
+    }
+    return false;
+  }
+
+  private boolean jj_3R_11() {
+    if (jj_scan_token(IDENT)) return true;
+    if (jj_3R_14()) return true;
+    if (jj_scan_token(LPAR)) return true;
+    return false;
+  }
+
+  private boolean jj_3R_16() {
+    if (jj_scan_token(COMMENT_START)) return true;
+    return false;
+  }
+
+  private boolean jj_3R_12() {
+    Token xsp;
+    xsp = jj_scanpos;
+    if (jj_scan_token(3)) {
+    jj_scanpos = xsp;
+    if (jj_scan_token(2)) return true;
+    }
+    return false;
+  }
+
+  private boolean jj_3_1() {
+    if (jj_3R_10()) return true;
+    return false;
+  }
+
+  /** Generated Token Manager. */
+  public MapCSSParserTokenManager token_source;
+  SimpleCharStream jj_input_stream;
+  /** Current token. */
+  public Token token;
+  /** Next token. */
+  public Token jj_nt;
+  private int jj_ntk;
+  private Token jj_scanpos, jj_lastpos;
+  private int jj_la;
+  private int jj_gen;
+  final private int[] jj_la1 = new int[32];
+  static private int[] jj_la1_0;
+  static {
+      jj_la1_init_0();
+   }
+   private static void jj_la1_init_0() {
+      jj_la1_0 = new int[] {0xc,0x12,0x80,0x4000080,0x4000080,0x400000,0x102,0x400000,0x102,0x800000,0x81000,0x81000,0x100000,0x4,0x2000000,0x2000004,0x20000,0x50000,0x50000,0x20000,0x102,0x800,0x200000,0x800,0x300405e,0x1000000,0x100,0x3000300,0x3000300,0x300405e,0x400000,0x300005e,};
+   }
+  final private JJCalls[] jj_2_rtns = new JJCalls[2];
+  private boolean jj_rescan = false;
+  private int jj_gc = 0;
+
+  /** Constructor with InputStream. */
+  public MapCSSParser(java.io.InputStream stream) {
+     this(stream, null);
+  }
+  /** Constructor with InputStream and supplied encoding */
+  public MapCSSParser(java.io.InputStream stream, String encoding) {
+    try { jj_input_stream = new SimpleCharStream(stream, encoding, 1, 1); } catch(java.io.UnsupportedEncodingException e) { throw new RuntimeException(e); }
+    token_source = new MapCSSParserTokenManager(jj_input_stream);
+    token = new Token();
+    jj_ntk = -1;
+    jj_gen = 0;
+    for (int i = 0; i < 32; i++) jj_la1[i] = -1;
+    for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
+  }
+
+  /** Reinitialise. */
+  public void ReInit(java.io.InputStream stream) {
+     ReInit(stream, null);
+  }
+  /** Reinitialise. */
+  public void ReInit(java.io.InputStream stream, String encoding) {
+    try { jj_input_stream.ReInit(stream, encoding, 1, 1); } catch(java.io.UnsupportedEncodingException e) { throw new RuntimeException(e); }
+    token_source.ReInit(jj_input_stream);
+    token = new Token();
+    jj_ntk = -1;
+    jj_gen = 0;
+    for (int i = 0; i < 32; i++) jj_la1[i] = -1;
+    for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
+  }
+
+  /** Constructor. */
+  public MapCSSParser(java.io.Reader stream) {
+    jj_input_stream = new SimpleCharStream(stream, 1, 1);
+    token_source = new MapCSSParserTokenManager(jj_input_stream);
+    token = new Token();
+    jj_ntk = -1;
+    jj_gen = 0;
+    for (int i = 0; i < 32; i++) jj_la1[i] = -1;
+    for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
+  }
+
+  /** Reinitialise. */
+  public void ReInit(java.io.Reader stream) {
+    jj_input_stream.ReInit(stream, 1, 1);
+    token_source.ReInit(jj_input_stream);
+    token = new Token();
+    jj_ntk = -1;
+    jj_gen = 0;
+    for (int i = 0; i < 32; i++) jj_la1[i] = -1;
+    for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
+  }
+
+  /** Constructor with generated Token Manager. */
+  public MapCSSParser(MapCSSParserTokenManager tm) {
+    token_source = tm;
+    token = new Token();
+    jj_ntk = -1;
+    jj_gen = 0;
+    for (int i = 0; i < 32; i++) jj_la1[i] = -1;
+    for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
+  }
+
+  /** Reinitialise. */
+  public void ReInit(MapCSSParserTokenManager tm) {
+    token_source = tm;
+    token = new Token();
+    jj_ntk = -1;
+    jj_gen = 0;
+    for (int i = 0; i < 32; i++) jj_la1[i] = -1;
+    for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
+  }
+
+  private Token jj_consume_token(int kind) throws ParseException {
+    Token oldToken;
+    if ((oldToken = token).next != null) token = token.next;
+    else token = token.next = token_source.getNextToken();
+    jj_ntk = -1;
+    if (token.kind == kind) {
+      jj_gen++;
+      if (++jj_gc > 100) {
+        jj_gc = 0;
+        for (int i = 0; i < jj_2_rtns.length; i++) {
+          JJCalls c = jj_2_rtns[i];
+          while (c != null) {
+            if (c.gen < jj_gen) c.first = null;
+            c = c.next;
+          }
+        }
+      }
+      return token;
+    }
+    token = oldToken;
+    jj_kind = kind;
+    throw generateParseException();
+  }
+
+  static private final class LookaheadSuccess extends java.lang.Error { }
+  final private LookaheadSuccess jj_ls = new LookaheadSuccess();
+  private boolean jj_scan_token(int kind) {
+    if (jj_scanpos == jj_lastpos) {
+      jj_la--;
+      if (jj_scanpos.next == null) {
+        jj_lastpos = jj_scanpos = jj_scanpos.next = token_source.getNextToken();
+      } else {
+        jj_lastpos = jj_scanpos = jj_scanpos.next;
+      }
+    } else {
+      jj_scanpos = jj_scanpos.next;
+    }
+    if (jj_rescan) {
+      int i = 0; Token tok = token;
+      while (tok != null && tok != jj_scanpos) { i++; tok = tok.next; }
+      if (tok != null) jj_add_error_token(kind, i);
+    }
+    if (jj_scanpos.kind != kind) return true;
+    if (jj_la == 0 && jj_scanpos == jj_lastpos) throw jj_ls;
+    return false;
+  }
+
+
+/** Get the next Token. */
+  final public Token getNextToken() {
+    if (token.next != null) token = token.next;
+    else token = token.next = token_source.getNextToken();
+    jj_ntk = -1;
+    jj_gen++;
+    return token;
+  }
+
+/** Get the specific Token. */
+  final public Token getToken(int index) {
+    Token t = token;
+    for (int i = 0; i < index; i++) {
+      if (t.next != null) t = t.next;
+      else t = t.next = token_source.getNextToken();
+    }
+    return t;
+  }
+
+  private int jj_ntk() {
+    if ((jj_nt=token.next) == null)
+      return (jj_ntk = (token.next=token_source.getNextToken()).kind);
+    else
+      return (jj_ntk = jj_nt.kind);
+  }
+
+  private java.util.List jj_expentries = new java.util.ArrayList();
+  private int[] jj_expentry;
+  private int jj_kind = -1;
+  private int[] jj_lasttokens = new int[100];
+  private int jj_endpos;
+
+  private void jj_add_error_token(int kind, int pos) {
+    if (pos >= 100) return;
+    if (pos == jj_endpos + 1) {
+      jj_lasttokens[jj_endpos++] = kind;
+    } else if (jj_endpos != 0) {
+      jj_expentry = new int[jj_endpos];
+      for (int i = 0; i < jj_endpos; i++) {
+        jj_expentry[i] = jj_lasttokens[i];
+      }
+      jj_entries_loop: for (java.util.Iterator it = jj_expentries.iterator(); it.hasNext();) {
+        int[] oldentry = (int[])(it.next());
+        if (oldentry.length == jj_expentry.length) {
+          for (int i = 0; i < jj_expentry.length; i++) {
+            if (oldentry[i] != jj_expentry[i]) {
+              continue jj_entries_loop;
+            }
+          }
+          jj_expentries.add(jj_expentry);
+          break jj_entries_loop;
+        }
+      }
+      if (pos != 0) jj_lasttokens[(jj_endpos = pos) - 1] = kind;
+    }
+  }
+
+  /** Generate ParseException. */
+  public ParseException generateParseException() {
+    jj_expentries.clear();
+    boolean[] la1tokens = new boolean[30];
+    if (jj_kind >= 0) {
+      la1tokens[jj_kind] = true;
+      jj_kind = -1;
+    }
+    for (int i = 0; i < 32; i++) {
+      if (jj_la1[i] == jj_gen) {
+        for (int j = 0; j < 32; j++) {
+          if ((jj_la1_0[i] & (1<<j)) != 0) {
+            la1tokens[j] = true;
+          }
+        }
+      }
+    }
+    for (int i = 0; i < 30; i++) {
+      if (la1tokens[i]) {
+        jj_expentry = new int[1];
+        jj_expentry[0] = i;
+        jj_expentries.add(jj_expentry);
+      }
+    }
+    jj_endpos = 0;
+    jj_rescan_token();
+    jj_add_error_token(0, 0);
+    int[][] exptokseq = new int[jj_expentries.size()][];
+    for (int i = 0; i < jj_expentries.size(); i++) {
+      exptokseq[i] = (int[])jj_expentries.get(i);
+    }
+    return new ParseException(token, exptokseq, tokenImage);
+  }
+
+  /** Enable tracing. */
+  final public void enable_tracing() {
+  }
+
+  /** Disable tracing. */
+  final public void disable_tracing() {
+  }
+
+  private void jj_rescan_token() {
+    jj_rescan = true;
+    for (int i = 0; i < 2; i++) {
+    try {
+      JJCalls p = jj_2_rtns[i];
+      do {
+        if (p.gen > jj_gen) {
+          jj_la = p.arg; jj_lastpos = jj_scanpos = p.first;
+          switch (i) {
+            case 0: jj_3_1(); break;
+            case 1: jj_3_2(); break;
+          }
+        }
+        p = p.next;
+      } while (p != null);
+      } catch(LookaheadSuccess ls) { }
+    }
+    jj_rescan = false;
+  }
+
+  private void jj_save(int index, int xla) {
+    JJCalls p = jj_2_rtns[index];
+    while (p.gen > jj_gen) {
+      if (p.next == null) { p = p.next = new JJCalls(); break; }
+      p = p.next;
+    }
+    p.gen = jj_gen + xla - jj_la; p.first = token; p.arg = xla;
+  }
+
+  static final class JJCalls {
+    int gen;
+    Token first;
+    int arg;
+    JJCalls next;
+  }
+
+}
Index: trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/parser/MapCSSParser.jj
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/parser/MapCSSParser.jj	(revision 3848)
+++ trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/parser/MapCSSParser.jj	(revision 3848)
@@ -0,0 +1,415 @@
+// License: GPL. For details, see LICENSE file.
+options {
+  STATIC = false;
+}
+
+PARSER_BEGIN(MapCSSParser)
+package org.openstreetmap.josm.gui.mappaint.mapcss.parser;
+
+import java.awt.Color;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.openstreetmap.josm.gui.mappaint.mapcss.Condition;
+import org.openstreetmap.josm.gui.mappaint.mapcss.Expression;
+import org.openstreetmap.josm.gui.mappaint.mapcss.Instruction;
+import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSRule;
+import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSStyleSource;
+import org.openstreetmap.josm.gui.mappaint.mapcss.Selector;
+import org.openstreetmap.josm.gui.mappaint.mapcss.Expression.FunctionExpression;
+import org.openstreetmap.josm.gui.mappaint.mapcss.Expression.LiteralExpression;
+import org.openstreetmap.josm.tools.Pair;
+
+public class MapCSSParser {
+}
+PARSER_END(MapCSSParser)
+
+/*************
+ * Token definitions
+ */
+
+<DEFAULT>
+TOKEN:
+{
+    < IDENT: ["a"-"z","A"-"Z","_"] ( ["a"-"z","A"-"Z","_","-","0"-"9"] )* >
+|   < UINT: ["1"-"9"] ( ["0"-"9"] )* >
+|   < UFLOAT: ( ["0"-"9"] )+ ( "." ( ["0"-"9"] )+ )? >
+|   < STRING: "\"" ( [" ","!","#"-"&","("-"[","]"-"~","\u0080"-"\uFFFF"] | "\\\"" | "\\\\" )*  "\"" >
+|   < #H: ["0"-"9","a"-"f","A"-"F"] >
+|   < HEXCOLOR: "#" ( <H><H><H><H><H><H> | <H><H><H> ) >
+|   < S: ( " " | "\t" | "\n" | "\r" | "\f" )+ >
+|   < STAR: "*" >
+|   < SLASH: "/" >
+|   < LBRACE: "{" >
+|   < RBRACE: "}" >
+|   < LSQUARE: "[" >
+|   < RSQUARE: "]" >
+|   < LPAR: "(" >
+|   < RPAR: ")" >
+|   < EQUAL: "=" >
+|   < EXCLAMATION: "!" >
+|   < EXCLAMATION_EQUAL: "!=" >
+|   < COLON: ":" >
+|   < DCOLON: "::" >
+|   < SEMICOLON: ";" >
+|   < COMMA: "," >
+|   < PIPE_Z: "|z" >
+|   < PLUS: "+" >
+|   < MINUS: "-" >
+|   < COMMENT_START: "/*" > : COMMENT
+|   < UNEXPECTED_CHAR : ~[] > // avoid TokenMgrErrors because they are hard to recover from
+}
+
+<COMMENT>
+TOKEN:
+{
+    < COMMENT_END: "*/" > : DEFAULT
+}
+
+<COMMENT>
+SKIP:
+{
+    < ~[] >
+}
+
+/*************
+ * Parser definitions
+ *
+ *                       rule
+ *  _______________________|______________________________
+ * |                                                      |
+ *        selector                      declaration
+ *  _________|___________________   _________|____________
+ * |                             | |                      |
+ *
+ * way|z11-12[highway=residential] { color: red; width: 3 }
+ *
+ *    |_____||___________________|   |_________|
+ *       |            |                   |
+ *     zoom       condition          instruction
+ *
+ * more general:
+ *
+ * way|z13-[a=b][c=d]::subpart, way|z-3[u=v]:closed::subpart2 { p1 : val; p2 : val; }
+ *
+ * 'val' can be a literal, or an expression like "prop(width, default) + 0.8".
+ *
+ */
+
+int uint() :
+{
+    Token i;
+}
+{
+    i=<UINT> { return Integer.parseInt(i.image); }
+}
+
+float ufloat() :
+{
+    Token f;
+}
+{
+    ( f=<UFLOAT> | f=<UINT> )
+    { return Float.parseFloat(f.image); }
+}
+
+String string() :
+{
+    Token t;
+}
+{
+    t=<STRING>
+    {
+        return t.image.substring(1, t.image.length() - 1).replace("\\\"", "\"").replace("\\\\", "\\");
+    }
+}
+
+String string_or_ident() :
+{
+    Token t;
+    String s;
+}
+{
+    t=<IDENT> { return t.image; } | s=string() { return s; }
+}
+
+/**
+ * white-space
+ */
+void s() :
+{
+}
+{
+    ( <S> )?
+}
+
+/**
+ * mix of white-space and comments
+ */
+void w() :
+{
+}
+{
+    ( <S> | ( <COMMENT_START> <COMMENT_END> ) )*
+}
+
+/**
+ * comma delimited list of floats (at least 2, all >= 0)
+ */
+float[] float_array() :
+{
+    float f;
+    List<Float> fs = new ArrayList<Float>();
+}
+{
+    f=ufloat() { fs.add(f); }
+    (
+        <COMMA> s()
+        f=ufloat() { fs.add(f); }
+    )+
+    {
+        float[] a = new float[fs.size()];
+        for (int i=0; i<fs.size(); ++i) {
+            a[i] = fs.get(i);
+        }
+        return a;
+    }
+}
+
+/**
+ * root
+ */
+void sheet(MapCSSStyleSource sheet):
+{
+    MapCSSRule r;
+    Token com = null;
+}
+{
+    w()
+    ( r=rule() { sheet.rules.add(r); } w() )*
+    <EOF>
+}
+
+MapCSSRule rule():
+{
+    List<Selector> selectors = new ArrayList<Selector>();
+    Selector sel;
+    List<Instruction> decl;
+}
+{
+    sel=selector() { selectors.add(sel); } w()
+    (
+        <COMMA> w()
+        sel=selector() { selectors.add(sel); } w()
+    )*
+    decl=declaration()
+    { return new MapCSSRule(selectors, decl); }
+}
+
+Selector selector() :
+{
+    Token base;
+    Condition c;
+    Pair<Integer, Integer> r = null;
+    List<Condition> conditions = new ArrayList<Condition>();
+    String sub = null;
+}
+{
+    ( base=<IDENT> | base=<STAR> )
+    ( r=zoom() )?
+    ( ( c=condition() | c=pseudoclass() ) { conditions.add(c); } )*
+    ( sub=subpart() )?
+    { return new Selector(base.image, r, conditions, sub); }
+}
+
+Pair<Integer, Integer> zoom() :
+{
+    Integer min = 0;
+    Integer max = Integer.MAX_VALUE;
+}
+{
+    <PIPE_Z>
+    (
+        ( <MINUS> max=uint() ) |
+        ( min=uint() ( <MINUS> ( max=uint() )? )? )
+    )
+    { return new Pair<Integer, Integer>(min, max); }
+}
+
+Condition condition() :
+{
+    boolean not = false;
+    String key;
+    String val;
+}
+{
+    <LSQUARE>
+    ( <EXCLAMATION> { not = true; } )?
+    key=string_or_ident()
+    (
+        ( <EXCLAMATION_EQUAL> { not = true; } | <EQUAL> )
+        val=string_or_ident()
+        <RSQUARE>
+        { return new Condition.KeyValueCondition(key, val, not ? Condition.Op.NEQ : Condition.Op.EQ); }
+    )?
+    <RSQUARE>
+    { return new Condition.KeyCondition(key, not); }
+}
+
+Condition pseudoclass() :
+{
+    Token t;
+    boolean not = false;
+}
+{
+    <COLON>
+    ( <EXCLAMATION> { not = true; } )? t=<IDENT>
+    { return new Condition.PseudoClassCondition(t.image, not); }
+}
+
+String subpart() :
+{
+    Token t;
+}
+{
+    <DCOLON>
+    ( t=<IDENT> | t=<STAR> )
+    { return t.image; }
+}
+
+List<Instruction> declaration() :
+{
+    List<Instruction> ins = new ArrayList<Instruction>();
+    Instruction i;
+}
+{
+    <LBRACE> w()
+    ( <RBRACE> { return ins; } )?
+    try {
+        i=instruction() { if (i != null) ins.add(i); }
+        (
+            <SEMICOLON> w()
+            ( <RBRACE> { return ins; } )?
+            i=instruction() { if (i != null) ins.add(i); }
+        )*
+        <RBRACE> { return ins; }
+    } catch (ParseException ex) {
+        error_skipto(RBRACE);
+        return ins;
+    }
+}
+
+Instruction instruction() :
+{
+    Token key;
+    Object val;
+}
+{
+    key=<IDENT> w()
+    <COLON> w()
+    (
+        LOOKAHEAD(2) // both number and float array start with a number
+            ( val=float_array() w() )
+        |
+            val=expression()
+    )
+    {
+        if (val instanceof LiteralExpression)
+            return new Instruction.AssignmentInstruction(key.image, ((LiteralExpression) val).evaluate(null));
+        else
+            return new Instruction.AssignmentInstruction(key.image, val);
+    }
+}
+
+Expression expression():
+{
+    List<Expression> args = new ArrayList<Expression>();
+    Expression e;
+    String op = null;
+}
+{
+    e=primary() { args.add(e); } w()
+    (
+            ( <PLUS> { op = "+"; } w() e=primary() { args.add(e); } w() )+
+        |
+            ( <STAR> { op = "*"; } w() e=primary() { args.add(e); } w() )+
+        |
+            ( <MINUS> { op = "-"; } w() e=primary() { args.add(e); } w() )
+        |
+            ( <SLASH> { op = "/"; } w() e=primary() { args.add(e); } w() )
+    )?
+    {
+        if (args.size() == 1)
+            return args.get(0);
+        return new FunctionExpression(op, args);
+    }
+}
+
+Expression primary() :
+{
+    Expression nested;
+    FunctionExpression fn;
+    Object lit;
+}
+{
+    LOOKAHEAD(2) // both function and identifier start with an identifier
+        fn=function() { return fn; }
+    |
+        lit=literal() { return new LiteralExpression(lit); }
+    |
+        ( <LPAR> w() nested=expression() <RPAR> ) { return nested; }
+}
+
+FunctionExpression function() :
+{
+    Token tmp;
+    Expression arg;
+    String name;
+    List<Expression> args = new ArrayList<Expression>();
+}
+{
+    tmp=<IDENT> { name = tmp.image; } w()
+    <LPAR> w()
+    arg=expression() { args.add(arg); }
+    ( <COMMA> w() arg=expression() { args.add(arg); } )*
+    <RPAR>
+    { return new FunctionExpression(name, args); }
+}
+
+Object literal() :
+{
+    Object val;
+    Token t;
+    float f;
+}
+{
+        val=string_or_ident() { return val; }
+    |
+        ( <PLUS> f=ufloat() ) { return new Instruction.RelativeFloat(f); }
+    |
+        ( <MINUS> f=ufloat() ) { return -f; }
+    |
+        f=ufloat() { return f; }
+    |
+        t=<HEXCOLOR>
+            {
+                String clr = t.image.substring(1);
+                if (clr.length() == 3) {
+                    clr = new String(new char[] {clr.charAt(0),clr.charAt(0),clr.charAt(1),clr.charAt(1),clr.charAt(2),clr.charAt(2)});
+                }
+                if (clr.length() != 6)
+                    throw new AssertionError();
+                return new Color(Integer.parseInt(clr, 16));
+            }
+}
+
+JAVACODE
+void error_skipto(int kind) {
+  ParseException e = generateParseException();
+  System.err.println(e);
+  Token t;
+  do {
+    t = getNextToken();
+  } while (t.kind != kind);
+}
+
Index: trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/parser/MapCSSParserConstants.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/parser/MapCSSParserConstants.java	(revision 3848)
+++ trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/parser/MapCSSParserConstants.java	(revision 3848)
@@ -0,0 +1,109 @@
+/* Generated By:JavaCC: Do not edit this line. MapCSSParserConstants.java */
+package org.openstreetmap.josm.gui.mappaint.mapcss.parser;
+
+
+/** 
+ * Token literal values and constants.
+ * Generated by org.javacc.parser.OtherFilesGen#start()
+ */
+public interface MapCSSParserConstants {
+
+  /** End of File. */
+  int EOF = 0;
+  /** RegularExpression Id. */
+  int IDENT = 1;
+  /** RegularExpression Id. */
+  int UINT = 2;
+  /** RegularExpression Id. */
+  int UFLOAT = 3;
+  /** RegularExpression Id. */
+  int STRING = 4;
+  /** RegularExpression Id. */
+  int H = 5;
+  /** RegularExpression Id. */
+  int HEXCOLOR = 6;
+  /** RegularExpression Id. */
+  int S = 7;
+  /** RegularExpression Id. */
+  int STAR = 8;
+  /** RegularExpression Id. */
+  int SLASH = 9;
+  /** RegularExpression Id. */
+  int LBRACE = 10;
+  /** RegularExpression Id. */
+  int RBRACE = 11;
+  /** RegularExpression Id. */
+  int LSQUARE = 12;
+  /** RegularExpression Id. */
+  int RSQUARE = 13;
+  /** RegularExpression Id. */
+  int LPAR = 14;
+  /** RegularExpression Id. */
+  int RPAR = 15;
+  /** RegularExpression Id. */
+  int EQUAL = 16;
+  /** RegularExpression Id. */
+  int EXCLAMATION = 17;
+  /** RegularExpression Id. */
+  int EXCLAMATION_EQUAL = 18;
+  /** RegularExpression Id. */
+  int COLON = 19;
+  /** RegularExpression Id. */
+  int DCOLON = 20;
+  /** RegularExpression Id. */
+  int SEMICOLON = 21;
+  /** RegularExpression Id. */
+  int COMMA = 22;
+  /** RegularExpression Id. */
+  int PIPE_Z = 23;
+  /** RegularExpression Id. */
+  int PLUS = 24;
+  /** RegularExpression Id. */
+  int MINUS = 25;
+  /** RegularExpression Id. */
+  int COMMENT_START = 26;
+  /** RegularExpression Id. */
+  int UNEXPECTED_CHAR = 27;
+  /** RegularExpression Id. */
+  int COMMENT_END = 28;
+
+  /** Lexical state. */
+  int DEFAULT = 0;
+  /** Lexical state. */
+  int COMMENT = 1;
+
+  /** Literal token values. */
+  String[] tokenImage = {
+    "<EOF>",
+    "<IDENT>",
+    "<UINT>",
+    "<UFLOAT>",
+    "<STRING>",
+    "<H>",
+    "<HEXCOLOR>",
+    "<S>",
+    "\"*\"",
+    "\"/\"",
+    "\"{\"",
+    "\"}\"",
+    "\"[\"",
+    "\"]\"",
+    "\"(\"",
+    "\")\"",
+    "\"=\"",
+    "\"!\"",
+    "\"!=\"",
+    "\":\"",
+    "\"::\"",
+    "\";\"",
+    "\",\"",
+    "\"|z\"",
+    "\"+\"",
+    "\"-\"",
+    "\"/*\"",
+    "<UNEXPECTED_CHAR>",
+    "\"*/\"",
+    "<token of kind 29>",
+  };
+
+}
Index: trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/parser/MapCSSParserTokenManager.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/parser/MapCSSParserTokenManager.java	(revision 3848)
+++ trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/parser/MapCSSParserTokenManager.java	(revision 3848)
@@ -0,0 +1,598 @@
+/* Generated By:JavaCC: Do not edit this line. MapCSSParserTokenManager.java */
+package org.openstreetmap.josm.gui.mappaint.mapcss.parser;
+import java.awt.Color;
+import java.util.ArrayList;
+import java.util.List;
+import org.openstreetmap.josm.gui.mappaint.mapcss.Condition;
+import org.openstreetmap.josm.gui.mappaint.mapcss.Expression;
+import org.openstreetmap.josm.gui.mappaint.mapcss.Instruction;
+import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSRule;
+import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSStyleSource;
+import org.openstreetmap.josm.gui.mappaint.mapcss.Selector;
+import org.openstreetmap.josm.gui.mappaint.mapcss.Expression.FunctionExpression;
+import org.openstreetmap.josm.gui.mappaint.mapcss.Expression.LiteralExpression;
+import org.openstreetmap.josm.tools.Pair;
+
+/** Token Manager. */
+public class MapCSSParserTokenManager implements MapCSSParserConstants
+{
+
+  /** Debug output. */
+  public  java.io.PrintStream debugStream = System.out;
+  /** Set debug output. */
+  public  void setDebugStream(java.io.PrintStream ds) { debugStream = ds; }
+private final int jjStopStringLiteralDfa_0(int pos, long active0)
+{
+   switch (pos)
+   {
+      default :
+         return -1;
+   }
+}
+private final int jjStartNfa_0(int pos, long active0)
+{
+   return jjMoveNfa_0(jjStopStringLiteralDfa_0(pos, active0), pos + 1);
+}
+private int jjStopAtPos(int pos, int kind)
+{
+   jjmatchedKind = kind;
+   jjmatchedPos = pos;
+   return pos + 1;
+}
+private int jjMoveStringLiteralDfa0_0()
+{
+   switch(curChar)
+   {
+      case 33:
+         jjmatchedKind = 17;
+         return jjMoveStringLiteralDfa1_0(0x40000L);
+      case 40:
+         return jjStopAtPos(0, 14);
+      case 41:
+         return jjStopAtPos(0, 15);
+      case 42:
+         return jjStopAtPos(0, 8);
+      case 43:
+         return jjStopAtPos(0, 24);
+      case 44:
+         return jjStopAtPos(0, 22);
+      case 45:
+         return jjStopAtPos(0, 25);
+      case 47:
+         jjmatchedKind = 9;
+         return jjMoveStringLiteralDfa1_0(0x4000000L);
+      case 58:
+         jjmatchedKind = 19;
+         return jjMoveStringLiteralDfa1_0(0x100000L);
+      case 59:
+         return jjStopAtPos(0, 21);
+      case 61:
+         return jjStopAtPos(0, 16);
+      case 91:
+         return jjStopAtPos(0, 12);
+      case 93:
+         return jjStopAtPos(0, 13);
+      case 123:
+         return jjStopAtPos(0, 10);
+      case 124:
+         return jjMoveStringLiteralDfa1_0(0x800000L);
+      case 125:
+         return jjStopAtPos(0, 11);
+      default :
+         return jjMoveNfa_0(0, 0);
+   }
+}
+private int jjMoveStringLiteralDfa1_0(long active0)
+{
+   try { curChar = input_stream.readChar(); }
+   catch(java.io.IOException e) {
+      jjStopStringLiteralDfa_0(0, active0);
+      return 1;
+   }
+   switch(curChar)
+   {
+      case 42:
+         if ((active0 & 0x4000000L) != 0L)
+            return jjStopAtPos(1, 26);
+         break;
+      case 58:
+         if ((active0 & 0x100000L) != 0L)
+            return jjStopAtPos(1, 20);
+         break;
+      case 61:
+         if ((active0 & 0x40000L) != 0L)
+            return jjStopAtPos(1, 18);
+         break;
+      case 122:
+         if ((active0 & 0x800000L) != 0L)
+            return jjStopAtPos(1, 23);
+         break;
+      default :
+         break;
+   }
+   return jjStartNfa_0(0, active0);
+}
+static final long[] jjbitVec0 = {
+   0xfffffffffffffffeL, 0xffffffffffffffffL, 0xffffffffffffffffL, 0xffffffffffffffffL
+};
+static final long[] jjbitVec2 = {
+   0x0L, 0x0L, 0xffffffffffffffffL, 0xffffffffffffffffL
+};
+private int jjMoveNfa_0(int startState, int curPos)
+{
+   int startsAt = 0;
+   jjnewStateCnt = 22;
+   int i = 1;
+   jjstateSet[0] = startState;
+   int kind = 0x7fffffff;
+   for (;;)
+   {
+      if (++jjround == 0x7fffffff)
+         ReInitRounds();
+      if (curChar < 64)
+      {
+         long l = 1L << curChar;
+         do
+         {
+            switch(jjstateSet[--i])
+            {
+               case 0:
+                  if ((0x3ff000000000000L & l) != 0L)
+                  {
+                     if (kind > 3)
+                        kind = 3;
+                     jjCheckNAddTwoStates(4, 5);
+                  }
+                  else if ((0x100003600L & l) != 0L)
+                  {
+                     if (kind > 7)
+                        kind = 7;
+                     jjCheckNAdd(21);
+                  }
+                  else if (curChar == 35)
+                     jjstateSet[jjnewStateCnt++] = 14;
+                  else if (curChar == 34)
+                     jjCheckNAddStates(0, 2);
+                  if ((0x3fe000000000000L & l) != 0L)
+                  {
+                     if (kind > 2)
+                        kind = 2;
+                     jjCheckNAdd(3);
+                  }
+                  break;
+               case 1:
+                  if ((0x3ff200000000000L & l) == 0L)
+                     break;
+                  if (kind > 1)
+                     kind = 1;
+                  jjstateSet[jjnewStateCnt++] = 1;
+                  break;
+               case 2:
+                  if ((0x3fe000000000000L & l) == 0L)
+                     break;
+                  if (kind > 2)
+                     kind = 2;
+                  jjCheckNAdd(3);
+                  break;
+               case 3:
+                  if ((0x3ff000000000000L & l) == 0L)
+                     break;
+                  if (kind > 2)
+                     kind = 2;
+                  jjCheckNAdd(3);
+                  break;
+               case 4:
+                  if ((0x3ff000000000000L & l) == 0L)
+                     break;
+                  if (kind > 3)
+                     kind = 3;
+                  jjCheckNAddTwoStates(4, 5);
+                  break;
+               case 5:
+                  if (curChar == 46)
+                     jjCheckNAdd(6);
+                  break;
+               case 6:
+                  if ((0x3ff000000000000L & l) == 0L)
+                     break;
+                  if (kind > 3)
+                     kind = 3;
+                  jjCheckNAdd(6);
+                  break;
+               case 7:
+               case 11:
+                  if (curChar == 34)
+                     jjCheckNAddStates(0, 2);
+                  break;
+               case 8:
+                  if ((0xffffff7b00000000L & l) != 0L)
+                     jjCheckNAddStates(0, 2);
+                  break;
+               case 9:
+                  if (curChar == 34 && kind > 4)
+                     kind = 4;
+                  break;
+               case 13:
+                  if (curChar == 35)
+                     jjstateSet[jjnewStateCnt++] = 14;
+                  break;
+               case 14:
+                  if ((0x3ff000000000000L & l) != 0L)
+                     jjAddStates(3, 4);
+                  break;
+               case 15:
+                  if ((0x3ff000000000000L & l) != 0L)
+                     jjstateSet[jjnewStateCnt++] = 16;
+                  break;
+               case 16:
+                  if ((0x3ff000000000000L & l) != 0L)
+                     jjstateSet[jjnewStateCnt++] = 17;
+                  break;
+               case 17:
+                  if ((0x3ff000000000000L & l) != 0L)
+                     jjstateSet[jjnewStateCnt++] = 18;
+                  break;
+               case 18:
+               case 20:
+                  if ((0x3ff000000000000L & l) != 0L)
+                     jjCheckNAdd(19);
+                  break;
+               case 19:
+                  if ((0x3ff000000000000L & l) != 0L && kind > 6)
+                     kind = 6;
+                  break;
+               case 21:
+                  if ((0x100003600L & l) == 0L)
+                     break;
+                  if (kind > 7)
+                     kind = 7;
+                  jjCheckNAdd(21);
+                  break;
+               default : break;
+            }
+         } while(i != startsAt);
+      }
+      else if (curChar < 128)
+      {
+         long l = 1L << (curChar & 077);
+         do
+         {
+            switch(jjstateSet[--i])
+            {
+               case 0:
+               case 1:
+                  if ((0x7fffffe87fffffeL & l) == 0L)
+                     break;
+                  if (kind > 1)
+                     kind = 1;
+                  jjCheckNAdd(1);
+                  break;
+               case 8:
+                  if ((0x7fffffffefffffffL & l) != 0L)
+                     jjCheckNAddStates(0, 2);
+                  break;
+               case 10:
+                  if (curChar == 92)
+                     jjAddStates(5, 6);
+                  break;
+               case 12:
+                  if (curChar == 92)
+                     jjCheckNAddStates(0, 2);
+                  break;
+               case 14:
+                  if ((0x7e0000007eL & l) != 0L)
+                     jjAddStates(3, 4);
+                  break;
+               case 15:
+                  if ((0x7e0000007eL & l) != 0L)
+                     jjstateSet[jjnewStateCnt++] = 16;
+                  break;
+               case 16:
+                  if ((0x7e0000007eL & l) != 0L)
+                     jjstateSet[jjnewStateCnt++] = 17;
+                  break;
+               case 17:
+                  if ((0x7e0000007eL & l) != 0L)
+                     jjstateSet[jjnewStateCnt++] = 18;
+                  break;
+               case 18:
+               case 20:
+                  if ((0x7e0000007eL & l) != 0L)
+                     jjCheckNAdd(19);
+                  break;
+               case 19:
+                  if ((0x7e0000007eL & l) != 0L && kind > 6)
+                     kind = 6;
+                  break;
+               default : break;
+            }
+         } while(i != startsAt);
+      }
+      else
+      {
+         int hiByte = (int)(curChar >> 8);
+         int i1 = hiByte >> 6;
+         long l1 = 1L << (hiByte & 077);
+         int i2 = (curChar & 0xff) >> 6;
+         long l2 = 1L << (curChar & 077);
+         do
+         {
+            switch(jjstateSet[--i])
+            {
+               case 8:
+                  if (jjCanMove_0(hiByte, i1, i2, l1, l2))
+                     jjAddStates(0, 2);
+                  break;
+               default : break;
+            }
+         } while(i != startsAt);
+      }
+      if (kind != 0x7fffffff)
+      {
+         jjmatchedKind = kind;
+         jjmatchedPos = curPos;
+         kind = 0x7fffffff;
+      }
+      ++curPos;
+      if ((i = jjnewStateCnt) == (startsAt = 22 - (jjnewStateCnt = startsAt)))
+         return curPos;
+      try { curChar = input_stream.readChar(); }
+      catch(java.io.IOException e) { return curPos; }
+   }
+}
+private int jjMoveStringLiteralDfa0_1()
+{
+   switch(curChar)
+   {
+      case 42:
+         return jjMoveStringLiteralDfa1_1(0x10000000L);
+      default :
+         return 1;
+   }
+}
+private int jjMoveStringLiteralDfa1_1(long active0)
+{
+   try { curChar = input_stream.readChar(); }
+   catch(java.io.IOException e) {
+      return 1;
+   }
+   switch(curChar)
+   {
+      case 47:
+         if ((active0 & 0x10000000L) != 0L)
+            return jjStopAtPos(1, 28);
+         break;
+      default :
+         return 2;
+   }
+   return 2;
+}
+static final int[] jjnextStates = {
+   8, 9, 10, 15, 20, 11, 12, 
+};
+private static final boolean jjCanMove_0(int hiByte, int i1, int i2, long l1, long l2)
+{
+   switch(hiByte)
+   {
+      case 0:
+         return ((jjbitVec2[i2] & l2) != 0L);
+      default : 
+         if ((jjbitVec0[i1] & l1) != 0L)
+            return true;
+         return false;
+   }
+}
+
+/** Token literal values. */
+public static final String[] jjstrLiteralImages = {
+"", null, null, null, null, null, null, null, "\52", "\57", "\173", "\175", 
+"\133", "\135", "\50", "\51", "\75", "\41", "\41\75", "\72", "\72\72", "\73", "\54", 
+"\174\172", "\53", "\55", "\57\52", null, "\52\57", null, };
+
+/** Lexer state names. */
+public static final String[] lexStateNames = {
+   "DEFAULT", 
+   "COMMENT", 
+};
+
+/** Lex State array. */
+public static final int[] jjnewLexState = {
+   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
+   -1, 1, -1, 0, -1, 
+};
+static final long[] jjtoToken = {
+   0x1fffffdfL, 
+};
+static final long[] jjtoSkip = {
+   0x20000000L, 
+};
+protected SimpleCharStream input_stream;
+private final int[] jjrounds = new int[22];
+private final int[] jjstateSet = new int[44];
+protected char curChar;
+/** Constructor. */
+public MapCSSParserTokenManager(SimpleCharStream stream){
+   if (SimpleCharStream.staticFlag)
+      throw new Error("ERROR: Cannot use a static CharStream class with a non-static lexical analyzer.");
+   input_stream = stream;
+}
+
+/** Constructor. */
+public MapCSSParserTokenManager(SimpleCharStream stream, int lexState){
+   this(stream);
+   SwitchTo(lexState);
+}
+
+/** Reinitialise parser. */
+public void ReInit(SimpleCharStream stream)
+{
+   jjmatchedPos = jjnewStateCnt = 0;
+   curLexState = defaultLexState;
+   input_stream = stream;
+   ReInitRounds();
+}
+private void ReInitRounds()
+{
+   int i;
+   jjround = 0x80000001;
+   for (i = 22; i-- > 0;)
+      jjrounds[i] = 0x80000000;
+}
+
+/** Reinitialise parser. */
+public void ReInit(SimpleCharStream stream, int lexState)
+{
+   ReInit(stream);
+   SwitchTo(lexState);
+}
+
+/** Switch to specified lex state. */
+public void SwitchTo(int lexState)
+{
+   if (lexState >= 2 || lexState < 0)
+      throw new TokenMgrError("Error: Ignoring invalid lexical state : " + lexState + ". State unchanged.", TokenMgrError.INVALID_LEXICAL_STATE);
+   else
+      curLexState = lexState;
+}
+
+protected Token jjFillToken()
+{
+   final Token t;
+   final String curTokenImage;
+   final int beginLine;
+   final int endLine;
+   final int beginColumn;
+   final int endColumn;
+   String im = jjstrLiteralImages[jjmatchedKind];
+   curTokenImage = (im == null) ? input_stream.GetImage() : im;
+   beginLine = input_stream.getBeginLine();
+   beginColumn = input_stream.getBeginColumn();
+   endLine = input_stream.getEndLine();
+   endColumn = input_stream.getEndColumn();
+   t = Token.newToken(jjmatchedKind, curTokenImage);
+
+   t.beginLine = beginLine;
+   t.endLine = endLine;
+   t.beginColumn = beginColumn;
+   t.endColumn = endColumn;
+
+   return t;
+}
+
+int curLexState = 0;
+int defaultLexState = 0;
+int jjnewStateCnt;
+int jjround;
+int jjmatchedPos;
+int jjmatchedKind;
+
+/** Get the next Token. */
+public Token getNextToken() 
+{
+  Token matchedToken;
+  int curPos = 0;
+
+  EOFLoop :
+  for (;;)
+  {   
+   try   
+   {     
+      curChar = input_stream.BeginToken();
+   }     
+   catch(java.io.IOException e)
+   {        
+      jjmatchedKind = 0;
+      matchedToken = jjFillToken();
+      return matchedToken;
+   }
+
+   switch(curLexState)
+   {
+     case 0:
+       jjmatchedKind = 0x7fffffff;
+       jjmatchedPos = 0;
+       curPos = jjMoveStringLiteralDfa0_0();
+       if (jjmatchedPos == 0 && jjmatchedKind > 27)
+       {
+          jjmatchedKind = 27;
+       }
+       break;
+     case 1:
+       jjmatchedKind = 0x7fffffff;
+       jjmatchedPos = 0;
+       curPos = jjMoveStringLiteralDfa0_1();
+       if (jjmatchedPos == 0 && jjmatchedKind > 29)
+       {
+          jjmatchedKind = 29;
+       }
+       break;
+   }
+     if (jjmatchedKind != 0x7fffffff)
+     {
+        if (jjmatchedPos + 1 < curPos)
+           input_stream.backup(curPos - jjmatchedPos - 1);
+        if ((jjtoToken[jjmatchedKind >> 6] & (1L << (jjmatchedKind & 077))) != 0L)
+        {
+           matchedToken = jjFillToken();
+       if (jjnewLexState[jjmatchedKind] != -1)
+         curLexState = jjnewLexState[jjmatchedKind];
+           return matchedToken;
+        }
+        else
+        {
+         if (jjnewLexState[jjmatchedKind] != -1)
+           curLexState = jjnewLexState[jjmatchedKind];
+           continue EOFLoop;
+        }
+     }
+     int error_line = input_stream.getEndLine();
+     int error_column = input_stream.getEndColumn();
+     String error_after = null;
+     boolean EOFSeen = false;
+     try { input_stream.readChar(); input_stream.backup(1); }
+     catch (java.io.IOException e1) {
+        EOFSeen = true;
+        error_after = curPos <= 1 ? "" : input_stream.GetImage();
+        if (curChar == '\n' || curChar == '\r') {
+           error_line++;
+           error_column = 0;
+        }
+        else
+           error_column++;
+     }
+     if (!EOFSeen) {
+        input_stream.backup(1);
+        error_after = curPos <= 1 ? "" : input_stream.GetImage();
+     }
+     throw new TokenMgrError(EOFSeen, curLexState, error_line, error_column, error_after, curChar, TokenMgrError.LEXICAL_ERROR);
+  }
+}
+
+private void jjCheckNAdd(int state)
+{
+   if (jjrounds[state] != jjround)
+   {
+      jjstateSet[jjnewStateCnt++] = state;
+      jjrounds[state] = jjround;
+   }
+}
+private void jjAddStates(int start, int end)
+{
+   do {
+      jjstateSet[jjnewStateCnt++] = jjnextStates[start];
+   } while (start++ != end);
+}
+private void jjCheckNAddTwoStates(int state1, int state2)
+{
+   jjCheckNAdd(state1);
+   jjCheckNAdd(state2);
+}
+
+private void jjCheckNAddStates(int start, int end)
+{
+   do {
+      jjCheckNAdd(jjnextStates[start]);
+   } while (start++ != end);
+}
+
+}
Index: trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/parser/ParseException.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/parser/ParseException.java	(revision 3848)
+++ trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/parser/ParseException.java	(revision 3848)
@@ -0,0 +1,198 @@
+/* Generated By:JavaCC: Do not edit this line. ParseException.java Version 4.1 */
+/* JavaCCOptions:KEEP_LINE_COL=null */
+package org.openstreetmap.josm.gui.mappaint.mapcss.parser;
+
+/**
+ * This exception is thrown when parse errors are encountered.
+ * You can explicitly create objects of this exception type by
+ * calling the method generateParseException in the generated
+ * parser.
+ *
+ * You can modify this class to customize your error reporting
+ * mechanisms so long as you retain the public fields.
+ */
+public class ParseException extends Exception {
+
+  /**
+   * This constructor is used by the method "generateParseException"
+   * in the generated parser.  Calling this constructor generates
+   * a new object of this type with the fields "currentToken",
+   * "expectedTokenSequences", and "tokenImage" set.  The boolean
+   * flag "specialConstructor" is also set to true to indicate that
+   * this constructor was used to create this object.
+   * This constructor calls its super class with the empty string
+   * to force the "toString" method of parent class "Throwable" to
+   * print the error message in the form:
+   *     ParseException: <result of getMessage>
+   */
+  public ParseException(Token currentTokenVal,
+                        int[][] expectedTokenSequencesVal,
+                        String[] tokenImageVal
+                       )
+  {
+    super("");
+    specialConstructor = true;
+    currentToken = currentTokenVal;
+    expectedTokenSequences = expectedTokenSequencesVal;
+    tokenImage = tokenImageVal;
+  }
+
+  /**
+   * The following constructors are for use by you for whatever
+   * purpose you can think of.  Constructing the exception in this
+   * manner makes the exception behave in the normal way - i.e., as
+   * documented in the class "Throwable".  The fields "errorToken",
+   * "expectedTokenSequences", and "tokenImage" do not contain
+   * relevant information.  The JavaCC generated code does not use
+   * these constructors.
+   */
+
+  public ParseException() {
+    super();
+    specialConstructor = false;
+  }
+
+  /** Constructor with message. */
+  public ParseException(String message) {
+    super(message);
+    specialConstructor = false;
+  }
+
+  /**
+   * This variable determines which constructor was used to create
+   * this object and thereby affects the semantics of the
+   * "getMessage" method (see below).
+   */
+  protected boolean specialConstructor;
+
+  /**
+   * This is the last token that has been consumed successfully.  If
+   * this object has been created due to a parse error, the token
+   * followng this token will (therefore) be the first error token.
+   */
+  public Token currentToken;
+
+  /**
+   * Each entry in this array is an array of integers.  Each array
+   * of integers represents a sequence of tokens (by their ordinal
+   * values) that is expected at this point of the parse.
+   */
+  public int[][] expectedTokenSequences;
+
+  /**
+   * This is a reference to the "tokenImage" array of the generated
+   * parser within which the parse error occurred.  This array is
+   * defined in the generated ...Constants interface.
+   */
+  public String[] tokenImage;
+
+  /**
+   * This method has the standard behavior when this object has been
+   * created using the standard constructors.  Otherwise, it uses
+   * "currentToken" and "expectedTokenSequences" to generate a parse
+   * error message and returns it.  If this object has been created
+   * due to a parse error, and you do not catch it (it gets thrown
+   * from the parser), then this method is called during the printing
+   * of the final stack trace, and hence the correct error message
+   * gets displayed.
+   */
+  public String getMessage() {
+    if (!specialConstructor) {
+      return super.getMessage();
+    }
+    StringBuffer expected = new StringBuffer();
+    int maxSize = 0;
+    for (int i = 0; i < expectedTokenSequences.length; i++) {
+      if (maxSize < expectedTokenSequences[i].length) {
+        maxSize = expectedTokenSequences[i].length;
+      }
+      for (int j = 0; j < expectedTokenSequences[i].length; j++) {
+        expected.append(tokenImage[expectedTokenSequences[i][j]]).append(' ');
+      }
+      if (expectedTokenSequences[i][expectedTokenSequences[i].length - 1] != 0) {
+        expected.append("...");
+      }
+      expected.append(eol).append("    ");
+    }
+    String retval = "Encountered \"";
+    Token tok = currentToken.next;
+    for (int i = 0; i < maxSize; i++) {
+      if (i != 0) retval += " ";
+      if (tok.kind == 0) {
+        retval += tokenImage[0];
+        break;
+      }
+      retval += " " + tokenImage[tok.kind];
+      retval += " \"";
+      retval += add_escapes(tok.image);
+      retval += " \"";
+      tok = tok.next; 
+    }
+    retval += "\" at line " + currentToken.next.beginLine + ", column " + currentToken.next.beginColumn;
+    retval += "." + eol;
+    if (expectedTokenSequences.length == 1) {
+      retval += "Was expecting:" + eol + "    ";
+    } else {
+      retval += "Was expecting one of:" + eol + "    ";
+    }
+    retval += expected.toString();
+    return retval;
+  }
+
+  /**
+   * The end of line string for this machine.
+   */
+  protected String eol = System.getProperty("line.separator", "\n");
+ 
+  /**
+   * Used to convert raw characters to their escaped version
+   * when these raw version cannot be used as part of an ASCII
+   * string literal.
+   */
+  protected String add_escapes(String str) {
+      StringBuffer retval = new StringBuffer();
+      char ch;
+      for (int i = 0; i < str.length(); i++) {
+        switch (str.charAt(i))
+        {
+           case 0 :
+              continue;
+           case '\b':
+              retval.append("\\b");
+              continue;
+           case '\t':
+              retval.append("\\t");
+              continue;
+           case '\n':
+              retval.append("\\n");
+              continue;
+           case '\f':
+              retval.append("\\f");
+              continue;
+           case '\r':
+              retval.append("\\r");
+              continue;
+           case '\"':
+              retval.append("\\\"");
+              continue;
+           case '\'':
+              retval.append("\\\'");
+              continue;
+           case '\\':
+              retval.append("\\\\");
+              continue;
+           default:
+              if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e) {
+                 String s = "0000" + Integer.toString(ch, 16);
+                 retval.append("\\u" + s.substring(s.length() - 4, s.length()));
+              } else {
+                 retval.append(ch);
+              }
+              continue;
+        }
+      }
+      return retval.toString();
+   }
+
+}
+/* JavaCC - OriginalChecksum=4a1c25b04ba539eb0999241910c51cc6 (do not edit this line) */
Index: trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/parser/SimpleCharStream.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/parser/SimpleCharStream.java	(revision 3848)
+++ trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/parser/SimpleCharStream.java	(revision 3848)
@@ -0,0 +1,472 @@
+/* Generated By:JavaCC: Do not edit this line. SimpleCharStream.java Version 4.1 */
+/* JavaCCOptions:STATIC=false */
+package org.openstreetmap.josm.gui.mappaint.mapcss.parser;
+
+/**
+ * An implementation of interface CharStream, where the stream is assumed to
+ * contain only ASCII characters (without unicode processing).
+ */
+
+public class SimpleCharStream
+{
+/** Whether parser is static. */
+  public static final boolean staticFlag = false;
+  int bufsize;
+  int available;
+  int tokenBegin;
+/** Position in buffer. */
+  public int bufpos = -1;
+  protected int bufline[];
+  protected int bufcolumn[];
+
+  protected int column = 0;
+  protected int line = 1;
+
+  protected boolean prevCharIsCR = false;
+  protected boolean prevCharIsLF = false;
+
+  protected java.io.Reader inputStream;
+
+  protected char[] buffer;
+  protected int maxNextCharInd = 0;
+  protected int inBuf = 0;
+  protected int tabSize = 8;
+
+  protected void setTabSize(int i) { tabSize = i; }
+  protected int getTabSize(int i) { return tabSize; }
+
+
+  protected void ExpandBuff(boolean wrapAround)
+  {
+     char[] newbuffer = new char[bufsize + 2048];
+     int newbufline[] = new int[bufsize + 2048];
+     int newbufcolumn[] = new int[bufsize + 2048];
+
+     try
+     {
+        if (wrapAround)
+        {
+           System.arraycopy(buffer, tokenBegin, newbuffer, 0, bufsize - tokenBegin);
+           System.arraycopy(buffer, 0, newbuffer,
+                                             bufsize - tokenBegin, bufpos);
+           buffer = newbuffer;
+
+           System.arraycopy(bufline, tokenBegin, newbufline, 0, bufsize - tokenBegin);
+           System.arraycopy(bufline, 0, newbufline, bufsize - tokenBegin, bufpos);
+           bufline = newbufline;
+
+           System.arraycopy(bufcolumn, tokenBegin, newbufcolumn, 0, bufsize - tokenBegin);
+           System.arraycopy(bufcolumn, 0, newbufcolumn, bufsize - tokenBegin, bufpos);
+           bufcolumn = newbufcolumn;
+
+           maxNextCharInd = (bufpos += (bufsize - tokenBegin));
+        }
+        else
+        {
+           System.arraycopy(buffer, tokenBegin, newbuffer, 0, bufsize - tokenBegin);
+           buffer = newbuffer;
+
+           System.arraycopy(bufline, tokenBegin, newbufline, 0, bufsize - tokenBegin);
+           bufline = newbufline;
+
+           System.arraycopy(bufcolumn, tokenBegin, newbufcolumn, 0, bufsize - tokenBegin);
+           bufcolumn = newbufcolumn;
+
+           maxNextCharInd = (bufpos -= tokenBegin);
+        }
+     }
+     catch (Throwable t)
+     {
+        throw new Error(t.getMessage());
+     }
+
+
+     bufsize += 2048;
+     available = bufsize;
+     tokenBegin = 0;
+  }
+
+  protected void FillBuff() throws java.io.IOException
+  {
+     if (maxNextCharInd == available)
+     {
+        if (available == bufsize)
+        {
+           if (tokenBegin > 2048)
+           {
+              bufpos = maxNextCharInd = 0;
+              available = tokenBegin;
+           }
+           else if (tokenBegin < 0)
+              bufpos = maxNextCharInd = 0;
+           else
+              ExpandBuff(false);
+        }
+        else if (available > tokenBegin)
+           available = bufsize;
+        else if ((tokenBegin - available) < 2048)
+           ExpandBuff(true);
+        else
+           available = tokenBegin;
+     }
+
+     int i;
+     try {
+        if ((i = inputStream.read(buffer, maxNextCharInd,
+                                    available - maxNextCharInd)) == -1)
+        {
+           inputStream.close();
+           throw new java.io.IOException();
+        }
+        else
+           maxNextCharInd += i;
+        return;
+     }
+     catch(java.io.IOException e) {
+        --bufpos;
+        backup(0);
+        if (tokenBegin == -1)
+           tokenBegin = bufpos;
+        throw e;
+     }
+  }
+
+/** Start. */
+  public char BeginToken() throws java.io.IOException
+  {
+     tokenBegin = -1;
+     char c = readChar();
+     tokenBegin = bufpos;
+
+     return c;
+  }
+
+  protected void UpdateLineColumn(char c)
+  {
+     column++;
+
+     if (prevCharIsLF)
+     {
+        prevCharIsLF = false;
+        line += (column = 1);
+     }
+     else if (prevCharIsCR)
+     {
+        prevCharIsCR = false;
+        if (c == '\n')
+        {
+           prevCharIsLF = true;
+        }
+        else
+           line += (column = 1);
+     }
+
+     switch (c)
+     {
+        case '\r' :
+           prevCharIsCR = true;
+           break;
+        case '\n' :
+           prevCharIsLF = true;
+           break;
+        case '\t' :
+           column--;
+           column += (tabSize - (column % tabSize));
+           break;
+        default :
+           break;
+     }
+
+     bufline[bufpos] = line;
+     bufcolumn[bufpos] = column;
+  }
+
+/** Read a character. */
+  public char readChar() throws java.io.IOException
+  {
+     if (inBuf > 0)
+     {
+        --inBuf;
+
+        if (++bufpos == bufsize)
+           bufpos = 0;
+
+        return buffer[bufpos];
+     }
+
+     if (++bufpos >= maxNextCharInd)
+        FillBuff();
+
+     char c = buffer[bufpos];
+
+     UpdateLineColumn(c);
+     return c;
+  }
+
+  /**
+   * @deprecated 
+   * @see #getEndColumn
+   */
+
+  public int getColumn() {
+     return bufcolumn[bufpos];
+  }
+
+  /**
+   * @deprecated 
+   * @see #getEndLine
+   */
+
+  public int getLine() {
+     return bufline[bufpos];
+  }
+
+  /** Get token end column number. */
+  public int getEndColumn() {
+     return bufcolumn[bufpos];
+  }
+
+  /** Get token end line number. */
+  public int getEndLine() {
+     return bufline[bufpos];
+  }
+
+  /** Get token beginning column number. */
+  public int getBeginColumn() {
+     return bufcolumn[tokenBegin];
+  }
+
+  /** Get token beginning line number. */
+  public int getBeginLine() {
+     return bufline[tokenBegin];
+  }
+
+/** Backup a number of characters. */
+  public void backup(int amount) {
+
+    inBuf += amount;
+    if ((bufpos -= amount) < 0)
+       bufpos += bufsize;
+  }
+
+  /** Constructor. */
+  public SimpleCharStream(java.io.Reader dstream, int startline,
+  int startcolumn, int buffersize)
+  {
+    inputStream = dstream;
+    line = startline;
+    column = startcolumn - 1;
+
+    available = bufsize = buffersize;
+    buffer = new char[buffersize];
+    bufline = new int[buffersize];
+    bufcolumn = new int[buffersize];
+  }
+
+  /** Constructor. */
+  public SimpleCharStream(java.io.Reader dstream, int startline,
+                          int startcolumn)
+  {
+     this(dstream, startline, startcolumn, 4096);
+  }
+
+  /** Constructor. */
+  public SimpleCharStream(java.io.Reader dstream)
+  {
+     this(dstream, 1, 1, 4096);
+  }
+
+  /** Reinitialise. */
+  public void ReInit(java.io.Reader dstream, int startline,
+  int startcolumn, int buffersize)
+  {
+    inputStream = dstream;
+    line = startline;
+    column = startcolumn - 1;
+
+    if (buffer == null || buffersize != buffer.length)
+    {
+      available = bufsize = buffersize;
+      buffer = new char[buffersize];
+      bufline = new int[buffersize];
+      bufcolumn = new int[buffersize];
+    }
+    prevCharIsLF = prevCharIsCR = false;
+    tokenBegin = inBuf = maxNextCharInd = 0;
+    bufpos = -1;
+  }
+
+  /** Reinitialise. */
+  public void ReInit(java.io.Reader dstream, int startline,
+                     int startcolumn)
+  {
+     ReInit(dstream, startline, startcolumn, 4096);
+  }
+
+  /** Reinitialise. */
+  public void ReInit(java.io.Reader dstream)
+  {
+     ReInit(dstream, 1, 1, 4096);
+  }
+  /** Constructor. */
+  public SimpleCharStream(java.io.InputStream dstream, String encoding, int startline,
+  int startcolumn, int buffersize) throws java.io.UnsupportedEncodingException
+  {
+     this(encoding == null ? new java.io.InputStreamReader(dstream) : new java.io.InputStreamReader(dstream, encoding), startline, startcolumn, buffersize);
+  }
+
+  /** Constructor. */
+  public SimpleCharStream(java.io.InputStream dstream, int startline,
+  int startcolumn, int buffersize)
+  {
+     this(new java.io.InputStreamReader(dstream), startline, startcolumn, buffersize);
+  }
+
+  /** Constructor. */
+  public SimpleCharStream(java.io.InputStream dstream, String encoding, int startline,
+                          int startcolumn) throws java.io.UnsupportedEncodingException
+  {
+     this(dstream, encoding, startline, startcolumn, 4096);
+  }
+
+  /** Constructor. */
+  public SimpleCharStream(java.io.InputStream dstream, int startline,
+                          int startcolumn)
+  {
+     this(dstream, startline, startcolumn, 4096);
+  }
+
+  /** Constructor. */
+  public SimpleCharStream(java.io.InputStream dstream, String encoding) throws java.io.UnsupportedEncodingException
+  {
+     this(dstream, encoding, 1, 1, 4096);
+  }
+
+  /** Constructor. */
+  public SimpleCharStream(java.io.InputStream dstream)
+  {
+     this(dstream, 1, 1, 4096);
+  }
+
+  /** Reinitialise. */
+  public void ReInit(java.io.InputStream dstream, String encoding, int startline,
+                          int startcolumn, int buffersize) throws java.io.UnsupportedEncodingException
+  {
+     ReInit(encoding == null ? new java.io.InputStreamReader(dstream) : new java.io.InputStreamReader(dstream, encoding), startline, startcolumn, buffersize);
+  }
+
+  /** Reinitialise. */
+  public void ReInit(java.io.InputStream dstream, int startline,
+                          int startcolumn, int buffersize)
+  {
+     ReInit(new java.io.InputStreamReader(dstream), startline, startcolumn, buffersize);
+  }
+
+  /** Reinitialise. */
+  public void ReInit(java.io.InputStream dstream, String encoding) throws java.io.UnsupportedEncodingException
+  {
+     ReInit(dstream, encoding, 1, 1, 4096);
+  }
+
+  /** Reinitialise. */
+  public void ReInit(java.io.InputStream dstream)
+  {
+     ReInit(dstream, 1, 1, 4096);
+  }
+  /** Reinitialise. */
+  public void ReInit(java.io.InputStream dstream, String encoding, int startline,
+                     int startcolumn) throws java.io.UnsupportedEncodingException
+  {
+     ReInit(dstream, encoding, startline, startcolumn, 4096);
+  }
+  /** Reinitialise. */
+  public void ReInit(java.io.InputStream dstream, int startline,
+                     int startcolumn)
+  {
+     ReInit(dstream, startline, startcolumn, 4096);
+  }
+  /** Get token literal value. */
+  public String GetImage()
+  {
+     if (bufpos >= tokenBegin)
+        return new String(buffer, tokenBegin, bufpos - tokenBegin + 1);
+     else
+        return new String(buffer, tokenBegin, bufsize - tokenBegin) +
+                              new String(buffer, 0, bufpos + 1);
+  }
+
+  /** Get the suffix. */
+  public char[] GetSuffix(int len)
+  {
+     char[] ret = new char[len];
+
+     if ((bufpos + 1) >= len)
+        System.arraycopy(buffer, bufpos - len + 1, ret, 0, len);
+     else
+     {
+        System.arraycopy(buffer, bufsize - (len - bufpos - 1), ret, 0,
+                                                          len - bufpos - 1);
+        System.arraycopy(buffer, 0, ret, len - bufpos - 1, bufpos + 1);
+     }
+
+     return ret;
+  }
+
+  /** Reset buffer when finished. */
+  public void Done()
+  {
+     buffer = null;
+     bufline = null;
+     bufcolumn = null;
+  }
+
+  /**
+   * Method to adjust line and column numbers for the start of a token.
+   */
+  public void adjustBeginLineColumn(int newLine, int newCol)
+  {
+     int start = tokenBegin;
+     int len;
+
+     if (bufpos >= tokenBegin)
+     {
+        len = bufpos - tokenBegin + inBuf + 1;
+     }
+     else
+     {
+        len = bufsize - tokenBegin + bufpos + 1 + inBuf;
+     }
+
+     int i = 0, j = 0, k = 0;
+     int nextColDiff = 0, columnDiff = 0;
+
+     while (i < len &&
+            bufline[j = start % bufsize] == bufline[k = ++start % bufsize])
+     {
+        bufline[j] = newLine;
+        nextColDiff = columnDiff + bufcolumn[k] - bufcolumn[j];
+        bufcolumn[j] = newCol + columnDiff;
+        columnDiff = nextColDiff;
+        i++;
+     } 
+
+     if (i < len)
+     {
+        bufline[j] = newLine++;
+        bufcolumn[j] = newCol + columnDiff;
+
+        while (i++ < len)
+        {
+           if (bufline[j = start % bufsize] != bufline[++start % bufsize])
+              bufline[j] = newLine++;
+           else
+              bufline[j] = newLine;
+        }
+     }
+
+     line = bufline[j];
+     column = bufcolumn[j];
+  }
+
+}
+/* JavaCC - OriginalChecksum=7a2fdaf13db0db2fa57514d3348bedd2 (do not edit this line) */
Index: trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/parser/Token.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/parser/Token.java	(revision 3848)
+++ trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/parser/Token.java	(revision 3848)
@@ -0,0 +1,124 @@
+/* Generated By:JavaCC: Do not edit this line. Token.java Version 4.1 */
+/* JavaCCOptions:TOKEN_EXTENDS=,KEEP_LINE_COL=null */
+package org.openstreetmap.josm.gui.mappaint.mapcss.parser;
+
+/**
+ * Describes the input token stream.
+ */
+
+public class Token {
+
+  /**
+   * An integer that describes the kind of this token.  This numbering
+   * system is determined by JavaCCParser, and a table of these numbers is
+   * stored in the file ...Constants.java.
+   */
+  public int kind;
+
+  /** The line number of the first character of this Token. */
+  public int beginLine;
+  /** The column number of the first character of this Token. */
+  public int beginColumn;
+  /** The line number of the last character of this Token. */
+  public int endLine;
+  /** The column number of the last character of this Token. */
+  public int endColumn;
+
+  /**
+   * The string image of the token.
+   */
+  public String image;
+
+  /**
+   * A reference to the next regular (non-special) token from the input
+   * stream.  If this is the last token from the input stream, or if the
+   * token manager has not read tokens beyond this one, this field is
+   * set to null.  This is true only if this token is also a regular
+   * token.  Otherwise, see below for a description of the contents of
+   * this field.
+   */
+  public Token next;
+
+  /**
+   * This field is used to access special tokens that occur prior to this
+   * token, but after the immediately preceding regular (non-special) token.
+   * If there are no such special tokens, this field is set to null.
+   * When there are more than one such special token, this field refers
+   * to the last of these special tokens, which in turn refers to the next
+   * previous special token through its specialToken field, and so on
+   * until the first special token (whose specialToken field is null).
+   * The next fields of special tokens refer to other special tokens that
+   * immediately follow it (without an intervening regular token).  If there
+   * is no such token, this field is null.
+   */
+  public Token specialToken;
+
+  /**
+   * An optional attribute value of the Token.
+   * Tokens which are not used as syntactic sugar will often contain
+   * meaningful values that will be used later on by the compiler or
+   * interpreter. This attribute value is often different from the image.
+   * Any subclass of Token that actually wants to return a non-null value can
+   * override this method as appropriate.
+   */
+  public Object getValue() {
+    return null;
+  }
+
+  /**
+   * No-argument constructor
+   */
+  public Token() {}
+
+  /**
+   * Constructs a new token for the specified Image.
+   */
+  public Token(int kind)
+  {
+     this(kind, null);
+  }
+
+  /**
+   * Constructs a new token for the specified Image and Kind.
+   */
+  public Token(int kind, String image)
+  {
+     this.kind = kind;
+     this.image = image;
+  }
+
+  /**
+   * Returns the image.
+   */
+  public String toString()
+  {
+     return image;
+  }
+
+  /**
+   * Returns a new Token object, by default. However, if you want, you
+   * can create and return subclass objects based on the value of ofKind.
+   * Simply add the cases to the switch for all those special cases.
+   * For example, if you have a subclass of Token called IDToken that
+   * you want to create if ofKind is ID, simply add something like :
+   *
+   *    case MyParserConstants.ID : return new IDToken(ofKind, image);
+   *
+   * to the following switch statement. Then you can cast matchedToken
+   * variable to the appropriate type and use sit in your lexical actions.
+   */
+  public static Token newToken(int ofKind, String image)
+  {
+     switch(ofKind)
+     {
+       default : return new Token(ofKind, image);
+     }
+  }
+
+  public static Token newToken(int ofKind)
+  {
+     return newToken(ofKind, null);
+  }
+
+}
+/* JavaCC - OriginalChecksum=2700a5d4bead55b977510f47df3d1aa9 (do not edit this line) */
Index: trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/parser/TokenMgrError.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/parser/TokenMgrError.java	(revision 3848)
+++ trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/parser/TokenMgrError.java	(revision 3848)
@@ -0,0 +1,140 @@
+/* Generated By:JavaCC: Do not edit this line. TokenMgrError.java Version 4.1 */
+/* JavaCCOptions: */
+package org.openstreetmap.josm.gui.mappaint.mapcss.parser;
+
+/** Token Manager Error. */
+public class TokenMgrError extends Error
+{
+
+   /*
+    * Ordinals for various reasons why an Error of this type can be thrown.
+    */
+
+   /**
+    * Lexical error occurred.
+    */
+   static final int LEXICAL_ERROR = 0;
+
+   /**
+    * An attempt was made to create a second instance of a static token manager.
+    */
+   static final int STATIC_LEXER_ERROR = 1;
+
+   /**
+    * Tried to change to an invalid lexical state.
+    */
+   static final int INVALID_LEXICAL_STATE = 2;
+
+   /**
+    * Detected (and bailed out of) an infinite loop in the token manager.
+    */
+   static final int LOOP_DETECTED = 3;
+
+   /**
+    * Indicates the reason why the exception is thrown. It will have
+    * one of the above 4 values.
+    */
+   int errorCode;
+
+   /**
+    * Replaces unprintable characters by their escaped (or unicode escaped)
+    * equivalents in the given string
+    */
+   protected static final String addEscapes(String str) {
+      StringBuffer retval = new StringBuffer();
+      char ch;
+      for (int i = 0; i < str.length(); i++) {
+        switch (str.charAt(i))
+        {
+           case 0 :
+              continue;
+           case '\b':
+              retval.append("\\b");
+              continue;
+           case '\t':
+              retval.append("\\t");
+              continue;
+           case '\n':
+              retval.append("\\n");
+              continue;
+           case '\f':
+              retval.append("\\f");
+              continue;
+           case '\r':
+              retval.append("\\r");
+              continue;
+           case '\"':
+              retval.append("\\\"");
+              continue;
+           case '\'':
+              retval.append("\\\'");
+              continue;
+           case '\\':
+              retval.append("\\\\");
+              continue;
+           default:
+              if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e) {
+                 String s = "0000" + Integer.toString(ch, 16);
+                 retval.append("\\u" + s.substring(s.length() - 4, s.length()));
+              } else {
+                 retval.append(ch);
+              }
+              continue;
+        }
+      }
+      return retval.toString();
+   }
+
+   /**
+    * Returns a detailed message for the Error when it is thrown by the
+    * token manager to indicate a lexical error.
+    * Parameters : 
+    *    EOFSeen     : indicates if EOF caused the lexical error
+    *    curLexState : lexical state in which this error occurred
+    *    errorLine   : line number when the error occurred
+    *    errorColumn : column number when the error occurred
+    *    errorAfter  : prefix that was seen before this error occurred
+    *    curchar     : the offending character
+    * Note: You can customize the lexical error message by modifying this method.
+    */
+   protected static String LexicalError(boolean EOFSeen, int lexState, int errorLine, int errorColumn, String errorAfter, char curChar) {
+      return("Lexical error at line " +
+           errorLine + ", column " +
+           errorColumn + ".  Encountered: " +
+           (EOFSeen ? "<EOF> " : ("\"" + addEscapes(String.valueOf(curChar)) + "\"") + " (" + (int)curChar + "), ") +
+           "after : \"" + addEscapes(errorAfter) + "\"");
+   }
+
+   /**
+    * You can also modify the body of this method to customize your error messages.
+    * For example, cases like LOOP_DETECTED and INVALID_LEXICAL_STATE are not
+    * of end-users concern, so you can return something like : 
+    *
+    *     "Internal Error : Please file a bug report .... "
+    *
+    * from this method for such cases in the release version of your parser.
+    */
+   public String getMessage() {
+      return super.getMessage();
+   }
+
+   /*
+    * Constructors of various flavors follow.
+    */
+
+   /** No arg constructor. */
+   public TokenMgrError() {
+   }
+
+   /** Constructor with message and reason. */
+   public TokenMgrError(String message, int reason) {
+      super(message);
+      errorCode = reason;
+   }
+
+   /** Full Constructor. */
+   public TokenMgrError(boolean EOFSeen, int lexState, int errorLine, int errorColumn, String errorAfter, char curChar, int reason) {
+      this(LexicalError(EOFSeen, lexState, errorLine, errorColumn, errorAfter, curChar), reason);
+   }
+}
+/* JavaCC - OriginalChecksum=b019ea8f6cda291b564c5f8df18b3ab6 (do not edit this line) */
Index: trunk/src/org/openstreetmap/josm/gui/mappaint/xml/XmlStyleSource.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/mappaint/xml/XmlStyleSource.java	(revision 3847)
+++ trunk/src/org/openstreetmap/josm/gui/mappaint/xml/XmlStyleSource.java	(revision 3848)
@@ -32,12 +32,12 @@
 public class XmlStyleSource extends StyleSource {
 
-    public final HashMap<String, IconPrototype> icons = new HashMap<String, IconPrototype>();
-    public final HashMap<String, LinePrototype> lines = new HashMap<String, LinePrototype>();
-    public final HashMap<String, LinemodPrototype> modifiers = new HashMap<String, LinemodPrototype>();
-    public final HashMap<String, AreaPrototype> areas = new HashMap<String, AreaPrototype>();
-    public final LinkedList<IconPrototype> iconsList = new LinkedList<IconPrototype>();
-    public final LinkedList<LinePrototype> linesList = new LinkedList<LinePrototype>();
-    public final LinkedList<LinemodPrototype> modifiersList = new LinkedList<LinemodPrototype>();
-    public final LinkedList<AreaPrototype> areasList = new LinkedList<AreaPrototype>();
+    protected final HashMap<String, IconPrototype> icons = new HashMap<String, IconPrototype>();
+    protected final HashMap<String, LinePrototype> lines = new HashMap<String, LinePrototype>();
+    protected final HashMap<String, LinemodPrototype> modifiers = new HashMap<String, LinemodPrototype>();
+    protected final HashMap<String, AreaPrototype> areas = new HashMap<String, AreaPrototype>();
+    protected final LinkedList<IconPrototype> iconsList = new LinkedList<IconPrototype>();
+    protected final LinkedList<LinePrototype> linesList = new LinkedList<LinePrototype>();
+    protected final LinkedList<LinemodPrototype> modifiersList = new LinkedList<LinemodPrototype>();
+    protected final LinkedList<AreaPrototype> areasList = new LinkedList<AreaPrototype>();
 
     public XmlStyleSource(String url, String name, String shortdescription) {
@@ -49,6 +49,18 @@
     }
 
+    protected void init() {
+        icons.clear();
+        lines.clear();
+        modifiers.clear();
+        areas.clear();
+        iconsList.clear();
+        linesList.clear();
+        modifiersList.clear();
+        areasList.clear();
+    }
+
     @Override
     public void loadStyleSource() {
+        init();
         try {
             MirroredInputStream in = new MirroredInputStream(url);
@@ -57,6 +69,8 @@
             if (zip != null) {
                 reader = new InputStreamReader(zip);
+                zipIcons = in.getFile();
             } else {
                 reader = new InputStreamReader(in);
+                zipIcons = null;
             }
 
@@ -288,5 +302,5 @@
                 def.putOrClear("dashes-background-color", p.line.dashedColor);
             }
-            Float refWidth = def.get("width", null, Float.class);
+            Float refWidth = def.getFloat("width", null);
             if (refWidth != null && p.linemods != null) {
                 int numOver = 0, numUnder = 0;
Index: trunk/src/org/openstreetmap/josm/gui/preferences/MapPaintPreference.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/preferences/MapPaintPreference.java	(revision 3847)
+++ trunk/src/org/openstreetmap/josm/gui/preferences/MapPaintPreference.java	(revision 3848)
@@ -21,6 +21,4 @@
 
 import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.data.osm.OsmPrimitive;
-import org.openstreetmap.josm.gui.layer.OsmDataLayer;
 import org.openstreetmap.josm.gui.mappaint.MapPaintStyles;
 import org.openstreetmap.josm.gui.preferences.SourceEditor.ExtendedSourceEntry;
@@ -30,5 +28,4 @@
     private SourceEditor sources;
     private JCheckBox enableIconDefault;
-    private JCheckBox enableDefault;
     private JComboBox styleCombo = new JComboBox();
 
@@ -40,6 +37,4 @@
 
     public void addGui(final PreferenceTabbedPane gui) {
-        enableDefault = new JCheckBox(tr("Enable built-in defaults"),
-                Main.pref.getBoolean("mappaint.style.enable-defaults", true));
         enableIconDefault = new JCheckBox(tr("Enable built-in icon defaults"),
                 Main.pref.getBoolean("mappaint.icon.enable-defaults", true));
@@ -168,5 +163,5 @@
 
     public boolean ok() {
-        Boolean restart = Main.pref.put("mappaint.style.enable-defaults", enableDefault.isSelected());
+        Boolean restart = false;
         if(Main.pref.put("mappaint.icon.enable-defaults", enableIconDefault.isSelected())) {
             restart = true;
@@ -178,11 +173,5 @@
         && Main.isDisplayingMapView())
         {
-          for(OsmDataLayer l : Main.map.mapView.getLayersOfType(OsmDataLayer.class))
-          {
-            for(OsmPrimitive osm : l.data.allPrimitives())
-            {
-              osm.clearCached();
-            }
-          }
+            MapPaintStyles.getStyles().clearCached();
         }
         return restart;
Index: trunk/src/org/openstreetmap/josm/tools/Utils.java
===================================================================
--- trunk/src/org/openstreetmap/josm/tools/Utils.java	(revision 3847)
+++ trunk/src/org/openstreetmap/josm/tools/Utils.java	(revision 3848)
@@ -1,4 +1,6 @@
 // License: GPL. For details, see LICENSE file.
 package org.openstreetmap.josm.tools;
+
+import java.util.Collection;
 
 public class Utils {
@@ -80,3 +82,31 @@
     }
 
+    /**
+     * Joins a list of strings (or objects that can be converted to string via
+     * Object.toString()) into a single string with fields separated by sep.
+     * @param sep the separator
+     * @param values collection of objects, null is converted to the
+     *  empty string
+     * @return null if values is null. The joined string otherwise.
+     */
+    public static String join(String sep, Collection<?> values) {
+        if (sep == null)
+            throw new IllegalArgumentException();
+        if (values == null)
+            return null;
+        if (values.isEmpty())
+            return "";
+        StringBuilder s = null;
+        for (Object a : values) {
+            if (a == null) {
+                a = "";
+            }
+            if(s != null) {
+                s.append(sep).append(a.toString());
+            } else {
+                s = new StringBuilder(a.toString());
+            }
+        }
+        return s.toString();
+    }
 }
