uk.co.thebadgerset.junit.extensions.util.CommandLineParser Maven / Gradle / Ivy
Go to download
JUnit Toolkit enhances JUnit with performance testing, asymptotic behaviour analysis, and concurrency testing.
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
package uk.co.thebadgerset.junit.extensions.util;
import java.text.CharacterIterator;
import java.text.StringCharacterIterator;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* CommandLineParser provides a utility for specifying the format of a command line and parsing command lines to ensure
* that they fit their specified format. A command line is made up of flags and options, both may be refered to as
* options. A flag is an option that does not take an argument (specifying it means it has the value 'true' and not
* specifying it means it has the value 'false'). Options must take arguments but they can be set up with defaults so
* that they take a default value when not set. Options may be mandatory in wich case it is an error not to specify
* them on the command line. Flags are never mandatory because they are implicitly set to false when not specified.
*
* Some examples command line are:
*
*
* - This one has two options that expect arguments:
*
* cruisecontrol -configfile cruisecontrol.xml -port 9000
*
* - This has one no-arg flag and two 'free' arguments:
*
* zip -r project.zip project/*
*
* - This one concatenates multiple flags into a single block with only one '-':
*
* jar -tvf mytar.tar
*
*
* The parsing rules are:
*
*
* - Flags may be combined after a single '-' because they never take arguments. Normally such flags are single letter
* flags but this is only a convention and not enforced. Flags of more than one letter are usually specified on their own.
*
- Options expecting arguments must always be on their own.
*
- The argument to an option may be seperated from it by whitespace or appended directly onto the option.
*
- The argument to an option may never begin with a '-' character.
*
- All other arguments not beginning with a '-' character are free arguments that do not belong to any option.
*
- The second or later of a set of duplicate or repeated flags override earlier ones.
*
- Options are matched up to the shortest matching option. This is because of the possibility of having no space
* between an option and its argument. This rules out the possibility of using two options where one is an opening
* substring of the other. For example, the options "foo" and "foobar" cannot be used on the same command line because
* it is not possible to distinguish the argument "-foobar" from being the "foobar" option or the "foo" option with
* the "bar" argument.
*
*
* By default, unknown options are simply ignored if specified on the command line. This behaviour may be changed
* so that the parser reports all unknowns as errors by using the {@link #setErrorsOnUnknowns} method.
*
* CRC Card
* Responsibilities Collaborations
* Accept a command line specification.
* Parse a command line into properties, validating it against its specification.
* Report all errors between a command line and its specification.
* Provide a formatted usage string for a command line.
* Provide a formatted options in force string for a command line.
* Allow errors on unknowns behaviour to be turned on or off.
*
*
* @author Rupert Smith
*/
public class CommandLineParser
{
/**
* Holds a mapping from command line option names to detailed information about those options.
* Use of a tree map ensures that the options are easy to print in alphabetical order as a usage string.
* An alternative might be to use a LinkedHashMap to print them in the order they are specified.
*/
private Map optionMap = new TreeMap();
/** Holds a list of parsing errors. */
private List parsingErrors = new ArrayList();
/** Holds the regular head matcher to match command line options with. */
private Matcher optionMatcher = null;
/** Holds the parsed command line properties after parsing. */
private Properties parsedProperties = null;
/** Holds any trailing name=value pairs specified in the free arguments. */
private Properties trailingProperties = null;
/** Flag used to indicate that errors should be created for unknown options. False by default. */
private boolean errorsOnUnknowns = false;
/**
* Creates a command line options parser from a command line specification. This is passed to this constructor
* as an array of arrays of strings. Each array of strings specifies the command line for a single option. A static
* array may therefore easily be used to configure the command line parser in a single method call with an easily
* readable format.
*
* Each array of strings must be 2, 3, 4 or 5 elements long. If any of the last three elements are missing they
* are assumed to be null. The elements specify the following parameters:
*
* - The name of the option without the leading '-'. For example, "file". To specify the format of the 'free'
* arguments use the option names "1", "2", ... and so on.
*
- The option comment. A line of text describing the usage of the option. For example, "The file to be processed."
*
- The options argument. This is a very short description of the argument to the option, often a single word
* or a reminder as to the arguments format. When this element is null the option is a flag and does not
* accept any arguments. For example, "filename" or "(unix | windows)" or null. The actual text specified
* is only used to print in the usage message to remind the user of the usage of the option.
*
- The mandatory flag. When set to "true" an option must always be specified. Any other value, including null,
* means that the option is mandatory. Flags are always mandatory (see class javadoc for explanation of why) so
* this is ignored for flags.
*
- A regular head describing the format that the argument must take. Ignored if null.
*
* An example call to this constructor is:
*
*
* CommandLineParser commandLine = new CommandLineParser(
* new String[][] {{"file", "The file to be processed. ", "filename", "true"},
* {"dir", "Directory to store results in. Current dir used if not set.", "out dir"},
* {"os", "Operating system EOL format to use.", "(windows | unix)", null, "windows\|unix"},
* {"v", "Verbose mode. Prints information about the processing as it goes."},
* {"1", "The processing command to run.", "command", "true", "add\|remove\|list"}});
*
*
* @param config The configuration as an array of arrays of strings.
*/
public CommandLineParser(String[][] config)
{
// Loop through all the command line option specifications creating details for each in the options map.
for (String[] nextOptionSpec : config)
{
addOption(nextOptionSpec[0], nextOptionSpec[1], (nextOptionSpec.length > 2) ? nextOptionSpec[2] : null,
(nextOptionSpec.length > 3) && ("true".equals(nextOptionSpec[3])),
(nextOptionSpec.length > 4) ? nextOptionSpec[4] : null);
}
}
/**
* Extracts all name=value pairs from the command line, sets them all as system properties and also returns
* a map of properties containing them.
*
* @param args The command line.
* @param commandLine The command line parser.
* @param properties The properties object to inject all parsed properties into (optional may be null).
*
* @return A set of properties containing all name=value pairs from the command line.
*/
public static Properties processCommandLine(String[] args, CommandLineParser commandLine, Properties properties)
{
// Capture the command line arguments or display errors and correct usage and then exit.
Properties options = null;
try
{
options = commandLine.parseCommandLine(args);
// Add all the command line options and trailing settings to properties if the optional properties object
// to copy them into has been set.
if (properties != null)
{
commandLine.addTrailingPairsToProperties(properties);
commandLine.addOptionsToProperties(properties);
}
}
catch (IllegalArgumentException e)
{
System.out.println(commandLine.getErrors());
System.out.println(commandLine.getUsage());
System.exit(1);
}
return options;
}
/**
* Lists all the parsing errors from the most recent parsing in a string.
*
* @return All the parsing errors from the most recent parsing.
*/
public String getErrors()
{
// Return the empty string if there are no errors.
if (parsingErrors.isEmpty())
{
return "";
}
// Concatenate all the parsing errors together.
String result = "";
for (String s : parsingErrors)
{
result += s;
}
return result;
}
/**
* Lists the properties set from the most recent parsing or an empty string if no parsing has been done yet.
*
* @return The properties set from the most recent parsing or an empty string if no parsing has been done yet.
*/
public String getOptionsInForce()
{
// Check if there are no properties to report and return and empty string if so.
if (parsedProperties == null)
{
return "";
}
// List all the properties.
String result = "Options in force:\n";
for (Map.Entry