org.conqat.lib.commons.options.CommandLine Maven / Gradle / Ivy
Show all versions of teamscale-lib-commons Show documentation
/*
* Copyright (c) CQSE GmbH
*
* Licensed 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 org.conqat.lib.commons.options;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.conqat.lib.commons.collections.CollectionUtils;
import org.conqat.lib.commons.reflect.TypeConversionException;
import org.conqat.lib.commons.string.StringUtils;
/**
* A class providing command line parsing and usage messages using GNU syntax.
*
* The GNU syntax is implemented as follows. There are short (single character) and long (multi
* character) options, just as provided by the AOption annotation. Short options are introduced
* using a single minus (e.g. '-h') while long options are introduced using a double minus (e.g.
* '--help'). The parameter for an option is either the next argument, or--in case of long
* options--possibly separated by an equals sign (e.g. '--file=test.txt'). Short options may be
* chained (e.g. '-xvf abc' instead of '-x -v -f abc'). For chained short options, only the last
* option may take a parameter. The separator '--' may be used to switch off argument processing for
* the remaining arguments, i.e. all other arguments are treated as left-overs.
*/
public class CommandLine {
/** Registry containing the options to be used by this instance */
private final OptionRegistry registry;
/**
* Constructor.
*
* @param registry
* Registry containing the options to be used by this instance.
*/
public CommandLine(OptionRegistry registry) {
this.registry = registry;
}
/**
* Parses the given command line parameters and applies the options found. The arguments not treated
* as options or parameters are returned (often they are treated as file arguments). If the syntax
* does not conform to the options in the registry, an {@link OptionException} is thrown.
*
* @param args
* the command line arguments to be parsed.
* @return the remaining arguments.
* @throws OptionException
* in case of syntax errors or invalid parameters.
*/
public String[] parse(String[] args) throws OptionException {
return parse(new CommandLineTokenStream(args));
}
/**
* Parses the command line parameters implicitly given by the token stream and applies the options
* found. The arguments not treated as options or parameters are returned (often they are treated as
* file arguments). If the syntax does not conform to the options in the registry an
* IllegalArgumentException is thrown.
*
* @param ts
* Token stream containing the arguments.
* @return Remaining arguments.
* @throws OptionException
* in case of syntax errors or invalid parameters.
*/
public String[] parse(CommandLineTokenStream ts) throws OptionException {
List remainingArgs = new ArrayList<>();
while (ts.hasNext()) {
if (ts.nextIsSeparator()) {
// discard separator '--'
ts.next();
// and swallow everything else
while (ts.hasNext()) {
remainingArgs.add(ts.next());
}
} else if (ts.nextIsLongOption()) {
String name = ts.nextLongOption();
OptionApplicator applicator = registry.getLongOption(name);
applyOption(applicator, formatLongOption(name), ts);
} else if (ts.nextIsShortOption()) {
char name = ts.nextShortOption();
OptionApplicator applicator = registry.getShortOption(name);
applyOption(applicator, formatShortOption(name), ts);
} else if (ts.nextIsFileArgument()) {
remainingArgs.add(ts.next());
} else {
throw new OptionException("Unexpected command line argument: " + ts.next());
}
}
return CollectionUtils.toArray(remainingArgs, String.class);
}
/**
* Applies an option and tests for various errors.
*
* @param applicator
* the applicator for the option.
* @param optionName
* the name of the option.
* @param ts
* the token stream used to get additional parameters.
*/
private static void applyOption(OptionApplicator applicator, String optionName, CommandLineTokenStream ts)
throws OptionException {
if (applicator == null) {
throw new OptionException("Unknown option: " + optionName);
}
if (applicator.requiresParameter()) {
if (!ts.nextIsParameter()) {
throw new OptionException("Missing argument for option: " + optionName);
}
do {
String parameter = ts.next();
try {
applicator.applyOption(parameter);
} catch (TypeConversionException e) {
throw new OptionException(
"Parameter " + parameter + " for option " + optionName + " is not of required type!");
}
} while (applicator.isGreedy() && ts.hasNext() && !(ts.nextIsLongOption() || ts.nextIsShortOption()));
} else {
applicator.applyOption();
}
}
/**
* Print the list of all supported options using reasonable default values for widths.
*
* @param pw
* the writer used for output.
*/
public void printUsage(PrintWriter pw) {
printUsage(pw, 20, 80);
}
/**
* Print the list of all supported options.
*
* @param pw
* the writer to print to.
* @param firstCol
* the width of the first column containing the option name (without the trailing space).
* @param width
* the maximal width of a line (aka terminal width).
*/
public void printUsage(PrintWriter pw, int firstCol, int width) {
List