Add legacy and default resolvers

This commit is contained in:
Eric Bottard
2015-12-10 16:30:53 +01:00
parent 3cd5a1be3b
commit 938bd7cfa2
18 changed files with 899 additions and 0 deletions

17
.gitignore vendored Normal file
View File

@@ -0,0 +1,17 @@
/.classpath
/.project
.settings/
.gradle
build
target/
/log.roo
/bin/
/*.log
/.idea
/*.iml
/*.ipr
/*.iws
out
.gradletasknamecache

52
pom.xml Normal file
View File

@@ -0,0 +1,52 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.shell</groupId>
<artifactId>spring-shell2</artifactId>
<version>2.0.0.BUILD-SNAPSHOT</version>
<packaging>jar</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.3.0.RELEASE</version>
</parent>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>jline</groupId>
<artifactId>jline</artifactId>
<version>3.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.shell</groupId>
<artifactId>spring-shell</artifactId>
<version>1.2.0.BUILD-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,38 @@
package org.springframework.shell2;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.shell.converters.ArrayConverter;
import org.springframework.shell.converters.AvailableCommandsConverter;
import org.springframework.shell.converters.SimpleFileConverter;
/**
*/
@SpringBootApplication
@ComponentScan(basePackageClasses = {ArrayConverter.class, Bootstrap.class}, excludeFilters = @ComponentScan.Filter(
type = FilterType.ASSIGNABLE_TYPE,
value = {AvailableCommandsConverter.class, SimpleFileConverter.class}))
public class Bootstrap {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(Bootstrap.class);
context.getBean(JLineShell.class).run();
}
@Bean
public ConversionService conversionService() {
return new DefaultConversionService();
}
@Bean
public ParameterResolver parameterResolver(ConversionService conversionService) {
return new DefaultParameterResolver(conversionService);
}
}

View File

@@ -0,0 +1,35 @@
package org.springframework.shell2;
import java.util.HashMap;
import java.util.Map;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;
/**
* Created by ericbottard on 09/12/15.
*/
@Component
public class DefaultMethodTargetResolver implements MethodTargetResolver {
@Override
public Map<String, MethodTarget> resolve(ApplicationContext applicationContext) {
Map<String,MethodTarget> methodTargets = new HashMap<>();
Map<String, Object> commandBeans = applicationContext.getBeansWithAnnotation(ShellComponent.class);
for (Object bean : commandBeans.values()) {
Class<?> clazz = bean.getClass();
ReflectionUtils.doWithMethods(clazz, method -> {
ShellMapping shellMapping = method.getAnnotation(ShellMapping.class);
String[] keys = shellMapping.value();
if (keys.length == 1 && "".equals(keys[0])) {
keys[0] = method.getName();
}
for (String key : keys) {
methodTargets.put(key, new MethodTarget(method, bean, shellMapping.help()));
}
}, method -> method.getAnnotation(ShellMapping.class) != null);
}
return methodTargets;
}
}

View File

@@ -0,0 +1,31 @@
package org.springframework.shell2;
import java.util.List;
import org.springframework.core.MethodParameter;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
/**
* Created by ericbottard on 09/12/15.
*/
class DefaultParameterResolver implements ParameterResolver {
private final ConversionService conversionService;
public DefaultParameterResolver(ConversionService conversionService) {
this.conversionService = conversionService;
}
@Override
public boolean supports(MethodParameter parameter) {
return true;
}
@Override
public Object resolve(MethodParameter methodParameter, List<String> words) {
TypeDescriptor targetType = new TypeDescriptor(methodParameter);
Object converted = conversionService.convert(words.get(methodParameter.getParameterIndex()), TypeDescriptor.valueOf(String.class), targetType);
return converted;
}
}

View File

@@ -0,0 +1,154 @@
package org.springframework.shell2;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.PostConstruct;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.executable.ExecutableValidator;
import org.jline.reader.Candidate;
import org.jline.reader.Completer;
import org.jline.reader.Highlighter;
import org.jline.reader.LineReader;
import org.jline.reader.LineReaderBuilder;
import org.jline.reader.ParsedLine;
import org.jline.reader.impl.DefaultParser;
import org.jline.terminal.Terminal;
import org.jline.terminal.TerminalBuilder;
import org.jline.utils.AttributedString;
import org.jline.utils.AttributedStyle;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.core.MethodParameter;
import org.springframework.core.convert.ConversionService;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;
/**
* Created by ericbottard on 26/11/15.
*/
@Component
public class JLineShell {
@Autowired
private ApplicationContext applicationContext;
@Autowired
private ConversionService conversionService;
private Map<String, Object> commandBeans;
private Map<String, MethodTarget> methodTargets = new HashMap<>();
private LineReader lineReader;
@Autowired
private List<ParameterResolver> parameterResolvers = new ArrayList<>();
@PostConstruct
public void init() throws Exception {
for (MethodTargetResolver resolver : applicationContext.getBeansOfType(MethodTargetResolver.class).values()) {
methodTargets.putAll(resolver.resolve(applicationContext));
}
Terminal terminal = TerminalBuilder.builder().build();
LineReaderBuilder lineReaderBuilder = LineReaderBuilder.builder()
.terminal(terminal)
.appName("Foo")
.completer(new Completer() {
@Override
public void complete(LineReader reader, ParsedLine line, List<Candidate> candidates) {
candidates.add(new Candidate("value", "displayed value", "v", "the description", null, null, false));
candidates.add(new Candidate("value1", "displayed value1", "v", "the description of v1", null, null, false));
}
})
.highlighter(new Highlighter() {
@Override
public AttributedString highlight(LineReader reader, String buffer) {
if (buffer.length() < 4) {
return new AttributedString(buffer, AttributedStyle.DEFAULT.blink().foreground(AttributedStyle.RED));
}
else {
return new AttributedString(buffer);
}
}
})
.parser(new DefaultParser());
lineReader = lineReaderBuilder.build();
}
public void run() {
while (true) {
lineReader.readLine("shell:>");
String separator = "";
StringBuilder candidateCommand = new StringBuilder();
MethodTarget methodTarget = null;
int c = 0;
int wordsUsedForCommandKey = 0;
for (String word : lineReader.getParsedLine().words()) {
c++;
candidateCommand.append(separator).append(word);
MethodTarget t = methodTargets.get(candidateCommand.toString());
if (t != null) {
methodTarget = t;
wordsUsedForCommandKey = c;
}
separator = " ";
}
List<String> words = lineReader.getParsedLine().words();
if (methodTarget != null) {
Parameter[] parameters = methodTarget.getMethod().getParameters();
Object[] rawArgs = new Object[parameters.length];
Object unresolved = new Object();
Arrays.fill(rawArgs, unresolved);
for (int i = 0; i < parameters.length; i++) {
MethodParameter methodParameter = new MethodParameter(methodTarget.getMethod(), i);
for (ParameterResolver resolver : parameterResolvers) {
if (resolver.supports(methodParameter)) {
rawArgs[i] = resolver.resolve(methodParameter, words.subList(wordsUsedForCommandKey, words.size()));
break;
}
}
if (rawArgs[i] == unresolved) {
throw new IllegalStateException("Could not resolve " + methodParameter);
}
}
ExecutableValidator executableValidator = Validation
.buildDefaultValidatorFactory().getValidator().forExecutables();
Set<ConstraintViolation<Object>> constraintViolations = executableValidator.validateParameters(methodTarget.getBean(),
methodTarget.getMethod(),
rawArgs);
System.out.println(constraintViolations);
Object result = null;
try {
result = ReflectionUtils.invokeMethod(methodTarget.getMethod(), methodTarget.getBean(), rawArgs);
}
catch (Exception e) {
result = e;
}
System.out.println(String.valueOf(result));
}
else {
System.out.println("No command found for " + words);
}
}
}
}

View File

@@ -0,0 +1,54 @@
package org.springframework.shell2;
import java.lang.reflect.Method;
/**
* Created by ericbottard on 27/11/15.
*/
public class MethodTarget {
private final Method method;
private final Object bean;
private final String help;
public MethodTarget(Method method, Object bean, String help) {
this.method = method;
this.bean = bean;
this.help = help;
}
public Method getMethod() {
return method;
}
public Object getBean() {
return bean;
}
public String getHelp() {
return help;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MethodTarget that = (MethodTarget) o;
if (!method.equals(that.method)) return false;
if (!bean.equals(that.bean)) return false;
return help.equals(that.help);
}
@Override
public int hashCode() {
int result = method.hashCode();
result = 31 * result + bean.hashCode();
result = 31 * result + help.hashCode();
return result;
}
}

View File

@@ -0,0 +1,14 @@
package org.springframework.shell2;
import java.util.Map;
import org.springframework.context.ApplicationContext;
/**
* Created by ericbottard on 09/12/15.
*/
public interface MethodTargetResolver {
public Map<String, MethodTarget> resolve(ApplicationContext context);
}

View File

@@ -0,0 +1,16 @@
package org.springframework.shell2;
import java.util.List;
import org.springframework.core.MethodParameter;
/**
* Created by ericbottard on 27/11/15.
*/
public interface ParameterResolver {
boolean supports(MethodParameter parameter);
public Object resolve(MethodParameter methodParameter, List<String> words);
}

View File

@@ -0,0 +1,21 @@
package org.springframework.shell2;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.stereotype.Component;
/**
* Created by ericbottard on 27/11/15.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Component
public @interface ShellComponent {
String value();
}

View File

@@ -0,0 +1,19 @@
package org.springframework.shell2;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Created by ericbottard on 27/11/15.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface ShellMapping {
String[] value() default "";
String help() default "";
}

View File

@@ -0,0 +1,18 @@
package org.springframework.shell2;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Created by ericbottard on 27/11/15.
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface ShellOption {
String[] value() default "";
}

View File

@@ -0,0 +1,35 @@
package org.springframework.shell2.legacy;
import java.util.HashMap;
import java.util.Map;
import org.springframework.context.ApplicationContext;
import org.springframework.shell.core.CommandMarker;
import org.springframework.shell.core.annotation.CliCommand;
import org.springframework.shell2.MethodTarget;
import org.springframework.shell2.MethodTargetResolver;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;
/**
* Created by ericbottard on 09/12/15.
*/
@Component
public class LegacyMethodTargetResolver implements MethodTargetResolver {
@Override
public Map<String, MethodTarget> resolve(ApplicationContext context) {
Map<String, MethodTarget> methodTargets = new HashMap<>();
Map<String, CommandMarker> beansOfType = context.getBeansOfType(CommandMarker.class);
for (Object bean : beansOfType.values()) {
Class<?> clazz = bean.getClass();
ReflectionUtils.doWithMethods(clazz, method -> {
CliCommand shellMapping = method.getAnnotation(CliCommand.class);
for (String key : shellMapping.value()) {
methodTargets.put(key, new MethodTarget(method, bean, shellMapping.help()));
}
}, method -> method.getAnnotation(CliCommand.class) != null);
}
return methodTargets;
}
}

View File

@@ -0,0 +1,112 @@
package org.springframework.shell2.legacy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.shell.core.Converter;
import org.springframework.shell.core.annotation.CliOption;
import org.springframework.shell2.ParameterResolver;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
/**
* Created by ericbottard on 09/12/15.
*/
@Component
public class LegacyParameterResolver implements ParameterResolver {
public static final Object UNSPECIFIED = new Object();
@Autowired(required = false)
private Collection<Converter<?>> converters = new ArrayList<>();
@Override
public boolean supports(MethodParameter parameter) {
return parameter.hasParameterAnnotation(CliOption.class);
}
@Override
public Object resolve(MethodParameter methodParameter, List<String> words) {
CliOption cliOption = methodParameter.getParameterAnnotation(CliOption.class);
Optional<Converter<?>> converter = converters.stream()
.filter(c -> c.supports(methodParameter.getParameterType(), cliOption.optionContext()))
.findFirst();
Map<String, String> values = parseOptions(words);
Map<String, Object> seenValues = convertValues(values, methodParameter, converter);
switch (seenValues.size()) {
case 0:
if (!cliOption.mandatory()) {
String value = cliOption.unspecifiedDefaultValue();
return converter
.orElseThrow(noConverterFound(cliOption.key()[0], value, methodParameter.getParameterType()))
.convertFromText(value, methodParameter.getParameterType(), cliOption.optionContext());
}
else {
throw new IllegalArgumentException("Could not find parameter values for " + prettifyKeys(Arrays.asList(cliOption.key())) + " in " + words);
}
case 1:
return seenValues.values().iterator().next();
default:
throw new RuntimeException("Option has been set multiple times via " + prettifyKeys(seenValues.keySet()));
}
}
private Map<String, String> parseOptions(List<String> words) {
Map<String, String> values = new HashMap<>();
for (int i = 0; i < words.size(); i++) {
String word = words.get(i);
if (word.startsWith("--")) {
String key = word.substring("--".length());
// If next word doesn't exist or starts with '--', this is an unary option. Store null
String value = i < words.size() - 1 && !words.get(i+1).startsWith("--") ? words.get(++i) : null;
Assert.isTrue(!values.containsKey(key), String.format("Option --%s has already been set", key));
values.put(key, value);
} // Must be the 'anonymous' option
else {
Assert.isTrue(!values.containsKey(""), "Anonymous option has already been set");
values.put("", word);
}
}
return values;
}
private Map<String, Object> convertValues(Map<String, String> values, MethodParameter methodParameter, Optional<Converter<?>> converter) {
Map<String, Object> seenValues = new HashMap<>();
CliOption option = methodParameter.getParameterAnnotation(CliOption.class);
for (String key : option.key()) {
if (values.containsKey(key)) {
String value = values.get(key);
if (value == null && !"__NULL__".equals(option.specifiedDefaultValue())) {
value = option.specifiedDefaultValue();
}
Class<?> parameterType = methodParameter.getParameterType();
seenValues.put(key, converter
.orElseThrow(noConverterFound(key, value, parameterType))
.convertFromText(value, parameterType, option.optionContext()));
}
}
return seenValues;
}
/**
* Return the list of possible keys for an option, suitable for displaying in an error message.
*/
private String prettifyKeys(Collection<String> keys) {
return keys.stream().map(s -> "".equals(s) ? "<anonymous>" : "--" + s).collect(Collectors.joining(", ", "[", "]"));
}
private Supplier<IllegalStateException> noConverterFound(String key, String value, Class<?> parameterType) {
return () -> new IllegalStateException("No converter found for --" + key + " from '" + value + "' to type " + parameterType);
}
}

View File

@@ -0,0 +1,9 @@
package org.springframework.shell2.legacy;
/**
* Created by ericbottard on 09/12/15.
*/
public enum ArtifactType {
source, processor, sink, task
}

View File

@@ -0,0 +1,45 @@
package org.springframework.shell2.legacy;
import java.lang.reflect.Method;
import org.springframework.shell.core.CommandMarker;
import org.springframework.shell.core.annotation.CliCommand;
import org.springframework.shell.core.annotation.CliOption;
import org.springframework.util.ReflectionUtils;
/**
* Created by ericbottard on 09/12/15.
*/
public class LegacyCommands implements CommandMarker {
public static final Method REGISTER_METHOD = ReflectionUtils.findMethod(LegacyCommands.class, "register", String.class, ArtifactType.class, String.class, boolean.class);
public static final Method SUM_METHOD = ReflectionUtils.findMethod(LegacyCommands.class, "sum", int.class, int.class);
@CliCommand(value = "register module", help = "Register a new module")
public String register(
@CliOption(mandatory = true,
key = {"", "name"},
help = "the name for the registered module")
String name,
@CliOption(mandatory = true,
key = {"type"},
help = "the type for the registered module")
ArtifactType type,
@CliOption(mandatory = true,
key = {"coordinates", "coords"},
help = "coordinates to the module archive")
String coordinates,
@CliOption(key = "force",
help = "force update if module already exists (only if not in use)",
specifiedDefaultValue = "true",
unspecifiedDefaultValue = "false")
boolean force) {
return String.format(("Successfully registered module '%s:%s'"), type, name);
}
@CliCommand("sum")
public int sum(@CliOption(key = "v1", unspecifiedDefaultValue = "38") int a, @CliOption(key = "v2", specifiedDefaultValue = "42") int b) {
return a + b;
}
}

View File

@@ -0,0 +1,60 @@
package org.springframework.shell2.legacy;
import static org.junit.Assert.assertThat;
import java.util.Map;
import org.hamcrest.collection.IsMapContaining;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.shell2.MethodTarget;
import org.springframework.shell2.MethodTargetResolver;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* Created by ericbottard on 09/12/15.
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = LegacyMethodTargetResolverTest.Config.class)
public class LegacyMethodTargetResolverTest {
@Autowired
private ApplicationContext applicationContext;
@Autowired
private LegacyCommands legacyCommands;
@Autowired
private MethodTargetResolver resolver;
@Test
public void findsMethodsAnnotatedWithCliCommand() throws Exception {
Map<String, MethodTarget> targets = resolver.resolve(applicationContext);
assertThat(targets, IsMapContaining.hasEntry(
"register module",
new MethodTarget(LegacyCommands.REGISTER_METHOD, legacyCommands, "Register a new module" )
));
}
@Configuration
static class Config {
@Bean
public LegacyCommands legacyCommands() {
return new LegacyCommands();
}
@Bean
public MethodTargetResolver methodTargetResolver() {
return new LegacyMethodTargetResolver();
}
}
}

View File

@@ -0,0 +1,169 @@
package org.springframework.shell2.legacy;
import static java.util.Arrays.asList;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import org.hamcrest.Matchers;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.MethodParameter;
import org.springframework.shell.converters.BooleanConverter;
import org.springframework.shell.converters.EnumConverter;
import org.springframework.shell.converters.StringConverter;
import org.springframework.shell.core.Converter;
import org.springframework.shell2.ParameterResolver;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = LegacyParameterResolverTest.Config.class)
public class LegacyParameterResolverTest {
private static final int NAME_OR_ANONYMOUS = 0;
private static final int TYPE = 1;
private static final int COORDINATES = 2;
private static final int FORCE = 3;
@Autowired
ParameterResolver parameterResolver;
@Rule
public ExpectedException thrown = ExpectedException.none();
@Test
public void supportsParameterAnnotatedWithCliOption() throws Exception {
MethodParameter methodParameter = new MethodParameter(LegacyCommands.REGISTER_METHOD, NAME_OR_ANONYMOUS);
boolean supports = parameterResolver.supports(methodParameter);
assertThat(supports, is(true));
}
@Test
public void resolvesParameterAnnotatedWithCliOption() throws Exception {
MethodParameter methodParameter = new MethodParameter(LegacyCommands.REGISTER_METHOD, NAME_OR_ANONYMOUS);
Object result = parameterResolver.resolve(methodParameter, asList("--foo bar --name baz --qix bux".split(" ")));
assertThat(result, is("baz"));
}
@Test
public void resolvesAnonymousParameterAnnotatedWithCliOption() throws Exception {
MethodParameter methodParameter = new MethodParameter(LegacyCommands.REGISTER_METHOD, NAME_OR_ANONYMOUS);
Object result = parameterResolver.resolve(methodParameter, asList("--foo bar baz --qix bux".split(" ")));
assertThat(result, is("baz"));
// As first param
result = parameterResolver.resolve(methodParameter, asList("baz --foo bar --qix bux".split(" ")));
assertThat(result, is("baz"));
}
@Test
public void usesLegacyConverters() throws Exception {
MethodParameter methodParameter = new MethodParameter(LegacyCommands.REGISTER_METHOD, TYPE);
Object result = parameterResolver.resolve(methodParameter, asList("--foo bar --name baz --qix bux --type processor".split(" ")));
assertThat(result, Matchers.is(ArtifactType.processor));
}
@Test
public void testUnspecifiedDefaultValue() throws Exception {
MethodParameter methodParameter = new MethodParameter(LegacyCommands.REGISTER_METHOD, FORCE);
Object result = parameterResolver.resolve(methodParameter, asList("--foo bar --name baz --qix bux".split(" ")));
assertThat(result, is(false));
}
@Test
public void testSpecifiedDefaultValue() throws Exception {
MethodParameter methodParameter = new MethodParameter(LegacyCommands.REGISTER_METHOD, FORCE);
assertThat(parameterResolver.resolve(methodParameter, asList("--force --foo bar --name baz --qix bux".split(" "))), is(true));
assertThat(parameterResolver.resolve(methodParameter, asList("--foo bar --name baz --qix bux --force".split(" "))), is(true));
}
@Test
public void testParameterNotFound() throws Exception {
MethodParameter methodParameter = new MethodParameter(LegacyCommands.REGISTER_METHOD, COORDINATES);
thrown.expect(IllegalArgumentException.class);
thrown.expectMessage("Could not find parameter values for [--coordinates, --coords] in [--force, --foo, bar, --name, baz, --qix, bux]");
parameterResolver.resolve(methodParameter, asList("--force --foo bar --name baz --qix bux".split(" ")));
}
@Test
public void testParameterFoundTooManyTimes() throws Exception {
MethodParameter methodParameter = new MethodParameter(LegacyCommands.REGISTER_METHOD, COORDINATES);
thrown.expect(IllegalArgumentException.class);
thrown.expectMessage("Option --coordinates has already been set");
// with the exact same name
parameterResolver.resolve(methodParameter, asList("--force --coordinates bar --coordinates baz --qix bux".split(" ")));
// or even with aliases
parameterResolver.resolve(methodParameter, asList("--force --coordinates bar --coords baz --qix bux".split(" ")));
}
@Test
public void testNoConverterFound() throws Exception {
MethodParameter methodParameter = new MethodParameter(LegacyCommands.SUM_METHOD, 0);
thrown.expect(IllegalStateException.class);
thrown.expectMessage("No converter found for --v1 from '1' to type int");
parameterResolver.resolve(methodParameter, asList("--v1 1 --v2 2".split(" ")));
}
@Test
public void testNoConverterFoundForUnspecifiedValue() throws Exception {
MethodParameter methodParameter = new MethodParameter(LegacyCommands.SUM_METHOD, 0);
thrown.expect(IllegalStateException.class);
thrown.expectMessage("No converter found for --v1 from '38' to type int");
parameterResolver.resolve(methodParameter, asList("--v2 2".split(" ")));
}
@Test
public void testNoConverterFoundForSpecifiedValue() throws Exception {
MethodParameter methodParameter = new MethodParameter(LegacyCommands.SUM_METHOD, 1);
thrown.expect(IllegalStateException.class);
thrown.expectMessage("No converter found for --v2 from '42' to type int");
parameterResolver.resolve(methodParameter, asList("--v1 1 --v2".split(" ")));
}
@Configuration
static class Config {
@Bean
public Converter<String> stringConverter() {
return new StringConverter();
}
@Bean
public Converter<Boolean> booleanConverter() {
return new BooleanConverter();
}
@Bean
public Converter<Enum<?>> enumConverter() {
return new EnumConverter();
}
@Bean
public ParameterResolver parameterResolver() {
return new LegacyParameterResolver();
}
}
}