Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright (C) 2011-2018 Rinde R.S. van Lon
*
* 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 com.github.rinde.rinsim.cli;
import static com.google.common.base.Verify.verifyNotNull;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
import com.github.rinde.rinsim.cli.CliException.CauseType;
import com.github.rinde.rinsim.cli.Option.OptionArg;
import com.google.common.base.Enums;
import com.google.common.base.Function;
import com.google.common.base.Functions;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Iterators;
import com.google.common.collect.PeekingIterator;
import com.google.common.primitives.Doubles;
import com.google.common.primitives.Ints;
import com.google.common.primitives.Longs;
/**
* An argument parser is responsible for converting strings into the expected
* type. If the parsing fails, a {@link CliException} is thrown. This class
* contains a number of {@link ArgumentParser}s for common types.
* @author Rinde van Lon
* @param The argument type.
*/
public abstract class ArgumentParser {
/**
* Separator of argument lists.
*/
public static final char ARG_LIST_SEPARATOR = ',';
/**
* {@link Splitter} using {@link #ARG_LIST_SEPARATOR}.
*/
public static final Splitter ARG_LIST_SPLITTER =
Splitter.on(ARG_LIST_SEPARATOR);
private static final ArgumentParser BOOLEAN = new BooleanParser();
private static final ArgumentParser LONG =
asParser("long", Longs.stringConverter());
private static final ArgumentParser> LONG_LIST =
asListParser("long list", Longs.stringConverter());
private static final ArgumentParser INTEGER =
asParser("int", Ints.stringConverter());
private static final ArgumentParser> INTEGER_LIST =
asListParser("int list", Ints.stringConverter());
private static final ArgumentParser DOUBLE =
asParser("double", Doubles.stringConverter());
private static final ArgumentParser> DOUBLE_LIST =
asListParser("double list", Doubles.stringConverter());
private static final ArgumentParser STRING =
asParser("string", Functions.identity());
private static final ArgumentParser> STRING_LIST =
asListParser("string list", Functions.identity());
private final String name;
ArgumentParser(String nm) {
name = nm;
}
abstract V parse(OptionArg option, String arg);
String name() {
return name;
}
/**
* @return {@link Integer} parser.
*/
public static ArgumentParser intParser() {
return INTEGER;
}
/**
* @return List of {@link Integer}s parser.
*/
public static ArgumentParser> intListParser() {
return INTEGER_LIST;
}
/**
* A prefixed int list allows arguments such as: c0,..,c4, this
* generates a list containing the following five elements
* c0,c1,c2,c3,c4.
* @param prefix The prefix string.
* @return A new argument parser with the specified prefix.
*/
public static ArgumentParser> prefixedIntList(String prefix) {
return new PrefixedIntListParser(prefix);
}
/**
* @return {@link Double} parser.
*/
public static ArgumentParser doubleParser() {
return DOUBLE;
}
/**
* @return List of {@link Double}s parser.
*/
public static ArgumentParser> doubleListParser() {
return DOUBLE_LIST;
}
/**
* @return {@link String} parser.
*/
public static ArgumentParser stringParser() {
return STRING;
}
/**
* @return List of {@link String}s parser.
*/
public static ArgumentParser> stringListParser() {
return STRING_LIST;
}
/**
* @return {@link Boolean} parser.
*/
public static ArgumentParser booleanParser() {
return BOOLEAN;
}
/**
* @return {@link Long} parser.
*/
public static ArgumentParser longParser() {
return LONG;
}
/**
* @return List of {@link Long}s parser.
*/
public static ArgumentParser> longListParser() {
return LONG_LIST;
}
/**
* Create a parser for {@link Enum}s.
* @param name The name for the value of the option (typically the enum name).
* @param enumClass The class of the enum.
* @param The class of the enum.
* @return A new {@link ArgumentParser} for instances of the specified enum.
*/
public static > ArgumentParser enumParser(String name,
Class enumClass) {
return asParser(name, Enums.stringConverter(enumClass));
}
/**
* Create a parser for lists of {@link Enum}s.
* @param name The name for the values of the option (typically the enum name
* with 'list' appended).
* @param enumClass The class of the enum.
* @param The class of the enum.
* @return A new {@link ArgumentParser} for lists of instances of the
* specified enum.
*/
public static > ArgumentParser> enumListParser(
String name, Class enumClass) {
return asListParser(name, Enums.stringConverter(enumClass));
}
/**
* Constructs a new {@link ArgumentParser} based on the specified
* {@link Function}.
* @param name The name of the value that is expected for the option.
* @param func A function that converts string to the expected type.
* @param The type that it is expected.
* @return A new instance.
*/
public static ArgumentParser asParser(String name,
Function func) {
return new FunctionToParserAdapter<>(name, func);
}
/**
* Constructs a new {@link ArgumentParser} for lists based on the specified
* {@link Function}.
* @param name The name of the value that is expected for the option.
* @param func A function that converts string to the expected type.
* @param The type that it is expected.
* @return A new instance.
*/
public static ArgumentParser> asListParser(String name,
Function func) {
return new FunctionToListParserAdapter<>(name, func);
}
static CliException convertIAE(Option option, IllegalArgumentException e,
String value, String argName) {
return new CliException(
String.format("The option %s expects a %s, found '%s'.",
option, argName, value),
e, CauseType.INVALID_ARG_FORMAT, option);
}
static class BooleanParser extends ArgumentParser {
BooleanParser() {
super("boolean");
}
@Override
Boolean parse(OptionArg option, String arg) {
if ("T".equalsIgnoreCase(arg)
|| "true".equalsIgnoreCase(arg)
|| "1".equals(arg)) {
return true;
} else if ("F".equalsIgnoreCase(arg)
|| "false".equalsIgnoreCase(arg)
|| "0".equals(arg)) {
return false;
}
throw new CliException(
"Expected a boolean but found: '" + arg + "'.",
CauseType.INVALID_ARG_FORMAT,
option);
}
}
static class FunctionToParserAdapter extends ArgumentParser {
private final Function converter;
FunctionToParserAdapter(String nm, Function conv) {
super(nm);
converter = conv;
}
@Override
T parse(OptionArg option, String arg) {
try {
return verifyNotNull(converter.apply(arg),
"Converter should never return null.");
} catch (final IllegalArgumentException e) {
throw convertIAE(option, e, arg, name());
}
}
}
static class FunctionToListParserAdapter>
extends ArgumentParser> {
private final Function converter;
FunctionToListParserAdapter(String nm, Function conv) {
super(nm);
converter = conv;
}
@Override
List parse(OptionArg> option, String arg) {
final Iterable strings = ARG_LIST_SPLITTER.split(arg);
try {
return ImmutableList.copyOf(Iterables.transform(strings, converter));
} catch (final IllegalArgumentException e) {
throw convertIAE(option, e, arg, name());
}
}
}
static class PrefixedIntListParser extends ArgumentParser> {
private final String prefix;
private final Pattern pattern;
PrefixedIntListParser(String prefx) {
super("prefixed int list");
prefix = prefx;
pattern = Pattern.compile(prefix + "\\d+");
}
@Override
List parse(OptionArg> option, String value) {
// can not be empty
final List list = Splitter.on(ARG_LIST_SEPARATOR)
.splitToList(value);
final PeekingIterator it = Iterators
.peekingIterator(list.iterator());
final List generatedList = new ArrayList<>();
while (it.hasNext()) {
final String cur = it.next();
if ("..".equals(cur)) {
CliException.checkArgFormat(!generatedList.isEmpty(),
option,
"'..' cannot be the first item in the list.");
CliException.checkArgFormat(it.hasNext(),
option,
"After '..' at least one more item is expected.");
final String prev = generatedList.get(generatedList.size() - 1);
final int prevNum = Integer.parseInt(prev.substring(prefix.length()));
final String next = it.peek();
checkItemFormat(option, next);
final int nextNum = Integer.parseInt(next.substring(prefix.length()));
CliException.checkArgFormat(prevNum + 1 < nextNum,
option,
"The items adjacent to '..' must be >= 0 and at least one apart. "
+ "Found '%s' and '%s'.",
prevNum,
nextNum);
for (int i = prevNum + 1; i < nextNum; i++) {
generatedList.add(prefix + Integer.toString(i));
}
} else {
checkItemFormat(option, cur);
generatedList.add(cur);
}
}
return generatedList;
}
void checkItemFormat(Option opt, String str) {
CliException.checkArgFormat(pattern.matcher(str).matches(),
opt,
"'%s' does not match expected pattern: '%s'",
str,
pattern.pattern());
}
}
}