logo separator

[mkgmap-dev] complete list of all mkgmap options

From Chris Miller chris.miller at kbcfp.com on Tue Aug 4 10:35:50 BST 2009

> What I believe we need is some fascist check in the options processing
> code that prints a message and quits the program if you specify an
> option that isn't recognised. Furthermore, it would then be possible
> to list the supported options with nothing left out or spelt
> incorrectly, etc.
> Cheers,
> Mark

Something we've done at my current job is to write a CLI framework that makes 
managing command line params very very easy. I think this project could benefit 
greatly from this approach due to its large number of (frequently changing?) 
parameters. Unfortunately I can't contribute our code, however I can describe 
in detail what we did and help with the implementation. Basically each application 
that uses command line params defines an interface that looks something like 

public interface MkGMapParams {
  @Option(defaultValue = "0", description = "The Family ID for the generated 
  int getFamilyId();

  @Option(defaultValue = Option.REQUIRED, description = "The Product ID for 
the generated map")
  int getProductId();

  @Option(defaultValue = Option.OPTIONAL, description = "The country the 
map represents")
  String getCountryName();

  @Option(description = "Specify this to make cycleways")
  boolean isMakeCycleways();

  enum MyEnum { One, Two }
  @Option(name = "custom-enum", defaultValue = "One", description = "My custom 
  MyEnum getEnum();
  // etc

To parse the parameters in code, we basically just make a call to our parser 
as follows:

MkGMapParams params = CliParser.parse(MkGMapParams.class, arguments);   // 
where arguments is a String... parameter

That's it, the 'params' object now has all our command line params, parsed 
into the correct type. If any of the parsing failed, error(s) explaining 
what was wrong, along with a list of valid options and descriptions, are 
displayed to the user.

Some valid usage examples:
java -jar mkgmap --product-id 99 --country-name bhutan --make-cycleways
java -jar mkgmap --product-id 99 --custom-enum Two

Some invalid usage examples:
java -jar mkgmap --family-id 99 --country-name bhutan --make-cycleways   
 (fails because --product-id is a required parameter)
java -jar mkgmap --product-id 99 --enum One    (fails because the 'enum' 
parameter must be specified as --custom-enum, not --enum)
java -jar mkgmap --product-id abc    (fails because --product-id is not an 
java -jar mkgmap --product-id 11 --family-id --make-cycleways  (fails because 
no value was specified for --family-id)

Here's how it works under the hood:

We have a converter interface defined as follows:

interface Converter<T> {
  T convert(String param) throws Exception;         // Convert from the string 
provided on the command line to a typesafe object.
  List<String> getValidValues() throws Exception;  // Provide a list of valid 
options (optional). eg a list of enum values.
  String getDefaultValue() throws Exception;        // Returns the default 
value (or null if the parameter is required).

We then have a map from type to converter class, eg:

defaultConverters.put(String.class, StringConverter.class);
defaultConverters.put(Integer.class, IntegerConverter.class);
defaultConverters.put(Integer.type, IntegerConverter.class);

We then use reflection (and the annotation info) to examine the interface 
and determine what the valid parameters are, whether they're required, what 
type they are etc. For each parameter we parse we convert the string to the 
appropriate type using the above map. Once that's done and no errors were 
found, we use Proxy.newProxyInstance() to create an instance of the parameter 
interface for the client application.

Note that we have a lot of additional functionality too like list handling, 
custom converters, inter-parameter dependency handling, custom validation, 
Guice support etc but the above is the guts of it. I hope it doesn't sound 
like too much work - it's really not! The best thing is it will force people 
to clearly define their parameters in the MkGMapParams interface since that's 
the only thing that'll be available anywhere beyond the main() method. It's 
very easy to use once the scaffolding is in place and it makes for nice centralised 
management of params and user friendly help and error messages.


More information about the mkgmap-dev mailing list