- Introduction
- Examples
- License
- Requirements
- Generating Javadocs
- Automated Testing
- Installing
- Building
ArgMatey is a Java command line interface that has the following features:
Command line option types whose syntax and behavior are based on the POSIX Utility Conventions, GNU's function getopt_long, and GNU's Argp interface:
- GNU long options (examples:
--help
--version
--output-file=file.txt
) - Long options (examples:
-help
-version
-output-file file.txt
) - POSIX options (examples:
-h
-v
-o file.txt
)
Command line argument parsing and handling with triggerable event methods, query methods, iterator methods, and descriptive methods.
To parse and handle command line arguments, a class must extend CLI
and have
its constructor invoke the superclass's constructor
CLI(String[] args, boolean posixCorrect)
and the instance of that
extending class must have its inherited public method CLI.handleArgs()
invoked. The extending class can have, override, or use the following methods:
Triggerable event methods that can be added:
@Option
annotated methods: invoked when any of the command line options defined by the method's@Option
annotations is encountered
Triggerable event methods that can be overridden:
CLI.beforeHandleArgs()
: invoked before parsing and handling the command line argumentsCLI.afterHandleArgs()
: invoked after parsing and handling the command line argumentsCLI.beforeHandleNext()
: invoked before parsing and handling the next part of the current command line argument or the next command line argumentCLI.afterHandleNext()
: invoked after parsing and handling the next part of the current command line argument or the next command line argumentCLI.displayProgramHelp()
: invoked when one of the command line options for the program help (--help
) is encountered and displays the program helpCLI.displayProgramVersion()
: invoked when one of the command line options for the program version (--version
) is encountered and displays the program versionCLI.handleNonparsedArg(String)
: invoked when a non-parsed command line argument is encounteredCLI.handleThrowable(Throwable)
: invoked when aThrowable
is thrown when parsing and handling the next part of the command line argument or the next command line argument
Query methods:
CLI.getArg()
: returns the current command line argumentCLI.getArg(int)
: returns the command line argument of the provided indexCLI.getArgCharIndex()
: returns the current character index of the current command line argumentCLI.getArgCount()
: returns the count of the command line argumentsCLI.getArgIndex()
: return the index of the current command line argumentCLI.getArgs()
: returns an array of the command line argumentsCLI.getParseResultHolder()
: returns the currentParseResultHolder
Iterator methods:
CLI.hasNext()
: returns a boolean value to indicate if thisCLI
has the next part of the current command line argument or the next command line argumentCLI.next()
: returns the next part of the current command line argument or the next command line argument
Descriptive methods:
CLI.getOptionGroups()
: returns an instance ofOptionGroups
defined by the@Option
annotated methodsCLI.getProgramDoc()
/CLI.setProgramDoc(String)
: returns/sets the documentation (description) of the programCLI.getProgramName()
/CLI.setProgramName(String)
: returns/sets the name of the programCLI.getProgramOperandsUsage()
/CLI.setProgramOperandsUsage(String)
: returns/sets the usage of the operands of the programCLI.getProgramVersion()
/CLI.setProgramVersion(String)
: returns/sets the version of the program
This style of command line argument parsing and handling has the following advantages:
- Custom interpretation of multiple occurrences of the same command line option
- Custom interpretation of multiple occurrences of command line options from the same group
- Custom interpretation of command line options and arguments based on the ordering provided
- Custom adaptability of the program based on the command line option or argument encountered
An example of using ArgMatey's command line argument parsing and handling can be found here.
Complete customization of the program usage and help. Every level of the program usage and help can be customized. The usage of one, a few, or all command line options of a particular type can be customized in a different format. The help text of one, a few, or all command line option groups can be customized in a different format. The entire program usage and help can be customized in a completely different format.
An example of customizing one aspect of the program help can be found here.
Single source code file. As an alternative to importing ArgMatey as a Maven dependency or a JAR file, ArgMatey can be imported to a project as a source code file.
The following is an example of using ArgMatey:
import com.github.jh3nd3rs0n.argmatey.ArgMatey;
import com.github.jh3nd3rs0n.argmatey.ArgMatey.Annotations.Option;
import com.github.jh3nd3rs0n.argmatey.ArgMatey.Annotations.OptionArgSpec;
import com.github.jh3nd3rs0n.argmatey.ArgMatey.CLI;
import com.github.jh3nd3rs0n.argmatey.ArgMatey.OptionType;
import com.github.jh3nd3rs0n.argmatey.ArgMatey.TerminationRequestedException;
/*
* Extends CLI to parse and handle command line options and
* arguments for this Java implementation of GNU's utility
* base64.
*/
public class Base64CLI extends CLI {
private int columnLimit;
private boolean decodingMode;
private String file;
private boolean garbageIgnored;
public Base64CLI(String[] args) {
super(args, false);
// for the program help and version information
this.setProgramDoc(new StringBuilder()
.append("Base64 encode or decode FILE, ")
.append("or standard input, to standard output.")
.append(String.format("%n%n"))
.append("With no FILE, or when FILE is -, ")
.append("read standard input.")
.toString());
this.setProgramName("base64");
this.setProgramOperandsUsage("[FILE]");
this.setProgramVersion("base64 1.0");
}
/*
* Invoked after parsing and handling the command line
* arguments.
*/
@Override
protected void afterHandleArgs()
throws TerminationRequestedException {
// post parsing can happen here
System.out.printf("columnLimit: %s%n", this.columnLimit);
System.out.printf("decodingMode: %s%n", this.decodingMode);
System.out.printf("file: %s%n", this.file);
System.out.printf("garbageIgnored: %s%n", this.garbageIgnored);
throw new TerminationRequestedException(0);
}
/*
* Invoked after parsing and handling the next part of the
* current command line argument or the next command line
* argument.
*/
@Override
protected void afterHandleNext()
throws TerminationRequestedException {
}
/*
* Invoked before parsing and handling the command line
* arguments.
*/
@Override
protected void beforeHandleArgs()
throws TerminationRequestedException {
// initialization
this.columnLimit = 76; // default
this.decodingMode = false;
this.file = null;
this.garbageIgnored = false;
}
/*
* Invoked before parsing and handling the next part of the
* current command line argument or the next command line
* argument.
*/
@Override
protected void beforeHandleNext()
throws TerminationRequestedException {
}
/*
* Invoked when a non-parsed command line argument is
* encountered. In this implementation, the non-parsed
* command line argument is the command line argument for
* FILE. The non-parsed command line argument is provided
* as an argument to the method parameter below.
*/
@Override
protected void handleNonparsedArg(String nonparsedArg)
throws TerminationRequestedException {
if (this.file != null) {
/*
* IllegalArgumentExceptions for invalid non-parsed
* command line arguments can be thrown.
*/
throw new IllegalArgumentException(String.format(
"extra operand: `%s'", nonparsedArg));
}
this.file = nonparsedArg;
}
/*
* Invoked when a Throwable is thrown when parsing and
* handling the next part of the command line argument or
* the next command line argument.
*/
@Override
protected void handleThrowable(Throwable t)
throws TerminationRequestedException {
System.err.printf("%s: %s%n", this.getProgramName(), throwable);
throw new TerminationRequestedException(-1);
}
/*
* Invoked when either of the command line options "-w" and
* "--wrap" is encountered. Command line option argument
* for either of the command line options "-w" and "--wrap"
* is provided as an argument to the method parameter below.
*
* Methods that are annotated with @Option that allow for a
* command line option argument must have only one method
* parameter. The method parameter's type must be a type or
* a java.util.List of a type that has either a static
* String conversion method or a constructor that has only
* one constructor parameter of type String.
*
* Alternatively, for any method parameter type, a class
* that extends StringConverter can be used to convert the
* command line option argument to that type. It can be
* supplied to @OptionArgSpec.stringConverter.
*/
@Option(
doc = "Wrap encoded lines after COLS character (default is 76)",
name = "w",
optionArgSpec = @OptionArgSpec(name = "COLS"),
type = OptionType.POSIX
)
@Option(
name = "wrap",
// optionArgSpec is provided from the above @Option.
type = OptionType.GNU_LONG
)
private void setColumnLimit(Integer i) {
int intValue = i.intValue();
if (intValue < 0) {
/*
* IllegalArgumentExceptions for invalid command
* line option arguments can be thrown.
*/
throw new IllegalArgumentException(
"must be a non-negative integer");
}
this.columnLimit = intValue;
}
/*
* Invoked when either of the command line options "-d" and
* "--decode" is encountered.
*
* Methods that are annotated with @Option that does not
* allow for a command line option argument can have no
* method parameters or only one method parameter of type
* boolean.
*/
@Option(
doc = "Decode data",
name = "d",
type = OptionType.POSIX
)
@Option(
name = "decode",
type = OptionType.GNU_LONG
)
private void setDecodingMode(boolean b) {
this.decodingMode = b; // always received as true
}
/*
* Invoked when either of the command line options "-i" and
* "--ignore-garbage" is encountered.
*
* Methods that are annotated with @Option that does not
* allow for a command line option argument can have no
* method parameters or only one method parameter of type
* boolean.
*/
@Option(
doc = "When decoding, ignore non-alphabet characters",
name = "i",
type = OptionType.POSIX
)
@Option(
name = "ignore-garbage",
type = OptionType.GNU_LONG
)
private void setGarbageIgnored(boolean b) {
this.garbageIgnored = b; // always received as true
}
public static void main(String[] args) {
// how to use a CLI such as Base64CLI
CLI cli = new Base64CLI(args);
try {
cli.handleArgs();
} catch (TerminationRequestedException e) {
System.exit(e.getExitStatusCode());
}
}
/*
* Output when using the command line option "--help" :
*
* Usage: base64 [OPTION]... [FILE]
* Base64 encode or decode FILE, or standard input, to standard output.
*
* With no FILE, or when FILE is -, read standard input.
*
* OPTIONS:
* -d, --decode
* Decode data
* --help
* Display this help and exit
* -i, --ignore-garbage
* When decoding, ignore non-alphabet characters
* --version
* Display version information and exit
* -w COLS, --wrap=COLS
* Wrap encoded lines after COLS character (default is 76)
*
*/
/*
* Output when using the command line option "--version" :
*
* base64 1.0
*
*/
/*
* Output when using the following command line arguments:
*
* -w 100 input.txt
*
* columnLimit: 100
* decodingMode: false
* file: input.txt
* garbageIgnored: false
*
*/
/*
* Output when using the following command line arguments:
*
* -di text.txt
*
* columnLimit: 76
* decodingMode: true
* file: text.txt
* garbageIgnored: true
*
*/
/*
* Output when using the following command line arguments:
*
* --wrap=10 -
*
* columnLimit: 10
* decodingMode: false
* file: -
* garbageIgnored: false
*
*/
/*
* Output when using the following command line arguments:
*
* --decode --ignore-garbage text.txt
*
* columnLimit: 76
* decodingMode: true
* file: text.txt
* garbageIgnored: true
*
*/
/*
* Output when using the bogus command line option "--bogus":
*
* base64: com.github.jh3nd3rs0n.argmatey.ArgMatey$UnknownOptionException: unknown option `--bogus'
*
*/
/*
* Output when using the following command line arguments:
*
* --wrap=-42 input.txt
*
* base64: com.github.jh3nd3rs0n.argmatey.ArgMatey$IllegalOptionArgException: illegal option argument `-42' for option `--wrap': java.lang.IllegalArgumentException: must be a non-negative integer
*
*/
/*
* Output when using the following command line arguments:
*
* text.txt anothertext.txt
*
* base64: com.github.jh3nd3rs0n.argmatey.ArgMatey$IllegalArgException: illegal argument `anothertext.txt': java.lang.IllegalArgumentException: extra operand: `anothertext.txt'
*
*/
}
The following is the earlier example using a customized
OptionGroupHelpTextProvider
:
// ...
import com.github.jh3nd3rs0n.argmatey.ArgMatey.Annotations.OptionGroupHelpTextProvider;
import com.github.jh3nd3rs0n.argmatey.ArgMatey.OptionGroupHelpTextParams;
public class Base64CLI extends CLI {
/*
* Provides the help text for a command line option group
* on a single line instead of multiple lines.
*/
private static class SingleLineOptionGroupHelpTextProvider
extends ArgMatey.OptionGroupHelpTextProvider {
@Override
public String getOptionGroupHelpText(OptionGroupHelpTextParams params) {
String optionGroupHelpText = null;
StringBuilder sb = null;
String doc = null;
for (ArgMatey.Option option : params.getDisplayableOptions()) {
if (sb == null) {
sb = new StringBuilder();
sb.append(" ");
} else {
sb.append(", ");
}
sb.append(option.getUsage());
if (doc == null) {
doc = option.getDoc();
}
}
if (sb != null) {
if (doc != null) {
final int docStart = 24;
int length = sb.length();
for (int i = 0; i < docStart - length; i++) {
sb.append(' ');
}
sb.append(doc);
}
optionGroupHelpText = sb.toString();
}
return optionGroupHelpText;
}
}
// ...
@Option(
doc = "Wrap encoded lines after COLS character (default is 76)",
name = "w",
optionArgSpec = @OptionArgSpec(name = "COLS"),
type = OptionType.POSIX
)
@Option(
name = "wrap",
type = OptionType.GNU_LONG
)
/*
* You can apply the OptionGroupHelpTextProvider to a
* particular command line option group...
*/
@OptionGroupHelpTextProvider(SingleLineOptionGroupHelpTextProvider.class)
private void setColumnLimit(Integer i) {
// ...
}
// ...
public static void main(String[] args) {
/*
* ... or you can apply the OptionGroupHelpTextProvider
* to all command line option groups.
*/
ArgMatey.OptionGroupHelpTextProvider provider = new SingleLineOptionGroupHelpTextProvider();
ArgMatey.OptionGroupHelpTextProvider.setDefault(provider);
// ...
}
/*
* Output when using the command line option "--help" :
*
* Usage: base64 [OPTION]... [FILE]
* Base64 encode or decode FILE, or standard input, to standard output.
*
* With no FILE, or when FILE is -, read standard input.
*
* OPTIONS:
* -d, --decode Decode data
* --help Display this help and exit
* -i, --ignore-garbage When decoding, ignore non-alphabet characters
* --version Display version information and exit
* -w COLS, --wrap=COLS Wrap encoded lines after COLS character (default is 76)
*
*/
}
The following are some examples of projects using ArgMatey:
- CafeBase64 (specific example: CafeBase64CLI.java)
- Jargyle (specific examples: JargyleCLI.java, ServerConfigurationCLI.java, ServerConfigurationFileCreatorCLI.java, ServerStarterCLI.java, Socks5UserManagerCLI.java)
ArgMatey is licensed under the MIT license
- Apache Maven 3.3.9 or higher (for generating javadocs, automated testing, installing, and building)
- Java 8 or higher
To generate javadocs, run the following commands:
cd argmatey
mvn clean javadoc:javadoc
After running the aforementioned commands, the javadocs can be found in the following path:
target/site/apidocs
To run automated testing, run the following command:
mvn clean test
To install, run the following command:
mvn clean install
To add a dependency on ArgMatey using Maven, use the following:
<dependency>
<groupId>com.github.jh3nd3rs0n</groupId>
<artifactId>argmatey</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
To build and package ArgMatey as a JAR file, run the following command:
mvn clean package
After running the aforementioned command, the JAR file can be found in the following path:
target/argmatey-VERSION.jar
VERSION
is further specified by the name of the actual JAR file.