logo separator

[mkgmap-dev] [PATCH v7] make maps in parallel

From Steve Ratcliffe steve at parabola.demon.co.uk on Sat May 16 10:36:54 BST 2009

Hi Mark

I couldn't see anything wrong with your patch that might
cause maps to be missed but it is hard to tell because
of the way that CommandArgs works.

So I've attached a patch that is based on your v7 patch that separates
out CommandArgs into one class that reads them and another that
contains the actual options.  The options given to the processor
are always copies so there is no need to further copy.

There is also a tweek to make sure that the files are
given to the combiners in the order they started.


..Steve
-------------- next part --------------
Index: test/uk/me/parabola/mkgmap/CommandArgsTest.java
===================================================================
--- test/uk/me/parabola/mkgmap/CommandArgsTest.java	(revision 1035)
+++ test/uk/me/parabola/mkgmap/CommandArgsTest.java	(working copy)
@@ -44,7 +44,7 @@
 	private static final String FILE3 = "00000003.osm";
 
 	private final ArgCollector proc = new ArgCollector();
-	private final CommandArgs carg = new CommandArgs(proc);
+	private final CommandArgsReader carg = new CommandArgsReader(proc);
 
 	/**
 	 * Test that the default mapnames are correct.  Should start with 63240001
Index: src/uk/me/parabola/mkgmap/combiners/TdbBuilder.java
===================================================================
--- src/uk/me/parabola/mkgmap/combiners/TdbBuilder.java	(revision 1035)
+++ src/uk/me/parabola/mkgmap/combiners/TdbBuilder.java	(working copy)
@@ -28,11 +28,12 @@
 import uk.me.parabola.imgfmt.app.Coord;
 import uk.me.parabola.imgfmt.app.map.Map;
 import uk.me.parabola.log.Logger;
-import uk.me.parabola.mkgmap.CommandArgs;
 import uk.me.parabola.mkgmap.build.MapBuilder;
 import uk.me.parabola.mkgmap.general.MapShape;
+import uk.me.parabola.mkgmap.CommandArgs;
 import uk.me.parabola.tdbfmt.DetailMapBlock;
 import uk.me.parabola.tdbfmt.TdbFile;
+import uk.me.parabola.util.EnhancedProperties;
 
 /**
  * Build the TDB file and the overview map.
@@ -57,7 +58,6 @@
 	 *
 	 * @param args The command line arguments as they are at the end of the list.
 	 * In otherwords if the same argument appears more than once, then it will
-	 * have the value that was set last.
 	 */
 	public void init(CommandArgs args) {
 		overviewMapname = args.get("overview-mapname", "63240000");
Index: src/uk/me/parabola/mkgmap/combiners/GmapsuppBuilder.java
===================================================================
--- src/uk/me/parabola/mkgmap/combiners/GmapsuppBuilder.java	(revision 1035)
+++ src/uk/me/parabola/mkgmap/combiners/GmapsuppBuilder.java	(working copy)
@@ -34,6 +34,7 @@
 import uk.me.parabola.imgfmt.sys.FileImgChannel;
 import uk.me.parabola.imgfmt.sys.ImgFS;
 import uk.me.parabola.log.Logger;
+import uk.me.parabola.util.EnhancedProperties;
 import uk.me.parabola.mkgmap.CommandArgs;
 
 /**
Index: src/uk/me/parabola/mkgmap/main/Main.java
===================================================================
--- src/uk/me/parabola/mkgmap/main/Main.java	(revision 1035)
+++ src/uk/me/parabola/mkgmap/main/Main.java	(working copy)
@@ -25,14 +25,20 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
 
 import uk.me.parabola.imgfmt.ExitException;
 import uk.me.parabola.log.Logger;
 import uk.me.parabola.mkgmap.ArgumentProcessor;
 import uk.me.parabola.mkgmap.CommandArgs;
+import uk.me.parabola.mkgmap.CommandArgsReader;
 import uk.me.parabola.mkgmap.Version;
 import uk.me.parabola.mkgmap.combiners.Combiner;
 import uk.me.parabola.mkgmap.combiners.FileInfo;
@@ -67,6 +73,11 @@
 	private String styleFile = "classpath:styles";
 	private boolean verbose;
 
+	private final List<Future<String>> futures = new LinkedList<Future<String>>();
+	private ExecutorService threadPool;
+	// default number of threads (limited by number of cores available below)
+	private int maxJobs = 4;
+
 	/**
 	 * The main program to make or combine maps.  We now use a two pass process,
 	 * first going through the arguments and make any maps and collect names
@@ -87,7 +98,7 @@
 
 		try {
 			// Read the command line arguments and process each filename found.
-			CommandArgs commandArgs = new CommandArgs(mm);
+			CommandArgsReader commandArgs = new CommandArgsReader(mm);
 			commandArgs.readArgs(args);
 		} catch (ExitException e) {
 			System.err.println(e.getMessage());
@@ -134,6 +145,13 @@
 		processMap.put("lbl", saver);
 		processMap.put("net", saver);
 		processMap.put("nod", saver);
+
+		// by default, have no more than 1 thread per core
+		// but this can be overridden with --max-jobs option
+		int numCores = Runtime.getRuntime().availableProcessors();
+		if(maxJobs > numCores)
+			maxJobs = numCores;
+
 	}
 
 	/**
@@ -142,14 +160,25 @@
 	 * @param args The command arguments.
 	 * @param filename The filename to process.
 	 */
-	public void processFilename(CommandArgs args, String filename) {
-		String ext = extractExtension(filename);
+	public void processFilename(final CommandArgs args, final String filename) {
+		final String ext = extractExtension(filename);
 		log.debug("file", filename, ", extension is", ext);
 
-		MapProcessor mp = mapMaker(ext);
-		String output = mp.makeMap(args, filename);
-		log.debug("adding output name", output);
-		filenames.add(output);
+		if (threadPool == null) {
+			log.info("Creating thread pool with", maxJobs, "threads");
+			System.out.println("Creating thread pool with " + maxJobs + " threads");
+			threadPool = Executors.newFixedThreadPool(maxJobs);
+		}
+		final MapProcessor mp = mapMaker(ext);
+
+		log.info("Submitting job " + filename);
+		futures.add(threadPool.submit(new Callable<String>() {
+				public String call() {
+					String output = mp.makeMap(args, filename);
+					log.debug("adding output name", output);
+					return output;
+				}
+			}));
 	}
 
 	private MapProcessor mapMaker(String ext) {
@@ -183,6 +212,12 @@
 			verbose = true;
 		} else if (opt.equals("list-styles")) {
 			listStyles();
+		} else if (opt.equals("max-jobs")) {
+			maxJobs = Integer.parseInt(val);
+			if(maxJobs < 1) {
+				log.warn("max-jobs has to be at least 1");
+				maxJobs = 1;
+			}
 		} else if (opt.equals("version")) {
 			System.err.println(Version.VERSION);
 			System.exit(0);
@@ -248,9 +283,23 @@
 	}
 
 	public void endOptions(CommandArgs args) {
+
+		if (threadPool != null)
+			threadPool.shutdown();
+		for (Future<String> f : futures) {
+			try {
+				filenames.add(f.get());
+			} catch(Exception e) {
+				log.error("" + e);
+				e.printStackTrace(System.err);
+			}
+		}
+
 		if (combiners.isEmpty())
 			return;
 
+		log.info("Combining maps");
+
 		// Get them all set up.
 		for (Combiner c : combiners)
 			c.init(args);
Index: src/uk/me/parabola/mkgmap/main/MapMaker.java
===================================================================
--- src/uk/me/parabola/mkgmap/main/MapMaker.java	(revision 1035)
+++ src/uk/me/parabola/mkgmap/main/MapMaker.java	(working copy)
@@ -31,6 +31,7 @@
 import uk.me.parabola.imgfmt.app.Coord;
 import uk.me.parabola.imgfmt.app.map.Map;
 import uk.me.parabola.log.Logger;
+import uk.me.parabola.mkgmap.CommandArgsReader;
 import uk.me.parabola.mkgmap.CommandArgs;
 import uk.me.parabola.mkgmap.build.MapBuilder;
 import uk.me.parabola.mkgmap.general.LoadableMapDataSource;
@@ -79,6 +80,7 @@
 		params.setBlockSize(args.getBlockSize());
 		params.setMapDescription(args.getDescription());
 
+		log.info("Started making", args.getMapname(), "(" + args.getDescription() + ")");
 		try {
 			Map map = Map.createMap(args.getMapname(), params);
 			setOptions(map, args);
@@ -139,7 +141,7 @@
 	}
 
 	private void makeAreaPOIs(CommandArgs args, LoadableMapDataSource src) {
-		String s = args.getProperties().getProperty("add-pois-to-areas");
+		String s = args.get("add-pois-to-areas", null);
 		if (s != null) {
 			
 			MapPointFastFindMap poiMap = new MapPointFastFindMap();
@@ -182,7 +184,7 @@
 
 	
 	void makeRoadNamePOIS(CommandArgs args, LoadableMapDataSource src) {
-		String rnp = args.getProperties().getProperty("road-name-pois", null);
+		String rnp = args.get("road-name-pois", null);
 		// are road name POIS wanted?
 		if(rnp != null) {
 			int rnpt = 0x640a; // Garmin type 'Locale'
Index: src/uk/me/parabola/mkgmap/main/MapProcessor.java
===================================================================
--- src/uk/me/parabola/mkgmap/main/MapProcessor.java	(revision 1035)
+++ src/uk/me/parabola/mkgmap/main/MapProcessor.java	(working copy)
@@ -16,6 +16,7 @@
  */
 package uk.me.parabola.mkgmap.main;
 
+import uk.me.parabola.mkgmap.CommandArgsReader;
 import uk.me.parabola.mkgmap.CommandArgs;
 
 /**
Index: src/uk/me/parabola/mkgmap/CommandArgsReader.java
===================================================================
--- src/uk/me/parabola/mkgmap/CommandArgsReader.java	(revision 1035)
+++ src/uk/me/parabola/mkgmap/CommandArgsReader.java	(working copy)
@@ -40,25 +40,26 @@
  *
  * @author Steve Ratcliffe
  */
-public class CommandArgs {
-	private static final Logger log = Logger.getLogger(CommandArgs.class);
+public class CommandArgsReader {
+	private static final Logger log = Logger.getLogger(CommandArgsReader.class);
 
+	private final ArgumentProcessor proc;
+
+	private boolean mapnameWasSet;
+
 	private final ArgList arglist = new ArgList();
 
+	private final EnhancedProperties args = new EnhancedProperties();
+
 	{
 		// Set some default values.  It is as if these were on the command
 		// line before any user supplied options.
-		arglist.add(new CommandOption("mapname", "63240001"));
-		arglist.add(new CommandOption("description", "OSM street map"));
-		arglist.add(new CommandOption("overview-mapname", "63240000"));
+		add(new CommandOption("mapname", "63240001"));
+		add(new CommandOption("description", "OSM street map"));
+		add(new CommandOption("overview-mapname", "63240000"));
 	}
 
-	private final ArgumentProcessor proc;
-	private final EnhancedProperties currentOptions = new EnhancedProperties();
-
-	private boolean mapnameWasSet;
-
-	public CommandArgs(ArgumentProcessor proc) {
+	public CommandArgsReader(ArgumentProcessor proc) {
 		this.proc = proc;
 	}
 
@@ -106,7 +107,7 @@
 
 			} else {
 				log.debug("adding filename:", arg);
-				arglist.add(new Filename(arg));
+				add(new Filename(arg));
 			}
 		}
 
@@ -119,82 +120,11 @@
 			a.processArg();
 		}
 
-		proc.endOptions(this);
+		proc.endOptions(new CommandArgs(this.args));
 	}
 
-	public EnhancedProperties getProperties() {
-		return arglist.getProperties();
-	}
 
-	public int get(String name, int def) {
-		return currentOptions.getProperty(name, def);
-	}
-
-	public String get(String name, String def) {
-		return currentOptions.getProperty(name, def);
-	}
-
-	// ////
-	// There are a number of methods to get specific arguments that follow.
-	// There are many more options in use however.  New code should mostly
-	// just use the get methods above.
-	// ////
-
-	public String getDescription() {
-		return arglist.getProperty("description");
-	}
-
-	public int getBlockSize() {
-		return get("block-size", 512);
-	}
-
-	public String getMapname() {
-		return arglist.getProperty("mapname");
-	}
-
-	public String getCharset() {
-		String charset = arglist.getProperty("latin1");
-		if (charset != null)
-			return "latin1";
-
-		// xcharset is the old value, use charset instead.
-		charset = arglist.getProperty("charset", arglist.getProperty("xcharset"));
-		if (charset != null)
-			return charset;
-
-		int cp = getCodePage();
-		if (cp != 0)
-			return "cp" + cp;
-
-		return "ascii";
-	}
-
-	public int getCodePage() {
-		int cp;
-
-		// xcode-page is the old name
-		String s = arglist.getProperty("code-page", arglist.getProperty("xcode-page", "0"));
-		try {
-			cp = Integer.parseInt(s);
-		} catch (NumberFormatException e) {
-			cp = 0;
-		}
-
-		return cp;
-	}
-
-	public boolean isForceUpper() {
-		return arglist.getProperty("lower-case") == null;
-	}
-
 	/**
-	 * Test for the existence of an argument.
-	 */
-	public boolean exists(String name) {
-		return currentOptions.containsKey(name);
-	}
-
-	/**
 	 * Add an option based on the option and value separately.
 	 * @param option The option name.
 	 * @param value Its value.
@@ -231,14 +161,26 @@
 
 		if (option.equals("input-file")) {
 			log.debug("adding filename", value);
-			arglist.add(new Filename(value));
+			add(new Filename(value));
 		} else if (option.equals("read-config")) {
 			readConfigFile(value);
 		} else {
-			arglist.add(opt);
+			add(opt);
 		}
 	}
 
+	private void add(CommandOption option) {
+		arglist.add(option);
+	}
+
+	private void add(Filename filename) {
+		arglist.add(filename);
+	}
+
+	public Iterator<ArgType> iterator() {
+		return arglist.iterator();
+	}
+
 	/**
 	 * Read a config file that contains more options.  When the number of
 	 * options becomes large it is more convenient to place them in a file.
@@ -260,63 +202,18 @@
 	}
 
 	/**
-	 * The arguments are held in this list.
-	 */
-	private class ArgList implements Iterable<ArgType> {
-		private final List<ArgType> alist = new ArrayList<ArgType>();
-
-		private int filenameCount;
-
-		public void add(CommandOption option) {
-			alist.add(option);
-		}
-
-		public void add(Filename name) {
-			filenameCount++;
-			alist.add(name);
-		}
-
-		public Iterator<ArgType> iterator() {
-			return alist.iterator();
-		}
-
-		public int getFilenameCount() {
-			return filenameCount;
-		}
-
-		public String getProperty(String name) {
-			return currentOptions.getProperty(name);
-		}
-
-		public String getProperty(String name, String def) {
-			String val = currentOptions.getProperty(name);
-			if (val == null)
-				val = def;
-			return val;
-		}
-
-		public EnhancedProperties getProperties() {
-			return currentOptions;
-		}
-
-		public void setProperty(String name, String value) {
-			currentOptions.setProperty(name, value);
-		}
-	}
-
-	/**
 	 * Interface that represents an argument type.  It provides a method for
 	 * the argument to be processed in order.  Options can be intersperced with
 	 * filenames.  The options take effect where they appear.
 	 */
-	private interface ArgType {
+	interface ArgType {
 		public abstract void processArg();
 	}
 
 	/**
 	 * A filename.
 	 */
-	private class Filename implements ArgType {
+	class Filename implements ArgType {
 		private final String name;
 		private boolean useFilenameAsMapname = true;
 
@@ -333,15 +230,15 @@
 			if (useFilenameAsMapname) {
 				mapname = extractMapName(name);
 				if (mapname != null)
-					arglist.setProperty("mapname", mapname);
+					args.setProperty("mapname", mapname);
 			}
 
 			// Now process the file
-			proc.processFilename(CommandArgs.this, name);
+			proc.processFilename(new CommandArgs(args), name);
 
 			// Increase the name number.  If the next arg sets it then that
 			// will override this new name.
-			mapname = arglist.getProperty("mapname");
+			mapname = args.getProperty("mapname");
 			try {
 				Formatter fmt = new Formatter();
 				try {
@@ -350,7 +247,7 @@
 				} catch (NumberFormatException e) {
 					fmt.format("%8.8s", mapname);
 				}
-				arglist.setProperty("mapname", fmt.toString());
+				args.setProperty("mapname", fmt.toString());
 			} catch (NumberFormatException e) {
 				// If the name is not a number then we just leave it alone...
 			}
@@ -373,7 +270,7 @@
 	/**
 	 * An option argument.  A key value pair.
 	 */
-	private class CommandOption implements ArgType {
+	class CommandOption implements ArgType {
 		private final Option option;
 
 		private CommandOption(Option option) {
@@ -385,7 +282,7 @@
 		}
 
 		public void processArg() {
-			currentOptions.setProperty(option.getOption(), option.getValue());
+			args.setProperty(option.getOption(), option.getValue());
 			proc.processOption(option.getOption(), option.getValue());
 		}
 
@@ -397,5 +294,37 @@
 			return option.getValue();
 		}
 	}
+	/**
+	 * The arguments are held in this list.
+	 */
+	class ArgList implements Iterable<CommandArgsReader.ArgType> {
+		private final List<ArgType> alist;
 
+		private int filenameCount;
+
+		ArgList() {
+			alist = new ArrayList<ArgType>();
+		}
+
+		public ArgList(ArgList args) {
+			alist = new ArrayList<CommandArgsReader.ArgType>(args.alist);
+		}
+
+		protected void add(CommandArgsReader.CommandOption option) {
+			alist.add(option);
+		}
+
+		public void add(CommandArgsReader.Filename name) {
+			filenameCount++;
+			alist.add(name);
+		}
+
+		public Iterator<ArgType> iterator() {
+			return alist.iterator();
+		}
+
+		public int getFilenameCount() {
+			return filenameCount;
+		}
+	}
 }
Index: src/uk/me/parabola/mkgmap/ArgumentProcessor.java
===================================================================
--- src/uk/me/parabola/mkgmap/ArgumentProcessor.java	(revision 1035)
+++ src/uk/me/parabola/mkgmap/ArgumentProcessor.java	(working copy)
@@ -16,6 +16,8 @@
  */
 package uk.me.parabola.mkgmap;
 
+import uk.me.parabola.util.EnhancedProperties;
+
 /**
  * Used to step through each filename that is given to the program.
  *
Index: src/uk/me/parabola/mkgmap/CommandArgs.java
===================================================================
--- src/uk/me/parabola/mkgmap/CommandArgs.java	(revision 1035)
+++ src/uk/me/parabola/mkgmap/CommandArgs.java	(working copy)
@@ -1,129 +1,20 @@
-/*
- * Copyright (C) 2006 Steve Ratcliffe
- * 
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License 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.
- * 
- * 
- * Author: Steve Ratcliffe
- * Create date: 01-Jan-2007
- */
 package uk.me.parabola.mkgmap;
 
-import java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Formatter;
-import java.util.Iterator;
-import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import uk.me.parabola.imgfmt.ExitException;
-import uk.me.parabola.log.Logger;
 import uk.me.parabola.util.EnhancedProperties;
 
-/**
- * Command line arguments for Main.  Arguments consist of options and filenames.
- * You read arguments from left to right and when a filename is encounted
- * the file is processed with the options that were in force at the time.
- * 
- * Since it is likely that the number of options will become quite large, you
- * can place options in a file.  Place the options each on a separate line
- * without the initial '--'.
- *
- * @author Steve Ratcliffe
- */
 public class CommandArgs {
-	private static final Logger log = Logger.getLogger(CommandArgs.class);
+	private final EnhancedProperties currentOptions;
 
-	private final ArgList arglist = new ArgList();
-
-	{
-		// Set some default values.  It is as if these were on the command
-		// line before any user supplied options.
-		arglist.add(new CommandOption("mapname", "63240001"));
-		arglist.add(new CommandOption("description", "OSM street map"));
-		arglist.add(new CommandOption("overview-mapname", "63240000"));
+	public CommandArgs() {
+		currentOptions = new EnhancedProperties();
 	}
 
-	private final ArgumentProcessor proc;
-	private final EnhancedProperties currentOptions = new EnhancedProperties();
-
-	private boolean mapnameWasSet;
-
-	public CommandArgs(ArgumentProcessor proc) {
-		this.proc = proc;
+	public CommandArgs(EnhancedProperties args) {
+		currentOptions = new EnhancedProperties(args);
 	}
 
-	/**
-	 * Read and interpret the command line arguments.  Most have a double hyphen
-	 * preceeding them and these work just the same if they are in a config
-	 * file.
-	 * <p/>
-	 * There are a few options that consist of a single hyphen followed by a
-	 * single letter that are short cuts for a long option.
-	 * <p/>
-	 * The -c option is special.  It is followed by the name of a file in which
-	 * there are further command line options.  Any option on the command line
-	 * that comes after the -c option will override the value that is set in
-	 * this file.
-	 *
-	 * @param args The command line arguments.
-	 */
-	public void readArgs(String[] args) {
-
-		proc.startOptions();
-
-		int i = 0;
-		while (i < args.length) {
-			String arg = args[i++];
-			if (arg.startsWith("--")) {
-				// This is a long style 'property' format option.
-				addOption(arg.substring(2));
-
-			} else if (arg.equals("-c")) {
-				// Config file
-				readConfigFile(args[i++]);
-
-			} else if (arg.equals("-n")) {
-				// Map name (should be an 8 digit number).
-				addOption("mapname", args[i++]);
-
-			} else if (arg.equals("-v")) {
-				// make commands more verbose
-				addOption("verbose");
-
-			} else if (arg.startsWith("-")) {
-				// this is an unrecognised option.
-				System.err.println("unrecognised option " + arg);
-
-			} else {
-				log.debug("adding filename:", arg);
-				arglist.add(new Filename(arg));
-			}
-		}
-
-		// If there is more than one filename argument we inform of this fact
-		// via a fake option.
-		proc.processOption("number-of-files", String.valueOf(arglist.getFilenameCount()));
-
-		// Now process the arguments in order.
-		for (ArgType a : arglist) {
-			a.processArg();
-		}
-
-		proc.endOptions(this);
-	}
-
 	public EnhancedProperties getProperties() {
-		return arglist.getProperties();
+		return currentOptions;
 	}
 
 	public int get(String name, int def) {
@@ -134,31 +25,31 @@
 		return currentOptions.getProperty(name, def);
 	}
 
+	public String getDescription() {
+		return currentOptions.getProperty("description");
+	}
+
 	// ////
 	// There are a number of methods to get specific arguments that follow.
 	// There are many more options in use however.  New code should mostly
 	// just use the get methods above.
 	// ////
 
-	public String getDescription() {
-		return arglist.getProperty("description");
-	}
-
 	public int getBlockSize() {
 		return get("block-size", 512);
 	}
 
 	public String getMapname() {
-		return arglist.getProperty("mapname");
+		return currentOptions.getProperty("mapname");
 	}
 
 	public String getCharset() {
-		String charset = arglist.getProperty("latin1");
+		String charset = currentOptions.getProperty("latin1");
 		if (charset != null)
 			return "latin1";
 
 		// xcharset is the old value, use charset instead.
-		charset = arglist.getProperty("charset", arglist.getProperty("xcharset"));
+		charset = currentOptions.getProperty("charset", currentOptions.getProperty("xcharset"));
 		if (charset != null)
 			return charset;
 
@@ -173,7 +64,7 @@
 		int cp;
 
 		// xcode-page is the old name
-		String s = arglist.getProperty("code-page", arglist.getProperty("xcode-page", "0"));
+		String s = currentOptions.getProperty("code-page", currentOptions.getProperty("xcode-page", "0"));
 		try {
 			cp = Integer.parseInt(s);
 		} catch (NumberFormatException e) {
@@ -184,7 +75,7 @@
 	}
 
 	public boolean isForceUpper() {
-		return arglist.getProperty("lower-case") == null;
+		return currentOptions.getProperty("lower-case") == null;
 	}
 
 	/**
@@ -194,208 +85,7 @@
 		return currentOptions.containsKey(name);
 	}
 
-	/**
-	 * Add an option based on the option and value separately.
-	 * @param option The option name.
-	 * @param value Its value.
-	 */
-	private void addOption(String option, String value) {
-		CommandOption opt = new CommandOption(option, value);
-		addOption(opt);
+	public void setProperty(String option, String value) {
+		currentOptions.setProperty(option, value);
 	}
-
-	/**
-	 * Add an option from a raw string.
-	 * @param optval The option=value string.
-	 */
-	private void addOption(String optval) {
-		CommandOption opt = new CommandOption(new Option(optval));
-		addOption(opt);
-	}
-
-	/**
-	 * Actually add the option.  Some of these are special in that they are
-	 * filename arguments or instructions to read options from another file.
-	 *
-	 * @param opt The decoded option.
-	 */
-	private void addOption(CommandOption opt) {
-		String option = opt.getOption();
-		String value = opt.getValue();
-
-		log.debug("adding option", option, value);
-
-		// Note if an explicit mapname is set
-		if (option.equals("mapname"))
-			mapnameWasSet = true;
-
-		if (option.equals("input-file")) {
-			log.debug("adding filename", value);
-			arglist.add(new Filename(value));
-		} else if (option.equals("read-config")) {
-			readConfigFile(value);
-		} else {
-			arglist.add(opt);
-		}
-	}
-
-	/**
-	 * Read a config file that contains more options.  When the number of
-	 * options becomes large it is more convenient to place them in a file.
-	 *
-	 * @param filename The filename to obtain options from.
-	 */
-	private void readConfigFile(String filename) {
-		Options opts = new Options(new OptionProcessor() {
-			public void processOption(Option opt) {
-				log.debug("incoming opt", opt.getOption(), opt.getValue());
-				addOption(new CommandOption(opt));
-			}
-		});
-		try {
-			opts.readOptionFile(filename);
-		} catch (IOException e) {
-			throw new ExitException("Failed to read option file", e);
-		}
-	}
-
-	/**
-	 * The arguments are held in this list.
-	 */
-	private class ArgList implements Iterable<ArgType> {
-		private final List<ArgType> alist = new ArrayList<ArgType>();
-
-		private int filenameCount;
-
-		public void add(CommandOption option) {
-			alist.add(option);
-		}
-
-		public void add(Filename name) {
-			filenameCount++;
-			alist.add(name);
-		}
-
-		public Iterator<ArgType> iterator() {
-			return alist.iterator();
-		}
-
-		public int getFilenameCount() {
-			return filenameCount;
-		}
-
-		public String getProperty(String name) {
-			return currentOptions.getProperty(name);
-		}
-
-		public String getProperty(String name, String def) {
-			String val = currentOptions.getProperty(name);
-			if (val == null)
-				val = def;
-			return val;
-		}
-
-		public EnhancedProperties getProperties() {
-			return currentOptions;
-		}
-
-		public void setProperty(String name, String value) {
-			currentOptions.setProperty(name, value);
-		}
-	}
-
-	/**
-	 * Interface that represents an argument type.  It provides a method for
-	 * the argument to be processed in order.  Options can be intersperced with
-	 * filenames.  The options take effect where they appear.
-	 */
-	private interface ArgType {
-		public abstract void processArg();
-	}
-
-	/**
-	 * A filename.
-	 */
-	private class Filename implements ArgType {
-		private final String name;
-		private boolean useFilenameAsMapname = true;
-
-		private Filename(String name) {
-			this.name = name;
-			if (mapnameWasSet)
-				useFilenameAsMapname = false;
-		}
-
-		public void processArg() {
-			// If there was no explicit mapname specified and the input filename
-			// looks like it contains an 8digit number then we use that.
-			String mapname;
-			if (useFilenameAsMapname) {
-				mapname = extractMapName(name);
-				if (mapname != null)
-					arglist.setProperty("mapname", mapname);
-			}
-
-			// Now process the file
-			proc.processFilename(CommandArgs.this, name);
-
-			// Increase the name number.  If the next arg sets it then that
-			// will override this new name.
-			mapname = arglist.getProperty("mapname");
-			try {
-				Formatter fmt = new Formatter();
-				try {
-					int n = Integer.parseInt(mapname);
-					fmt.format("%08d", ++n);
-				} catch (NumberFormatException e) {
-					fmt.format("%8.8s", mapname);
-				}
-				arglist.setProperty("mapname", fmt.toString());
-			} catch (NumberFormatException e) {
-				// If the name is not a number then we just leave it alone...
-			}
-		}
-
-		private String extractMapName(String path) {
-
-			File file = new File(path);
-			String fname = file.getName();
-			Pattern pat = Pattern.compile("([0-9]{8})");
-			Matcher matcher = pat.matcher(fname);
-			boolean found = matcher.find();
-			if (found)
-				return matcher.group(1);
-
-			return null;
-		}
-	}
-
-	/**
-	 * An option argument.  A key value pair.
-	 */
-	private class CommandOption implements ArgType {
-		private final Option option;
-
-		private CommandOption(Option option) {
-			this.option = option;
-		}
-
-		private CommandOption(String key, String val) {
-			this.option = new Option(key, val);
-		}
-
-		public void processArg() {
-			currentOptions.setProperty(option.getOption(), option.getValue());
-			proc.processOption(option.getOption(), option.getValue());
-		}
-
-		public String getOption() {
-			return option.getOption();
-		}
-
-		public String getValue() {
-			return option.getValue();
-		}
-	}
-
-}
+}
\ No newline at end of file
Index: src/uk/me/parabola/util/EnhancedProperties.java
===================================================================
--- src/uk/me/parabola/util/EnhancedProperties.java	(revision 1035)
+++ src/uk/me/parabola/util/EnhancedProperties.java	(working copy)
@@ -17,6 +17,7 @@
 package uk.me.parabola.util;
 
 import java.util.Properties;
+import java.util.Enumeration;
 
 /**
  * Wrapper that behaves as an enhanced properties class that has getProperty
@@ -25,7 +26,20 @@
  * @author Steve Ratcliffe
  */
 public class EnhancedProperties extends Properties {
+	
+	public EnhancedProperties() {
+	}
 
+	public EnhancedProperties(Properties defaults) {
+		// We copy values, rather than making them default values so that
+		// we can enumerate all the options.
+		Enumeration<?> en = defaults.propertyNames();
+		while (en.hasMoreElements()) {
+			String key = (String) en.nextElement();
+			setProperty(key, defaults.getProperty(key));
+		}
+	}
+
 	/**
 	 * Get a property as an integer value.  If the property does not exist
 	 * or the value is not a valid integer, then the default value is returned
Index: resources/help/en/options
===================================================================
--- resources/help/en/options	(revision 1035)
+++ resources/help/en/options	(working copy)
@@ -101,6 +101,11 @@
 --overview-mapname
 
 Misc options:
+--max-jobs=''number''
+	Limit the number of maps processed concurrently to the
+	specified number. By default, the limit is set to the number
+	of CPU cores up to a maximum of 4.
+
 --block-size=''number''
 	Changes the block size that is used in the generated map.
 	There is no general reason why you would want to do this.


More information about the mkgmap-dev mailing list