diff --git a/pom.xml b/pom.xml index 9d56f8c7..35f683f3 100644 --- a/pom.xml +++ b/pom.xml @@ -39,6 +39,12 @@ spring-shell 1.2.0.BUILD-SNAPSHOT + + com.beust + jcommander + 1.48 + true + diff --git a/src/main/java/org/springframework/shell2/jcommander/JCommanderParameterResolver.java b/src/main/java/org/springframework/shell2/jcommander/JCommanderParameterResolver.java new file mode 100644 index 00000000..a6105ed6 --- /dev/null +++ b/src/main/java/org/springframework/shell2/jcommander/JCommanderParameterResolver.java @@ -0,0 +1,63 @@ +package org.springframework.shell2.jcommander; + +import java.lang.annotation.Annotation; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; + +import com.beust.jcommander.DynamicParameter; +import com.beust.jcommander.JCommander; +import com.beust.jcommander.Parameter; +import com.beust.jcommander.ParametersDelegate; + +import org.springframework.beans.BeanUtils; +import org.springframework.core.MethodParameter; +import org.springframework.shell2.ParameterResolver; +import org.springframework.util.ReflectionUtils; + +/** + * Created by ericbottard on 15/12/15. + */ +public class JCommanderParameterResolver implements ParameterResolver { + + private static final Collection> JCOMMANDER_ANNOTATIONS = + Arrays.asList(Parameter.class, DynamicParameter.class, ParametersDelegate.class); + + @Override + public boolean supports(MethodParameter parameter) { + AtomicBoolean isSupported = new AtomicBoolean(false); + + Class parameterType = parameter.getParameterType(); + ReflectionUtils.doWithFields(parameterType, field -> { + ReflectionUtils.makeAccessible(field); + boolean hasAnnotation = Arrays.asList(field.getAnnotations()) + .stream() + .map(Annotation::annotationType) + .anyMatch(JCOMMANDER_ANNOTATIONS::contains); + isSupported.compareAndSet(false, hasAnnotation); + + }); + + ReflectionUtils.doWithMethods(parameterType, method -> { + ReflectionUtils.makeAccessible(method); + boolean hasAnnotation = Arrays.asList(method.getAnnotations()) + .stream() + .map(Annotation::annotationType) + .anyMatch(Parameter.class::equals); + isSupported.compareAndSet(false, hasAnnotation); + }); + return isSupported.get(); + } + + @Override + public Object resolve(MethodParameter methodParameter, List words) { + Object pojo = BeanUtils.instantiateClass(methodParameter.getParameterType()); + JCommander jCommander = new JCommander(); + jCommander.addObject(pojo); + jCommander.setAcceptUnknownOptions(true); + jCommander.parse(words.toArray(new String[words.size()])); + return pojo; + } + +} diff --git a/src/test/java/org/springframework/shell2/jcommander/FieldCollins.java b/src/test/java/org/springframework/shell2/jcommander/FieldCollins.java new file mode 100644 index 00000000..541a9f17 --- /dev/null +++ b/src/test/java/org/springframework/shell2/jcommander/FieldCollins.java @@ -0,0 +1,45 @@ +package org.springframework.shell2.jcommander; + +import java.util.ArrayList; +import java.util.List; + +import com.beust.jcommander.Parameter; + +/** + * Created by ericbottard on 15/12/15. + */ +public class FieldCollins { + + @Parameter(names = "--name") + private String name; + + @Parameter(names = "-level") + private int level; + + @Parameter(description = "rest") + private List rest = new ArrayList<>(); + + public List getRest() { + return rest; + } + + public void setRest(List rest) { + this.rest = rest; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getLevel() { + return level; + } + + public void setLevel(int level) { + this.level = level; + } +} diff --git a/src/test/java/org/springframework/shell2/jcommander/JCommanderParameterResolverTest.java b/src/test/java/org/springframework/shell2/jcommander/JCommanderParameterResolverTest.java new file mode 100644 index 00000000..3594cc52 --- /dev/null +++ b/src/test/java/org/springframework/shell2/jcommander/JCommanderParameterResolverTest.java @@ -0,0 +1,45 @@ +package org.springframework.shell2.jcommander; + +import static java.util.Arrays.asList; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; + +import java.lang.reflect.Method; + +import org.junit.Test; + +import org.springframework.core.MethodParameter; +import org.springframework.util.ReflectionUtils; + +/** + * Created by ericbottard on 15/12/15. + */ +public class JCommanderParameterResolverTest { + + public static final Method COMMAND_METHOD = ReflectionUtils.findMethod(MyLordCommands.class, "genesis", FieldCollins.class); + + private JCommanderParameterResolver resolver = new JCommanderParameterResolver(); + + @Test + public void testSupportsJCommanderPojos() throws Exception { + + assertThat(resolver.supports(new MethodParameter(COMMAND_METHOD, 0)), is(true)); + } + + @Test + public void testDoesNotSupportsNonJCommanderPojos() throws Exception { + Method method = ReflectionUtils.findMethod(MyLordCommands.class, "apocalypse", String.class); + + assertThat(resolver.supports(new MethodParameter(method, 0)), is(false)); + } + + @Test + public void testPojoValuesAreCorrectlySet() { + MethodParameter methodParameter = new MethodParameter(COMMAND_METHOD, 0); + FieldCollins resolved = (FieldCollins) resolver.resolve(methodParameter, asList("--name foo -level 2 something-else yet-something-else".split(" "))); + assertThat(resolved.getName(), is("foo")); + assertThat(resolved.getLevel(), is(2)); + assertThat(resolved.getRest(), contains("something-else", "yet-something-else")); + } +} diff --git a/src/test/java/org/springframework/shell2/jcommander/MyLordCommands.java b/src/test/java/org/springframework/shell2/jcommander/MyLordCommands.java new file mode 100644 index 00000000..2158c8bb --- /dev/null +++ b/src/test/java/org/springframework/shell2/jcommander/MyLordCommands.java @@ -0,0 +1,16 @@ +package org.springframework.shell2.jcommander; + +/** + * Created by ericbottard on 15/12/15. + */ +public class MyLordCommands { + + public void genesis(FieldCollins fieldCollins) { + + } + + public void apocalypse(String param) { + + } + +}