package org.graalvm.component.installer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import static org.graalvm.component.installer.Commands.DO_NOT_PROCESS_OPTIONS;
public class SimpleGetopt {
private LinkedList<String> parameters;
private final Map<String, String> globalOptions;
private final Map<String, Map<String, String>> commandOptions = new HashMap<>();
private final Map<String, String> optValues = new HashMap<>();
private final LinkedList<String> positionalParameters = new LinkedList<>();
private String command;
private boolean ignoreUnknownCommands;
private boolean unknownCommand;
private List<String> unknownOptions;
private Map<String, String> abbreviations = new HashMap<>();
private final Map<String, Map<String, String>> commandAbbreviations = new HashMap<>();
public SimpleGetopt(Map<String, String> globalOptions) {
this.globalOptions = globalOptions;
}
public void setParameters(LinkedList<String> parameters) {
this.parameters = parameters;
}
public SimpleGetopt ignoreUnknownOptions(boolean ignore) {
this.unknownOptions = ignore ? new ArrayList<>() : null;
return this;
}
public SimpleGetopt ignoreUnknownCommands(boolean ignore) {
this.ignoreUnknownCommands = ignore;
return this;
}
public List<String> getUnknownOptions() {
return unknownOptions == null ? Collections.emptyList() : unknownOptions;
}
public RuntimeException err(String messageKey, Object... args) {
throw ComponentInstaller.err(messageKey, args);
}
private String findCommand(String cmdString) {
String cmd = cmdString;
if (cmd.isEmpty()) {
if (ignoreUnknownCommands) {
return null;
}
throw err("ERROR_MissingCommand");
}
String selCommand = null;
for (String s : commandOptions.keySet()) {
if (s.startsWith(cmdString)) {
if (selCommand != null) {
throw err("ERROR_AmbiguousCommand", cmdString, selCommand, s);
}
selCommand = s;
if (s.length() == cmdString.length()) {
break;
}
}
}
if (selCommand == null) {
if (ignoreUnknownCommands) {
unknownCommand = true;
command = cmdString;
return null;
}
throw err("ERROR_UnknownCommand", cmdString);
}
command = selCommand;
return command;
}
private static final String NO_ABBREV = "**no-abbrev";
private boolean hasCommand() {
return command != null && !unknownCommand;
}
@SuppressWarnings("StringEquality")
Map<String, String> computeAbbreviations(Collection<String> optNames) {
Map<String, String> result = new HashMap<>();
for (String o : optNames) {
if (o.length() < 2) {
continue;
}
result.put(o, NO_ABBREV);
for (int i = 2; i < o.length(); i++) {
String s = o.substring(0, i);
String fullName = result.get(s);
if (fullName == null) {
result.put(s, o);
} else {
result.put(s, NO_ABBREV);
}
}
}
for (Iterator<Entry<String, String>> ens = result.entrySet().iterator(); ens.hasNext();) {
Entry<String, String> en = ens.next();
if (NO_ABBREV.equals(en.getValue())) {
ens.remove();
}
}
return result;
}
Collection<String> getAllOptions() {
Set<String> s = new HashSet<>();
s.addAll(globalOptions.keySet());
for (Map<String, String> cmdOpts : commandOptions.values()) {
s.addAll(cmdOpts.keySet());
}
for (Iterator<String> it = s.iterator(); it.hasNext();) {
String opt = it.next();
if (opt.length() == 1 && !Character.isLetterOrDigit(opt.charAt(0))) {
it.remove();
}
}
return s;
}
void computeAbbreviations() {
abbreviations = computeAbbreviations(globalOptions.keySet());
for (String c : commandOptions.keySet()) {
Set<String> names = new HashSet<>(commandOptions.get(c).keySet());
names.addAll(globalOptions.keySet());
Map<String, String> commandAbbrevs = computeAbbreviations(names);
commandAbbreviations.put(c, commandAbbrevs);
}
}
public void process() {
computeAbbreviations();
while (true) {
String p = parameters.peek();
if (p == null) {
break;
}
if (!p.startsWith("-")) {
if (command == null) {
findCommand(parameters.poll());
Map<String, String> cOpts = commandOptions.get(command);
if (cOpts != null) {
for (String s : optValues.keySet()) {
if (s.length() > 1) {
continue;
}
if ("X".equals(cOpts.get(s))) {
unknownOption(s, command);
break;
}
}
if (cOpts.containsKey(DO_NOT_PROCESS_OPTIONS)) {
positionalParameters.addAll(parameters);
break;
}
} else {
positionalParameters.add(p);
}
} else {
positionalParameters.add(parameters.poll());
}
continue;
} else if (p.length() == 1 || "--".equals(p)) {
parameters.poll();
positionalParameters.addAll(parameters);
break;
}
String param = parameters.poll();
boolean nextParam = p.startsWith("--");
String optName;
int optCharIndex = 1;
while (optCharIndex < param.length()) {
if (nextParam) {
optName = param.substring(2);
param = processOptSpec(optName, optCharIndex, param, nextParam);
} else {
optName = param.substring(optCharIndex, optCharIndex + 1);
optCharIndex += optName.length();
param = processOptSpec(optName, optCharIndex, param, nextParam);
}
if (optValues.get("h") != null) {
return;
}
if (nextParam) {
break;
}
}
}
}
private void unknownOption(String option, String cmd) {
if (unknownOptions == null) {
if (cmd == null) {
throw err("ERROR_UnsupportedGlobalOption", option);
} else {
throw err("ERROR_UnsupportedOption", option, cmd);
}
} else {
unknownOptions.add(option);
}
}
private String processOptSpec(String o, int optCharIndex, String optParam, boolean nextParam) {
String param = optParam;
String optSpec = null;
String optName = o;
Map<String, String> cmdSpec = null;
if (hasCommand()) {
Map<String, String> cmdAbbrevs = commandAbbreviations.get(command);
String fullO = cmdAbbrevs.get(optName);
if (fullO != null) {
optName = fullO;
}
cmdSpec = commandOptions.get(command);
String c = cmdSpec.get(optName);
if (c != null && optName.length() > 1) {
optSpec = cmdSpec.get(c);
optName = c;
} else {
optSpec = c;
}
}
if (optSpec == null) {
String fullO = abbreviations.get(optName);
if (fullO != null) {
optName = fullO;
}
String c = globalOptions.get(optName);
if (c != null && optName.length() > 1) {
optSpec = globalOptions.get(c);
optName = c;
} else {
optSpec = c;
}
}
if (optSpec != null && optSpec.startsWith("=")) {
String s = optSpec.substring(1);
String nspec = null;
if (cmdSpec != null) {
nspec = cmdSpec.get(s);
}
if (nspec == null) {
nspec = globalOptions.get(s);
}
if (nspec != null) {
optSpec = nspec;
optName = s;
}
}
if (optSpec == null) {
if (unknownCommand) {
return param;
}
unknownOption(optName, command);
return param;
}
String optVal = "";
switch (optSpec) {
case "s":
if (nextParam) {
optVal = parameters.poll();
if (optVal == null) {
throw err("ERROR_OptionNeedsParameter", o, command);
}
} else {
if (optCharIndex < param.length()) {
optVal = param.substring(optCharIndex);
param = "";
} else if (parameters.isEmpty()) {
throw err("ERROR_OptionNeedsParameter", o, command);
} else {
optVal = parameters.poll();
}
}
break;
case "X":
unknownOption(o, command);
return param;
case "":
break;
}
optValues.put(optName, optVal);
return param;
}
public String getCommand() {
return command;
}
public void addCommandOptions(String commandName, Map<String, String> optSpec) {
commandOptions.put(commandName, new HashMap<>(optSpec));
}
void addCommandOption(String commandName, String optName, String optVal) {
commandOptions.get(commandName).put(optName, optVal);
}
public Map<String, String> getOptValues() {
return optValues;
}
public LinkedList<String> getPositionalParameters() {
return positionalParameters;
}
}