Index: /trunk/src/org/openstreetmap/josm/data/osm/visitor/paint/StyledMapRenderer.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/osm/visitor/paint/StyledMapRenderer.java	(revision 9277)
+++ /trunk/src/org/openstreetmap/josm/data/osm/visitor/paint/StyledMapRenderer.java	(revision 9278)
@@ -59,19 +59,19 @@
 import org.openstreetmap.josm.data.osm.visitor.paint.relations.MultipolygonCache;
 import org.openstreetmap.josm.gui.NavigatableComponent;
-import org.openstreetmap.josm.gui.mappaint.AreaElemStyle;
-import org.openstreetmap.josm.gui.mappaint.BoxTextElemStyle;
-import org.openstreetmap.josm.gui.mappaint.BoxTextElemStyle.HorizontalTextAlignment;
-import org.openstreetmap.josm.gui.mappaint.BoxTextElemStyle.VerticalTextAlignment;
-import org.openstreetmap.josm.gui.mappaint.ElemStyle;
 import org.openstreetmap.josm.gui.mappaint.ElemStyles;
-import org.openstreetmap.josm.gui.mappaint.MapImage;
 import org.openstreetmap.josm.gui.mappaint.MapPaintStyles;
-import org.openstreetmap.josm.gui.mappaint.NodeElemStyle;
-import org.openstreetmap.josm.gui.mappaint.NodeElemStyle.Symbol;
-import org.openstreetmap.josm.gui.mappaint.RepeatImageElemStyle.LineImageAlignment;
 import org.openstreetmap.josm.gui.mappaint.StyleCache.StyleList;
-import org.openstreetmap.josm.gui.mappaint.TextElement;
 import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSStyleSource;
 import org.openstreetmap.josm.gui.mappaint.mapcss.Selector;
+import org.openstreetmap.josm.gui.mappaint.styleelement.AreaElement;
+import org.openstreetmap.josm.gui.mappaint.styleelement.BoxTextElement;
+import org.openstreetmap.josm.gui.mappaint.styleelement.BoxTextElement.HorizontalTextAlignment;
+import org.openstreetmap.josm.gui.mappaint.styleelement.BoxTextElement.VerticalTextAlignment;
+import org.openstreetmap.josm.gui.mappaint.styleelement.MapImage;
+import org.openstreetmap.josm.gui.mappaint.styleelement.NodeElement;
+import org.openstreetmap.josm.gui.mappaint.styleelement.NodeElement.Symbol;
+import org.openstreetmap.josm.gui.mappaint.styleelement.RepeatImageElement.LineImageAlignment;
+import org.openstreetmap.josm.gui.mappaint.styleelement.StyleElement;
+import org.openstreetmap.josm.gui.mappaint.styleelement.TextLabel;
 import org.openstreetmap.josm.tools.CompositeList;
 import org.openstreetmap.josm.tools.Geometry;
@@ -190,9 +190,9 @@
 
     private static class StyleRecord implements Comparable<StyleRecord> {
-        private final ElemStyle style;
+        private final StyleElement style;
         private final OsmPrimitive osm;
         private final int flags;
 
-        StyleRecord(ElemStyle style, OsmPrimitive osm, int flags) {
+        StyleRecord(StyleElement style, OsmPrimitive osm, int flags) {
             this.style = style;
             this.osm = osm;
@@ -223,7 +223,7 @@
 
             // simple node on top of icons and shapes
-            if (this.style == NodeElemStyle.SIMPLE_NODE_ELEMSTYLE && other.style != NodeElemStyle.SIMPLE_NODE_ELEMSTYLE)
+            if (this.style == NodeElement.SIMPLE_NODE_ELEMSTYLE && other.style != NodeElement.SIMPLE_NODE_ELEMSTYLE)
                 return 1;
-            if (this.style != NodeElemStyle.SIMPLE_NODE_ELEMSTYLE && other.style == NodeElemStyle.SIMPLE_NODE_ELEMSTYLE)
+            if (this.style != NodeElement.SIMPLE_NODE_ELEMSTYLE && other.style == NodeElement.SIMPLE_NODE_ELEMSTYLE)
                 return -1;
 
@@ -418,5 +418,5 @@
      * @param text text style to use
      */
-    private void displayText(GlyphVector gv, String s, int x, int y, boolean disabled, TextElement text) {
+    private void displayText(GlyphVector gv, String s, int x, int y, boolean disabled, TextLabel text) {
         if (gv == null && s.isEmpty()) return;
         if (isInactiveMode || disabled) {
@@ -472,5 +472,5 @@
      */
     protected void drawArea(OsmPrimitive osm, Path2D.Double path, Color color,
-            MapImage fillImage, Float extent, Path2D.Double pfClip, boolean disabled, TextElement text) {
+            MapImage fillImage, Float extent, Path2D.Double pfClip, boolean disabled, TextLabel text) {
 
         Shape area = path.createTransformedShape(nc.getAffineTransform());
@@ -525,5 +525,5 @@
     }
 
-    private void drawAreaText(OsmPrimitive osm, TextElement text, Shape area) {
+    private void drawAreaText(OsmPrimitive osm, TextLabel text, Shape area) {
         if (text != null && isShowNames()) {
             // abort if we can't compose the label to be rendered
@@ -612,5 +612,5 @@
      * @param text The text to write on the area.
      */
-    public void drawArea(Relation r, Color color, MapImage fillImage, Float extent, Float extentThreshold, boolean disabled, TextElement text) {
+    public void drawArea(Relation r, Color color, MapImage fillImage, Float extent, Float extentThreshold, boolean disabled, TextLabel text) {
         Multipolygon multipolygon = MultipolygonCache.getInstance().get(nc, r);
         if (!r.isDisabled() && !multipolygon.getOuterWays().isEmpty()) {
@@ -648,5 +648,5 @@
      * @param text The text to write on the area.
      */
-    public void drawArea(Way w, Color color, MapImage fillImage, Float extent, Float extentThreshold, boolean disabled, TextElement text) {
+    public void drawArea(Way w, Color color, MapImage fillImage, Float extent, Float extentThreshold, boolean disabled, TextLabel text) {
         Path2D.Double pfClip = null;
         if (extent != null) {
@@ -678,10 +678,10 @@
     }
 
-    public void drawBoxText(Node n, BoxTextElemStyle bs) {
+    public void drawBoxText(Node n, BoxTextElement bs) {
         if (!isShowNames() || bs == null)
             return;
 
         Point p = nc.getPoint(n);
-        TextElement text = bs.text;
+        TextLabel text = bs.text;
         String s = text.labelCompositionStrategy.compose(n);
         if (s == null) return;
@@ -1204,5 +1204,5 @@
      * @param text The text definition (font/.../text content) to draw.
      */
-    public void drawTextOnPath(Way way, TextElement text) {
+    public void drawTextOnPath(Way way, TextLabel text) {
         if (way == null || text == null)
             return;
@@ -1826,5 +1826,5 @@
         public void add(Node osm, int flags) {
             StyleList sl = styles.get(osm, circum, nc);
-            for (ElemStyle s : sl) {
+            for (StyleElement s : sl) {
                 output.add(new StyleRecord(s, osm, flags));
             }
@@ -1833,8 +1833,8 @@
         public void add(Relation osm, int flags) {
             StyleList sl = styles.get(osm, circum, nc);
-            for (ElemStyle s : sl) {
-                if (drawMultipolygon && drawArea && s instanceof AreaElemStyle && (flags & FLAG_DISABLED) == 0) {
+            for (StyleElement s : sl) {
+                if (drawMultipolygon && drawArea && s instanceof AreaElement && (flags & FLAG_DISABLED) == 0) {
                     output.add(new StyleRecord(s, osm, flags));
-                } else if (drawRestriction && s instanceof NodeElemStyle) {
+                } else if (drawRestriction && s instanceof NodeElement) {
                     output.add(new StyleRecord(s, osm, flags));
                 }
@@ -1844,6 +1844,6 @@
         public void add(Way osm, int flags) {
             StyleList sl = styles.get(osm, circum, nc);
-            for (ElemStyle s : sl) {
-                if (!(drawArea && (flags & FLAG_DISABLED) == 0) && s instanceof AreaElemStyle) {
+            for (StyleElement s : sl) {
+                if (!(drawArea && (flags & FLAG_DISABLED) == 0) && s instanceof AreaElement) {
                     continue;
                 }
Index: /trunk/src/org/openstreetmap/josm/data/validation/tests/MultipolygonTest.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/validation/tests/MultipolygonTest.java	(revision 9277)
+++ /trunk/src/org/openstreetmap/josm/data/validation/tests/MultipolygonTest.java	(revision 9278)
@@ -31,5 +31,5 @@
 import org.openstreetmap.josm.data.validation.TestError;
 import org.openstreetmap.josm.gui.DefaultNameFormatter;
-import org.openstreetmap.josm.gui.mappaint.AreaElemStyle;
+import org.openstreetmap.josm.gui.mappaint.styleelement.AreaElement;
 import org.openstreetmap.josm.gui.mappaint.ElemStyles;
 import org.openstreetmap.josm.gui.mappaint.MapPaintStyles;
@@ -182,5 +182,5 @@
 
             if (styles != null && !"boundary".equals(r.get("type"))) {
-                AreaElemStyle area = ElemStyles.getAreaElemStyle(r, false);
+                AreaElement area = ElemStyles.getAreaElemStyle(r, false);
                 boolean areaStyle = area != null;
                 // If area style was not found for relation then use style of ways
@@ -206,5 +206,5 @@
                 if (area != null) {
                     for (Way wInner : polygon.getInnerWays()) {
-                        AreaElemStyle areaInner = ElemStyles.getAreaElemStyle(wInner, false);
+                        AreaElement areaInner = ElemStyles.getAreaElemStyle(wInner, false);
 
                         if (areaInner != null && area.equals(areaInner)) {
@@ -218,5 +218,5 @@
                     }
                     for (Way wOuter : polygon.getOuterWays()) {
-                        AreaElemStyle areaOuter = ElemStyles.getAreaElemStyle(wOuter, false);
+                        AreaElement areaOuter = ElemStyles.getAreaElemStyle(wOuter, false);
                         if (areaOuter != null) {
                             List<OsmPrimitive> l = new ArrayList<>();
Index: /trunk/src/org/openstreetmap/josm/gui/dialogs/InspectPrimitiveDialog.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/dialogs/InspectPrimitiveDialog.java	(revision 9277)
+++ /trunk/src/org/openstreetmap/josm/gui/dialogs/InspectPrimitiveDialog.java	(revision 9278)
@@ -39,5 +39,5 @@
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
 import org.openstreetmap.josm.gui.mappaint.Cascade;
-import org.openstreetmap.josm.gui.mappaint.ElemStyle;
+import org.openstreetmap.josm.gui.mappaint.styleelement.StyleElement;
 import org.openstreetmap.josm.gui.mappaint.ElemStyles;
 import org.openstreetmap.josm.gui.mappaint.MapPaintStyles;
@@ -363,5 +363,5 @@
                 txtMappaint.append(tr("\n\nList of generated Styles:\n"));
                 StyleList sl = elemstyles.get(osm, scale, nc);
-                for (ElemStyle s : sl) {
+                for (StyleElement s : sl) {
                     txtMappaint.append(" * ").append(s).append('\n');
                 }
Index: unk/src/org/openstreetmap/josm/gui/mappaint/AreaElemStyle.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/mappaint/AreaElemStyle.java	(revision 9277)
+++ 	(revision )
@@ -1,151 +1,0 @@
-// License: GPL. For details, see LICENSE file.
-package org.openstreetmap.josm.gui.mappaint;
-
-import java.awt.Color;
-import java.util.Objects;
-
-import org.openstreetmap.josm.Main;
-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;
-import org.openstreetmap.josm.data.osm.visitor.paint.PaintColors;
-import org.openstreetmap.josm.data.osm.visitor.paint.StyledMapRenderer;
-import org.openstreetmap.josm.gui.mappaint.MapPaintStyles.IconReference;
-import org.openstreetmap.josm.tools.CheckParameterUtil;
-import org.openstreetmap.josm.tools.Utils;
-
-public class AreaElemStyle extends ElemStyle {
-
-    /**
-     * If fillImage == null, color is the fill-color, otherwise
-     * an arbitrary color value sampled from the fillImage
-     */
-    public Color color;
-    public MapImage fillImage;
-    public TextElement text;
-    public Float extent;
-    public Float extentThreshold;
-
-    protected AreaElemStyle(Cascade c, Color color, MapImage fillImage, Float extent, Float extentThreshold, TextElement text) {
-        super(c, 1f);
-        CheckParameterUtil.ensureParameterNotNull(color);
-        this.color = color;
-        this.fillImage = fillImage;
-        this.extent = extent;
-        this.extentThreshold = extentThreshold;
-        this.text = text;
-    }
-
-    public static AreaElemStyle create(final Environment env) {
-        final Cascade c = env.mc.getCascade(env.layer);
-        MapImage fillImage = null;
-        Color color;
-
-        IconReference iconRef = c.get(FILL_IMAGE, null, IconReference.class);
-        if (iconRef != null) {
-            fillImage = new MapImage(iconRef.iconName, iconRef.source, false);
-
-            color = new Color(fillImage.getImage(false).getRGB(
-                    fillImage.getWidth() / 2, fillImage.getHeight() / 2)
-            );
-
-            fillImage.alpha = Math.min(255, Math.max(0, Integer.valueOf(Main.pref.getInteger("mappaint.fill-image-alpha", 255))));
-            Integer pAlpha = Utils.color_float2int(c.get(FILL_OPACITY, null, float.class));
-            if (pAlpha != null) {
-                fillImage.alpha = pAlpha;
-            }
-        } else {
-            color = c.get(FILL_COLOR, null, Color.class);
-            if (color != null) {
-                int alpha = color.getAlpha();
-                if (alpha == 255) {
-                    // Assume alpha value has not been specified by the user if
-                    // is set to fully opaque. Use default value in this case.
-                    // It is not an ideal solution, but a little tricky to get this
-                    // right, especially as named map colors can be changed in
-                    // the preference GUI and written to the preferences file.
-                    alpha = Math.min(255, Math.max(0, Integer.valueOf(Main.pref.getInteger("mappaint.fillalpha", 50))));
-                }
-                Integer pAlpha = Utils.color_float2int(c.get(FILL_OPACITY, null, float.class));
-                if (pAlpha != null) {
-                    alpha = pAlpha;
-                }
-                color = new Color(color.getRed(), color.getGreen(), color.getBlue(), alpha);
-            }
-        }
-
-        TextElement text = null;
-        Keyword textPos = c.get(TEXT_POSITION, null, Keyword.class);
-        if (textPos == null || "center".equals(textPos.val)) {
-            text = TextElement.create(env, PaintColors.AREA_TEXT.get(), true);
-        }
-
-        Float extent = c.get(FILL_EXTENT, null, float.class);
-        Float extentThreshold = c.get(FILL_EXTENT_THRESHOLD, null, float.class);
-
-        if (color != null)
-            return new AreaElemStyle(c, color, fillImage, extent, extentThreshold, text);
-        else
-            return null;
-    }
-
-    @Override
-    public void paintPrimitive(OsmPrimitive osm, MapPaintSettings paintSettings, StyledMapRenderer painter,
-            boolean selected, boolean outermember, boolean member) {
-        Color myColor = color;
-        if (osm instanceof Way) {
-            if (color != null) {
-                if (selected) {
-                    myColor = paintSettings.getSelectedColor(color.getAlpha());
-                } else if (outermember) {
-                    myColor = paintSettings.getRelationSelectedColor(color.getAlpha());
-                }
-            }
-            painter.drawArea((Way) osm, myColor, fillImage, extent, extentThreshold, painter.isInactiveMode() || osm.isDisabled(), text);
-        } else if (osm instanceof Relation) {
-            if (color != null && (selected || outermember)) {
-                myColor = paintSettings.getRelationSelectedColor(color.getAlpha());
-            }
-            painter.drawArea((Relation) osm, myColor, fillImage, extent, extentThreshold, painter.isInactiveMode() || osm.isDisabled(), text);
-        }
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (obj == null || getClass() != obj.getClass())
-            return false;
-        if (!super.equals(obj))
-            return false;
-        AreaElemStyle other = (AreaElemStyle) obj;
-        // we should get the same image object due to caching
-        if (!Objects.equals(fillImage, other.fillImage))
-            return false;
-        if (!Objects.equals(color, other.color))
-            return false;
-        if (extent != other.extent)
-            return false;
-        if (extentThreshold != other.extentThreshold)
-            return false;
-        if (!Objects.equals(text, other.text))
-            return false;
-        return true;
-    }
-
-    @Override
-    public int hashCode() {
-        int hash = 3;
-        hash = 61 * hash + color.hashCode();
-        hash = 61 * hash + (extent != null ? Float.floatToIntBits(extent) : 0);
-        hash = 61 * hash + (extentThreshold != null ? Float.floatToIntBits(extent) : 0);
-        hash = 61 * hash + (fillImage != null ? fillImage.hashCode() : 0);
-        hash = 61 * hash + (text != null ? text.hashCode() : 0);
-        return hash;
-    }
-
-    @Override
-    public String toString() {
-        return "AreaElemStyle{" + super.toString() + "color=" + Utils.toString(color) +
-                " fillImage=[" + fillImage + "]}";
-    }
-}
Index: unk/src/org/openstreetmap/josm/gui/mappaint/BoxTextElemStyle.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/mappaint/BoxTextElemStyle.java	(revision 9277)
+++ 	(revision )
@@ -1,242 +1,0 @@
-// License: GPL. For details, see LICENSE file.
-package org.openstreetmap.josm.gui.mappaint;
-
-import java.awt.Color;
-import java.awt.Rectangle;
-
-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.PaintColors;
-import org.openstreetmap.josm.data.osm.visitor.paint.StyledMapRenderer;
-import org.openstreetmap.josm.tools.CheckParameterUtil;
-
-/**
- * Text style attached to a style with a bounding box, like an icon or a symbol.
- */
-public class BoxTextElemStyle extends ElemStyle {
-
-    public enum HorizontalTextAlignment { LEFT, CENTER, RIGHT }
-
-    public enum VerticalTextAlignment { ABOVE, TOP, CENTER, BOTTOM, BELOW }
-
-    public interface BoxProvider {
-        BoxProviderResult get();
-    }
-
-    public static class BoxProviderResult {
-        private final Rectangle box;
-        private final boolean temporary;
-
-        public BoxProviderResult(Rectangle box, boolean temporary) {
-            this.box = box;
-            this.temporary = temporary;
-        }
-
-        /**
-         * Returns the box.
-         * @return the box
-         */
-        public Rectangle getBox() {
-            return box;
-        }
-
-        /**
-         * Determines if the box can change in future calls of the {@link BoxProvider#get()} method
-         * @return {@code true} if the box can change in future calls of the {@code BoxProvider#get()} method
-         */
-        public boolean isTemporary() {
-            return temporary;
-        }
-    }
-
-    public static class SimpleBoxProvider implements BoxProvider {
-        private final Rectangle box;
-
-        /**
-         * Constructs a new {@code SimpleBoxProvider}.
-         * @param box the box
-         */
-        public SimpleBoxProvider(Rectangle box) {
-            this.box = box;
-        }
-
-        @Override
-        public BoxProviderResult get() {
-            return new BoxProviderResult(box, false);
-        }
-
-        @Override
-        public int hashCode() {
-            return box.hashCode();
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            if (!(obj instanceof BoxProvider))
-                return false;
-            final BoxProvider other = (BoxProvider) obj;
-            BoxProviderResult resultOther = other.get();
-            if (resultOther.isTemporary()) return false;
-            return box.equals(resultOther.getBox());
-        }
-    }
-
-    public static final Rectangle ZERO_BOX = new Rectangle(0, 0, 0, 0);
-
-    public TextElement text;
-    // Either boxProvider or box is not null. If boxProvider is different from
-    // null, this means, that the box can still change in future, otherwise
-    // it is fixed.
-    protected BoxProvider boxProvider;
-    protected Rectangle box;
-    public HorizontalTextAlignment hAlign;
-    public VerticalTextAlignment vAlign;
-
-    public BoxTextElemStyle(Cascade c, TextElement text, BoxProvider boxProvider, Rectangle box,
-            HorizontalTextAlignment hAlign, VerticalTextAlignment vAlign) {
-        super(c, 5f);
-        CheckParameterUtil.ensureParameterNotNull(text);
-        CheckParameterUtil.ensureParameterNotNull(hAlign);
-        CheckParameterUtil.ensureParameterNotNull(vAlign);
-        this.text = text;
-        this.boxProvider = boxProvider;
-        this.box = box == null ? ZERO_BOX : box;
-        this.hAlign = hAlign;
-        this.vAlign = vAlign;
-    }
-
-    public static BoxTextElemStyle create(Environment env, BoxProvider boxProvider) {
-        return create(env, boxProvider, null);
-    }
-
-    public static BoxTextElemStyle create(Environment env, Rectangle box) {
-        return create(env, null, box);
-    }
-
-    public static BoxTextElemStyle create(Environment env, BoxProvider boxProvider, Rectangle box) {
-        initDefaultParameters();
-        Cascade c = env.mc.getCascade(env.layer);
-
-        TextElement text = TextElement.create(env, DEFAULT_TEXT_COLOR, false);
-        if (text == null) return null;
-        // Skip any primitives that don't have text to draw. (Styles are recreated for any tag change.)
-        // The concrete text to render is not cached in this object, but computed for each
-        // repaint. This way, one BoxTextElemStyle object can be used by multiple primitives (to save memory).
-        if (text.labelCompositionStrategy.compose(env.osm) == null) return null;
-
-        HorizontalTextAlignment hAlign = HorizontalTextAlignment.RIGHT;
-        Keyword hAlignKW = c.get(TEXT_ANCHOR_HORIZONTAL, Keyword.RIGHT, Keyword.class);
-        switch (hAlignKW.val) {
-            case "left":
-                hAlign = HorizontalTextAlignment.LEFT;
-                break;
-            case "center":
-                hAlign = HorizontalTextAlignment.CENTER;
-        }
-        VerticalTextAlignment vAlign = VerticalTextAlignment.BOTTOM;
-        Keyword vAlignKW = c.get(TEXT_ANCHOR_VERTICAL, Keyword.BOTTOM, Keyword.class);
-        switch (vAlignKW.val) {
-            case "bottom":
-                vAlign = VerticalTextAlignment.BOTTOM;
-                break;
-            case "above":
-                vAlign = VerticalTextAlignment.ABOVE;
-                break;
-            case "top":
-                vAlign = VerticalTextAlignment.TOP;
-                break;
-            case "center":
-                vAlign = VerticalTextAlignment.CENTER;
-                break;
-            case "below":
-                vAlign = VerticalTextAlignment.BELOW;
-        }
-
-        return new BoxTextElemStyle(c, text, boxProvider, box, hAlign, vAlign);
-    }
-
-    public Rectangle getBox() {
-        if (boxProvider != null) {
-            BoxProviderResult result = boxProvider.get();
-            if (!result.isTemporary()) {
-                box = result.getBox();
-                boxProvider = null;
-            }
-            return result.getBox();
-        }
-        return box;
-    }
-
-    public static final BoxTextElemStyle SIMPLE_NODE_TEXT_ELEMSTYLE;
-    static {
-        MultiCascade mc = new MultiCascade();
-        Cascade c = mc.getOrCreateCascade("default");
-        c.put(TEXT, Keyword.AUTO);
-        Node n = new Node();
-        n.put("name", "dummy");
-        SIMPLE_NODE_TEXT_ELEMSTYLE = create(new Environment(n, mc, "default", null), NodeElemStyle.SIMPLE_NODE_ELEMSTYLE.getBoxProvider());
-        if (SIMPLE_NODE_TEXT_ELEMSTYLE == null) throw new AssertionError();
-    }
-
-    /*
-     * Caches the default text color from the preferences.
-     *
-     * FIXME: the cache isn't updated if the user changes the preference during a JOSM
-     * session. There should be preference listener updating this cache.
-     */
-    private static volatile Color DEFAULT_TEXT_COLOR;
-
-    private static void initDefaultParameters() {
-        if (DEFAULT_TEXT_COLOR != null) return;
-        DEFAULT_TEXT_COLOR = PaintColors.TEXT.get();
-    }
-
-    @Override
-    public void paintPrimitive(OsmPrimitive osm, MapPaintSettings settings, StyledMapRenderer painter,
-            boolean selected, boolean outermember, boolean member) {
-        if (osm instanceof Node) {
-            painter.drawBoxText((Node) osm, this);
-        }
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (!super.equals(obj))
-            return false;
-        if (obj == null || getClass() != obj.getClass())
-            return false;
-        final BoxTextElemStyle other = (BoxTextElemStyle) obj;
-        if (!text.equals(other.text)) return false;
-        if (boxProvider != null) {
-            if (!boxProvider.equals(other.boxProvider)) return false;
-        } else if (other.boxProvider != null)
-            return false;
-        else {
-            if (!box.equals(other.box)) return false;
-        }
-        if (hAlign != other.hAlign) return false;
-        if (vAlign != other.vAlign) return false;
-        return true;
-    }
-
-    @Override
-    public int hashCode() {
-        int hash = super.hashCode();
-        hash = 97 * hash + text.hashCode();
-        if (boxProvider != null) {
-            hash = 97 * hash + boxProvider.hashCode();
-        } else {
-            hash = 97 * hash + box.hashCode();
-        }
-        hash = 97 * hash + hAlign.hashCode();
-        hash = 97 * hash + vAlign.hashCode();
-        return hash;
-    }
-
-    @Override
-    public String toString() {
-        return "BoxTextElemStyle{" + super.toString() + ' ' + text.toStringImpl()
-                + " box=" + box + " hAlign=" + hAlign + " vAlign=" + vAlign + '}';
-    }
-}
Index: unk/src/org/openstreetmap/josm/gui/mappaint/ElemStyle.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/mappaint/ElemStyle.java	(revision 9277)
+++ 	(revision )
@@ -1,233 +1,0 @@
-// License: GPL. For details, see LICENSE file.
-package org.openstreetmap.josm.gui.mappaint;
-
-import java.awt.Font;
-import java.util.HashMap;
-import java.util.Map;
-
-import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.data.osm.OsmPrimitive;
-import org.openstreetmap.josm.data.osm.visitor.paint.MapPaintSettings;
-import org.openstreetmap.josm.data.osm.visitor.paint.StyledMapRenderer;
-import org.openstreetmap.josm.gui.mappaint.mapcss.Instruction.RelativeFloat;
-
-public abstract class ElemStyle implements StyleKeys {
-
-    protected static final int ICON_IMAGE_IDX = 0;
-    protected static final int ICON_WIDTH_IDX = 1;
-    protected static final int ICON_HEIGHT_IDX = 2;
-    protected static final int ICON_OPACITY_IDX = 3;
-    protected static final int ICON_OFFSET_X_IDX = 4;
-    protected static final int ICON_OFFSET_Y_IDX = 5;
-    protected static final String[] ICON_KEYS = {ICON_IMAGE, ICON_WIDTH, ICON_HEIGHT, ICON_OPACITY, ICON_OFFSET_X, ICON_OFFSET_Y};
-    protected static final String[] REPEAT_IMAGE_KEYS = {REPEAT_IMAGE, REPEAT_IMAGE_WIDTH, REPEAT_IMAGE_HEIGHT, REPEAT_IMAGE_OPACITY,
-            null, null};
-
-    public float majorZIndex;
-    public float zIndex;
-    public float objectZIndex;
-    public boolean isModifier;  // false, if style can serve as main style for the
-    // primitive; true, if it is a highlight or modifier
-
-    public ElemStyle(float major_z_index, float z_index, float object_z_index, boolean isModifier) {
-        this.majorZIndex = major_z_index;
-        this.zIndex = z_index;
-        this.objectZIndex = object_z_index;
-        this.isModifier = isModifier;
-    }
-
-    protected ElemStyle(Cascade c, float default_major_z_index) {
-        majorZIndex = c.get(MAJOR_Z_INDEX, default_major_z_index, Float.class);
-        zIndex = c.get(Z_INDEX, 0f, Float.class);
-        objectZIndex = c.get(OBJECT_Z_INDEX, 0f, Float.class);
-        isModifier = c.get(MODIFIER, Boolean.FALSE, Boolean.class);
-    }
-
-    /**
-     * draws a primitive
-     * @param primitive primitive to draw
-     * @param paintSettings paint settings
-     * @param painter painter
-     * @param selected true, if primitive is selected
-     * @param outermember true, if primitive is not selected and outer member of a selected multipolygon relation
-     * @param member true, if primitive is not selected and member of a selected relation
-     */
-    public abstract void paintPrimitive(OsmPrimitive primitive, MapPaintSettings paintSettings, StyledMapRenderer painter,
-            boolean selected, boolean outermember, boolean member);
-
-    public boolean isProperLineStyle() {
-        return false;
-    }
-
-    /**
-     * Get a property value of type Width
-     * @param c the cascade
-     * @param key property key for the width value
-     * @param relativeTo reference width. Only needed, when relative width syntax is used, e.g. "+4".
-     * @return width
-     */
-    protected static Float getWidth(Cascade c, String key, Float relativeTo) {
-        Float width = c.get(key, null, Float.class, true);
-        if (width != null) {
-            if (width > 0)
-                return width;
-        } else {
-            Keyword widthKW = c.get(key, null, Keyword.class, true);
-            if (Keyword.THINNEST.equals(widthKW))
-                return 0f;
-            if (Keyword.DEFAULT.equals(widthKW))
-                return (float) MapPaintSettings.INSTANCE.getDefaultSegmentWidth();
-            if (relativeTo != null) {
-                RelativeFloat width_rel = c.get(key, null, RelativeFloat.class, true);
-                if (width_rel != null)
-                    return relativeTo + width_rel.val;
-            }
-        }
-        return null;
-    }
-
-    /* ------------------------------------------------------------------------------- */
-    /* cached values                                                                   */
-    /* ------------------------------------------------------------------------------- */
-    /*
-     * Two preference values and the set of created fonts are cached in order to avoid
-     * expensive lookups and to avoid too many font objects
-     *
-     * FIXME: cached preference values are not updated if the user changes them during
-     * a JOSM session. Should have a listener listening to preference changes.
-     */
-    private static volatile String DEFAULT_FONT_NAME;
-    private static volatile Float DEFAULT_FONT_SIZE;
-    private static final Object lock = new Object();
-
-    // thread save access (double-checked locking)
-    private static Float getDefaultFontSize() {
-        Float s = DEFAULT_FONT_SIZE;
-        if (s == null) {
-            synchronized (lock) {
-                s = DEFAULT_FONT_SIZE;
-                if (s == null) {
-                    DEFAULT_FONT_SIZE = s = (float) Main.pref.getInteger("mappaint.fontsize", 8);
-                }
-            }
-        }
-        return s;
-    }
-
-    private static String getDefaultFontName() {
-        String n = DEFAULT_FONT_NAME;
-        if (n == null) {
-            synchronized (lock) {
-                n = DEFAULT_FONT_NAME;
-                if (n == null) {
-                    DEFAULT_FONT_NAME = n = Main.pref.get("mappaint.font", "Droid Sans");
-                }
-            }
-        }
-        return n;
-    }
-
-    private static class FontDescriptor {
-        public String name;
-        public int style;
-        public int size;
-
-        FontDescriptor(String name, int style, int size) {
-            this.name = name;
-            this.style = style;
-            this.size = size;
-        }
-
-        @Override
-        public int hashCode() {
-            final int prime = 31;
-            int result = 1;
-            result = prime * result + ((name == null) ? 0 : name.hashCode());
-            result = prime * result + size;
-            result = prime * result + style;
-            return result;
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            if (this == obj)
-                return true;
-            if (obj == null)
-                return false;
-            if (getClass() != obj.getClass())
-                return false;
-            FontDescriptor other = (FontDescriptor) obj;
-            if (name == null) {
-                if (other.name != null)
-                    return false;
-            } else if (!name.equals(other.name))
-                return false;
-            if (size != other.size)
-                return false;
-            if (style != other.style)
-                return false;
-            return true;
-        }
-    }
-
-    private static final Map<FontDescriptor, Font> FONT_MAP = new HashMap<>();
-
-    private static Font getCachedFont(FontDescriptor fd) {
-        Font f = FONT_MAP.get(fd);
-        if (f != null) return f;
-        f = new Font(fd.name, fd.style, fd.size);
-        FONT_MAP.put(fd, f);
-        return f;
-    }
-
-    private static Font getCachedFont(String name, int style, int size) {
-        return getCachedFont(new FontDescriptor(name, style, size));
-    }
-
-    protected static Font getFont(Cascade c, String s) {
-        String name = c.get(FONT_FAMILY, getDefaultFontName(), String.class);
-        float size = c.get(FONT_SIZE, getDefaultFontSize(), Float.class);
-        int weight = Font.PLAIN;
-        if ("bold".equalsIgnoreCase(c.get(FONT_WEIGHT, null, String.class))) {
-            weight = Font.BOLD;
-        }
-        int style = Font.PLAIN;
-        if ("italic".equalsIgnoreCase(c.get(FONT_STYLE, null, String.class))) {
-            style = Font.ITALIC;
-        }
-        Font f = getCachedFont(name, style | weight, Math.round(size));
-        if (f.canDisplayUpTo(s) == -1)
-            return f;
-        else {
-            // fallback if the string contains characters that cannot be
-            // rendered by the selected font
-            return getCachedFont("SansSerif", style | weight, Math.round(size));
-        }
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (!(o instanceof ElemStyle))
-            return false;
-        ElemStyle s = (ElemStyle) o;
-        return isModifier == s.isModifier &&
-                majorZIndex == s.majorZIndex &&
-                zIndex == s.zIndex &&
-                objectZIndex == s.objectZIndex;
-    }
-
-    @Override
-    public int hashCode() {
-        int hash = 5;
-        hash = 41 * hash + Float.floatToIntBits(this.majorZIndex);
-        hash = 41 * hash + Float.floatToIntBits(this.zIndex);
-        hash = 41 * hash + Float.floatToIntBits(this.objectZIndex);
-        hash = 41 * hash + (isModifier ? 1 : 0);
-        return hash;
-    }
-
-    @Override
-    public String toString() {
-        return String.format("z_idx=[%s/%s/%s] ", majorZIndex, zIndex, objectZIndex) + (isModifier ? "modifier " : "");
-    }
-}
Index: /trunk/src/org/openstreetmap/josm/gui/mappaint/ElemStyles.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/mappaint/ElemStyles.java	(revision 9277)
+++ /trunk/src/org/openstreetmap/josm/gui/mappaint/ElemStyles.java	(revision 9278)
@@ -23,4 +23,12 @@
 import org.openstreetmap.josm.gui.mappaint.StyleCache.StyleList;
 import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSStyleSource;
+import org.openstreetmap.josm.gui.mappaint.styleelement.AreaElement;
+import org.openstreetmap.josm.gui.mappaint.styleelement.BoxTextElement;
+import org.openstreetmap.josm.gui.mappaint.styleelement.LineElement;
+import org.openstreetmap.josm.gui.mappaint.styleelement.LineTextElement;
+import org.openstreetmap.josm.gui.mappaint.styleelement.NodeElement;
+import org.openstreetmap.josm.gui.mappaint.styleelement.RepeatImageElement;
+import org.openstreetmap.josm.gui.mappaint.styleelement.StyleElement;
+import org.openstreetmap.josm.gui.mappaint.styleelement.TextLabel;
 import org.openstreetmap.josm.gui.util.GuiHelper;
 import org.openstreetmap.josm.tools.Pair;
@@ -97,14 +105,14 @@
         if (osm instanceof Node && isDefaultNodes()) {
             if (p.a.isEmpty()) {
-                if (TextElement.AUTO_LABEL_COMPOSITION_STRATEGY.compose(osm) != null) {
-                    p.a = NodeElemStyle.DEFAULT_NODE_STYLELIST_TEXT;
+                if (TextLabel.AUTO_LABEL_COMPOSITION_STRATEGY.compose(osm) != null) {
+                    p.a = NodeElement.DEFAULT_NODE_STYLELIST_TEXT;
                 } else {
-                    p.a = NodeElemStyle.DEFAULT_NODE_STYLELIST;
+                    p.a = NodeElement.DEFAULT_NODE_STYLELIST;
                 }
             } else {
                 boolean hasNonModifier = false;
                 boolean hasText = false;
-                for (ElemStyle s : p.a) {
-                    if (s instanceof BoxTextElemStyle) {
+                for (StyleElement s : p.a) {
+                    if (s instanceof BoxTextElement) {
                         hasText = true;
                     } else {
@@ -115,8 +123,8 @@
                 }
                 if (!hasNonModifier) {
-                    p.a = new StyleList(p.a, NodeElemStyle.SIMPLE_NODE_ELEMSTYLE);
+                    p.a = new StyleList(p.a, NodeElement.SIMPLE_NODE_ELEMSTYLE);
                     if (!hasText) {
-                        if (TextElement.AUTO_LABEL_COMPOSITION_STRATEGY.compose(osm) != null) {
-                            p.a = new StyleList(p.a, BoxTextElemStyle.SIMPLE_NODE_TEXT_ELEMSTYLE);
+                        if (TextLabel.AUTO_LABEL_COMPOSITION_STRATEGY.compose(osm) != null) {
+                            p.a = new StyleList(p.a, BoxTextElement.SIMPLE_NODE_TEXT_ELEMSTYLE);
                         }
                     }
@@ -125,5 +133,5 @@
         } else if (osm instanceof Way && isDefaultLines()) {
             boolean hasProperLineStyle = false;
-            for (ElemStyle s : p.a) {
+            for (StyleElement s : p.a) {
                 if (s.isProperLineStyle()) {
                     hasProperLineStyle = true;
@@ -132,6 +140,6 @@
             }
             if (!hasProperLineStyle) {
-                AreaElemStyle area = Utils.find(p.a, AreaElemStyle.class);
-                LineElemStyle line = area == null ? LineElemStyle.UNTAGGED_WAY : LineElemStyle.createSimpleLineStyle(area.color, true);
+                AreaElement area = Utils.find(p.a, AreaElement.class);
+                LineElement line = area == null ? LineElement.UNTAGGED_WAY : LineElement.createSimpleLineStyle(area.color, true);
                 p.a = new StyleList(p.a, line);
             }
@@ -197,8 +205,8 @@
                     boolean hasIndependentLineStyle = false;
                     if (!isOuterWayOfSomeMP) { // do this only one time
-                        List<ElemStyle> tmp = new ArrayList<>(p.a.size());
-                        for (ElemStyle s : p.a) {
-                            if (s instanceof AreaElemStyle) {
-                                wayColor = ((AreaElemStyle) s).color;
+                        List<StyleElement> tmp = new ArrayList<>(p.a.size());
+                        for (StyleElement s : p.a) {
+                            if (s instanceof AreaElement) {
+                                wayColor = ((AreaElement) s).color;
                             } else {
                                 tmp.add(s);
@@ -217,6 +225,6 @@
                             mpElemStyles = getStyleCacheWithRange(r, scale, nc);
                         }
-                        ElemStyle mpLine = null;
-                        for (ElemStyle s : mpElemStyles.a) {
+                        StyleElement mpLine = null;
+                        for (StyleElement s : mpElemStyles.a) {
                             if (s.isProperLineStyle()) {
                                 mpLine = s;
@@ -229,5 +237,5 @@
                             break;
                         } else if (wayColor == null && isDefaultLines()) {
-                            AreaElemStyle mpArea = Utils.find(mpElemStyles.a, AreaElemStyle.class);
+                            AreaElement mpArea = Utils.find(mpElemStyles.a, AreaElement.class);
                             if (mpArea != null) {
                                 wayColor = mpArea.color;
@@ -240,5 +248,5 @@
                 if (isDefaultLines()) {
                     boolean hasLineStyle = false;
-                    for (ElemStyle s : p.a) {
+                    for (StyleElement s : p.a) {
                         if (s.isProperLineStyle()) {
                             hasLineStyle = true;
@@ -247,5 +255,5 @@
                     }
                     if (!hasLineStyle) {
-                        p.a = new StyleList(p.a, LineElemStyle.createSimpleLineStyle(wayColor, true));
+                        p.a = new StyleList(p.a, LineElement.createSimpleLineStyle(wayColor, true));
                     }
                 }
@@ -265,6 +273,6 @@
                     p = generateStyles(osm, scale, false);
                     boolean hasIndependentElemStyle = false;
-                    for (ElemStyle s : p.a) {
-                        if (s.isProperLineStyle() || s instanceof AreaElemStyle) {
+                    for (StyleElement s : p.a) {
+                        if (s.isProperLineStyle() || s instanceof AreaElement) {
                             hasIndependentElemStyle = true;
                             break;
@@ -277,11 +285,11 @@
                             mpElemStyles = get(ref, scale, nc);
                         }
-                        for (ElemStyle mpS : mpElemStyles) {
-                            if (mpS instanceof AreaElemStyle) {
-                                mpColor = ((AreaElemStyle) mpS).color;
+                        for (StyleElement mpS : mpElemStyles) {
+                            if (mpS instanceof AreaElement) {
+                                mpColor = ((AreaElement) mpS).color;
                                 break;
                             }
                         }
-                        p.a = new StyleList(p.a, LineElemStyle.createSimpleLineStyle(mpColor, true));
+                        p.a = new StyleList(p.a, LineElement.createSimpleLineStyle(mpColor, true));
                     }
                     return p;
@@ -292,5 +300,5 @@
             Pair<StyleList, Range> p = generateStyles(osm, scale, true);
             if (drawMultipolygon && ((Relation) osm).isMultipolygon()) {
-                if (!Utils.exists(p.a, AreaElemStyle.class) && Main.pref.getBoolean("multipolygon.deprecated.outerstyle", true)) {
+                if (!Utils.exists(p.a, AreaElement.class) && Main.pref.getBoolean("multipolygon.deprecated.outerstyle", true)) {
                     // look at outer ways to find area style
                     Multipolygon multipolygon = MultipolygonCache.getInstance().get(nc, (Relation) osm);
@@ -298,5 +306,5 @@
                         Pair<StyleList, Range> wayStyles = generateStyles(w, scale, false);
                         p.b = Range.cut(p.b, wayStyles.b);
-                        ElemStyle area = Utils.find(wayStyles.a, AreaElemStyle.class);
+                        StyleElement area = Utils.find(wayStyles.a, AreaElement.class);
                         if (area != null) {
                             p.a = new StyleList(p.a, area);
@@ -326,5 +334,5 @@
     public Pair<StyleList, Range> generateStyles(OsmPrimitive osm, double scale, boolean pretendWayIsClosed) {
 
-        List<ElemStyle> sl = new ArrayList<>();
+        List<StyleElement> sl = new ArrayList<>();
         MultiCascade mc = new MultiCascade();
         Environment env = new Environment(osm, mc, null, null);
@@ -342,28 +350,28 @@
             env.layer = e.getKey();
             if (osm instanceof Way) {
-                addIfNotNull(sl, AreaElemStyle.create(env));
-                addIfNotNull(sl, RepeatImageElemStyle.create(env));
-                addIfNotNull(sl, LineElemStyle.createLine(env));
-                addIfNotNull(sl, LineElemStyle.createLeftCasing(env));
-                addIfNotNull(sl, LineElemStyle.createRightCasing(env));
-                addIfNotNull(sl, LineElemStyle.createCasing(env));
-                addIfNotNull(sl, LineTextElemStyle.create(env));
+                addIfNotNull(sl, AreaElement.create(env));
+                addIfNotNull(sl, RepeatImageElement.create(env));
+                addIfNotNull(sl, LineElement.createLine(env));
+                addIfNotNull(sl, LineElement.createLeftCasing(env));
+                addIfNotNull(sl, LineElement.createRightCasing(env));
+                addIfNotNull(sl, LineElement.createCasing(env));
+                addIfNotNull(sl, LineTextElement.create(env));
             } else if (osm instanceof Node) {
-                NodeElemStyle nodeStyle = NodeElemStyle.create(env);
+                NodeElement nodeStyle = NodeElement.create(env);
                 if (nodeStyle != null) {
                     sl.add(nodeStyle);
-                    addIfNotNull(sl, BoxTextElemStyle.create(env, nodeStyle.getBoxProvider()));
+                    addIfNotNull(sl, BoxTextElement.create(env, nodeStyle.getBoxProvider()));
                 } else {
-                    addIfNotNull(sl, BoxTextElemStyle.create(env, NodeElemStyle.SIMPLE_NODE_ELEMSTYLE_BOXPROVIDER));
+                    addIfNotNull(sl, BoxTextElement.create(env, NodeElement.SIMPLE_NODE_ELEMSTYLE_BOXPROVIDER));
                 }
             } else if (osm instanceof Relation) {
                 if (((Relation) osm).isMultipolygon()) {
-                    addIfNotNull(sl, AreaElemStyle.create(env));
-                    addIfNotNull(sl, RepeatImageElemStyle.create(env));
-                    addIfNotNull(sl, LineElemStyle.createLine(env));
-                    addIfNotNull(sl, LineElemStyle.createCasing(env));
-                    addIfNotNull(sl, LineTextElemStyle.create(env));
+                    addIfNotNull(sl, AreaElement.create(env));
+                    addIfNotNull(sl, RepeatImageElement.create(env));
+                    addIfNotNull(sl, LineElement.createLine(env));
+                    addIfNotNull(sl, LineElement.createCasing(env));
+                    addIfNotNull(sl, LineTextElement.create(env));
                 } else if ("restriction".equals(osm.get("type"))) {
-                    addIfNotNull(sl, NodeElemStyle.create(env));
+                    addIfNotNull(sl, NodeElement.create(env));
                 }
             }
@@ -448,19 +456,19 @@
 
     /**
-     * Returns the first AreaElemStyle for a given primitive.
+     * Returns the first AreaElement for a given primitive.
      * @param p the OSM primitive
      * @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.
-     * @return first AreaElemStyle found or {@code null}.
-     */
-    public static AreaElemStyle getAreaElemStyle(OsmPrimitive p, boolean pretendWayIsClosed) {
+     * @return first AreaElement found or {@code null}.
+     */
+    public static AreaElement getAreaElemStyle(OsmPrimitive p, boolean pretendWayIsClosed) {
         MapCSSStyleSource.STYLE_SOURCE_LOCK.readLock().lock();
         try {
             if (MapPaintStyles.getStyles() == null)
                 return null;
-            for (ElemStyle s : MapPaintStyles.getStyles().generateStyles(p, 1.0, pretendWayIsClosed).a) {
-                if (s instanceof AreaElemStyle)
-                    return (AreaElemStyle) s;
+            for (StyleElement s : MapPaintStyles.getStyles().generateStyles(p, 1.0, pretendWayIsClosed).a) {
+                if (s instanceof AreaElement)
+                    return (AreaElement) s;
             }
             return null;
@@ -471,10 +479,10 @@
 
     /**
-     * Determines whether primitive has an AreaElemStyle.
+     * Determines whether primitive has an AreaElement.
      * @param p the OSM primitive
      * @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.
-     * @return {@code true} if primitive has an AreaElemStyle
+     * @return {@code true} if primitive has an AreaElement
      */
     public static boolean hasAreaElemStyle(OsmPrimitive p, boolean pretendWayIsClosed) {
@@ -483,7 +491,7 @@
 
     /**
-     * Determines whether primitive has <b>only</b> an AreaElemStyle.
+     * Determines whether primitive has <b>only</b> an AreaElement.
      * @param p the OSM primitive
-     * @return {@code true} if primitive has only an AreaElemStyle
+     * @return {@code true} if primitive has only an AreaElement
      * @since 7486
      */
@@ -497,6 +505,6 @@
                 return false;
             }
-            for (ElemStyle s : styles) {
-                if (!(s instanceof AreaElemStyle)) {
+            for (StyleElement s : styles) {
+                if (!(s instanceof AreaElement)) {
                     return false;
                 }
Index: unk/src/org/openstreetmap/josm/gui/mappaint/LabelCompositionStrategy.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/mappaint/LabelCompositionStrategy.java	(revision 9277)
+++ 	(revision )
@@ -1,304 +1,0 @@
-// License: GPL. For details, see LICENSE file.
-package org.openstreetmap.josm.gui.mappaint;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.data.Preferences.PreferenceChangeEvent;
-import org.openstreetmap.josm.data.Preferences.PreferenceChangedListener;
-import org.openstreetmap.josm.data.osm.OsmPrimitive;
-import org.openstreetmap.josm.tools.LanguageInfo;
-
-/**
- * <p>Provides an abstract parent class and three concrete sub classes for various
- * strategies on how to compose the text label which can be rendered close to a node
- * or within an area in an OSM map.</p>
- *
- * <p>The three strategies below support three rules for composing a label:
- * <ul>
- *   <li>{@link StaticLabelCompositionStrategy} - the label is given by a static text
- *   specified in the MapCSS style file</li>
- *
- *   <li>{@link TagLookupCompositionStrategy} - the label is given by the content of a
- *   tag whose name specified in the MapCSS style file</li>
- *
- *   <li>{@link DeriveLabelFromNameTagsCompositionStrategy} - the label is given by the value
- *   of one
- *   of the configured "name tags". The list of relevant name tags can be configured
- *   in the JOSM preferences
- *   content of a tag whose name specified in the MapCSS style file, see the preference
- *   options <tt>mappaint.nameOrder</tt> and <tt>mappaint.nameComplementOrder</tt>.</li>
- * </ul>
- *
- */
-public abstract class LabelCompositionStrategy {
-
-    /**
-     * Replies the text value to be rendered as label for the primitive {@code primitive}.
-     *
-     * @param primitive the primitive
-     *
-     * @return the text value to be rendered or null, if primitive is null or
-     * if no suitable value could be composed
-     */
-    public abstract String compose(OsmPrimitive primitive);
-
-    public static class StaticLabelCompositionStrategy extends LabelCompositionStrategy {
-        private final String defaultLabel;
-
-        public StaticLabelCompositionStrategy(String defaultLabel) {
-            this.defaultLabel = defaultLabel;
-        }
-
-        @Override
-        public String compose(OsmPrimitive primitive) {
-            return defaultLabel;
-        }
-
-        public String getDefaultLabel() {
-            return defaultLabel;
-        }
-
-        @Override
-        public String toString() {
-            return '{' + getClass().getSimpleName() + " defaultLabel=" + defaultLabel + '}';
-        }
-
-        @Override
-        public int hashCode() {
-            final int prime = 31;
-            int result = 1;
-            result = prime * result + ((defaultLabel == null) ? 0 : defaultLabel.hashCode());
-            return result;
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            if (this == obj)
-                return true;
-            if (obj == null)
-                return false;
-            if (getClass() != obj.getClass())
-                return false;
-            StaticLabelCompositionStrategy other = (StaticLabelCompositionStrategy) obj;
-            if (defaultLabel == null) {
-                if (other.defaultLabel != null)
-                    return false;
-            } else if (!defaultLabel.equals(other.defaultLabel))
-                return false;
-            return true;
-        }
-    }
-
-    public static class TagLookupCompositionStrategy extends LabelCompositionStrategy {
-
-        private final String defaultLabelTag;
-
-        public TagLookupCompositionStrategy(String defaultLabelTag) {
-            if (defaultLabelTag != null) {
-                defaultLabelTag = defaultLabelTag.trim();
-                if (defaultLabelTag.isEmpty()) {
-                    defaultLabelTag = null;
-                }
-            }
-            this.defaultLabelTag = defaultLabelTag;
-        }
-
-        @Override
-        public String compose(OsmPrimitive primitive) {
-            if (defaultLabelTag == null) return null;
-            if (primitive == null) return null;
-            return primitive.get(defaultLabelTag);
-        }
-
-        public String getDefaultLabelTag() {
-            return defaultLabelTag;
-        }
-
-        @Override
-        public String toString() {
-            return '{' + getClass().getSimpleName() + " defaultLabelTag=" + defaultLabelTag + '}';
-        }
-
-        @Override
-        public int hashCode() {
-            final int prime = 31;
-            int result = 1;
-            result = prime * result + ((defaultLabelTag == null) ? 0 : defaultLabelTag.hashCode());
-            return result;
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            if (this == obj)
-                return true;
-            if (obj == null)
-                return false;
-            if (getClass() != obj.getClass())
-                return false;
-            TagLookupCompositionStrategy other = (TagLookupCompositionStrategy) obj;
-            if (defaultLabelTag == null) {
-                if (other.defaultLabelTag != null)
-                    return false;
-            } else if (!defaultLabelTag.equals(other.defaultLabelTag))
-                return false;
-            return true;
-        }
-    }
-
-    public static class DeriveLabelFromNameTagsCompositionStrategy
-        extends LabelCompositionStrategy implements PreferenceChangedListener {
-
-        /**
-         * The list of default name tags from which a label candidate is derived.
-         */
-        private static final String[] DEFAULT_NAME_TAGS = {
-            "name:" + LanguageInfo.getJOSMLocaleCode(),
-            "name",
-            "int_name",
-            "distance",
-            "ref",
-            "operator",
-            "brand",
-            "addr:housenumber"
-        };
-
-        /**
-         * The list of default name complement tags from which a label candidate is derived.
-         */
-        private static final String[] DEFAULT_NAME_COMPLEMENT_TAGS = {
-            "capacity"
-        };
-
-        private List<String> nameTags = new ArrayList<>();
-        private List<String> nameComplementTags = new ArrayList<>();
-
-        /**
-         * <p>Creates the strategy and initializes its name tags from the preferences.</p>
-         */
-        public DeriveLabelFromNameTagsCompositionStrategy() {
-            initNameTagsFromPreferences();
-        }
-
-        private static List<String> buildNameTags(List<String> nameTags) {
-            if (nameTags == null) {
-                nameTags = Collections.emptyList();
-            }
-            List<String> result = new ArrayList<>();
-            for (String tag: nameTags) {
-                if (tag == null) {
-                    continue;
-                }
-                tag = tag.trim();
-                if (tag.isEmpty()) {
-                    continue;
-                }
-                result.add(tag);
-            }
-            return result;
-        }
-
-        /**
-         * Sets the name tags to be looked up in order to build up the label.
-         *
-         * @param nameTags the name tags. null values are ignored.
-         */
-        public void setNameTags(List<String> nameTags) {
-            this.nameTags = buildNameTags(nameTags);
-        }
-
-        /**
-         * Sets the name complement tags to be looked up in order to build up the label.
-         *
-         * @param nameComplementTags the name complement tags. null values are ignored.
-         * @since 6541
-         */
-        public void setNameComplementTags(List<String> nameComplementTags) {
-            this.nameComplementTags = buildNameTags(nameComplementTags);
-        }
-
-        /**
-         * Replies an unmodifiable list of the name tags used to compose the label.
-         *
-         * @return the list of name tags
-         */
-        public List<String> getNameTags() {
-            return Collections.unmodifiableList(nameTags);
-        }
-
-        /**
-         * Replies an unmodifiable list of the name complement tags used to compose the label.
-         *
-         * @return the list of name complement tags
-         * @since 6541
-         */
-        public List<String> getNameComplementTags() {
-            return Collections.unmodifiableList(nameComplementTags);
-        }
-
-        /**
-         * Initializes the name tags to use from a list of default name tags (see
-         * {@link #DEFAULT_NAME_TAGS} and {@link #DEFAULT_NAME_COMPLEMENT_TAGS})
-         * and from name tags configured in the preferences using the keys
-         * <tt>mappaint.nameOrder</tt> and <tt>mappaint.nameComplementOrder</tt>.
-         */
-        public final void initNameTagsFromPreferences() {
-            if (Main.pref == null) {
-                this.nameTags = new ArrayList<>(Arrays.asList(DEFAULT_NAME_TAGS));
-                this.nameComplementTags = new ArrayList<>(Arrays.asList(DEFAULT_NAME_COMPLEMENT_TAGS));
-            } else {
-                this.nameTags = new ArrayList<>(
-                        Main.pref.getCollection("mappaint.nameOrder", Arrays.asList(DEFAULT_NAME_TAGS))
-                );
-                this.nameComplementTags = new ArrayList<>(
-                        Main.pref.getCollection("mappaint.nameComplementOrder", Arrays.asList(DEFAULT_NAME_COMPLEMENT_TAGS))
-                );
-            }
-        }
-
-        private String getPrimitiveName(OsmPrimitive n) {
-            StringBuilder name = new StringBuilder();
-            if (!n.hasKeys()) return null;
-            for (String rn : nameTags) {
-                String val = n.get(rn);
-                if (val != null) {
-                    name.append(val);
-                    break;
-                }
-            }
-            for (String rn : nameComplementTags) {
-                String comp = n.get(rn);
-                if (comp != null) {
-                    if (name.length() == 0) {
-                        name.append(comp);
-                    } else {
-                        name.append(" (").append(comp).append(')');
-                    }
-                    break;
-                }
-            }
-            return name.toString();
-        }
-
-        @Override
-        public String compose(OsmPrimitive primitive) {
-            if (primitive == null) return null;
-            return getPrimitiveName(primitive);
-        }
-
-        @Override
-        public String toString() {
-            return "{" + getClass().getSimpleName() +'}';
-        }
-
-        @Override
-        public void preferenceChanged(PreferenceChangeEvent e) {
-            if (e.getKey() != null && e.getKey().startsWith("mappaint.name")) {
-                initNameTagsFromPreferences();
-            }
-        }
-    }
-}
Index: unk/src/org/openstreetmap/josm/gui/mappaint/LineElemStyle.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/mappaint/LineElemStyle.java	(revision 9277)
+++ 	(revision )
@@ -1,385 +1,0 @@
-// License: GPL. For details, see LICENSE file.
-package org.openstreetmap.josm.gui.mappaint;
-
-import java.awt.BasicStroke;
-import java.awt.Color;
-import java.util.Arrays;
-import java.util.Objects;
-
-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.Way;
-import org.openstreetmap.josm.data.osm.visitor.paint.MapPaintSettings;
-import org.openstreetmap.josm.data.osm.visitor.paint.PaintColors;
-import org.openstreetmap.josm.data.osm.visitor.paint.StyledMapRenderer;
-import org.openstreetmap.josm.gui.mappaint.mapcss.Instruction.RelativeFloat;
-import org.openstreetmap.josm.tools.Utils;
-
-public class LineElemStyle extends ElemStyle {
-
-    public static LineElemStyle createSimpleLineStyle(Color color, boolean isAreaEdge) {
-        MultiCascade mc = new MultiCascade();
-        Cascade c = mc.getOrCreateCascade("default");
-        c.put(WIDTH, Keyword.DEFAULT);
-        c.put(COLOR, color != null ? color : PaintColors.UNTAGGED.get());
-        c.put(OPACITY, 1f);
-        if (isAreaEdge) {
-            c.put(Z_INDEX, -3f);
-        }
-        return createLine(new Environment(null, mc, "default", null));
-    }
-
-    public static final LineElemStyle UNTAGGED_WAY = createSimpleLineStyle(null, false);
-
-    private BasicStroke line;
-    public Color color;
-    public Color dashesBackground;
-    public float offset;
-    public float realWidth; // the real width of this line in meter
-
-    private BasicStroke dashesLine;
-
-    public enum LineType {
-        NORMAL("", 3f),
-        CASING("casing-", 2f),
-        LEFT_CASING("left-casing-", 2.1f),
-        RIGHT_CASING("right-casing-", 2.1f);
-
-        public final String prefix;
-        public final float defaultMajorZIndex;
-
-        LineType(String prefix, float default_major_z_index) {
-            this.prefix = prefix;
-            this.defaultMajorZIndex = default_major_z_index;
-        }
-    }
-
-    protected LineElemStyle(Cascade c, float default_major_z_index, BasicStroke line, Color color, BasicStroke dashesLine,
-            Color dashesBackground, float offset, float realWidth) {
-        super(c, default_major_z_index);
-        this.line = line;
-        this.color = color;
-        this.dashesLine = dashesLine;
-        this.dashesBackground = dashesBackground;
-        this.offset = offset;
-        this.realWidth = realWidth;
-    }
-
-    public static LineElemStyle createLine(Environment env) {
-        return createImpl(env, LineType.NORMAL);
-    }
-
-    public static LineElemStyle createLeftCasing(Environment env) {
-        LineElemStyle leftCasing = createImpl(env, LineType.LEFT_CASING);
-        if (leftCasing != null) {
-            leftCasing.isModifier = true;
-        }
-        return leftCasing;
-    }
-
-    public static LineElemStyle createRightCasing(Environment env) {
-        LineElemStyle rightCasing = createImpl(env, LineType.RIGHT_CASING);
-        if (rightCasing != null) {
-            rightCasing.isModifier = true;
-        }
-        return rightCasing;
-    }
-
-    public static LineElemStyle createCasing(Environment env) {
-        LineElemStyle casing = createImpl(env, LineType.CASING);
-        if (casing != null) {
-            casing.isModifier = true;
-        }
-        return casing;
-    }
-
-    private static LineElemStyle createImpl(Environment env, LineType type) {
-        Cascade c = env.mc.getCascade(env.layer);
-        Cascade c_def = env.mc.getCascade("default");
-        Float width;
-        switch (type) {
-            case NORMAL:
-                width = getWidth(c, WIDTH, getWidth(c_def, WIDTH, null));
-                break;
-            case CASING:
-                Float casingWidth = c.get(type.prefix + WIDTH, null, Float.class, true);
-                if (casingWidth == null) {
-                    RelativeFloat rel_casingWidth = c.get(type.prefix + WIDTH, null, RelativeFloat.class, true);
-                    if (rel_casingWidth != null) {
-                        casingWidth = rel_casingWidth.val / 2;
-                    }
-                }
-                if (casingWidth == null)
-                    return null;
-                width = getWidth(c, WIDTH, getWidth(c_def, WIDTH, null));
-                if (width == null) {
-                    width = 0f;
-                }
-                width += 2 * casingWidth;
-                break;
-            case LEFT_CASING:
-            case RIGHT_CASING:
-                width = getWidth(c, type.prefix + WIDTH, null);
-                break;
-            default:
-                throw new AssertionError();
-        }
-        if (width == null)
-            return null;
-
-        float realWidth = c.get(type.prefix + REAL_WIDTH, 0f, Float.class);
-        if (realWidth > 0 && MapPaintSettings.INSTANCE.isUseRealWidth()) {
-
-            /* if we have a "width" tag, try use it */
-            String widthTag = env.osm.get("width");
-            if (widthTag == null) {
-                widthTag = env.osm.get("est_width");
-            }
-            if (widthTag != null) {
-                try {
-                    realWidth = Float.parseFloat(widthTag);
-                } catch (NumberFormatException nfe) {
-                    Main.warn(nfe);
-                }
-            }
-        }
-
-        Float offset = c.get(OFFSET, 0f, Float.class);
-        switch (type) {
-            case NORMAL:
-                break;
-            case CASING:
-                offset += c.get(type.prefix + OFFSET, 0f, Float.class);
-                break;
-            case LEFT_CASING:
-            case RIGHT_CASING:
-                Float baseWidthOnDefault = getWidth(c_def, WIDTH, null);
-                Float baseWidth = getWidth(c, WIDTH, baseWidthOnDefault);
-                if (baseWidth == null || baseWidth < 2f) {
-                    baseWidth = 2f;
-                }
-                float casingOffset = c.get(type.prefix + OFFSET, 0f, Float.class);
-                casingOffset += baseWidth / 2 + width / 2;
-                /* flip sign for the right-casing-offset */
-                if (type == LineType.RIGHT_CASING) {
-                    casingOffset *= -1f;
-                }
-                offset += casingOffset;
-                break;
-        }
-
-        int alpha = 255;
-        Color color = c.get(type.prefix + COLOR, null, Color.class);
-        if (color != null) {
-            alpha = color.getAlpha();
-        }
-        if (type == LineType.NORMAL && color == null) {
-            color = c.get(FILL_COLOR, null, Color.class);
-        }
-        if (color == null) {
-            color = PaintColors.UNTAGGED.get();
-        }
-
-        Integer pAlpha = Utils.color_float2int(c.get(type.prefix + OPACITY, null, Float.class));
-        if (pAlpha != null) {
-            alpha = pAlpha;
-        }
-        color = new Color(color.getRed(), color.getGreen(), color.getBlue(), alpha);
-
-        float[] dashes = c.get(type.prefix + DASHES, null, float[].class, true);
-        if (dashes != null) {
-            boolean hasPositive = false;
-            for (float f : dashes) {
-                if (f > 0) {
-                    hasPositive = true;
-                }
-                if (f < 0) {
-                    dashes = null;
-                    break;
-                }
-            }
-            if (!hasPositive || (dashes != null && dashes.length == 0)) {
-                dashes = null;
-            }
-        }
-        float dashesOffset = c.get(type.prefix + DASHES_OFFSET, 0f, Float.class);
-        Color dashesBackground = c.get(type.prefix + DASHES_BACKGROUND_COLOR, null, Color.class);
-        if (dashesBackground != null) {
-            pAlpha = Utils.color_float2int(c.get(type.prefix + DASHES_BACKGROUND_OPACITY, null, Float.class));
-            if (pAlpha != null) {
-                alpha = pAlpha;
-            }
-            dashesBackground = new Color(dashesBackground.getRed(), dashesBackground.getGreen(),
-                    dashesBackground.getBlue(), alpha);
-        }
-
-        Integer cap = null;
-        Keyword capKW = c.get(type.prefix + LINECAP, null, Keyword.class);
-        if (capKW != null) {
-            if ("none".equals(capKW.val)) {
-                cap = BasicStroke.CAP_BUTT;
-            } else if ("round".equals(capKW.val)) {
-                cap = BasicStroke.CAP_ROUND;
-            } else if ("square".equals(capKW.val)) {
-                cap = BasicStroke.CAP_SQUARE;
-            }
-        }
-        if (cap == null) {
-            cap = dashes != null ? BasicStroke.CAP_BUTT : BasicStroke.CAP_ROUND;
-        }
-
-        Integer join = null;
-        Keyword joinKW = c.get(type.prefix + LINEJOIN, null, Keyword.class);
-        if (joinKW != null) {
-            if ("round".equals(joinKW.val)) {
-                join = BasicStroke.JOIN_ROUND;
-            } else if ("miter".equals(joinKW.val)) {
-                join = BasicStroke.JOIN_MITER;
-            } else if ("bevel".equals(joinKW.val)) {
-                join = BasicStroke.JOIN_BEVEL;
-            }
-        }
-        if (join == null) {
-            join = BasicStroke.JOIN_ROUND;
-        }
-
-        float miterlimit = c.get(type.prefix + MITERLIMIT, 10f, Float.class);
-        if (miterlimit < 1f) {
-            miterlimit = 10f;
-        }
-
-        BasicStroke line = new BasicStroke(width, cap, join, miterlimit, dashes, dashesOffset);
-        BasicStroke dashesLine = null;
-
-        if (dashes != null && dashesBackground != null) {
-            float[] dashes2 = new float[dashes.length];
-            System.arraycopy(dashes, 0, dashes2, 1, dashes.length - 1);
-            dashes2[0] = dashes[dashes.length-1];
-            dashesLine = new BasicStroke(width, cap, join, miterlimit, dashes2, dashes2[0] + dashesOffset);
-        }
-
-        return new LineElemStyle(c, type.defaultMajorZIndex, line, color, dashesLine, dashesBackground, offset, realWidth);
-    }
-
-    @Override
-    public void paintPrimitive(OsmPrimitive primitive, MapPaintSettings paintSettings, StyledMapRenderer painter,
-            boolean selected, boolean outermember, boolean member) {
-        Way w = (Way) primitive;
-        /* show direction arrows, if draw.segment.relevant_directions_only is not set,
-        the way is tagged with a direction key
-        (even if the tag is negated as in oneway=false) or the way is selected */
-        boolean showOrientation = !isModifier && (selected || paintSettings.isShowDirectionArrow()) && !paintSettings.isUseRealWidth();
-        boolean showOneway = !isModifier && !selected &&
-                !paintSettings.isUseRealWidth() &&
-                paintSettings.isShowOnewayArrow() && w.hasDirectionKeys();
-        boolean onewayReversed = w.reversedDirection();
-        /* head only takes over control if the option is true,
-        the direction should be shown at all and not only because it's selected */
-        boolean showOnlyHeadArrowOnly = showOrientation && !selected && paintSettings.isShowHeadArrowOnly();
-        Node lastN;
-
-        Color myDashedColor = dashesBackground;
-        BasicStroke myLine = line, myDashLine = dashesLine;
-        if (realWidth > 0 && paintSettings.isUseRealWidth() && !showOrientation) {
-            float myWidth = (int) (100 /  (float) (painter.getCircum() / realWidth));
-            if (myWidth < line.getLineWidth()) {
-                myWidth = line.getLineWidth();
-            }
-            myLine = new BasicStroke(myWidth, line.getEndCap(), line.getLineJoin(),
-                    line.getMiterLimit(), line.getDashArray(), line.getDashPhase());
-            if (dashesLine != null) {
-                myDashLine = new BasicStroke(myWidth, dashesLine.getEndCap(), dashesLine.getLineJoin(),
-                        dashesLine.getMiterLimit(), dashesLine.getDashArray(), dashesLine.getDashPhase());
-            }
-        }
-
-        Color myColor = color;
-        if (selected) {
-            myColor = paintSettings.getSelectedColor(color.getAlpha());
-        } else if (member || outermember) {
-            myColor = paintSettings.getRelationSelectedColor(color.getAlpha());
-        } else if (w.isDisabled()) {
-            myColor = paintSettings.getInactiveColor();
-            myDashedColor = paintSettings.getInactiveColor();
-        }
-
-        painter.drawWay(w, myColor, myLine, myDashLine, myDashedColor, offset, showOrientation,
-                showOnlyHeadArrowOnly, showOneway, onewayReversed);
-
-        if (paintSettings.isShowOrderNumber() && !painter.isInactiveMode()) {
-            int orderNumber = 0;
-            lastN = null;
-            for (Node n : w.getNodes()) {
-                if (lastN != null) {
-                    orderNumber++;
-                    painter.drawOrderNumber(lastN, n, orderNumber, myColor);
-                }
-                lastN = n;
-            }
-        }
-    }
-
-    @Override
-    public boolean isProperLineStyle() {
-        return !isModifier;
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (obj == null || getClass() != obj.getClass())
-            return false;
-        if (!super.equals(obj))
-            return false;
-        final LineElemStyle other = (LineElemStyle) obj;
-        return Objects.equals(line, other.line) &&
-            Objects.equals(color, other.color) &&
-            Objects.equals(dashesLine, other.dashesLine) &&
-            Objects.equals(dashesBackground, other.dashesBackground) &&
-            offset == other.offset &&
-            realWidth == other.realWidth;
-    }
-
-    @Override
-    public int hashCode() {
-        int hash = super.hashCode();
-        hash = 29 * hash + line.hashCode();
-        hash = 29 * hash + color.hashCode();
-        hash = 29 * hash + (dashesLine != null ? dashesLine.hashCode() : 0);
-        hash = 29 * hash + (dashesBackground != null ? dashesBackground.hashCode() : 0);
-        hash = 29 * hash + Float.floatToIntBits(offset);
-        hash = 29 * hash + Float.floatToIntBits(realWidth);
-        return hash;
-    }
-
-    @Override
-    public String toString() {
-        return "LineElemStyle{" + super.toString() + "width=" + line.getLineWidth() +
-            " realWidth=" + realWidth + " color=" + Utils.toString(color) +
-            " dashed=" + Arrays.toString(line.getDashArray()) +
-            (line.getDashPhase() == 0 ? "" : " dashesOffses=" + line.getDashPhase()) +
-            " dashedColor=" + Utils.toString(dashesBackground) +
-            " linejoin=" + linejoinToString(line.getLineJoin()) +
-            " linecap=" + linecapToString(line.getEndCap()) +
-            (offset == 0 ? "" : " offset=" + offset) +
-            '}';
-    }
-
-    public String linejoinToString(int linejoin) {
-        switch (linejoin) {
-            case BasicStroke.JOIN_BEVEL: return "bevel";
-            case BasicStroke.JOIN_ROUND: return "round";
-            case BasicStroke.JOIN_MITER: return "miter";
-            default: return null;
-        }
-    }
-
-    public String linecapToString(int linecap) {
-        switch (linecap) {
-            case BasicStroke.CAP_BUTT: return "none";
-            case BasicStroke.CAP_ROUND: return "round";
-            case BasicStroke.CAP_SQUARE: return "square";
-            default: return null;
-        }
-    }
-}
Index: unk/src/org/openstreetmap/josm/gui/mappaint/LineTextElemStyle.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/mappaint/LineTextElemStyle.java	(revision 9277)
+++ 	(revision )
@@ -1,60 +1,0 @@
-// License: GPL. For details, see LICENSE file.
-package org.openstreetmap.josm.gui.mappaint;
-
-import java.util.Objects;
-
-import org.openstreetmap.josm.data.osm.OsmPrimitive;
-import org.openstreetmap.josm.data.osm.Way;
-import org.openstreetmap.josm.data.osm.visitor.paint.MapPaintSettings;
-import org.openstreetmap.josm.data.osm.visitor.paint.PaintColors;
-import org.openstreetmap.josm.data.osm.visitor.paint.StyledMapRenderer;
-
-public class LineTextElemStyle extends ElemStyle {
-
-    private final TextElement text;
-
-    protected LineTextElemStyle(Cascade c, TextElement text) {
-        super(c, 4.9f);
-        this.text = text;
-    }
-
-    public static LineTextElemStyle create(final Environment env) {
-        final Cascade c = env.mc.getCascade(env.layer);
-
-        Keyword textPos = c.get(TEXT_POSITION, null, Keyword.class);
-        if (textPos != null && !"line".equals(textPos.val))
-            return null;
-
-        TextElement text = TextElement.create(env, PaintColors.TEXT.get(), false);
-        if (text == null)
-            return null;
-        return new LineTextElemStyle(c, text);
-    }
-
-    @Override
-    public void paintPrimitive(OsmPrimitive primitive, MapPaintSettings paintSettings, StyledMapRenderer painter,
-            boolean selected, boolean outermember, boolean member) {
-        Way w = (Way) primitive;
-        painter.drawTextOnPath(w, text);
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (obj == null || getClass() != obj.getClass())
-            return false;
-        if (!super.equals(obj))
-            return false;
-        final LineTextElemStyle other = (LineTextElemStyle) obj;
-        return Objects.equals(text, other.text);
-    }
-
-    @Override
-    public int hashCode() {
-        return text.hashCode();
-    }
-
-    @Override
-    public String toString() {
-        return "LineTextElemStyle{" + super.toString() + "text=" + text + '}';
-    }
-}
Index: unk/src/org/openstreetmap/josm/gui/mappaint/MapImage.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/mappaint/MapImage.java	(revision 9277)
+++ 	(revision )
@@ -1,242 +1,0 @@
-// License: GPL. For details, see LICENSE file.
-package org.openstreetmap.josm.gui.mappaint;
-
-import java.awt.Graphics;
-import java.awt.Image;
-import java.awt.Rectangle;
-import java.awt.image.BufferedImage;
-import java.util.Objects;
-
-import javax.swing.ImageIcon;
-
-import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.gui.mappaint.BoxTextElemStyle.BoxProvider;
-import org.openstreetmap.josm.gui.mappaint.BoxTextElemStyle.BoxProviderResult;
-import org.openstreetmap.josm.gui.util.GuiHelper;
-import org.openstreetmap.josm.tools.ImageProvider;
-import org.openstreetmap.josm.tools.ImageProvider.ImageCallback;
-import org.openstreetmap.josm.tools.Utils;
-
-/**
- * An image that will be displayed on the map.
- */
-public class MapImage {
-
-    private static final int MAX_SIZE = 48;
-
-    /**
-     * ImageIcon can change while the image is loading.
-     */
-    private BufferedImage img;
-
-    public int alpha = 255;
-    public String name;
-    public StyleSource source;
-    public boolean autoRescale;
-    public int width = -1;
-    public int height = -1;
-    public int offsetX;
-    public int offsetY;
-
-    private boolean temporary;
-    private BufferedImage disabledImgCache;
-
-    public MapImage(String name, StyleSource source) {
-        this(name, source, true);
-    }
-
-    public MapImage(String name, StyleSource source, boolean autoRescale) {
-        this.name = name;
-        this.source = source;
-        this.autoRescale = autoRescale;
-    }
-
-    /**
-     * Get the image associated with this MapImage object.
-     *
-     * @param disabled {@code} true to request disabled version, {@code false} for the standard version
-     * @return the image
-     */
-    public BufferedImage getImage(boolean disabled) {
-        if (disabled) {
-            return getDisabled();
-        } else {
-            return getImage();
-        }
-    }
-
-    private BufferedImage getDisabled() {
-        if (disabledImgCache != null)
-                return disabledImgCache;
-        if (img == null)
-            getImage(); // fix #7498 ?
-        Image disImg = GuiHelper.getDisabledImage(img);
-        if (disImg instanceof BufferedImage) {
-            disabledImgCache = (BufferedImage) disImg;
-        } else {
-            disabledImgCache = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
-            Graphics g = disabledImgCache.getGraphics();
-            g.drawImage(disImg, 0, 0, null);
-            g.dispose();
-        }
-        return disabledImgCache;
-    }
-
-    private BufferedImage getImage() {
-        if (img != null)
-            return img;
-        temporary = false;
-        new ImageProvider(name)
-                .setDirs(MapPaintStyles.getIconSourceDirs(source))
-                .setId("mappaint."+source.getPrefName())
-                .setArchive(source.zipIcons)
-                .setInArchiveDir(source.getZipEntryDirName())
-                .setWidth(width)
-                .setHeight(height)
-                .setOptional(true)
-                .getInBackground(new ImageCallback() {
-                    @Override
-                    public void finished(ImageIcon result) {
-                        synchronized (MapImage.this) {
-                            if (result == null) {
-                                ImageIcon noIcon = MapPaintStyles.getNoIcon_Icon(source);
-                                img = noIcon == null ? null : (BufferedImage) noIcon.getImage();
-                            } else {
-                                img = (BufferedImage) rescale(result.getImage());
-                            }
-                            if (temporary) {
-                                disabledImgCache = null;
-                                Main.map.mapView.preferenceChanged(null); // otherwise repaint is ignored, because layer hasn't changed
-                                Main.map.mapView.repaint();
-                            }
-                            temporary = false;
-                        }
-                    }
-                }
-        );
-        synchronized (this) {
-            if (img == null) {
-                img = (BufferedImage) ImageProvider.get("clock").getImage();
-                temporary = true;
-            }
-        }
-        return img;
-    }
-
-    public int getWidth() {
-        return getImage().getWidth(null);
-    }
-
-    public int getHeight() {
-        return getImage().getHeight(null);
-    }
-
-    public float getAlphaFloat() {
-        return Utils.color_int2float(alpha);
-    }
-
-    /**
-     * Determines if image is not completely loaded and {@code getImage()} returns a temporary image.
-     * @return {@code true} if image is not completely loaded and getImage() returns a temporary image
-     */
-    public boolean isTemporary() {
-        return temporary;
-    }
-
-    protected class MapImageBoxProvider implements BoxProvider {
-        @Override
-        public BoxProviderResult get() {
-            return new BoxProviderResult(box(), temporary);
-        }
-
-        private Rectangle box() {
-            int w = getWidth(), h = getHeight();
-            if (mustRescale(getImage())) {
-                w = 16;
-                h = 16;
-            }
-            return new Rectangle(-w/2, -h/2, w, h);
-        }
-
-        private MapImage getParent() {
-            return MapImage.this;
-        }
-
-        @Override
-        public int hashCode() {
-            return MapImage.this.hashCode();
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            if (!(obj instanceof BoxProvider))
-                return false;
-            if (obj instanceof MapImageBoxProvider) {
-                MapImageBoxProvider other = (MapImageBoxProvider) obj;
-                return MapImage.this.equals(other.getParent());
-            } else if (temporary) {
-                return false;
-            } else {
-                final BoxProvider other = (BoxProvider) obj;
-                BoxProviderResult resultOther = other.get();
-                if (resultOther.isTemporary()) return false;
-                return box().equals(resultOther.getBox());
-            }
-        }
-    }
-
-    public BoxProvider getBoxProvider() {
-        return new MapImageBoxProvider();
-    }
-
-    /**
-     * Rescale excessively large images.
-     * @param image the unscaled image
-     * @return The scaled down version to 16x16 pixels if the image height and width exceeds 48 pixels and no size has been explicitely specified
-     */
-    private Image rescale(Image image) {
-        if (image == null) return null;
-        // Scale down large (.svg) images to 16x16 pixels if no size is explicitely specified
-        if (mustRescale(image)) {
-            return ImageProvider.createBoundedImage(image, 16);
-        } else {
-            return image;
-        }
-    }
-
-    private boolean mustRescale(Image image) {
-        return autoRescale && width  == -1 && image.getWidth(null) > MAX_SIZE
-             && height == -1 && image.getHeight(null) > MAX_SIZE;
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (obj == null || getClass() != obj.getClass())
-            return false;
-        final MapImage other = (MapImage) obj;
-        // img changes when image is fully loaded and can't be used for equality check.
-        return  alpha == other.alpha &&
-                Objects.equals(name, other.name) &&
-                Objects.equals(source, other.source) &&
-                autoRescale == other.autoRescale &&
-                width == other.width &&
-                height == other.height;
-    }
-
-    @Override
-    public int hashCode() {
-        int hash = 7;
-        hash = 67 * hash + alpha;
-        hash = 67 * hash + name.hashCode();
-        hash = 67 * hash + source.hashCode();
-        hash = 67 * hash + (autoRescale ? 1 : 0);
-        hash = 67 * hash + width;
-        hash = 67 * hash + height;
-        return hash;
-    }
-
-    @Override
-    public String toString() {
-        return name;
-    }
-}
Index: /trunk/src/org/openstreetmap/josm/gui/mappaint/MapPaintStyles.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/mappaint/MapPaintStyles.java	(revision 9277)
+++ /trunk/src/org/openstreetmap/josm/gui/mappaint/MapPaintStyles.java	(revision 9278)
@@ -29,4 +29,7 @@
 import org.openstreetmap.josm.gui.mappaint.StyleCache.StyleList;
 import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSStyleSource;
+import org.openstreetmap.josm.gui.mappaint.styleelement.MapImage;
+import org.openstreetmap.josm.gui.mappaint.styleelement.NodeElement;
+import org.openstreetmap.josm.gui.mappaint.styleelement.StyleElement;
 import org.openstreetmap.josm.gui.mappaint.xml.XmlStyleSource;
 import org.openstreetmap.josm.gui.preferences.SourceEntry;
@@ -192,7 +195,7 @@
             }
             if (styleList != null) {
-                for (ElemStyle style : styleList) {
-                    if (style instanceof NodeElemStyle) {
-                        MapImage mapImage = ((NodeElemStyle) style).mapImage;
+                for (StyleElement style : styleList) {
+                    if (style instanceof NodeElement) {
+                        MapImage mapImage = ((NodeElement) style).mapImage;
                         if (mapImage != null) {
                             if (includeDeprecatedIcon || mapImage.name == null || !"misc/deprecated.png".equals(mapImage.name)) {
Index: unk/src/org/openstreetmap/josm/gui/mappaint/NodeElemStyle.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/mappaint/NodeElemStyle.java	(revision 9277)
+++ 	(revision )
@@ -1,403 +1,0 @@
-// License: GPL. For details, see LICENSE file.
-package org.openstreetmap.josm.gui.mappaint;
-
-import java.awt.BasicStroke;
-import java.awt.Color;
-import java.awt.Rectangle;
-import java.awt.Stroke;
-import java.util.Objects;
-
-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.Relation;
-import org.openstreetmap.josm.data.osm.visitor.paint.MapPaintSettings;
-import org.openstreetmap.josm.data.osm.visitor.paint.StyledMapRenderer;
-import org.openstreetmap.josm.gui.mappaint.BoxTextElemStyle.BoxProvider;
-import org.openstreetmap.josm.gui.mappaint.BoxTextElemStyle.SimpleBoxProvider;
-import org.openstreetmap.josm.gui.mappaint.MapPaintStyles.IconReference;
-import org.openstreetmap.josm.gui.mappaint.StyleCache.StyleList;
-import org.openstreetmap.josm.gui.util.RotationAngle;
-import org.openstreetmap.josm.tools.Utils;
-
-/**
- * applies for Nodes and turn restriction relations
- */
-public class NodeElemStyle extends ElemStyle implements StyleKeys {
-    public final MapImage mapImage;
-    public final RotationAngle mapImageAngle;
-    public final Symbol symbol;
-
-    public enum SymbolShape { SQUARE, CIRCLE, TRIANGLE, PENTAGON, HEXAGON, HEPTAGON, OCTAGON, NONAGON, DECAGON }
-
-    public static class Symbol {
-        public SymbolShape symbol;
-        public int size;
-        public Stroke stroke;
-        public Color strokeColor;
-        public Color fillColor;
-
-        public Symbol(SymbolShape symbol, int size, Stroke stroke, Color strokeColor, Color fillColor) {
-            if (stroke != null && strokeColor == null)
-                throw new IllegalArgumentException("Stroke given without color");
-            if (stroke == null && fillColor == null)
-                throw new IllegalArgumentException("Either a stroke or a fill color must be given");
-            this.symbol = symbol;
-            this.size = size;
-            this.stroke = stroke;
-            this.strokeColor = strokeColor;
-            this.fillColor = fillColor;
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            if (obj == null || getClass() != obj.getClass())
-                return false;
-            final Symbol other = (Symbol) obj;
-            return  symbol == other.symbol &&
-                    size == other.size &&
-                    Objects.equals(stroke, other.stroke) &&
-                    Objects.equals(strokeColor, other.strokeColor) &&
-                    Objects.equals(fillColor, other.fillColor);
-        }
-
-        @Override
-        public int hashCode() {
-            int hash = 7;
-            hash = 67 * hash + symbol.hashCode();
-            hash = 67 * hash + size;
-            hash = 67 * hash + (stroke != null ? stroke.hashCode() : 0);
-            hash = 67 * hash + (strokeColor != null ? strokeColor.hashCode() : 0);
-            hash = 67 * hash + (fillColor != null ? fillColor.hashCode() : 0);
-            return hash;
-        }
-
-        @Override
-        public String toString() {
-            return "symbol=" + symbol + " size=" + size +
-                    (stroke != null ? " stroke=" + stroke + " strokeColor=" + strokeColor : "") +
-                    (fillColor != null ? " fillColor=" + fillColor : "");
-        }
-    }
-
-    public static final NodeElemStyle SIMPLE_NODE_ELEMSTYLE;
-    public static final BoxProvider SIMPLE_NODE_ELEMSTYLE_BOXPROVIDER;
-    static {
-        MultiCascade mc = new MultiCascade();
-        mc.getOrCreateCascade("default");
-        SIMPLE_NODE_ELEMSTYLE = create(new Environment(null, mc, "default", null), 4.1f, true);
-        if (SIMPLE_NODE_ELEMSTYLE == null) throw new AssertionError();
-        SIMPLE_NODE_ELEMSTYLE_BOXPROVIDER = SIMPLE_NODE_ELEMSTYLE.getBoxProvider();
-    }
-
-    public static final StyleList DEFAULT_NODE_STYLELIST = new StyleList(NodeElemStyle.SIMPLE_NODE_ELEMSTYLE);
-    public static final StyleList DEFAULT_NODE_STYLELIST_TEXT = new StyleList(NodeElemStyle.SIMPLE_NODE_ELEMSTYLE,
-            BoxTextElemStyle.SIMPLE_NODE_TEXT_ELEMSTYLE);
-
-    protected NodeElemStyle(Cascade c, MapImage mapImage, Symbol symbol, float default_major_z_index, RotationAngle rotationAngle) {
-        super(c, default_major_z_index);
-        this.mapImage = mapImage;
-        this.symbol = symbol;
-        this.mapImageAngle = rotationAngle;
-    }
-
-    public static NodeElemStyle create(Environment env) {
-        return create(env, 4f, false);
-    }
-
-    private static NodeElemStyle create(Environment env, float default_major_z_index, boolean allowDefault) {
-        Cascade c = env.mc.getCascade(env.layer);
-
-        MapImage mapImage = createIcon(env, ICON_KEYS);
-        Symbol symbol = null;
-        if (mapImage == null) {
-            symbol = createSymbol(env);
-        }
-        RotationAngle rotationAngle = null;
-        final Float angle = c.get(ICON_ROTATION, null, Float.class, true);
-        if (angle != null) {
-            rotationAngle = RotationAngle.buildStaticRotation(angle);
-        } else {
-            final Keyword rotationKW = c.get(ICON_ROTATION, null, Keyword.class);
-            if (rotationKW != null) {
-                if ("way".equals(rotationKW.val)) {
-                    rotationAngle = RotationAngle.buildWayDirectionRotation();
-                } else {
-                    try {
-                        rotationAngle = RotationAngle.buildStaticRotation(rotationKW.val);
-                    } catch (IllegalArgumentException ignore) {
-                        if (Main.isTraceEnabled()) {
-                            Main.trace(ignore.getMessage());
-                        }
-                    }
-                }
-            }
-        }
-
-        // optimization: if we neither have a symbol, nor a mapImage
-        // we don't have to check for the remaining style properties and we don't
-        // have to allocate a node element style.
-        if (!allowDefault && symbol == null && mapImage == null) return null;
-
-        return new NodeElemStyle(c, mapImage, symbol, default_major_z_index, rotationAngle);
-    }
-
-    public static MapImage createIcon(final Environment env, final String[] keys) {
-        Cascade c = env.mc.getCascade(env.layer);
-
-        final IconReference iconRef = c.get(keys[ICON_IMAGE_IDX], null, IconReference.class, true);
-        if (iconRef == null)
-            return null;
-
-        Cascade c_def = env.mc.getCascade("default");
-
-        Float widthOnDefault = c_def.get(keys[ICON_WIDTH_IDX], null, Float.class);
-        if (widthOnDefault != null && widthOnDefault <= 0) {
-            widthOnDefault = null;
-        }
-        Float widthF = getWidth(c, keys[ICON_WIDTH_IDX], widthOnDefault);
-
-        Float heightOnDefault = c_def.get(keys[ICON_HEIGHT_IDX], null, Float.class);
-        if (heightOnDefault != null && heightOnDefault <= 0) {
-            heightOnDefault = null;
-        }
-        Float heightF = getWidth(c, keys[ICON_HEIGHT_IDX], heightOnDefault);
-
-        int width = widthF == null ? -1 : Math.round(widthF);
-        int height = heightF == null ? -1 : Math.round(heightF);
-
-        float offsetXF = 0f;
-        float offsetYF = 0f;
-        if (keys[ICON_OFFSET_X_IDX] != null) {
-            offsetXF = c.get(keys[ICON_OFFSET_X_IDX], 0f, Float.class);
-            offsetYF = c.get(keys[ICON_OFFSET_Y_IDX], 0f, Float.class);
-        }
-
-        final MapImage mapImage = new MapImage(iconRef.iconName, iconRef.source);
-
-        mapImage.width = width;
-        mapImage.height = height;
-        mapImage.offsetX = Math.round(offsetXF);
-        mapImage.offsetY = Math.round(offsetYF);
-
-        mapImage.alpha = Math.min(255, Math.max(0, Integer.valueOf(Main.pref.getInteger("mappaint.icon-image-alpha", 255))));
-        Integer pAlpha = Utils.color_float2int(c.get(keys[ICON_OPACITY_IDX], null, float.class));
-        if (pAlpha != null) {
-            mapImage.alpha = pAlpha;
-        }
-        return mapImage;
-    }
-
-    private static Symbol createSymbol(Environment env) {
-        Cascade c = env.mc.getCascade(env.layer);
-        Cascade c_def = env.mc.getCascade("default");
-
-        SymbolShape shape;
-        Keyword shapeKW = c.get("symbol-shape", null, Keyword.class);
-        if (shapeKW == null)
-            return null;
-        if ("square".equals(shapeKW.val)) {
-            shape = SymbolShape.SQUARE;
-        } else if ("circle".equals(shapeKW.val)) {
-            shape = SymbolShape.CIRCLE;
-        } else if ("triangle".equals(shapeKW.val)) {
-            shape = SymbolShape.TRIANGLE;
-        } else if ("pentagon".equals(shapeKW.val)) {
-            shape = SymbolShape.PENTAGON;
-        } else if ("hexagon".equals(shapeKW.val)) {
-            shape = SymbolShape.HEXAGON;
-        } else if ("heptagon".equals(shapeKW.val)) {
-            shape = SymbolShape.HEPTAGON;
-        } else if ("octagon".equals(shapeKW.val)) {
-            shape = SymbolShape.OCTAGON;
-        } else if ("nonagon".equals(shapeKW.val)) {
-            shape = SymbolShape.NONAGON;
-        } else if ("decagon".equals(shapeKW.val)) {
-            shape = SymbolShape.DECAGON;
-        } else
-            return null;
-
-        Float sizeOnDefault = c_def.get("symbol-size", null, Float.class);
-        if (sizeOnDefault != null && sizeOnDefault <= 0) {
-            sizeOnDefault = null;
-        }
-        Float size = getWidth(c, "symbol-size", sizeOnDefault);
-
-        if (size == null) {
-            size = 10f;
-        }
-
-        if (size <= 0)
-            return null;
-
-        Float strokeWidthOnDefault = getWidth(c_def, "symbol-stroke-width", null);
-        Float strokeWidth = getWidth(c, "symbol-stroke-width", strokeWidthOnDefault);
-
-        Color strokeColor = c.get("symbol-stroke-color", null, Color.class);
-
-        if (strokeWidth == null && strokeColor != null) {
-            strokeWidth = 1f;
-        } else if (strokeWidth != null && strokeColor == null) {
-            strokeColor = Color.ORANGE;
-        }
-
-        Stroke stroke = null;
-        if (strokeColor != null) {
-            Integer strokeAlpha = Utils.color_float2int(c.get("symbol-stroke-opacity", null, Float.class));
-            if (strokeAlpha != null) {
-                strokeColor = new Color(strokeColor.getRed(), strokeColor.getGreen(),
-                        strokeColor.getBlue(), strokeAlpha);
-            }
-            stroke = new BasicStroke(strokeWidth);
-        }
-
-        Color fillColor = c.get("symbol-fill-color", null, Color.class);
-        if (stroke == null && fillColor == null) {
-            fillColor = Color.BLUE;
-        }
-
-        if (fillColor != null) {
-            Integer fillAlpha = Utils.color_float2int(c.get("symbol-fill-opacity", null, Float.class));
-            if (fillAlpha != null) {
-                fillColor = new Color(fillColor.getRed(), fillColor.getGreen(),
-                        fillColor.getBlue(), fillAlpha);
-            }
-        }
-
-        return new Symbol(shape, Math.round(size), stroke, strokeColor, fillColor);
-    }
-
-    @Override
-    public void paintPrimitive(OsmPrimitive primitive, MapPaintSettings settings, StyledMapRenderer painter,
-            boolean selected, boolean outermember, boolean member) {
-        if (primitive instanceof Node) {
-            Node n = (Node) primitive;
-            if (mapImage != null && painter.isShowIcons()) {
-                painter.drawNodeIcon(n, mapImage, painter.isInactiveMode() || n.isDisabled(), selected, member,
-                        mapImageAngle == null ? 0.0 : mapImageAngle.getRotationAngle(primitive));
-            } else if (symbol != null) {
-                Color fillColor = symbol.fillColor;
-                if (fillColor != null) {
-                    if (painter.isInactiveMode() || n.isDisabled()) {
-                        fillColor = settings.getInactiveColor();
-                    } else if (selected) {
-                        fillColor = settings.getSelectedColor(fillColor.getAlpha());
-                    } else if (member) {
-                        fillColor = settings.getRelationSelectedColor(fillColor.getAlpha());
-                    }
-                }
-                Color strokeColor = symbol.strokeColor;
-                if (strokeColor != null) {
-                    if (painter.isInactiveMode() || n.isDisabled()) {
-                        strokeColor = settings.getInactiveColor();
-                    } else if (selected) {
-                        strokeColor = settings.getSelectedColor(strokeColor.getAlpha());
-                    } else if (member) {
-                        strokeColor = settings.getRelationSelectedColor(strokeColor.getAlpha());
-                    }
-                }
-                painter.drawNodeSymbol(n, symbol, fillColor, strokeColor);
-            } else {
-                Color color;
-                boolean isConnection = n.isConnectionNode();
-
-                if (painter.isInactiveMode() || 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);
-
-            }
-        } else if (primitive instanceof Relation && mapImage != null) {
-            painter.drawRestriction((Relation) primitive, mapImage, painter.isInactiveMode() || primitive.isDisabled());
-        }
-    }
-
-    public BoxProvider getBoxProvider() {
-        if (mapImage != null)
-            return mapImage.getBoxProvider();
-        else if (symbol != null)
-            return new SimpleBoxProvider(new Rectangle(-symbol.size/2, -symbol.size/2, symbol.size, symbol.size));
-        else {
-            // This is only executed once, so no performance concerns.
-            // However, it would be better, if the settings could be changed at runtime.
-            int size = Utils.max(
-                    Main.pref.getInteger("mappaint.node.selected-size", 5),
-                    Main.pref.getInteger("mappaint.node.unselected-size", 3),
-                    Main.pref.getInteger("mappaint.node.connection-size", 5),
-                    Main.pref.getInteger("mappaint.node.tagged-size", 3)
-            );
-            return new SimpleBoxProvider(new Rectangle(-size/2, -size/2, size, size));
-        }
-    }
-
-    @Override
-    public int hashCode() {
-        int hash = super.hashCode();
-        hash = 17 * hash + (mapImage != null ? mapImage.hashCode() : 0);
-        hash = 17 * hash + (symbol != null ? symbol.hashCode() : 0);
-        hash = 17 * hash + (mapImageAngle != null ? mapImageAngle.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 (!Objects.equals(mapImage, other.mapImage))
-            return false;
-        if (!Objects.equals(symbol, other.symbol))
-            return false;
-        if (!Objects.equals(mapImageAngle, other.mapImageAngle))
-            return false;
-        return true;
-    }
-
-    @Override
-    public String toString() {
-        StringBuilder s = new StringBuilder("NodeElemStyle{");
-        s.append(super.toString());
-        if (mapImage != null) {
-            s.append(" icon=[" + mapImage + ']');
-        }
-        if (symbol != null) {
-            s.append(" symbol=[" + symbol + ']');
-        }
-        if (mapImageAngle != null) {
-            s.append(" mapImageAngle=[" + mapImageAngle + ']');
-        }
-        s.append('}');
-        return s.toString();
-    }
-}
Index: unk/src/org/openstreetmap/josm/gui/mappaint/RepeatImageElemStyle.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/mappaint/RepeatImageElemStyle.java	(revision 9277)
+++ 	(revision )
@@ -1,95 +1,0 @@
-// License: GPL. For details, see LICENSE file.
-package org.openstreetmap.josm.gui.mappaint;
-
-import org.openstreetmap.josm.data.osm.OsmPrimitive;
-import org.openstreetmap.josm.data.osm.Way;
-import org.openstreetmap.josm.data.osm.visitor.paint.MapPaintSettings;
-import org.openstreetmap.josm.data.osm.visitor.paint.StyledMapRenderer;
-import org.openstreetmap.josm.tools.CheckParameterUtil;
-
-public class RepeatImageElemStyle extends ElemStyle implements StyleKeys {
-
-    public enum LineImageAlignment { TOP, CENTER, BOTTOM }
-
-    public MapImage pattern;
-    public float offset;
-    public float spacing;
-    public float phase;
-    public LineImageAlignment align;
-
-    public RepeatImageElemStyle(Cascade c, MapImage pattern, float offset, float spacing, float phase, LineImageAlignment align) {
-        super(c, 2.9f);
-        CheckParameterUtil.ensureParameterNotNull(pattern);
-        CheckParameterUtil.ensureParameterNotNull(align);
-        this.pattern = pattern;
-        this.offset = offset;
-        this.spacing = spacing;
-        this.phase = phase;
-        this.align = align;
-    }
-
-    public static RepeatImageElemStyle create(Environment env) {
-        MapImage pattern = NodeElemStyle.createIcon(env, REPEAT_IMAGE_KEYS);
-        if (pattern == null)
-            return null;
-        Cascade c = env.mc.getCascade(env.layer);
-        float offset = c.get(REPEAT_IMAGE_OFFSET, 0f, Float.class);
-        float spacing = c.get(REPEAT_IMAGE_SPACING, 0f, Float.class);
-        float phase = -c.get(REPEAT_IMAGE_PHASE, 0f, Float.class);
-
-        LineImageAlignment align = LineImageAlignment.CENTER;
-        Keyword alignKW = c.get(REPEAT_IMAGE_ALIGN, Keyword.CENTER, Keyword.class);
-        if ("top".equals(alignKW.val)) {
-            align = LineImageAlignment.TOP;
-        } else if ("bottom".equals(alignKW.val)) {
-            align = LineImageAlignment.BOTTOM;
-        }
-
-        return new RepeatImageElemStyle(c, pattern, offset, spacing, phase, align);
-    }
-
-    @Override
-    public void paintPrimitive(OsmPrimitive primitive, MapPaintSettings paintSettings, StyledMapRenderer painter,
-            boolean selected, boolean outermember, boolean member) {
-        Way w = (Way) primitive;
-        painter.drawRepeatImage(w, pattern, painter.isInactiveMode() || w.isDisabled(), offset, spacing, phase, align);
-    }
-
-    @Override
-    public boolean isProperLineStyle() {
-        return true;
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (obj == null || getClass() != obj.getClass())
-            return false;
-        if (!super.equals(obj))
-            return false;
-        final RepeatImageElemStyle other = (RepeatImageElemStyle) obj;
-        if (!this.pattern.equals(other.pattern)) return false;
-        if (this.offset != other.offset) return false;
-        if (this.spacing != other.spacing) return false;
-        if (this.phase != other.phase) return false;
-        if (this.align != other.align) return false;
-        return true;
-    }
-
-    @Override
-    public int hashCode() {
-        int hash = 7;
-        hash = 83 * hash + this.pattern.hashCode();
-        hash = 83 * hash + Float.floatToIntBits(this.offset);
-        hash = 83 * hash + Float.floatToIntBits(this.spacing);
-        hash = 83 * hash + Float.floatToIntBits(this.phase);
-        hash = 83 * hash + this.align.hashCode();
-        return hash;
-    }
-
-    @Override
-    public String toString() {
-        return "RepeatImageStyle{" + super.toString() + "pattern=[" + pattern +
-                "], offset=" + offset + ", spacing=" + spacing +
-                ", phase=" + (-phase) + ", align=" + align + '}';
-    }
-}
Index: /trunk/src/org/openstreetmap/josm/gui/mappaint/StyleCache.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/mappaint/StyleCache.java	(revision 9277)
+++ /trunk/src/org/openstreetmap/josm/gui/mappaint/StyleCache.java	(revision 9278)
@@ -10,4 +10,5 @@
 
 import org.openstreetmap.josm.data.osm.Storage;
+import org.openstreetmap.josm.gui.mappaint.styleelement.StyleElement;
 import org.openstreetmap.josm.tools.Pair;
 
@@ -16,6 +17,6 @@
  * Splits the range of possible scale values (0 &lt; scale &lt; +Infinity) into multiple
  * subranges, for each scale range it keeps a list of styles.
- * Immutable class, equals &amp; hashCode is required (the same for StyleList, ElemStyle
- * and its subclasses).
+ * Immutable class, equals &amp; hashCode is required (the same for StyleList, StyleElement
+ and its subclasses).
  */
 public final class StyleCache {
@@ -46,6 +47,6 @@
      * List of Styles, immutable
      */
-    public static class StyleList implements Iterable<ElemStyle> {
-        private final List<ElemStyle> lst;
+    public static class StyleList implements Iterable<StyleElement> {
+        private final List<StyleElement> lst;
 
         /**
@@ -56,13 +57,13 @@
         }
 
-        public StyleList(ElemStyle... init) {
+        public StyleList(StyleElement... init) {
             lst = new ArrayList<>(Arrays.asList(init));
         }
 
-        public StyleList(Collection<ElemStyle> sl) {
+        public StyleList(Collection<StyleElement> sl) {
             lst = new ArrayList<>(sl);
         }
 
-        public StyleList(StyleList sl, ElemStyle s) {
+        public StyleList(StyleList sl, StyleElement s) {
             lst = new ArrayList<>(sl.lst);
             lst.add(s);
@@ -70,5 +71,5 @@
 
         @Override
-        public Iterator<ElemStyle> iterator() {
+        public Iterator<StyleElement> iterator() {
             return lst.iterator();
         }
Index: unk/src/org/openstreetmap/josm/gui/mappaint/TextElement.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/mappaint/TextElement.java	(revision 9277)
+++ 	(revision )
@@ -1,223 +1,0 @@
-// License: GPL. For details, see LICENSE file.
-package org.openstreetmap.josm.gui.mappaint;
-
-import java.awt.Color;
-import java.awt.Font;
-import java.util.Objects;
-
-import org.openstreetmap.josm.data.osm.OsmPrimitive;
-import org.openstreetmap.josm.gui.mappaint.LabelCompositionStrategy.DeriveLabelFromNameTagsCompositionStrategy;
-import org.openstreetmap.josm.gui.mappaint.LabelCompositionStrategy.StaticLabelCompositionStrategy;
-import org.openstreetmap.josm.gui.mappaint.LabelCompositionStrategy.TagLookupCompositionStrategy;
-import org.openstreetmap.josm.gui.mappaint.MapPaintStyles.TagKeyReference;
-import org.openstreetmap.josm.tools.CheckParameterUtil;
-import org.openstreetmap.josm.tools.Utils;
-
-/**
- * Represents the rendering style for a textual label placed somewhere on the map.
- * @since 3880
- */
-public class TextElement implements StyleKeys {
-    public static final LabelCompositionStrategy AUTO_LABEL_COMPOSITION_STRATEGY = new DeriveLabelFromNameTagsCompositionStrategy();
-
-    /** the strategy for building the actual label value for a given a {@link OsmPrimitive}.
-     * Check for null before accessing.
-     */
-    public LabelCompositionStrategy labelCompositionStrategy;
-    /** the font to be used when rendering*/
-    public Font font;
-    public int xOffset;
-    public int yOffset;
-    public Color color;
-    public Float haloRadius;
-    public Color haloColor;
-
-    /**
-     * Creates a new text element
-     *
-     * @param strategy the strategy indicating how the text is composed for a specific {@link OsmPrimitive} to be rendered.
-     * If null, no label is rendered.
-     * @param font the font to be used. Must not be null.
-     * @param xOffset x offset
-     * @param yOffset y offset
-     * @param color the color to be used. Must not be null
-     * @param haloRadius halo radius
-     * @param haloColor halo color
-     */
-    public TextElement(LabelCompositionStrategy strategy, Font font, int xOffset, int yOffset, Color color, Float haloRadius, Color haloColor) {
-        CheckParameterUtil.ensureParameterNotNull(font);
-        CheckParameterUtil.ensureParameterNotNull(color);
-        labelCompositionStrategy = strategy;
-        this.font = font;
-        this.xOffset = xOffset;
-        this.yOffset = yOffset;
-        this.color = color;
-        this.haloRadius = haloRadius;
-        this.haloColor = haloColor;
-    }
-
-    /**
-     * Copy constructor
-     *
-     * @param other the other element.
-     */
-    public TextElement(TextElement other) {
-        this.labelCompositionStrategy = other.labelCompositionStrategy;
-        this.font = other.font;
-        this.xOffset = other.xOffset;
-        this.yOffset = other.yOffset;
-        this.color = other.color;
-        this.haloColor = other.haloColor;
-        this.haloRadius = other.haloRadius;
-    }
-
-    /**
-     * Derives a suitable label composition strategy from the style properties in {@code c}.
-     *
-     * @param c the style properties
-     * @param defaultAnnotate whether to return {@link #AUTO_LABEL_COMPOSITION_STRATEGY} if not strategy is found
-     * @return the label composition strategy, or {@code null}
-     */
-    protected static LabelCompositionStrategy buildLabelCompositionStrategy(Cascade c, boolean defaultAnnotate) {
-        /*
-         * If the cascade includes a TagKeyReference we will lookup the rendered label
-         * from a tag value.
-         */
-        TagKeyReference tkr = c.get(TEXT, null, TagKeyReference.class, true);
-        if (tkr != null)
-            return new TagLookupCompositionStrategy(tkr.key);
-
-        /*
-         * Check whether the label composition strategy is given by a keyword
-         */
-        Keyword keyword = c.get(TEXT, null, Keyword.class, true);
-        if (Keyword.AUTO.equals(keyword))
-            return AUTO_LABEL_COMPOSITION_STRATEGY;
-
-        /*
-         * Do we have a static text label?
-         */
-        String text = c.get(TEXT, null, String.class, true);
-        if (text != null)
-            return new StaticLabelCompositionStrategy(text);
-        return defaultAnnotate ? AUTO_LABEL_COMPOSITION_STRATEGY : null;
-    }
-
-    /**
-     * Builds a text element from style properties in {@code c} and the
-     * default text color {@code defaultTextColor}
-     *
-     * @param env the environment
-     * @param defaultTextColor the default text color. Must not be null.
-     * @param defaultAnnotate true, if a text label shall be rendered by default, even if the style sheet
-     *   doesn't include respective style declarations
-     * @return the text element or null, if the style properties don't include
-     * properties for text rendering
-     * @throws IllegalArgumentException if {@code defaultTextColor} is null
-     */
-    public static TextElement create(Environment env, Color defaultTextColor, boolean defaultAnnotate) {
-        CheckParameterUtil.ensureParameterNotNull(defaultTextColor);
-        Cascade c = env.mc.getCascade(env.layer);
-
-        LabelCompositionStrategy strategy = buildLabelCompositionStrategy(c, defaultAnnotate);
-        if (strategy == null) return null;
-        String s = strategy.compose(env.osm);
-        if (s == null) return null;
-        Font font = ElemStyle.getFont(c, s);
-
-        float xOffset = 0;
-        float yOffset = 0;
-        float[] offset = c.get(TEXT_OFFSET, null, float[].class);
-        if (offset != null) {
-            if (offset.length == 1) {
-                yOffset = offset[0];
-            } else if (offset.length >= 2) {
-                xOffset = offset[0];
-                yOffset = offset[1];
-            }
-        }
-        xOffset = c.get(TEXT_OFFSET_X, xOffset, Float.class);
-        yOffset = c.get(TEXT_OFFSET_Y, yOffset, Float.class);
-
-        Color color = c.get(TEXT_COLOR, defaultTextColor, Color.class);
-        float alpha = c.get(TEXT_OPACITY, 1f, Float.class);
-        color = new Color(color.getRed(), color.getGreen(),
-                color.getBlue(), Utils.color_float2int(alpha));
-
-        Float haloRadius = c.get(TEXT_HALO_RADIUS, null, Float.class);
-        if (haloRadius != null && haloRadius <= 0) {
-            haloRadius = null;
-        }
-        Color haloColor = null;
-        if (haloRadius != null) {
-            haloColor = c.get(TEXT_HALO_COLOR, Utils.complement(color), Color.class);
-            float haloAlpha = c.get(TEXT_HALO_OPACITY, 1f, Float.class);
-            haloColor = new Color(haloColor.getRed(), haloColor.getGreen(),
-                    haloColor.getBlue(), Utils.color_float2int(haloAlpha));
-        }
-
-        return new TextElement(strategy, font, (int) xOffset, -(int) yOffset, color, haloRadius, haloColor);
-    }
-
-    /**
-     * Replies the label to be rendered for the primitive {@code osm}.
-     *
-     * @param osm the OSM object
-     * @return the label, or null, if {@code osm} is null or if no label can be
-     * derived for {@code osm}
-     */
-    public String getString(OsmPrimitive osm) {
-        if (labelCompositionStrategy == null) return null;
-        return labelCompositionStrategy.compose(osm);
-    }
-
-    @Override
-    public String toString() {
-        return "TextElement{" + toStringImpl() + '}';
-    }
-
-    protected String toStringImpl() {
-        StringBuilder sb = new StringBuilder(96);
-        sb.append("labelCompositionStrategy=").append(labelCompositionStrategy)
-          .append(" font=").append(font);
-        if (xOffset != 0) {
-            sb.append(" xOffset=").append(xOffset);
-        }
-        if (yOffset != 0) {
-            sb.append(" yOffset=").append(yOffset);
-        }
-        sb.append(" color=").append(Utils.toString(color));
-        if (haloRadius != null) {
-            sb.append(" haloRadius=").append(haloRadius)
-              .append(" haloColor=").append(haloColor);
-        }
-        return sb.toString();
-    }
-
-    @Override
-    public int hashCode() {
-        int hash = 3;
-        hash = 79 * hash + (labelCompositionStrategy != null ? labelCompositionStrategy.hashCode() : 0);
-        hash = 79 * hash + font.hashCode();
-        hash = 79 * hash + xOffset;
-        hash = 79 * hash + yOffset;
-        hash = 79 * hash + color.hashCode();
-        hash = 79 * hash + (haloRadius != null ? Float.floatToIntBits(haloRadius) : 0);
-        hash = 79 * hash + (haloColor != null ? haloColor.hashCode() : 0);
-        return hash;
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (obj == null || getClass() != obj.getClass())
-            return false;
-        final TextElement other = (TextElement) obj;
-        return Objects.equals(labelCompositionStrategy, other.labelCompositionStrategy) &&
-        Objects.equals(font, other.font) &&
-        xOffset == other.xOffset &&
-        yOffset == other.yOffset &&
-        Objects.equals(color, other.color) &&
-        Objects.equals(haloRadius, other.haloRadius) &&
-        Objects.equals(haloColor, other.haloColor);
-    }
-}
Index: /trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSStyleSource.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSStyleSource.java	(revision 9277)
+++ /trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSStyleSource.java	(revision 9278)
@@ -38,5 +38,4 @@
 import org.openstreetmap.josm.gui.mappaint.Cascade;
 import org.openstreetmap.josm.gui.mappaint.Environment;
-import org.openstreetmap.josm.gui.mappaint.LineElemStyle;
 import org.openstreetmap.josm.gui.mappaint.MultiCascade;
 import org.openstreetmap.josm.gui.mappaint.Range;
@@ -56,4 +55,5 @@
 import org.openstreetmap.josm.gui.mappaint.mapcss.parsergen.ParseException;
 import org.openstreetmap.josm.gui.mappaint.mapcss.parsergen.TokenMgrError;
+import org.openstreetmap.josm.gui.mappaint.styleelement.LineElement;
 import org.openstreetmap.josm.gui.preferences.SourceEntry;
 import org.openstreetmap.josm.io.CachedFile;
@@ -113,5 +113,5 @@
             }
         }
-        for (LineElemStyle.LineType lt : LineElemStyle.LineType.values()) {
+        for (LineElement.LineType lt : LineElement.LineType.values()) {
             SUPPORTED_KEYS.add(lt.prefix + StyleKeys.COLOR);
             SUPPORTED_KEYS.add(lt.prefix + StyleKeys.DASHES);
Index: /trunk/src/org/openstreetmap/josm/gui/mappaint/styleelement/AreaElement.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/mappaint/styleelement/AreaElement.java	(revision 9278)
+++ /trunk/src/org/openstreetmap/josm/gui/mappaint/styleelement/AreaElement.java	(revision 9278)
@@ -0,0 +1,154 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.mappaint.styleelement;
+
+import java.awt.Color;
+import java.util.Objects;
+
+import org.openstreetmap.josm.Main;
+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;
+import org.openstreetmap.josm.data.osm.visitor.paint.PaintColors;
+import org.openstreetmap.josm.data.osm.visitor.paint.StyledMapRenderer;
+import org.openstreetmap.josm.gui.mappaint.Cascade;
+import org.openstreetmap.josm.gui.mappaint.Environment;
+import org.openstreetmap.josm.gui.mappaint.Keyword;
+import org.openstreetmap.josm.gui.mappaint.MapPaintStyles.IconReference;
+import org.openstreetmap.josm.tools.CheckParameterUtil;
+import org.openstreetmap.josm.tools.Utils;
+
+public class AreaElement extends StyleElement {
+
+    /**
+     * If fillImage == null, color is the fill-color, otherwise
+     * an arbitrary color value sampled from the fillImage
+     */
+    public Color color;
+    public MapImage fillImage;
+    public TextLabel text;
+    public Float extent;
+    public Float extentThreshold;
+
+    protected AreaElement(Cascade c, Color color, MapImage fillImage, Float extent, Float extentThreshold, TextLabel text) {
+        super(c, 1f);
+        CheckParameterUtil.ensureParameterNotNull(color);
+        this.color = color;
+        this.fillImage = fillImage;
+        this.extent = extent;
+        this.extentThreshold = extentThreshold;
+        this.text = text;
+    }
+
+    public static AreaElement create(final Environment env) {
+        final Cascade c = env.mc.getCascade(env.layer);
+        MapImage fillImage = null;
+        Color color;
+
+        IconReference iconRef = c.get(FILL_IMAGE, null, IconReference.class);
+        if (iconRef != null) {
+            fillImage = new MapImage(iconRef.iconName, iconRef.source, false);
+
+            color = new Color(fillImage.getImage(false).getRGB(
+                    fillImage.getWidth() / 2, fillImage.getHeight() / 2)
+            );
+
+            fillImage.alpha = Math.min(255, Math.max(0, Integer.valueOf(Main.pref.getInteger("mappaint.fill-image-alpha", 255))));
+            Integer pAlpha = Utils.color_float2int(c.get(FILL_OPACITY, null, float.class));
+            if (pAlpha != null) {
+                fillImage.alpha = pAlpha;
+            }
+        } else {
+            color = c.get(FILL_COLOR, null, Color.class);
+            if (color != null) {
+                int alpha = color.getAlpha();
+                if (alpha == 255) {
+                    // Assume alpha value has not been specified by the user if
+                    // is set to fully opaque. Use default value in this case.
+                    // It is not an ideal solution, but a little tricky to get this
+                    // right, especially as named map colors can be changed in
+                    // the preference GUI and written to the preferences file.
+                    alpha = Math.min(255, Math.max(0, Integer.valueOf(Main.pref.getInteger("mappaint.fillalpha", 50))));
+                }
+                Integer pAlpha = Utils.color_float2int(c.get(FILL_OPACITY, null, float.class));
+                if (pAlpha != null) {
+                    alpha = pAlpha;
+                }
+                color = new Color(color.getRed(), color.getGreen(), color.getBlue(), alpha);
+            }
+        }
+
+        TextLabel text = null;
+        Keyword textPos = c.get(TEXT_POSITION, null, Keyword.class);
+        if (textPos == null || "center".equals(textPos.val)) {
+            text = TextLabel.create(env, PaintColors.AREA_TEXT.get(), true);
+        }
+
+        Float extent = c.get(FILL_EXTENT, null, float.class);
+        Float extentThreshold = c.get(FILL_EXTENT_THRESHOLD, null, float.class);
+
+        if (color != null)
+            return new AreaElement(c, color, fillImage, extent, extentThreshold, text);
+        else
+            return null;
+    }
+
+    @Override
+    public void paintPrimitive(OsmPrimitive osm, MapPaintSettings paintSettings, StyledMapRenderer painter,
+            boolean selected, boolean outermember, boolean member) {
+        Color myColor = color;
+        if (osm instanceof Way) {
+            if (color != null) {
+                if (selected) {
+                    myColor = paintSettings.getSelectedColor(color.getAlpha());
+                } else if (outermember) {
+                    myColor = paintSettings.getRelationSelectedColor(color.getAlpha());
+                }
+            }
+            painter.drawArea((Way) osm, myColor, fillImage, extent, extentThreshold, painter.isInactiveMode() || osm.isDisabled(), text);
+        } else if (osm instanceof Relation) {
+            if (color != null && (selected || outermember)) {
+                myColor = paintSettings.getRelationSelectedColor(color.getAlpha());
+            }
+            painter.drawArea((Relation) osm, myColor, fillImage, extent, extentThreshold, painter.isInactiveMode() || osm.isDisabled(), text);
+        }
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null || getClass() != obj.getClass())
+            return false;
+        if (!super.equals(obj))
+            return false;
+        AreaElement other = (AreaElement) obj;
+        // we should get the same image object due to caching
+        if (!Objects.equals(fillImage, other.fillImage))
+            return false;
+        if (!Objects.equals(color, other.color))
+            return false;
+        if (extent != other.extent)
+            return false;
+        if (extentThreshold != other.extentThreshold)
+            return false;
+        if (!Objects.equals(text, other.text))
+            return false;
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 3;
+        hash = 61 * hash + color.hashCode();
+        hash = 61 * hash + (extent != null ? Float.floatToIntBits(extent) : 0);
+        hash = 61 * hash + (extentThreshold != null ? Float.floatToIntBits(extent) : 0);
+        hash = 61 * hash + (fillImage != null ? fillImage.hashCode() : 0);
+        hash = 61 * hash + (text != null ? text.hashCode() : 0);
+        return hash;
+    }
+
+    @Override
+    public String toString() {
+        return "AreaElemStyle{" + super.toString() + "color=" + Utils.toString(color) +
+                " fillImage=[" + fillImage + "]}";
+    }
+}
Index: /trunk/src/org/openstreetmap/josm/gui/mappaint/styleelement/BoxTextElement.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/mappaint/styleelement/BoxTextElement.java	(revision 9278)
+++ /trunk/src/org/openstreetmap/josm/gui/mappaint/styleelement/BoxTextElement.java	(revision 9278)
@@ -0,0 +1,246 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.mappaint.styleelement;
+
+import java.awt.Color;
+import java.awt.Rectangle;
+
+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.PaintColors;
+import org.openstreetmap.josm.data.osm.visitor.paint.StyledMapRenderer;
+import org.openstreetmap.josm.gui.mappaint.Cascade;
+import org.openstreetmap.josm.gui.mappaint.Environment;
+import org.openstreetmap.josm.gui.mappaint.Keyword;
+import org.openstreetmap.josm.gui.mappaint.MultiCascade;
+import org.openstreetmap.josm.tools.CheckParameterUtil;
+
+/**
+ * Text style attached to a style with a bounding box, like an icon or a symbol.
+ */
+public class BoxTextElement extends StyleElement {
+
+    public enum HorizontalTextAlignment { LEFT, CENTER, RIGHT }
+
+    public enum VerticalTextAlignment { ABOVE, TOP, CENTER, BOTTOM, BELOW }
+
+    public interface BoxProvider {
+        BoxProviderResult get();
+    }
+
+    public static class BoxProviderResult {
+        private final Rectangle box;
+        private final boolean temporary;
+
+        public BoxProviderResult(Rectangle box, boolean temporary) {
+            this.box = box;
+            this.temporary = temporary;
+        }
+
+        /**
+         * Returns the box.
+         * @return the box
+         */
+        public Rectangle getBox() {
+            return box;
+        }
+
+        /**
+         * Determines if the box can change in future calls of the {@link BoxProvider#get()} method
+         * @return {@code true} if the box can change in future calls of the {@code BoxProvider#get()} method
+         */
+        public boolean isTemporary() {
+            return temporary;
+        }
+    }
+
+    public static class SimpleBoxProvider implements BoxProvider {
+        private final Rectangle box;
+
+        /**
+         * Constructs a new {@code SimpleBoxProvider}.
+         * @param box the box
+         */
+        public SimpleBoxProvider(Rectangle box) {
+            this.box = box;
+        }
+
+        @Override
+        public BoxProviderResult get() {
+            return new BoxProviderResult(box, false);
+        }
+
+        @Override
+        public int hashCode() {
+            return box.hashCode();
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (!(obj instanceof BoxProvider))
+                return false;
+            final BoxProvider other = (BoxProvider) obj;
+            BoxProviderResult resultOther = other.get();
+            if (resultOther.isTemporary()) return false;
+            return box.equals(resultOther.getBox());
+        }
+    }
+
+    public static final Rectangle ZERO_BOX = new Rectangle(0, 0, 0, 0);
+
+    public TextLabel text;
+    // Either boxProvider or box is not null. If boxProvider is different from
+    // null, this means, that the box can still change in future, otherwise
+    // it is fixed.
+    protected BoxProvider boxProvider;
+    protected Rectangle box;
+    public HorizontalTextAlignment hAlign;
+    public VerticalTextAlignment vAlign;
+
+    public BoxTextElement(Cascade c, TextLabel text, BoxProvider boxProvider, Rectangle box,
+            HorizontalTextAlignment hAlign, VerticalTextAlignment vAlign) {
+        super(c, 5f);
+        CheckParameterUtil.ensureParameterNotNull(text);
+        CheckParameterUtil.ensureParameterNotNull(hAlign);
+        CheckParameterUtil.ensureParameterNotNull(vAlign);
+        this.text = text;
+        this.boxProvider = boxProvider;
+        this.box = box == null ? ZERO_BOX : box;
+        this.hAlign = hAlign;
+        this.vAlign = vAlign;
+    }
+
+    public static BoxTextElement create(Environment env, BoxProvider boxProvider) {
+        return create(env, boxProvider, null);
+    }
+
+    public static BoxTextElement create(Environment env, Rectangle box) {
+        return create(env, null, box);
+    }
+
+    public static BoxTextElement create(Environment env, BoxProvider boxProvider, Rectangle box) {
+        initDefaultParameters();
+        Cascade c = env.mc.getCascade(env.layer);
+
+        TextLabel text = TextLabel.create(env, DEFAULT_TEXT_COLOR, false);
+        if (text == null) return null;
+        // Skip any primitives that don't have text to draw. (Styles are recreated for any tag change.)
+        // The concrete text to render is not cached in this object, but computed for each
+        // repaint. This way, one BoxTextElement object can be used by multiple primitives (to save memory).
+        if (text.labelCompositionStrategy.compose(env.osm) == null) return null;
+
+        HorizontalTextAlignment hAlign = HorizontalTextAlignment.RIGHT;
+        Keyword hAlignKW = c.get(TEXT_ANCHOR_HORIZONTAL, Keyword.RIGHT, Keyword.class);
+        switch (hAlignKW.val) {
+            case "left":
+                hAlign = HorizontalTextAlignment.LEFT;
+                break;
+            case "center":
+                hAlign = HorizontalTextAlignment.CENTER;
+        }
+        VerticalTextAlignment vAlign = VerticalTextAlignment.BOTTOM;
+        Keyword vAlignKW = c.get(TEXT_ANCHOR_VERTICAL, Keyword.BOTTOM, Keyword.class);
+        switch (vAlignKW.val) {
+            case "bottom":
+                vAlign = VerticalTextAlignment.BOTTOM;
+                break;
+            case "above":
+                vAlign = VerticalTextAlignment.ABOVE;
+                break;
+            case "top":
+                vAlign = VerticalTextAlignment.TOP;
+                break;
+            case "center":
+                vAlign = VerticalTextAlignment.CENTER;
+                break;
+            case "below":
+                vAlign = VerticalTextAlignment.BELOW;
+        }
+
+        return new BoxTextElement(c, text, boxProvider, box, hAlign, vAlign);
+    }
+
+    public Rectangle getBox() {
+        if (boxProvider != null) {
+            BoxProviderResult result = boxProvider.get();
+            if (!result.isTemporary()) {
+                box = result.getBox();
+                boxProvider = null;
+            }
+            return result.getBox();
+        }
+        return box;
+    }
+
+    public static final BoxTextElement SIMPLE_NODE_TEXT_ELEMSTYLE;
+    static {
+        MultiCascade mc = new MultiCascade();
+        Cascade c = mc.getOrCreateCascade("default");
+        c.put(TEXT, Keyword.AUTO);
+        Node n = new Node();
+        n.put("name", "dummy");
+        SIMPLE_NODE_TEXT_ELEMSTYLE = create(new Environment(n, mc, "default", null), NodeElement.SIMPLE_NODE_ELEMSTYLE.getBoxProvider());
+        if (SIMPLE_NODE_TEXT_ELEMSTYLE == null) throw new AssertionError();
+    }
+
+    /*
+     * Caches the default text color from the preferences.
+     *
+     * FIXME: the cache isn't updated if the user changes the preference during a JOSM
+     * session. There should be preference listener updating this cache.
+     */
+    private static volatile Color DEFAULT_TEXT_COLOR;
+
+    private static void initDefaultParameters() {
+        if (DEFAULT_TEXT_COLOR != null) return;
+        DEFAULT_TEXT_COLOR = PaintColors.TEXT.get();
+    }
+
+    @Override
+    public void paintPrimitive(OsmPrimitive osm, MapPaintSettings settings, StyledMapRenderer painter,
+            boolean selected, boolean outermember, boolean member) {
+        if (osm instanceof Node) {
+            painter.drawBoxText((Node) osm, this);
+        }
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!super.equals(obj))
+            return false;
+        if (obj == null || getClass() != obj.getClass())
+            return false;
+        final BoxTextElement other = (BoxTextElement) obj;
+        if (!text.equals(other.text)) return false;
+        if (boxProvider != null) {
+            if (!boxProvider.equals(other.boxProvider)) return false;
+        } else if (other.boxProvider != null)
+            return false;
+        else {
+            if (!box.equals(other.box)) return false;
+        }
+        if (hAlign != other.hAlign) return false;
+        if (vAlign != other.vAlign) return false;
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = super.hashCode();
+        hash = 97 * hash + text.hashCode();
+        if (boxProvider != null) {
+            hash = 97 * hash + boxProvider.hashCode();
+        } else {
+            hash = 97 * hash + box.hashCode();
+        }
+        hash = 97 * hash + hAlign.hashCode();
+        hash = 97 * hash + vAlign.hashCode();
+        return hash;
+    }
+
+    @Override
+    public String toString() {
+        return "BoxTextElemStyle{" + super.toString() + ' ' + text.toStringImpl()
+                + " box=" + box + " hAlign=" + hAlign + " vAlign=" + vAlign + '}';
+    }
+}
Index: /trunk/src/org/openstreetmap/josm/gui/mappaint/styleelement/LabelCompositionStrategy.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/mappaint/styleelement/LabelCompositionStrategy.java	(revision 9278)
+++ /trunk/src/org/openstreetmap/josm/gui/mappaint/styleelement/LabelCompositionStrategy.java	(revision 9278)
@@ -0,0 +1,304 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.mappaint.styleelement;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.Preferences.PreferenceChangeEvent;
+import org.openstreetmap.josm.data.Preferences.PreferenceChangedListener;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.tools.LanguageInfo;
+
+/**
+ * <p>Provides an abstract parent class and three concrete sub classes for various
+ * strategies on how to compose the text label which can be rendered close to a node
+ * or within an area in an OSM map.</p>
+ *
+ * <p>The three strategies below support three rules for composing a label:
+ * <ul>
+ *   <li>{@link StaticLabelCompositionStrategy} - the label is given by a static text
+ *   specified in the MapCSS style file</li>
+ *
+ *   <li>{@link TagLookupCompositionStrategy} - the label is given by the content of a
+ *   tag whose name specified in the MapCSS style file</li>
+ *
+ *   <li>{@link DeriveLabelFromNameTagsCompositionStrategy} - the label is given by the value
+ *   of one
+ *   of the configured "name tags". The list of relevant name tags can be configured
+ *   in the JOSM preferences
+ *   content of a tag whose name specified in the MapCSS style file, see the preference
+ *   options <tt>mappaint.nameOrder</tt> and <tt>mappaint.nameComplementOrder</tt>.</li>
+ * </ul>
+ *
+ */
+public abstract class LabelCompositionStrategy {
+
+    /**
+     * Replies the text value to be rendered as label for the primitive {@code primitive}.
+     *
+     * @param primitive the primitive
+     *
+     * @return the text value to be rendered or null, if primitive is null or
+     * if no suitable value could be composed
+     */
+    public abstract String compose(OsmPrimitive primitive);
+
+    public static class StaticLabelCompositionStrategy extends LabelCompositionStrategy {
+        private final String defaultLabel;
+
+        public StaticLabelCompositionStrategy(String defaultLabel) {
+            this.defaultLabel = defaultLabel;
+        }
+
+        @Override
+        public String compose(OsmPrimitive primitive) {
+            return defaultLabel;
+        }
+
+        public String getDefaultLabel() {
+            return defaultLabel;
+        }
+
+        @Override
+        public String toString() {
+            return '{' + getClass().getSimpleName() + " defaultLabel=" + defaultLabel + '}';
+        }
+
+        @Override
+        public int hashCode() {
+            final int prime = 31;
+            int result = 1;
+            result = prime * result + ((defaultLabel == null) ? 0 : defaultLabel.hashCode());
+            return result;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj)
+                return true;
+            if (obj == null)
+                return false;
+            if (getClass() != obj.getClass())
+                return false;
+            StaticLabelCompositionStrategy other = (StaticLabelCompositionStrategy) obj;
+            if (defaultLabel == null) {
+                if (other.defaultLabel != null)
+                    return false;
+            } else if (!defaultLabel.equals(other.defaultLabel))
+                return false;
+            return true;
+        }
+    }
+
+    public static class TagLookupCompositionStrategy extends LabelCompositionStrategy {
+
+        private final String defaultLabelTag;
+
+        public TagLookupCompositionStrategy(String defaultLabelTag) {
+            if (defaultLabelTag != null) {
+                defaultLabelTag = defaultLabelTag.trim();
+                if (defaultLabelTag.isEmpty()) {
+                    defaultLabelTag = null;
+                }
+            }
+            this.defaultLabelTag = defaultLabelTag;
+        }
+
+        @Override
+        public String compose(OsmPrimitive primitive) {
+            if (defaultLabelTag == null) return null;
+            if (primitive == null) return null;
+            return primitive.get(defaultLabelTag);
+        }
+
+        public String getDefaultLabelTag() {
+            return defaultLabelTag;
+        }
+
+        @Override
+        public String toString() {
+            return '{' + getClass().getSimpleName() + " defaultLabelTag=" + defaultLabelTag + '}';
+        }
+
+        @Override
+        public int hashCode() {
+            final int prime = 31;
+            int result = 1;
+            result = prime * result + ((defaultLabelTag == null) ? 0 : defaultLabelTag.hashCode());
+            return result;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj)
+                return true;
+            if (obj == null)
+                return false;
+            if (getClass() != obj.getClass())
+                return false;
+            TagLookupCompositionStrategy other = (TagLookupCompositionStrategy) obj;
+            if (defaultLabelTag == null) {
+                if (other.defaultLabelTag != null)
+                    return false;
+            } else if (!defaultLabelTag.equals(other.defaultLabelTag))
+                return false;
+            return true;
+        }
+    }
+
+    public static class DeriveLabelFromNameTagsCompositionStrategy
+        extends LabelCompositionStrategy implements PreferenceChangedListener {
+
+        /**
+         * The list of default name tags from which a label candidate is derived.
+         */
+        private static final String[] DEFAULT_NAME_TAGS = {
+            "name:" + LanguageInfo.getJOSMLocaleCode(),
+            "name",
+            "int_name",
+            "distance",
+            "ref",
+            "operator",
+            "brand",
+            "addr:housenumber"
+        };
+
+        /**
+         * The list of default name complement tags from which a label candidate is derived.
+         */
+        private static final String[] DEFAULT_NAME_COMPLEMENT_TAGS = {
+            "capacity"
+        };
+
+        private List<String> nameTags = new ArrayList<>();
+        private List<String> nameComplementTags = new ArrayList<>();
+
+        /**
+         * <p>Creates the strategy and initializes its name tags from the preferences.</p>
+         */
+        public DeriveLabelFromNameTagsCompositionStrategy() {
+            initNameTagsFromPreferences();
+        }
+
+        private static List<String> buildNameTags(List<String> nameTags) {
+            if (nameTags == null) {
+                nameTags = Collections.emptyList();
+            }
+            List<String> result = new ArrayList<>();
+            for (String tag: nameTags) {
+                if (tag == null) {
+                    continue;
+                }
+                tag = tag.trim();
+                if (tag.isEmpty()) {
+                    continue;
+                }
+                result.add(tag);
+            }
+            return result;
+        }
+
+        /**
+         * Sets the name tags to be looked up in order to build up the label.
+         *
+         * @param nameTags the name tags. null values are ignored.
+         */
+        public void setNameTags(List<String> nameTags) {
+            this.nameTags = buildNameTags(nameTags);
+        }
+
+        /**
+         * Sets the name complement tags to be looked up in order to build up the label.
+         *
+         * @param nameComplementTags the name complement tags. null values are ignored.
+         * @since 6541
+         */
+        public void setNameComplementTags(List<String> nameComplementTags) {
+            this.nameComplementTags = buildNameTags(nameComplementTags);
+        }
+
+        /**
+         * Replies an unmodifiable list of the name tags used to compose the label.
+         *
+         * @return the list of name tags
+         */
+        public List<String> getNameTags() {
+            return Collections.unmodifiableList(nameTags);
+        }
+
+        /**
+         * Replies an unmodifiable list of the name complement tags used to compose the label.
+         *
+         * @return the list of name complement tags
+         * @since 6541
+         */
+        public List<String> getNameComplementTags() {
+            return Collections.unmodifiableList(nameComplementTags);
+        }
+
+        /**
+         * Initializes the name tags to use from a list of default name tags (see
+         * {@link #DEFAULT_NAME_TAGS} and {@link #DEFAULT_NAME_COMPLEMENT_TAGS})
+         * and from name tags configured in the preferences using the keys
+         * <tt>mappaint.nameOrder</tt> and <tt>mappaint.nameComplementOrder</tt>.
+         */
+        public final void initNameTagsFromPreferences() {
+            if (Main.pref == null) {
+                this.nameTags = new ArrayList<>(Arrays.asList(DEFAULT_NAME_TAGS));
+                this.nameComplementTags = new ArrayList<>(Arrays.asList(DEFAULT_NAME_COMPLEMENT_TAGS));
+            } else {
+                this.nameTags = new ArrayList<>(
+                        Main.pref.getCollection("mappaint.nameOrder", Arrays.asList(DEFAULT_NAME_TAGS))
+                );
+                this.nameComplementTags = new ArrayList<>(
+                        Main.pref.getCollection("mappaint.nameComplementOrder", Arrays.asList(DEFAULT_NAME_COMPLEMENT_TAGS))
+                );
+            }
+        }
+
+        private String getPrimitiveName(OsmPrimitive n) {
+            StringBuilder name = new StringBuilder();
+            if (!n.hasKeys()) return null;
+            for (String rn : nameTags) {
+                String val = n.get(rn);
+                if (val != null) {
+                    name.append(val);
+                    break;
+                }
+            }
+            for (String rn : nameComplementTags) {
+                String comp = n.get(rn);
+                if (comp != null) {
+                    if (name.length() == 0) {
+                        name.append(comp);
+                    } else {
+                        name.append(" (").append(comp).append(')');
+                    }
+                    break;
+                }
+            }
+            return name.toString();
+        }
+
+        @Override
+        public String compose(OsmPrimitive primitive) {
+            if (primitive == null) return null;
+            return getPrimitiveName(primitive);
+        }
+
+        @Override
+        public String toString() {
+            return "{" + getClass().getSimpleName() +'}';
+        }
+
+        @Override
+        public void preferenceChanged(PreferenceChangeEvent e) {
+            if (e.getKey() != null && e.getKey().startsWith("mappaint.name")) {
+                initNameTagsFromPreferences();
+            }
+        }
+    }
+}
Index: /trunk/src/org/openstreetmap/josm/gui/mappaint/styleelement/LineElement.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/mappaint/styleelement/LineElement.java	(revision 9278)
+++ /trunk/src/org/openstreetmap/josm/gui/mappaint/styleelement/LineElement.java	(revision 9278)
@@ -0,0 +1,389 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.mappaint.styleelement;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.util.Arrays;
+import java.util.Objects;
+
+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.Way;
+import org.openstreetmap.josm.data.osm.visitor.paint.MapPaintSettings;
+import org.openstreetmap.josm.data.osm.visitor.paint.PaintColors;
+import org.openstreetmap.josm.data.osm.visitor.paint.StyledMapRenderer;
+import org.openstreetmap.josm.gui.mappaint.Cascade;
+import org.openstreetmap.josm.gui.mappaint.Environment;
+import org.openstreetmap.josm.gui.mappaint.Keyword;
+import org.openstreetmap.josm.gui.mappaint.MultiCascade;
+import org.openstreetmap.josm.gui.mappaint.mapcss.Instruction.RelativeFloat;
+import org.openstreetmap.josm.tools.Utils;
+
+public class LineElement extends StyleElement {
+
+    public static LineElement createSimpleLineStyle(Color color, boolean isAreaEdge) {
+        MultiCascade mc = new MultiCascade();
+        Cascade c = mc.getOrCreateCascade("default");
+        c.put(WIDTH, Keyword.DEFAULT);
+        c.put(COLOR, color != null ? color : PaintColors.UNTAGGED.get());
+        c.put(OPACITY, 1f);
+        if (isAreaEdge) {
+            c.put(Z_INDEX, -3f);
+        }
+        return createLine(new Environment(null, mc, "default", null));
+    }
+
+    public static final LineElement UNTAGGED_WAY = createSimpleLineStyle(null, false);
+
+    private BasicStroke line;
+    public Color color;
+    public Color dashesBackground;
+    public float offset;
+    public float realWidth; // the real width of this line in meter
+
+    private BasicStroke dashesLine;
+
+    public enum LineType {
+        NORMAL("", 3f),
+        CASING("casing-", 2f),
+        LEFT_CASING("left-casing-", 2.1f),
+        RIGHT_CASING("right-casing-", 2.1f);
+
+        public final String prefix;
+        public final float defaultMajorZIndex;
+
+        LineType(String prefix, float default_major_z_index) {
+            this.prefix = prefix;
+            this.defaultMajorZIndex = default_major_z_index;
+        }
+    }
+
+    protected LineElement(Cascade c, float default_major_z_index, BasicStroke line, Color color, BasicStroke dashesLine,
+            Color dashesBackground, float offset, float realWidth) {
+        super(c, default_major_z_index);
+        this.line = line;
+        this.color = color;
+        this.dashesLine = dashesLine;
+        this.dashesBackground = dashesBackground;
+        this.offset = offset;
+        this.realWidth = realWidth;
+    }
+
+    public static LineElement createLine(Environment env) {
+        return createImpl(env, LineType.NORMAL);
+    }
+
+    public static LineElement createLeftCasing(Environment env) {
+        LineElement leftCasing = createImpl(env, LineType.LEFT_CASING);
+        if (leftCasing != null) {
+            leftCasing.isModifier = true;
+        }
+        return leftCasing;
+    }
+
+    public static LineElement createRightCasing(Environment env) {
+        LineElement rightCasing = createImpl(env, LineType.RIGHT_CASING);
+        if (rightCasing != null) {
+            rightCasing.isModifier = true;
+        }
+        return rightCasing;
+    }
+
+    public static LineElement createCasing(Environment env) {
+        LineElement casing = createImpl(env, LineType.CASING);
+        if (casing != null) {
+            casing.isModifier = true;
+        }
+        return casing;
+    }
+
+    private static LineElement createImpl(Environment env, LineType type) {
+        Cascade c = env.mc.getCascade(env.layer);
+        Cascade c_def = env.mc.getCascade("default");
+        Float width;
+        switch (type) {
+            case NORMAL:
+                width = getWidth(c, WIDTH, getWidth(c_def, WIDTH, null));
+                break;
+            case CASING:
+                Float casingWidth = c.get(type.prefix + WIDTH, null, Float.class, true);
+                if (casingWidth == null) {
+                    RelativeFloat rel_casingWidth = c.get(type.prefix + WIDTH, null, RelativeFloat.class, true);
+                    if (rel_casingWidth != null) {
+                        casingWidth = rel_casingWidth.val / 2;
+                    }
+                }
+                if (casingWidth == null)
+                    return null;
+                width = getWidth(c, WIDTH, getWidth(c_def, WIDTH, null));
+                if (width == null) {
+                    width = 0f;
+                }
+                width += 2 * casingWidth;
+                break;
+            case LEFT_CASING:
+            case RIGHT_CASING:
+                width = getWidth(c, type.prefix + WIDTH, null);
+                break;
+            default:
+                throw new AssertionError();
+        }
+        if (width == null)
+            return null;
+
+        float realWidth = c.get(type.prefix + REAL_WIDTH, 0f, Float.class);
+        if (realWidth > 0 && MapPaintSettings.INSTANCE.isUseRealWidth()) {
+
+            /* if we have a "width" tag, try use it */
+            String widthTag = env.osm.get("width");
+            if (widthTag == null) {
+                widthTag = env.osm.get("est_width");
+            }
+            if (widthTag != null) {
+                try {
+                    realWidth = Float.parseFloat(widthTag);
+                } catch (NumberFormatException nfe) {
+                    Main.warn(nfe);
+                }
+            }
+        }
+
+        Float offset = c.get(OFFSET, 0f, Float.class);
+        switch (type) {
+            case NORMAL:
+                break;
+            case CASING:
+                offset += c.get(type.prefix + OFFSET, 0f, Float.class);
+                break;
+            case LEFT_CASING:
+            case RIGHT_CASING:
+                Float baseWidthOnDefault = getWidth(c_def, WIDTH, null);
+                Float baseWidth = getWidth(c, WIDTH, baseWidthOnDefault);
+                if (baseWidth == null || baseWidth < 2f) {
+                    baseWidth = 2f;
+                }
+                float casingOffset = c.get(type.prefix + OFFSET, 0f, Float.class);
+                casingOffset += baseWidth / 2 + width / 2;
+                /* flip sign for the right-casing-offset */
+                if (type == LineType.RIGHT_CASING) {
+                    casingOffset *= -1f;
+                }
+                offset += casingOffset;
+                break;
+        }
+
+        int alpha = 255;
+        Color color = c.get(type.prefix + COLOR, null, Color.class);
+        if (color != null) {
+            alpha = color.getAlpha();
+        }
+        if (type == LineType.NORMAL && color == null) {
+            color = c.get(FILL_COLOR, null, Color.class);
+        }
+        if (color == null) {
+            color = PaintColors.UNTAGGED.get();
+        }
+
+        Integer pAlpha = Utils.color_float2int(c.get(type.prefix + OPACITY, null, Float.class));
+        if (pAlpha != null) {
+            alpha = pAlpha;
+        }
+        color = new Color(color.getRed(), color.getGreen(), color.getBlue(), alpha);
+
+        float[] dashes = c.get(type.prefix + DASHES, null, float[].class, true);
+        if (dashes != null) {
+            boolean hasPositive = false;
+            for (float f : dashes) {
+                if (f > 0) {
+                    hasPositive = true;
+                }
+                if (f < 0) {
+                    dashes = null;
+                    break;
+                }
+            }
+            if (!hasPositive || (dashes != null && dashes.length == 0)) {
+                dashes = null;
+            }
+        }
+        float dashesOffset = c.get(type.prefix + DASHES_OFFSET, 0f, Float.class);
+        Color dashesBackground = c.get(type.prefix + DASHES_BACKGROUND_COLOR, null, Color.class);
+        if (dashesBackground != null) {
+            pAlpha = Utils.color_float2int(c.get(type.prefix + DASHES_BACKGROUND_OPACITY, null, Float.class));
+            if (pAlpha != null) {
+                alpha = pAlpha;
+            }
+            dashesBackground = new Color(dashesBackground.getRed(), dashesBackground.getGreen(),
+                    dashesBackground.getBlue(), alpha);
+        }
+
+        Integer cap = null;
+        Keyword capKW = c.get(type.prefix + LINECAP, null, Keyword.class);
+        if (capKW != null) {
+            if ("none".equals(capKW.val)) {
+                cap = BasicStroke.CAP_BUTT;
+            } else if ("round".equals(capKW.val)) {
+                cap = BasicStroke.CAP_ROUND;
+            } else if ("square".equals(capKW.val)) {
+                cap = BasicStroke.CAP_SQUARE;
+            }
+        }
+        if (cap == null) {
+            cap = dashes != null ? BasicStroke.CAP_BUTT : BasicStroke.CAP_ROUND;
+        }
+
+        Integer join = null;
+        Keyword joinKW = c.get(type.prefix + LINEJOIN, null, Keyword.class);
+        if (joinKW != null) {
+            if ("round".equals(joinKW.val)) {
+                join = BasicStroke.JOIN_ROUND;
+            } else if ("miter".equals(joinKW.val)) {
+                join = BasicStroke.JOIN_MITER;
+            } else if ("bevel".equals(joinKW.val)) {
+                join = BasicStroke.JOIN_BEVEL;
+            }
+        }
+        if (join == null) {
+            join = BasicStroke.JOIN_ROUND;
+        }
+
+        float miterlimit = c.get(type.prefix + MITERLIMIT, 10f, Float.class);
+        if (miterlimit < 1f) {
+            miterlimit = 10f;
+        }
+
+        BasicStroke line = new BasicStroke(width, cap, join, miterlimit, dashes, dashesOffset);
+        BasicStroke dashesLine = null;
+
+        if (dashes != null && dashesBackground != null) {
+            float[] dashes2 = new float[dashes.length];
+            System.arraycopy(dashes, 0, dashes2, 1, dashes.length - 1);
+            dashes2[0] = dashes[dashes.length-1];
+            dashesLine = new BasicStroke(width, cap, join, miterlimit, dashes2, dashes2[0] + dashesOffset);
+        }
+
+        return new LineElement(c, type.defaultMajorZIndex, line, color, dashesLine, dashesBackground, offset, realWidth);
+    }
+
+    @Override
+    public void paintPrimitive(OsmPrimitive primitive, MapPaintSettings paintSettings, StyledMapRenderer painter,
+            boolean selected, boolean outermember, boolean member) {
+        Way w = (Way) primitive;
+        /* show direction arrows, if draw.segment.relevant_directions_only is not set,
+        the way is tagged with a direction key
+        (even if the tag is negated as in oneway=false) or the way is selected */
+        boolean showOrientation = !isModifier && (selected || paintSettings.isShowDirectionArrow()) && !paintSettings.isUseRealWidth();
+        boolean showOneway = !isModifier && !selected &&
+                !paintSettings.isUseRealWidth() &&
+                paintSettings.isShowOnewayArrow() && w.hasDirectionKeys();
+        boolean onewayReversed = w.reversedDirection();
+        /* head only takes over control if the option is true,
+        the direction should be shown at all and not only because it's selected */
+        boolean showOnlyHeadArrowOnly = showOrientation && !selected && paintSettings.isShowHeadArrowOnly();
+        Node lastN;
+
+        Color myDashedColor = dashesBackground;
+        BasicStroke myLine = line, myDashLine = dashesLine;
+        if (realWidth > 0 && paintSettings.isUseRealWidth() && !showOrientation) {
+            float myWidth = (int) (100 /  (float) (painter.getCircum() / realWidth));
+            if (myWidth < line.getLineWidth()) {
+                myWidth = line.getLineWidth();
+            }
+            myLine = new BasicStroke(myWidth, line.getEndCap(), line.getLineJoin(),
+                    line.getMiterLimit(), line.getDashArray(), line.getDashPhase());
+            if (dashesLine != null) {
+                myDashLine = new BasicStroke(myWidth, dashesLine.getEndCap(), dashesLine.getLineJoin(),
+                        dashesLine.getMiterLimit(), dashesLine.getDashArray(), dashesLine.getDashPhase());
+            }
+        }
+
+        Color myColor = color;
+        if (selected) {
+            myColor = paintSettings.getSelectedColor(color.getAlpha());
+        } else if (member || outermember) {
+            myColor = paintSettings.getRelationSelectedColor(color.getAlpha());
+        } else if (w.isDisabled()) {
+            myColor = paintSettings.getInactiveColor();
+            myDashedColor = paintSettings.getInactiveColor();
+        }
+
+        painter.drawWay(w, myColor, myLine, myDashLine, myDashedColor, offset, showOrientation,
+                showOnlyHeadArrowOnly, showOneway, onewayReversed);
+
+        if (paintSettings.isShowOrderNumber() && !painter.isInactiveMode()) {
+            int orderNumber = 0;
+            lastN = null;
+            for (Node n : w.getNodes()) {
+                if (lastN != null) {
+                    orderNumber++;
+                    painter.drawOrderNumber(lastN, n, orderNumber, myColor);
+                }
+                lastN = n;
+            }
+        }
+    }
+
+    @Override
+    public boolean isProperLineStyle() {
+        return !isModifier;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null || getClass() != obj.getClass())
+            return false;
+        if (!super.equals(obj))
+            return false;
+        final LineElement other = (LineElement) obj;
+        return Objects.equals(line, other.line) &&
+            Objects.equals(color, other.color) &&
+            Objects.equals(dashesLine, other.dashesLine) &&
+            Objects.equals(dashesBackground, other.dashesBackground) &&
+            offset == other.offset &&
+            realWidth == other.realWidth;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = super.hashCode();
+        hash = 29 * hash + line.hashCode();
+        hash = 29 * hash + color.hashCode();
+        hash = 29 * hash + (dashesLine != null ? dashesLine.hashCode() : 0);
+        hash = 29 * hash + (dashesBackground != null ? dashesBackground.hashCode() : 0);
+        hash = 29 * hash + Float.floatToIntBits(offset);
+        hash = 29 * hash + Float.floatToIntBits(realWidth);
+        return hash;
+    }
+
+    @Override
+    public String toString() {
+        return "LineElemStyle{" + super.toString() + "width=" + line.getLineWidth() +
+            " realWidth=" + realWidth + " color=" + Utils.toString(color) +
+            " dashed=" + Arrays.toString(line.getDashArray()) +
+            (line.getDashPhase() == 0 ? "" : " dashesOffses=" + line.getDashPhase()) +
+            " dashedColor=" + Utils.toString(dashesBackground) +
+            " linejoin=" + linejoinToString(line.getLineJoin()) +
+            " linecap=" + linecapToString(line.getEndCap()) +
+            (offset == 0 ? "" : " offset=" + offset) +
+            '}';
+    }
+
+    public String linejoinToString(int linejoin) {
+        switch (linejoin) {
+            case BasicStroke.JOIN_BEVEL: return "bevel";
+            case BasicStroke.JOIN_ROUND: return "round";
+            case BasicStroke.JOIN_MITER: return "miter";
+            default: return null;
+        }
+    }
+
+    public String linecapToString(int linecap) {
+        switch (linecap) {
+            case BasicStroke.CAP_BUTT: return "none";
+            case BasicStroke.CAP_ROUND: return "round";
+            case BasicStroke.CAP_SQUARE: return "square";
+            default: return null;
+        }
+    }
+}
Index: /trunk/src/org/openstreetmap/josm/gui/mappaint/styleelement/LineTextElement.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/mappaint/styleelement/LineTextElement.java	(revision 9278)
+++ /trunk/src/org/openstreetmap/josm/gui/mappaint/styleelement/LineTextElement.java	(revision 9278)
@@ -0,0 +1,63 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.mappaint.styleelement;
+
+import java.util.Objects;
+
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.data.osm.visitor.paint.MapPaintSettings;
+import org.openstreetmap.josm.data.osm.visitor.paint.PaintColors;
+import org.openstreetmap.josm.data.osm.visitor.paint.StyledMapRenderer;
+import org.openstreetmap.josm.gui.mappaint.Cascade;
+import org.openstreetmap.josm.gui.mappaint.Environment;
+import org.openstreetmap.josm.gui.mappaint.Keyword;
+
+public class LineTextElement extends StyleElement {
+
+    private final TextLabel text;
+
+    protected LineTextElement(Cascade c, TextLabel text) {
+        super(c, 4.9f);
+        this.text = text;
+    }
+
+    public static LineTextElement create(final Environment env) {
+        final Cascade c = env.mc.getCascade(env.layer);
+
+        Keyword textPos = c.get(TEXT_POSITION, null, Keyword.class);
+        if (textPos != null && !"line".equals(textPos.val))
+            return null;
+
+        TextLabel text = TextLabel.create(env, PaintColors.TEXT.get(), false);
+        if (text == null)
+            return null;
+        return new LineTextElement(c, text);
+    }
+
+    @Override
+    public void paintPrimitive(OsmPrimitive primitive, MapPaintSettings paintSettings, StyledMapRenderer painter,
+            boolean selected, boolean outermember, boolean member) {
+        Way w = (Way) primitive;
+        painter.drawTextOnPath(w, text);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null || getClass() != obj.getClass())
+            return false;
+        if (!super.equals(obj))
+            return false;
+        final LineTextElement other = (LineTextElement) obj;
+        return Objects.equals(text, other.text);
+    }
+
+    @Override
+    public int hashCode() {
+        return text.hashCode();
+    }
+
+    @Override
+    public String toString() {
+        return "LineTextElemStyle{" + super.toString() + "text=" + text + '}';
+    }
+}
Index: /trunk/src/org/openstreetmap/josm/gui/mappaint/styleelement/MapImage.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/mappaint/styleelement/MapImage.java	(revision 9278)
+++ /trunk/src/org/openstreetmap/josm/gui/mappaint/styleelement/MapImage.java	(revision 9278)
@@ -0,0 +1,244 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.mappaint.styleelement;
+
+import java.awt.Graphics;
+import java.awt.Image;
+import java.awt.Rectangle;
+import java.awt.image.BufferedImage;
+import java.util.Objects;
+
+import javax.swing.ImageIcon;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.gui.mappaint.MapPaintStyles;
+import org.openstreetmap.josm.gui.mappaint.StyleSource;
+import org.openstreetmap.josm.gui.mappaint.styleelement.BoxTextElement.BoxProvider;
+import org.openstreetmap.josm.gui.mappaint.styleelement.BoxTextElement.BoxProviderResult;
+import org.openstreetmap.josm.gui.util.GuiHelper;
+import org.openstreetmap.josm.tools.ImageProvider;
+import org.openstreetmap.josm.tools.ImageProvider.ImageCallback;
+import org.openstreetmap.josm.tools.Utils;
+
+/**
+ * An image that will be displayed on the map.
+ */
+public class MapImage {
+
+    private static final int MAX_SIZE = 48;
+
+    /**
+     * ImageIcon can change while the image is loading.
+     */
+    private BufferedImage img;
+
+    public int alpha = 255;
+    public String name;
+    public StyleSource source;
+    public boolean autoRescale;
+    public int width = -1;
+    public int height = -1;
+    public int offsetX;
+    public int offsetY;
+
+    private boolean temporary;
+    private BufferedImage disabledImgCache;
+
+    public MapImage(String name, StyleSource source) {
+        this(name, source, true);
+    }
+
+    public MapImage(String name, StyleSource source, boolean autoRescale) {
+        this.name = name;
+        this.source = source;
+        this.autoRescale = autoRescale;
+    }
+
+    /**
+     * Get the image associated with this MapImage object.
+     *
+     * @param disabled {@code} true to request disabled version, {@code false} for the standard version
+     * @return the image
+     */
+    public BufferedImage getImage(boolean disabled) {
+        if (disabled) {
+            return getDisabled();
+        } else {
+            return getImage();
+        }
+    }
+
+    private BufferedImage getDisabled() {
+        if (disabledImgCache != null)
+                return disabledImgCache;
+        if (img == null)
+            getImage(); // fix #7498 ?
+        Image disImg = GuiHelper.getDisabledImage(img);
+        if (disImg instanceof BufferedImage) {
+            disabledImgCache = (BufferedImage) disImg;
+        } else {
+            disabledImgCache = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
+            Graphics g = disabledImgCache.getGraphics();
+            g.drawImage(disImg, 0, 0, null);
+            g.dispose();
+        }
+        return disabledImgCache;
+    }
+
+    private BufferedImage getImage() {
+        if (img != null)
+            return img;
+        temporary = false;
+        new ImageProvider(name)
+                .setDirs(MapPaintStyles.getIconSourceDirs(source))
+                .setId("mappaint."+source.getPrefName())
+                .setArchive(source.zipIcons)
+                .setInArchiveDir(source.getZipEntryDirName())
+                .setWidth(width)
+                .setHeight(height)
+                .setOptional(true)
+                .getInBackground(new ImageCallback() {
+                    @Override
+                    public void finished(ImageIcon result) {
+                        synchronized (MapImage.this) {
+                            if (result == null) {
+                                ImageIcon noIcon = MapPaintStyles.getNoIcon_Icon(source);
+                                img = noIcon == null ? null : (BufferedImage) noIcon.getImage();
+                            } else {
+                                img = (BufferedImage) rescale(result.getImage());
+                            }
+                            if (temporary) {
+                                disabledImgCache = null;
+                                Main.map.mapView.preferenceChanged(null); // otherwise repaint is ignored, because layer hasn't changed
+                                Main.map.mapView.repaint();
+                            }
+                            temporary = false;
+                        }
+                    }
+                }
+        );
+        synchronized (this) {
+            if (img == null) {
+                img = (BufferedImage) ImageProvider.get("clock").getImage();
+                temporary = true;
+            }
+        }
+        return img;
+    }
+
+    public int getWidth() {
+        return getImage().getWidth(null);
+    }
+
+    public int getHeight() {
+        return getImage().getHeight(null);
+    }
+
+    public float getAlphaFloat() {
+        return Utils.color_int2float(alpha);
+    }
+
+    /**
+     * Determines if image is not completely loaded and {@code getImage()} returns a temporary image.
+     * @return {@code true} if image is not completely loaded and getImage() returns a temporary image
+     */
+    public boolean isTemporary() {
+        return temporary;
+    }
+
+    protected class MapImageBoxProvider implements BoxProvider {
+        @Override
+        public BoxProviderResult get() {
+            return new BoxProviderResult(box(), temporary);
+        }
+
+        private Rectangle box() {
+            int w = getWidth(), h = getHeight();
+            if (mustRescale(getImage())) {
+                w = 16;
+                h = 16;
+            }
+            return new Rectangle(-w/2, -h/2, w, h);
+        }
+
+        private MapImage getParent() {
+            return MapImage.this;
+        }
+
+        @Override
+        public int hashCode() {
+            return MapImage.this.hashCode();
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (!(obj instanceof BoxProvider))
+                return false;
+            if (obj instanceof MapImageBoxProvider) {
+                MapImageBoxProvider other = (MapImageBoxProvider) obj;
+                return MapImage.this.equals(other.getParent());
+            } else if (temporary) {
+                return false;
+            } else {
+                final BoxProvider other = (BoxProvider) obj;
+                BoxProviderResult resultOther = other.get();
+                if (resultOther.isTemporary()) return false;
+                return box().equals(resultOther.getBox());
+            }
+        }
+    }
+
+    public BoxProvider getBoxProvider() {
+        return new MapImageBoxProvider();
+    }
+
+    /**
+     * Rescale excessively large images.
+     * @param image the unscaled image
+     * @return The scaled down version to 16x16 pixels if the image height and width exceeds 48 pixels and no size has been explicitely specified
+     */
+    private Image rescale(Image image) {
+        if (image == null) return null;
+        // Scale down large (.svg) images to 16x16 pixels if no size is explicitely specified
+        if (mustRescale(image)) {
+            return ImageProvider.createBoundedImage(image, 16);
+        } else {
+            return image;
+        }
+    }
+
+    private boolean mustRescale(Image image) {
+        return autoRescale && width  == -1 && image.getWidth(null) > MAX_SIZE
+             && height == -1 && image.getHeight(null) > MAX_SIZE;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null || getClass() != obj.getClass())
+            return false;
+        final MapImage other = (MapImage) obj;
+        // img changes when image is fully loaded and can't be used for equality check.
+        return  alpha == other.alpha &&
+                Objects.equals(name, other.name) &&
+                Objects.equals(source, other.source) &&
+                autoRescale == other.autoRescale &&
+                width == other.width &&
+                height == other.height;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 7;
+        hash = 67 * hash + alpha;
+        hash = 67 * hash + name.hashCode();
+        hash = 67 * hash + source.hashCode();
+        hash = 67 * hash + (autoRescale ? 1 : 0);
+        hash = 67 * hash + width;
+        hash = 67 * hash + height;
+        return hash;
+    }
+
+    @Override
+    public String toString() {
+        return name;
+    }
+}
Index: /trunk/src/org/openstreetmap/josm/gui/mappaint/styleelement/NodeElement.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/mappaint/styleelement/NodeElement.java	(revision 9278)
+++ /trunk/src/org/openstreetmap/josm/gui/mappaint/styleelement/NodeElement.java	(revision 9278)
@@ -0,0 +1,407 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.mappaint.styleelement;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Rectangle;
+import java.awt.Stroke;
+import java.util.Objects;
+
+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.Relation;
+import org.openstreetmap.josm.data.osm.visitor.paint.MapPaintSettings;
+import org.openstreetmap.josm.data.osm.visitor.paint.StyledMapRenderer;
+import org.openstreetmap.josm.gui.mappaint.Cascade;
+import org.openstreetmap.josm.gui.mappaint.Environment;
+import org.openstreetmap.josm.gui.mappaint.Keyword;
+import org.openstreetmap.josm.gui.mappaint.MapPaintStyles.IconReference;
+import org.openstreetmap.josm.gui.mappaint.MultiCascade;
+import org.openstreetmap.josm.gui.mappaint.StyleCache.StyleList;
+import org.openstreetmap.josm.gui.mappaint.styleelement.BoxTextElement.BoxProvider;
+import org.openstreetmap.josm.gui.mappaint.styleelement.BoxTextElement.SimpleBoxProvider;
+import org.openstreetmap.josm.gui.util.RotationAngle;
+import org.openstreetmap.josm.tools.Utils;
+
+/**
+ * applies for Nodes and turn restriction relations
+ */
+public class NodeElement extends StyleElement {
+    public final MapImage mapImage;
+    public final RotationAngle mapImageAngle;
+    public final Symbol symbol;
+
+    public enum SymbolShape { SQUARE, CIRCLE, TRIANGLE, PENTAGON, HEXAGON, HEPTAGON, OCTAGON, NONAGON, DECAGON }
+
+    public static class Symbol {
+        public SymbolShape symbol;
+        public int size;
+        public Stroke stroke;
+        public Color strokeColor;
+        public Color fillColor;
+
+        public Symbol(SymbolShape symbol, int size, Stroke stroke, Color strokeColor, Color fillColor) {
+            if (stroke != null && strokeColor == null)
+                throw new IllegalArgumentException("Stroke given without color");
+            if (stroke == null && fillColor == null)
+                throw new IllegalArgumentException("Either a stroke or a fill color must be given");
+            this.symbol = symbol;
+            this.size = size;
+            this.stroke = stroke;
+            this.strokeColor = strokeColor;
+            this.fillColor = fillColor;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj == null || getClass() != obj.getClass())
+                return false;
+            final Symbol other = (Symbol) obj;
+            return  symbol == other.symbol &&
+                    size == other.size &&
+                    Objects.equals(stroke, other.stroke) &&
+                    Objects.equals(strokeColor, other.strokeColor) &&
+                    Objects.equals(fillColor, other.fillColor);
+        }
+
+        @Override
+        public int hashCode() {
+            int hash = 7;
+            hash = 67 * hash + symbol.hashCode();
+            hash = 67 * hash + size;
+            hash = 67 * hash + (stroke != null ? stroke.hashCode() : 0);
+            hash = 67 * hash + (strokeColor != null ? strokeColor.hashCode() : 0);
+            hash = 67 * hash + (fillColor != null ? fillColor.hashCode() : 0);
+            return hash;
+        }
+
+        @Override
+        public String toString() {
+            return "symbol=" + symbol + " size=" + size +
+                    (stroke != null ? " stroke=" + stroke + " strokeColor=" + strokeColor : "") +
+                    (fillColor != null ? " fillColor=" + fillColor : "");
+        }
+    }
+
+    public static final NodeElement SIMPLE_NODE_ELEMSTYLE;
+    public static final BoxProvider SIMPLE_NODE_ELEMSTYLE_BOXPROVIDER;
+    static {
+        MultiCascade mc = new MultiCascade();
+        mc.getOrCreateCascade("default");
+        SIMPLE_NODE_ELEMSTYLE = create(new Environment(null, mc, "default", null), 4.1f, true);
+        if (SIMPLE_NODE_ELEMSTYLE == null) throw new AssertionError();
+        SIMPLE_NODE_ELEMSTYLE_BOXPROVIDER = SIMPLE_NODE_ELEMSTYLE.getBoxProvider();
+    }
+
+    public static final StyleList DEFAULT_NODE_STYLELIST = new StyleList(NodeElement.SIMPLE_NODE_ELEMSTYLE);
+    public static final StyleList DEFAULT_NODE_STYLELIST_TEXT = new StyleList(NodeElement.SIMPLE_NODE_ELEMSTYLE,
+            BoxTextElement.SIMPLE_NODE_TEXT_ELEMSTYLE);
+
+    protected NodeElement(Cascade c, MapImage mapImage, Symbol symbol, float default_major_z_index, RotationAngle rotationAngle) {
+        super(c, default_major_z_index);
+        this.mapImage = mapImage;
+        this.symbol = symbol;
+        this.mapImageAngle = rotationAngle;
+    }
+
+    public static NodeElement create(Environment env) {
+        return create(env, 4f, false);
+    }
+
+    private static NodeElement create(Environment env, float default_major_z_index, boolean allowDefault) {
+        Cascade c = env.mc.getCascade(env.layer);
+
+        MapImage mapImage = createIcon(env, ICON_KEYS);
+        Symbol symbol = null;
+        if (mapImage == null) {
+            symbol = createSymbol(env);
+        }
+        RotationAngle rotationAngle = null;
+        final Float angle = c.get(ICON_ROTATION, null, Float.class, true);
+        if (angle != null) {
+            rotationAngle = RotationAngle.buildStaticRotation(angle);
+        } else {
+            final Keyword rotationKW = c.get(ICON_ROTATION, null, Keyword.class);
+            if (rotationKW != null) {
+                if ("way".equals(rotationKW.val)) {
+                    rotationAngle = RotationAngle.buildWayDirectionRotation();
+                } else {
+                    try {
+                        rotationAngle = RotationAngle.buildStaticRotation(rotationKW.val);
+                    } catch (IllegalArgumentException ignore) {
+                        if (Main.isTraceEnabled()) {
+                            Main.trace(ignore.getMessage());
+                        }
+                    }
+                }
+            }
+        }
+
+        // optimization: if we neither have a symbol, nor a mapImage
+        // we don't have to check for the remaining style properties and we don't
+        // have to allocate a node element style.
+        if (!allowDefault && symbol == null && mapImage == null) return null;
+
+        return new NodeElement(c, mapImage, symbol, default_major_z_index, rotationAngle);
+    }
+
+    public static MapImage createIcon(final Environment env, final String[] keys) {
+        Cascade c = env.mc.getCascade(env.layer);
+
+        final IconReference iconRef = c.get(keys[ICON_IMAGE_IDX], null, IconReference.class, true);
+        if (iconRef == null)
+            return null;
+
+        Cascade c_def = env.mc.getCascade("default");
+
+        Float widthOnDefault = c_def.get(keys[ICON_WIDTH_IDX], null, Float.class);
+        if (widthOnDefault != null && widthOnDefault <= 0) {
+            widthOnDefault = null;
+        }
+        Float widthF = getWidth(c, keys[ICON_WIDTH_IDX], widthOnDefault);
+
+        Float heightOnDefault = c_def.get(keys[ICON_HEIGHT_IDX], null, Float.class);
+        if (heightOnDefault != null && heightOnDefault <= 0) {
+            heightOnDefault = null;
+        }
+        Float heightF = getWidth(c, keys[ICON_HEIGHT_IDX], heightOnDefault);
+
+        int width = widthF == null ? -1 : Math.round(widthF);
+        int height = heightF == null ? -1 : Math.round(heightF);
+
+        float offsetXF = 0f;
+        float offsetYF = 0f;
+        if (keys[ICON_OFFSET_X_IDX] != null) {
+            offsetXF = c.get(keys[ICON_OFFSET_X_IDX], 0f, Float.class);
+            offsetYF = c.get(keys[ICON_OFFSET_Y_IDX], 0f, Float.class);
+        }
+
+        final MapImage mapImage = new MapImage(iconRef.iconName, iconRef.source);
+
+        mapImage.width = width;
+        mapImage.height = height;
+        mapImage.offsetX = Math.round(offsetXF);
+        mapImage.offsetY = Math.round(offsetYF);
+
+        mapImage.alpha = Math.min(255, Math.max(0, Integer.valueOf(Main.pref.getInteger("mappaint.icon-image-alpha", 255))));
+        Integer pAlpha = Utils.color_float2int(c.get(keys[ICON_OPACITY_IDX], null, float.class));
+        if (pAlpha != null) {
+            mapImage.alpha = pAlpha;
+        }
+        return mapImage;
+    }
+
+    private static Symbol createSymbol(Environment env) {
+        Cascade c = env.mc.getCascade(env.layer);
+        Cascade c_def = env.mc.getCascade("default");
+
+        SymbolShape shape;
+        Keyword shapeKW = c.get("symbol-shape", null, Keyword.class);
+        if (shapeKW == null)
+            return null;
+        if ("square".equals(shapeKW.val)) {
+            shape = SymbolShape.SQUARE;
+        } else if ("circle".equals(shapeKW.val)) {
+            shape = SymbolShape.CIRCLE;
+        } else if ("triangle".equals(shapeKW.val)) {
+            shape = SymbolShape.TRIANGLE;
+        } else if ("pentagon".equals(shapeKW.val)) {
+            shape = SymbolShape.PENTAGON;
+        } else if ("hexagon".equals(shapeKW.val)) {
+            shape = SymbolShape.HEXAGON;
+        } else if ("heptagon".equals(shapeKW.val)) {
+            shape = SymbolShape.HEPTAGON;
+        } else if ("octagon".equals(shapeKW.val)) {
+            shape = SymbolShape.OCTAGON;
+        } else if ("nonagon".equals(shapeKW.val)) {
+            shape = SymbolShape.NONAGON;
+        } else if ("decagon".equals(shapeKW.val)) {
+            shape = SymbolShape.DECAGON;
+        } else
+            return null;
+
+        Float sizeOnDefault = c_def.get("symbol-size", null, Float.class);
+        if (sizeOnDefault != null && sizeOnDefault <= 0) {
+            sizeOnDefault = null;
+        }
+        Float size = getWidth(c, "symbol-size", sizeOnDefault);
+
+        if (size == null) {
+            size = 10f;
+        }
+
+        if (size <= 0)
+            return null;
+
+        Float strokeWidthOnDefault = getWidth(c_def, "symbol-stroke-width", null);
+        Float strokeWidth = getWidth(c, "symbol-stroke-width", strokeWidthOnDefault);
+
+        Color strokeColor = c.get("symbol-stroke-color", null, Color.class);
+
+        if (strokeWidth == null && strokeColor != null) {
+            strokeWidth = 1f;
+        } else if (strokeWidth != null && strokeColor == null) {
+            strokeColor = Color.ORANGE;
+        }
+
+        Stroke stroke = null;
+        if (strokeColor != null) {
+            Integer strokeAlpha = Utils.color_float2int(c.get("symbol-stroke-opacity", null, Float.class));
+            if (strokeAlpha != null) {
+                strokeColor = new Color(strokeColor.getRed(), strokeColor.getGreen(),
+                        strokeColor.getBlue(), strokeAlpha);
+            }
+            stroke = new BasicStroke(strokeWidth);
+        }
+
+        Color fillColor = c.get("symbol-fill-color", null, Color.class);
+        if (stroke == null && fillColor == null) {
+            fillColor = Color.BLUE;
+        }
+
+        if (fillColor != null) {
+            Integer fillAlpha = Utils.color_float2int(c.get("symbol-fill-opacity", null, Float.class));
+            if (fillAlpha != null) {
+                fillColor = new Color(fillColor.getRed(), fillColor.getGreen(),
+                        fillColor.getBlue(), fillAlpha);
+            }
+        }
+
+        return new Symbol(shape, Math.round(size), stroke, strokeColor, fillColor);
+    }
+
+    @Override
+    public void paintPrimitive(OsmPrimitive primitive, MapPaintSettings settings, StyledMapRenderer painter,
+            boolean selected, boolean outermember, boolean member) {
+        if (primitive instanceof Node) {
+            Node n = (Node) primitive;
+            if (mapImage != null && painter.isShowIcons()) {
+                painter.drawNodeIcon(n, mapImage, painter.isInactiveMode() || n.isDisabled(), selected, member,
+                        mapImageAngle == null ? 0.0 : mapImageAngle.getRotationAngle(primitive));
+            } else if (symbol != null) {
+                Color fillColor = symbol.fillColor;
+                if (fillColor != null) {
+                    if (painter.isInactiveMode() || n.isDisabled()) {
+                        fillColor = settings.getInactiveColor();
+                    } else if (selected) {
+                        fillColor = settings.getSelectedColor(fillColor.getAlpha());
+                    } else if (member) {
+                        fillColor = settings.getRelationSelectedColor(fillColor.getAlpha());
+                    }
+                }
+                Color strokeColor = symbol.strokeColor;
+                if (strokeColor != null) {
+                    if (painter.isInactiveMode() || n.isDisabled()) {
+                        strokeColor = settings.getInactiveColor();
+                    } else if (selected) {
+                        strokeColor = settings.getSelectedColor(strokeColor.getAlpha());
+                    } else if (member) {
+                        strokeColor = settings.getRelationSelectedColor(strokeColor.getAlpha());
+                    }
+                }
+                painter.drawNodeSymbol(n, symbol, fillColor, strokeColor);
+            } else {
+                Color color;
+                boolean isConnection = n.isConnectionNode();
+
+                if (painter.isInactiveMode() || 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);
+
+            }
+        } else if (primitive instanceof Relation && mapImage != null) {
+            painter.drawRestriction((Relation) primitive, mapImage, painter.isInactiveMode() || primitive.isDisabled());
+        }
+    }
+
+    public BoxProvider getBoxProvider() {
+        if (mapImage != null)
+            return mapImage.getBoxProvider();
+        else if (symbol != null)
+            return new SimpleBoxProvider(new Rectangle(-symbol.size/2, -symbol.size/2, symbol.size, symbol.size));
+        else {
+            // This is only executed once, so no performance concerns.
+            // However, it would be better, if the settings could be changed at runtime.
+            int size = Utils.max(
+                    Main.pref.getInteger("mappaint.node.selected-size", 5),
+                    Main.pref.getInteger("mappaint.node.unselected-size", 3),
+                    Main.pref.getInteger("mappaint.node.connection-size", 5),
+                    Main.pref.getInteger("mappaint.node.tagged-size", 3)
+            );
+            return new SimpleBoxProvider(new Rectangle(-size/2, -size/2, size, size));
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = super.hashCode();
+        hash = 17 * hash + (mapImage != null ? mapImage.hashCode() : 0);
+        hash = 17 * hash + (symbol != null ? symbol.hashCode() : 0);
+        hash = 17 * hash + (mapImageAngle != null ? mapImageAngle.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 NodeElement other = (NodeElement) obj;
+        // we should get the same image object due to caching
+        if (!Objects.equals(mapImage, other.mapImage))
+            return false;
+        if (!Objects.equals(symbol, other.symbol))
+            return false;
+        if (!Objects.equals(mapImageAngle, other.mapImageAngle))
+            return false;
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder s = new StringBuilder("NodeElemStyle{");
+        s.append(super.toString());
+        if (mapImage != null) {
+            s.append(" icon=[" + mapImage + ']');
+        }
+        if (symbol != null) {
+            s.append(" symbol=[" + symbol + ']');
+        }
+        if (mapImageAngle != null) {
+            s.append(" mapImageAngle=[" + mapImageAngle + ']');
+        }
+        s.append('}');
+        return s.toString();
+    }
+}
Index: /trunk/src/org/openstreetmap/josm/gui/mappaint/styleelement/RepeatImageElement.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/mappaint/styleelement/RepeatImageElement.java	(revision 9278)
+++ /trunk/src/org/openstreetmap/josm/gui/mappaint/styleelement/RepeatImageElement.java	(revision 9278)
@@ -0,0 +1,98 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.mappaint.styleelement;
+
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.data.osm.visitor.paint.MapPaintSettings;
+import org.openstreetmap.josm.data.osm.visitor.paint.StyledMapRenderer;
+import org.openstreetmap.josm.gui.mappaint.Cascade;
+import org.openstreetmap.josm.gui.mappaint.Environment;
+import org.openstreetmap.josm.gui.mappaint.Keyword;
+import org.openstreetmap.josm.tools.CheckParameterUtil;
+
+public class RepeatImageElement extends StyleElement {
+
+    public enum LineImageAlignment { TOP, CENTER, BOTTOM }
+
+    public MapImage pattern;
+    public float offset;
+    public float spacing;
+    public float phase;
+    public LineImageAlignment align;
+
+    public RepeatImageElement(Cascade c, MapImage pattern, float offset, float spacing, float phase, LineImageAlignment align) {
+        super(c, 2.9f);
+        CheckParameterUtil.ensureParameterNotNull(pattern);
+        CheckParameterUtil.ensureParameterNotNull(align);
+        this.pattern = pattern;
+        this.offset = offset;
+        this.spacing = spacing;
+        this.phase = phase;
+        this.align = align;
+    }
+
+    public static RepeatImageElement create(Environment env) {
+        MapImage pattern = NodeElement.createIcon(env, REPEAT_IMAGE_KEYS);
+        if (pattern == null)
+            return null;
+        Cascade c = env.mc.getCascade(env.layer);
+        float offset = c.get(REPEAT_IMAGE_OFFSET, 0f, Float.class);
+        float spacing = c.get(REPEAT_IMAGE_SPACING, 0f, Float.class);
+        float phase = -c.get(REPEAT_IMAGE_PHASE, 0f, Float.class);
+
+        LineImageAlignment align = LineImageAlignment.CENTER;
+        Keyword alignKW = c.get(REPEAT_IMAGE_ALIGN, Keyword.CENTER, Keyword.class);
+        if ("top".equals(alignKW.val)) {
+            align = LineImageAlignment.TOP;
+        } else if ("bottom".equals(alignKW.val)) {
+            align = LineImageAlignment.BOTTOM;
+        }
+
+        return new RepeatImageElement(c, pattern, offset, spacing, phase, align);
+    }
+
+    @Override
+    public void paintPrimitive(OsmPrimitive primitive, MapPaintSettings paintSettings, StyledMapRenderer painter,
+            boolean selected, boolean outermember, boolean member) {
+        Way w = (Way) primitive;
+        painter.drawRepeatImage(w, pattern, painter.isInactiveMode() || w.isDisabled(), offset, spacing, phase, align);
+    }
+
+    @Override
+    public boolean isProperLineStyle() {
+        return true;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null || getClass() != obj.getClass())
+            return false;
+        if (!super.equals(obj))
+            return false;
+        final RepeatImageElement other = (RepeatImageElement) obj;
+        if (!this.pattern.equals(other.pattern)) return false;
+        if (this.offset != other.offset) return false;
+        if (this.spacing != other.spacing) return false;
+        if (this.phase != other.phase) return false;
+        if (this.align != other.align) return false;
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 7;
+        hash = 83 * hash + this.pattern.hashCode();
+        hash = 83 * hash + Float.floatToIntBits(this.offset);
+        hash = 83 * hash + Float.floatToIntBits(this.spacing);
+        hash = 83 * hash + Float.floatToIntBits(this.phase);
+        hash = 83 * hash + this.align.hashCode();
+        return hash;
+    }
+
+    @Override
+    public String toString() {
+        return "RepeatImageStyle{" + super.toString() + "pattern=[" + pattern +
+                "], offset=" + offset + ", spacing=" + spacing +
+                ", phase=" + (-phase) + ", align=" + align + '}';
+    }
+}
Index: /trunk/src/org/openstreetmap/josm/gui/mappaint/styleelement/StyleElement.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/mappaint/styleelement/StyleElement.java	(revision 9278)
+++ /trunk/src/org/openstreetmap/josm/gui/mappaint/styleelement/StyleElement.java	(revision 9278)
@@ -0,0 +1,236 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.mappaint.styleelement;
+
+import java.awt.Font;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.visitor.paint.MapPaintSettings;
+import org.openstreetmap.josm.data.osm.visitor.paint.StyledMapRenderer;
+import org.openstreetmap.josm.gui.mappaint.Cascade;
+import org.openstreetmap.josm.gui.mappaint.Keyword;
+import org.openstreetmap.josm.gui.mappaint.StyleKeys;
+import org.openstreetmap.josm.gui.mappaint.mapcss.Instruction.RelativeFloat;
+
+public abstract class StyleElement implements StyleKeys {
+
+    protected static final int ICON_IMAGE_IDX = 0;
+    protected static final int ICON_WIDTH_IDX = 1;
+    protected static final int ICON_HEIGHT_IDX = 2;
+    protected static final int ICON_OPACITY_IDX = 3;
+    protected static final int ICON_OFFSET_X_IDX = 4;
+    protected static final int ICON_OFFSET_Y_IDX = 5;
+    protected static final String[] ICON_KEYS = {ICON_IMAGE, ICON_WIDTH, ICON_HEIGHT, ICON_OPACITY, ICON_OFFSET_X, ICON_OFFSET_Y};
+    protected static final String[] REPEAT_IMAGE_KEYS = {REPEAT_IMAGE, REPEAT_IMAGE_WIDTH, REPEAT_IMAGE_HEIGHT, REPEAT_IMAGE_OPACITY,
+            null, null};
+
+    public float majorZIndex;
+    public float zIndex;
+    public float objectZIndex;
+    public boolean isModifier;  // false, if style can serve as main style for the
+    // primitive; true, if it is a highlight or modifier
+
+    public StyleElement(float major_z_index, float z_index, float object_z_index, boolean isModifier) {
+        this.majorZIndex = major_z_index;
+        this.zIndex = z_index;
+        this.objectZIndex = object_z_index;
+        this.isModifier = isModifier;
+    }
+
+    protected StyleElement(Cascade c, float default_major_z_index) {
+        majorZIndex = c.get(MAJOR_Z_INDEX, default_major_z_index, Float.class);
+        zIndex = c.get(Z_INDEX, 0f, Float.class);
+        objectZIndex = c.get(OBJECT_Z_INDEX, 0f, Float.class);
+        isModifier = c.get(MODIFIER, Boolean.FALSE, Boolean.class);
+    }
+
+    /**
+     * draws a primitive
+     * @param primitive primitive to draw
+     * @param paintSettings paint settings
+     * @param painter painter
+     * @param selected true, if primitive is selected
+     * @param outermember true, if primitive is not selected and outer member of a selected multipolygon relation
+     * @param member true, if primitive is not selected and member of a selected relation
+     */
+    public abstract void paintPrimitive(OsmPrimitive primitive, MapPaintSettings paintSettings, StyledMapRenderer painter,
+            boolean selected, boolean outermember, boolean member);
+
+    public boolean isProperLineStyle() {
+        return false;
+    }
+
+    /**
+     * Get a property value of type Width
+     * @param c the cascade
+     * @param key property key for the width value
+     * @param relativeTo reference width. Only needed, when relative width syntax is used, e.g. "+4".
+     * @return width
+     */
+    protected static Float getWidth(Cascade c, String key, Float relativeTo) {
+        Float width = c.get(key, null, Float.class, true);
+        if (width != null) {
+            if (width > 0)
+                return width;
+        } else {
+            Keyword widthKW = c.get(key, null, Keyword.class, true);
+            if (Keyword.THINNEST.equals(widthKW))
+                return 0f;
+            if (Keyword.DEFAULT.equals(widthKW))
+                return (float) MapPaintSettings.INSTANCE.getDefaultSegmentWidth();
+            if (relativeTo != null) {
+                RelativeFloat width_rel = c.get(key, null, RelativeFloat.class, true);
+                if (width_rel != null)
+                    return relativeTo + width_rel.val;
+            }
+        }
+        return null;
+    }
+
+    /* ------------------------------------------------------------------------------- */
+    /* cached values                                                                   */
+    /* ------------------------------------------------------------------------------- */
+    /*
+     * Two preference values and the set of created fonts are cached in order to avoid
+     * expensive lookups and to avoid too many font objects
+     *
+     * FIXME: cached preference values are not updated if the user changes them during
+     * a JOSM session. Should have a listener listening to preference changes.
+     */
+    private static volatile String DEFAULT_FONT_NAME;
+    private static volatile Float DEFAULT_FONT_SIZE;
+    private static final Object lock = new Object();
+
+    // thread save access (double-checked locking)
+    private static Float getDefaultFontSize() {
+        Float s = DEFAULT_FONT_SIZE;
+        if (s == null) {
+            synchronized (lock) {
+                s = DEFAULT_FONT_SIZE;
+                if (s == null) {
+                    DEFAULT_FONT_SIZE = s = (float) Main.pref.getInteger("mappaint.fontsize", 8);
+                }
+            }
+        }
+        return s;
+    }
+
+    private static String getDefaultFontName() {
+        String n = DEFAULT_FONT_NAME;
+        if (n == null) {
+            synchronized (lock) {
+                n = DEFAULT_FONT_NAME;
+                if (n == null) {
+                    DEFAULT_FONT_NAME = n = Main.pref.get("mappaint.font", "Droid Sans");
+                }
+            }
+        }
+        return n;
+    }
+
+    private static class FontDescriptor {
+        public String name;
+        public int style;
+        public int size;
+
+        FontDescriptor(String name, int style, int size) {
+            this.name = name;
+            this.style = style;
+            this.size = size;
+        }
+
+        @Override
+        public int hashCode() {
+            final int prime = 31;
+            int result = 1;
+            result = prime * result + ((name == null) ? 0 : name.hashCode());
+            result = prime * result + size;
+            result = prime * result + style;
+            return result;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj)
+                return true;
+            if (obj == null)
+                return false;
+            if (getClass() != obj.getClass())
+                return false;
+            FontDescriptor other = (FontDescriptor) obj;
+            if (name == null) {
+                if (other.name != null)
+                    return false;
+            } else if (!name.equals(other.name))
+                return false;
+            if (size != other.size)
+                return false;
+            if (style != other.style)
+                return false;
+            return true;
+        }
+    }
+
+    private static final Map<FontDescriptor, Font> FONT_MAP = new HashMap<>();
+
+    private static Font getCachedFont(FontDescriptor fd) {
+        Font f = FONT_MAP.get(fd);
+        if (f != null) return f;
+        f = new Font(fd.name, fd.style, fd.size);
+        FONT_MAP.put(fd, f);
+        return f;
+    }
+
+    private static Font getCachedFont(String name, int style, int size) {
+        return getCachedFont(new FontDescriptor(name, style, size));
+    }
+
+    protected static Font getFont(Cascade c, String s) {
+        String name = c.get(FONT_FAMILY, getDefaultFontName(), String.class);
+        float size = c.get(FONT_SIZE, getDefaultFontSize(), Float.class);
+        int weight = Font.PLAIN;
+        if ("bold".equalsIgnoreCase(c.get(FONT_WEIGHT, null, String.class))) {
+            weight = Font.BOLD;
+        }
+        int style = Font.PLAIN;
+        if ("italic".equalsIgnoreCase(c.get(FONT_STYLE, null, String.class))) {
+            style = Font.ITALIC;
+        }
+        Font f = getCachedFont(name, style | weight, Math.round(size));
+        if (f.canDisplayUpTo(s) == -1)
+            return f;
+        else {
+            // fallback if the string contains characters that cannot be
+            // rendered by the selected font
+            return getCachedFont("SansSerif", style | weight, Math.round(size));
+        }
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (!(o instanceof StyleElement))
+            return false;
+        StyleElement s = (StyleElement) o;
+        return isModifier == s.isModifier &&
+                majorZIndex == s.majorZIndex &&
+                zIndex == s.zIndex &&
+                objectZIndex == s.objectZIndex;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 5;
+        hash = 41 * hash + Float.floatToIntBits(this.majorZIndex);
+        hash = 41 * hash + Float.floatToIntBits(this.zIndex);
+        hash = 41 * hash + Float.floatToIntBits(this.objectZIndex);
+        hash = 41 * hash + (isModifier ? 1 : 0);
+        return hash;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("z_idx=[%s/%s/%s] ", majorZIndex, zIndex, objectZIndex) + (isModifier ? "modifier " : "");
+    }
+}
Index: /trunk/src/org/openstreetmap/josm/gui/mappaint/styleelement/TextLabel.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/mappaint/styleelement/TextLabel.java	(revision 9278)
+++ /trunk/src/org/openstreetmap/josm/gui/mappaint/styleelement/TextLabel.java	(revision 9278)
@@ -0,0 +1,227 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.mappaint.styleelement;
+
+import java.awt.Color;
+import java.awt.Font;
+import java.util.Objects;
+
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.gui.mappaint.Cascade;
+import org.openstreetmap.josm.gui.mappaint.Environment;
+import org.openstreetmap.josm.gui.mappaint.Keyword;
+import org.openstreetmap.josm.gui.mappaint.MapPaintStyles.TagKeyReference;
+import org.openstreetmap.josm.gui.mappaint.StyleKeys;
+import org.openstreetmap.josm.gui.mappaint.styleelement.LabelCompositionStrategy.DeriveLabelFromNameTagsCompositionStrategy;
+import org.openstreetmap.josm.gui.mappaint.styleelement.LabelCompositionStrategy.StaticLabelCompositionStrategy;
+import org.openstreetmap.josm.gui.mappaint.styleelement.LabelCompositionStrategy.TagLookupCompositionStrategy;
+import org.openstreetmap.josm.tools.CheckParameterUtil;
+import org.openstreetmap.josm.tools.Utils;
+
+/**
+ * Represents the rendering style for a textual label placed somewhere on the map.
+ * @since 3880
+ */
+public class TextLabel implements StyleKeys {
+    public static final LabelCompositionStrategy AUTO_LABEL_COMPOSITION_STRATEGY = new DeriveLabelFromNameTagsCompositionStrategy();
+
+    /** the strategy for building the actual label value for a given a {@link OsmPrimitive}.
+     * Check for null before accessing.
+     */
+    public LabelCompositionStrategy labelCompositionStrategy;
+    /** the font to be used when rendering*/
+    public Font font;
+    public int xOffset;
+    public int yOffset;
+    public Color color;
+    public Float haloRadius;
+    public Color haloColor;
+
+    /**
+     * Creates a new text element
+     *
+     * @param strategy the strategy indicating how the text is composed for a specific {@link OsmPrimitive} to be rendered.
+     * If null, no label is rendered.
+     * @param font the font to be used. Must not be null.
+     * @param xOffset x offset
+     * @param yOffset y offset
+     * @param color the color to be used. Must not be null
+     * @param haloRadius halo radius
+     * @param haloColor halo color
+     */
+    public TextLabel(LabelCompositionStrategy strategy, Font font, int xOffset, int yOffset, Color color, Float haloRadius, Color haloColor) {
+        CheckParameterUtil.ensureParameterNotNull(font);
+        CheckParameterUtil.ensureParameterNotNull(color);
+        labelCompositionStrategy = strategy;
+        this.font = font;
+        this.xOffset = xOffset;
+        this.yOffset = yOffset;
+        this.color = color;
+        this.haloRadius = haloRadius;
+        this.haloColor = haloColor;
+    }
+
+    /**
+     * Copy constructor
+     *
+     * @param other the other element.
+     */
+    public TextLabel(TextLabel other) {
+        this.labelCompositionStrategy = other.labelCompositionStrategy;
+        this.font = other.font;
+        this.xOffset = other.xOffset;
+        this.yOffset = other.yOffset;
+        this.color = other.color;
+        this.haloColor = other.haloColor;
+        this.haloRadius = other.haloRadius;
+    }
+
+    /**
+     * Derives a suitable label composition strategy from the style properties in {@code c}.
+     *
+     * @param c the style properties
+     * @param defaultAnnotate whether to return {@link #AUTO_LABEL_COMPOSITION_STRATEGY} if not strategy is found
+     * @return the label composition strategy, or {@code null}
+     */
+    protected static LabelCompositionStrategy buildLabelCompositionStrategy(Cascade c, boolean defaultAnnotate) {
+        /*
+         * If the cascade includes a TagKeyReference we will lookup the rendered label
+         * from a tag value.
+         */
+        TagKeyReference tkr = c.get(TEXT, null, TagKeyReference.class, true);
+        if (tkr != null)
+            return new TagLookupCompositionStrategy(tkr.key);
+
+        /*
+         * Check whether the label composition strategy is given by a keyword
+         */
+        Keyword keyword = c.get(TEXT, null, Keyword.class, true);
+        if (Keyword.AUTO.equals(keyword))
+            return AUTO_LABEL_COMPOSITION_STRATEGY;
+
+        /*
+         * Do we have a static text label?
+         */
+        String text = c.get(TEXT, null, String.class, true);
+        if (text != null)
+            return new StaticLabelCompositionStrategy(text);
+        return defaultAnnotate ? AUTO_LABEL_COMPOSITION_STRATEGY : null;
+    }
+
+    /**
+     * Builds a text element from style properties in {@code c} and the
+     * default text color {@code defaultTextColor}
+     *
+     * @param env the environment
+     * @param defaultTextColor the default text color. Must not be null.
+     * @param defaultAnnotate true, if a text label shall be rendered by default, even if the style sheet
+     *   doesn't include respective style declarations
+     * @return the text element or null, if the style properties don't include
+     * properties for text rendering
+     * @throws IllegalArgumentException if {@code defaultTextColor} is null
+     */
+    public static TextLabel create(Environment env, Color defaultTextColor, boolean defaultAnnotate) {
+        CheckParameterUtil.ensureParameterNotNull(defaultTextColor);
+        Cascade c = env.mc.getCascade(env.layer);
+
+        LabelCompositionStrategy strategy = buildLabelCompositionStrategy(c, defaultAnnotate);
+        if (strategy == null) return null;
+        String s = strategy.compose(env.osm);
+        if (s == null) return null;
+        Font font = StyleElement.getFont(c, s);
+
+        float xOffset = 0;
+        float yOffset = 0;
+        float[] offset = c.get(TEXT_OFFSET, null, float[].class);
+        if (offset != null) {
+            if (offset.length == 1) {
+                yOffset = offset[0];
+            } else if (offset.length >= 2) {
+                xOffset = offset[0];
+                yOffset = offset[1];
+            }
+        }
+        xOffset = c.get(TEXT_OFFSET_X, xOffset, Float.class);
+        yOffset = c.get(TEXT_OFFSET_Y, yOffset, Float.class);
+
+        Color color = c.get(TEXT_COLOR, defaultTextColor, Color.class);
+        float alpha = c.get(TEXT_OPACITY, 1f, Float.class);
+        color = new Color(color.getRed(), color.getGreen(),
+                color.getBlue(), Utils.color_float2int(alpha));
+
+        Float haloRadius = c.get(TEXT_HALO_RADIUS, null, Float.class);
+        if (haloRadius != null && haloRadius <= 0) {
+            haloRadius = null;
+        }
+        Color haloColor = null;
+        if (haloRadius != null) {
+            haloColor = c.get(TEXT_HALO_COLOR, Utils.complement(color), Color.class);
+            float haloAlpha = c.get(TEXT_HALO_OPACITY, 1f, Float.class);
+            haloColor = new Color(haloColor.getRed(), haloColor.getGreen(),
+                    haloColor.getBlue(), Utils.color_float2int(haloAlpha));
+        }
+
+        return new TextLabel(strategy, font, (int) xOffset, -(int) yOffset, color, haloRadius, haloColor);
+    }
+
+    /**
+     * Replies the label to be rendered for the primitive {@code osm}.
+     *
+     * @param osm the OSM object
+     * @return the label, or null, if {@code osm} is null or if no label can be
+     * derived for {@code osm}
+     */
+    public String getString(OsmPrimitive osm) {
+        if (labelCompositionStrategy == null) return null;
+        return labelCompositionStrategy.compose(osm);
+    }
+
+    @Override
+    public String toString() {
+        return "TextElement{" + toStringImpl() + '}';
+    }
+
+    protected String toStringImpl() {
+        StringBuilder sb = new StringBuilder(96);
+        sb.append("labelCompositionStrategy=").append(labelCompositionStrategy)
+          .append(" font=").append(font);
+        if (xOffset != 0) {
+            sb.append(" xOffset=").append(xOffset);
+        }
+        if (yOffset != 0) {
+            sb.append(" yOffset=").append(yOffset);
+        }
+        sb.append(" color=").append(Utils.toString(color));
+        if (haloRadius != null) {
+            sb.append(" haloRadius=").append(haloRadius)
+              .append(" haloColor=").append(haloColor);
+        }
+        return sb.toString();
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 3;
+        hash = 79 * hash + (labelCompositionStrategy != null ? labelCompositionStrategy.hashCode() : 0);
+        hash = 79 * hash + font.hashCode();
+        hash = 79 * hash + xOffset;
+        hash = 79 * hash + yOffset;
+        hash = 79 * hash + color.hashCode();
+        hash = 79 * hash + (haloRadius != null ? Float.floatToIntBits(haloRadius) : 0);
+        hash = 79 * hash + (haloColor != null ? haloColor.hashCode() : 0);
+        return hash;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null || getClass() != obj.getClass())
+            return false;
+        final TextLabel other = (TextLabel) obj;
+        return Objects.equals(labelCompositionStrategy, other.labelCompositionStrategy) &&
+        Objects.equals(font, other.font) &&
+        xOffset == other.xOffset &&
+        yOffset == other.yOffset &&
+        Objects.equals(color, other.color) &&
+        Objects.equals(haloRadius, other.haloRadius) &&
+        Objects.equals(haloColor, other.haloColor);
+    }
+}
Index: /trunk/src/org/openstreetmap/josm/tools/ImageProvider.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/tools/ImageProvider.java	(revision 9277)
+++ /trunk/src/org/openstreetmap/josm/tools/ImageProvider.java	(revision 9278)
@@ -59,10 +59,9 @@
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
 import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
-import org.openstreetmap.josm.gui.mappaint.ElemStyle;
-import org.openstreetmap.josm.gui.mappaint.MapImage;
 import org.openstreetmap.josm.gui.mappaint.MapPaintStyles;
-import org.openstreetmap.josm.gui.mappaint.NodeElemStyle;
 import org.openstreetmap.josm.gui.mappaint.Range;
 import org.openstreetmap.josm.gui.mappaint.StyleCache.StyleList;
+import org.openstreetmap.josm.gui.mappaint.styleelement.MapImage;
+import org.openstreetmap.josm.gui.mappaint.styleelement.StyleElement;
 import org.openstreetmap.josm.gui.tagging.presets.TaggingPreset;
 import org.openstreetmap.josm.gui.tagging.presets.TaggingPresets;
@@ -82,4 +81,5 @@
 import com.kitfox.svg.SVGDiagram;
 import com.kitfox.svg.SVGUniverse;
+import org.openstreetmap.josm.gui.mappaint.styleelement.NodeElement;
 
 /**
@@ -1300,7 +1300,7 @@
         if (primitive instanceof org.openstreetmap.josm.data.osm.Node) {
             Pair<StyleList, Range> nodeStyles = MapPaintStyles.getStyles().generateStyles(primitive, 100, false);
-            for (ElemStyle style : nodeStyles.a) {
-                if (style instanceof NodeElemStyle) {
-                    NodeElemStyle nodeStyle = (NodeElemStyle) style;
+            for (StyleElement style : nodeStyles.a) {
+                if (style instanceof NodeElement) {
+                    NodeElement nodeStyle = (NodeElement) style;
                     MapImage icon = nodeStyle.mapImage;
                     if (icon != null) {
