// Argument conversion time
Annotation[][] parameterAnnotations = methodTarget.getMethod().getParameterAnnotations();
if (parameterAnnotations.length == 0) {
// No args
return new ParseResult(methodTarget.getMethod(), methodTarget.getTarget(), null);
}
// Oh well, we need to convert some arguments
final List<Object> arguments = new ArrayList<Object>(methodTarget.getMethod().getParameterTypes().length);
// Attempt to parse
Map<String, String> options = null;
try {
options = new Tokenizer(methodTarget.getRemainingBuffer()).getTokens();
}
catch (TokenizingException te) {
String commandKey = methodTarget.getKey();
reportTokenizingException(commandKey, te);
return null;
}
catch (IllegalArgumentException e) {
LOGGER.warning(ExceptionUtils.extractRootCause(e).getMessage());
return null;
}
final Set<CliOption> cliOptions = getCliOptions(parameterAnnotations);
for (CliOption cliOption : cliOptions) {
Class<?> requiredType = methodTarget.getMethod().getParameterTypes()[arguments.size()];
if (cliOption.systemProvided()) {
Object result;
if (SimpleParser.class.isAssignableFrom(requiredType)) {
result = this;
}
else {
LOGGER.warning("Parameter type '" + requiredType + "' is not system provided");
return null;
}
arguments.add(result);
continue;
}
// Obtain the value the user specified, taking care to ensure they only specified it via a single alias
String value = null;
String sourcedFrom = null;
for (String possibleKey : cliOption.key()) {
if (options.containsKey(possibleKey)) {
if (sourcedFrom != null) {
LOGGER.warning("You cannot specify option '" + possibleKey
+ "' when you have also specified '" + sourcedFrom + "' in the same command");
return null;
}
sourcedFrom = possibleKey;
value = options.get(possibleKey);
}
}
// Ensure the user specified a value if the value is mandatory or
// key and value must appear in pair
boolean mandatory = !StringUtils.hasText(value) && cliOption.mandatory();
boolean specifiedKey = !StringUtils.hasText(value) && options.containsKey(sourcedFrom);
boolean specifiedKeyWithoutValue = false;
if (specifiedKey) {
value = cliOption.specifiedDefaultValue();
if ("__NULL__".equals(value)) {
specifiedKeyWithoutValue = true;
}
}
if (mandatory || specifiedKeyWithoutValue) {
if ("".equals(cliOption.key()[0])) {
StringBuilder message = new StringBuilder("You should specify a default option ");
if (cliOption.key().length > 1) {
message.append("(otherwise known as option '").append(cliOption.key()[1]).append("') ");
}
message.append("for this command");
LOGGER.warning(message.toString());
}
else {
printHintMessage(cliOptions, options);
}
return null;
}
// Accept a default if the user specified the option, but didn't provide a value
if ("".equals(value)) {
value = cliOption.specifiedDefaultValue();
}
// Accept a default if the user didn't specify the option at all
if (value == null) {
value = cliOption.unspecifiedDefaultValue();
}
// Special token that denotes a null value is sought (useful for default values)
if ("__NULL__".equals(value)) {
if (requiredType.isPrimitive()) {
LOGGER.warning("Nulls cannot be presented to primitive type " + requiredType.getSimpleName()
+ " for option '" + StringUtils.arrayToCommaDelimitedString(cliOption.key()) + "'");
return null;
}
arguments.add(null);
continue;
}
// Now we're ready to perform a conversion
try {
CliOptionContext.setOptionContext(cliOption.optionContext());
CliSimpleParserContext.setSimpleParserContext(this);
Object result;
Converter<?> c = null;
for (Converter<?> candidate : converters) {
if (candidate.supports(requiredType, cliOption.optionContext())) {
// Found a usable converter
c = candidate;
break;
}
}
if (c == null) {
throw new IllegalStateException("TODO: Add basic type conversion");
// TODO Fall back to a normal SimpleTypeConverter and attempt conversion
// SimpleTypeConverter simpleTypeConverter = new SimpleTypeConverter();
// result = simpleTypeConverter.convertIfNecessary(value, requiredType, mp);
}
// Use the converter
result = c.convertFromText(value, requiredType, cliOption.optionContext());
// If the option has been specified to be mandatory then the result should never be null
if (result == null && cliOption.mandatory()) {
throw new IllegalStateException();
}
arguments.add(result);
}
catch (RuntimeException e) {
LOGGER.warning(e.getClass().getName() + ": Failed to convert '" + value + "' to type "
+ requiredType.getSimpleName() + " for option '"
+ StringUtils.arrayToCommaDelimitedString(cliOption.key()) + "'");
if (StringUtils.hasText(e.getMessage())) {
LOGGER.warning(e.getMessage());
}
return null;
}
finally {
CliOptionContext.resetOptionContext();
CliSimpleParserContext.resetSimpleParserContext();
}
}
// Check for options specified by the user but are unavailable for the command
Set<String> unavailableOptions = getSpecifiedUnavailableOptions(cliOptions, options);
if (!unavailableOptions.isEmpty()) {
StringBuilder message = new StringBuilder();
if (unavailableOptions.size() == 1) {
message.append("Option '").append(unavailableOptions.iterator().next())
.append("' is not available for this command. ");
}
else {
message.append("Options ")
.append(StringUtils.collectionToDelimitedString(unavailableOptions, ", ", "'", "'"))
.append(" are not available for this command. ");
}
message.append("Use tab assist or the \"help\" command to see the legal options");
LOGGER.warning(message.toString());
return null;
}
return new ParseResult(methodTarget.getMethod(), methodTarget.getTarget(), arguments.toArray());
}
}