Index: resources/styles/default/points
===================================================================
--- resources/styles/default/points	(revision 2796)
+++ resources/styles/default/points	(working copy)
@@ -8,8 +8,6 @@
 # and http://wiki.openstreetmap.org/wiki/Mkgmap/help/Custom_styles
 # for more information.
 
-include 'inc/address';
-
 barrier=bollard | barrier=bus_trap
     { add mkgmap:bicycle='${bicycle|subst:private=>no|def:yes}'; add mkgmap:foot='${foot|subst:private=>no|def:yes}'; addaccess no }
 barrier=block | barrier=cycle_barrier | barrier=stile | barrier=kissing_gate
@@ -273,3 +271,8 @@
 
 include 'inc/water_points';
 include 'inc/landuse_points';
+
+<finalize>
+# The finalizer section is executed for each element when a rule with an element type matches
+
+include 'inc/address';
\ No newline at end of file
Index: resources/styles/default/polygons
===================================================================
--- resources/styles/default/polygons	(revision 2796)
+++ resources/styles/default/polygons	(working copy)
@@ -8,12 +8,6 @@
 # and http://wiki.openstreetmap.org/wiki/Mkgmap/help/Custom_styles
 # for more information.
 
-# polygons do not have address information in the Garmin map format
-# so as long as the address information is not required in a stlye rule
-# it is not required to include the address rules 
-# include 'inc/address';
-
-
 leisure=* & sport=* & name=* { set name='${name} (${sport})' }
 leisure=* & sport=* & name!=* { add name='${sport}' }
 
@@ -80,3 +74,7 @@
 
 include 'inc/water_polygons';
 include 'inc/landuse_polygons';
+
+<finalize>
+# The finalizer section is executed for each element when a rule with an element type matches
+
Index: resources/styles/default/lines
===================================================================
--- resources/styles/default/lines	(revision 2796)
+++ resources/styles/default/lines	(working copy)
@@ -8,7 +8,6 @@
 # and http://wiki.openstreetmap.org/wiki/Mkgmap/help/Custom_styles
 # for more information.
 
-include 'inc/address';
 include 'inc/refs';
 
 aeroway=runway [0x27 resolution 20]
@@ -98,11 +97,6 @@
 (man_made=pier | man_made=piste:halfpipe) & area!=yes
 {add highway=footway; name '${ref} ${name}' | '${ref}' | '${name}' }
 
-# calculate the road speed based on maxspeed tag
-include 'inc/roadspeed'; 
-# calculate the access rules
-include 'inc/access';
-
 # Roundabouts
 junction=roundabout & highway=trunk [0x0c road_class=3 road_speed=2 resolution 18]
 junction=roundabout & highway=primary [0x0c road_class=3 road_speed=2 resolution 19]
@@ -192,3 +186,13 @@
 
 include 'inc/water_lines';
 include 'inc/contour_lines';
+
+<finalize>
+# The finalizer section is executed for each element when a rule with an element type matches
+
+include 'inc/address';
+# calculate the road speed based on maxspeed tag
+include 'inc/roadspeed'; 
+# calculate the access rules
+include 'inc/access';
+
Index: src/uk/me/parabola/mkgmap/main/StyleTester.java
===================================================================
--- src/uk/me/parabola/mkgmap/main/StyleTester.java	(revision 2796)
+++ src/uk/me/parabola/mkgmap/main/StyleTester.java	(working copy)
@@ -29,7 +29,6 @@
 import java.util.Formatter;
 import java.util.List;
 import java.util.Locale;
-import java.util.Properties;
 import java.util.Set;
 import java.util.regex.Pattern;
 
@@ -75,6 +74,7 @@
 import uk.me.parabola.mkgmap.reader.osm.xml.Osm5XmlHandler;
 import uk.me.parabola.mkgmap.reader.osm.xml.Osm5XmlHandler.SaxHandler;
 import uk.me.parabola.mkgmap.scan.SyntaxException;
+import uk.me.parabola.mkgmap.scan.Token;
 import uk.me.parabola.mkgmap.scan.TokenScanner;
 import uk.me.parabola.util.EnhancedProperties;
 
@@ -621,9 +621,11 @@
 		 */
 		private class ReferenceRuleSet implements Rule {
 			private final List<Rule> rules = new ArrayList<Rule>();
-
+			private Rule finalizeRule = null;
+			
 			public void add(Rule rule) {
 				rules.add(rule);
+				rule.setFinalizeRule(finalizeRule);
 			}
 
 			public void addAll(ReferenceRuleSet rs) {
@@ -665,6 +667,13 @@
 				}
 				return sb.toString();
 			}
+
+			public void setFinalizeRule(Rule finalizeRule) {
+				for (Rule rule : rules) {
+					rule.setFinalizeRule(finalizeRule);
+				}
+				this.finalizeRule = finalizeRule;
+			}
 		}
 
 		/**
@@ -679,6 +688,7 @@
 
 			private final ReferenceRuleSet rules;
 			private TokenScanner scanner;
+			private boolean inFinalizeSection = false;
 
 			public SimpleRuleFileReader(FeatureKind kind, LevelInfo[] levels, ReferenceRuleSet rules) {
 				this.rules = rules;
@@ -706,6 +716,9 @@
 				// Read all the rules in the file.
 				scanner.skipSpace();
 				while (!scanner.isEndOfFile()) {
+					if (checkCommand(scanner))
+						continue;
+					
 					Op expr = expressionReader.readConditions();
 
 					ActionList actions = actionReader.readActions();
@@ -722,6 +735,33 @@
 				}
 			}
 
+			private boolean checkCommand(TokenScanner scanner) {
+				scanner.skipSpace();
+				if (scanner.isEndOfFile())
+					return false;
+
+				if (inFinalizeSection == false && scanner.checkToken("<")) {
+					Token token = scanner.nextToken();
+					if (scanner.checkToken("finalize")) {
+						Token finalizeToken = scanner.nextToken();
+						if (scanner.checkToken(">")) {
+							// consume the > token
+							scanner.nextToken();
+							// mark start of the finalize block
+							inFinalizeSection = true;
+							return true;
+						} else {
+							scanner.pushToken(finalizeToken);
+							scanner.pushToken(token);
+						}
+					} else {
+						scanner.pushToken(token);
+					}
+				}
+				scanner.skipSpace();
+				return false;
+			}
+			
 			/**
 			 * Save the expression as a rule.
 			 */
Index: src/uk/me/parabola/mkgmap/osmstyle/function/TypeFunction.java
===================================================================
--- src/uk/me/parabola/mkgmap/osmstyle/function/TypeFunction.java	(revision 0)
+++ src/uk/me/parabola/mkgmap/osmstyle/function/TypeFunction.java	(revision 0)
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2013.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 or
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+package uk.me.parabola.mkgmap.osmstyle.function;
+
+import uk.me.parabola.mkgmap.reader.osm.Element;
+import uk.me.parabola.mkgmap.reader.osm.Node;
+import uk.me.parabola.mkgmap.reader.osm.Relation;
+import uk.me.parabola.mkgmap.reader.osm.Way;
+
+public class TypeFunction extends StyleFunction {
+
+	public TypeFunction() {
+		super(null);
+	}
+
+	public boolean supportsNode() {
+		return true;
+	}
+
+	public boolean supportsWay() {
+		return true;
+	}
+
+	public boolean supportsRelation() {
+		return true;
+	}
+	
+	public String value(Element el) {
+		if (el instanceof Node)
+			return "node";
+		if (el instanceof Way) 
+			return "way";
+		if (el instanceof Relation)
+			return "relation";
+		return null;
+	}
+
+	public String getName() {
+		return "type";
+	}
+
+}
Index: src/uk/me/parabola/mkgmap/osmstyle/function/FunctionFactory.java
===================================================================
--- src/uk/me/parabola/mkgmap/osmstyle/function/FunctionFactory.java	(revision 2796)
+++ src/uk/me/parabola/mkgmap/osmstyle/function/FunctionFactory.java	(working copy)
@@ -44,6 +44,8 @@
 			return new MaxSpeedFunction(SpeedUnit.KMH);
 		if ("maxspeedmph".equals(name))
 			return new MaxSpeedFunction(SpeedUnit.MPH);
+		if ("type".equals(name))
+			return new TypeFunction();
 		
 		return null;
 	}
Index: src/uk/me/parabola/mkgmap/osmstyle/eval/ExpressionReader.java
===================================================================
--- src/uk/me/parabola/mkgmap/osmstyle/eval/ExpressionReader.java	(revision 2796)
+++ src/uk/me/parabola/mkgmap/osmstyle/eval/ExpressionReader.java	(working copy)
@@ -230,6 +230,9 @@
 		case RELATION:
 			if (function.supportsRelation()) supported = true;
 			break;
+		case ALL:
+			if (function.supportsNode() || function.supportsWay() || function.supportsRelation()) supported = true;
+			break;
 		}
 
 		if (!supported)
Index: src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java
===================================================================
--- src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java	(revision 2796)
+++ src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java	(working copy)
@@ -218,19 +218,29 @@
 		else
 			rules = wayRules;
 		
-		rules.resolveType(way, new TypeResult() {
-			public void add(Element el, GType type) {
-				if (type.isContinueSearch()) {
-					// If not already copied, do so now
-					if (el == way)
-						el = way.copy();
-				}
-				postConvertRules(el, type);
-				addConvertedWay((Way) el, type);
-			}
-		});
+		wayTypeResult.setWay(way);
+		rules.resolveType(way, wayTypeResult);
 	}
 
+	private final WayTypeResult wayTypeResult = new WayTypeResult();
+	private class WayTypeResult implements TypeResult 
+	{
+		private Way way;
+		public void setWay(Way way) {
+			this.way = way;
+		}
+		
+		public void add(Element el, GType type) {
+			if (type.isContinueSearch()) {
+				// If not already copied, do so now
+				if (el == way) 
+					el = way.copy();
+			}
+			postConvertRules(el, type);
+			addConvertedWay((Way) el, type);
+		}
+	}
+	
 	private void addConvertedWay(Way way, GType foundType) {
 		if (foundType.getFeatureKind() == FeatureKind.POLYLINE) {
 		    if(foundType.isRoad() &&
@@ -263,17 +273,27 @@
 
 		housenumberGenerator.addNode(node);
 		
-		nodeRules.resolveType(node, new TypeResult() {
+		nodeTypeResult.setNode(node);
+		nodeRules.resolveType(node, nodeTypeResult);
+	}
+	
+	private NodeTypeResult nodeTypeResult = new NodeTypeResult();
+	private class NodeTypeResult implements TypeResult {
+		private Node node;
+		public void setNode(Node node) {
+			this.node = node;
+		}
+		
 			public void add(Element el, GType type) {
 				if (type.isContinueSearch()) {
 					// If not already copied, do so now
-					if (el == node)
+					if (el == node) 
 						el = node.copy();
 				}
 				postConvertRules(el, type);
 				addPoint((Node) el, type);
 			}
-		});
+		
 	}
 
 	/**
@@ -476,7 +496,6 @@
 		}
 		lines = null;
 		lineTypes = null;
-
 		// add the roads after the other lines
 		for (int i = 0; i < roads.size(); i++){
 			Way road = roads.get(i);
@@ -540,7 +559,6 @@
 			if(nodeId != null && w1 != null && w2 != null)
 				collector.addThroughRoute(nodeId, w1.getId(), w2.getId());
 		}
-
 		// return memory to GC
 		nodeIdMap = null;
 		throughRouteRelations.clear();
Index: src/uk/me/parabola/mkgmap/osmstyle/ActionRule.java
===================================================================
--- src/uk/me/parabola/mkgmap/osmstyle/ActionRule.java	(revision 2796)
+++ src/uk/me/parabola/mkgmap/osmstyle/ActionRule.java	(working copy)
@@ -39,7 +39,15 @@
 	private final Op expression;
 	private final List<Action> actions;
 	private final GType type;
+	private Rule finalizeRule;
 
+	/** Finalize rules must not have an element type definition so the add method must never be called. */
+	private final static TypeResult finalizeTypeResult = new TypeResult() {
+		public void add(Element el, GType type) {
+			throw new UnsupportedOperationException("Finalize rules must not contain an action block.");
+		}
+	};	
+	
 	public ActionRule(Op expression, List<Action> actions, GType type) {
 		assert actions != null;
 		this.expression = expression;
@@ -71,6 +79,10 @@
 		for (Action a : actions)
 			a.perform(element);
 
+		if (type != null && finalizeRule != null)
+			// there is a type so first execute the finalize rules
+			finalizeRule.resolveType(element, finalizeTypeResult);
+		
 		result.add(element, type);
 	}
 
@@ -91,4 +103,8 @@
 
 		return fmt.toString();
 	}
+
+	public void setFinalizeRule(Rule finalizeRule) {
+		this.finalizeRule = finalizeRule;
+	}
 }
Index: src/uk/me/parabola/mkgmap/osmstyle/ExpressionRule.java
===================================================================
--- src/uk/me/parabola/mkgmap/osmstyle/ExpressionRule.java	(revision 2796)
+++ src/uk/me/parabola/mkgmap/osmstyle/ExpressionRule.java	(working copy)
@@ -24,25 +24,43 @@
 
 /**
  * A rule that contains a condition.  If the condition is matched by the
- * element then the held gtype is returned.
+ * element then the finalize rule is executed and the held gtype is returned.
  * 
  * @author Steve Ratcliffe
  */
 public class ExpressionRule implements Rule {
 	private final Op expression;
 	private final GType gtype;
+	private Rule finalizeRule;
 
+	/** Finalize rules must not have an element type definition so the add method must never be called. */
+	private final static TypeResult finalizeTypeResult = new TypeResult() {
+		public void add(Element el, GType type) {
+			throw new UnsupportedOperationException("Finalize rules must not contain an action block.");
+		}
+	};
+	
 	public ExpressionRule(Op expression, GType gtype) {
 		this.expression = expression;
 		this.gtype = gtype;
 	}
 
 	public void resolveType(Element el, TypeResult result) {
-		if (expression.eval(el))
+		if (expression.eval(el)) {
+			// expression matches
+			if (finalizeRule != null) {
+				// run the finalize rules
+				finalizeRule.resolveType(el, finalizeTypeResult);
+			}
 			result.add(el, gtype);
+		}
 	}
 
 	public String toString() {
 		return expression.toString() + ' ' + gtype;
 	}
+
+	public void setFinalizeRule(Rule finalizeRule) {
+		this.finalizeRule = finalizeRule;
+	}
 }
Index: src/uk/me/parabola/mkgmap/osmstyle/RuleSet.java
===================================================================
--- src/uk/me/parabola/mkgmap/osmstyle/RuleSet.java	(revision 2796)
+++ src/uk/me/parabola/mkgmap/osmstyle/RuleSet.java	(working copy)
@@ -151,4 +151,15 @@
 	public void addUsedTags(Collection<String> usedTags) {
 		this.usedTags.addAll(usedTags);
 	}
+
+	public void setFinalizeRule(Rule finalizeRule) {
+		if (rules == null) {
+			// this method must be called after prepare() is called so
+			// that we have rules to which the finalize rules can be applied
+			throw new IllegalStateException("First call prepare() before setting the finalize rules");
+		}
+		for (Rule rule : rules) 
+			rule.setFinalizeRule(finalizeRule);
+		
+	}
 }
Index: src/uk/me/parabola/mkgmap/osmstyle/RuleFileReader.java
===================================================================
--- src/uk/me/parabola/mkgmap/osmstyle/RuleFileReader.java	(revision 2796)
+++ src/uk/me/parabola/mkgmap/osmstyle/RuleFileReader.java	(working copy)
@@ -61,12 +61,17 @@
 	private final TypeReader typeReader;
 
 	private final RuleSet rules;
+	private final RuleSet finalizeRules;
 	private final boolean performChecks;
 	private final Map<Integer, List<Integer>> overlays;
 
-	public RuleFileReader(FeatureKind kind, LevelInfo[] levels, RuleSet rules, boolean performChecks, Map<Integer, List<Integer>> overlays) {
+	private boolean inFinalizeSection = false;
+	
+	public RuleFileReader(FeatureKind kind, LevelInfo[] levels, RuleSet rules, boolean performChecks, 
+			Map<Integer, List<Integer>> overlays) {
 		this.kind = kind;
 		this.rules = rules;
+		this.finalizeRules = new RuleSet();
 		this.performChecks = performChecks;
 		this.overlays = overlays;
 		typeReader = new TypeReader(kind, levels);
@@ -81,6 +86,8 @@
 	public void load(StyleFileLoader loader, String name) throws FileNotFoundException {
 		loadFile(loader, name);
 		rules.prepare();
+		finalizeRules.prepare();
+		rules.setFinalizeRule(finalizeRules);
 	}
 
 	/**
@@ -193,6 +200,25 @@
 				// Wrong syntax for include statement, so push back token to allow a possible expression to be read
 				scanner.pushToken(token);
 			}
+		} 
+		// check if it is the start label of the <finalize> section
+		else if (inFinalizeSection == false && scanner.checkToken("<")) {
+			Token token = scanner.nextToken();
+			if (scanner.checkToken("finalize")) {
+				Token finalizeToken = scanner.nextToken();
+				if (scanner.checkToken(">")) {
+					// consume the > token
+					scanner.nextToken();
+					// mark start of the finalize block
+					inFinalizeSection = true;
+					return true;
+				} else {
+					scanner.pushToken(finalizeToken);
+					scanner.pushToken(token);
+				}
+			} else {
+				scanner.pushToken(token);
+			}
 		}
 		scanner.skipSpace();
 		return false;
@@ -212,6 +238,10 @@
 	private void saveRule(TokenScanner scanner, Op op, ActionList actions, GType gt) {
 		log.info("EXP", op, ", type=", gt);
 
+		// check if the type definition is allowed
+		if (inFinalizeSection && gt != null)
+			throw new SyntaxException(scanner, "Element type definition is not allowed in <finalize> section");
+		
 		//System.out.println("From: " + op);
 		Op op2 = rearrangeExpression(op);
 		//System.out.println("TO  : " + op2);
@@ -486,7 +516,10 @@
 		else
 			rule = new ActionRule(expr, actions.getList(), gt);
 
-		rules.add(keystring, rule, actions.getChangeableTags());
+		if (inFinalizeSection)
+			finalizeRules.add(keystring, rule, actions.getChangeableTags());
+		else
+			rules.add(keystring, rule, actions.getChangeableTags());
 	}
 
 	public static void main(String[] args) throws FileNotFoundException {
Index: src/uk/me/parabola/mkgmap/reader/osm/Rule.java
===================================================================
--- src/uk/me/parabola/mkgmap/reader/osm/Rule.java	(revision 2796)
+++ src/uk/me/parabola/mkgmap/reader/osm/Rule.java	(working copy)
@@ -32,4 +32,12 @@
 	 * @param result The resolved Garmin type that will go into the map.
 	 */
 	public void resolveType(Element el, TypeResult result);
+	
+	/**
+	 * Sets the finalize rules that are executed when 
+	 * an element type definition matches.
+	 * 
+	 * @param finalizeRule finalize rule(s)
+	 */
+	public void setFinalizeRule(Rule finalizeRule);
 }
Index: src/uk/me/parabola/mkgmap/reader/osm/FeatureKind.java
===================================================================
--- src/uk/me/parabola/mkgmap/reader/osm/FeatureKind.java	(revision 2796)
+++ src/uk/me/parabola/mkgmap/reader/osm/FeatureKind.java	(working copy)
@@ -23,6 +23,7 @@
 	POLYLINE,
 	POLYGON,
 
-	// This is not really a feature kind, as there is no corresponding Garmin object.
+	// These are not really feature kinds, as there is no corresponding Garmin object.
 	RELATION,
+	ALL,
 }
Index: test/resources/teststyles/finalize/info
===================================================================
--- test/resources/teststyles/finalize/info	(revision 0)
+++ test/resources/teststyles/finalize/info	(revision 0)
@@ -0,0 +1,18 @@
+#
+# This file contains information about the style.
+# It is completely optional.
+# 
+
+
+# A short one line description of the style
+#
+summary: A test style for finalize rules
+
+# Version of the style itself
+#
+version=1.0
+
+# A longer description of the style.
+description {
+
+}
Index: test/resources/teststyles/finalize/version
===================================================================
--- test/resources/teststyles/finalize/version	(revision 0)
+++ test/resources/teststyles/finalize/version	(revision 0)
@@ -0,0 +1 @@
+1
Index: test/resources/teststyles/finalize/lines
===================================================================
--- test/resources/teststyles/finalize/lines	(revision 0)
+++ test/resources/teststyles/finalize/lines	(revision 0)
@@ -0,0 +1,15 @@
+
+# Expression rule must be matched by the finalize 
+highway=residential [0x01 road_class=0 road_speed=0 resolution 22]
+
+#
+highway=track { set road=yes } [0x01 road_class=0 road_speed=0 resolution 22]
+
+# no finalize rule must match
+highway=secondary { set road=yes }
+road=yes { delete road }
+highway=secondary [0x01 road_class=0 road_speed=0 resolution 22]
+
+<finalize>
+highway=residential { name 'OK' }
+road=yes { name 'OK' }
Index: test/resources/teststyles/finalize/options
===================================================================
--- test/resources/teststyles/finalize/options	(revision 0)
+++ test/resources/teststyles/finalize/options	(revision 0)
@@ -0,0 +1,8 @@
+
+#
+# Style options.
+#
+
+# The levels specification for this style
+#
+levels = 0:24, 1:22, 2:20, 3:18, 4:16
Index: test/uk/me/parabola/mkgmap/osmstyle/StyledConverterTest.java
===================================================================
--- test/uk/me/parabola/mkgmap/osmstyle/StyledConverterTest.java	(revision 2796)
+++ test/uk/me/parabola/mkgmap/osmstyle/StyledConverterTest.java	(working copy)
@@ -16,10 +16,12 @@
  */
 package uk.me.parabola.mkgmap.osmstyle;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
 import java.io.FileNotFoundException;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Properties;
 
 import uk.me.parabola.imgfmt.app.Coord;
 import uk.me.parabola.imgfmt.app.CoordNode;
@@ -35,8 +37,6 @@
 
 import org.junit.Test;
 
-import static org.junit.Assert.*;
-
 
 /**
  * High level tests of the complete converter chain, using an actual
@@ -204,7 +204,23 @@
 		assertEquals("found pedestrian type", 6, lines.get(0).getType());
 	}
 
+	@Test
+	public void testFinalizer() throws FileNotFoundException {
+		String[][] tests = new String[][] {{"residential","OK"}, {"track","OK"},{"secondary",null}};
+		
+		for (String[] test : tests ) {
+			lines.clear();
+			converter = makeConverter("finalize");
+			Way w = makeWay();
+			w.addTag("highway", test[0]);
+			converter.convertWay(w);
+			converter.end();
 
+			assertEquals("lines converted", 1, lines.size());
+			assertEquals("wrong name set by the finalize block", test[1], lines.get(0).getName());
+		}
+	}
+	
 	private Way makeWay() {
 		Way way = new Way(1);
 		way.addPoint(new Coord(100, 100));
