What if we extend the mkgmap style language with an 'import' or
'include' directive familiar from other languages? There already is the
base-style directive (used in resources/styles/marine/info), but it
allows a style to import only one other style.

There is no real reason why it should be restricted to one. Attached
patch fixes that.

If you have a style "c" which has

base-style: a
base-style: b

Then style c will pull in the rules from a and b, so that rules
in c will come first, followed by those in b and then those in a.

They are in reverse order because the rule that matches is first is
the one that gets picked and we want c to override b and b to override a.

Both a and b can have their own base-style options and so on, as now.


..Steve
Index: src/uk/me/parabola/mkgmap/osmstyle/StylePrinter.java
===================================================================
--- src/uk/me/parabola/mkgmap/osmstyle/StylePrinter.java	(revision 2063)
+++ src/uk/me/parabola/mkgmap/osmstyle/StylePrinter.java	(revision )
@@ -3,6 +3,7 @@
 import java.io.Writer;
 import java.util.Formatter;
 import java.util.Map;
+import java.util.Map.Entry;
 
 import uk.me.parabola.mkgmap.reader.osm.Rule;
 import uk.me.parabola.mkgmap.reader.osm.Style;
@@ -59,7 +60,7 @@
 			fmt.format("name-tag-list: %s\n", fmtArray(style.getNameTagList()));
 
 		if (generalOptions != null) {
-			for (Map.Entry<String, String> entry : generalOptions.entrySet())
+			for (Entry<String, String> entry : generalOptions.entrySet())
 				fmt.format("%s: %s\n", entry.getKey(), entry.getValue());
 		}
 	}
@@ -69,8 +70,12 @@
 		StyleInfo styleInfo = style.getInfo();
 		fmt.format("version %s\n", dumpInfoVal(styleInfo.getVersion()));
 		fmt.format("summary %s\n", dumpInfoVal(styleInfo.getSummary()));
-		if (styleInfo.getBaseStyleName() != null)
-			fmt.format("base-style %s\n", dumpInfoVal(styleInfo.getBaseStyleName()));
+
+		// The base styles are combined already, so should not be output. Retained as comments for
+		// documentation/testing purposes.
+		for (String name : styleInfo.baseStyles())
+			fmt.format("# base-style %s\n", dumpInfoVal(name));
+
 		fmt.format("description %s\n", dumpInfoVal(styleInfo.getLongDescription()));
 	}
 
Index: test/uk/me/parabola/mkgmap/osmstyle/StyledConverterTest.java
===================================================================
--- test/uk/me/parabola/mkgmap/osmstyle/StyledConverterTest.java	(revision 2063)
+++ test/uk/me/parabola/mkgmap/osmstyle/StyledConverterTest.java	(revision )
@@ -144,6 +144,44 @@
 	}
 
 	@Test
+	public void testMultipleBase() throws FileNotFoundException {
+		converter = makeConverter("d");
+
+		convertTag("a", "a");
+		assertEquals(1, lines.get(0).getType());
+
+		convertTag("b", "b");
+		assertEquals(1, lines.get(0).getType());
+
+		convertTag("c", "c");
+		assertEquals(1, lines.get(0).getType());
+
+		convertTag("d", "d");
+		assertEquals(1, lines.get(0).getType());
+
+		convertTag("override", "ab");
+		assertEquals(2, lines.get(0).getType());
+
+		for (String s : new String[]{"ac", "bc"}) {
+			convertTag("override", s);
+			assertEquals(3, lines.get(0).getType());
+		}
+
+		for (String s : new String[]{"ad", "bd", "cd"}) {
+			convertTag("override", s);
+			assertEquals(4, lines.get(0).getType());
+		}
+	}
+
+	private Way convertTag(String key, String value) {
+		lines.clear();
+		Way way = makeWay();
+		way.addTag(key, value);
+		converter.convertWay(way);
+		return way;
+	}
+
+	@Test
 	public void testFileConflicts() throws FileNotFoundException {
 		converter = makeConverter("waycombine");
 		Way w = makeWay();
Index: .idea/dictionaries/steve.xml
===================================================================
--- .idea/dictionaries/steve.xml	(revision 2063)
+++ .idea/dictionaries/steve.xml	(revision )
@@ -32,6 +32,7 @@
       <w>gmapsupp</w>
       <w>gpsmapedit</w>
       <w>gtype</w>
+      <w>gtypes</w>
       <w>hashlist</w>
       <w>heibler</w>
       <w>imgfmt</w>
@@ -97,6 +98,7 @@
       <w>xcharset</w>
       <w>xcode</w>
       <w>xmlattr</w>
+      <w>zip'ed</w>
       <w>zoraster</w>
     </words>
   </dictionary>
Index: src/uk/me/parabola/mkgmap/reader/osm/StyleInfo.java
===================================================================
--- src/uk/me/parabola/mkgmap/reader/osm/StyleInfo.java	(revision 2063)
+++ src/uk/me/parabola/mkgmap/reader/osm/StyleInfo.java	(revision )
@@ -16,6 +16,9 @@
  */
 package uk.me.parabola.mkgmap.reader.osm;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * Information about a style.  This is so style authors can include
  * descriptions of their styles within the style itself.
@@ -27,7 +30,7 @@
 	private String version;
 	private String summary;
 	private String longDescription;
-	private String baseStyleName;
+	private final List<String> baseStyleNames = new ArrayList<String>();
 
 
 	public String getSummary() {
@@ -54,11 +57,11 @@
 		this.longDescription = longDescription;
 	}
 
-	public String getBaseStyleName() {
-		return baseStyleName;
+	public Iterable<String> baseStyles() {
+		return baseStyleNames;
 	}
 
-	public void setBaseStyleName(String value) {
-		this.baseStyleName = value.trim();
+	public void addBaseStyleName(String name) {
+		baseStyleNames.add(name.trim());
 	}
 }
Index: src/uk/me/parabola/mkgmap/osmstyle/StyleImpl.java
===================================================================
--- src/uk/me/parabola/mkgmap/osmstyle/StyleImpl.java	(revision 2063)
+++ src/uk/me/parabola/mkgmap/osmstyle/StyleImpl.java	(revision )
@@ -31,7 +31,9 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.ListIterator;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Properties;
 import java.util.Set;
 import java.util.regex.Pattern;
@@ -101,7 +103,7 @@
 	private StyleInfo info = new StyleInfo();
 
 	// Set if this style is based on another one.
-	private StyleImpl baseStyle;
+	private final List<StyleImpl> baseStyles = new ArrayList<StyleImpl>();
 
 	// A list of tag names to be used as the element name
 	private String[] nameTagList;
@@ -134,18 +136,26 @@
 
 		readInfo();
 
-		readBaseStyle();
-		if (baseStyle != null)
-			mergeStyle(baseStyle);
+		for (String baseName : info.baseStyles())
+			readBaseStyle(baseName);
 
+		for (StyleImpl baseStyle : baseStyles)
+			mergeOptions(baseStyle);
+
 		readOptions();
 		readRules();
 
 		readOverlays();
 
 		readMapFeatures();
-		if (baseStyle != null)
-			mergeRules(baseStyle);
+
+		ListIterator<StyleImpl> listIterator = baseStyles.listIterator(baseStyles.size());
+		while (listIterator.hasPrevious())
+			mergeRules(listIterator.previous());
+
+		// OR: other way
+		//for (StyleImpl s : baseStyles)
+		//	mergeRules(s);
 	}
 
 	public String[] getNameTagList() {
@@ -170,8 +180,8 @@
 	 * @param config The command line options.
 	 */
 	public void applyOptionOverride(Properties config) {
-		Set<Map.Entry<Object,Object>> entries = config.entrySet();
-		for (Map.Entry<Object,Object> ent : entries) {
+		Set<Entry<Object,Object>> entries = config.entrySet();
+		for (Entry<Object,Object> ent : entries) {
 			String key = (String) ent.getKey();
 			String val = (String) ent.getValue();
 
@@ -345,13 +355,13 @@
 	private void initFromMapFeatures(MapFeatureReader mfr) {
 		addBackwardCompatibleRules();
 
-		for (Map.Entry<String, GType> me : mfr.getLineFeatures().entrySet())
+		for (Entry<String, GType> me : mfr.getLineFeatures().entrySet())
 			lines.add(me.getKey(), createRule(me.getKey(), me.getValue()), Collections.<String>emptySet());
 
-		for (Map.Entry<String, GType> me : mfr.getShapeFeatures().entrySet())
+		for (Entry<String, GType> me : mfr.getShapeFeatures().entrySet())
 			polygons.add(me.getKey(), createRule(me.getKey(), me.getValue()), Collections.<String>emptySet());
 
-		for (Map.Entry<String, GType> me : mfr.getPointFeatures().entrySet())
+		for (Entry<String, GType> me : mfr.getPointFeatures().entrySet())
 			nodes.add(me.getKey(), createRule(me.getKey(), me.getValue()), Collections.<String>emptySet());
 	}
 
@@ -463,7 +473,7 @@
 					else if (word.equals("version")) {
 						info.setVersion(value);
 					} else if (word.equals("base-style")) {
-						info.setBaseStyleName(value);
+						info.addBaseStyleName(value);
 					} else if (word.equals("description")) {
 						info.setLongDescription(value);
 					}
@@ -498,14 +508,14 @@
 	 * the current styles 'info' section and any option or rule specified
 	 * in the current style will override any corresponding item in the
 	 * base style.
+	 * @param name The name of the base style
 	 */
-	private void readBaseStyle() {
-		String name = info.getBaseStyleName();
+	private void readBaseStyle(String name) {
 		if (name == null)
 			return;
 
 		try {
-			baseStyle = new StyleImpl(location, name);
+			baseStyles.add(new StyleImpl(location, name));
 		} catch (SyntaxException e) {
 			System.err.println("Error in style: " + e.getMessage());
 		} catch (FileNotFoundException e) {
@@ -515,31 +525,28 @@
 			log.debug("could not open base style file", e);
 
 			try {
-				baseStyle = new StyleImpl(null, name);
+				baseStyles.add(new StyleImpl(null, name));
 			} catch (SyntaxException se) {
 				System.err.println("Error in style: " + se.getMessage());
 			} catch (FileNotFoundException e1) {
-				baseStyle = null;
 				log.error("Could not find base style", e);
 			}
 		}
 	}
 
 	/**
-	 * Merge another style into this one.  The style will have a lower
-	 * priority, in other words if rules in the current style match the
-	 * 'other' one, then the current rule wins.
+	 * Merge another style's options into this one.  The style will have a lower
+	 * priority, in other words any option set in 'other' and this style will
+	 * take the value given in this style.
 	 *
-	 * This is called from the options file, and options from the other
-	 * file are processed as if they were included in the current option
-	 * file at the point of inclusion.
-	 * 
 	 * This is used to base styles on other ones, without having to repeat
 	 * everything.
+	 *
+	 * @see #mergeRules(StyleImpl)
 	 */
-	private void mergeStyle(StyleImpl other) {
+	private void mergeOptions(StyleImpl other) {
 		this.nameTagList = other.nameTagList;
-		for (Map.Entry<String, String> ent : other.generalOptions.entrySet()) {
+		for (Entry<String, String> ent : other.generalOptions.entrySet()) {
 			String opt = ent.getKey();
 			String val = ent.getValue();
 			if (opt.equals("name-tag-list")) {
@@ -560,6 +567,11 @@
 	/**
 	 * Merge rules from the base style.  This has to called after this
 	 * style's rules are read.
+	 *
+	 * The other rules have a lower priority than the rules in this file; it is as if they
+	 * were appended to the rule files of this style.
+	 *
+	 * @see #mergeOptions(StyleImpl) 
 	 */
 	private void mergeRules(StyleImpl other) {
 		lines.merge(other.lines);
_______________________________________________
mkgmap-dev mailing list
[email protected]
http://www.mkgmap.org.uk/mailman/listinfo/mkgmap-dev

Reply via email to