package act.cli.util;
/*-
* #%L
* ACT Framework
* %%
* Copyright (C) 2014 - 2017 ActFramework
* %%
* 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.
* #L%
*/
import act.cli.InvalidCommandLineException;
import org.osgl.util.C;
import org.osgl.util.E;
import org.osgl.util.S;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Parse a command line
*/
public class CommandLineParser {
private String command;
private String raw;
/*
* Map value part to lead part. E.g.
* {@code -n 10} create an entry ("-n", "10") and
* {@code -b} create an entry ("-b", "true")
*/
private Map<String, String> options;
private List<String> arguments;
static final Pattern P_QUOTATION = Pattern.compile("([^\"]\\S*|\".+?\")\\s*");
static final Pattern P_APOSTROPHE = Pattern.compile("([^\']\\S*|\'.+?\')\\s*");
public CommandLineParser(String line) {
E.illegalArgumentIf(S.blank(line));
raw = line.trim();
options = C.newMap();
List<String> sl = C.newList();
Pattern ptn = choosePattern(raw);
Matcher m = ptn.matcher(raw);
while (m.find()) {
String s = m.group(1);
if (ptn == P_QUOTATION) {
s = S.strip(s, "\"", "\"");
} else {
s = S.strip(s, "\'", "\'");
}
sl.add(s);
}
parse(sl);
}
private Pattern choosePattern(String s) {
Pattern ptn = P_APOSTROPHE;
int p1 = s.indexOf('"');
if (p1 > -1) {
int p2 = s.indexOf('\'');
if (p2 < 0 || p1 < p2) {
ptn = P_QUOTATION;
}
}
return ptn;
}
public Map<String, String> getOptions() {
return options;
}
public List<String> getArguments() {
return arguments;
}
public String commandLine() {
return raw;
}
private void parse(List<String> tokens) {
command = tokens.remove(0).intern();
arguments = C.newList();
String lead = null;
for (String cur : tokens) {
if (cur.startsWith("-")) {
// todo handle --option=a, --option:b style
String[] sa = cur.split("[=:]");
switch (sa.length) {
case 1:
break;
case 2:
options.put(sa[0], sa[1]);
continue;
default:
throw new InvalidCommandLineException("uknown option: %s", cur);
}
if (null != lead) {
options.put(lead, "true");
}
lead = cur;
} else {
if (null != lead) {
options.put(lead, cur);
lead = null;
} else {
arguments.add(cur);
}
}
}
if (null != lead) {
options.put(lead, "true");
}
}
public String command() {
return command;
}
public List<String> arguments() {
return C.list(arguments);
}
public String argument(int id) {
if (id >= arguments.size()) {
return null;
}
return arguments.get(id);
}
public int argumentCount() {
return arguments.size();
}
public int optionCount() {
return arguments.size();
}
/**
* When a command has only one option and no arguments the
* user can supply the option as argument to suppress leads typing.
*
* This method will return the single argument as an option with
* any leads. If the parser found there are more than one argument
* or there are options then this method will return `null`
* @return
*/
public String argumentAsOption() {
if (1 == argumentCount() && 0 == options.size()) {
return argument(0);
}
return null;
}
public boolean getBoolean(String lead1, String lead2) {
String s = o(lead1, lead2);
return "true" == s;
}
public Boolean getBooleanObject(String lead1, String lead2) {
return getBoolean(lead1, lead2);
}
public byte getByte(String lead1, String lead2, Byte def) {
Byte b = getByteObject(lead1, lead2, def);
if (null != b) {
return b;
}
throw new InvalidCommandLineException("Cannot get byte type option");
}
public Byte getByteObject(String lead1, String lead2, Byte def) {
String s = o(lead1, lead2);
return null == s ? def : Byte.valueOf(s);
}
public char getChar(String lead1, String lead2, Character def) {
Character c = getCharObject(lead1, lead2, def);
if (null != c) {
return c;
}
throw new InvalidCommandLineException("Cannot get char type option");
}
public Character getCharObject(String lead1, String lead2, Character def) {
String s = o(lead1, lead2);
return null == s ? def : s.charAt(0);
}
public short getShort(String lead1, String lead2, Short def) {
Short o = getShortObject(lead1, lead2, def);
if (null != o) {
return o;
}
throw new InvalidCommandLineException("Cannot get short type option");
}
public Short getShortObject(String lead1, String lead2, Short def) {
String s = o(lead1, lead2);
return null == s ? def : Short.valueOf(s);
}
public int getInt(String lead1, String lead2, Integer def) {
Integer n = getIntObject(lead1, lead2, def);
if (null != n) {
return n;
}
throw new InvalidCommandLineException("Cannot get int type option");
}
public Integer getIntObject(String lead1, String lead2, Integer def) {
String s = o(lead1, lead2);
return null == s ? def : Integer.valueOf(s);
}
public float getFloat(String lead1, String lead2, Float def) {
Float o = getFloatObject(lead1, lead2, def);
if (null != o) {
return o;
}
throw new InvalidCommandLineException("Cannot get float type option");
}
public Float getFloatObject(String lead1, String lead2, Float def) {
String s = o(lead1, lead2);
return null == s ? def : Float.valueOf(s);
}
public long getLong(String lead1, String lead2, Long def) {
Long o = getLongObject(lead1, lead2, def);
if (null != o) {
return o;
}
throw new InvalidCommandLineException("Cannot get long type option");
}
public Double getDoubleObject(String lead1, String lead2, Double def) {
String s = o(lead1, lead2);
return null == s ? def : Double.valueOf(s);
}
public double getDouble(String lead1, String lead2, Double def) {
Double o = getDoubleObject(lead1, lead2, def);
if (null != o) {
return o;
}
throw new InvalidCommandLineException("Cannot get double type option");
}
public Long getLongObject(String lead1, String lead2, Long def) {
String s = o(lead1, lead2);
return null == s ? def : Long.valueOf(s);
}
public String getString(String lead1, String lead2) {
return o(lead1, lead2);
}
private String o(String lead1, String lead2) {
String s = options.get(lead1);
return null == s ? options.get(lead2) : s;
}
}