// Copyright (C) 2001 - 2007 by Oliver Goldman // All Rights Reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted. // // Additional information available at http://software.charlie-dog.com. //package com.charliedog.argv; package org.freehep.util.argv; import java.util.*; import java.io.*; import java.math.*; /** * A parser for processing command line arguments. It is typically used as such: * <PRE> * // Initialize arguments that may appear in the command line. By convention, * // '-help' simply prints the command line usage and exits. * * StringOption destination = new StringOption( "-dest", "localhost", "Destination for request tests" ); * BooleanOption help = new BooleanOption( "-help", "Describe command line args", true ); * * // Initialize and invoke the parser. Note that arguments not consumed during * // the parse are returned in case they may be subject to additional processing, * // etc. Variable 'args' is assumed to contain the String array passed to main(). * * ArgumentParser parser = new ArgumentParser(); * parser.add( destination ); * parser.add( skipClasses ); * parser.add( skipRequests ); * List extra = parser.parse( args ); * * // For this application, extra arguments will be treated as a usage error. * * if( !extra.isEmpty() || help.getValue()) * { * PrintWriter out = new PrintWriter( System.out ); * parser.printUsage( out ); * out.close(); * System.exit( 0 ); * } * </PRE> * * @see BooleanOption * @see StringOption */ public class ArgumentParser { private List/*<Option>*/ options = new LinkedList(); private List/*<Parameter>*/ parameters = new LinkedList(); private String name; public ArgumentParser(String name) { this.name = name; } /** * Add a new option to be taken into consideration during the next parse. * Behavior when an option is added multiple times or if multiple options * share the same flag is undefined. */ public void add( Option opt ) { options.add( opt ); } public void add( Parameter param ) { parameters.add( param ); } /** * Dumps the usage of the program and each parameter and option to out. */ public void printUsage( OutputStream out ) { PrintStream p = new PrintStream(out); p.print( "Usage: "+name); if (!options.isEmpty()) p.print(" [-options]"); if (!parameters.isEmpty()) { Iterator i1 = parameters.iterator(); while( i1.hasNext()) { Parameter param = (Parameter)( i1.next()); p.print(" "); p.print(param.getName()); } p.println(); p.println(); p.println( "parameters:" ); Iterator i2 = parameters.iterator(); int w = 0; while( i2.hasNext()) { Parameter param = (Parameter)( i2.next()); w = Math.max(w, param.getName().length()); } Iterator i3 = parameters.iterator(); while( i3.hasNext()) { Parameter param = (Parameter)( i3.next()); p.print(" "); p.print(pad(param.getName(), w)); p.print(" "); p.println(param.getUsage()); } } else { p.println(); } if (!options.isEmpty()) { p.println(); p.println( "options:" ); Iterator i1 = options.iterator(); int w = 0; while( i1.hasNext()) { Option opt = (Option)( i1.next()); w = Math.max(w, opt.getOption().length()); } Iterator i2 = options.iterator(); while( i2.hasNext()) { Option opt = (Option)( i2.next()); p.print(" "); p.print(pad(opt.getOption(), w)); p.print(" "); p.println(opt.getUsage()); } } } /** * Parses the given argument list according to all Options * registered with this parser. Returns any arguments not * consumed by the parse. */ public List parse( String args[] ) throws MissingArgumentException, ArgumentFormatException { List/*<String>*/ list = Arrays.asList( args ); return parse(list); } /** * Parses the given argument list according to all Options * registered with this parser. Returns any arguments not * consumed by the parse. */ public List parse( List/*<String>*/ args ) throws MissingArgumentException, ArgumentFormatException { List extras = new LinkedList(); try { perValue: while( !args.isEmpty()) { // Give each Option a shot at parsing the list in its // current form. Stop on the first match. Iterator i = options.iterator(); while( i.hasNext()) { Option opt = (Option)( i.next()); int numArgsConsumed = opt.parse( args ); if( numArgsConsumed > 0 ) { args = args.subList( numArgsConsumed, args.size()); continue perValue; } } // If no matches were found, move the first value to the extras // list and try again. Don't use values.remove( 0 ) here because // it is an optional method. extras.add( args.get( 0 )); args = args.subList( 1, args.size()); } // now parse the parameters in order Iterator i = parameters.iterator(); while( i.hasNext()) { Parameter parameter = (Parameter)( i.next()); int numArgsConsumed = parameter.parse( extras ); if( numArgsConsumed > 0 ) { extras = extras.subList( numArgsConsumed, extras.size()); } } } catch (BailOutException boe) { // bailed out, no extras return Collections.EMPTY_LIST; } return extras; } private String pad(String s, int w) { if (w < s.length()) return s.substring(0, w); StringBuffer sb = new StringBuffer(s); for (int i=w-s.length(); i>0; i--) { sb.append(" "); } return sb.toString(); } /** * Test driver. */ public static void main( String ignored[] ) throws MissingArgumentException, ArgumentFormatException { String empty[] = {}; String exact[] = { "-string1", "value1", "-n1", "0.0314159e2" }; String extra[] = { "-string1", "value1", "-n2", "10", "-bool1", "-string2", "value2" }; StringOption string1 = new StringOption( "-string1", "string1", "" ); StringOption string3 = new StringOption( "-string3", "string3", "value3", "" ); BooleanOption bool1 = new BooleanOption( "-bool1", "" ); NumberOption n1 = new NumberOption( "-n1", "n1", "" ); ArgumentParser parser = new ArgumentParser("ArgumentParser"); parser.add( string1 ); parser.add( string3 ); parser.add( bool1 ); parser.add( n1 ); // ---------------------------------------------------------------------- System.out.println( "Test 01-001: Run arg parser w/ no args" ); parser.parse( empty ); if( string1.getValue() != null ) { throw new RuntimeException( "Default value for string1 failed" ); } if( string3.getValue() != "value3" ) { throw new RuntimeException( "Default value for string3 failed" ); } if( bool1.getValue() != false ) { throw new RuntimeException( "Default value for bool1 failed" ); } if( n1.getValue() != null ) { throw new RuntimeException( "Default value for n1 failed" ); } // ---------------------------------------------------------------------- System.out.println( "Test 01-002: Run arg parser w/ args" ); List remaining = parser.parse( exact ); System.out.println( "Test 02-001: Check string1" ); if( !string1.getValue().equals( "value1" )) { throw new RuntimeException( "Parsing string1 failed" ); } if( string3.getValue() != "value3" ) { throw new RuntimeException( "Default value for string3 failed" ); } if( bool1.getValue() != false ) { throw new RuntimeException( "Default value for bool1 failed" ); } if( !n1.getValue().equals( new BigDecimal( "3.14159" ))) { throw new RuntimeException( "Default value for n1 failed" ); } if( !remaining.isEmpty()) { throw new RuntimeException( "Should be no remaining args" ); } // ---------------------------------------------------------------------- System.out.println( "Test 01-003: Run arg parser w/ extra args" ); remaining = parser.parse( extra ); System.out.println( "Test 02-001: Check string1" ); if( !string1.getValue().equals( "value1" )) { throw new RuntimeException( "Parsing string1 failed" ); } System.out.println( "Test 02-002: Check bool1" ); if( !bool1.getValue()) { throw new RuntimeException( "bool1 should have been true" ); } System.out.println( "Test 02-003: Check remaining args" ); if( !remaining.equals( Arrays.asList( new String[] { "-n2", "10", "-string2", "value2" }))) { throw new RuntimeException( "Remaining args incorrect" ); } System.out.println( "Test 02-004: Check string3" ); if( !string3.getValue().equals( "value3" )) { throw new RuntimeException( "Default for string3 failed" ); } // ---------------------------------------------------------------------- System.out.println( "Test 3: ListArgument" ); ListParameter lp = new ListParameter( "vacuum", "list of vacuums" ); parser.add( lp ); remaining = parser.parse( extra ); // List will consume everything starting with -n2, which is the // first argument not recognized by some other parser. if( !lp.getValue().equals( Arrays.asList( new String[] { "-n2", "10", "-bool1", "-string2", "value2" }))) { throw new RuntimeException( "List args incorrect" ); } } }