io.cucumber.core.options.CommandlineOptionsParser Maven / Gradle / Ivy
The newest version!
package io.cucumber.core.options;
import io.cucumber.core.exception.CucumberException;
import io.cucumber.core.feature.GluePath;
import io.cucumber.core.logging.Logger;
import io.cucumber.core.logging.LoggerFactory;
import io.cucumber.datatable.DataTable;
import io.cucumber.datatable.DataTableFormatter;
import io.cucumber.gherkin.GherkinDialect;
import io.cucumber.gherkin.GherkinDialectProvider;
import io.cucumber.tagexpressions.TagExpressionParser;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.function.Function;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import static io.cucumber.core.cli.CommandlineOptions.COUNT;
import static io.cucumber.core.cli.CommandlineOptions.DRY_RUN;
import static io.cucumber.core.cli.CommandlineOptions.DRY_RUN_SHORT;
import static io.cucumber.core.cli.CommandlineOptions.GLUE;
import static io.cucumber.core.cli.CommandlineOptions.GLUE_SHORT;
import static io.cucumber.core.cli.CommandlineOptions.HELP;
import static io.cucumber.core.cli.CommandlineOptions.HELP_SHORT;
import static io.cucumber.core.cli.CommandlineOptions.I18N;
import static io.cucumber.core.cli.CommandlineOptions.MONOCHROME;
import static io.cucumber.core.cli.CommandlineOptions.MONOCHROME_SHORT;
import static io.cucumber.core.cli.CommandlineOptions.NAME;
import static io.cucumber.core.cli.CommandlineOptions.NAME_SHORT;
import static io.cucumber.core.cli.CommandlineOptions.NO_DRY_RUN;
import static io.cucumber.core.cli.CommandlineOptions.NO_MONOCHROME;
import static io.cucumber.core.cli.CommandlineOptions.NO_SUMMARY;
import static io.cucumber.core.cli.CommandlineOptions.OBJECT_FACTORY;
import static io.cucumber.core.cli.CommandlineOptions.ORDER;
import static io.cucumber.core.cli.CommandlineOptions.PLUGIN;
import static io.cucumber.core.cli.CommandlineOptions.PLUGIN_SHORT;
import static io.cucumber.core.cli.CommandlineOptions.PUBLISH;
import static io.cucumber.core.cli.CommandlineOptions.SNIPPETS;
import static io.cucumber.core.cli.CommandlineOptions.TAGS;
import static io.cucumber.core.cli.CommandlineOptions.TAGS_SHORT;
import static io.cucumber.core.cli.CommandlineOptions.THREADS;
import static io.cucumber.core.cli.CommandlineOptions.UUID_GENERATOR;
import static io.cucumber.core.cli.CommandlineOptions.VERSION;
import static io.cucumber.core.cli.CommandlineOptions.VERSION_SHORT;
import static io.cucumber.core.cli.CommandlineOptions.WIP;
import static io.cucumber.core.cli.CommandlineOptions.WIP_SHORT;
import static io.cucumber.core.options.ObjectFactoryParser.parseObjectFactory;
import static io.cucumber.core.options.UuidGeneratorParser.parseUuidGenerator;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Arrays.asList;
import static java.util.stream.Collectors.joining;
public final class CommandlineOptionsParser {
private static final Logger log = LoggerFactory.getLogger(CommandlineOptionsParser.class);
private static final String CORE_VERSION = ResourceBundle.getBundle("io.cucumber.core.version")
.getString("cucumber-jvm.version");
// IMPORTANT! Make sure USAGE.txt is always uptodate if this class changes.
private static final String USAGE_RESOURCE = "/io/cucumber/core/options/USAGE.txt";
private final PrintWriter out;
private Byte exitCode = null;
public CommandlineOptionsParser(OutputStream outputStream) {
out = new PrintWriter(outputStream, true);
}
public Optional exitStatus() {
return Optional.ofNullable(exitCode);
}
public RuntimeOptionsBuilder parse(String... args) {
return parse(Arrays.asList(args));
}
private RuntimeOptionsBuilder parse(List args) {
args = new ArrayList<>(args);
RuntimeOptionsBuilder parsedOptions = new RuntimeOptionsBuilder();
while (!args.isEmpty()) {
String arg = args.remove(0).trim();
if (arg.equals(HELP) || arg.equals(HELP_SHORT)) {
printUsage();
exitCode = 0;
return parsedOptions;
} else if (arg.equals(VERSION) || arg.equals(VERSION_SHORT)) {
out.println(CORE_VERSION);
exitCode = 0;
return parsedOptions;
} else if (arg.equals(I18N)) {
String nextArg = removeArgFor(arg, args);
exitCode = printI18n(nextArg);
return parsedOptions;
} else if (arg.equals(THREADS)) {
int threads = Integer.parseInt(removeArgFor(arg, args));
if (threads < 1) {
out.println("--threads must be > 0");
exitCode = 1;
return parsedOptions;
}
parsedOptions.setThreads(threads);
} else if (arg.equals(GLUE) || arg.equals(GLUE_SHORT)) {
String gluePath = removeArgFor(arg, args);
URI parse = GluePath.parse(gluePath);
parsedOptions.addGlue(parse);
} else if (arg.equals(TAGS) || arg.equals(TAGS_SHORT)) {
parsedOptions.addTagFilter(TagExpressionParser.parse(removeArgFor(arg, args)));
} else if (arg.equals(PUBLISH)) {
parsedOptions.setPublish(true);
} else if (arg.equals(PLUGIN) || arg.equals(PLUGIN_SHORT)) {
String pluginName = removeArgFor(arg, args);
if (pluginName.equals("null_summary")) {
log.warn(
() -> "Use '--no-summary' instead of '-p/--plugin null_summary'. '-p/--plugin null_summary' will be removed in a future release.");
parsedOptions.setNoSummary();
} else if (pluginName.equals("default_summary")) {
log.warn(
() -> "Use '-p/--plugin summary' instead of '-p/--plugin default_summary'. '-p/--plugin default_summary' will be removed in a future release.");
parsedOptions.addPluginName("summary");
} else {
parsedOptions.addPluginName(pluginName);
}
} else if (arg.equals(DRY_RUN) || arg.equals(DRY_RUN_SHORT)) {
parsedOptions.setDryRun(true);
} else if (arg.equals(NO_DRY_RUN)) {
parsedOptions.setDryRun(false);
} else if (arg.equals(NO_SUMMARY)) {
parsedOptions.setNoSummary();
} else if (arg.equals(MONOCHROME) || arg.equals(MONOCHROME_SHORT)) {
parsedOptions.setMonochrome(true);
} else if (arg.equals(NO_MONOCHROME)) {
parsedOptions.setMonochrome(false);
} else if (arg.equals(SNIPPETS)) {
String nextArg = removeArgFor(arg, args);
parsedOptions.setSnippetType(SnippetTypeParser.parseSnippetType(nextArg));
} else if (arg.equals(NAME) || arg.equals(NAME_SHORT)) {
String nextArg = removeArgFor(arg, args);
Pattern pattern = Pattern.compile(nextArg);
parsedOptions.addNameFilter(pattern);
} else if (arg.equals(WIP) || arg.equals(WIP_SHORT)) {
parsedOptions.setWip(true);
} else if (arg.equals(ORDER)) {
parsedOptions.setPickleOrder(PickleOrderParser.parse(removeArgFor(arg, args)));
} else if (arg.equals(COUNT)) {
int count = Integer.parseInt(removeArgFor(arg, args));
if (count < 1) {
out.println("--count must be > 0");
exitCode = 1;
return parsedOptions;
}
parsedOptions.setCount(count);
} else if (arg.equals(OBJECT_FACTORY)) {
String objectFactoryClassName = removeArgFor(arg, args);
parsedOptions.setObjectFactoryClass(parseObjectFactory(objectFactoryClassName));
} else if (arg.equals(UUID_GENERATOR)) {
String uuidGeneratorClassName = removeArgFor(arg, args);
parsedOptions.setUuidGeneratorClass(parseUuidGenerator(uuidGeneratorClassName));
} else if (arg.startsWith("-")) {
out.println("Unknown option: " + arg);
printUsage();
exitCode = 1;
return parsedOptions;
} else if (!arg.isEmpty()) {
FeatureWithLinesOrRerunPath parsed = FeatureWithLinesOrRerunPath.parse(arg);
parsed.getFeaturesToRerun().ifPresent(parsedOptions::addRerun);
parsed.getFeatureWithLines().ifPresent(parsedOptions::addFeature);
}
}
return parsedOptions;
}
private void printUsage() {
out.println(loadUsageText());
}
private String removeArgFor(String arg, List args) {
if (!args.isEmpty()) {
return args.remove(0);
}
printUsage();
throw new CucumberException("Missing argument for " + arg);
}
private byte printI18n(String language) {
GherkinDialectProvider dialectProvider = new GherkinDialectProvider();
Set languages = dialectProvider.getLanguages();
if (language.equalsIgnoreCase("help")) {
if (language.equalsIgnoreCase("help")) {
List dialects = languages.stream()
.map(dialectProvider::getDialect)
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList());
int widestLanguage = findWidest(dialects, GherkinDialect::getLanguage);
int widestName = findWidest(dialects, GherkinDialect::getName);
int widestNativeName = findWidest(dialects, GherkinDialect::getNativeName);
for (GherkinDialect dialect : dialects) {
printDialect(dialect, widestLanguage, widestName, widestNativeName);
}
return 0x0;
}
}
if (languages.contains(language)) {
dialectProvider.getDialect(language)
.ifPresent(this::printKeywordsFor);
}
out.println("Unrecognised ISO language code");
return 0x1;
}
private String loadUsageText() {
try (
InputStream usageResourceStream = CommandlineOptionsParser.class.getResourceAsStream(USAGE_RESOURCE);
BufferedReader br = new BufferedReader(new InputStreamReader(usageResourceStream, UTF_8))) {
return br.lines().collect(joining(System.lineSeparator()));
} catch (Exception e) {
return "Could not load usage text: " + e;
}
}
private int findWidest(List dialects, Function getNativeName) {
return dialects.stream()
.map(getNativeName)
.mapToInt(String::length)
.max()
.orElse(0);
}
private void printDialect(GherkinDialect dialect, int widestLanguage, int widestName, int widestNativeName) {
String langCode = rightPad(dialect.getLanguage(), widestLanguage);
String name = rightPad(dialect.getName(), widestName);
String nativeName = rightPad(dialect.getNativeName(), widestNativeName);
out.println(langCode + name + nativeName);
}
private byte printKeywordsFor(GherkinDialect dialect) {
StringBuilder builder = new StringBuilder();
List> table = new ArrayList<>();
addKeywordRow(table, "feature", dialect.getFeatureKeywords());
addKeywordRow(table, "background", dialect.getBackgroundKeywords());
addKeywordRow(table, "scenario", dialect.getScenarioKeywords());
addKeywordRow(table, "scenario outline", dialect.getScenarioOutlineKeywords());
addKeywordRow(table, "examples", dialect.getExamplesKeywords());
addKeywordRow(table, "given", dialect.getGivenKeywords());
addKeywordRow(table, "when", dialect.getWhenKeywords());
addKeywordRow(table, "then", dialect.getThenKeywords());
addKeywordRow(table, "and", dialect.getAndKeywords());
addKeywordRow(table, "but", dialect.getButKeywords());
addCodeKeywordRow(table, "given", dialect.getGivenKeywords());
addCodeKeywordRow(table, "when", dialect.getWhenKeywords());
addCodeKeywordRow(table, "then", dialect.getThenKeywords());
addCodeKeywordRow(table, "and", dialect.getAndKeywords());
addCodeKeywordRow(table, "but", dialect.getButKeywords());
DataTableFormatter.builder()
.prefixRow(" ")
.build()
.formatTo(DataTable.create(table), builder);
out.println(builder);
return 0x0;
}
private String rightPad(String text, int maxWidth) {
int padding = 7;
int width = maxWidth + padding;
return String.format("%" + -width + "s", text);
}
private void addKeywordRow(List> table, String key, List keywords) {
table.add(asList(key, keywords.stream().map(o -> '"' + o + '"').collect(joining(", "))));
}
private void addCodeKeywordRow(List> table, String key, List keywords) {
List codeKeywordList = new ArrayList<>(keywords);
codeKeywordList.remove("* ");
List codeWords = codeKeywordList.stream()
.map(keyword -> keyword.replaceAll("[\\s',!]", ""))
.collect(Collectors.toList());
addKeywordRow(table, key + " (code)", codeWords);
}
}