CmdLn Javadoc/*
* Copyright (C) 2007-2010 Stephen Ostermiller
* http://ostermiller.org/contact.pl?regarding=Java+Utilities
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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.
*
* See LICENSE.txt for details.
*/
package com.Ostermiller.util;
import java.io.*;
import java.util.*;
/**
* Command line argument parser for Java command line programs.
*
* More information about this class and code samples for suggested use are
* available from <a target="_top" href=
* "http://ostermiller.org/utils/CmdLn.html">ostermiller.org</a>.
*
* @author Stephen Ostermiller http://ostermiller.org/contact.pl?regarding=Java+Utilities
* @since ostermillerutils 1.07.00
*/
public final class CmdLn {
/**
* The original unparsed command line arguments as
* passed in to the constructor.
*
* @since ostermillerutils 1.07.00
*/
private String[] arguments;
/**
* Description to be put in the help.
*
* @since ostermillerutils 1.07.00
*/
private String description;
/**
* A list of all the options.
* Filled by the public addOptions() methods.
*
* @since ostermillerutils 1.07.00
*/
private ArrayList<CmdLnOption> options = new ArrayList<CmdLnOption>();
/**
* Map the options to their found results.
*
* @since ostermillerutils 1.07.00
*/
private HashMap<CmdLnOption, ArrayList<CmdLnResult>> optionsToResults = new HashMap<CmdLnOption, ArrayList<CmdLnResult>>();
/**
* List of all results that have been found.
*
* @since ostermillerutils 1.07.00
*/
private ArrayList<CmdLnResult> results = new ArrayList<CmdLnResult>();
/**
* A list of the arguments that do not belong to any option.
*
* @since ostermillerutils 1.07.00
*/
private ArrayList<String> leftOverArguments = new ArrayList<String>();
/**
* Mapping of long options to their command line option object.
* Created during the parse method.
*
* @since ostermillerutils 1.07.00
*/
private HashMap<String,CmdLnOption> longOptions;
/**
* Mapping of short options to their command line option object.
* Created during the parse method.
*
* @since ostermillerutils 1.07.00
*/
private HashMap<Character,CmdLnOption> shortOptions;
/**
* An ordered set of what long options start with. Typically contains
* a single entry "--".
*
* @since ostermillerutils 1.07.00
*/
private LinkedHashSet<String> longOptionsStart = new LinkedHashSet<String>(1);
/**
* An ordered set of what short options start with. Typically contains
* a single entry "-".
*
* @since ostermillerutils 1.07.00
*/
private LinkedHashSet<String> shortOptionsStart = new LinkedHashSet<String>(1);
/**
* A set of strings that indicate that everything following should be regarded
* as non-option arguments.
*
* @since ostermillerutils 1.07.00
*/
private HashSet<String> nonOptionSeparators = new HashSet<String>(1);
/**
* A set of strings that break an option up into an option and an argument.
*
* @since ostermillerutils 1.07.00
*/
private HashSet<Character> optionArgumentDelimiters = new HashSet<Character>(3);
/**
* Whether or not the parse method has been called yet.
* Used to determine if setters are appropriate or should throw illegal state exceptions.
*
* @since ostermillerutils 1.07.00
*/
private boolean parsed = false;
/**
* New command line options with the given command line arguments
*
* @param arguments command line arguments from main method
*
* @since ostermillerutils 1.07.00
*/
public CmdLn(String[] arguments){
if (arguments == null) throw new IllegalArgumentException("Command line arguments cannot be null.");
for(String argument: arguments){
if (argument == null) throw new IllegalArgumentException("Each command line argument cannot be null.");
}
this.arguments = arguments.clone();
// Defaults
longOptionsStart.add("--");
shortOptionsStart.add("-");
nonOptionSeparators.add("--");
optionArgumentDelimiters.add('=');
optionArgumentDelimiters.add(':');
optionArgumentDelimiters.add(' ');
}
/**
* Set the delimiters that separate a option name from a value
* within a single argument.
*
* This is to support arguments of the form "--name=value". The
* equals sign can separate the option name ("name") from the
* option argument ("value").
*
* By default, the delimiters are '=', ':', and ' '.
* @param delimiters list of delimiters
* @return this for method chaining.
*
* @since ostermillerutils 1.07.00
*/
public CmdLn setOptionArgumentDelimiters(char[] delimiters){
optionArgumentDelimiters.clear();
for (char c: delimiters){
optionArgumentDelimiters.add(c);
}
return this;
}
/**
* Set what long options and short options start with.
* Typically long options start with "--" and short options with "-"
* (this is the default).
*
* @param longOptionsStart What long options start with (default "--") or null for no long options;
* @param shortOptionsStart What short options start with (default "-") or null for no short options;
* @throws IllegalArgumentException if the long or short start is the empty string
* @return this for method chaining
*
* @since ostermillerutils 1.07.00
*/
public CmdLn setOptionStarts(String longOptionsStart, String shortOptionsStart){
setOptionStarts(
new String[]{longOptionsStart},
new String[]{shortOptionsStart}
);
return this;
}
/**
* Set what long options and short options start with.
* Typically long options start with "--" and short options with "-" and
* this is the default.
* <p>
* The first option start in each array will be the canonical option start
* that is used in the help message.
*
* @param longOptionsStart What long options start with (default "--") or null or empty for no long options;
* @param shortOptionsStart What short options start with (default "-") or null or empty for no short options;
* @throws IllegalArgumentException if the long or short start is the empty string
* @return this for method chaining
*
* @since ostermillerutils 1.07.00
*/
public CmdLn setOptionStarts(String[] longOptionsStart, String[] shortOptionsStart){
this.longOptionsStart.clear();
if (longOptionsStart != null){
for (String optionStart: longOptionsStart){
if (optionStart != null){
if ("".equals(optionStart)) throw new IllegalArgumentException("long option start cannot be the empty string");
this.longOptionsStart.add(optionStart);
}
}
}
this.shortOptionsStart.clear();
if (shortOptionsStart != null){
for (String optionStart: shortOptionsStart){
if (optionStart != null){
if ("".equals(optionStart)) throw new IllegalArgumentException("short option start cannot be the empty string");
this.shortOptionsStart.add(optionStart);
}
}
}
return this;
}
/**
* Set the "stop" option that causes any following arguments to be treated
* as non-option arguments, even if they look like an option.
* <p>
* The default non-option separator is "--".
*
* @param nonOptionSeparators List of arguments that stop processing options and treat remaining arguments as non option arguments.
* @return this for method chaining
*
* @since ostermillerutils 1.07.00
*/
public CmdLn setNonOptionSeparators(String[] nonOptionSeparators){
this.nonOptionSeparators.clear();
if (nonOptionSeparators != null){
for (String nonOptionSeparator: nonOptionSeparators){
this.nonOptionSeparators.add(nonOptionSeparator);
}
}
return this;
}
/**
* Set the description for the program. This description will be printed
* on the first line of the help message.
*
* @param description short description about the program
* @return this for method chaining
*
* @since ostermillerutils 1.07.00
*/
public CmdLn setDescription(String description){
this.description = description;
return this;
}
/**
* Add options.
*
* @param options options to be added.
* @throws NullPointerException if the options are null.
* @throws NullPointerException if any option in the collection is null.
* @throws IllegalStateException if the command line has already been parsed.
* @return this for method chaining
*
* @since ostermillerutils 1.07.00
*/
public CmdLn addOptions(Collection<CmdLnOption> options){
for(CmdLnOption option: options){
addOption(option);
}
return this;
}
/**
* Add options.
*
* @param options options to be added.
* @throws NullPointerException if the options are null.
* @throws NullPointerException if any option in the collection is null.
* @throws IllegalStateException if the command line has already been parsed.
* @return this for method chaining
*
* @since ostermillerutils 1.07.00
*/
public CmdLn addOptions(CmdLnOption[] options){
for(CmdLnOption option: options){
addOption(option);
}
return this;
}
/**
* Add option.
*
* @param option option to be added.
* @throws NullPointerException if the option is null.
* @throws IllegalStateException if the command line has already been parsed.
* @return this for method chaining
*
* @since ostermillerutils 1.07.00
*/
public CmdLn addOption(CmdLnOption option){
if (parsed) throw new IllegalStateException("Can no longer add options");
options.add(option);
return this;
}
/**
* Get the option associated with the given argument.
* Parses the command line if not already parsed.
*
* @param s long argument
* @return option associated with the given argument.
* @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
* @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
* @throws ExtraCmdLnArgumentException if a command line option has too many arguments during parsing.
*
* @since ostermillerutils 1.07.00
*/
private CmdLnOption getOption(String s){
parse();
return longOptions.get(s);
}
/**
* Get the option associated with the given argument.
* Parses the command line if not already parsed.
*
* @param c short argument
* @return option associated with the given argument.
* @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
* @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
* @throws ExtraCmdLnArgumentException if a command line option has too many arguments during parsing.
*
* @since ostermillerutils 1.07.00
*/
private CmdLnOption getOption(Character c){
parse();
return shortOptions.get(c);
}
/**
* Get the option associated with the given argument.
* Parses the command line if not already parsed.
*
* @param c short argument
* @return option associated with the given argument.
* @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
* @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
* @throws ExtraCmdLnArgumentException if a command line option has too many arguments during parsing.
*
* @since ostermillerutils 1.07.00
*/
private CmdLnOption getOption(char c){
parse();
return shortOptions.get(c);
}
/**
* Get the last result associated with the given argument.
* If a option is in the command line multiple times, typically
* the last one should be the one that wins as an override mechanism.
* Parses the command line if not already parsed.
*
* @param s long argument
* @return result for argument, or null if not associated with an option or not present in the command line.
* @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
* @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
* @throws ExtraCmdLnArgumentException if a command line option has too many arguments during parsing.
*
* @since ostermillerutils 1.07.00
*/
public CmdLnResult getResult(String s){
parse();
return getResult(getOption(s));
}
/**
* Get the last result associated with the given argument.
* If a option is in the command line multiple times, typically
* the last one should be the one that wins as an override mechanism.
* Parses the command line if not already parsed.
*
* @param c short argument
* @return result for argument, or null if not associated with an option or not present in the command line.
* @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
* @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
* @throws ExtraCmdLnArgumentException if a command line option has too many arguments during parsing.
*
* @since ostermillerutils 1.07.00
*/
public CmdLnResult getResult(Character c){
parse();
return getResult(getOption(c));
}
/**
* Get the last result associated with the given argument.
* If a option is in the command line multiple times, typically
* the last one should be the one that wins as an override mechanism.
* Parses the command line if not already parsed.
*
* @param c short argument
* @return result for argument, or null if not associated with an option or not present in the command line.
* @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
* @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
* @throws ExtraCmdLnArgumentException if a command line option has too many arguments during parsing.
*
* @since ostermillerutils 1.07.00
*/
public CmdLnResult getResult(char c){
parse();
return getResult(getOption(c));
}
/**
* Get the last result associated with the option.
* If a option is in the command line multiple times, typically
* the last one should be the one that wins as an override mechanism.
* Parses the command line if not already parsed.
*
* @param option command line option
* @return result for argument, or null if option not added to this command line or not present in the command line.
* @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
* @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
* @throws ExtraCmdLnArgumentException if a command line option has too many arguments during parsing.
*
* @since ostermillerutils 1.07.00
*/
public CmdLnResult getResult(CmdLnOption option){
parse();
if (option == null) return null;
ArrayList<CmdLnResult> results = optionsToResults.get(option);
if (results == null) return null;
return results.get(results.size()-1);
}
/**
* Get all the results in
* the order in which they appear in the command line.
* Parses the command line if not already parsed.
* @return unmodifiable list of all options and arguments that were specified on the command line
* @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
* @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
* @throws ExtraCmdLnArgumentException if a command line option has too many arguments during parsing.
*
* @since ostermillerutils 1.07.00
*/
public List<CmdLnResult> getResults(){
parse();
return Collections.unmodifiableList(results);
}
/**
* Get all the results associated with the given argument in
* the order in which they appear in the command line.
* If a option is in the command line multiple times, typically
* the last one should be the one that wins as an override mechanism,
* however it may sometimes be useful to know about all of them.
* Parses the command line if not already parsed.
*
* @param s long argument
* @return results for argument, or null if not associated with an option or not present in the command line.
* @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
* @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
* @throws ExtraCmdLnArgumentException if a command line option has too many arguments during parsing.
*
* @since ostermillerutils 1.07.00
*/
public List<CmdLnResult> getResults(String s){
parse();
return getResults(getOption(s));
}
/**
* Get all the results associated with the given argument in
* the order in which they appear in the command line.
* If a option is in the command line multiple times, typically
* the last one should be the one that wins as an override mechanism,
* however it may sometimes be useful to know about all of them.
* Parses the command line if not already parsed.
*
* @param c short argument
* @return results for argument, or null if not associated with an option or not present in the command line.
* @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
* @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
*
* @since ostermillerutils 1.07.00
*/
public List<CmdLnResult> getResults(Character c){
parse();
return getResults(getOption(c));
}
/**
* Get all the results associated with the given argument in
* the order in which they appear in the command line.
* If a option is in the command line multiple times, typically
* the last one should be the one that wins as an override mechanism,
* however it may sometimes be useful to know about all of them.
* Parses the command line if not already parsed.
*
* @param c short argument
* @return results for argument, or null if not associated with an option or not present in the command line.
* @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
* @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
* @throws ExtraCmdLnArgumentException if a command line option has too many arguments during parsing.
*
* @since ostermillerutils 1.07.00
*/
public List<CmdLnResult> getResults(char c){
parse();
return getResults(getOption(c));
}
/**
* Get all the results associated with the given argument in
* the order in which they appear in the command line.
* If a option is in the command line multiple times, typically
* the last one should be the one that wins as an override mechanism,
* however it may sometimes be useful to know about all of them.
* Parses the command line if not already parsed.
*
* @param option command line option
* @return results for option, or null if option not added to this command line or not present in the command line.
* @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
* @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
* @throws ExtraCmdLnArgumentException if a command line option has too many arguments during parsing.
*
* @since ostermillerutils 1.07.00
*/
public List<CmdLnResult> getResults(CmdLnOption option){
parse();
if (option == null) return null;
ArrayList<CmdLnResult> results = optionsToResults.get(option);
if (results == null) return null;
return Collections.unmodifiableList(results);
}
/**
* Whether or not the specified option is present in the command line.
* Parses the command line if not already parsed.
*
* @param s long argument
* @return true iff the option (or its synonyms) is present.
* @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
* @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
* @throws ExtraCmdLnArgumentException if a command line option has too many arguments during parsing.
*
* @since ostermillerutils 1.07.00
*/
public boolean present(String s){
parse();
return present(getOption(s));
}
/**
* Whether or not the specified option is present in the command line.
* Parses the command line if not already parsed.
*
* @param c short argument
* @return true iff the option (or its synonyms) is present.
* @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
* @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
* @throws ExtraCmdLnArgumentException if a command line option has too many arguments during parsing.
*
* @since ostermillerutils 1.07.00
*/
public boolean present(Character c){
parse();
return present(getOption(c));
}
/**
* Whether or not the specified option is present in the command line.
* Parses the command line if not already parsed.
*
* @param c short argument
* @return true iff the option (or its synonyms) is present.
* @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
* @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
* @throws ExtraCmdLnArgumentException if a command line option has too many arguments during parsing.
*
* @since ostermillerutils 1.07.00
*/
public boolean present(char c){
parse();
return present(getOption(c));
}
/**
* Whether or not the specified option is present in the command line.
* Parses the command line if not already parsed.
*
* @param option command line option
* @return true iff the option is present.
* @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
* @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
* @throws ExtraCmdLnArgumentException if a command line option has too many arguments during parsing.
*
* @since ostermillerutils 1.07.00
*/
public boolean present(CmdLnOption option){
parse();
if (option == null) return false;
ArrayList<CmdLnResult> results = optionsToResults.get(option);
return (results != null);
}
/**
* The number of times the specified option is present in the command line.
* Parses the command line if not already parsed.
*
* @param s long argument
* @return true iff the option (or its synonyms) is present.
* @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
* @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
* @throws ExtraCmdLnArgumentException if a command line option has too many arguments during parsing.
*
* @since ostermillerutils 1.07.00
*/
public int occurrences(String s){
parse();
return occurrences(getOption(s));
}
/**
* The number of times the specified option is present in the command line.
* Parses the command line if not already parsed.
*
* @param c short argument
* @return true iff the option (or its synonyms) is present.
* @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
* @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
* @throws ExtraCmdLnArgumentException if a command line option has too many arguments during parsing.
*
* @since ostermillerutils 1.07.00
*/
public int occurrences(Character c){
parse();
return occurrences(getOption(c));
}
/**
* The number of times the specified option is present in the command line.
* Parses the command line if not already parsed.
*
* @param c short argument
* @return true iff the option (or its synonyms) is present.
* @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
* @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
* @throws ExtraCmdLnArgumentException if a command line option has too many arguments during parsing.
*
* @since ostermillerutils 1.07.00
*/
public int occurrences(char c){
parse();
return occurrences(getOption(c));
}
/**
* The number of times the specified option is present in the command line.
* Parses the command line if not already parsed.
*
* @param option command line option
* @return true iff the option is present.
* @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
* @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
* @throws ExtraCmdLnArgumentException if a command line option has too many arguments during parsing.
*
* @since ostermillerutils 1.07.00
*/
public int occurrences(CmdLnOption option){
parse();
if (option == null) return 0;
ArrayList<CmdLnResult> results = optionsToResults.get(option);
if (results == null) return 0;
return results.size();
}
/**
* Get the left over arguments -- the arguments that are not
* associated with any arguments.
* Parses the command line if not already parsed.
*
* @return unmodifiable list of arguments
* @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
* @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
* @throws ExtraCmdLnArgumentException if a command line option has too many arguments during parsing.
*
* @since ostermillerutils 1.07.00
*/
public List<String> getNonOptionArguments(){
parse();
return Collections.unmodifiableList(leftOverArguments);
}
/**
* Go through the options and sort out the long and the short options
* into their own hash maps.
*
* @since ostermillerutils 1.07.00
*/
private void processOptions(){
longOptions = new HashMap<String,CmdLnOption>();
shortOptions= new HashMap<Character,CmdLnOption>();
for (CmdLnOption option: options){
option.setImmutable();
for (String name: option.getLongNames()){
if (longOptions.containsKey(name)){
throw new IllegalArgumentException("More than one long option has the name: '" + name + "'");
}
longOptions.put(name, option);
}
for (Character c: option.getShortNames()){
if (shortOptions.containsKey(c)){
throw new IllegalArgumentException("More than one short option has the character: '" + c + "'");
}
shortOptions.put(c, option);
}
}
}
/**
* Determine whether the given argument is a long option.
* Check all the long argument starts to see if it starts with any of them.
* However, if it *is* a option start, it is not a long option.
*
* @param argument command line argument to check to see if it is a long argument
* @return the argument name (strip off the argument start) or null if not a long argument
*
* @since ostermillerutils 1.07.00
*/
private String getLongOptionName(String argument){
if (shortOptionsStart.contains(argument) || longOptionsStart.contains(argument)){
return null;
}
for (String optionStart: longOptionsStart){
if (argument.startsWith(optionStart)){
return argument.substring(optionStart.length());
}
}
return null;
}
/**
* Determine whether the given argument is a short option.
* Check all the short argument starts to see if it starts with any of them.
* However, if it *is* a option start, it is not a short option.
*
* @param argument command line argument to check to see if it is a short argument
* @return the argument name (strip off the argument start) or null if not a short argument
*
* @since ostermillerutils 1.07.00
*/
private String getShortOptionName(String sArgument){
if (shortOptionsStart.contains(sArgument) || longOptionsStart.contains(sArgument)){
return null;
}
for (String optionStart: shortOptionsStart){
if (sArgument.startsWith(optionStart)){
return sArgument.substring(optionStart.length());
}
}
return null;
}
/**
* Record the result in all needed places
*
* @param result to record
*
* @since ostermillerutils 1.07.00
*/
private void addResult(CmdLnResult result){
ArrayList<CmdLnResult> results = optionsToResults.get(result.getOption());
if (results == null){
results = new ArrayList<CmdLnResult>(1);
optionsToResults.put(result.getOption(), results);
}
results.add(result);
this.results.add(result);
}
/**
* Find the index of of a option argument delimiter
* @param argument argument to search
* @return index of delimiter or -1 if none found
*
* @since ostermillerutils 1.07.00
*/
private int getIndexOfDelimiter(String argument){
int index = -1;
for(Character delimiter: optionArgumentDelimiters){
int i = argument.indexOf(delimiter);
if (i > 0 && (index==-1 || i<index)) index = i;
}
return index;
}
/**
* Look through the arguments in order, see if they are a long option, a short
* option, an argument to an option, or a non-option argument.
*
* @throws UnknownCmdLnOptionException if an unexpected option is encountered.
* @throws ExtraCmdLnArgumentException if a command line option has too many arguments.
*
* @since ostermillerutils 1.07.00
*/
private void runThroughArguments(){
CmdLnResult lastResult = null;
String optionName = null;
boolean seenNonOptionSeparator = false;
for(String argument: arguments){
if (seenNonOptionSeparator){
// Non-option argument
// No arguments after the separator are options or option arguments
leftOverArguments.add(argument);
} else if (nonOptionSeparators.contains(argument)){
// No arguments after the separator will be options or option arguments
seenNonOptionSeparator = true;
} else if ((optionName = getLongOptionName(argument)) != null){
int delimiterIndex = getIndexOfDelimiter(optionName);
String optionArgument = null;
if (delimiterIndex > 0){
optionArgument = optionName.substring(delimiterIndex+1);
optionName = optionName.substring(0, delimiterIndex);
}
// Long option
CmdLnOption option = longOptions.get(optionName);
if (option == null){
throw new UnknownCmdLnOptionException().setArgument(argument).setOption(optionName);
}
lastResult = new CmdLnResult(option);
addResult(lastResult);
if (optionArgument != null){
if (lastResult.hasAllArguments()){
throw new ExtraCmdLnArgumentException().setResult(lastResult);
}
lastResult.addArgument(optionArgument);
}
} else if ((optionName = getShortOptionName(argument)) != null){
// Short options
int delimiterIndex = getIndexOfDelimiter(optionName);
for (int i=0; i<optionName.length(); i++){
// Short option
Character c = optionName.charAt(i);
CmdLnOption option = shortOptions.get(c);
if (option == null){
throw new UnknownCmdLnOptionException().setArgument(argument).setOption(Character.toString(c));
}
lastResult = new CmdLnResult(option);
addResult(lastResult);
if (delimiterIndex == i+1){
String optionArgument = optionName.substring(delimiterIndex+1);
if (lastResult.hasAllArguments()){
throw new ExtraCmdLnArgumentException().setResult(lastResult);
}
lastResult.addArgument(optionArgument);
i = optionName.length(); // break out of loop
}
}
} else {
// Argument
if (lastResult != null && !lastResult.hasAllArguments()){
// Option argument
lastResult.addArgument(argument);
} else {
// Non-option argument
leftOverArguments.add(argument);
}
}
}
}
/**
* Parse the command line options if they have not already been parsed.
* Process the options,
* run through the arguments,
* ensure that all the arguments are good,
* call any listeners.
* <p>
* Once this command line has been parsed, options may no longer be added.
*
* @throws UnknownCmdLnOptionException if an unexpected option is encountered
* @throws MissingCmdLnArgumentException if a command line option does not have enough arguments.
* @throws ExtraCmdLnArgumentException if a command line option has too many arguments.
*
* @since ostermillerutils 1.07.00
*/
public void parse(){
if (parsed) return;
parsed = true;
processOptions();
runThroughArguments();
ensureOptionsSatisfied();
callListeners();
}
/**
* Throw an exception if any option doesn't have enough arguments.
*
* @throws MissingCmdLnArgumentException if a command line option does not have enough arguments.
*
* @since ostermillerutils 1.07.00
*/
private void ensureOptionsSatisfied(){
for (CmdLnResult result: results){
if (result.requiresMoreArguments()){
throw new MissingCmdLnArgumentException().setResult(result);
}
}
}
/**
* Call the call back methods in the results
*
* @since ostermillerutils 1.07.00
*/
private void callListeners() {
for (CmdLnResult result: results){
CmdLnListener callback = result.getOption().getListener();
if (callback != null){
callback.found(result);
}
}
}
/**
* Get the canonical start of long options. (Usually "--")
*
* @return the first long option start
*
* @since ostermillerutils 1.07.00
*/
String getFirstLongOptionsStart(){
if (longOptionsStart.size() == 0) return null;
return longOptionsStart.iterator().next();
}
/**
* Get the canonical start of short options. (Usually "-")
*
* @return the first short option start
*
* @since ostermillerutils 1.07.00
*/
String getFirstShortOptionsStart(){
if (shortOptionsStart.size() == 0) return null;
return shortOptionsStart.iterator().next();
}
/**
* Print help for the command line options. Help will be in this format:
* <pre>
* program description
* --argument -a <?> argument a description
* --another -b argument b description
* --third -c <?> argument c description
* </pre>
* Indentation is the default twenty, and line width is the default 80.
* Parses the command line if not already parsed.
*
* @return Help as a string.
* @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
* @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
*
* @since ostermillerutils 1.07.00
*/
public String getHelp(){
StringWriter out = new StringWriter();
printHelp(out);
return out.toString();
}
/**
* Print help for the command line options to standard output. Help will be in this format:
* <pre>
* program description
* --argument -a <?> argument a description
* --another -b argument b description
* --third -c <?> argument c description
* </pre>
* Indentation is the default twenty, and line width is the default eighty.
* Parses the command line if not already parsed.
*
* @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
* @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
*
* @since ostermillerutils 1.07.00
*/
public void printHelp(){
printHelp(System.out);
}
/**
* Print help for the command line options.
* Indentation is the default twenty, and line width is the default eighty.
* Parses the command line if not already parsed.
*
* @param out destination to which the help is written.
* @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
* @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
*
* @since ostermillerutils 1.07.00
*/
public void printHelp(OutputStream out){
printHelp(new PrintWriter(new OutputStreamWriter(out)));
}
/**
* Print help for the command line options. Help will be in this format:
* <pre>
* program description
* --argument -a <?> argument a description
* --another -b argument b description
* --third -c <?> argument c description
* </pre>
* Indentation is the default twenty, and line width is the default eighty.
* Parses the command line if not already parsed.
*
* @param out destination to which the help is written.
* @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
* @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
*
* @since ostermillerutils 1.07.00
*/
public void printHelp(Writer out){
printHelp(new PrintWriter(out));
}
/**
* Print help for the command line options. Help will be in this format:
* <pre>
* program description
* --argument -a <?> argument a description
* --another -b argument b description
* --third -c <?> argument c description
* </pre>
* Indentation is the default twenty, and line width is the default eighty.
* Parses the command line if not already parsed.
*
* @param out destination to which the help is written.
* @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
* @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
*
* @since ostermillerutils 1.07.00
*/
public void printHelp(PrintWriter out){
printHelp(out, 20, 80);
}
/**
* Print help for the command line options. Help will be in this format:
* <pre>
* program description
* --argument -a <?> argument a description
* --another -b argument b description
* --third -c <?> argument c description
* </pre>
* Parses the command line if not already parsed.
*
* @return Help as a string.
* @param indent the maximum number of characters to which all descriptions should be indented.
* @param width the number of characters at which text should be wrapped.
* @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
* @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
*
* @since ostermillerutils 1.07.00
*/
public String getHelp(int indent, int width){
StringWriter out = new StringWriter();
printHelp(out, indent, width);
return out.toString();
}
/**
* Print help for the command line options to standard output. Help will be in this format:
* <pre>
* program description
* --argument -a <?> argument a description
* --another -b argument b description
* --third -c <?> argument c description
* </pre>
* Parses the command line if not already parsed.
*
* @param indent the maximum number of characters to which all descriptions should be indented.
* @param width the number of characters at which text should be wrapped.
* @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
* @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
*
* @since ostermillerutils 1.07.00
*/
public void printHelp(int indent, int width){
printHelp(System.out, indent, width);
}
/**
* Print help for the command line options. Help will be in this format:
* <pre>
* program description
* --argument -a <?> argument a description
* --another -b argument b description
* --third -c <?> argument c description
* </pre>
* Parses the command line if not already parsed.
*
* @param out destination to which the help is written.
* @param indent the maximum number of characters to which all descriptions should be indented.
* @param width the number of characters at which text should be wrapped.
* @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
* @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
*
* @since ostermillerutils 1.07.00
*/
public void printHelp(OutputStream out, int indent, int width){
printHelp(new PrintWriter(new OutputStreamWriter(out)), indent, width);
}
/**
* Print help for the command line options. Help will be in this format:
* <pre>
* program description
* --argument -a <?> argument a description
* --another -b argument b description
* --third -c <?> argument c description
* </pre>
* Parses the command line if not already parsed.
*
* @param out destination to which the help is written.
* @param indent the maximum number of characters to which all descriptions should be indented.
* @param width the number of characters at which text should be wrapped.
* @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
* @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
*
* @since ostermillerutils 1.07.00
*/
public void printHelp(Writer out, int indent, int width){
printHelp(new PrintWriter(out), indent, width);
}
/**
* Print help for the command line options. Help will be in this format:
* <pre>
* program description
* --argument -a <?> argument a description
* --another -b argument b description
* --third -c <?> argument c description
* </pre>
* Parses the command line if not already parsed.
*
* @param out destination to which the help is written.
* @param indent the maximum number of characters to which all descriptions should be indented.
* @param width the number of characters at which text should be wrapped.
* @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
* @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
*
* @since ostermillerutils 1.07.00
*/
public void printHelp(PrintWriter out, int indent, int width){
parse();
String shortOptionsStart = getFirstShortOptionsStart();
String longOptionsStart = getFirstLongOptionsStart();
if (description != null){
out.println(description);
}
int maxIndent = 0;
for(CmdLnOption option: options){
int optionIndent = option.getHelpArgumentsLength(longOptionsStart, shortOptionsStart);
if (optionIndent > maxIndent){
maxIndent = optionIndent;
}
}
if (maxIndent < indent){
indent = maxIndent;
}
for(CmdLnOption option: options){
String optionHelp = option.getHelp(longOptionsStart, shortOptionsStart, indent, width);
if (optionHelp != null){
out.println(optionHelp);
}
}
out.flush();
}
}