Index: src/org/openstreetmap/josm/data/osm/visitor/paint/MapPainter.java
===================================================================
--- src/org/openstreetmap/josm/data/osm/visitor/paint/MapPainter.java	(revision 3986)
+++ src/org/openstreetmap/josm/data/osm/visitor/paint/MapPainter.java	(working copy)
@@ -38,12 +38,11 @@
 import org.openstreetmap.josm.gui.NavigatableComponent;
 import org.openstreetmap.josm.gui.mappaint.NodeElemStyle;
 import org.openstreetmap.josm.gui.mappaint.NodeElemStyle.HorizontalTextAlignment;
-import org.openstreetmap.josm.gui.mappaint.NodeElemStyle.Symbol;
 import org.openstreetmap.josm.gui.mappaint.NodeElemStyle.NodeTextElement;
+import org.openstreetmap.josm.gui.mappaint.NodeElemStyle.Symbol;
 import org.openstreetmap.josm.gui.mappaint.NodeElemStyle.VerticalTextAlignment;
 import org.openstreetmap.josm.gui.mappaint.TextElement;
 import org.openstreetmap.josm.tools.ImageProvider;
-import org.openstreetmap.josm.tools.LanguageInfo;
 import org.openstreetmap.josm.tools.Pair;
 
 public class MapPainter {
@@ -73,8 +72,6 @@
 
     private final boolean leftHandTraffic;
 
-    private final Collection<String> regionalNameOrder;
-
     private static final double PHI = Math.toRadians(20);
     private static final double cosPHI = Math.cos(PHI);
     private static final double sinPHI = Math.sin(PHI);
@@ -103,8 +100,6 @@
         this.virtualNodeSpace = Main.pref.getInteger("mappaint.node.virtual-space", 70);
         this.segmentNumberSpace = Main.pref.getInteger("mappaint.segmentnumber.space", 40);
 
-        String[] names = {"name:" + LanguageInfo.getJOSMLocaleCode(), "name", "int_name", "ref", "operator", "brand", "addr:housenumber"};
-        this.regionalNameOrder = Main.pref.getCollection("mappaint.nameOrder", Arrays.asList(names));
         this.circum = circum;
         this.leftHandTraffic = leftHandTraffic;
     }
@@ -117,7 +112,7 @@
      *              e.g. oneway street or waterway
      * @param onewayReversed for oneway=-1 and similar
      */
-    public void drawWay(Way way, Color color, BasicStroke line, BasicStroke dashes, Color dashedColor, 
+    public void drawWay(Way way, Color color, BasicStroke line, BasicStroke dashes, Color dashedColor,
             TextElement text, boolean showOrientation, boolean showHeadArrowOnly,
             boolean showOneway, boolean onewayReversed) {
 
@@ -252,7 +247,7 @@
     private void drawTextOnPath(Way way, TextElement text) {
         if (text == null)
             return;
-        String name = text.getString(way, this);
+        String name = text.getString(way);
         if (name == null || name.equals(""))
             return;
 
@@ -291,8 +286,8 @@
         double tStart;
 
         if (p1[0] < p2[0] &&
-            p1[2] < Math.PI/2 &&
-            p1[2] > -Math.PI/2) {
+                p1[2] < Math.PI/2 &&
+                p1[2] > -Math.PI/2) {
             angleOffset = 0;
             offsetSign = 1;
             tStart = t1;
@@ -346,8 +341,8 @@
                 continue;
             }
             return new double[] {poly.xpoints[i-1]+(totalLen - curLen)/segLen*dx,
-                                 poly.ypoints[i-1]+(totalLen - curLen)/segLen*dy,
-                                 Math.atan2(dy, dx)};
+                    poly.ypoints[i-1]+(totalLen - curLen)/segLen*dy,
+                    Math.atan2(dy, dx)};
         }
         return null;
     }
@@ -494,9 +489,12 @@
         if (!isShowNames() || text == null)
             return;
 
-        String s = text.textKey == null ? getNodeName(n) : n.get(text.textKey);
-        if (s == null)
-            return;
+        /*
+         * abort if we can't compose the label to be rendered
+         */
+        if (text.labelCompositionStrategy == null) return;
+        String s = text.labelCompositionStrategy.compose(n);
+        if (s == null) return;
 
         Font defaultFont = g.getFont();
         g.setFont(text.font);
@@ -597,9 +595,12 @@
         }
 
         if (text != null && isShowNames()) {
-            String name = text.textKey == null ? getAreaName(osm) : osm.get(text.textKey);
-            if (name == null)
-                return;
+            /*
+             * abort if we can't compose the label to be rendered
+             */
+            if (text.labelCompositionStrategy == null) return;
+            String name = text.labelCompositionStrategy.compose(osm);
+            if (name == null) return;
 
             Rectangle pb = polygon.getBounds();
             FontMetrics fontMetrics = g.getFontMetrics(orderFont); // if slow, use cache
@@ -930,34 +931,6 @@
         }
     }
 
-    //TODO Not a good place for this method
-    public String getNodeName(Node n) {
-        String name = null;
-        if (n.hasKeys()) {
-            for (String rn : regionalNameOrder) {
-                name = n.get(rn);
-                if (name != null) {
-                    break;
-                }
-            }
-        }
-        return name;
-    }
-
-    //TODO Not a good place for this method
-    public String getAreaName(OsmPrimitive w) {
-        String name = null;
-        if (w.hasKeys()) {
-            for (String rn : regionalNameOrder) {
-                name = w.get(rn);
-                if (name != null) {
-                    break;
-                }
-            }
-        }
-        return name;
-    }
-
     public boolean isInactive() {
         return inactive;
     }
Index: src/org/openstreetmap/josm/gui/mappaint/ElemStyle.java
===================================================================
--- src/org/openstreetmap/josm/gui/mappaint/ElemStyle.java	(revision 3986)
+++ src/org/openstreetmap/josm/gui/mappaint/ElemStyle.java	(working copy)
@@ -16,7 +16,7 @@
     public float z_index;
     public float object_z_index;
     public boolean isModifier;  // false, if style can serve as main style for the
-                                // primitive; true, if it is a highlight or modifier
+    // primitive; true, if it is a highlight or modifier
 
     public ElemStyle(float z_index, float object_z_index, boolean isModifier) {
         this.z_index = z_index;
@@ -63,7 +63,7 @@
         String name = c.get("font-family", Main.pref.get("mappaint.font", "Helvetica"), String.class);
         float size = c.get("font-size", (float) Main.pref.getInteger("mappaint.fontsize", 8), Float.class);
         int weight = Font.PLAIN;
-        Keyword weightKW = c.get("font-wheight", null, Keyword.class);
+        Keyword weightKW = c.get("font-weight", null, Keyword.class);
         if (weightKW != null && equal(weightKW, "bold")) {
             weight = Font.BOLD;
         }
Index: src/org/openstreetmap/josm/gui/mappaint/LabelCompositionStrategy.java
===================================================================
--- src/org/openstreetmap/josm/gui/mappaint/LabelCompositionStrategy.java	(revision 0)
+++ src/org/openstreetmap/josm/gui/mappaint/LabelCompositionStrategy.java	(revision 0)
@@ -0,0 +1,244 @@
+// 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.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
+ *   option <tt>mappaint.nameOrder</tt>.</li>
+ * </ul>
+ * </p>
+ *
+ */
+public abstract class LabelCompositionStrategy {
+
+    static public class StaticLabelCompositionStrategy extends LabelCompositionStrategy {
+        private 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;
+        }
+    }
+
+    static public class TagLookupCompositionStrategy extends LabelCompositionStrategy {
+
+        private 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;
+        }
+    }
+
+    static public class DeriveLabelFromNameTagsCompositionStrategy extends LabelCompositionStrategy {
+
+        /**
+         * The list of default name tags from which a label candidate is derived.
+         */
+        static public final String[] DEFAULT_NAME_TAGS = {
+            "name:" + LanguageInfo.getJOSMLocaleCode(),
+            "name",
+            "int_name",
+            "ref",
+            "operator",
+            "brand",
+            "addr:housenumber"
+        };
+
+        private  List<String> nameTags = new ArrayList<String>();
+
+        /**
+         * <p>Creates the strategy and initializes its name tags from the preferences.</p>
+         * 
+         * <p><strong>Note:</strong> If the list of name tags in the preferences changes, strategy instances
+         * are not notified. It's up to the client to listen to preference changes and
+         * invoke {@link #initNameTagsFromPreferences()} accordingly.</p>
+         * 
+         */
+        public DeriveLabelFromNameTagsCompositionStrategy() {
+            initNameTagsFromPreferences();
+        }
+
+        /**
+         * Sets the name tags to be looked up in order to build up the label
+         * 
+         * @param nameTags the name tags. null values are ignore.
+         */
+        public void setNameTags(List<String> nameTags){
+            if (nameTags == null) {
+                nameTags = Collections.emptyList();
+            }
+            this.nameTags = new ArrayList<String>();
+            for(String tag: nameTags) {
+                if (tag == null) {
+                    continue;
+                }
+                tag = tag.trim();
+                if (tag.isEmpty()) {
+                    continue;
+                }
+                this.nameTags.add(tag);
+            }
+        }
+
+        /**
+         * 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);
+        }
+
+        /**
+         * Initializes the name tags to use from a list of default name tags (see
+         * {@link #DEFAULT_NAME_TAGS}) and from name tags configured in the preferences
+         * using the preference key <tt>mappaint.nameOrder</tt>.
+         */
+        public void initNameTagsFromPreferences() {
+            if (Main.pref == null){
+                this.nameTags = new ArrayList<String>(Arrays.asList(DEFAULT_NAME_TAGS));
+            } else {
+                this.nameTags = new ArrayList<String>(
+                        Main.pref.getCollection("mappaint.nameOrder", Arrays.asList(DEFAULT_NAME_TAGS))
+                );
+            }
+        }
+
+        private String getPrimitiveName(OsmPrimitive n) {
+            String name = null;
+            if (!n.hasKeys()) return null;
+            for (String rn : nameTags) {
+                name = n.get(rn);
+                if (name != null) return name;
+            }
+            return null;
+        }
+
+        @Override
+        public String compose(OsmPrimitive primitive) {
+            if (primitive == null) return null;
+            return getPrimitiveName(primitive);
+        }
+
+        @Override
+        public String toString() {
+            return "{" + getClass().getSimpleName() +"}";
+        }
+    }
+
+    /**
+     * 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
+     */
+    abstract public String compose(OsmPrimitive primitive);
+}
Index: src/org/openstreetmap/josm/gui/mappaint/MultiCascade.java
===================================================================
--- src/org/openstreetmap/josm/gui/mappaint/MultiCascade.java	(revision 3986)
+++ src/org/openstreetmap/josm/gui/mappaint/MultiCascade.java	(working copy)
@@ -6,13 +6,15 @@
 import java.util.Map;
 import java.util.Map.Entry;
 
+import org.openstreetmap.josm.tools.CheckParameterUtil;
+
 /**
  * Several layers / cascades, e.g. one for the main Line and one for each overlay.
  * The range is (0,Infinity) at first and it shrinks in the process when
  * StyleSources apply zoom level dependent properties.
  */
 public class MultiCascade {
-    
+
     private Map<String, Cascade> layers;
     public Range range;
 
@@ -27,8 +29,7 @@
      * a clone of the "*" layer, if it exists.
      */
     public Cascade getOrCreateCascade(String layer) {
-        if (layer == null)
-            throw new IllegalArgumentException();
+        CheckParameterUtil.ensureParameterNotNull(layer);
         Cascade c = layers.get(layer);
         if (c == null) {
             if (layers.containsKey("*")) {
Index: src/org/openstreetmap/josm/gui/mappaint/NodeElemStyle.java
===================================================================
--- src/org/openstreetmap/josm/gui/mappaint/NodeElemStyle.java	(revision 3986)
+++ src/org/openstreetmap/josm/gui/mappaint/NodeElemStyle.java	(working copy)
@@ -25,6 +25,7 @@
  * applies for Nodes and turn restriction relations
  */
 public class NodeElemStyle extends ElemStyle {
+    //static private final Logger logger = Logger.getLogger(NodeElemStyle.class.getName());
 
     public ImageIcon icon;
     public int iconAlpha;
@@ -62,10 +63,10 @@
                 return false;
             final Symbol other = (Symbol) obj;
             return  symbol == other.symbol &&
-                    size == other.size &&
-                    equal(stroke, other.stroke) &&
-                    equal(strokeColor, other.strokeColor) &&
-                    equal(fillColor, other.fillColor);
+            size == other.size &&
+            equal(stroke, other.stroke) &&
+            equal(strokeColor, other.strokeColor) &&
+            equal(fillColor, other.fillColor);
         }
 
         @Override
@@ -82,8 +83,8 @@
         @Override
         public String toString() {
             return "symbol=" + symbol + " size=" + size +
-                    (stroke != null ? (" stroke=" + stroke + " strokeColor=" + strokeColor) : "") +
-                    (fillColor != null ? (" fillColor=" + fillColor) : "");
+            (stroke != null ? (" stroke=" + stroke + " strokeColor=" + strokeColor) : "") +
+            (fillColor != null ? (" fillColor=" + fillColor) : "");
         }
     }
 
@@ -107,7 +108,7 @@
                 return false;
             final NodeTextElement other = (NodeTextElement) obj;
             return hAlign == other.hAlign &&
-                    vAlign == other.vAlign;
+            vAlign == other.vAlign;
         }
 
         @Override
@@ -162,9 +163,6 @@
             symbol = createSymbol(env);
         }
 
-        if (icon == null && symbol == null && !allowOnlyText)
-            return null;
-
         NodeTextElement text = null;
         TextElement te = TextElement.create(c, PaintColors.TEXT.get());
         if (te != null) {
@@ -192,7 +190,7 @@
             }
             text = new NodeTextElement(te, hAlign, vAlign);
         }
-        
+
         return new NodeElemStyle(c, icon, iconAlpha, symbol, text);
     }
 
@@ -224,7 +222,7 @@
             shape = SymbolShape.DECAGON;
         } else
             return null;
-        
+
         Float sizeOnDefault = c_def.get("symbol-size", null, Float.class);
         if (sizeOnDefault != null && sizeOnDefault <= 0) {
             sizeOnDefault = null;
@@ -258,8 +256,9 @@
         }
 
         Color fillColor = c.get("symbol-fill-color", null, Color.class);
-        if (stroke == null && fillColor == null)
+        if (stroke == null && fillColor == null) {
             fillColor = Color.BLUE;
+        }
 
         if (fillColor != null) {
             float fillAlpha = c.get("symbol-fill-opacity", 1f, Float.class);
@@ -335,14 +334,14 @@
                     }
 
                     final int size = Utils.max((selected ? settings.getSelectedNodeSize() : 0),
-                                            (n.isTagged() ? settings.getTaggedNodeSize() : 0),
-                                            (isConnection ? settings.getConnectionNodeSize() : 0),
-                                            settings.getUnselectedNodeSize());
+                            (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();
+                    (n.isTagged() && settings.isFillTaggedNode()) ||
+                    (isConnection && settings.isFillConnectionNode()) ||
+                    settings.isFillUnselectedNode();
 
                     painter.drawNode(n, color, size, fill, text);
                 }
@@ -394,8 +393,8 @@
     @Override
     public String toString() {
         return "NodeElemStyle{" + super.toString() +
-                (icon != null ? ("icon=" + icon + " iconAlpha=" + iconAlpha) : "") +
-                (symbol != null ? (" symbol=[" + symbol + "]") : "") + '}';
+        (icon != null ? ("icon=" + icon + " iconAlpha=" + iconAlpha) : "") +
+        (symbol != null ? (" symbol=[" + symbol + "]") : "") + '}';
     }
 
 }
Index: src/org/openstreetmap/josm/gui/mappaint/TextElement.java
===================================================================
--- src/org/openstreetmap/josm/gui/mappaint/TextElement.java	(revision 3986)
+++ src/org/openstreetmap/josm/gui/mappaint/TextElement.java	(working copy)
@@ -1,31 +1,52 @@
 // License: GPL. For details, see LICENSE file.
 package org.openstreetmap.josm.gui.mappaint;
 
-import static org.openstreetmap.josm.tools.Utils.equal;
-
 import java.awt.Color;
 import java.awt.Font;
+
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
-import org.openstreetmap.josm.data.osm.visitor.paint.MapPainter;
-
+import org.openstreetmap.josm.gui.mappaint.LabelCompositionStrategy.DeriveLabelFromNameTagsCompositionStrategy;
+import org.openstreetmap.josm.gui.mappaint.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.
+ *
+ */
 public class TextElement {
-    // textKey == null means automatic generation of text string, otherwise
-    // the corresponding tag value is used
-    public String textKey;
+    //static private final Logger logger = Logger.getLogger(TextElement.class.getName());
+
+    static private final LabelCompositionStrategy AUTO_LABEL_COMPOSITION_STRATEGY = new DeriveLabelFromNameTagsCompositionStrategy();
+
+    /** 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;
+    /** the strategy for building the actual label value for a given a {@link OsmPrimitive}.
+     * Check for null before accessing.
+     */
+    public LabelCompositionStrategy labelCompositionStrategy;
 
-    public TextElement(String textKey, Font font, int xOffset, int yOffset, Color color, Float haloRadius, 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
+     * @param yOffset
+     * @param color the color to be used. Must not be null
+     * @param haloRadius
+     * @param haloColor
+     */
+    public TextElement(LabelCompositionStrategy strategy, Font font, int xOffset, int yOffset, Color color, Float haloRadius, Color haloColor) {
         CheckParameterUtil.ensureParameterNotNull(font);
         CheckParameterUtil.ensureParameterNotNull(color);
-        this.textKey = textKey;
+        labelCompositionStrategy = strategy;
         this.font = font;
         this.xOffset = xOffset;
         this.yOffset = yOffset;
@@ -34,8 +55,13 @@
         this.haloColor = haloColor;
     }
 
+    /**
+     * Copy constructor
+     * 
+     * @param other the other element.
+     */
     public TextElement(TextElement other) {
-        this.textKey = other.textKey;
+        this.labelCompositionStrategy = other.labelCompositionStrategy;
         this.font = other.font;
         this.xOffset = other.xOffset;
         this.yOffset = other.yOffset;
@@ -44,17 +70,39 @@
         this.haloRadius = other.haloRadius;
     }
 
-    public static TextElement create(Cascade c, Color defTextColor) {
-
-        String textKey = null;
+    /**
+     * Derives a suitable label composition strategy from the style properties in
+     * {@code c}.
+     * 
+     * @param c the style properties
+     * @return the label composition strategy
+     */
+    protected static LabelCompositionStrategy buildLabelCompositionStrategy(Cascade c){
         Keyword textKW = c.get("text", null, Keyword.class, true);
         if (textKW == null) {
-            textKey = c.get("text", null, String.class);
-            if (textKey == null)
-                return null;
-        } else if (!textKW.val.equals("auto"))
-            return null;
+            String textKey = c.get("text", null, String.class);
+            if (textKey == null) return null;
+            return new TagLookupCompositionStrategy(textKey);
+        } else if (textKW.val.equals("auto"))
+            return AUTO_LABEL_COMPOSITION_STRATEGY;
+        else
+            return new TagLookupCompositionStrategy(textKW.val);
+    }
 
+    /**
+     * Builds a text element from style properties in {@code c} and the
+     * default text color {@code defaultTextColor}
+     * 
+     * @param c the style properties
+     * @param defaultTextColor the default text color. Must not be null.
+     * @return the text element or null, if the style properties don't include
+     * properties for text rendering
+     * @throws IllegalArgumentException thrown if {@code defaultTextColor} is null
+     */
+    public static TextElement create(Cascade c, Color defaultTextColor)  throws IllegalArgumentException{
+        CheckParameterUtil.ensureParameterNotNull(defaultTextColor, "defaultTextColor");
+
+        LabelCompositionStrategy strategy = buildLabelCompositionStrategy(c);
         Font font = ElemStyle.getFont(c);
 
         float xOffset = 0;
@@ -70,8 +118,8 @@
         }
         xOffset = c.get("text-offset-x", xOffset, Float.class);
         yOffset = c.get("text-offset-y", yOffset, Float.class);
-        
-        Color color = c.get("text-color", defTextColor, Color.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));
@@ -88,42 +136,86 @@
                     haloColor.getBlue(), Utils.color_float2int(haloAlpha));
         }
 
-        return new TextElement(textKey, font, (int) xOffset, - (int) yOffset, color, haloRadius, haloColor);
+        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 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 boolean equals(Object obj) {
-        if (obj == null || getClass() != obj.getClass())
-            return false;
-        final TextElement other = (TextElement) obj;
-        return  equal(textKey, other.textKey) &&
-                equal(font, other.font) &&
-                xOffset == other.xOffset &&
-                yOffset == other.yOffset &&
-                equal(color, other.color) &&
-                equal(haloRadius, other.haloRadius) &&
-                equal(haloColor, other.haloColor);
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("{TextElement ");
+        sb.append("strategy=");
+        sb.append(labelCompositionStrategy == null ? "null" : labelCompositionStrategy.toString());
+        sb.append("}");
+        return sb.toString();
     }
 
+    /* -------------------------------------------------------------------------------- */
+    /* equals and hashCode  (generated by Eclipse, regenerate if necessary)             */
+    /* -------------------------------------------------------------------------------- */
     @Override
     public int hashCode() {
-        int hash = 3;
-        hash = 79 * hash + (textKey != null ? textKey.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;
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((color == null) ? 0 : color.hashCode());
+        result = prime * result + ((font == null) ? 0 : font.hashCode());
+        result = prime * result + ((haloColor == null) ? 0 : haloColor.hashCode());
+        result = prime * result + ((haloRadius == null) ? 0 : haloRadius.hashCode());
+        result = prime * result + ((labelCompositionStrategy == null) ? 0 : labelCompositionStrategy.hashCode());
+        result = prime * result + xOffset;
+        result = prime * result + yOffset;
+        return result;
     }
 
-    public String getString(OsmPrimitive osm, MapPainter painter) {
-        if (textKey == null)
-            return painter.getAreaName(osm);
-        else
-            return osm.get(textKey);
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        TextElement other = (TextElement) obj;
+        if (color == null) {
+            if (other.color != null)
+                return false;
+        } else if (!color.equals(other.color))
+            return false;
+        if (font == null) {
+            if (other.font != null)
+                return false;
+        } else if (!font.equals(other.font))
+            return false;
+        if (haloColor == null) {
+            if (other.haloColor != null)
+                return false;
+        } else if (!haloColor.equals(other.haloColor))
+            return false;
+        if (haloRadius == null) {
+            if (other.haloRadius != null)
+                return false;
+        } else if (!haloRadius.equals(other.haloRadius))
+            return false;
+        if (labelCompositionStrategy == null) {
+            if (other.labelCompositionStrategy != null)
+                return false;
+        } else if (!labelCompositionStrategy.equals(other.labelCompositionStrategy))
+            return false;
+        if (xOffset != other.xOffset)
+            return false;
+        if (yOffset != other.yOffset)
+            return false;
+        return true;
     }
-
-
 }
Index: src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSStyleSource.java
===================================================================
--- src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSStyleSource.java	(revision 3986)
+++ src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSStyleSource.java	(working copy)
@@ -27,7 +27,8 @@
 import org.openstreetmap.josm.tools.Utils;
 
 public class MapCSSStyleSource extends StyleSource {
-    
+    //static private final Logger logger = Logger.getLogger(MapCSSStyleSource.class.getName());
+
     final public List<MapCSSRule> rules;
     private Color backgroundColorOverride;
 
@@ -65,6 +66,7 @@
         }
     }
 
+    @Override
     public InputStream getSourceInputStream() throws IOException {
         MirroredInputStream in = new MirroredInputStream(url);
         InputStream zip = in.getZipEntry("mapcss", "style");
@@ -107,26 +109,28 @@
         Environment env = new Environment(n, mc, "default", this);
 
         NEXT_RULE:
-        for (MapCSSRule r : rules) {
-            for (Selector s : r.selectors) {
-                if ((s instanceof GeneralSelector)) {
-                    GeneralSelector gs = (GeneralSelector) s;
-                    if (gs.base.equals(type))
-                     {
-                        for (Condition cnd : gs.conds) {
-                            if (!cnd.applies(env))
-                                continue NEXT_RULE;
+            for (MapCSSRule r : rules) {
+                for (Selector s : r.selectors) {
+                    if ((s instanceof GeneralSelector)) {
+                        GeneralSelector gs = (GeneralSelector) s;
+                        if (gs.base.equals(type))
+                        {
+                            for (Condition cnd : gs.conds) {
+                                if (!cnd.applies(env)) {
+                                    continue NEXT_RULE;
+                                }
+                            }
+                            for (Instruction i : r.declaration) {
+                                i.execute(env);
+                            }
                         }
-                        for (Instruction i : r.declaration) {
-                            i.execute(env);
-                        }
                     }
                 }
             }
-        }
         return mc.getCascade("default");
     }
 
+    @Override
     public Color getBackgroundColorOverride() {
         return backgroundColorOverride;
     }
@@ -159,7 +163,7 @@
                                 i.execute(env);
                             }
                         }
-                    } 
+                    }
                     env.layer = sub;
                     for (Instruction i : r.declaration) {
                         i.execute(env);
Index: test/data/styles/label-from-tag.mapcss
===================================================================
--- test/data/styles/label-from-tag.mapcss	(revision 0)
+++ test/data/styles/label-from-tag.mapcss	(revision 0)
@@ -0,0 +1,20 @@
+/*
+ * Simple test style sheet. Includes a style for nodes where the label is derived
+ * from the value of a specific tag.
+ *
+ */
+ 
+meta {
+    title: "Test style - Deriving labels from tags";
+}
+
+canvas {
+    background-color: #000000;
+}
+
+node {
+   text: my_label_tag;  /* take the value of the tag 'my_label_tag' as text */
+   text-color: white;   
+   font-size: 12;
+}
+
Index: test/functional/org/openstreetmap/josm/fixtures/JOSMFixture.java
===================================================================
--- test/functional/org/openstreetmap/josm/fixtures/JOSMFixture.java	(revision 3986)
+++ test/functional/org/openstreetmap/josm/fixtures/JOSMFixture.java	(working copy)
@@ -10,7 +10,7 @@
 import java.util.logging.Logger;
 
 import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.data.osm.DataSetMergerTest;
+import org.openstreetmap.josm.data.Preferences;
 import org.openstreetmap.josm.data.projection.Mercator;
 import org.openstreetmap.josm.io.OsmApi;
 import org.openstreetmap.josm.tools.I18n;
@@ -39,7 +39,7 @@
         // load properties
         //
         try {
-            testProperties.load(DataSetMergerTest.class.getResourceAsStream(testPropertiesResourceName));
+            testProperties.load(JOSMFixture.class.getResourceAsStream(testPropertiesResourceName));
         } catch(Exception e){
             logger.log(Level.SEVERE, MessageFormat.format("failed to load property file ''{0}''", testPropertiesResourceName));
             fail(MessageFormat.format("failed to load property file ''{0}''. \nMake sure the path ''$project_root/test/config'' is on the classpath.", testPropertiesResourceName));
@@ -57,6 +57,7 @@
             }
         }
         System.setProperty("josm.home", josmHome);
+        Main.pref = new Preferences();
         I18n.init();
         // initialize the plaform hook, and
         Main.determinePlatformHook();
Index: test/unit/org/openstreetmap/josm/gui/mappaint/AllMappaintTests.groovy
===================================================================
--- test/unit/org/openstreetmap/josm/gui/mappaint/AllMappaintTests.groovy	(revision 0)
+++ test/unit/org/openstreetmap/josm/gui/mappaint/AllMappaintTests.groovy	(revision 0)
@@ -0,0 +1,15 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.mappaint
+
+import junit.framework.TestCase;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+
+@RunWith(Suite.class)
+@Suite.SuiteClasses([
+    LabelCompositionStrategyTest.class,
+    MapCSSWithExtendedTextDirectivesTest.class
+])
+public class AllMappaintTests extends TestCase{}
+
Index: test/unit/org/openstreetmap/josm/gui/mappaint/LabelCompositionStrategyTest.groovy
===================================================================
--- test/unit/org/openstreetmap/josm/gui/mappaint/LabelCompositionStrategyTest.groovy	(revision 0)
+++ test/unit/org/openstreetmap/josm/gui/mappaint/LabelCompositionStrategyTest.groovy	(revision 0)
@@ -0,0 +1,66 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.mappaint
+
+import org.junit.*
+import org.openstreetmap.josm.fixtures.JOSMFixture;
+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.data.osm.Node;
+
+class LabelCompositionStrategyTest {
+    
+    @BeforeClass
+    public static void createJOSMFixture(){
+        JOSMFixture.createUnitTestFixture().init()
+    }
+
+    @Test
+    public void createStaticLabelCompositionStrategy() {
+        def n = new Node()
+        
+        def strat = new StaticLabelCompositionStrategy(null)
+        assert strat.compose(n) == null
+        
+        strat = new StaticLabelCompositionStrategy("a label")
+        assert strat.compose(n) == "a label"        
+    }
+    
+    @Test
+    public void createTagLookupCompositionStrategy() {
+        def n = new Node()
+        n.put("my-tag", "my-value")
+        
+        def strat = new TagLookupCompositionStrategy(null)
+        assert strat.compose(n) == null
+        
+        strat = new TagLookupCompositionStrategy("name")
+        assert strat.compose(n) == null
+        
+        strat = new TagLookupCompositionStrategy("my-tag")
+        assert strat.compose(n) == "my-value"
+    }
+    
+    @Test
+    public void createDeriveLabelFromNameTagsCompositionStrategy() {
+        def n 
+        def strat
+        
+        strat = new DeriveLabelFromNameTagsCompositionStrategy()
+        strat.setNameTags(null)
+        assert strat.getNameTags() == []
+        
+        strat = new DeriveLabelFromNameTagsCompositionStrategy()
+        strat.setNameTags(["name", "brand"])
+        assert strat.getNameTags() == ["name", "brand"]
+        
+        n = new Node()
+        n.put("brand", "my brand")        
+        assert strat.compose(n) == "my brand"
+        
+        n = new Node()
+        n.put("name", "my name")
+        n.put("brand", "my brand")
+        assert strat.compose(n) == "my name"        
+    }
+}
Index: test/unit/org/openstreetmap/josm/gui/mappaint/MapCSSWithExtendedTextDirectivesTest.groovy
===================================================================
--- test/unit/org/openstreetmap/josm/gui/mappaint/MapCSSWithExtendedTextDirectivesTest.groovy	(revision 0)
+++ test/unit/org/openstreetmap/josm/gui/mappaint/MapCSSWithExtendedTextDirectivesTest.groovy	(revision 0)
@@ -0,0 +1,58 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.mappaint
+
+import java.awt.Color;
+
+import org.junit.*;
+import org.openstreetmap.josm.fixtures.JOSMFixture 
+import org.openstreetmap.josm.gui.mappaint.LabelCompositionStrategy.DeriveLabelFromNameTagsCompositionStrategy 
+import org.openstreetmap.josm.gui.mappaint.LabelCompositionStrategy.StaticLabelCompositionStrategy 
+import org.openstreetmap.josm.gui.mappaint.LabelCompositionStrategy.TagLookupCompositionStrategy 
+class MapCSSWithExtendedTextDirectivesTest {
+    
+
+    @BeforeClass
+    public static void createJOSMFixture(){
+        JOSMFixture.createUnitTestFixture().init()
+    }
+
+    @Test
+    public void createAutoTextElement() {
+        Cascade c = new Cascade()
+        c.put("text", new Keyword("auto"))
+        
+        TextElement te = TextElement.create(c, Color.WHITE)
+        assert te.labelCompositionStrategy != null
+        assert te.labelCompositionStrategy instanceof DeriveLabelFromNameTagsCompositionStrategy
+    }
+    
+    @Test
+    public void createTextElementComposingTextFromTag() {
+        Cascade c = new Cascade()
+        c.put("text", "my_name")
+        
+        TextElement te = TextElement.create(c, Color.WHITE)
+        assert te.labelCompositionStrategy != null
+        assert te.labelCompositionStrategy instanceof TagLookupCompositionStrategy
+        assert te.labelCompositionStrategy.getDefaultLabelTag() == "my_name"
+    }
+    
+    @Test
+    public void createTextElementComposingTextFromTag_2() {
+        Cascade c = new Cascade()
+        c.put("text", new Keyword("my_name"))
+        
+        TextElement te = TextElement.create(c, Color.WHITE)
+        assert te.labelCompositionStrategy != null
+        assert te.labelCompositionStrategy instanceof TagLookupCompositionStrategy
+        assert te.labelCompositionStrategy.getDefaultLabelTag() == "my_name"
+    }
+        
+    @Test
+    public void createNullStrategy() {
+        Cascade c = new Cascade()
+        
+        TextElement te = TextElement.create(c, Color.WHITE)
+        assert te.labelCompositionStrategy == null
+    }
+}
