The simplicity in JOpt "Simple" arises from two guiding principles:
To the first principle: You will not see support in JOpt Simple for option "groups", alternative option prefixes (+, /), enforced multiplicity of option arguments, etc. JOpt Simple believes you can create a useful and understandable CLI without all that stuff. If you feel as though you need any of those features, there are lots of other choices out there. The author of JOpt Simple believes you'll want to leverage its easy configuration, parsing, and option interrogation APIs instead of using more feature-laden, but perhaps more confusing libraries.
To the second principle: JOpt Simple will make every attempt to keep the API free of clutter. The API is well factored, making it intuitive to use, and the entire library is well tested, making it more reliable and predictable. If you cannot look at the Javadoc and quickly get a sense of what you need to do to use JOpt Simple, then JOpt Simple has failed. So by all means, let the author know what needs improved.
With that said, let's take a tour through JOpt Simple's features.
JOpt Simple supports short options and long options, using a syntax that attempts to take from the best of POSIX getopt() and GNU getopt_long().
Short options begin with a single hyphen (-) followed by a single letter or digit, or question mark (?), or dot (.).
package joptsimple.examples; import joptsimple.OptionParser; import joptsimple.OptionSet; import org.junit.Test; import static org.junit.Assert.*; public class ShortOptionsTest { @Test public void supportsShortOptions() { OptionParser parser = new OptionParser( "aB?*." ); OptionSet options = parser.parse( "-a", "-B", "-?" ); assertTrue( options.has( "a" ) ); assertTrue( options.has( "B" ) ); assertTrue( options.has( "?" ) ); assertFalse( options.has( "." ) ); } }
When you construct an OptionParser with a string of short option characters, you configure that parser to recognize the options with those characters.
Short options can accept single arguments. The argument can be made required or optional. When you construct an OptionParser with a string of short option characters, append a single colon (:) to an option character to configure that option to require an argument. Append two colons (::) to an option character to configure that option to accept an optional argument. Append an asterisk (*) to an option character, but before any "argument" indicators, to configure that option as a "help" option.
The syntax of the option specification string given to the OptionParser constructor should look familiar to you if you have used GNU's getopt() before.
package joptsimple.examples; import static java.util.Collections.*; import joptsimple.OptionParser; import joptsimple.OptionSet; import org.junit.Test; import static org.junit.Assert.*; public class ShortOptionsWithArgumentsTest { @Test public void allowsOptionsToAcceptArguments() { OptionParser parser = new OptionParser( "fc:q::" ); OptionSet options = parser.parse( "-f", "-c", "foo", "-q" ); assertTrue( options.has( "f" ) ); assertTrue( options.has( "c" ) ); assertTrue( options.hasArgument( "c" ) ); assertEquals( "foo", options.valueOf( "c" ) ); assertEquals( singletonList( "foo" ), options.valuesOf( "c" ) ); assertTrue( options.has( "q" ) ); assertFalse( options.hasArgument( "q" ) ); assertNull( options.valueOf( "q" ) ); assertEquals( emptyList(), options.valuesOf( "q" ) ); } }
A short option's argument can occur:
package joptsimple.examples; import joptsimple.OptionParser; import joptsimple.OptionSet; import org.junit.Test; import static org.junit.Assert.*; public class ShortOptionsWithArgumentPositioningTest { @Test public void allowsDifferentFormsOfPairingArgumentWithOption() { OptionParser parser = new OptionParser( "a:b:c::" ); OptionSet options = parser.parse( "-a", "foo", "-bbar", "-c=baz" ); assertTrue( options.has( "a" ) ); assertTrue( options.hasArgument( "a" ) ); assertEquals( "foo", options.valueOf( "a" ) ); assertTrue( options.has( "b" ) ); assertTrue( options.hasArgument( "b" ) ); assertEquals( "bar", options.valueOf( "b" ) ); assertTrue( options.has( "c" ) ); assertTrue( options.hasArgument( "c" ) ); assertEquals( "baz", options.valueOf( "c" ) ); } }
To specify n arguments for a single option, specify the option n times on the command line, once for each argument. JOpt Simple reports the arguments given to the option in the order in which they were encountered on the command line.
package joptsimple.examples; import static java.util.Arrays.*; import joptsimple.OptionException; import joptsimple.OptionParser; import joptsimple.OptionSet; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import static org.junit.Assert.*; import static org.junit.rules.ExpectedException.*; public class ShortOptionsWithMultipleArgumentsForSingleOptionTest { @Rule public final ExpectedException thrown = none(); @Test public void allowsMultipleValuesForAnOption() { OptionParser parser = new OptionParser( "a:" ); OptionSet options = parser.parse( "-a", "foo", "-abar", "-a=baz" ); assertTrue( options.has( "a" ) ); assertTrue( options.hasArgument( "a" ) ); assertEquals( asList( "foo", "bar", "baz" ), options.valuesOf( "a" ) ); thrown.expect( OptionException.class ); options.valueOf( "a" ); } }
Short options can be clustered in a single argument.
package joptsimple.examples; import joptsimple.OptionParser; import joptsimple.OptionSet; import org.junit.Test; import static org.junit.Assert.*; public class ShortOptionsClusteringTest { @Test public void allowsClusteringShortOptions() { OptionParser parser = new OptionParser( "aBcd" ); OptionSet options = parser.parse( "-cdBa" ); assertTrue( options.has( "a" ) ); assertTrue( options.has( "B" ) ); assertTrue( options.has( "c" ) ); assertTrue( options.has( "d" ) ); } }
If one of the short options can accept an argument, the remaining characters are interpreted as the argument for that option.
package joptsimple.examples; import joptsimple.OptionParser; import joptsimple.OptionSet; import org.junit.Test; import static org.junit.Assert.*; public class ShortOptionsClusteringWithArgumentTest { @Test public void allowsClusteringShortOptionsThatAcceptArguments() { OptionParser parser = new OptionParser(); parser.accepts( "a" ); parser.accepts( "B" ); parser.accepts( "c" ).withRequiredArg(); OptionSet options = parser.parse( "-aBcfoo" ); assertTrue( options.has( "a" ) ); assertTrue( options.has( "B" ) ); assertTrue( options.has( "c" ) ); assertEquals( "foo", options.valueOf( "c" ) ); } }
Long options begin with two hyphens (--), followed by multiple letters, digits, hyphens, question marks, or dots. A hyphen cannot be the first character of a long option specification when configuring the parser.
Whereas short options can be configured using a constructor argument to OptionParser, both long and short options can be configured using a "fluent interface" API, that enables some very descriptive and powerful features.
package joptsimple.examples; import joptsimple.OptionParser; import joptsimple.OptionSet; import org.junit.Test; import static org.junit.Assert.*; public class LongOptionsTest { @Test public void acceptsLongOptions() { OptionParser parser = new OptionParser(); parser.accepts( "flag" ); parser.accepts( "verbose" ); OptionSet options = parser.parse( "--flag" ); assertTrue( options.has( "flag" ) ); assertFalse( options.has( "verbose" ) ); } }
Like short options, long options can accept single arguments. The argument can be made required or optional. Use the methods withRequiredArg() and withOptionalArg() on the return value of OptionParser.accepts() to signal that an option takes a required or optional argument.
package joptsimple.examples; import static java.util.Collections.*; import joptsimple.OptionParser; import joptsimple.OptionSet; import org.junit.Test; import static org.junit.Assert.*; public class LongOptionsWithArgumentsTest { @Test public void supportsLongOptionsWithArgumentsAndAbbreviations() { OptionParser parser = new OptionParser(); parser.accepts( "flag" ); parser.accepts( "count" ).withRequiredArg(); parser.accepts( "level" ).withOptionalArg(); OptionSet options = parser.parse( "-flag", "--co", "3", "--lev" ); assertTrue( options.has( "flag" ) ); assertTrue( options.has( "count" ) ); assertTrue( options.hasArgument( "count" ) ); assertEquals( "3", options.valueOf( "count" ) ); assertEquals( singletonList( "3" ), options.valuesOf( "count" ) ); assertTrue( options.has( "level" ) ); assertFalse( options.hasArgument( "level" ) ); assertNull( options.valueOf( "level" ) ); assertEquals( emptyList(), options.valuesOf( "level" ) ); } @Test public void supportsLongOptionsWithEmptyArguments() { OptionParser parser = new OptionParser(); parser.accepts( "verbose" ); parser.accepts( "brief" ); parser.accepts( "add" ); parser.accepts( "append" ); parser.accepts( "delete" ).withRequiredArg(); parser.accepts( "create" ).withRequiredArg(); parser.accepts( "file" ).withRequiredArg(); OptionSet options = parser.parse( "--delete", "", "--add" ); assertTrue( options.has( "delete" ) ); assertTrue( options.hasArgument( "delete" ) ); assertEquals( "", options.valueOf( "delete" ) ); assertTrue( options.has( "add" ) ); options = parser.parse( "--delete=", "--add" ); assertTrue( options.has( "delete" ) ); assertTrue( options.hasArgument( "delete" ) ); assertEquals( "", options.valueOf( "delete" ) ); assertTrue( options.has( "add" ) ); } }
Notice in the example above that the command line uses abbreviations of command line options. You can abbreviate options so long as the abbreviation is unambiguous. Even though you can abbreviate the options on the command line, you cannot address the OptionSet using those abbreviations. You can use a special constructor for the OptionParser that turns off abbreviation matching.
As demonstrated in the example above, you can use a single hyphen instead of a double hyphen to specify a long option -- but be careful that doing so doesn't introduce ambiguity.
A long option's argument can occur:
package joptsimple.examples; import joptsimple.OptionParser; import joptsimple.OptionSet; import org.junit.Test; import static org.junit.Assert.*; public class LongOptionsWithArgumentPositioningTest { @Test public void allowsDifferentFormsOfPairingArgumentWithOption() { OptionParser parser = new OptionParser(); parser.accepts( "count" ).withRequiredArg(); parser.accepts( "level" ).withOptionalArg(); OptionSet options = parser.parse( "--count", "4", "--level=3" ); assertTrue( options.has( "count" ) ); assertTrue( options.hasArgument( "count" ) ); assertEquals( "4", options.valueOf( "count" ) ); assertTrue( options.has( "level" ) ); assertTrue( options.hasArgument( "level" ) ); assertEquals( "3", options.valueOf( "level" ) ); } }
The option -W is reserved. If you tell the parser to recognize alternative long options, then it will treat, for example, -W foo=bar as the long option foo with argument bar, as though you had written --foo=bar.
You can specify -W as a valid short option, or use it as an abbreviation for a long option, but recognizing alternative long options will always supersede this behavior.
To recognize alternative long options, either construct an OptionParser with a string of short option characters containing the sequence W; (a capital W followed by a semicolon), or call the method OptionParser.recognizeAlternativeLongOptions().
package joptsimple.examples; import joptsimple.OptionParser; import joptsimple.OptionSet; import org.junit.Test; import static org.junit.Assert.*; public class AlternativeLongOptionsTest { @Test public void handlesAlternativeLongOptions() { OptionParser parser = new OptionParser( "W;" ); parser.recognizeAlternativeLongOptions( true ); // same effect as above parser.accepts( "level" ).withRequiredArg(); OptionSet options = parser.parse( "-W", "level=5" ); assertTrue( options.has( "level" ) ); assertTrue( options.hasArgument( "level" ) ); assertEquals( "5", options.valueOf( "level" ) ); } }
Without action other than the with*Arg() methods, arguments of options are returned as Strings. For backwards compatibility, OptionSet.valueOf(String) and OptionSet.valuesOf(String) return Object and List<?>, respectively, so to get the values out as Strings, you will need to downcast the results of those methods.
You can tell JOpt Simple to convert the arguments of options to different Java types via the ofType() method on the return value of with*Arg(). The Class argument of ofType() must represent a Java class that has either:
If the class has both, the valueOf() method is used.
Note that enums have a valueOf() method.
package joptsimple.examples; import joptsimple.OptionParser; import joptsimple.OptionSet; import org.junit.Test; import static org.junit.Assert.*; public class OptionArgumentValueTypeTest { @Test public void convertsArgumentsToJavaValueTypes() { OptionParser parser = new OptionParser(); parser.accepts( "flag" ); parser.accepts( "count" ).withRequiredArg().ofType( Integer.class ); parser.accepts( "level" ).withOptionalArg().ofType( Level.class ); OptionSet options = parser.parse( "--count", "3", "--level", "DEBUG" ); assertTrue( options.has( "count" ) ); assertTrue( options.hasArgument( "count" ) ); assertEquals( 3, options.valueOf( "count" ) ); assertTrue( options.has( "level" ) ); assertTrue( options.hasArgument( "level" ) ); assertEquals( Level.DEBUG, options.valueOf( "level" ) ); } }
Another way to convert arguments of options is to specify a converter object via withValuesConvertedBy(). This is useful when the desired type for the arguments does not meet the requirements that ofType() sets forth. Such objects may not perform any "conversion" at all, but rather can validate that arguments conform to certain restrictions before passing through as-is.
You can also do this for the non-option arguments of your command line, if you desire to treat them all as a single type.
package joptsimple.examples; import joptsimple.OptionParser; import joptsimple.OptionSet; import org.joda.time.LocalDate; import org.junit.Test; import static joptsimple.util.DateConverter.*; import static joptsimple.util.RegexMatcher.*; import static org.junit.Assert.*; public class OptionArgumentConverterTest { @Test public void usesConvertersOnOptionArgumentsWhenTold() { OptionParser parser = new OptionParser(); parser.accepts( "birthdate" ).withRequiredArg().withValuesConvertedBy( datePattern( "MM/dd/yy" ) ); parser.accepts( "ssn" ).withRequiredArg().withValuesConvertedBy( regex( "\\d{3}-\\d{2}-\\d{4}" )); OptionSet options = parser.parse( "--birthdate", "02/24/05", "--ssn", "123-45-6789" ); assertEquals( new LocalDate( 2005, 2, 24 ).toDate(), options.valueOf( "birthdate" ) ); assertEquals( "123-45-6789", options.valueOf( "ssn" ) ); } }
In the previous examples, we have been discarding the return values of the methods of JOpt Simple's fluent interface. If instead you retain them in variables of type OptionSpec, you can use them to retrieve arguments of options in a type-safe manner.
You can also do this for the non-option arguments of your command line, if you desire to treat them all as a single type.
package joptsimple.examples; import java.io.File; import static java.util.Arrays.*; import joptsimple.OptionParser; import joptsimple.OptionSet; import joptsimple.OptionSpec; import org.junit.Test; import static java.util.Collections.*; import static org.junit.Assert.*; public class TypesafeOptionArgumentRetrievalTest { @Test public void allowsTypesafeRetrievalOfOptionArguments() { OptionParser parser = new OptionParser(); OptionSpec<Integer> count = parser.accepts( "count" ).withRequiredArg().ofType( Integer.class ); OptionSpec<File> outputDir = parser.accepts( "output-dir" ).withOptionalArg().ofType( File.class ); OptionSpec<Void> verbose = parser.accepts( "verbose" ); OptionSpec<File> files = parser.nonOptions().ofType( File.class ); OptionSet options = parser.parse( "--count", "3", "--output-dir", "/tmp", "--verbose", "a.txt", "b.txt" ); assertTrue( options.has( verbose ) ); assertTrue( options.has( count ) ); assertTrue( options.hasArgument( count ) ); Integer expectedCount = 3; assertEquals( expectedCount, options.valueOf( count ) ); assertEquals( expectedCount, count.value( options ) ); assertEquals( singletonList( expectedCount ), options.valuesOf( count ) ); assertEquals( singletonList( expectedCount ), count.values( options ) ); assertEquals( asList( new File( "a.txt" ), new File( "b.txt" ) ), options.valuesOf( files ) ); assertTrue( options.has( outputDir ) ); assertTrue( options.hasArgument( outputDir ) ); File expectedFile = new File( "/tmp" ); assertEquals( expectedFile, options.valueOf( outputDir ) ); assertEquals( expectedFile, outputDir.value( options ) ); assertEquals( singletonList( expectedFile ), options.valuesOf( outputDir ) ); assertEquals( singletonList( expectedFile ), outputDir.values( options ) ); assertEquals( asList( new File( "a.txt" ), new File( "b.txt" ) ), files.values( options ) ); } }
As an integration aid for other libraries, you can use OptionSet.asMap() to obtain a mapping of OptionSpec to option values, for example to create a properties map of options.
Here is sample code to create properties whose keys have a common prefix. The key is choosen as the first non-short option:
package joptsimple.examples; import com.google.common.base.Joiner; import joptsimple.OptionParser; import joptsimple.OptionSet; import joptsimple.OptionSpec; import org.junit.Test; import java.io.File; import java.util.List; import java.util.Map.Entry; import java.util.Properties; import static org.junit.Assert.assertEquals; public class ExportOptionsTest { private static Properties asProperties( OptionSet options, String prefix ) { Properties properties = new Properties(); for ( Entry<OptionSpec<?>, List<?>> entry : options.asMap().entrySet() ) { OptionSpec<?> spec = entry.getKey(); properties.setProperty( asPropertyKey( prefix, spec ), asPropertyValue( entry.getValue(), options.has( spec ) ) ); } return properties; } private static String asPropertyKey( String prefix, OptionSpec<?> spec ) { List<String> flags = spec.options(); for ( String flag : flags ) if ( 1 < flag.length() ) return null == prefix ? flag : ( prefix + '.' + flag ); throw new IllegalArgumentException( "No usable non-short flag: " + flags ); } private static String asPropertyValue( List<?> values, boolean present ) { // Simple flags have no values; treat presence/absence as true/false return values.isEmpty() ? String.valueOf( present ) : Joiner.on( "," ).join( values ); } @Test public void allowsExportOfOptions() { Properties expected = new Properties(); expected.setProperty( "rice.count", "3" ); // Cannot check path as string directly - Windows flips the leading slash expected.setProperty( "rice.output-dir", new File( "/tmp" ).toString() ); expected.setProperty( "rice.fun", "false" ); expected.setProperty( "rice.verbose", "true" ); OptionParser parser = new OptionParser(); OptionSpec<Integer> count = parser.accepts( "count" ).withRequiredArg().ofType( Integer.class ); OptionSpec<File> outputDir = parser.accepts( "output-dir" ).withOptionalArg().ofType( File.class ); OptionSpec<Void> verbose = parser.accepts( "verbose" ); OptionSpec<Void> fun = parser.accepts( "fun" ); OptionSpec<File> files = parser.nonOptions().ofType( File.class ); OptionSet options = parser.parse( "--count", "3", "--output-dir", "/tmp", "--verbose", "a.txt", "b.txt" ); assertEquals( expected, asProperties( options, "rice" ) ); } }
Often it is convenient to specify default values for the arguments of certain command line options. To do this, call the defaultsTo() method.
package joptsimple.examples; import java.io.File; import joptsimple.OptionException; import joptsimple.OptionParser; import joptsimple.OptionSet; import joptsimple.OptionSpec; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import static joptsimple.examples.Level.*; import static org.junit.Assert.*; import static org.junit.rules.ExpectedException.*; public class DefaultValuesForOptionArgumentsTest { @Rule public final ExpectedException thrown = none(); @Test public void allowsSpecificationOfDefaultValues() throws Exception { File tempDir = new File( System.getProperty( "java.io.tmpdir" ) ); File tempFile = File.createTempFile( "aFile", ".txt" ); OptionParser parser = new OptionParser(); OptionSpec<File> infile = parser.accepts( "infile" ).withRequiredArg().ofType( File.class ).defaultsTo( tempFile ); OptionSpec<File> outdir = parser.accepts( "outdir" ).withRequiredArg().ofType( File.class ).defaultsTo( tempDir ); OptionSpec<Integer> bufferSize = parser.accepts( "buffer-size" ).withOptionalArg().ofType( Integer.class ).defaultsTo( 4096 ); OptionSpec<Level> level = parser.accepts( "level" ).withOptionalArg().ofType( Level.class ).defaultsTo( INFO ); OptionSpec<Integer> count = parser.accepts( "count" ).withOptionalArg().ofType( Integer.class ).defaultsTo( 10 ); OptionSet options = parser.parse( "--level", "WARNING", "--count", "--infile", "/etc/passwd" ); assertEquals( new File( "/etc/passwd" ), infile.value( options ) ); assertTrue( options.has( infile ) ); assertTrue( options.hasArgument( infile ) ); assertEquals( tempDir, outdir.value( options ) ); assertFalse( options.has( outdir ) ); assertFalse( options.hasArgument( outdir ) ); assertEquals( Integer.valueOf( 4096 ), bufferSize.value( options ) ); assertFalse( options.has( bufferSize ) ); assertFalse( options.hasArgument( bufferSize ) ); assertEquals( WARNING, level.value( options ) ); assertTrue( options.has( level ) ); assertTrue( options.hasArgument( level ) ); assertEquals( Integer.valueOf( 10 ), count.value( options ) ); assertTrue( options.has( count ) ); assertFalse( options.hasArgument( count ) ); thrown.expect( OptionException.class ); parser.parse( "--outdir" ); } }
You can see that defaultsTo() should relieve you of the burden of having to check has() and/or hasArgument() on an OptionSet for a given option, and has no bearing on the return values of those methods. Specifying a default value for an option with a required argument does not mean that you can elide an argument for the option on the command line.
The type of values defaultsTo() expects is dictated by the class given by a previous call to ofType() or withValuesConvertedBy(); if no such call has been made, the type is String.
You can indicate that a given option must be present on the command line via the required() method. Only options that accept arguments can be made "required".
An option designated as a "help" option via forHelp(), when present on the command line, causes missing "required" options not to reject the command line.
package joptsimple.examples; import joptsimple.OptionException; import joptsimple.OptionParser; import joptsimple.OptionSet; import org.junit.Test; import static org.junit.Assert.*; public class RequiredOptionsTest { @Test( expected = OptionException.class ) public void allowsSpecificationOfRequiredOptions() { OptionParser parser = new OptionParser() { { accepts( "userid" ).withRequiredArg().required(); accepts( "password" ).withRequiredArg().required(); } }; parser.parse( "--userid", "bob" ); } @Test public void aHelpOptionMeansRequiredOptionsNeedNotBePresent() { OptionParser parser = new OptionParser() { { accepts( "userid" ).withRequiredArg().required(); accepts( "password" ).withRequiredArg().required(); accepts( "help" ).forHelp(); } }; OptionSet options = parser.parse( "--help" ); assertTrue( options.has( "help" ) ); } @Test( expected = OptionException.class ) public void missingHelpOptionMeansRequiredOptionsMustBePresent() { OptionParser parser = new OptionParser() { { accepts( "userid" ).withRequiredArg().required(); accepts( "password" ).withRequiredArg().required(); accepts( "help" ).forHelp(); } }; parser.parse( "" ); } }
You can indicate that a given option must be present on the command line if some other option is present on the command line via the requiredIf() method. Any option can be made "required if".
An option designated as a "help" option via forHelp(), when present on the command line, causes missing "required if" options not to reject the command line.
package joptsimple.examples; import joptsimple.OptionParser; public class RequiredIfExample { public static void main( String[] args ) { OptionParser parser = new OptionParser(); parser.accepts( "ftp" ); parser.accepts( "username" ).requiredIf( "ftp" ).withRequiredArg(); parser.accepts( "password" ).requiredIf( "ftp" ).withRequiredArg(); parser.parse( "--ftp" ); } }
You can also indicate that a given option must be present on the command line if some other option is NOT present on the command line via the requiredUnless() method. Any option can be made "required unless", but, to avoid potential conflicts, it should not be "required if" at the same time.
package joptsimple.examples; import joptsimple.OptionParser; public class RequiredUnlessExample { public static void main( String[] args ) { OptionParser parser = new OptionParser(); parser.accepts( "anonymous" ); parser.accepts( "username" ).requiredUnless( "anonymous" ).withRequiredArg(); parser.accepts( "password" ).requiredUnless( "anonymous" ).withRequiredArg(); parser.parse( "--anonymous" ); } }
Similarly to requiredIf() and requiredUnless(), you can indicate that a given option can be present on the command line only if/unless some other option is present on the command line, via the availableIf() and availableUnless() methods.
Sometimes it is useful to allow many different options to share the same meaning in the program that uses them. To specify that options are to be treated as synonymous, use the acceptsAll() method of OptionParser.
package joptsimple.examples; import java.util.List; import static java.util.Arrays.*; import joptsimple.OptionParser; import joptsimple.OptionSet; import org.junit.Test; import static java.util.Collections.*; import static org.junit.Assert.*; public class OptionSynonymTest { @Test public void supportsOptionSynonyms() { OptionParser parser = new OptionParser(); List<String> synonyms = asList( "message", "blurb", "greeting" ); parser.acceptsAll( synonyms ).withRequiredArg(); String expectedMessage = "Hello"; OptionSet options = parser.parse( "--message", expectedMessage ); for ( String each : synonyms ) { assertTrue( each, options.has( each ) ); assertTrue( each, options.hasArgument( each ) ); assertEquals( each, expectedMessage, options.valueOf( each ) ); assertEquals( each, singletonList( expectedMessage ), options.valuesOf( each ) ); } } }
Another way to specify multiple arguments for an option is to tell the parser to treat a single argument containing multiple delimited values as multiple arguments for the option using the withValuesSeparatedBy() method.
package joptsimple.examples; import java.io.File; import java.util.stream.Stream; import static java.io.File.*; import static java.util.Arrays.*; import static java.util.stream.Collectors.*; import joptsimple.OptionParser; import joptsimple.OptionSet; import joptsimple.OptionSpec; import org.junit.Test; import static org.junit.Assert.*; public class MultipleDelimitedArgumentsTest { @Test public void supportsMultipleDelimitedArguments() { OptionParser parser = new OptionParser(); OptionSpec<File> path = parser.accepts( "path" ).withRequiredArg().ofType( File.class ) .withValuesSeparatedBy( pathSeparatorChar ); OptionSet options = parser.parse( "--path", Stream.of( "/tmp", "/var", "/opt" ).collect( joining( pathSeparator ) ) ); assertTrue( options.has( path ) ); assertTrue( options.hasArgument( path ) ); assertEquals( asList( new File( "/tmp" ), new File( "/var" ), new File( "/opt" ) ), options.valuesOf( path ) ); } }
An argument consisting only of two hyphens (--) signals that the remaining arguments are to be treated as non-options.
An argument consisting only of a single hyphen is considered a non-option argument (though it can be an argument of an option). Many Unix programs treat single hyphens as stand-ins for the standard input or standard output stream.
Any arguments which are not options or arguments of options can be retrieved via method nonOptionArguments() on OptionSet. If the double hyphen is an argument, it is ignored and is not a non-option argument.
package joptsimple.examples; import static java.util.Arrays.*; import static java.util.Collections.*; import joptsimple.OptionParser; import joptsimple.OptionSet; import org.junit.Test; import static org.junit.Assert.*; public class SignallingEndOfOptionsTest { @Test public void doubleHyphenSignalsEndOfOptions() { OptionParser parser = new OptionParser( "ab:c::de:f::" ); OptionSet options = parser.parse( "-a", "-b=foo", "-c=bar", "--", "-d", "-e", "baz", "-f", "biz" ); assertTrue( options.has( "a" ) ); assertFalse( options.hasArgument( "a" ) ); assertTrue( options.has( "b" ) ); assertTrue( options.hasArgument( "b" ) ); assertEquals( singletonList( "foo" ), options.valuesOf( "b" ) ); assertTrue( options.has( "c" ) ); assertTrue( options.hasArgument( "c" ) ); assertEquals( singletonList( "bar" ), options.valuesOf( "c" ) ); assertFalse( options.has( "d" ) ); assertFalse( options.has( "e" ) ); assertFalse( options.has( "f" ) ); assertEquals( asList( "-d", "-e", "baz", "-f", "biz" ), options.nonOptionArguments() ); } }
By default, as with GNU getopt(), JOpt Simple allows intermixing of options and non-options. If, however, the parser has been created to be "POSIX-ly correct", then the first argument that does not look lexically like an option, and is not a required argument of a preceding option, signals the end of options. You can still bind optional arguments to their options using the abutting (for short options) or = syntax.
Unlike GNU getopt(), JOptSimple does not honor the environment variable POSIXLY_CORRECT. "POSIX-ly correct" parsers are configured by either:
package joptsimple.examples; import static java.util.Arrays.*; import static java.util.Collections.*; import joptsimple.OptionParser; import joptsimple.OptionSet; import org.junit.Test; import static org.junit.Assert.*; public class PosixlyCorrectTest { @Test public void supportsPosixlyCorrectBehavior() { OptionParser parser = new OptionParser( "i:j::k" ); String[] arguments = { "-ibar", "-i", "junk", "xyz", "-jixnay", "foo", "-k", "blah", "--", "bah" }; OptionSet options = parser.parse( arguments ); assertTrue( options.has( "i" ) ); assertTrue( options.has( "j" ) ); assertTrue( options.has( "k" ) ); assertEquals( asList( "bar", "junk" ), options.valuesOf( "i" ) ); assertEquals( singletonList( "ixnay" ), options.valuesOf( "j" ) ); assertEquals( asList( "xyz", "foo", "blah", "bah" ), options.nonOptionArguments() ); parser.posixlyCorrect( true ); options = parser.parse( arguments ); assertTrue( options.has( "i" ) ); assertFalse( options.has( "j" ) ); assertFalse( options.has( "k" ) ); assertEquals( asList( "bar", "junk" ), options.valuesOf( "i" ) ); assertEquals( emptyList(), options.valuesOf( "j" ) ); assertEquals( asList( "xyz", "-jixnay", "foo", "-k", "blah", "--", "bah" ), options.nonOptionArguments() ); } }
If the parser detects an option whose argument is optional, and the next argument "looks like" an option, that argument is not treated as the argument to the option, but as a potentially valid option. If, on the other hand, the optional argument is typed as a derivative of Number, then that argument is treated as the negative number argument of the option, even if the parser recognizes the corresponding numeric option.
package joptsimple.examples; import static java.util.Collections.*; import joptsimple.OptionParser; import joptsimple.OptionSet; import org.junit.Test; import static org.junit.Assert.*; public class SpecialOptionalArgumentHandlingTest { @Test public void handlesNegativeNumberOptionalArguments() { OptionParser parser = new OptionParser(); parser.accepts( "a" ).withOptionalArg().ofType( Integer.class ); parser.accepts( "2" ); OptionSet options = parser.parse( "-a", "-2" ); assertTrue( options.has( "a" ) ); assertFalse( options.has( "2" ) ); assertEquals( singletonList( -2 ), options.valuesOf( "a" ) ); options = parser.parse( "-2", "-a" ); assertTrue( options.has( "a" ) ); assertTrue( options.has( "2" ) ); assertEquals( emptyList(), options.valuesOf( "a" ) ); } }
When you call method OptionParser.printHelpOn(), JOpt Simple will write a help screen (80-column width) describing all the options it is configured with, along with types of option arguments, whether the option is required (in angle brackets) or optional (in square brackets), etc. To give an option a description, use OptionParser.accepts*() with a description argument. To give an option argument a description, use describedAs() on the return value of with*Arg().
package joptsimple.examples; import java.io.File; import static java.io.File.*; import static java.util.Arrays.*; import joptsimple.OptionParser; import static joptsimple.util.DateConverter.*; public class HelpScreenExample { public static void main( String[] args ) throws Exception { OptionParser parser = new OptionParser() { { accepts( "c" ).withRequiredArg().ofType( Integer.class ) .describedAs( "count" ).defaultsTo( 1 ); accepts( "q" ).withOptionalArg().ofType( Double.class ) .describedAs( "quantity" ); accepts( "d", "some date" ).withRequiredArg().required() .withValuesConvertedBy( datePattern( "MM/dd/yy" ) ); acceptsAll( asList( "v", "talkative", "chatty" ), "be more verbose" ); accepts( "output-file" ).withOptionalArg().ofType( File.class ) .describedAs( "file" ); acceptsAll( asList( "h", "?" ), "show help" ).forHelp(); acceptsAll( asList( "cp", "classpath" ) ).withRequiredArg() .describedAs( "path1" + pathSeparatorChar + "path2:..." ) .ofType( File.class ) .withValuesSeparatedBy( pathSeparatorChar ); } }; parser.printHelpOn( System.out ); } }
Here is what the help screen looks like for the example above:
Option (* = required) Description --------------------- ----------- -?, -h show help -c <Integer: count> (default: 1) --classpath, --cp <File: path1: path2:...> * -d <MM/dd/yy> some date --output-file [File: file] -q [Double: quantity] -v, --chatty, --talkative be more verbose
If you want to create your own help screen, give method OptionParser.formatHelpWith() a HelpFormatter that builds the help screen as a String. When you call OptionParser.printHelpOn(), JOpt Simple will use your HelpFormatter to produce the help and write it to the given stream.
For example, this program:
package joptsimple.examples; import java.io.File; import java.util.HashSet; import java.util.Map; import static java.io.File.*; import static java.util.Arrays.*; import joptsimple.HelpFormatter; import joptsimple.OptionDescriptor; import joptsimple.OptionParser; import static joptsimple.util.DateConverter.*; public class HelpFormatterExample { private static class MyFormatter implements HelpFormatter { public String format( Map<String, ? extends OptionDescriptor> options ) { StringBuilder buffer = new StringBuilder(); for ( OptionDescriptor each : new HashSet<>( options.values() ) ) { buffer.append( lineFor( each ) ); } return buffer.toString(); } private String lineFor( OptionDescriptor descriptor ) { if ( descriptor.representsNonOptions() ) { return descriptor.argumentDescription() + '(' + descriptor.argumentTypeIndicator() + "): " + descriptor.description() + System.getProperty( "line.separator" ); } StringBuilder line = new StringBuilder( descriptor.options().toString() ); line.append( ": description = " ).append( descriptor.description() ); line.append( ", required = " ).append( descriptor.isRequired() ); line.append( ", accepts arguments = " ).append( descriptor.acceptsArguments() ); line.append( ", requires argument = " ).append( descriptor.requiresArgument() ); line.append( ", argument description = " ).append( descriptor.argumentDescription() ); line.append( ", argument type indicator = " ).append( descriptor.argumentTypeIndicator() ); line.append( ", default values = " ).append( descriptor.defaultValues() ); line.append( System.getProperty( "line.separator" ) ); return line.toString(); } } public static void main( String[] args ) throws Exception { OptionParser parser = new OptionParser() { { accepts( "c" ).withRequiredArg().ofType( Integer.class ) .describedAs( "count" ).defaultsTo( 1 ); accepts( "q" ).withOptionalArg().ofType( Double.class ) .describedAs( "quantity" ); accepts( "d", "some date" ).withRequiredArg().required() .withValuesConvertedBy( datePattern( "MM/dd/yy" ) ); acceptsAll( asList( "v", "talkative", "chatty" ), "be more verbose" ); accepts( "output-file" ).withOptionalArg().ofType( File.class ) .describedAs( "file" ); acceptsAll( asList( "h", "?" ), "show help" ).forHelp(); acceptsAll( asList( "cp", "classpath" ) ).withRequiredArg() .describedAs( "path1" + pathSeparatorChar + "path2:..." ) .ofType( File.class ) .withValuesSeparatedBy( pathSeparatorChar ); nonOptions( "files to chew on" ).ofType( File.class ).describedAs( "input files" ); } }; parser.formatHelpWith( new MyFormatter() ); parser.printHelpOn( System.out ); } }
yields the following output:
[c]: description = , required = false, accepts arguments = true, requires argument = true, argument description = count, argument type indicator = java.lang.Integer, default values = [1] [d]: description = some date, required = true, accepts arguments = true, requires argument = true, argument description = , argument type indicator = MM/dd/yy, default values = [] [classpath, cp]: description = , required = false, accepts arguments = true, requires argument = true, argument description = path1:path2:..., argument type indicator = java.io.File, default values = [] [?, h]: description = show help, required = false, accepts arguments = false, requires argument = false, argument description = , argument type indicator = , default values = [] input files(java.io.File): files to chew on [q]: description = , required = false, accepts arguments = true, requires argument = false, argument description = quantity, argument type indicator = java.lang.Double, default values = [] [output-file]: description = , required = false, accepts arguments = true, requires argument = false, argument description = file, argument type indicator = java.io.File, default values = [] [v, chatty, talkative]: description = be more verbose, required = false, accepts arguments = false, requires argument = false, argument description = , argument type indicator = , default values = []
JOpt Simple's classes raise some derivative of OptionException if they encounter problems during parsing. These exceptions are unchecked, so you don't have to do anything with such an exception if you don't want to. The rationale behind this decision is that you will most likely be invoking JOpt Simple's functionality from a main() method or very near to it, where a failure such as unrecognized arguments can just stop down the JVM and yield a stack trace without much user or programmer inconvenience. So, without any exception handling at all, a user would see something like this:
Exception in thread "main" joptsimple.UnrecognizedOptionException: x is not a recognized option at joptsimple.OptionException.unrecognizedOption(OptionException.java:108) at joptsimple.OptionParser.validateOptionCharacters(OptionParser.java:642) at joptsimple.OptionParser.handleShortOptionCluster(OptionParser.java:537) at joptsimple.OptionParser.handleShortOptionToken(OptionParser.java:532) at joptsimple.OptionParserState$2.handleArgument(OptionParserState.java:59) at joptsimple.OptionParser.parse(OptionParser.java:405) at joptsimple.examples.ExceptionExample.main(ExceptionExample.java:9)
If you want to handle the exception yourself, you can catch OptionException in your code, and do whatever you please with the contents of the exception, perhaps using the help generation facility.
Sometimes you want to ignore unrecognized options on the command line.
For example, you might be interested in handling only a part of the arguments given. Or you might want to pass on options to another program and not bother the user with providing two hyphens (--) to indicate the end of known options. Or maybe you want to provide future forwards/backwards compatibility when you foresee passing in new options to old code (or old code invoking new code with "old" arguments).
You can achieve this by using the method OptionParser.allowsUnrecognizedOptions(). When you call this method, then any unrecognized options handed to parse() are treated as non-option arguments, rather than causing an exception to be raised.
package joptsimple.examples; import joptsimple.OptionParser; import joptsimple.OptionSet; import org.junit.Test; import static java.util.Collections.*; import static org.junit.Assert.*; public class UnrecognizedOptionsAllowedTest { @Test public void acceptsLongOptions() { OptionParser parser = new OptionParser(); parser.allowsUnrecognizedOptions(); parser.accepts( "f" ); OptionSet options = parser.parse( "-f", "-d" ); assertTrue( options.has( "f" ) ); assertFalse( options.has( "d" ) ); assertEquals( singletonList( "-d" ), options.nonOptionArguments() ); } }