Index: trunk/data/tagging-preset.xsd
===================================================================
--- trunk/data/tagging-preset.xsd	(revision 4430)
+++ trunk/data/tagging-preset.xsd	(revision 4431)
@@ -4,6 +4,6 @@
 	elementFormDefault="qualified">
 
-	<!-- Localized attributes (for example de.description are not supported 
-		by xsd, so every element needs <anyAttribute/> To cover at least some common 
+	<!-- Localized attributes (for example de.description are not supported
+		by xsd, so every element needs <anyAttribute/> To cover at least some common
 		errors, elements have specified prohibited attributes -->
 
@@ -62,4 +62,6 @@
 		<attribute name="icon" type="string" />
 		<attribute name="type" type="string" />
+		<attribute name="name_template" type="string"/>
+		<attribute name="name_template_filter" type="string"/>
 
 		<attribute name="text" use="prohibited" />
Index: trunk/src/org/openstreetmap/josm/data/gpx/WayPoint.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/gpx/WayPoint.java	(revision 4430)
+++ trunk/src/org/openstreetmap/josm/data/gpx/WayPoint.java	(revision 4431)
@@ -74,5 +74,5 @@
     /**
      * <p>Replies the projected east/north coordinates.</p>
-     * 
+     *
      * <p>Uses the {@link Main#getProjection() global projection} to project the lan/lon-coordinates.
      * Internally caches the projected coordinates.</p>
@@ -80,8 +80,8 @@
      * <p><strong>Caveat:</strong> doesn't listen to projection changes. Clients must
      * {@link #reproject() trigger a reprojection} or {@link #invalidateEastNorthCache() invalidate the internal cache}.</p>
-     * 
+     *
      * @return the east north coordinates or {@code null}
      * @see #invalidateEastNorthCache()
-     * 
+     *
      */
     public final EastNorth getEastNorth() {
@@ -123,6 +123,9 @@
 
     @Override
-    public Object getTemplateValue(String name) {
-        return attr.get(name);
+    public Object getTemplateValue(String name, boolean special) {
+        if (!special)
+            return attr.get(name);
+        else
+            return null;
     }
 
Index: trunk/src/org/openstreetmap/josm/data/osm/AbstractPrimitive.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/AbstractPrimitive.java	(revision 4430)
+++ trunk/src/org/openstreetmap/josm/data/osm/AbstractPrimitive.java	(revision 4431)
@@ -17,5 +17,5 @@
 
 public abstract class AbstractPrimitive implements IPrimitive {
-    
+
     private static final AtomicLong idCounter = new AtomicLong(0);
 
@@ -305,5 +305,5 @@
         }
     }
-    
+
     /**
      * Marks this primitive as being modified.
@@ -373,5 +373,5 @@
         return (flags & FLAG_VISIBLE) != 0;
     }
-    
+
     /**
      * Sets whether this primitive is visible, i.e. whether it is known on the server
@@ -414,5 +414,5 @@
         return (flags & FLAG_INCOMPLETE) != 0;
     }
-    
+
     protected String getFlagsAsString() {
         StringBuilder builder = new StringBuilder();
@@ -528,5 +528,5 @@
         }
     }
-    
+
     /**
      * Remove the given key from the list
@@ -589,4 +589,16 @@
     }
 
+    public final String getIgnoreCase(String key) {
+        String[] keys = this.keys;
+        if (key == null)
+            return null;
+        if (keys == null)
+            return null;
+        for (int i=0; i<keys.length;i+=2) {
+            if (keys[i].equalsIgnoreCase(key)) return keys[i+1];
+        }
+        return null;
+    }
+
     @Override
     public final Collection<String> keySet() {
@@ -642,5 +654,5 @@
      */
     abstract protected void keysChangedImpl(Map<String, String> originalKeys);
-    
+
     /**
      * Replies the name of this primitive. The default implementation replies the value
@@ -680,12 +692,4 @@
         return getName();
     }
-    
-    /**
-     * Replies the display name of a primitive formatted by <code>formatter</code>
-     *
-     * @return the display name
-     */
-    @Override
-    public abstract String getDisplayName(NameFormatter formatter);
 
 }
Index: trunk/src/org/openstreetmap/josm/data/osm/IPrimitive.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/IPrimitive.java	(revision 4430)
+++ trunk/src/org/openstreetmap/josm/data/osm/IPrimitive.java	(revision 4431)
@@ -30,9 +30,8 @@
     int getChangesetId();
     void setChangesetId(int changesetId);
-    
+
     void visit(PrimitiveVisitor visitor);
     String getName();
     String getLocalName();
-    String getDisplayName(NameFormatter formatter);
 
 }
Index: trunk/src/org/openstreetmap/josm/data/osm/NameFormatter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/NameFormatter.java	(revision 4430)
+++ trunk/src/org/openstreetmap/josm/data/osm/NameFormatter.java	(revision 4431)
@@ -5,7 +5,7 @@
 
 public interface NameFormatter {
-    String format(INode node);
-    String format(IWay way);
-    String format(IRelation relation);
+    String format(Node node);
+    String format(Way way);
+    String format(Relation relation);
     String format(Changeset changeset);
 
Index: trunk/src/org/openstreetmap/josm/data/osm/NodeData.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/NodeData.java	(revision 4430)
+++ trunk/src/org/openstreetmap/josm/data/osm/NodeData.java	(revision 4431)
@@ -25,5 +25,5 @@
         return lat != Double.NaN && lon != Double.NaN;
     }
-    
+
     @Override
     public LatLon getCoor() {
@@ -77,7 +77,3 @@
     }
 
-    @Override
-    public String getDisplayName(NameFormatter formatter) {
-        return formatter.format(this);
-    }
 }
Index: trunk/src/org/openstreetmap/josm/data/osm/OsmPrimitive.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/OsmPrimitive.java	(revision 4430)
+++ trunk/src/org/openstreetmap/josm/data/osm/OsmPrimitive.java	(revision 4431)
@@ -25,4 +25,5 @@
 import org.openstreetmap.josm.tools.CheckParameterUtil;
 import org.openstreetmap.josm.tools.Predicate;
+import org.openstreetmap.josm.tools.template_engine.TemplateEngineDataProvider;
 
 /**
@@ -36,5 +37,8 @@
  * @author imi
  */
-abstract public class OsmPrimitive extends AbstractPrimitive implements Comparable<OsmPrimitive> {
+abstract public class OsmPrimitive extends AbstractPrimitive implements Comparable<OsmPrimitive>, TemplateEngineDataProvider {
+    private static final String SPECIAL_VALUE_ID = "id";
+    private static final String SPECIAL_VALUE_LOCAL_NAME = "localname";
+
 
     /**
@@ -412,5 +416,5 @@
         super.updateFlags(flag, value);
     }
-    
+
     @Override
     protected final void updateFlags(int flag, boolean value) {
@@ -621,7 +625,7 @@
 
         String directionDefault = "oneway? | incline=* | aerialway=* | "+
-        "waterway=stream | waterway=river | waterway=canal | waterway=drain | waterway=rapids | "+
-        "\"piste:type\"=downhill | \"piste:type\"=sled | man_made=\"piste:halfpipe\" | "+
-        "junction=roundabout";
+                "waterway=stream | waterway=river | waterway=canal | waterway=drain | waterway=rapids | "+
+                "\"piste:type\"=downhill | \"piste:type\"=sled | man_made=\"piste:halfpipe\" | "+
+                "junction=roundabout";
 
         try {
@@ -700,5 +704,5 @@
      * Keys handling
      ------------*/
-    
+
     @Override
     public final void setKeys(Map<String, String> keys) {
@@ -710,5 +714,5 @@
         }
     }
-    
+
     @Override
     public final void put(String key, String value) {
@@ -719,6 +723,6 @@
             writeUnlock(locked);
         }
-    }  
-    
+    }
+
     @Override
     public final void remove(String key) {
@@ -739,6 +743,6 @@
             writeUnlock(locked);
         }
-    }  
-    
+    }
+
     @Override
     protected final void keysChangedImpl(Map<String, String> originalKeys) {
@@ -857,5 +861,5 @@
     /**
      * <p>Visits {@code visitor} for all referrers.</p>
-     * 
+     *
      * @param visitor the visitor. Ignored, if null.
      */
@@ -889,15 +893,15 @@
         if (referrers == null) return false;
         checkDataset();
-        if (referrers instanceof OsmPrimitive) {
-          return n<=1 && referrers instanceof Way && ((OsmPrimitive)referrers).dataSet == dataSet;
-        } else {
-          int counter=0;
-          for (OsmPrimitive o : (OsmPrimitive[])referrers) {
-            if (dataSet == o.dataSet && o instanceof Way) {
-              if (++counter >= n) 
-                return true;
-            }
-          }
-          return false;
+        if (referrers instanceof OsmPrimitive)
+            return n<=1 && referrers instanceof Way && ((OsmPrimitive)referrers).dataSet == dataSet;
+        else {
+            int counter=0;
+            for (OsmPrimitive o : (OsmPrimitive[])referrers) {
+                if (dataSet == o.dataSet && o instanceof Way) {
+                    if (++counter >= n)
+                        return true;
+                }
+            }
+            return false;
         }
     }
@@ -1002,11 +1006,11 @@
 
         return
-        isDeleted() == other.isDeleted()
-        && isModified() == other.isModified()
-        && timestamp == other.timestamp
-        && version == other.version
-        && isVisible() == other.isVisible()
-        && (user == null ? other.user==null : user==other.user)
-        && changesetId == other.changesetId;
+                isDeleted() == other.isDeleted()
+                && isModified() == other.isModified()
+                && timestamp == other.timestamp
+                && version == other.version
+                && isVisible() == other.isVisible()
+                && (user == null ? other.user==null : user==other.user)
+                && changesetId == other.changesetId;
     }
 
@@ -1101,3 +1105,40 @@
     }
 
+    /**
+     * Replies the display name of a primitive formatted by <code>formatter</code>
+     *
+     * @return the display name
+     */
+    public abstract String getDisplayName(NameFormatter formatter);
+
+    @Override
+    public Collection<String> getTemplateKeys() {
+        Collection<String> keySet = keySet();
+        List<String> result = new ArrayList<String>(keySet.size() + 2);
+        result.add(SPECIAL_VALUE_ID);
+        result.add(SPECIAL_VALUE_LOCAL_NAME);
+        result.addAll(keySet);
+        return result;
+    }
+
+    @Override
+    public Object getTemplateValue(String name, boolean special) {
+        if (special) {
+            String lc = name.toLowerCase();
+            if (SPECIAL_VALUE_ID.equals(lc))
+                return getId();
+            else if (SPECIAL_VALUE_LOCAL_NAME.equals(lc))
+                return getLocalName();
+            else
+                return null;
+
+        } else
+            return getIgnoreCase(name);
+    }
+
+    @Override
+    public boolean evaluateCondition(Match condition) {
+        return condition.match(this);
+    }
+
 }
Index: trunk/src/org/openstreetmap/josm/data/osm/RelationData.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/RelationData.java	(revision 4430)
+++ trunk/src/org/openstreetmap/josm/data/osm/RelationData.java	(revision 4431)
@@ -62,14 +62,9 @@
         return OsmPrimitiveType.RELATION;
     }
-    
-    @Override 
+
+    @Override
     public void visit(PrimitiveVisitor visitor) {
         visitor.visit(this);
     }
 
-    @Override
-    public String getDisplayName(NameFormatter formatter) {
-        return formatter.format(this);
-    }
-
 }
Index: trunk/src/org/openstreetmap/josm/data/osm/Way.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/Way.java	(revision 4430)
+++ trunk/src/org/openstreetmap/josm/data/osm/Way.java	(revision 4431)
@@ -162,5 +162,5 @@
         visitor.visit(this);
     }
-    
+
     @Override public void visit(PrimitiveVisitor visitor) {
         visitor.visit(this);
@@ -453,5 +453,4 @@
 
 
-    @Override
     public String getDisplayName(NameFormatter formatter) {
         return formatter.format(this);
Index: trunk/src/org/openstreetmap/josm/data/osm/WayData.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/WayData.java	(revision 4430)
+++ trunk/src/org/openstreetmap/josm/data/osm/WayData.java	(revision 4431)
@@ -4,4 +4,5 @@
 import java.util.ArrayList;
 import java.util.List;
+
 import org.openstreetmap.josm.data.osm.visitor.PrimitiveVisitor;
 
@@ -57,14 +58,9 @@
         return OsmPrimitiveType.WAY;
     }
-    
-    @Override 
+
+    @Override
     public void visit(PrimitiveVisitor visitor) {
         visitor.visit(this);
     }
 
-    @Override
-    public String getDisplayName(NameFormatter formatter) {
-        return formatter.format(this);
-    }
-
 }
Index: trunk/src/org/openstreetmap/josm/gui/DefaultNameFormatter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/DefaultNameFormatter.java	(revision 4430)
+++ trunk/src/org/openstreetmap/josm/gui/DefaultNameFormatter.java	(revision 4431)
@@ -19,14 +19,10 @@
 import org.openstreetmap.josm.data.coor.CoordinateFormat;
 import org.openstreetmap.josm.data.osm.Changeset;
-import org.openstreetmap.josm.data.osm.INode;
 import org.openstreetmap.josm.data.osm.IPrimitive;
 import org.openstreetmap.josm.data.osm.IRelation;
-import org.openstreetmap.josm.data.osm.IWay;
 import org.openstreetmap.josm.data.osm.NameFormatter;
 import org.openstreetmap.josm.data.osm.Node;
-import org.openstreetmap.josm.data.osm.OsmPrimitive;
 import org.openstreetmap.josm.data.osm.OsmUtils;
 import org.openstreetmap.josm.data.osm.Relation;
-import org.openstreetmap.josm.data.osm.RelationMember;
 import org.openstreetmap.josm.data.osm.Way;
 import org.openstreetmap.josm.data.osm.history.HistoryNameFormatter;
@@ -35,4 +31,6 @@
 import org.openstreetmap.josm.data.osm.history.HistoryRelation;
 import org.openstreetmap.josm.data.osm.history.HistoryWay;
+import org.openstreetmap.josm.gui.tagging.TaggingPreset;
+import org.openstreetmap.josm.tools.TaggingPresetNameTemplateList;
 
 /**
@@ -57,5 +55,5 @@
         return instance;
     }
-    
+
     /**
      * Registers a format hook. Adds the hook at the first position of the format hooks.
@@ -103,5 +101,5 @@
             namingTagsForRelations = new ArrayList<String>(
                     Main.pref.getCollection("relation.nameOrder", Arrays.asList(DEFAULT_NAMING_TAGS_FOR_RELATIONS))
-            );
+                    );
         }
         return namingTagsForRelations;
@@ -116,12 +114,12 @@
      * @return the decorated name
      */
-    protected String decorateNameWithId(String name, IPrimitive primitive) {
-        if (Main.pref.getBoolean("osm-primitives.showid"))
-            if (Main.pref.getBoolean("osm-primitives.showid.new-primitives"))
-                return name + tr(" [id: {0}]", primitive.getUniqueId());
-            else
-                return name + tr(" [id: {0}]", primitive.getId());
-        else
-            return name;
+    protected void decorateNameWithId(StringBuilder name, IPrimitive primitive) {
+        if (Main.pref.getBoolean("osm-primitives.showid")) {
+            if (Main.pref.getBoolean("osm-primitives.showid.new-primitives")) {
+                name.append(tr(" [id: {0}]", primitive.getUniqueId()));
+            } else {
+                name.append(tr(" [id: {0}]", primitive.getId()));
+            }
+        }
     }
 
@@ -132,50 +130,58 @@
      * @return the name
      */
-    public String format(INode node) {
-        String name = "";
+    public String format(Node node) {
+        StringBuilder name = new StringBuilder();
         if (node.isIncomplete()) {
-            name = tr("incomplete");
-        } else {
-            if (Main.pref.getBoolean("osm-primitives.localize-name", true)) {
-                name = node.getLocalName();
+            name.append(tr("incomplete"));
+        } else {
+            TaggingPreset preset = TaggingPresetNameTemplateList.getInstance().findPresetTemplate(node);
+            if (preset == null) {
+                String n;
+                if (Main.pref.getBoolean("osm-primitives.localize-name", true)) {
+                    n = node.getLocalName();
+                } else {
+                    n = node.getName();
+                }
+                if(n == null)
+                {
+                    String s;
+                    if((s = node.get("addr:housename")) != null) {
+                        /* I18n: name of house as parameter */
+                        n = tr("House {0}", s);
+                    }
+                    if(n == null && (s = node.get("addr:housenumber")) != null) {
+                        String t = node.get("addr:street");
+                        if(t != null) {
+                            /* I18n: house number, street as parameter, number should remain
+                        before street for better visibility */
+                            n =  tr("House number {0} at {1}", s, t);
+                        }
+                        else {
+                            /* I18n: house number as parameter */
+                            n = tr("House number {0}", s);
+                        }
+                    }
+                }
+
+                if (n == null) {
+                    n = node.isNew() ? tr("node") : ""+ node.getId();
+                }
+                name.append(n);
             } else {
-                name = node.getName();
-            }
-            if(name == null)
-            {
-                String s;
-                if((s = node.get("addr:housename")) != null) {
-                    /* I18n: name of house as parameter */
-                    name = tr("House {0}", s);
-                }
-                if(name == null && (s = node.get("addr:housenumber")) != null) {
-                    String t = node.get("addr:street");
-                    if(t != null) {
-                        /* I18n: house number, street as parameter, number should remain
-                        before street for better visibility */
-                        name =  tr("House number {0} at {1}", s, t);
-                    }
-                    else {
-                        /* I18n: house number as parameter */
-                        name = tr("House number {0}", s);
-                    }
-                }
-            }
-
-            if (name == null) {
-                name = node.isNew() ? tr("node") : ""+ node.getId();
-            }
-            name += " \u200E(" + node.getCoor().latToString(CoordinateFormat.getDefaultFormat()) + ", " + node.getCoor().lonToString(CoordinateFormat.getDefaultFormat()) + ")";
-        }
-        name = decorateNameWithId(name, node);
-
+                preset.nameTemplate.appendText(name, node);
+            }
+            name.append(" \u200E(").append(node.getCoor().latToString(CoordinateFormat.getDefaultFormat())).append(", ").append(node.getCoor().lonToString(CoordinateFormat.getDefaultFormat())).append(")");
+        }
+        decorateNameWithId(name, node);
+
+
+        String result = name.toString();
         for (NameFormatterHook hook: formatHooks) {
-            String hookResult = hook.checkFormat(node, name);
-            if (hookResult != null) {
+            String hookResult = hook.checkFormat(node, result);
+            if (hookResult != null)
                 return hookResult;
-            }
-        }
-
-        return name;
+        }
+
+        return result;
     }
 
@@ -198,43 +204,54 @@
      * @return the name
      */
-    public String format(IWay way) {
-        String name = "";
+    public String format(Way way) {
+        StringBuilder name = new StringBuilder();
         if (way.isIncomplete()) {
-            name = tr("incomplete");
-        } else {
-            if (Main.pref.getBoolean("osm-primitives.localize-name", true)) {
-                name = way.getLocalName();
+            name.append(tr("incomplete"));
+        } else {
+            TaggingPreset preset = TaggingPresetNameTemplateList.getInstance().findPresetTemplate(way);
+            if (preset == null) {
+                String n;
+                if (Main.pref.getBoolean("osm-primitives.localize-name", true)) {
+                    n = way.getLocalName();
+                } else {
+                    n = way.getName();
+                }
+                if (n == null) {
+                    n = way.get("ref");
+                }
+                if (n == null) {
+                    n =
+                            (way.get("highway") != null) ? tr("highway") :
+                                (way.get("railway") != null) ? tr("railway") :
+                                    (way.get("waterway") != null) ? tr("waterway") :
+                                        (way.get("landuse") != null) ? tr("landuse") : null;
+                }
+                if(n == null)
+                {
+                    String s;
+                    if((s = way.get("addr:housename")) != null) {
+                        /* I18n: name of house as parameter */
+                        n = tr("House {0}", s);
+                    }
+                    if(n == null && (s = way.get("addr:housenumber")) != null) {
+                        String t = way.get("addr:street");
+                        if(t != null) {
+                            /* I18n: house number, street as parameter, number should remain
+                        before street for better visibility */
+                            n =  tr("House number {0} at {1}", s, t);
+                        }
+                        else {
+                            /* I18n: house number as parameter */
+                            n = tr("House number {0}", s);
+                        }
+                    }
+                }
+                if(n == null || n.length() == 0) {
+                    n = String.valueOf(way.getId());
+                }
+
+                name.append(n);
             } else {
-                name = way.getName();
-            }
-            if (name == null) {
-                name = way.get("ref");
-            }
-            if (name == null) {
-                name =
-                    (way.get("highway") != null) ? tr("highway") :
-                        (way.get("railway") != null) ? tr("railway") :
-                            (way.get("waterway") != null) ? tr("waterway") :
-                                (way.get("landuse") != null) ? tr("landuse") : null;
-            }
-            if(name == null)
-            {
-                String s;
-                if((s = way.get("addr:housename")) != null) {
-                    /* I18n: name of house as parameter */
-                    name = tr("House {0}", s);
-                }
-                if(name == null && (s = way.get("addr:housenumber")) != null) {
-                    String t = way.get("addr:street");
-                    if(t != null) {
-                        /* I18n: house number, street as parameter, number should remain
-                        before street for better visibility */
-                        name =  tr("House number {0} at {1}", s, t);
-                    }
-                    else {
-                        /* I18n: house number as parameter */
-                        name = tr("House number {0}", s);
-                    }
-                }
+                preset.nameTemplate.appendText(name, way);
             }
 
@@ -242,7 +259,4 @@
             if (nodesNo > 1 && way.isClosed()) {
                 nodesNo--;
-            }
-            if(name == null || name.length() == 0) {
-                name = String.valueOf(way.getId());
             }
             /* note: length == 0 should no longer happen, but leave the bracket code
@@ -250,16 +264,16 @@
             /* I18n: count of nodes as parameter */
             String nodes = trn("{0} node", "{0} nodes", nodesNo, nodesNo);
-            name += (name.length() > 0) ? " ("+nodes+")" : nodes;
-        }
-        name = decorateNameWithId(name, way);
-        
+            name.append(" (").append(nodes).append(")");
+        }
+        decorateNameWithId(name, way);
+
+        String result = name.toString();
         for (NameFormatterHook hook: formatHooks) {
-            String hookResult = hook.checkFormat(way, name);
-            if (hookResult != null) {
+            String hookResult = hook.checkFormat(way, result);
+            if (hookResult != null)
                 return hookResult;
-            }
-        }
-
-        return name;
+        }
+
+        return result;
     }
 
@@ -282,10 +296,37 @@
      * @return the name
      */
-    public String format(IRelation relation) {
-        String name;
+    public String format(Relation relation) {
+        StringBuilder name = new StringBuilder();
         if (relation.isIncomplete()) {
-            name = tr("incomplete");
-        } else {
-            name = getRelationTypeName(relation);
+            name.append(tr("incomplete"));
+        } else {
+            TaggingPreset preset = TaggingPresetNameTemplateList.getInstance().findPresetTemplate(relation);
+
+            formatRelationNameAndType(relation, name, preset);
+
+            int mbno = relation.getMembersCount();
+            name.append(trn("{0} member", "{0} members", mbno, mbno));
+
+            if (relation.hasIncompleteMembers()) {
+                name.append(", ").append(tr("incomplete"));
+            }
+
+            name.append(")");
+        }
+        decorateNameWithId(name, relation);
+
+        String result = name.toString();
+        for (NameFormatterHook hook: formatHooks) {
+            String hookResult = hook.checkFormat(relation, result);
+            if (hookResult != null)
+                return hookResult;
+        }
+
+        return result;
+    }
+
+    private void formatRelationNameAndType(Relation relation, StringBuilder result, TaggingPreset preset) {
+        if (preset == null) {
+            result.append(getRelationTypeName(relation));
             String relationName = getRelationName(relation);
             if (relationName == null) {
@@ -294,27 +335,9 @@
                 relationName = "\"" + relationName + "\"";
             }
-            name += " (" + relationName + ", ";
-
-            int mbno = relation.getMembersCount();
-            name += trn("{0} member", "{0} members", mbno, mbno);
-
-            if (relation instanceof Relation) {
-                if (((Relation) relation).hasIncompleteMembers()) {
-                    name += ", "+tr("incomplete");
-                }
-            }
-
-            name += ")";
-        }
-        name = decorateNameWithId(name, relation);
-
-        for (NameFormatterHook hook: formatHooks) {
-            String hookResult = hook.checkFormat(relation, name);
-            if (hookResult != null) {
-                return hookResult;
-            }
-        }
-
-        return name;
+            result.append(" (").append(relationName).append(", ");
+        } else {
+            preset.nameTemplate.appendText(result, relation);
+            result.append("(");
+        }
     }
 
@@ -322,47 +345,65 @@
         @Override
         public int compare(Relation r1, Relation r2) {
-            String type1 = getRelationTypeName(r1);
-            String type2 = getRelationTypeName(r2);
-
-            int comp = type1.compareTo(type2);
-            if (comp != 0)
-                return comp;
-
-            String name1 = getRelationName(r1);
-            String name2 = getRelationName(r2);
-
-            if (name1 == null && name2 == null)
-                return (r1.getUniqueId() > r2.getUniqueId())?1:-1;
-            else if (name1 == null)
-                return -1;
-            else if (name2 == null)
-                return 1;
-            else if (!name1.isEmpty() && !name2.isEmpty() && Character.isDigit(name1.charAt(0)) && Character.isDigit(name2.charAt(0))) {
-                //Compare numerically
-                String ln1 = getLeadingNumber(name1);
-                String ln2 = getLeadingNumber(name2);
-
-                comp = Long.valueOf(ln1).compareTo(Long.valueOf(ln2));
-                if (comp != 0)
-                    return comp;
-
-                // put 1 before 0001
-                comp = ln1.compareTo(ln2);
-                if (comp != 0)
-                    return comp;
-
-                comp = name1.substring(ln1.length()).compareTo(name2.substring(ln2.length()));
+            //TODO This doesn't work correctly with formatHooks
+
+            TaggingPreset preset1 = TaggingPresetNameTemplateList.getInstance().findPresetTemplate(r1);
+            TaggingPreset preset2 = TaggingPresetNameTemplateList.getInstance().findPresetTemplate(r2);
+
+            if (preset1 != null || preset2 != null) {
+                StringBuilder name1 = new StringBuilder();
+                formatRelationNameAndType(r1, name1, preset1);
+                StringBuilder name2 = new StringBuilder();
+                formatRelationNameAndType(r2, name2, preset2);
+
+                int comp = name1.toString().compareTo(name2.toString());
                 if (comp != 0)
                     return comp;
             } else {
-                comp = name1.compareToIgnoreCase(name2);
+
+                String type1 = getRelationTypeName(r1);
+                String type2 = getRelationTypeName(r2);
+
+                int comp = type1.compareTo(type2);
                 if (comp != 0)
                     return comp;
-            }
+
+                String name1 = getRelationName(r1);
+                String name2 = getRelationName(r2);
+
+                if (name1 == null && name2 == null)
+                    return (r1.getUniqueId() > r2.getUniqueId())?1:-1;
+                else if (name1 == null)
+                    return -1;
+                else if (name2 == null)
+                    return 1;
+                else if (!name1.isEmpty() && !name2.isEmpty() && Character.isDigit(name1.charAt(0)) && Character.isDigit(name2.charAt(0))) {
+                    //Compare numerically
+                    String ln1 = getLeadingNumber(name1);
+                    String ln2 = getLeadingNumber(name2);
+
+                    comp = Long.valueOf(ln1).compareTo(Long.valueOf(ln2));
+                    if (comp != 0)
+                        return comp;
+
+                    // put 1 before 0001
+                    comp = ln1.compareTo(ln2);
+                    if (comp != 0)
+                        return comp;
+
+                    comp = name1.substring(ln1.length()).compareTo(name2.substring(ln2.length()));
+                    if (comp != 0)
+                        return comp;
+                } else {
+                    comp = name1.compareToIgnoreCase(name2);
+                    if (comp != 0)
+                        return comp;
+                }
+            }
+
 
             if (r1.getMembersCount() != r2.getMembersCount())
                 return (r1.getMembersCount() > r2.getMembersCount())?1:-1;
 
-            comp = Boolean.valueOf(r1.hasIncompleteMembers()).compareTo(Boolean.valueOf(r2.hasIncompleteMembers()));
+            int comp = Boolean.valueOf(r1.hasIncompleteMembers()).compareTo(Boolean.valueOf(r2.hasIncompleteMembers()));
             if (comp != 0)
                 return comp;
@@ -408,10 +449,9 @@
             name += "["+admin_level+"]";
         }
-        
+
         for (NameFormatterHook hook: formatHooks) {
             String hookResult = hook.checkRelationTypeName(relation, name);
-            if (hookResult != null) {
+            if (hookResult != null)
                 return hookResult;
-            }
         }
 
@@ -562,5 +602,5 @@
                             (way.get("waterway") != null) ? tr("waterway") :
                                 (way.get("landuse") != null) ? tr("landuse") : ""
-            );
+                    );
         }
 
Index: trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/Marker.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/Marker.java	(revision 4430)
+++ trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/Marker.java	(revision 4431)
@@ -378,6 +378,6 @@
 
     @Override
-    public List<String> getTemplateKeys() {
-        List<String> result;
+    public Collection<String> getTemplateKeys() {
+        Collection<String> result;
         if (dataProvider != null) {
             result = dataProvider.getTemplateKeys();
@@ -401,5 +401,5 @@
 
     @Override
-    public Object getTemplateValue(String name) {
+    public Object getTemplateValue(String name, boolean special) {
         if (MARKER_FORMATTED_OFFSET.equals(name))
             return formatOffset();
@@ -407,5 +407,5 @@
             return offset;
         else if (dataProvider != null)
-            return dataProvider.getTemplateValue(name);
+            return dataProvider.getTemplateValue(name, special);
         else
             return null;
Index: trunk/src/org/openstreetmap/josm/gui/preferences/GPXSettingsPanel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/preferences/GPXSettingsPanel.java	(revision 4430)
+++ trunk/src/org/openstreetmap/josm/gui/preferences/GPXSettingsPanel.java	(revision 4431)
@@ -33,5 +33,5 @@
     private static final int WAYPOINT_LABEL_CUSTOM = 6;
     private static final String[] LABEL_PATTERN_TEMPLATE = new String[] {Marker.LABEL_PATTERN_AUTO, Marker.LABEL_PATTERN_NAME,
-        Marker.LABEL_PATTERN_DESC, "{*}", "?{ '{name}' | '{desc}' | '{formattedWaypointOffset}' }", ""};
+        Marker.LABEL_PATTERN_DESC, "{special:everything}", "?{ '{name}' | '{desc}' | '{formattedWaypointOffset}' }", ""};
     private static final String[] LABEL_PATTERN_DESC = new String[] {tr("Auto"), /* gpx data field name */ trc("gpx_field", "Name"),
         /* gpx data field name */ trc("gpx_field", "Desc(ription)"), tr("Everything"), tr("Name or offset"), tr("None"), tr("Custom")};
Index: trunk/src/org/openstreetmap/josm/gui/tagging/TaggingPreset.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/tagging/TaggingPreset.java	(revision 4430)
+++ trunk/src/org/openstreetmap/josm/gui/tagging/TaggingPreset.java	(revision 4431)
@@ -47,4 +47,6 @@
 
 import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.search.SearchCompiler;
+import org.openstreetmap.josm.actions.search.SearchCompiler.Match;
 import org.openstreetmap.josm.command.ChangePropertyCommand;
 import org.openstreetmap.josm.command.Command;
@@ -76,4 +78,7 @@
 import org.openstreetmap.josm.tools.UrlLabel;
 import org.openstreetmap.josm.tools.XmlObjectParser;
+import org.openstreetmap.josm.tools.template_engine.ParseError;
+import org.openstreetmap.josm.tools.template_engine.TemplateEntry;
+import org.openstreetmap.josm.tools.template_engine.TemplateParser;
 import org.xml.sax.SAXException;
 
@@ -121,7 +126,6 @@
         protected void initAutoCompletionField(AutoCompletingTextField field, String key) {
             OsmDataLayer layer = Main.main.getEditLayer();
-            if (layer == null) {
+            if (layer == null)
                 return;
-            }
             AutoCompletionList list = new AutoCompletionList();
             Main.main.getEditLayer().data.getAutoCompletionManager().populateWithTagValues(list, key);
@@ -303,18 +307,17 @@
             String v = (value instanceof JComboBox)
                     ? ((JComboBox) value).getEditor().getItem().toString()
-                    : ((JTextField) value).getText();
-            v = v.trim();
-
-            if (!"false".equals(use_last_as_default)) {
-                lastValue.put(key, v);
-            }
-            if (v.equals(originalValue) || (originalValue == null && v.length() == 0)) {
-                return;
-            }
-
-            if (delete_if_empty && v.length() == 0) {
-                v = null;
-            }
-            changedTags.add(new Tag(key, v));
+                            : ((JTextField) value).getText();
+                    v = v.trim();
+
+                    if (!"false".equals(use_last_as_default)) {
+                        lastValue.put(key, v);
+                    }
+                    if (v.equals(originalValue) || (originalValue == null && v.length() == 0))
+                        return;
+
+                    if (delete_if_empty && v.length() == 0) {
+                        v = null;
+                    }
+                    changedTags.add(new Tag(key, v));
         }
 
@@ -491,10 +494,10 @@
                 e.display_value = (locale_display_values == null)
                         ? (values_context == null ? tr(fixPresetString(display_array[i]))
-                        : trc(values_context, fixPresetString(display_array[i]))) : display_array[i];
-                if (short_descriptions_array != null) {
-                    e.short_description = locale_short_descriptions == null ? tr(fixPresetString(short_descriptions_array[i]))
-                            : fixPresetString(short_descriptions_array[i]);
-                }
-                lhm.put(value_array[i], e);
+                                : trc(values_context, fixPresetString(display_array[i]))) : display_array[i];
+                        if (short_descriptions_array != null) {
+                            e.short_description = locale_short_descriptions == null ? tr(fixPresetString(short_descriptions_array[i]))
+                                    : fixPresetString(short_descriptions_array[i]);
+                        }
+                        lhm.put(value_array[i], e);
             }
 
@@ -544,10 +547,8 @@
             // no change if same as before
             if (originalValue == null) {
-                if (value.length() == 0) {
+                if (value.length() == 0)
                     return;
-                }
-            } else if (value.equals(originalValue.toString())) {
+            } else if (value.equals(originalValue.toString()))
                 return;
-            }
 
             if (delete_if_empty && value.length() == 0) {
@@ -676,9 +677,8 @@
         @Override
         protected String getDisplayIfNull(String display) {
-            if (combo.isEditable()) {
+            if (combo.isEditable())
                 return combo.getEditor().getItem().toString();
-            } else {
+            else
                 return display;
-            }
 
         }
@@ -1008,4 +1008,6 @@
     public EnumSet<PresetType> types;
     public List<Item> data = new LinkedList<Item>();
+    public TemplateEntry nameTemplate;
+    public Match nameTemplateFilter;
     private static HashMap<String,String> lastValue = new HashMap<String,String>();
 
@@ -1088,4 +1090,23 @@
         this.types = getType(types);
     }
+
+    public void setName_template(String pattern) throws SAXException {
+        try {
+            this.nameTemplate = new TemplateParser(pattern).parse();
+        } catch (ParseError e) {
+            System.err.println("Error while parsing " + pattern + ": " + e.getMessage());
+            throw new SAXException(e);
+        }
+    }
+
+    public void setName_template_filter(String filter) throws SAXException {
+        try {
+            this.nameTemplateFilter = SearchCompiler.compile(filter, false, false);
+        } catch (org.openstreetmap.josm.actions.search.SearchCompiler.ParseError e) {
+            System.err.println("Error while parsing" + filter + ": " + e.getMessage());
+            throw new SAXException(e);
+        }
+    }
+
 
     public static List<TaggingPreset> readAll(Reader in, boolean validate) throws SAXException {
@@ -1199,5 +1220,5 @@
                         tr("Error"),
                         JOptionPane.ERROR_MESSAGE
-                );
+                        );
             } catch (SAXException e) {
                 System.err.println(e.getMessage());
@@ -1209,5 +1230,5 @@
                         tr("Error"),
                         JOptionPane.ERROR_MESSAGE
-                );
+                        );
             }
             zipIcons = null;
Index: trunk/src/org/openstreetmap/josm/tools/TaggingPresetNameTemplateList.java
===================================================================
--- trunk/src/org/openstreetmap/josm/tools/TaggingPresetNameTemplateList.java	(revision 4431)
+++ trunk/src/org/openstreetmap/josm/tools/TaggingPresetNameTemplateList.java	(revision 4431)
@@ -0,0 +1,116 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.tools;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.gui.preferences.TaggingPresetPreference;
+import org.openstreetmap.josm.gui.tagging.TaggingPreset;
+import org.openstreetmap.josm.gui.tagging.TaggingPreset.Check;
+import org.openstreetmap.josm.gui.tagging.TaggingPreset.Combo;
+import org.openstreetmap.josm.gui.tagging.TaggingPreset.PresetType;
+import org.openstreetmap.josm.gui.tagging.TaggingPreset.Text;
+
+/**
+ * List of tagging presets with name templates, allows to find appropriate template based on existing primitive
+ */
+public class TaggingPresetNameTemplateList {
+
+    private static TaggingPresetNameTemplateList instance;
+
+    public static TaggingPresetNameTemplateList getInstance() {
+        if (instance == null) {
+            instance = new TaggingPresetNameTemplateList();
+        }
+        return instance;
+    }
+
+    private final List<TaggingPreset> presetsWithPattern = new ArrayList<TaggingPreset>();
+
+    private TaggingPresetNameTemplateList() {
+        for (TaggingPreset tp: TaggingPresetPreference.taggingPresets) {
+            if (tp.nameTemplate != null) {
+                presetsWithPattern.add(tp);
+            }
+        }
+    }
+
+    public TaggingPreset findPresetTemplate(OsmPrimitive primitive) {
+
+        PresetType presetType;
+        switch (primitive.getType()) {
+        case NODE:
+            presetType = PresetType.NODE;
+            break;
+        case WAY:
+            if (((Way) primitive).isClosed()) {
+                presetType = PresetType.CLOSEDWAY;
+            } else {
+                presetType = PresetType.WAY;
+            }
+            break;
+        case RELATION:
+            presetType = PresetType.RELATION;
+            break;
+        default:
+            throw new AssertionError();
+        }
+
+        for(TaggingPreset t : presetsWithPattern) {
+
+
+            if (       t.types == null
+                    || t.types.contains(presetType)
+                    || (presetType == PresetType.CLOSEDWAY && t.types.contains(PresetType.WAY))) {
+                int found = 0;
+
+                if (t.nameTemplateFilter != null) {
+                    if (t.nameTemplateFilter.match(primitive))
+                        return t;
+                    else {
+                        continue;
+                    }
+                }
+
+                for(TaggingPreset.Item i : t.data) {
+                    if(i instanceof TaggingPreset.Key) {
+                        String val = ((TaggingPreset.Key)i).value;
+                        String key = ((TaggingPreset.Key)i).key;
+                        // we subtract 100 if not found and add 1 if found
+                        if (val != null && val.equals(primitive.get(key))) {
+                            found+=1;
+                        } else {
+                            found-=100;
+                        }
+                    } else {
+                        String key = null;
+                        if ((i instanceof Text) && ((Text)i).required) {
+                            key = ((Text)i).key;
+                        } else if ((i instanceof Combo) && ((Combo)i).required) {
+                            key = ((Combo)i).key;
+                        } else if ((i instanceof Check) && ((Check)i).required) {
+                            key = ((Check)i).key;
+                        }
+                        if (key != null) {
+                            if (primitive.get(key) != null) {
+                                found += 1;
+                            } else {
+                                found -= 100;
+                            }
+                        }
+                    }
+                }
+
+                if(found > 0)
+                    return t; // First matching preset wins
+            }
+        }
+
+        return null;
+
+    }
+
+
+}
Index: trunk/src/org/openstreetmap/josm/tools/template_engine/TemplateEngineDataProvider.java
===================================================================
--- trunk/src/org/openstreetmap/josm/tools/template_engine/TemplateEngineDataProvider.java	(revision 4430)
+++ trunk/src/org/openstreetmap/josm/tools/template_engine/TemplateEngineDataProvider.java	(revision 4431)
@@ -2,11 +2,11 @@
 package org.openstreetmap.josm.tools.template_engine;
 
-import java.util.List;
+import java.util.Collection;
 
 import org.openstreetmap.josm.actions.search.SearchCompiler.Match;
 
 public interface TemplateEngineDataProvider {
-    List<String> getTemplateKeys();
-    Object getTemplateValue(String name);
+    Collection<String> getTemplateKeys();
+    Object getTemplateValue(String name, boolean special);
     boolean evaluateCondition(Match condition);
 }
Index: trunk/src/org/openstreetmap/josm/tools/template_engine/Variable.java
===================================================================
--- trunk/src/org/openstreetmap/josm/tools/template_engine/Variable.java	(revision 4430)
+++ trunk/src/org/openstreetmap/josm/tools/template_engine/Variable.java	(revision 4431)
@@ -2,19 +2,31 @@
 package org.openstreetmap.josm.tools.template_engine;
 
-import java.util.List;
+import java.util.Collection;
 
 
 public class Variable implements TemplateEntry {
 
+    private static final String SPECIAL_VARIABLE_PREFIX = "special:";
+    private static final String SPECIAL_VALUE_EVERYTHING = "everything";
+
+
     private final String variableName;
+    private final boolean special;
 
     public Variable(String variableName) {
-        this.variableName = variableName;
+        if (variableName.toLowerCase().startsWith(SPECIAL_VARIABLE_PREFIX)) {
+            this.variableName = variableName.substring(SPECIAL_VARIABLE_PREFIX.length());
+            // special:special:key means that real key named special:key is needed, not special variable
+            this.special = !this.variableName.toLowerCase().startsWith(SPECIAL_VARIABLE_PREFIX);
+        } else {
+            this.variableName = variableName;
+            this.special = false;
+        }
     }
 
     @Override
     public void appendText(StringBuilder result, TemplateEngineDataProvider dataProvider) {
-        if ("*".equals(variableName)) {
-            List<String> keys = dataProvider.getTemplateKeys();
+        if (special && SPECIAL_VALUE_EVERYTHING.equals(variableName)) {
+            Collection<String> keys = dataProvider.getTemplateKeys();
             boolean first = true;
             for (String key: keys) {
@@ -24,8 +36,8 @@
                     first = false;
                 }
-                result.append(key).append("=").append(dataProvider.getTemplateValue(key));
+                result.append(key).append("=").append(dataProvider.getTemplateValue(key, false));
             }
         } else {
-            Object value = dataProvider.getTemplateValue(variableName);
+            Object value = dataProvider.getTemplateValue(variableName, special);
             if (value != null) {
                 result.append(value);
@@ -36,8 +48,8 @@
     @Override
     public boolean isValid(TemplateEngineDataProvider dataProvider) {
-        if ("*".equals(variableName))
+        if (special && SPECIAL_VALUE_EVERYTHING.equals(variableName))
             return true;
         else
-            return dataProvider.getTemplateValue(variableName) != null;
+            return dataProvider.getTemplateValue(variableName, special) != null;
     }
 
@@ -47,3 +59,7 @@
     }
 
+    public boolean isSpecial() {
+        return special;
+    }
+
 }
Index: trunk/test/unit/org/openstreetmap/josm/tools/template_engine/TemplateEngineTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/tools/template_engine/TemplateEngineTest.java	(revision 4430)
+++ trunk/test/unit/org/openstreetmap/josm/tools/template_engine/TemplateEngineTest.java	(revision 4431)
@@ -7,10 +7,19 @@
 
 import org.junit.Assert;
+import org.junit.BeforeClass;
 import org.junit.Test;
+import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.actions.search.SearchCompiler;
 import org.openstreetmap.josm.actions.search.SearchCompiler.Match;
+import org.openstreetmap.josm.data.Preferences;
+import org.openstreetmap.josm.data.osm.Relation;
 import org.unitils.reflectionassert.ReflectionAssert;
 
 public class TemplateEngineTest {
+
+    @BeforeClass
+    public static void before() {
+        Main.pref = new Preferences();
+    }
 
     @Test
@@ -62,11 +71,20 @@
     TemplateEngineDataProvider dataProvider = new TemplateEngineDataProvider() {
         @Override
-        public Object getTemplateValue(String name) {
-            if ("name".equals(name))
-                return "waypointName";
-            else if ("number".equals(name))
-                return 10;
-            else
-                return null;
+        public Object getTemplateValue(String name, boolean special) {
+            if (special) {
+                if ("localName".equals(name))
+                    return "localName";
+                else
+                    return null;
+            } else {
+                if ("name".equals(name))
+                    return "waypointName";
+                else if ("number".equals(name))
+                    return 10;
+                else if ("special:key".equals(name))
+                    return "specialKey";
+                else
+                    return null;
+            }
         }
         @Override
@@ -90,6 +108,23 @@
 
     @Test
+    public void testFillingSearchExpression() throws Exception {
+        TemplateParser parser = new TemplateParser("?{ admin_level = 2 'NUTS 1' | admin_level = 4 'NUTS 2' |  '{admin_level}'}");
+        TemplateEntry templateEntry = parser.parse();
+
+        StringBuilder sb = new StringBuilder();
+        Relation r = new Relation();
+        r.put("admin_level", "2");
+        templateEntry.appendText(sb, r);
+        Assert.assertEquals("NUTS 1", sb.toString());
+
+        sb.setLength(0);
+        r.put("admin_level", "5");
+        templateEntry.appendText(sb, r);
+        Assert.assertEquals("5", sb.toString());
+    }
+
+    @Test
     public void testPrintAll() throws Exception {
-        TemplateParser parser = new TemplateParser("{*}");
+        TemplateParser parser = new TemplateParser("{special:everything}");
         TemplateEntry entry = parser.parse();
         StringBuilder sb = new StringBuilder();
@@ -107,4 +142,15 @@
     }
 
+    @Test
+    public void testSpecialVariable() throws Exception {
+        TemplateParser parser = new TemplateParser("{name}u{special:localName}u{special:special:key}");
+        TemplateEntry templateEntry = parser.parse();
+
+        StringBuilder sb = new StringBuilder();
+        templateEntry.appendText(sb, dataProvider);
+        Assert.assertEquals("waypointNameulocalNameuspecialKey", sb.toString());
+
+    }
+
 
 }
