Index: trunk/src/org/openstreetmap/josm/data/osm/OsmPrimitive.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/OsmPrimitive.java	(revision 3835)
+++ trunk/src/org/openstreetmap/josm/data/osm/OsmPrimitive.java	(revision 3836)
@@ -300,5 +300,4 @@
      *--------*/
     public StyleCache mappaintStyle = null;
-    public int mappaintDrawnCode = 0;
 
     /* This should not be called from outside. Fixing the UI to add relevant
@@ -307,5 +306,4 @@
     public void clearCached()
     {
-        mappaintDrawnCode = 0;
         mappaintStyle = null;
     }
@@ -808,4 +806,16 @@
     public boolean isSelected() {
         return dataSet != null && dataSet.isSelected(this);
+    }
+
+    public boolean isMemberOfSelected() {
+        if (referrers == null)
+            return false;
+        if (referrers instanceof OsmPrimitive)
+            return referrers instanceof Relation && ((OsmPrimitive) referrers).isSelected();
+        for (OsmPrimitive ref : (OsmPrimitive[]) referrers) {
+            if (ref instanceof Relation && ref.isSelected())
+                return true;
+        }
+        return false;
     }
 
Index: trunk/src/org/openstreetmap/josm/data/osm/Way.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/Way.java	(revision 3835)
+++ trunk/src/org/openstreetmap/josm/data/osm/Way.java	(revision 3836)
@@ -110,14 +110,4 @@
         }
         return false;
-    }
-
-    /* mappaint data */
-    public boolean isMappaintArea = false;
-    public Integer mappaintDrawnAreaCode = 0;
-    /* end of mappaint data */
-    @Override public void clearCached() {
-        super.clearCached();
-        isMappaintArea = false;
-        mappaintDrawnAreaCode = 0;
     }
 
Index: trunk/src/org/openstreetmap/josm/data/osm/visitor/paint/MapPaintVisitor.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/visitor/paint/MapPaintVisitor.java	(revision 3835)
+++ trunk/src/org/openstreetmap/josm/data/osm/visitor/paint/MapPaintVisitor.java	(revision 3836)
@@ -3,18 +3,12 @@
 
 import java.awt.Graphics2D;
-import java.awt.Polygon;
-import java.awt.Rectangle;
 import java.awt.RenderingHints;
-import java.awt.geom.Point2D;
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
-import java.util.LinkedList;
+import java.util.List;
 
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.Bounds;
-import org.openstreetmap.josm.data.coor.EastNorth;
-import org.openstreetmap.josm.data.coor.LatLon;
 import org.openstreetmap.josm.data.osm.BBox;
 import org.openstreetmap.josm.data.osm.DataSet;
@@ -22,359 +16,95 @@
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
 import org.openstreetmap.josm.data.osm.Relation;
-import org.openstreetmap.josm.data.osm.RelationMember;
 import org.openstreetmap.josm.data.osm.Way;
-import org.openstreetmap.josm.data.osm.visitor.AbstractVisitor;
-import org.openstreetmap.josm.data.osm.visitor.paint.relations.Multipolygon;
-import org.openstreetmap.josm.data.osm.visitor.paint.relations.Multipolygon.PolyData;
 import org.openstreetmap.josm.gui.NavigatableComponent;
 import org.openstreetmap.josm.gui.mappaint.AreaElemStyle;
 import org.openstreetmap.josm.gui.mappaint.ElemStyle;
 import org.openstreetmap.josm.gui.mappaint.ElemStyles;
-import org.openstreetmap.josm.gui.mappaint.IconElemStyle;
 import org.openstreetmap.josm.gui.mappaint.LineElemStyle;
 import org.openstreetmap.josm.gui.mappaint.MapPaintStyles;
-import org.openstreetmap.josm.gui.mappaint.StyleCache;
+import org.openstreetmap.josm.gui.mappaint.NodeElemStyle;
+import org.openstreetmap.josm.gui.mappaint.StyleCache.StyleList;
+import org.openstreetmap.josm.tools.Pair;
 
 public class MapPaintVisitor implements PaintVisitor {
 
     private Graphics2D g;
+    private boolean inactive;
     private NavigatableComponent nc;
 
-    private boolean zoomLevelDisplay;
-    private boolean drawMultipolygon;
-    private boolean drawRestriction;
-    private boolean leftHandTraffic;
     private ElemStyles styles;
     private double circum;
-    private double dist;
-    private static int paintid = 0;
-    private EastNorth minEN;
-    private EastNorth maxEN;
     private MapPainter painter;
     private MapPaintSettings paintSettings;
-
-    private boolean inactive;
-
-    protected boolean isZoomOk(ElemStyle e) {
-        if (!zoomLevelDisplay) /* show everything if the user wishes so */
-            return true;
-
-        if(e == null) /* the default for things that don't have a rule (show, if scale is smaller than 1500m) */
-            return (circum < 1500);
-
-        return !(circum >= e.maxScale || circum < e.minScale);
-    }
-
-    public StyleCache getPrimitiveStyle(OsmPrimitive osm, boolean nodefault) {
-        if(osm.mappaintStyle == null)
-        {
-            if(styles != null) {
-                osm.mappaintStyle = styles.get(osm);
-                if(osm instanceof Way) {
-                    ((Way)osm).isMappaintArea = styles.isArea(osm);
-                }
-            }
-            if (osm.mappaintStyle.equals(StyleCache.EMPTY_STYLECACHE)) {
-                if(osm instanceof Node)
-                    osm.mappaintStyle = StyleCache.SIMPLE_NODE_STYLECACHE;
-                else if (osm instanceof Way)
-                    osm.mappaintStyle = StyleCache.UNTAGGED_WAY_STYLECACHE;
-            }
-        }
-        if (nodefault && osm.mappaintStyle.equals(StyleCache.UNTAGGED_WAY_STYLECACHE))
-            return StyleCache.EMPTY_STYLECACHE;
-        return osm.mappaintStyle;
-    }
-
-    public IconElemStyle getPrimitiveNodeStyle(OsmPrimitive osm) {
-        if(osm.mappaintStyle == null && styles != null) {
-            IconElemStyle icon = styles.getIcon(osm);
-            osm.mappaintStyle = StyleCache.create(icon);
-            return icon;
-        }
-        for (ElemStyle s : osm.mappaintStyle.getStyles()) {
-            if (s instanceof IconElemStyle)
-                return (IconElemStyle) s;
-        }
-        return null;
-    }
-
-    public boolean isPrimitiveArea(Way osm) {
-        if(osm.mappaintStyle == null && styles != null) {
-            osm.mappaintStyle = styles.get(osm);
-            osm.isMappaintArea = styles.isArea(osm);
-        }
-        return osm.isMappaintArea;
-    }
-
-    public void drawNode(Node n) {
-        /* check, if the node is visible at all */
-        EastNorth en = n.getEastNorth();
-        if((en.east()  > maxEN.east() ) ||
-                (en.north() > maxEN.north()) ||
-                (en.east()  < minEN.east() ) ||
-                (en.north() < minEN.north()))
-            return;
-
-        StyleCache sc = getPrimitiveStyle(n, false);
-
-        for (ElemStyle s : sc.getStyles()) {
-            if (isZoomOk(s)) {
-                s.paintPrimitive(n, paintSettings, painter, data.isSelected(n), false);
-            }
-
-        }
-    }
-
-    public void drawWay(Way w, int fillAreas) {
-        if(w.getNodesCount() < 2)
-            return;
-
-        if (w.hasIncompleteNodes())
-            return;
-
-        /* check, if the way is visible at all */
-        double minx = 10000;
-        double maxx = -10000;
-        double miny = 10000;
-        double maxy = -10000;
-
-        for (Node n : w.getNodes())
-        {
-            if(n.getEastNorth().east() > maxx) {
-                maxx = n.getEastNorth().east();
-            }
-            if(n.getEastNorth().north() > maxy) {
-                maxy = n.getEastNorth().north();
-            }
-            if(n.getEastNorth().east() < minx) {
-                minx = n.getEastNorth().east();
-            }
-            if(n.getEastNorth().north() < miny) {
-                miny = n.getEastNorth().north();
-            }
-        }
-
-        if ((minx > maxEN.east()) ||
-                (miny > maxEN.north()) ||
-                (maxx < minEN.east()) ||
-                (maxy < minEN.north()))
-            return;
-
-        StyleCache sc = getPrimitiveStyle(w, false);
-        for (ElemStyle s : sc.getStyles()) {
-            if(!isZoomOk(s))
-                return;
-            if (fillAreas > dist || !(s instanceof AreaElemStyle)) {
-                s.paintPrimitive(w, paintSettings, painter, data.isSelected(w), false);
-            }
-        }
-    }
-
-    public void paintUnselectedRelation(Relation r) {
-        if (drawMultipolygon && "multipolygon".equals(r.get("type")))
-            drawMultipolygon(r);
-        else if (drawRestriction && "restriction".equals(r.get("type"))) {
-            IconElemStyle nodeStyle = getPrimitiveNodeStyle(r);
-            if (nodeStyle != null) {
-                painter.drawRestriction(r, leftHandTraffic, nodeStyle);
-            }
-        }
-    }
-
-    public boolean drawMultipolygon(Relation r) {
-        boolean drawn = false;
-
-        Multipolygon multipolygon = new Multipolygon(nc);
-        multipolygon.load(r);
-
-        AreaElemStyle areaStyle = null;
-        LineElemStyle lineStyle = null;
-        for (ElemStyle s : getPrimitiveStyle(r, false).getStyles()) {
-            if (s instanceof AreaElemStyle) {
-                areaStyle = (AreaElemStyle) s;
-            } else if (s instanceof LineElemStyle) {
-                lineStyle = (LineElemStyle) s;
-            }
-        }
-
-        boolean disabled = r.isDisabled();
-        // If area style was not found for relation then use style of ways
-        if(styles != null && areaStyle == null) {
-            for (Way w : multipolygon.getOuterWays()) {
-                for (ElemStyle s : styles.getArea(w).getStyles()) {
-                    if (s instanceof AreaElemStyle) {
-                        areaStyle = (AreaElemStyle) s;
-                    } else if (s instanceof LineElemStyle) {
-                        lineStyle = (LineElemStyle) s;
-                    }
-                }
-                disabled = disabled || w.isDisabled();
-                if(areaStyle != null) {
-                    break;
-                }
-            }
-        }
-
-        if (areaStyle != null) {
-            boolean zoomok = isZoomOk(areaStyle);
-            boolean visible = false;
-
-            drawn = true;
-
-            if(zoomok && !disabled && !multipolygon.getOuterWays().isEmpty()) {
-                for (PolyData pd : multipolygon.getCombinedPolygons()) {
-                    Polygon p = pd.get();
-                    if(!isPolygonVisible(p)) {
-                        continue;
-                    }
-
-                    boolean selected = pd.selected || data.isSelected(r);
-                    painter.drawArea(p, selected ? paintSettings.getRelationSelectedColor()
-                                : areaStyle.color, painter.getAreaName(r));
-                    visible = true;
-                }
-            }
-
-            if(!visible)
-                return drawn;
-            for (Way wInner : multipolygon.getInnerWays()) {
-                StyleCache inner = getPrimitiveStyle(wInner, true);
-                AreaElemStyle innerArea = null;
-                for (ElemStyle s : inner.getStyles()) {
-                    if (s instanceof AreaElemStyle) {
-                        innerArea = (AreaElemStyle) s;
-                        break;
-                    }
-                }
-
-                if(inner.getStyles().isEmpty()) {
-                    if (data.isSelected(wInner) || disabled)
-                        continue;
-                    if(zoomok && (wInner.mappaintDrawnCode != paintid || multipolygon.getOuterWays().isEmpty())) {
-                        lineStyle.paintPrimitive(wInner, paintSettings,
-                                painter, (data.isSelected(wInner) || data.isSelected(r)), false);
-                    }
-                    wInner.mappaintDrawnCode = paintid;
-                }
-                else {
-                    if(areaStyle.equals(innerArea)) {
-                        wInner.mappaintDrawnAreaCode = paintid;
-                        
-                        if(!data.isSelected(wInner)) {
-                            wInner.mappaintDrawnCode = paintid;
-                            drawWay(wInner, 0);
-                        }
-                    }
-                }
-            }
-            for (Way wOuter : multipolygon.getOuterWays()) {
-                StyleCache outer = getPrimitiveStyle(wOuter, true);
-                boolean hasOuterArea = false;
-                for (ElemStyle s : outer.getStyles()) {
-                    if (s instanceof AreaElemStyle) {
-                        hasOuterArea = true;
-                        break;
-                    }
-                }
-
-                if (outer.getStyles().isEmpty()) {
-                    // Selected ways are drawn at the very end
-                    if (data.isSelected(wOuter))
-                        continue;
-                    if(zoomok) {
-                        lineStyle.paintPrimitive(wOuter, paintSettings, painter,
-                            (data.isSelected(wOuter) || data.isSelected(r)), r.isSelected());
-                    }
-                    wOuter.mappaintDrawnCode = paintid;
-                } else if (hasOuterArea) {
-                    wOuter.mappaintDrawnAreaCode = paintid;
-                    if(!data.isSelected(wOuter)) {
-                        wOuter.mappaintDrawnCode = paintid;
-                        drawWay(wOuter, 0);
-                    }
-                }
-            }
-        }
-        return drawn;
-    }
-
-    protected boolean isPolygonVisible(Polygon polygon) {
-        Rectangle bounds = polygon.getBounds();
-        if (bounds.width == 0 && bounds.height == 0) return false;
-        if (bounds.x > nc.getWidth()) return false;
-        if (bounds.y > nc.getHeight()) return false;
-        if (bounds.x + bounds.width < 0) return false;
-        if (bounds.y + bounds.height < 0) return false;
-        return true;
-    }
-
-    protected Point2D getCentroid(Polygon p)
-    {
-        double cx = 0.0, cy = 0.0, a = 0.0;
-
-        // usually requires points[0] == points[npoints] and can then use i+1 instead of j.
-        // Faked it slowly using j.  If this is really gets used, this should be fixed.
-        for (int i = 0;  i < p.npoints;  i++) {
-            int j = i+1 == p.npoints ? 0 : i+1;
-            a += (p.xpoints[i] * p.ypoints[j]) - (p.ypoints[i] * p.xpoints[j]);
-            cx += (p.xpoints[i] + p.xpoints[j]) * (p.xpoints[i] * p.ypoints[j] - p.ypoints[i] * p.xpoints[j]);
-            cy += (p.ypoints[i] + p.ypoints[j]) * (p.xpoints[i] * p.ypoints[j] - p.ypoints[i] * p.xpoints[j]);
-        }
-        return new Point2D.Double(cx / (3.0*a), cy / (3.0*a));
-    }
-
-    protected double getArea(Polygon p)
-    {
-        double sum = 0.0;
-
-        // usually requires points[0] == points[npoints] and can then use i+1 instead of j.
-        // Faked it slowly using j.  If this is really gets used, this should be fixed.
-        for (int i = 0;  i < p.npoints;  i++) {
-            int j = i+1 == p.npoints ? 0 : i+1;
-            sum = sum + (p.xpoints[i] * p.ypoints[j]) - (p.ypoints[i] * p.xpoints[j]);
-        }
-        return Math.abs(sum/2.0);
-    }
-
-    DataSet data;
-
-    <T extends OsmPrimitive> Collection<T> selectedLast(final DataSet data, Collection <T> prims) {
-        ArrayList<T> sorted = new ArrayList<T>(prims);
-        Collections.sort(sorted,
-                new Comparator<T>() {
-            public int compare(T o1, T o2) {
-                boolean s1 = data.isSelected(o1);
-                boolean s2 = data.isSelected(o2);
-                if (s1 && !s2)
-                    return 1;
-                if (!s1 && s2)
-                    return -1;
-                return o1.compareTo(o2);
-            }
-        });
-        return sorted;
-    }
-
-    /* Shows areas before non-areas */
+    private DataSet data;
+
+    private class StyleCollector {
+        private List<Pair<ElemStyle, OsmPrimitive>> styleElems;
+        protected boolean memberSelected = false;
+        private Class klass;
+
+        public StyleCollector(Class<?> klass) {
+            styleElems = new ArrayList<Pair<ElemStyle, OsmPrimitive>>();
+            this.klass = klass;
+        }
+
+        public void add(OsmPrimitive osm) {
+            StyleList sl = styles.get(osm, circum, nc);
+            for (ElemStyle s : sl) {
+                if (klass.isInstance(s)) {
+                    styleElems.add(new Pair<ElemStyle, OsmPrimitive>(s, osm));
+                }
+            }
+        }
+
+        public void drawAll() {
+            Collections.sort(styleElems, STYLE_COMPARATOR);
+            for (Pair<ElemStyle, OsmPrimitive> p : styleElems) {
+                p.a.paintPrimitive(p.b, paintSettings, painter, data.isSelected(p.b), memberSelected);
+            }
+        }
+
+        public boolean isMemberSelected() {
+            return memberSelected;
+        }
+
+        public void setMemberSelected(boolean memberSelected) {
+            this.memberSelected = memberSelected;
+        }
+    }
+
+    private final static Comparator<Pair<ElemStyle, OsmPrimitive>> STYLE_COMPARATOR = new Comparator<Pair<ElemStyle, OsmPrimitive>>() {
+        @Override
+        public int compare(Pair<ElemStyle, OsmPrimitive> p1, Pair<ElemStyle, OsmPrimitive> p2) {
+            int d1 = Float.compare(p1.a.z_index, p2.a.z_index);
+            if (d1 != 0)
+                return d1;
+            if (p1.a == NodeElemStyle.SIMPLE_NODE_ELEMSTYLE && p2.a != NodeElemStyle.SIMPLE_NODE_ELEMSTYLE)
+                return 1;
+            if (p1.a != NodeElemStyle.SIMPLE_NODE_ELEMSTYLE && p2.a == NodeElemStyle.SIMPLE_NODE_ELEMSTYLE)
+                return -1;
+            // newer primitives to the front
+            long id = p1.b.getUniqueId() - p2.b.getUniqueId();
+            if (id > 0)
+                return 1;
+            if (id < 0)
+                return -1;
+            return Float.compare(p1.a.object_z_index, p2.a.object_z_index);
+        }
+    };
+
     public void visitAll(final DataSet data, boolean virtual, Bounds bounds) {
         //long start = System.currentTimeMillis();
         BBox bbox = new BBox(bounds);
         this.data = data;
-        ++paintid;
-
-        int fillAreas = Main.pref.getInteger("mappaint.fillareas", 10000000);
-        LatLon ll1 = nc.getLatLon(0, 0);
-        LatLon ll2 = nc.getLatLon(100, 0);
-        dist = ll1.greatCircleDistance(ll2);
-
-        zoomLevelDisplay = Main.pref.getBoolean("mappaint.zoomLevelDisplay", false);
+
+        styles = MapPaintStyles.getStyles();
+
         circum = nc.getDist100Pixel();
-        styles = MapPaintStyles.getStyles();
-        drawMultipolygon = Main.pref.getBoolean("mappaint.multipolygon", true);
-        drawRestriction = Main.pref.getBoolean("mappaint.restriction", true);
-        leftHandTraffic = Main.pref.getBoolean("mappaint.lefthandtraffic", false);
-        minEN = nc.getEastNorth(0, nc.getHeight() - 1);
-        maxEN = nc.getEastNorth(nc.getWidth() - 1, 0);
+        boolean drawArea = circum <= Main.pref.getInteger("mappaint.fillareas", 10000000);
+        boolean drawMultipolygon = drawArea && Main.pref.getBoolean("mappaint.multipolygon", true);
+        styles.setDrawMultipolygon(drawMultipolygon);
+        boolean drawRestriction = Main.pref.getBoolean("mappaint.restriction", true);
+        boolean leftHandTraffic = Main.pref.getBoolean("mappaint.lefthandtraffic", false);
 
         g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
@@ -383,120 +113,101 @@
 
         this.paintSettings = MapPaintSettings.INSTANCE;
-        this.painter = new MapPainter(paintSettings, g, inactive, nc, virtual, dist, circum);
-
-        if (fillAreas > dist && styles != null && styles.hasAreas()) {
-            Collection<Way> noAreaWays = new LinkedList<Way>();
-            final Collection<Way> ways = data.searchWays(bbox);
-
-            /*** disabled ***/
-            for (final Way osm : ways) {
-                if (osm.isDisabled() && osm.isDrawable() && osm.mappaintDrawnCode != paintid) {
-                    drawWay(osm, 0);
-                    osm.mappaintDrawnCode = paintid;
-                }
-            }
-
-            /*** RELATIONS ***/
-            for (final Relation osm: data.searchRelations(bbox)) {
-                if (osm.isDrawable()) {
-                    paintUnselectedRelation(osm);
-                }
-            }
-
-            /*** AREAS ***/
-            for (final Way osm : selectedLast(data, ways)) {
-                if (osm.isDrawable() && osm.mappaintDrawnCode != paintid) {
-                    if (isPrimitiveArea(osm)) {
-                        if(osm.mappaintDrawnAreaCode != paintid)
-                            drawWay(osm, fillAreas);
-                    } else if(!data.isSelected(osm)) {
-                        noAreaWays.add(osm);
-                    }
-                }
-            }
-
-            /*** WAYS ***/
-            for (final Way osm : noAreaWays) {
-                drawWay(osm, 0);
-                osm.mappaintDrawnCode = paintid;
-            }
-        } else {
-            drawMultipolygon = false;
-            final Collection<Way> ways = data.searchWays(bbox);
-
-            /*** WAYS (disabled)  ***/
-            for (final Way way: ways) {
-                if (way.isDisabled() && way.isDrawable() && !data.isSelected(way)) {
-                    drawWay(way, 0);
-                    way.mappaintDrawnCode = paintid;
-                }
-            }
-
-            /*** RELATIONS ***/
-            for (final Relation osm: data.searchRelations(bbox)) {
-                if (osm.isDrawable()) {
-                    paintUnselectedRelation(osm);
-                }
-            }
-
-            /*** WAYS (filling disabled)  ***/
-            for (final Way way: ways) {
-                if (way.isDrawable() && !data.isSelected(way)) {
-                    drawWay(way, 0);
-                }
-            }
-        }
-
-        /*** SELECTED  ***/
-        for (final OsmPrimitive osm : data.getSelected()) {
-            if (osm.isUsable() && !(osm instanceof Node) && (osm instanceof Relation || osm.mappaintDrawnCode != paintid)) {
-                osm.visit(new AbstractVisitor() {
-                    public void visit(Way w) {
-                        drawWay(w, 0);
-                    }
-
-                    public void visit(Node n) {
-                        // Selected nodes are painted in following part
-                    }
-
-                    public void visit(Relation r) {
-                        for (RelationMember m : r.getMembers()) {
-                            OsmPrimitive osm = m.getMember();
-                            if(osm.isDrawable()) {
-                                StyleCache sc = getPrimitiveStyle(m.getMember(), false);
-                                if(osm instanceof Way)
-                                {
-                                    for (ElemStyle s : sc.getStyles()) {
-                                        if (!(s instanceof AreaElemStyle)) {
-                                            s.paintPrimitive(osm, paintSettings, painter, data.isSelected(osm), true);
-                                        }
-                                    }
-                                }
-                                else if(osm instanceof Node)
-                                {
-                                    for (ElemStyle s : sc.getStyles()) {
-                                        if (isZoomOk(s)) {
-                                            s.paintPrimitive(osm, paintSettings, painter, data.isSelected(osm), true);
-                                        }
-                                    }
-                                }
-                                osm.mappaintDrawnCode = paintid;
-                            }
-                        }
-                    }
-                });
-            }
-        }
-
-        /*** NODES ***/
-        for (final Node osm: data.searchNodes(bbox)) {
-            if (!osm.isIncomplete() && !osm.isDeleted() && (data.isSelected(osm) || !osm.isDisabledAndHidden())
-                    && osm.mappaintDrawnCode != paintid) {
-                drawNode(osm);
-            }
-        }
+        this.painter = new MapPainter(paintSettings, g, inactive, nc, virtual, circum, leftHandTraffic);
+
+        StyleCollector scDisabledLines = new StyleCollector(LineElemStyle.class);
+        StyleCollector scSelectedLines = new StyleCollector(LineElemStyle.class);
+        StyleCollector scSelectedAreas = new StyleCollector(AreaElemStyle.class);
+        StyleCollector scMemberLines = new StyleCollector(LineElemStyle.class);
+        scMemberLines.setMemberSelected(true);
+        StyleCollector scNormalAreas = new StyleCollector(AreaElemStyle.class);
+        StyleCollector scNormalLines = new StyleCollector(LineElemStyle.class);
+        for (final Way w : data.searchWays(bbox)) {
+            if (w.isDrawable()) {
+                if (w.isDisabled()) {
+                    scDisabledLines.add(w);
+                } else if (w.isSelected()) {
+                    scSelectedLines.add(w);
+                    if (drawArea) {
+                        scSelectedAreas.add(w);
+                    }
+                } else if (w.isMemberOfSelected()) {
+                    scMemberLines.add(w);
+                    if (drawArea) {
+                        scNormalAreas.add(w);
+                    }
+                } else {
+                    scNormalLines.add(w);
+                    if (drawArea) {
+                        scNormalAreas.add(w);
+                    }
+                }
+            }
+        }
+        scDisabledLines.drawAll();
+        scDisabledLines = null;
+
+        StyleCollector scDisabledNodes = new StyleCollector(NodeElemStyle.class);
+        StyleCollector scSelectedNodes = new StyleCollector(NodeElemStyle.class);
+        StyleCollector scMemberNodes = new StyleCollector(NodeElemStyle.class);
+        scMemberNodes.setMemberSelected(true);
+        StyleCollector scNormalNodes = new StyleCollector(NodeElemStyle.class);
+        for (final Node n: data.searchNodes(bbox)) {
+            if (n.isDrawable()) {
+                if (n.isDisabled()) {
+                    scDisabledNodes.add(n);
+                } else if (n.isSelected()) {
+                    scSelectedNodes.add(n);
+                } else if (n.isMemberOfSelected()) {
+                    scMemberNodes.add(n);
+                } else {
+                    scNormalNodes.add(n);
+                }
+            }
+        }
+        scDisabledNodes.drawAll();
+        scDisabledNodes = null;
+
+        StyleCollector scDisabledRestrictions = new StyleCollector(NodeElemStyle.class);
+        StyleCollector scNormalRestrictions = new StyleCollector(NodeElemStyle.class);
+        StyleCollector scSelectedRestrictions = new StyleCollector(NodeElemStyle.class);
+        for (Relation r: data.searchRelations(bbox)) {
+            if (r.isDrawable()) {
+                if (r.isDisabled()) {
+                    if (drawRestriction) {
+                        scDisabledRestrictions.add(r);
+                    }
+                } else if (r.isSelected()) {
+                    if (drawMultipolygon) {
+                        scSelectedAreas.add(r);
+                    }
+                    if (drawRestriction) {
+                        scSelectedRestrictions.add(r);
+                    }
+                } else {
+                    if (drawMultipolygon) {
+                        scNormalAreas.add(r);
+                    }
+                    if (drawRestriction) {
+                        scNormalRestrictions.add(r);
+                    }
+                }
+            }
+        }
+        scDisabledRestrictions.drawAll();
+        scDisabledRestrictions = null;
+
+        scNormalAreas.drawAll();
+        scSelectedAreas.drawAll();
+        scNormalLines.drawAll();
+        scMemberLines.drawAll();
+        scSelectedLines.drawAll();
+        scNormalNodes.drawAll();
+        scNormalRestrictions.drawAll();
+        scMemberNodes.drawAll();
+        scSelectedRestrictions.drawAll();
+        scSelectedNodes.drawAll();
 
         painter.drawVirtualNodes(data.searchWays(bbox));
-        //System.err.println("PAINTING TOOK "+(System.currentTimeMillis() - start));
+        //System.err.println("PAINTING TOOK "+(System.currentTimeMillis() - start)+ " (at scale "+circum+")");
     }
 
Index: trunk/src/org/openstreetmap/josm/data/osm/visitor/paint/MapPainter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/visitor/paint/MapPainter.java	(revision 3835)
+++ trunk/src/org/openstreetmap/josm/data/osm/visitor/paint/MapPainter.java	(revision 3836)
@@ -26,6 +26,8 @@
 import org.openstreetmap.josm.data.osm.RelationMember;
 import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.data.osm.visitor.paint.relations.Multipolygon;
+import org.openstreetmap.josm.data.osm.visitor.paint.relations.Multipolygon.PolyData;
 import org.openstreetmap.josm.gui.NavigatableComponent;
-import org.openstreetmap.josm.gui.mappaint.IconElemStyle;
+import org.openstreetmap.josm.gui.mappaint.NodeElemStyle;
 import org.openstreetmap.josm.tools.ImageProvider;
 import org.openstreetmap.josm.tools.LanguageInfo;
@@ -58,4 +60,6 @@
     private final double circum;
 
+    private final boolean leftHandTraffic;
+
     private final Collection<String> regionalNameOrder;
 
@@ -66,12 +70,12 @@
     public MapPainter(MapPaintSettings settings, Graphics2D g, 
         boolean inactive, NavigatableComponent nc, boolean virtual, 
-        double dist, double circum) {
-
+        double circum, boolean leftHandTraffic)
+    {
         this.g = g;
         this.inactive = inactive;
         this.nc = nc;
-        this.useStrokes = settings.getUseStrokesDistance() > dist;
-        this.showNames = settings.getShowNamesDistance() > dist;
-        this.showIcons = settings.getShowIconsDistance() > dist;
+        this.useStrokes = settings.getUseStrokesDistance() > circum;
+        this.showNames = settings.getShowNamesDistance() > circum;
+        this.showIcons = settings.getShowIconsDistance() > circum;
         this.outlineOnly = settings.isOutlineOnly();
 
@@ -93,7 +97,8 @@
         this.regionalNameOrder = Main.pref.getCollection("mappaint.nameOrder", Arrays.asList(names));
         this.circum = circum;
-    }
-
-    public void drawWay(Way way, Color color, int width, float dashed[], Color dashedColor, boolean showDirection,
+        this.leftHandTraffic = leftHandTraffic;
+    }
+
+    public void drawWay(Way way, Color color, float width, float dashed[], Color dashedColor, boolean showDirection,
             boolean reversedDirection, boolean showHeadArrowOnly) {
 
@@ -154,8 +159,8 @@
     }
 
-    private void displaySegments(GeneralPath path, GeneralPath arrows, Color color, int width, float dashed[], Color dashedColor) {
+    private void displaySegments(GeneralPath path, GeneralPath arrows, Color color, float width, float dashed[], Color dashedColor) {
         g.setColor(inactive ? inactiveColor : color);
         if (useStrokes) {
-            if (dashed.length > 0) {
+            if (dashed == null || dashed.length > 0) {
                 g.setStroke(new BasicStroke(width,BasicStroke.CAP_BUTT,BasicStroke.JOIN_ROUND,0, dashed,0));
             } else {
@@ -168,5 +173,5 @@
         if(!inactive && useStrokes && dashedColor != null) {
             g.setColor(dashedColor);
-            if (dashed.length > 0) {
+            if (dashed == null || dashed.length > 0) {
                 float[] dashedOffset = new float[dashed.length];
                 System.arraycopy(dashed, 1, dashedOffset, 0, dashed.length - 1);
@@ -319,4 +324,28 @@
     }
 
+    public void drawArea(Relation r, Color color, String name) {
+        Multipolygon multipolygon = new Multipolygon(nc);
+        multipolygon.load(r);
+        if(!r.isDisabled() && !multipolygon.getOuterWays().isEmpty()) {
+            for (PolyData pd : multipolygon.getCombinedPolygons()) {
+                Polygon p = pd.get();
+                if(!isPolygonVisible(p)) {
+                    continue;
+                }
+                drawArea(p, color, getAreaName(r));
+            }
+        }
+    }
+
+    private boolean isPolygonVisible(Polygon polygon) {
+        Rectangle bounds = polygon.getBounds();
+        if (bounds.width == 0 && bounds.height == 0) return false;
+        if (bounds.x > nc.getWidth()) return false;
+        if (bounds.y > nc.getHeight()) return false;
+        if (bounds.x + bounds.width < 0) return false;
+        if (bounds.y + bounds.height < 0) return false;
+        return true;
+    }
+
     public void drawRestriction(ImageIcon icon, Point pVia, double vx, double vx2, double vy, double vy2, double iconAngle, boolean selected) {
         /* rotate icon with direction last node in from to */
@@ -334,5 +363,5 @@
     }
 
-    public void drawRestriction(Relation r, boolean leftHandTraffic, IconElemStyle icon) {
+    public void drawRestriction(Relation r, NodeElemStyle icon) {
 
         Way fromWay = null;
Index: trunk/src/org/openstreetmap/josm/data/validation/tests/MultipolygonTest.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/validation/tests/MultipolygonTest.java	(revision 3835)
+++ trunk/src/org/openstreetmap/josm/data/validation/tests/MultipolygonTest.java	(revision 3836)
@@ -26,5 +26,4 @@
 import org.openstreetmap.josm.gui.mappaint.ElemStyles;
 import org.openstreetmap.josm.gui.mappaint.MapPaintStyles;
-import org.openstreetmap.josm.gui.mappaint.StyleCache;
 import org.openstreetmap.josm.gui.mappaint.xml.AreaPrototype;
 
@@ -47,4 +46,6 @@
     private final List<List<Node>> nonClosedWays = new ArrayList<List<Node>>();
 
+    private final double SCALE = 1.0; // arbitrary scale - we could test every possible scale, but this should suffice
+
     public MultipolygonTest() {
         super(tr("Multipolygon"),
@@ -115,7 +116,9 @@
     public void visit(Way w) {
         if (styles != null && !w.isClosed()) {
-            AreaPrototype e = styles.getAreaProto(w);
-            if (e != null && ! e.closed) {
-                errors.add( new TestError(this, Severity.WARNING, tr("Area style way is not closed"), NOT_CLOSED,  w));
+            for (ElemStyle s : styles.generateStyles(w, SCALE, null, false).a) {
+                if (s instanceof AreaElemStyle) {
+                    errors.add( new TestError(this, Severity.WARNING, tr("Area style way is not closed"), NOT_CLOSED,  w));
+                    break;
+                }
             }
         }
@@ -143,8 +146,7 @@
             List<List<Node>> outerWays = joinWays(polygon.getOuterWays());
             if (styles != null) {
-                StyleCache sc = styles.get(r);
 
                 AreaElemStyle area = null;
-                for (ElemStyle s : sc.getStyles()) {
+                for (ElemStyle s : styles.generateStyles(r, SCALE, null, false).a) {
                     if (s instanceof AreaElemStyle) {
                         area = (AreaElemStyle) s;
@@ -155,8 +157,8 @@
                 if (area == null) {
                     errors.add( new TestError(this, Severity.OTHER, tr("No style in multipolygon relation"),
-                    NO_STYLE_POLYGON, r));
+                            NO_STYLE_POLYGON, r));
                     for (Way w : polygon.getOuterWays()) {
 
-                        for (ElemStyle s : styles.getArea(w).getStyles()) {
+                        for (ElemStyle s : styles.generateStyles(r, SCALE, null, true).a) {
                             if (s instanceof AreaElemStyle) {
                                 area = (AreaElemStyle) s;
@@ -173,5 +175,5 @@
                     for (Way wInner : polygon.getInnerWays()) {
                         AreaElemStyle areaInner = null;
-                        for (ElemStyle s : styles.get(wInner).getStyles()) {
+                        for (ElemStyle s : styles.generateStyles(wInner, SCALE, null, false).a) {
                             if (s instanceof AreaElemStyle) {
                                 areaInner = (AreaElemStyle) s;
@@ -185,10 +187,10 @@
                             l.add(wInner);
                             errors.add( new TestError(this, Severity.WARNING, tr("Style for inner way equals multipolygon"),
-                            INNER_STYLE_MISMATCH, l, Collections.singletonList(wInner)));
+                                    INNER_STYLE_MISMATCH, l, Collections.singletonList(wInner)));
                         }
                     }
                     for (Way wOuter : polygon.getOuterWays()) {
                         AreaElemStyle areaOuter = null;
-                        for (ElemStyle s : styles.get(wOuter).getStyles()) {
+                        for (ElemStyle s : styles.generateStyles(wOuter, SCALE, null, false).a) {
                             if (s instanceof AreaElemStyle) {
                                 areaOuter = (AreaElemStyle) s;
Index: trunk/src/org/openstreetmap/josm/gui/mappaint/AreaElemStyle.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/mappaint/AreaElemStyle.java	(revision 3835)
+++ trunk/src/org/openstreetmap/josm/gui/mappaint/AreaElemStyle.java	(revision 3836)
@@ -5,4 +5,5 @@
 
 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.data.osm.visitor.paint.MapPaintSettings;
@@ -14,7 +15,14 @@
     public Color color;
 
-    public AreaElemStyle(long minScale, long maxScale, Color color) {
-        super(minScale, maxScale);
+    protected AreaElemStyle(Cascade c, Color color) {
+        super(c);
         this.color = color;
+    }
+
+    public static AreaElemStyle create(Cascade c) {
+        Color color = c.get("fill-color", null, Color.class);
+        if (color == null)
+            return null;
+        return new AreaElemStyle(c, color);
     }
 
@@ -25,5 +33,6 @@
             String name = painter.isShowNames() ? painter.getAreaName(w) : null;
             painter.drawArea(w, w.isSelected() ? paintSettings.getSelectedColor() : color, name);
-            // line.paintPrimitive(way, paintSettings, painter, selected);
+        } else if (primitive instanceof Relation) {
+            painter.drawArea((Relation) primitive, selected ? paintSettings.getRelationSelectedColor() : color, painter.getAreaName(primitive));
         }
     }
@@ -40,10 +49,10 @@
     @Override
     public int hashCode() {
-        return color.hashCode();
+        return 11 * super.hashCode() + color.hashCode();
     }
 
     @Override
     public String toString() {
-        return "AreaElemStyle{" + "color=" + color + '}';
+        return "AreaElemStyle{" + super.toString() + "color=" + color + '}';
     }
 }
Index: trunk/src/org/openstreetmap/josm/gui/mappaint/Cascade.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/mappaint/Cascade.java	(revision 3836)
+++ trunk/src/org/openstreetmap/josm/gui/mappaint/Cascade.java	(revision 3836)
@@ -0,0 +1,53 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.mappaint;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Simple map of properties with dynamic typing.
+ */
+public class Cascade {
+    
+    public static final Cascade EMPTY_CASCADE = new Cascade();
+
+    protected Map<String, Object> prop = new HashMap<String, Object>();
+
+    /**
+     * Get value for the given key
+     * @param key the key
+     * @param def default value, can be null
+     * @param klass the same as T
+     * @return if a value with class klass has been mapped to key, returns this
+     *      value, def otherwise
+     */
+    public <T> T get(String key, T def, Class klass) {
+        if (def != null && !klass.isInstance(def))
+            throw new IllegalArgumentException();
+        Object o = prop.get(key);
+        if (o == null)
+            return def;
+        if (klass.isInstance(o)) {
+            @SuppressWarnings("unchecked") T res = (T) klass.cast(o);
+            return res;
+        }
+        System.err.println(String.format("Warning: wrong type for mappaint property %s: %s expected, but %s found!", key, klass, o.getClass()));
+        return def;
+    }
+
+    public void put(String key, Object val) {
+        prop.put(key, val);
+    }
+
+    public void putOrClear(String key, Object val) {
+        if (val != null) {
+            prop.put(key, val);
+        } else {
+            prop.remove(key);
+        }
+    }
+
+    public void remove(String key) {
+        prop.remove(key);
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/mappaint/ElemStyle.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/mappaint/ElemStyle.java	(revision 3835)
+++ trunk/src/org/openstreetmap/josm/gui/mappaint/ElemStyle.java	(revision 3836)
@@ -7,12 +7,19 @@
 
 abstract public class ElemStyle {
-    // zoom range to display the feature
-    public long minScale;
-    public long maxScale;
+    
+    public float z_index;
+    public float object_z_index;
 
-    public ElemStyle(long minScale, long maxScale) {
-        this.minScale = minScale;
-        this.maxScale = maxScale;
+    public ElemStyle(float z_index, float object_z_index) {
+        this.z_index = z_index;
+        this.object_z_index = object_z_index;
     }
+
+    protected ElemStyle(Cascade c) {
+        z_index = c.get("z-index", 0f, Float.class);
+        object_z_index = c.get("object-z-index", 0f, Float.class);
+    }
+
+    public abstract void paintPrimitive(OsmPrimitive primitive, MapPaintSettings paintSettings, MapPainter painter, boolean selected, boolean member);
 
     @Override
@@ -21,12 +28,20 @@
             return false;
         ElemStyle s = (ElemStyle) o;
-        return minScale == s.minScale && maxScale == s.maxScale;
+        return z_index == s.z_index && object_z_index == s.object_z_index;
     }
 
     @Override
     public int hashCode() {
-        return getClass().hashCode();
+        int hash = 5;
+        hash = 41 * hash + Float.floatToIntBits(this.z_index);
+        hash = 41 * hash + Float.floatToIntBits(this.object_z_index);
+        return hash;
     }
 
-    public abstract void paintPrimitive(OsmPrimitive primitive, MapPaintSettings paintSettings, MapPainter painter, boolean selected, boolean member);
+    @Override
+    public String toString() {
+        if (z_index != 0f || object_z_index != 0f)
+            return String.format("z_idx=%s/%s ", z_index, object_z_index);
+        return "";
+    }
 }
Index: trunk/src/org/openstreetmap/josm/gui/mappaint/ElemStyles.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/mappaint/ElemStyles.java	(revision 3835)
+++ trunk/src/org/openstreetmap/josm/gui/mappaint/ElemStyles.java	(revision 3836)
@@ -2,10 +2,11 @@
 package org.openstreetmap.josm.gui.mappaint;
 
-import static org.openstreetmap.josm.tools.Utils.equal;
-
+import java.awt.Color;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.List;
+import java.util.Map.Entry;
 import java.util.Set;
 
@@ -13,15 +14,18 @@
 import org.openstreetmap.josm.data.osm.Node;
 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.gui.mappaint.xml.AreaPrototype;
-import org.openstreetmap.josm.gui.mappaint.xml.IconPrototype;
-import org.openstreetmap.josm.gui.mappaint.xml.LinePrototype;
-import org.openstreetmap.josm.gui.mappaint.xml.LinemodPrototype;
+import org.openstreetmap.josm.data.osm.visitor.paint.relations.Multipolygon;
+import org.openstreetmap.josm.gui.NavigatableComponent;
+import org.openstreetmap.josm.gui.mappaint.StyleCache.StyleList;
 import org.openstreetmap.josm.gui.mappaint.xml.XmlStyleSource;
 import org.openstreetmap.josm.tools.FilteredCollection;
+import org.openstreetmap.josm.tools.Pair;
 import org.openstreetmap.josm.tools.Predicate;
+import org.openstreetmap.josm.tools.Utils;
 
 public class ElemStyles {
     private List<XmlStyleSource> styleSources;
+    private boolean drawMultipolygon;
 
     public ElemStyles()
@@ -41,116 +45,8 @@
             @Override
             public boolean evaluate(XmlStyleSource s) {
-                return equal(s.getPrefName(), name);
+                return Utils.equal(s.getPrefName(), name);
             }
 
         });
-    }
-
-    public static class WayPrototypesRecord {
-        public LinePrototype line;
-        public List<LinemodPrototype> linemods;
-        public AreaPrototype area;
-
-        public List<ElemStyle> createStyles() {
-            List<ElemStyle> ret = new ArrayList<ElemStyle>();
-            if (area != null) {
-                ret.add(area.createStyle());
-            }
-            if (line != null) {
-                ret.add(line.createStyle());
-            } else {
-                if (area != null) {
-                    ret.add(LineElemStyle.createSimpleLineStyle(area.color));
-                } else {
-                    ret.add(LineElemStyle.UNTAGGED_WAY);
-                }
-            }
-
-            if (linemods != null) {
-                for (LinemodPrototype p : linemods) {
-                    LineElemStyle s = p.createStyle(line.getWidth());
-                    if (p.over) {
-                        ret.add(s);
-                    } else {
-                        ret.add(0, s);
-                    }
-                }
-            }
-            return ret;
-        }
-    }
-
-    public StyleCache get(OsmPrimitive osm) {
-        if (osm instanceof Node) {
-            IconPrototype icon = getNode(osm);
-            if (icon == null)
-                return StyleCache.EMPTY_STYLECACHE;
-            return StyleCache.create(icon.createStyle());
-        } else {
-            WayPrototypesRecord p = get(osm, false);
-            return StyleCache.create(p.createStyles());
-        }
-    }
-
-    public IconPrototype getNode(OsmPrimitive osm) {
-        IconPrototype icon = null;
-        for (XmlStyleSource s : getStyleSources()) {
-            icon = s.getNode(osm, icon);
-        }
-        return icon;
-    }
-
-    private WayPrototypesRecord get(OsmPrimitive osm, boolean forceArea) {
-        WayPrototypesRecord p = new WayPrototypesRecord();
-        for (XmlStyleSource s : getStyleSources()) {
-            s.get(osm, forceArea || !(osm instanceof Way) || ((Way) osm).isClosed(), p);
-        }
-        return p;
-    }
-
-    public boolean hasAreas() {
-        for (XmlStyleSource s : getStyleSources()) {
-            if (s.hasAreas())
-                return true;
-        }
-        return false;
-    }
-
-    public boolean isArea(OsmPrimitive osm) {
-        for (XmlStyleSource s : getStyleSources()) {
-            if (s.isArea(osm))
-                return true;
-        }
-        return false;
-    }
-
-    public StyleCache getArea(Way osm) {
-        if (osm.hasKeys()) {
-            /* force area mode also for unclosed ways */
-            WayPrototypesRecord p = get(osm, true);
-            if (p.area != null)
-                return StyleCache.create(p.createStyles());
-        }
-        return StyleCache.EMPTY_STYLECACHE;
-    }
-
-    public AreaPrototype getAreaProto(Way osm) {
-        if (osm.hasKeys()) {
-            /* force area mode also for unclosed ways */
-            WayPrototypesRecord p = get(osm, true);
-            if (p.area != null)
-                return p.area;
-        }
-        return null;
-    }
-
-    public IconElemStyle getIcon(OsmPrimitive osm) {
-        if (!osm.hasKeys())
-            return null;
-        NodeElemStyle icon = getNode(osm).createStyle();
-        if (icon instanceof IconElemStyle) {
-            return (IconElemStyle) icon;
-        }
-        return null;
     }
 
@@ -165,3 +61,199 @@
         return names;
     }
+
+    public StyleList get(OsmPrimitive osm, double scale, NavigatableComponent nc) {
+        return getStyleCacheWithRange(osm, scale, nc).a;
+    }
+
+    public Pair<StyleList, Range> getStyleCacheWithRange(OsmPrimitive osm, double scale, NavigatableComponent nc) {
+        if (osm.mappaintStyle == null) {
+            osm.mappaintStyle = StyleCache.EMPTY_STYLECACHE;
+        } else {
+            Pair<StyleList, Range> lst = osm.mappaintStyle.getWithRange(scale);
+            if (lst.a != null)
+                return lst;
+        }
+        Pair<StyleList, Range> p = getImpl(osm, scale, nc);
+        if (osm instanceof Node && p.a.isEmpty()) {
+            p.a = StyleList.SIMPLE_NODE;
+        } else if (osm instanceof Way && !Utils.exists(p.a, LineElemStyle.class)) {
+            AreaElemStyle area = Utils.find(p.a, AreaElemStyle.class);
+            LineElemStyle line = (area == null ? LineElemStyle.UNTAGGED_WAY : LineElemStyle.createSimpleLineStyle(area.color));
+            p.a = new StyleList(p.a, line);
+        }
+        osm.mappaintStyle = osm.mappaintStyle.put(p.a, p.b);
+        return p;
+    }
+
+    private Pair<StyleList, Range> getImpl(OsmPrimitive osm, double scale, NavigatableComponent nc) {
+        if (osm instanceof Node)
+        {
+            return generateStyles(osm, scale, null, false);
+        } 
+        else if (osm instanceof Way)
+        {
+            Pair<StyleList, Range> p = generateStyles(osm, scale, null, false);
+
+            boolean isOuterWayOfSomeMP = false;
+            boolean hasIndependentLineElemStyle = false;
+            Color wayColor = null;
+
+            for (OsmPrimitive referrer : osm.getReferrers()) {
+                Relation r = (Relation) referrer;
+                if (!drawMultipolygon || !"multipolygon".equals(r.get("type"))  || !r.isUsable()) {
+                    continue;
+                }
+                Multipolygon multipolygon = new Multipolygon(nc);
+                multipolygon.load(r);
+
+                if (multipolygon.getOuterWays().contains(osm)) {
+                    if (!isOuterWayOfSomeMP) { // do this only one time
+                        List<ElemStyle> tmp = new ArrayList<ElemStyle>(p.a.size());
+                        for (ElemStyle s : p.a) {
+                            if (s instanceof AreaElemStyle) {
+                                wayColor = ((AreaElemStyle) s).color;
+                            } else {
+                                tmp.add(s);
+                            }
+                        }
+                        p.a = new StyleList(tmp);
+                        isOuterWayOfSomeMP = true;
+                        hasIndependentLineElemStyle = Utils.exists(p.a, LineElemStyle.class);
+                    }
+
+                    if (!hasIndependentLineElemStyle) {
+                        Pair<StyleList, Range> mpElemStyles = getStyleCacheWithRange(r, scale, nc);
+                        LineElemStyle mpLine = Utils.find(mpElemStyles.a, LineElemStyle.class);
+                        if (mpLine != null) {
+                                p.a = new StyleList(p.a, mpLine);
+                                p.b = Range.cut(p.b, mpElemStyles.b);
+                                break;
+                        } else if (wayColor == null) {
+                            AreaElemStyle mpArea = Utils.find(mpElemStyles.a, AreaElemStyle.class);
+                            if (mpArea != null) {
+                                p.b = Range.cut(p.b, mpElemStyles.b);
+                                wayColor = mpArea.color;
+                            }
+                        }
+                    }
+                }
+            }
+            if (isOuterWayOfSomeMP) {
+                if (!Utils.exists(p.a, LineElemStyle.class)) {
+                    p.a = new StyleList(p.a, LineElemStyle.createSimpleLineStyle(wayColor));
+                }
+                return p;
+            }
+
+            for (OsmPrimitive referrer : osm.getReferrers()) {
+                Relation ref = (Relation) referrer;
+                if (!drawMultipolygon || !"multipolygon".equals(ref.get("type"))  || !ref.isUsable()) {
+                    continue;
+                }
+                Multipolygon multipolygon = new Multipolygon(nc);
+                multipolygon.load(ref);
+
+                if (multipolygon.getInnerWays().contains(osm)) {
+                    Iterator<Way> it = multipolygon.getOuterWays().iterator();
+                    p = generateStyles(osm, scale, it.hasNext() ? it.next() : null, false);
+                    boolean hasIndependentElemStyle = false;
+                    for (ElemStyle s : p.a) {
+                        if (s instanceof LineElemStyle || s instanceof AreaElemStyle) {
+                            hasIndependentElemStyle = true;
+                        }
+                    }
+                    if (!hasIndependentElemStyle && !multipolygon.getOuterWays().isEmpty()) {
+                        StyleList mpElemStyles = get(ref, scale, nc);
+                        Color mpColor = null;
+                        for (ElemStyle mpS : mpElemStyles) {
+                            if (mpS instanceof AreaElemStyle) {
+                                mpColor = ((AreaElemStyle) mpS).color;
+                                break;
+                            }
+                        }
+                        p.a = new StyleList(p.a, LineElemStyle.createSimpleLineStyle(mpColor));
+                    }
+                    return p;
+                }
+            }
+            return p;
+        } 
+        else if (osm instanceof Relation)
+        {
+            Pair<StyleList, Range> p = generateStyles(osm, scale, null, true);
+            if (drawMultipolygon && "multipolygon".equals(osm.get("type"))) {
+                if (!Utils.exists(p.a, AreaElemStyle.class)) {
+                    // look at outer ways to find area style
+                    Multipolygon multipolygon = new Multipolygon(nc);
+                    multipolygon.load((Relation) osm);
+                    for (Way w : multipolygon.getOuterWays()) {
+                        Pair<StyleList, Range> wayStyles = generateStyles(w, scale, null, false);
+                        ElemStyle area = Utils.find(wayStyles.a, AreaElemStyle.class);
+                        if (area != null) {
+                            p.a = new StyleList(p.a, area);
+                            p.b = Range.cut(p.b, wayStyles.b);
+                            break;
+                        }
+                    }
+                }
+            }
+            return p;
+        }
+        return null;
+    }
+
+    /**
+     * @param multipolyOuterWay support for a very old multipolygon tagging style
+     * where you add the tags both to the outer and the inner way.
+     * However, independent inner way style is also possible.
+     * @param pretendWayIsClosed For styles that require the way to be closed,
+     * we pretend it is. This is useful for generating area styles from the (segmented)
+     * outer ways of a multipolygon.
+     */
+    public Pair<StyleList, Range> generateStyles(OsmPrimitive osm, double scale, OsmPrimitive multipolyOuterWay, boolean pretendWayIsClosed) {
+
+        List<ElemStyle> sl = new ArrayList<ElemStyle>();
+        MultiCascade mc = new MultiCascade();
+
+        for (XmlStyleSource s : styleSources) {
+            s.apply(mc, osm, scale, multipolyOuterWay, pretendWayIsClosed);
+        }
+
+        for (Entry<String, Cascade> e : mc.entrySet()) {
+            if ("*".equals(e.getKey()))
+                continue;
+            Cascade c = e.getValue();
+            if (osm instanceof Way) {
+                addIfNotNull(sl, AreaElemStyle.create(c));
+                addIfNotNull(sl, LineElemStyle.createLine(c));
+                addIfNotNull(sl, LineElemStyle.createCasing(c));
+            } else if (osm instanceof Node) {
+                addIfNotNull(sl, NodeElemStyle.create(c));
+            } else if (osm instanceof Relation) {
+                if ("multipolygon".equals(osm.get("type"))) {
+                    addIfNotNull(sl, AreaElemStyle.create(c));
+                    addIfNotNull(sl, LineElemStyle.createLine(c));
+                    addIfNotNull(sl, LineElemStyle.createCasing(c));
+                } else if ("restriction".equals(osm.get("type"))) {
+                    addIfNotNull(sl, NodeElemStyle.create(c));
+                }
+            }
+        }
+
+        return new Pair<StyleList, Range>(new StyleList(sl), mc.range);
+    }
+
+    private static <T> void addIfNotNull(List<T> list, T obj) {
+        if (obj != null) {
+            list.add(obj);
+        }
+    }
+
+    public boolean isDrawMultipolygon() {
+        return drawMultipolygon;
+    }
+
+    public void setDrawMultipolygon(boolean drawMultipolygon) {
+        this.drawMultipolygon = drawMultipolygon;
+    }
 }
Index: trunk/src/org/openstreetmap/josm/gui/mappaint/IconElemStyle.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/mappaint/IconElemStyle.java	(revision 3835)
+++ 	(revision )
@@ -1,61 +1,0 @@
-// License: GPL. For details, see LICENSE file.
-package org.openstreetmap.josm.gui.mappaint;
-
-import javax.swing.GrayFilter;
-import javax.swing.ImageIcon;
-
-import org.openstreetmap.josm.data.osm.Node;
-import org.openstreetmap.josm.data.osm.OsmPrimitive;
-import org.openstreetmap.josm.data.osm.visitor.paint.MapPaintSettings;
-import org.openstreetmap.josm.data.osm.visitor.paint.MapPainter;
-
-public class IconElemStyle extends NodeElemStyle {
-    public ImageIcon icon;
-    private ImageIcon disabledIcon;
-
-    public IconElemStyle(long minScale, long maxScale, ImageIcon icon) {
-        super(minScale, maxScale);
-        this.icon = icon;
-    }
-
-    public ImageIcon getDisabledIcon() {
-        if (disabledIcon != null)
-            return disabledIcon;
-        if (icon == null)
-            return null;
-        return disabledIcon = new ImageIcon(GrayFilter.createDisabledImage(icon.getImage()));
-    }
-
-    @Override
-    public void paintPrimitive(OsmPrimitive primitive, MapPaintSettings settings, MapPainter painter, boolean selected, boolean member) {
-        if (painter.isShowIcons()) {
-            Node n = (Node) primitive;
-            painter.drawNodeIcon(n, (painter.isInactive() || n.isDisabled())?getDisabledIcon():icon, selected, member, getName(n, painter));
-        } else {
-            SimpleNodeElemStyle.INSTANCE.paintPrimitive(primitive, settings, painter, selected, member);
-        }
-
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (obj == null || getClass() != obj.getClass())
-            return false;
-        if (!super.equals(obj))
-            return false;
-        
-        final IconElemStyle other = (IconElemStyle) obj;
-        // we should get the same image object due to caching
-        return this.icon.getImage() == other.icon.getImage();
-    }
-
-    @Override
-    public int hashCode() {
-        return icon.getImage().hashCode();
-    }
-
-    @Override
-    public String toString() {
-        return "IconElemStyle{" + "icon=" + icon + '}';
-    }
-}
Index: trunk/src/org/openstreetmap/josm/gui/mappaint/LineElemStyle.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/mappaint/LineElemStyle.java	(revision 3835)
+++ trunk/src/org/openstreetmap/josm/gui/mappaint/LineElemStyle.java	(revision 3836)
@@ -15,22 +15,17 @@
 public class LineElemStyle extends ElemStyle {
 
-    public static final LineElemStyle UNTAGGED_WAY;
+    public static LineElemStyle createSimpleLineStyle(Color color) {
+        return new LineElemStyle(Cascade.EMPTY_CASCADE, -1f, 0f, color != null ? color : PaintColors.UNTAGGED.get(), null, null);
+    }
+    public static final LineElemStyle UNTAGGED_WAY = createSimpleLineStyle(null);
 
-    static {
-        UNTAGGED_WAY = new LineElemStyle(0, Long.MAX_VALUE, -1, 0, PaintColors.UNTAGGED.get(), new float[0], null);
-    }
-
-    public static LineElemStyle createSimpleLineStyle(Color color) {
-        return new LineElemStyle(0, Long.MAX_VALUE, -1, 0, color, new float[0], null);
-    }
-
-    private int width;
-    public int realWidth; //the real width of this line in meter
+    private float width;
+    public float realWidth; // the real width of this line in meter
     public Color color;
     private float[] dashed;
     public Color dashedColor;
 
-    public LineElemStyle(long minScale, long maxScale, int width, int realWidth, Color color, float[] dashed, Color dashedColor) {
-        super(minScale, maxScale);
+    protected LineElemStyle(Cascade c, float width, float realWidth, Color color, float[] dashed, Color dashedColor) {
+        super(c);
         setWidth(width);
         this.realWidth = realWidth;
@@ -38,4 +33,31 @@
         this.dashed = dashed;
         this.dashedColor = dashedColor;
+    }
+
+    public static LineElemStyle createLine(Cascade c) {
+        return createImpl(c, "");
+    }
+
+    public static LineElemStyle createCasing(Cascade c) {
+        return createImpl(c, "casing-");
+    }
+
+    private static LineElemStyle createImpl(Cascade c, String prefix) {
+        Float width = c.get(prefix + "width", null, Float.class);
+        if (width == null)
+            return null;
+
+        float realWidth = c.get(prefix + "real-width", 0f, Float.class);
+        Color color = c.get(prefix + "color", null, Color.class);
+        if (color == null) {
+            color = c.get(prefix + "fill-color", null, Color.class);
+        }
+        if (color == null) {
+            color = PaintColors.UNTAGGED_WAY.get();
+        }
+        float[] dashes = c.get(prefix + "dashes", null, float[].class);
+        Color dashesBackground = c.get(prefix + "dashes-background-color", null, Color.class);
+
+        return new LineElemStyle(c, width, realWidth, color, dashes, dashesBackground);
     }
 
@@ -55,5 +77,5 @@
 
         Color myDashedColor = dashedColor;
-        int myWidth = getWidth();
+        float myWidth = getWidth();
 
         if (realWidth > 0 && paintSettings.isUseRealWidth() && !showDirection) {
@@ -67,5 +89,5 @@
             if(widthTag != null) {
                 try {
-                    realWidth = Integer.parseInt(widthTag);
+                    realWidth = new Float(Integer.parseInt(widthTag));
                 }
                 catch(NumberFormatException nfe) {
@@ -107,11 +129,11 @@
     }
 
-    public int getWidth() {
-        if (width == -1)
+    public float getWidth() {
+        if (width == -1f)
             return MapPaintSettings.INSTANCE.getDefaultSegmentWidth();
         return width;
     }
 
-    public void setWidth(int width) {
+    public void setWidth(float width) {
         this.width = width;
     }
@@ -133,10 +155,10 @@
     @Override
     public int hashCode() {
-        int hash = 3;
-        hash = 29 * hash + this.width;
-        hash = 29 * hash + this.realWidth;
-        hash = 29 * hash + this.color.hashCode();
-        hash = 29 * hash + Arrays.hashCode(this.dashed);
-        hash = 29 * hash + (this.dashedColor != null ? this.dashedColor.hashCode() : 0);
+        int hash = super.hashCode();
+        hash = 29 * hash + Float.floatToIntBits(width);
+        hash = 29 * hash + Float.floatToIntBits(realWidth);
+        hash = 29 * hash + color.hashCode();
+        hash = 29 * hash + Arrays.hashCode(dashed);
+        hash = 29 * hash + (dashedColor != null ? dashedColor.hashCode() : 0);
         return hash;
     }
@@ -144,5 +166,5 @@
     @Override
     public String toString() {
-        return "LineElemStyle{" + "width=" + width + " realWidth=" + realWidth + " color=" + color + " dashed=" + Arrays.toString(dashed) + " dashedColor=" + dashedColor + '}';
+        return "LineElemStyle{" + super.toString() + "width=" + width + " realWidth=" + realWidth + " color=" + color + " dashed=" + Arrays.toString(dashed) + " dashedColor=" + dashedColor + '}';
     }
 }
Index: trunk/src/org/openstreetmap/josm/gui/mappaint/MultiCascade.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/mappaint/MultiCascade.java	(revision 3836)
+++ trunk/src/org/openstreetmap/josm/gui/mappaint/MultiCascade.java	(revision 3836)
@@ -0,0 +1,28 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.mappaint;
+
+import java.util.HashMap;
+
+/**
+ * Several cascades, e.g. one for the main Line and one for each overlay.
+ * The range is (0,Inf) at first and it shrinks in the process when
+ * StyleSources apply zoom level dependent properties.
+ */
+public class MultiCascade extends HashMap<String, Cascade> {
+    public Range range;
+
+    public MultiCascade() {
+        super();
+        range = new Range();
+    }
+
+    public Cascade getCascade(String key) {
+        Cascade ret = get(key);
+        if (ret == null) {
+            ret = new Cascade();
+            put(key, ret);
+        }
+        return ret;
+    }
+
+}
Index: trunk/src/org/openstreetmap/josm/gui/mappaint/NodeElemStyle.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/mappaint/NodeElemStyle.java	(revision 3835)
+++ trunk/src/org/openstreetmap/josm/gui/mappaint/NodeElemStyle.java	(revision 3836)
@@ -2,13 +2,110 @@
 package org.openstreetmap.josm.gui.mappaint;
 
+import java.awt.Color;
+
+import javax.swing.GrayFilter;
+import javax.swing.ImageIcon;
+
 import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.Relation;
+import org.openstreetmap.josm.data.osm.visitor.paint.MapPaintSettings;
 import org.openstreetmap.josm.data.osm.visitor.paint.MapPainter;
+import org.openstreetmap.josm.gui.mappaint.MapPaintStyles.IconReference;
+import org.openstreetmap.josm.tools.Utils;
 
-abstract public class NodeElemStyle extends ElemStyle {
+/**
+ * applies for Nodes and turn restriction relations
+ */
+public class NodeElemStyle extends ElemStyle {
     public boolean annotate;
     public String annotation_key;
+    public ImageIcon icon;
+    private ImageIcon disabledIcon;
 
-    public NodeElemStyle(long minScale, long maxScale) {
-        super(minScale, maxScale);
+    public static final NodeElemStyle SIMPLE_NODE_ELEMSTYLE = new NodeElemStyle(Cascade.EMPTY_CASCADE, true, null, null);
+
+    protected NodeElemStyle(Cascade c, boolean annotate, String annotation_key, ImageIcon icon) {
+        super(c);
+        this.annotate = annotate;
+        this.annotation_key = annotation_key;
+        this.icon = icon;
+    }
+
+    public static NodeElemStyle create(Cascade c) {
+        IconReference iconRef = c.get("icon-image", null, IconReference.class);
+        if (iconRef == null)
+            return null;
+
+        ImageIcon icon = MapPaintStyles.getIcon(iconRef);
+        String text = c.get("text", null, String.class);
+
+        boolean annotate = text != null;
+        String annotation_key = null;
+
+        if (annotate && !"yes".equalsIgnoreCase(text)) {
+            annotation_key = text;
+        }
+        return new NodeElemStyle(c, annotate, annotation_key, icon);
+    }
+
+    @Override
+    public void paintPrimitive(OsmPrimitive primitive, MapPaintSettings settings, MapPainter painter, boolean selected, boolean member) {
+        if (primitive instanceof Node) {
+            Node n = (Node) primitive;
+            if (icon != null && painter.isShowIcons()) {
+                painter.drawNodeIcon(n, (painter.isInactive() || n.isDisabled()) ? getDisabledIcon() : icon,
+                        selected, member, getName(n, painter));
+            } else {
+                if (n.isHighlighted()) {
+                    painter.drawNode(n, settings.getHighlightColor(), settings.getSelectedNodeSize(), settings.isFillSelectedNode(), getName(n, painter));
+                } else {
+                    Color color;
+                    boolean isConnection = n.isConnectionNode();
+
+                    if (painter.isInactive() || n.isDisabled()) {
+                        color = settings.getInactiveColor();
+                    } else if (selected) {
+                        color = settings.getSelectedColor();
+                    } else if (member) {
+                        color = settings.getRelationSelectedColor();
+                    } else if (isConnection) {
+                        if (n.isTagged()) {
+                            color = settings.getTaggedConnectionColor();
+                        } else {
+                            color = settings.getConnectionColor();
+                        }
+                    } else {
+                        if (n.isTagged()) {
+                            color = settings.getTaggedColor();
+                        } else {
+                            color = settings.getNodeColor();
+                        }
+                    }
+
+                    final int size = Utils.max((selected ? settings.getSelectedNodeSize() : 0),
+                                            (n.isTagged() ? settings.getTaggedNodeSize() : 0),
+                                            (isConnection ? settings.getConnectionNodeSize() : 0),
+                                            settings.getUnselectedNodeSize());
+
+                    final boolean fill = (selected && settings.isFillSelectedNode()) ||
+                                            (n.isTagged() && settings.isFillTaggedNode()) ||
+                                            (isConnection && settings.isFillConnectionNode()) ||
+                                            settings.isFillUnselectedNode();
+
+                    painter.drawNode(n, color, size, fill, getName(n, painter));
+                }
+            }
+        } else if (primitive instanceof Relation) {
+            painter.drawRestriction((Relation) primitive, this);
+        }
+    }
+
+    public ImageIcon getDisabledIcon() {
+        if (disabledIcon != null)
+            return disabledIcon;
+        if (icon == null)
+            return null;
+        return disabledIcon = new ImageIcon(GrayFilter.createDisabledImage(icon.getImage()));
     }
 
@@ -23,3 +120,36 @@
         return null;
     }
+
+    @Override
+    public int hashCode() {
+        int hash = super.hashCode();
+        hash = 17 * hash + (annotate ? 1 : 0);
+        hash = 17 * hash + (annotation_key != null ? annotation_key.hashCode() : 0);
+        hash = 17 * hash + (icon != null ? icon.getImage().hashCode() : 0);
+        return hash;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null || getClass() != obj.getClass())
+            return false;
+        if (!super.equals(obj))
+            return false;
+
+        final NodeElemStyle other = (NodeElemStyle) obj;
+        // we should get the same image object due to caching
+        if (icon != other.icon && (icon == null || other.icon == null || icon.getImage() != other.icon.getImage()))
+            return false;
+        if (annotate != other.annotate)
+            return false;
+        if (!Utils.equal(annotation_key, annotation_key))
+            return false;
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return "NodeElemStyle{" + super.toString() + "annotate=" + annotate + " annotation_key=" + annotation_key + " icon=" + icon + '}';
+    }
+
 }
Index: trunk/src/org/openstreetmap/josm/gui/mappaint/Range.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/mappaint/Range.java	(revision 3836)
+++ trunk/src/org/openstreetmap/josm/gui/mappaint/Range.java	(revision 3836)
@@ -0,0 +1,79 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.mappaint;
+
+/**
+ * An interval of the form "lower < x <= upper" where 0 <= lower < upper.
+ * (upper can be Double.POSITIVE_INFINITY)
+ * immutable class
+ */
+public class Range {
+    private double lower;
+    private double upper;
+
+    public Range() {
+        this.lower = 0;
+        this.upper = Double.POSITIVE_INFINITY;
+    }
+
+    public Range(double lower, double upper) {
+        if (lower < 0 || lower >= upper)
+            throw new IllegalArgumentException();
+        this.lower = lower;
+        this.upper = upper;
+    }
+
+    public boolean contains(double x) {
+        return lower < x && x <= upper;
+    }
+
+    /**
+     * provides the intersection of 2 overlapping ranges
+     */
+    public static Range cut(Range a, Range b) {
+        if (b.lower >= a.upper || b.upper <= a.lower)
+            throw new IllegalArgumentException();
+        return new Range(Math.max(a.lower, b.lower), Math.min(a.upper, b.upper));
+    }
+
+    /**
+     * under the premise, that x is within this range,
+     * and not within the other range, it shrinks this range in a way
+     * to exclude the other range, but still contain x.
+     *
+     * x                  |
+     *
+     * this   (------------------------------]
+     *
+     * other                   (-------]  or
+     *                         (-----------------]
+     *
+     * result (----------------]
+     */
+    public Range reduceAround(double x, Range other) {
+        if (!contains(x))
+            throw new IllegalArgumentException();
+        if (other.contains(x))
+            throw new IllegalArgumentException();
+
+        if (x < other.lower && other.lower < upper)
+            return new Range(lower, other.lower);
+
+        if (this.lower < other.upper && other.upper < x)
+            return new Range(other.upper, this.upper);
+
+        return this;
+    }
+
+    public double getLower() {
+        return lower;
+    }
+
+    public double getUpper() {
+        return upper;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("|s%s-%s", lower, upper);
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/mappaint/SimpleNodeElemStyle.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/mappaint/SimpleNodeElemStyle.java	(revision 3835)
+++ 	(revision )
@@ -1,68 +1,0 @@
-// License: GPL. For details, see LICENSE file.
-package org.openstreetmap.josm.gui.mappaint;
-
-import java.awt.Color;
-import org.openstreetmap.josm.data.osm.Node;
-import org.openstreetmap.josm.data.osm.OsmPrimitive;
-import org.openstreetmap.josm.data.osm.visitor.paint.MapPaintSettings;
-import org.openstreetmap.josm.data.osm.visitor.paint.MapPainter;
-
-public class SimpleNodeElemStyle extends NodeElemStyle {
-
-    public static final SimpleNodeElemStyle INSTANCE = new SimpleNodeElemStyle();
-
-    private SimpleNodeElemStyle() {
-        super(0, Long.MAX_VALUE);
-        annotate = true;
-    }
-
-    private static int max(int a, int b, int c, int d) {
-        return Math.max(Math.max(a, b), Math.max(c, d));
-    }
-
-    @Override
-    public void paintPrimitive(OsmPrimitive primitive, MapPaintSettings settings, MapPainter painter,
-            boolean selected, boolean member) {
-        Node n = (Node)primitive;
-
-        if (n.isHighlighted()) {
-            painter.drawNode(n, settings.getHighlightColor(), settings.getSelectedNodeSize(), settings.isFillSelectedNode(), getName(n, painter));
-        } else {
-
-            Color color;
-            boolean isConnection = n.isConnectionNode();
-
-            if (painter.isInactive() || n.isDisabled()) {
-                color = settings.getInactiveColor();
-            } else if (selected) {
-                color = settings.getSelectedColor();
-            } else if (member) {
-                color = settings.getRelationSelectedColor();
-            } else if (isConnection) {
-                if (n.isTagged()) {
-                    color = settings.getTaggedConnectionColor();
-                } else {
-                    color = settings.getConnectionColor();
-                }
-            } else {
-                if (n.isTagged()) {
-                    color = settings.getTaggedColor();
-                } else {
-                    color = settings.getNodeColor();
-                }
-            }
-
-            final int size = max((selected ? settings.getSelectedNodeSize() : 0),
-                                    (n.isTagged() ? settings.getTaggedNodeSize() : 0),
-                                    (isConnection ? settings.getConnectionNodeSize() : 0),
-                                    settings.getUnselectedNodeSize());
-
-            final boolean fill = (selected && settings.isFillSelectedNode()) ||
-                                    (n.isTagged() && settings.isFillTaggedNode()) ||
-                                    (isConnection && settings.isFillConnectionNode()) ||
-                                    settings.isFillUnselectedNode();
-
-            painter.drawNode(n, color, size, fill, getName(n, painter));
-        }
-    }
-}
Index: trunk/src/org/openstreetmap/josm/gui/mappaint/StyleCache.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/mappaint/StyleCache.java	(revision 3835)
+++ trunk/src/org/openstreetmap/josm/gui/mappaint/StyleCache.java	(revision 3836)
@@ -5,60 +5,220 @@
 import java.util.Arrays;
 import java.util.Collection;
-import java.util.Collections;
+import java.util.Iterator;
 import java.util.List;
 
-import org.openstreetmap.josm.data.osm.OsmPrimitive;
 import org.openstreetmap.josm.data.osm.Storage;
-import org.openstreetmap.josm.data.osm.visitor.paint.MapPaintSettings;
-import org.openstreetmap.josm.data.osm.visitor.paint.MapPainter;
-
+import org.openstreetmap.josm.tools.Pair;
+import org.openstreetmap.josm.tools.Utils;
+
+/**
+ * Caches styles for a single primitive.
+ * Splits the range of possible scale values (0 < scale < +Infinity) into multiple
+ * subranges, for each scale range it keeps a list of styles.
+ * Immutable class, equals & hashCode is required (the same for StyleList, ElemStyle
+ * and its subclasses).
+ */
 public class StyleCache {
-
-    private List<ElemStyle> styles;
+    /* list of boundaries for the scale ranges */
+    ArrayList<Double> bd;
+    /* styles for each scale range */
+    ArrayList<StyleList> data;
 
     private final static Storage<StyleCache> internPool = new Storage<StyleCache>(); // TODO: clean up the intern pool from time to time (after purge or layer removal)
 
-    public final static StyleCache EMPTY_STYLECACHE = create();
-    public final static StyleCache SIMPLE_NODE_STYLECACHE = create(SimpleNodeElemStyle.INSTANCE);
-    public final static StyleCache UNTAGGED_WAY_STYLECACHE = create(LineElemStyle.UNTAGGED_WAY);
-
+    public final static StyleCache EMPTY_STYLECACHE = (new StyleCache()).intern();
     
     private StyleCache() {
-        styles = new ArrayList<ElemStyle>();
-    }
-
-    public static StyleCache create() {
-        StyleCache sc = new StyleCache();
-        sc.styles = new ArrayList<ElemStyle>();
-        return sc.intern();
-    }
-
-    public static StyleCache create(ElemStyle... styles) {
-        StyleCache sc = new StyleCache();
-        sc.styles = Arrays.asList(styles);
-        return sc.intern();
-    }
-
-    public static StyleCache create(Collection<ElemStyle> styles) {
-        StyleCache sc = new StyleCache();
-        sc.styles = new ArrayList<ElemStyle>(styles);
-        return sc.intern();
-    }
-
-    public Collection<ElemStyle> getStyles() {
-        return Collections.unmodifiableList(styles);
-    }
-
-    /**
-     * like String.intern() (reduce memory consumption)
+        bd = new ArrayList<Double>();
+        bd.add(0.0);
+        bd.add(Double.POSITIVE_INFINITY);
+        data = new ArrayList<StyleList>();
+        data.add(null);
+    }
+
+    private StyleCache(StyleCache s) {
+        bd = new ArrayList<Double>(s.bd);
+        data = new ArrayList<StyleList>(s.data);
+    }
+
+    /**
+     * List of Styles, immutable
+     */
+    public static class StyleList implements Iterable<ElemStyle>
+    {
+        private List<ElemStyle> lst;
+
+        public static final StyleList SIMPLE_NODE = new StyleList(NodeElemStyle.SIMPLE_NODE_ELEMSTYLE);
+
+        public StyleList() {
+            lst = new ArrayList<ElemStyle>();
+        }
+
+        public StyleList(ElemStyle... init) {
+            lst = new ArrayList<ElemStyle>(Arrays.asList(init));
+        }
+
+        public StyleList(Collection<ElemStyle> sl) {
+            lst = new ArrayList<ElemStyle>(sl);
+        }
+
+        public StyleList(StyleList sl, ElemStyle s) {
+            lst = new ArrayList<ElemStyle>(sl.lst);
+            lst.add(s);
+        }
+
+        @Override
+        public Iterator<ElemStyle> iterator() {
+            return lst.iterator();
+        }
+
+        public boolean isEmpty() {
+            return lst.isEmpty();
+        }
+
+        public int size() {
+            return lst.size();
+        }
+
+        @Override
+        public String toString() {
+            return lst.toString();
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj == null || getClass() != obj.getClass())
+                return false;
+            final StyleList other = (StyleList) obj;
+            return Utils.equal(lst, other.lst);
+        }
+
+        @Override
+        public int hashCode() {
+            return lst.hashCode();
+        }
+    }
+
+    /**
+     * looks up styles for a certain scale value
+     */
+    public StyleList get(double scale) {
+        if (scale <= 0)
+            throw new IllegalArgumentException();
+        for (int i=0; i<data.size(); ++i) {
+            if (bd.get(i) < scale && scale <= bd.get(i+1)) {
+                return data.get(i);
+            }
+        }
+        throw new AssertionError();
+    }
+
+    /**
+     * looks up styles for a certain scale value and additionally returns
+     * the scale range for the returned styles
+     */
+    public Pair<StyleList, Range> getWithRange(double scale) {
+        if (scale <= 0)
+            throw new IllegalArgumentException();
+        for (int i=0; i<data.size(); ++i) {
+            if (bd.get(i) < scale && scale <= bd.get(i+1)) {
+                return new Pair<StyleList, Range>(data.get(i), new Range(bd.get(i), bd.get(i+1)));
+            }
+        }
+        throw new AssertionError();
+    }
+
+    public StyleCache put(StyleList sl, Range r) {
+        return put(sl, r.getLower(), r.getUpper());
+    }
+
+    /**
+     * add a new styles to the cache. this is only possible, if
+     * for this scale range, there is nothing in the cache yet.
+     */
+    public StyleCache put(StyleList sl, double lower, double upper) {
+        StyleCache s = new StyleCache(this);
+        s.putImpl(sl, lower, upper);
+        s.consistencyTest();
+        return s.intern();
+    }
+
+    /**
+     * ASCII-art explanation:
+     *
+     *              data[i]
+     *  --|-------|---------|--
+     * bd[i-1]  bd[i]    bd[i+1]
+     *
+     *         (--------]
+     *       lower     upper
+     */
+    private void putImpl(StyleList sl, double lower, double upper) {
+        int i=0;
+        while (bd.get(i) < lower) {
+            ++i;
+        }
+        if (bd.get(i) == lower) {
+            if (upper > bd.get(i+1))
+                throw new AssertionError("the new range must be within a single subrange");
+            if (data.get(i) != null)
+                throw new AssertionError("the new range must be within a subrange that has no data");
+
+            //  --|-------|--------|--
+            //   i-1      i       i+1
+            //            (--------]
+            if (bd.get(i+1) == upper) {
+                data.set(i, sl);
+            }
+            //  --|-------|--------|--
+            //   i-1      i       i+1
+            //            (-----]
+            else {
+                bd.add(i+1, upper);
+                data.add(i, sl);
+            }
+            return;
+        } else {
+            if (bd.get(i) < upper)
+                throw new AssertionError("the new range must be within a single subrange");
+            if (data.get(i-1) != null)
+                throw new AssertionError();
+
+            //  --|-------|--------|--
+            //   i-1      i       i+1
+            //       (--]   or
+            //       (----]
+            bd.add(i, lower);
+            data.add(i, sl);
+            
+            //  --|--|----|--------|--
+            //   i-1 i   i+1      i+2
+            //       (--]
+            if (bd.get(i+1) > upper) {
+                bd.add(i+1, upper);
+                data.add(i+1, null);
+            }
+            return;
+        }
+    }
+    
+    public void consistencyTest() {
+        if (bd.size() < 2) throw new AssertionError();
+        if (data.size() < 1) throw new AssertionError();
+        if (bd.size() != data.size() + 1) throw new AssertionError();
+        if (bd.get(0) != 0) throw new AssertionError();
+        if (bd.get(bd.size() - 1) != Double.POSITIVE_INFINITY) throw new AssertionError();
+        for (int i=0; i<data.size() - 1; ++i) {
+            if (bd.get(i) >= bd.get(i + 1)) throw new AssertionError();
+        }
+    }
+
+    /**
+     * Like String.intern() (reduce memory consumption).
+     * StyleCache must not be changed after it has
+     * been added to the intern pool.
      */
     public StyleCache intern() {
         return internPool.putUnique(this);
-    }
-
-    public void paint(OsmPrimitive primitive, MapPaintSettings paintSettings, MapPainter painter, boolean selected, boolean member) {
-        for (ElemStyle s : styles) {
-            s.paintPrimitive(primitive, paintSettings, painter, selected, member);
-        }
     }
 
@@ -67,15 +227,19 @@
         if (obj == null || getClass() != obj.getClass())
             return false;
-        return styles.equals(((StyleCache) obj).styles);
+        final StyleCache other = (StyleCache) obj;
+        return bd.equals(other.bd) && data.equals(other.data);
     }
 
     @Override
     public int hashCode() {
-        return styles.hashCode();
+        int hash = 7;
+        hash = 23 * hash + bd.hashCode();
+        hash = 23 * hash + data.hashCode();
+        return hash;
     }
 
     @Override
     public String toString() {
-        return "SC{" + styles + '}';
+        return "SC{" + bd + ' ' + data + '}';
     }
 }
Index: trunk/src/org/openstreetmap/josm/gui/mappaint/xml/AreaPrototype.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/mappaint/xml/AreaPrototype.java	(revision 3835)
+++ trunk/src/org/openstreetmap/josm/gui/mappaint/xml/AreaPrototype.java	(revision 3836)
@@ -4,12 +4,12 @@
 import java.awt.Color;
 
-import org.openstreetmap.josm.gui.mappaint.AreaElemStyle;
+import org.openstreetmap.josm.gui.mappaint.Range;
 
 public class AreaPrototype extends Prototype {
     public Color color;
-    public boolean closed;
+    public boolean closed; // if true, it does not apply to unclosed ways
 
-    public AreaPrototype (AreaPrototype a, long maxScale, long minScale) {
-        super(maxScale, minScale);
+    public AreaPrototype (AreaPrototype a, Range range) {
+        super(range);
         this.color = a.color;
         this.closed = a.closed;
@@ -22,11 +22,8 @@
     public void init()
     {
+        priority = 0;
+        range = new Range();
         closed = false;
         color = null;
-        priority = 0;
-    }
-
-    public AreaElemStyle createStyle() {
-        return new AreaElemStyle(minScale, maxScale, color);
     }
 }
Index: trunk/src/org/openstreetmap/josm/gui/mappaint/xml/IconPrototype.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/mappaint/xml/IconPrototype.java	(revision 3835)
+++ trunk/src/org/openstreetmap/josm/gui/mappaint/xml/IconPrototype.java	(revision 3836)
@@ -2,17 +2,14 @@
 package org.openstreetmap.josm.gui.mappaint.xml;
 
-import org.openstreetmap.josm.gui.mappaint.IconElemStyle;
-import org.openstreetmap.josm.gui.mappaint.MapPaintStyles;
 import org.openstreetmap.josm.gui.mappaint.MapPaintStyles.IconReference;
-import org.openstreetmap.josm.gui.mappaint.NodeElemStyle;
-import org.openstreetmap.josm.gui.mappaint.SimpleNodeElemStyle;
+import org.openstreetmap.josm.gui.mappaint.Range;
 
 public class IconPrototype extends Prototype {
     
     public IconReference icon;
-    public boolean annotate;
+    public Boolean annotate;
 
-    public IconPrototype (IconPrototype i, long maxScale, long minScale) {
-        super(maxScale, minScale);
+    public IconPrototype (IconPrototype i, Range range) {
+        super(range);
         this.icon = i.icon;
         this.annotate = i.annotate;
@@ -24,17 +21,8 @@
 
     public void init() {
+        priority = 0;
+        range = new Range();
         icon = null;
-        priority = 0;
-        annotate = true;
-    }
-
-    public NodeElemStyle createStyle() {
-        if (icon == null) {
-            return SimpleNodeElemStyle.INSTANCE;
-        } else {
-            IconElemStyle i = new IconElemStyle(minScale, maxScale, MapPaintStyles.getIcon(icon));
-            i.annotate = annotate;
-            return i;
-        }
+        annotate = null;
     }
 }
Index: trunk/src/org/openstreetmap/josm/gui/mappaint/xml/LinePrototype.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/mappaint/xml/LinePrototype.java	(revision 3835)
+++ trunk/src/org/openstreetmap/josm/gui/mappaint/xml/LinePrototype.java	(revision 3836)
@@ -6,5 +6,5 @@
 import org.openstreetmap.josm.data.osm.visitor.paint.MapPaintSettings;
 import org.openstreetmap.josm.data.osm.visitor.paint.PaintColors;
-import org.openstreetmap.josm.gui.mappaint.LineElemStyle;
+import org.openstreetmap.josm.gui.mappaint.Range;
 import org.openstreetmap.josm.tools.I18n;
 
@@ -12,11 +12,11 @@
 
     protected int width;
-    public int realWidth; //the real width of this line in meter
+    public Integer realWidth; // the real width of this line in meter
     public Color color;
     protected float[] dashed;
     public Color dashedColor;
 
-    public LinePrototype(LinePrototype s, long maxScale, long minScale) {
-        super(maxScale, minScale);
+    public LinePrototype(LinePrototype s, Range range) {
+        super(range);
         this.width = s.width;
         this.realWidth = s.realWidth;
@@ -32,9 +32,10 @@
     public void init()
     {
+        priority = 0;
+        range = new Range();
         width = -1;
-        realWidth = 0;
-        dashed = new float[0];
+        realWidth = null;
+        dashed = null;
         dashedColor = null;
-        priority = 0;
         color = PaintColors.UNTAGGED.get();
     }
@@ -45,5 +46,5 @@
 
     public void setDashed(float[] dashed) {
-        if (dashed.length == 0) {
+        if (dashed == null || dashed.length == 0) {
             this.dashed = dashed;
             return;
@@ -75,7 +76,3 @@
         this.width = width;
     }
-
-    public LineElemStyle createStyle() {
-        return new LineElemStyle(minScale, maxScale, width, realWidth, color, dashed, dashedColor);
-    }
 }
Index: trunk/src/org/openstreetmap/josm/gui/mappaint/xml/LinemodPrototype.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/mappaint/xml/LinemodPrototype.java	(revision 3835)
+++ trunk/src/org/openstreetmap/josm/gui/mappaint/xml/LinemodPrototype.java	(revision 3836)
@@ -2,5 +2,5 @@
 package org.openstreetmap.josm.gui.mappaint.xml;
 
-import org.openstreetmap.josm.gui.mappaint.LineElemStyle;
+import org.openstreetmap.josm.gui.mappaint.Range;
 
 public class LinemodPrototype extends LinePrototype implements Comparable<LinemodPrototype> {
@@ -11,6 +11,6 @@
     public WidthMode widthMode;
 
-    public LinemodPrototype(LinemodPrototype s, long maxScale, long minScale) {
-        super(s, maxScale, minScale);
+    public LinemodPrototype(LinemodPrototype s, Range range) {
+        super(s, range);
         this.over = s.over;
         this.widthMode = s.widthMode;
@@ -28,7 +28,7 @@
 
     // get width for overlays
-    public int getWidth(int ref)
+    public float getWidth(float ref)
     {
-        int res;
+        float res;
         if(widthMode == WidthMode.ABSOLUTE) {
             res = width;
@@ -60,16 +60,3 @@
             return 0;
     }
-
-    /**
-     * this method cannot be used for LinemodPrototypes
-     *  - use createStyle(int) instead
-     */
-    @Override
-    public LineElemStyle createStyle() {
-        throw new UnsupportedOperationException();
-    }
-
-    public LineElemStyle createStyle(int refWidth) {
-        return new LineElemStyle(minScale, maxScale, getWidth(refWidth), realWidth, color, dashed, dashedColor);
-    }
 }
Index: trunk/src/org/openstreetmap/josm/gui/mappaint/xml/Prototype.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/mappaint/xml/Prototype.java	(revision 3835)
+++ trunk/src/org/openstreetmap/josm/gui/mappaint/xml/Prototype.java	(revision 3836)
@@ -6,9 +6,9 @@
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
 import org.openstreetmap.josm.data.osm.OsmUtils;
+import org.openstreetmap.josm.gui.mappaint.Range;
 
 abstract public class Prototype {
     // zoom range to display the feature
-    public long minScale;
-    public long maxScale;
+    public Range range;
 
     public int priority;
@@ -16,20 +16,9 @@
     public Collection<XmlCondition> conditions = null;
 
-    public Prototype(long maxScale, long minScale) {
-        this.maxScale = maxScale;
-        this.minScale = minScale;
+    public Prototype(Range range) {
+        this.range = range;
     }
 
     public Prototype() {
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        return (o instanceof Prototype) && (((Prototype) o).getCode().equals(getCode()));
-    }
-
-    @Override
-    public int hashCode() {
-        return getClass().hashCode();
     }
 
Index: trunk/src/org/openstreetmap/josm/gui/mappaint/xml/XmlStyleSource.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/mappaint/xml/XmlStyleSource.java	(revision 3835)
+++ trunk/src/org/openstreetmap/josm/gui/mappaint/xml/XmlStyleSource.java	(revision 3836)
@@ -10,10 +10,15 @@
 import java.util.List;
 
+import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.osm.Node;
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
 import org.openstreetmap.josm.data.osm.OsmUtils;
+import org.openstreetmap.josm.data.osm.Relation;
 import org.openstreetmap.josm.data.osm.Way;
-import org.openstreetmap.josm.gui.mappaint.ElemStyles.WayPrototypesRecord;
+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.preferences.SourceEntry;
+import org.openstreetmap.josm.tools.Utils;
 
 public class XmlStyleSource extends SourceEntry {
@@ -39,27 +44,56 @@
     }
 
-    public IconPrototype getNode(OsmPrimitive primitive, IconPrototype icon) {
+    private static class WayPrototypesRecord {
+        public LinePrototype line;
+        public List<LinemodPrototype> linemods;
+        public AreaPrototype area;
+    }
+
+    private <T extends Prototype> T update(T current, T candidate, Double scale, MultiCascade mc) {
+        return requiresUpdate(current, candidate, scale, mc) ? candidate : current;
+    }
+
+    /**
+     * checks whether a certain match is better than the current match
+     * @param current can be null
+     * @param candidate the new Prototype that could be used instead
+     * @param scale ignored if null, otherwise checks if scale is within the range of candidate
+     * @param mc side effect: update the valid region for the current MultiCascade
+     */
+    private boolean requiresUpdate(Prototype current, Prototype candidate, Double scale, MultiCascade mc) {
+        if (current == null || candidate.priority >= current.priority) {
+            if (scale == null)
+                return true;
+
+            if (candidate.range.contains(scale)) {
+                mc.range = Range.cut(mc.range, candidate.range);
+                return true;
+            } else {
+                mc.range = mc.range.reduceAround(scale, candidate.range);
+                return false;
+            }
+        }
+        return false;
+    }
+
+    private IconPrototype getNode(OsmPrimitive primitive, Double scale, MultiCascade mc) {
+        IconPrototype icon = null;
         for (String key : primitive.keySet()) {
             String val = primitive.get(key);
-            IconPrototype style;
-            if ((style = icons.get("n" + key + "=" + val)) != null) {
-                if (icon == null || style.priority >= icon.priority) {
-                    icon = style;
-                }
-            }
-            if ((style = icons.get("b" + key + "=" + OsmUtils.getNamedOsmBoolean(val))) != null) {
-                if (icon == null || style.priority >= icon.priority) {
-                    icon = style;
-                }
-            }
-            if ((style = icons.get("x" + key)) != null) {
-                if (icon == null || style.priority >= icon.priority) {
-                    icon = style;
-                }
+            IconPrototype p;
+            if ((p = icons.get("n" + key + "=" + val)) != null) {
+                icon = update(icon, p, scale, mc);
+            }
+            if ((p = icons.get("b" + key + "=" + OsmUtils.getNamedOsmBoolean(val))) != null) {
+                icon = update(icon, p, scale, mc);
+            }
+            if ((p = icons.get("x" + key)) != null) {
+                icon = update(icon, p, scale, mc);
             }
         }
         for (IconPrototype s : iconsList) {
-            if ((icon == null || s.priority >= icon.priority) && s.check(primitive)) {
-                icon = s;
+            if (s.check(primitive))
+            {
+                icon = update(icon, s, scale, mc);
             }
         }
@@ -72,5 +106,5 @@
      *  multipolygon relations.
      */
-    public void get(OsmPrimitive primitive, boolean closed, WayPrototypesRecord p) {
+    private void get(OsmPrimitive primitive, boolean closed, WayPrototypesRecord p, Double scale, MultiCascade mc) {
         String lineIdx = null;
         HashMap<String, LinemodPrototype> overlayMap = new HashMap<String, LinemodPrototype>();
@@ -81,54 +115,68 @@
             LinemodPrototype styleLinemod;
             String idx = "n" + key + "=" + val;
-            if ((styleArea = areas.get(idx)) != null && (p.area == null || styleArea.priority >= p.area.priority) && (closed || !styleArea.closed)) {
-                p.area = styleArea;
-            }
-            if ((styleLine = lines.get(idx)) != null && (p.line == null || styleLine.priority >= p.line.priority)) {
-                p.line = styleLine;
-                lineIdx = idx;
+            if ((styleArea = areas.get(idx)) != null && (closed || !styleArea.closed)) {
+                p.area = update(p.area, styleArea, scale, mc);
+            }
+            if ((styleLine = lines.get(idx)) != null) {
+                if (requiresUpdate(p.line, styleLine, scale, mc)) {
+                    p.line = styleLine;
+                    lineIdx = idx;
+                }
             }
             if ((styleLinemod = modifiers.get(idx)) != null) {
-                overlayMap.put(idx, styleLinemod);
+                if (requiresUpdate(null, styleLinemod, scale, mc)) {
+                    overlayMap.put(idx, styleLinemod);
+                }
             }
             idx = "b" + key + "=" + OsmUtils.getNamedOsmBoolean(val);
-            if ((styleArea = areas.get(idx)) != null && (p.area == null || styleArea.priority >= p.area.priority) && (closed || !styleArea.closed)) {
-                p.area = styleArea;
-            }
-            if ((styleLine = lines.get(idx)) != null && (p.line == null || styleLine.priority >= p.line.priority)) {
-                p.line = styleLine;
-                lineIdx = idx;
+            if ((styleArea = areas.get(idx)) != null && (closed || !styleArea.closed)) {
+                p.area = update(p.area, styleArea, scale, mc);
+            }
+            if ((styleLine = lines.get(idx)) != null) {
+                if (requiresUpdate(p.line, styleLine, scale, mc)) {
+                    p.line = styleLine;
+                    lineIdx = idx;
+                }
             }
             if ((styleLinemod = modifiers.get(idx)) != null) {
-                overlayMap.put(idx, styleLinemod);
+                if (requiresUpdate(null, styleLinemod, scale, mc)) {
+                    overlayMap.put(idx, styleLinemod);
+                }
             }
             idx = "x" + key;
-            if ((styleArea = areas.get(idx)) != null && (p.area == null || styleArea.priority >= p.area.priority) && (closed || !styleArea.closed)) {
-                p.area = styleArea;
-            }
-            if ((styleLine = lines.get(idx)) != null && (p.line == null || styleLine.priority >= p.line.priority)) {
-                p.line = styleLine;
-                lineIdx = idx;
+            if ((styleArea = areas.get(idx)) != null && (closed || !styleArea.closed)) {
+                p.area = update(p.area, styleArea, scale, mc);
+            }
+            if ((styleLine = lines.get(idx)) != null) {
+                if (requiresUpdate(p.line, styleLine, scale, mc)) {
+                    p.line = styleLine;
+                    lineIdx = idx;
+                }
             }
             if ((styleLinemod = modifiers.get(idx)) != null) {
-                overlayMap.put(idx, styleLinemod);
+                if (requiresUpdate(null, styleLinemod, scale, mc)) {
+                    overlayMap.put(idx, styleLinemod);
+                }
             }
         }
         for (AreaPrototype s : areasList) {
-            if ((p.area == null || s.priority >= p.area.priority) && (closed || !s.closed) && s.check(primitive)) {
-                p.area = s;
+            if ((closed || !s.closed) && s.check(primitive)) {
+                p.area = update(p.area, s, scale, mc);
             }
         }
         for (LinePrototype s : linesList) {
-            if ((p.line == null || s.priority >= p.line.priority) && s.check(primitive)) {
-                p.line = s;
+            if (s.check(primitive)) {
+                p.line = update(p.line, s, scale, mc);
             }
         }
         for (LinemodPrototype s : modifiersList) {
             if (s.check(primitive)) {
-                overlayMap.put(s.getCode(), s);
+                if (requiresUpdate(null, s, scale, mc)) {
+                    overlayMap.put(s.getCode(), s);
+                }
             }
         }
         overlayMap.remove(lineIdx); // do not use overlay if linestyle is from the same rule (example: railway=tram)
-        if (!overlayMap.isEmpty() && p.line != null) {
+        if (!overlayMap.isEmpty()) {
             List<LinemodPrototype> tmp = new LinkedList<LinemodPrototype>();
             if (p.linemods != null) {
@@ -139,35 +187,4 @@
             p.linemods = tmp;
         }
-    }
-
-    public boolean isArea(OsmPrimitive o) {
-        if (o.hasKeys() && !(o instanceof Node)) {
-            boolean noclosed = o instanceof Way && !((Way) o).isClosed();
-            Iterator<String> iterator = o.keySet().iterator();
-            while (iterator.hasNext()) {
-                String key = iterator.next();
-                String val = o.get(key);
-                AreaPrototype s = areas.get("n" + key + "=" + val);
-                if (s == null || (s.closed && noclosed)) {
-                    s = areas.get("b" + key + "=" + OsmUtils.getNamedOsmBoolean(val));
-                }
-                if (s == null || (s.closed && noclosed)) {
-                    s = areas.get("x" + key);
-                }
-                if (s != null && !(s.closed && noclosed)) {
-                    return true;
-                }
-            }
-            for (AreaPrototype s : areasList) {
-                if (!(s.closed && noclosed) && s.check(o)) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    public boolean hasAreas() {
-        return areas.size() > 0;
     }
 
@@ -202,3 +219,70 @@
          }
      }
+
+    public void apply(MultiCascade mc, OsmPrimitive osm, double scale, OsmPrimitive multipolyOuterWay, boolean pretendWayIsClosed) {
+        Cascade def = mc.getCascade("default");
+        boolean useMinMaxScale = Main.pref.getBoolean("mappaint.zoomLevelDisplay", false);
+
+        if (osm instanceof Node || (osm instanceof Relation && "restriction".equals(osm.get("type")))) {
+            IconPrototype icon = getNode(osm, (useMinMaxScale ? scale : null), mc);
+            if (icon != null) {
+                def.put("icon-image", icon.icon);
+                if (osm instanceof Node) {
+                    if (icon.annotate != null) {
+                        if (icon.annotate) {
+                            def.put("text", "yes");
+                        } else {
+                            def.remove("text");
+                        }
+                    }
+                }
+            }
+        } else if (osm instanceof Way || (osm instanceof Relation && "multipolygon".equals(osm.get("type")))) {
+            WayPrototypesRecord p = new WayPrototypesRecord();
+            get(osm, pretendWayIsClosed || !(osm instanceof Way) || ((Way) osm).isClosed(), p, (useMinMaxScale ? scale : null), mc);
+            if (p.line != null) {
+                def.put("width", new Float(p.line.getWidth()));
+                def.putOrClear("real-width", p.line.realWidth != null ? new Float(p.line.realWidth) : null);
+                def.putOrClear("color", p.line.color);
+                def.putOrClear("dashes", p.line.getDashed());
+                def.putOrClear("dashes-background-color", p.line.dashedColor);
+            }
+            Float refWidth = def.get("width", null, Float.class);
+            if (refWidth != null && p.linemods != null) {
+                int numOver = 0, numUnder = 0;
+
+                while (mc.containsKey(String.format("over_%d", ++numOver))) {}
+                while (mc.containsKey(String.format("under_%d", ++numUnder))) {}
+
+                for (LinemodPrototype mod : p.linemods) {
+                    Cascade c;
+                    if (mod.over) {
+                        c = mc.getCascade(String.format("over_%d", numOver));
+                        c.put("object-z-index", new Float(numOver));
+                        ++numOver;
+                    } else {
+                        c = mc.getCascade(String.format("under_%d", numUnder));
+                        c.put("object-z-index", new Float(-numUnder));
+                        ++numUnder;
+                    }
+                    c.put("width", new Float(mod.getWidth(refWidth)));
+                    c.putOrClear("color", mod.color);
+                    c.putOrClear("dashes", mod.getDashed());
+                    c.putOrClear("dashes-background-color", mod.dashedColor);
+                }
+            }
+            if (multipolyOuterWay != null) {
+                WayPrototypesRecord p2 = new WayPrototypesRecord();
+                get(multipolyOuterWay, true, p2, (useMinMaxScale ? scale : null), mc);
+                if (Utils.equal(p.area, p2.area)) {
+                    p.area = null;
+                }
+            }
+            if (p.area != null) {
+                def.putOrClear("fill-color", p.area.color);
+                def.remove("fill-image");
+            }
+        }
+    }
+
 }
Index: trunk/src/org/openstreetmap/josm/gui/mappaint/xml/XmlStyleSourceHandler.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/mappaint/xml/XmlStyleSourceHandler.java	(revision 3835)
+++ trunk/src/org/openstreetmap/josm/gui/mappaint/xml/XmlStyleSourceHandler.java	(revision 3836)
@@ -8,4 +8,5 @@
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.gui.mappaint.MapPaintStyles.IconReference;
+import org.openstreetmap.josm.gui.mappaint.Range;
 import org.openstreetmap.josm.tools.ColorHelper;
 import org.xml.sax.Attributes;
@@ -23,6 +24,6 @@
         XmlCondition cond = new XmlCondition();
         Collection<XmlCondition> conditions;
-        long scaleMax;
-        long scaleMin;
+        double scaleMax;
+        double scaleMin;
         LinePrototype line = new LinePrototype();
         LinemodPrototype linemod = new LinemodPrototype();
@@ -32,5 +33,5 @@
         {
             conditions = null;
-            scaleMax = 1000000000;
+            scaleMax = Double.POSITIVE_INFINITY;
             scaleMin = 0;
             line.init();
@@ -102,5 +103,5 @@
                         dashed = new float[]{9};
                     } else {
-                        dashed = new float[0];
+                        dashed = null;
                     }
                 }
@@ -249,20 +250,20 @@
             {
                 style.add(rule.cond, rule.conditions,
-                        new LinePrototype(rule.line, rule.scaleMax, rule.scaleMin));
+                        new LinePrototype(rule.line, new Range(rule.scaleMin, rule.scaleMax)));
             }
             if(hadLineMod)
             {
                 style.add(rule.cond, rule.conditions,
-                        new LinemodPrototype(rule.linemod, rule.scaleMax, rule.scaleMin));
+                        new LinemodPrototype(rule.linemod, new Range(rule.scaleMin, rule.scaleMax)));
             }
             if(hadIcon)
             {
                 style.add(rule.cond, rule.conditions,
-                        new IconPrototype(rule.icon, rule.scaleMax, rule.scaleMin));
+                        new IconPrototype(rule.icon, new Range(rule.scaleMin, rule.scaleMax)));
             }
             if(hadArea)
             {
                 style.add(rule.cond, rule.conditions,
-                        new AreaPrototype(rule.area, rule.scaleMax, rule.scaleMin));
+                        new AreaPrototype(rule.area, new Range(rule.scaleMin, rule.scaleMax)));
             }
             inRule = false;
Index: trunk/src/org/openstreetmap/josm/tools/SubclassFilteredCollection.java
===================================================================
--- trunk/src/org/openstreetmap/josm/tools/SubclassFilteredCollection.java	(revision 3835)
+++ trunk/src/org/openstreetmap/josm/tools/SubclassFilteredCollection.java	(revision 3836)
@@ -13,5 +13,5 @@
  * @param <S> element type of the underlying collection
  * @param <T> element type of filtered collection (and subclass of S). The predicate
- *      must except only objects of type T.
+ *      must accept only objects of type T.
  */
 public class SubclassFilteredCollection<S, T extends S> extends AbstractCollection<T> {
@@ -50,5 +50,7 @@
             S old = current;
             current = null;
-            return (T) old;
+            // we are save because predicate only accepts objects of type T
+            @SuppressWarnings("unchecked") T res = (T) old;
+            return res;
         }
 
Index: trunk/src/org/openstreetmap/josm/tools/Utils.java
===================================================================
--- trunk/src/org/openstreetmap/josm/tools/Utils.java	(revision 3835)
+++ trunk/src/org/openstreetmap/josm/tools/Utils.java	(revision 3836)
@@ -4,10 +4,36 @@
 public class Utils {
 
-    public static <T> boolean exists(Iterable<? extends T> coll, Predicate<? super T> pred) {
-        for (T el : coll) {
-            if (pred.evaluate(el))
+    public static <T> boolean exists(Iterable<? extends T> collection, Predicate<? super T> predicate) {
+        for (T item : collection) {
+            if (predicate.evaluate(item))
                 return true;
         }
         return false;
+    }
+
+    public static <T> boolean exists(Iterable collection, Class<? extends T> klass) {
+        for (Object item : collection) {
+            if (klass.isInstance(item))
+                return true;
+        }
+        return false;
+    }
+
+    public static <T> T find(Iterable<? extends T> collection, Predicate<? super T> predicate) {
+        for (T item : collection) {
+            if (predicate.evaluate(item))
+                return item;
+        }
+        return null;
+    }
+
+    public static <T> T find(Iterable collection, Class<? extends T> klass) {
+        for (Object item : collection) {
+            if (klass.isInstance(item)) {
+                @SuppressWarnings("unchecked") T res = (T) item;
+                return res;
+            }
+        }
+        return null;
     }
 
@@ -28,9 +54,13 @@
     }
 
+    public static int max(int a, int b, int c, int d) {
+        return Math.max(Math.max(a, b), Math.max(c, d));
+    }
+
     /**
      * for convenience: test whether 2 objects are either both null or a.equals(b)
      */
     public static <T> boolean equal(T a, T b) {
-        if (a == null && b == null)
+        if (a == b)
             return true;
         return (a != null && a.equals(b));
