From a277c53d8d508bf1b52bcf41c3bc9218f1bd27d4 Mon Sep 17 00:00:00 2001 From: Eric Bottard Date: Thu, 22 Jun 2017 18:57:06 +0200 Subject: [PATCH] Implement describe() for JCommander --- pom.xml | 2 +- .../shell2/ParameterDescription.java | 28 +++++++++++++++-- .../org/springframework/shell2/Utils.java | 7 +++++ .../JCommanderParameterResolver.java | 31 +++++++++++++++---- .../shell2/jcommander/FieldCollins.java | 4 +-- .../JCommanderParameterResolverTest.java | 24 ++++++++++++++ .../shell2/standard/commands/Help.java | 16 +++------- 7 files changed, 88 insertions(+), 24 deletions(-) diff --git a/pom.xml b/pom.xml index 0c9ffe36..e3dd6f02 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ 3.3.0 - 3.2.0 + 3.8.0 1.8 diff --git a/spring-shell2-core/src/main/java/org/springframework/shell2/ParameterDescription.java b/spring-shell2-core/src/main/java/org/springframework/shell2/ParameterDescription.java index 60851d5b..bca8b5a2 100644 --- a/spring-shell2-core/src/main/java/org/springframework/shell2/ParameterDescription.java +++ b/spring-shell2-core/src/main/java/org/springframework/shell2/ParameterDescription.java @@ -16,7 +16,9 @@ package org.springframework.shell2; +import java.util.Collections; import java.util.List; +import java.util.Objects; import java.util.Optional; import org.springframework.core.MethodParameter; @@ -61,7 +63,7 @@ public class ParameterDescription { /** * The list of 'keys' that can be used to specify this parameter, if any. */ - private List keys; + private List keys = Collections.emptyList(); /** * Depending on the {@link ParameterResolver}, whether keys are mandatory to identify this parameter. @@ -102,7 +104,7 @@ public class ParameterDescription { } public ParameterDescription defaultValue(String defaultValue) { - this.defaultValue = Optional.of(defaultValue); + this.defaultValue = Optional.ofNullable(defaultValue); return this; } @@ -145,10 +147,30 @@ public class ParameterDescription { @Override public String toString() { - return String.format("%s %s", keys().iterator().next(), formal()); + return String.format("%s %s", keys.isEmpty() ? "" : keys().iterator().next(), formal()); } public MethodParameter parameter() { return parameter; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ParameterDescription that = (ParameterDescription) o; + return mandatoryKey == that.mandatoryKey && + Objects.equals(parameter, that.parameter) && + Objects.equals(type, that.type) && + Objects.equals(formal, that.formal) && + Objects.equals(defaultValue, that.defaultValue) && + Objects.equals(defaultValueWhenFlag, that.defaultValueWhenFlag) && + Objects.equals(keys, that.keys) && + Objects.equals(help, that.help); + } + + @Override + public int hashCode() { + return Objects.hash(parameter, type, formal, defaultValue, defaultValueWhenFlag, keys, mandatoryKey, help); + } } diff --git a/spring-shell2-core/src/main/java/org/springframework/shell2/Utils.java b/spring-shell2-core/src/main/java/org/springframework/shell2/Utils.java index 3f91319c..a60fb461 100644 --- a/spring-shell2-core/src/main/java/org/springframework/shell2/Utils.java +++ b/spring-shell2-core/src/main/java/org/springframework/shell2/Utils.java @@ -20,6 +20,8 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Executable; import java.lang.reflect.Method; import java.lang.reflect.Parameter; +import java.util.stream.IntStream; +import java.util.stream.Stream; import org.springframework.core.DefaultParameterNameDiscoverer; import org.springframework.core.MethodParameter; @@ -76,4 +78,9 @@ public class Utils { return methodParameter; } + public static Stream createMethodParameters(Executable executable) { + return IntStream.range(0, executable.getParameterCount()) + .mapToObj(i -> createMethodParameter(executable, i)); + } + } diff --git a/spring-shell2-jcommander-adapter/src/main/java/org/springframework/shell2/jcommander/JCommanderParameterResolver.java b/spring-shell2-jcommander-adapter/src/main/java/org/springframework/shell2/jcommander/JCommanderParameterResolver.java index fe5cb4ec..526841cc 100644 --- a/spring-shell2-jcommander-adapter/src/main/java/org/springframework/shell2/jcommander/JCommanderParameterResolver.java +++ b/spring-shell2-jcommander-adapter/src/main/java/org/springframework/shell2/jcommander/JCommanderParameterResolver.java @@ -16,6 +16,8 @@ package org.springframework.shell2.jcommander; +import static org.springframework.shell2.Utils.unCamelify; + import java.lang.annotation.Annotation; import java.util.Arrays; import java.util.Collection; @@ -34,6 +36,7 @@ import org.springframework.shell2.CompletionContext; import org.springframework.shell2.CompletionProposal; import org.springframework.shell2.ParameterDescription; import org.springframework.shell2.ParameterResolver; +import org.springframework.shell2.Utils; import org.springframework.stereotype.Component; import org.springframework.util.ReflectionUtils; @@ -74,17 +77,33 @@ public class JCommanderParameterResolver implements ParameterResolver { @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 jCommander = createJCommander(methodParameter); jCommander.parse(words.toArray(new String[words.size()])); - return pojo; + return jCommander.getObjects().get(0); + } + + private JCommander createJCommander(MethodParameter methodParameter) { + Object pojo = BeanUtils.instantiateClass(methodParameter.getParameterType()); + JCommander jCommander = new JCommander(pojo); + jCommander.setAcceptUnknownOptions(true); + return jCommander; } @Override public Stream describe(MethodParameter parameter) { - throw new UnsupportedOperationException(); + JCommander jCommander = createJCommander(parameter); + com.beust.jcommander.ParameterDescription mainParameter = jCommander.getMainParameter(); + return Stream.concat( + jCommander.getParameters().stream(), + mainParameter != null ? Stream.of(mainParameter) : Stream.empty() + ) + .map(j -> new ParameterDescription(parameter, unCamelify(j.getParameterized().getType().getSimpleName())) + .keys(Arrays.asList(j.getParameter().names())) + .help(j.getDescription()) + .mandatoryKey(!j.equals(mainParameter)) + // Not ideal as this does not take reverse-conversion into account, but just toString() + .defaultValue(j.getDefault() == null ? "" : String.valueOf(j.getDefault())) + ); } @Override diff --git a/spring-shell2-jcommander-adapter/src/test/java/org/springframework/shell2/jcommander/FieldCollins.java b/spring-shell2-jcommander-adapter/src/test/java/org/springframework/shell2/jcommander/FieldCollins.java index a8ef3dae..cd379227 100644 --- a/spring-shell2-jcommander-adapter/src/test/java/org/springframework/shell2/jcommander/FieldCollins.java +++ b/spring-shell2-jcommander-adapter/src/test/java/org/springframework/shell2/jcommander/FieldCollins.java @@ -29,11 +29,11 @@ import com.beust.jcommander.Parameter; */ public class FieldCollins { - @Parameter(names = "--name") + @Parameter(names = {"--name", "-n"}, description = "what's in a name?") private String name; @Parameter(names = "-level") - private int level; + private int level = 3; @Parameter(description = "rest") private List rest = new ArrayList<>(); diff --git a/spring-shell2-jcommander-adapter/src/test/java/org/springframework/shell2/jcommander/JCommanderParameterResolverTest.java b/spring-shell2-jcommander-adapter/src/test/java/org/springframework/shell2/jcommander/JCommanderParameterResolverTest.java index 0428ffa1..fa33ebc6 100644 --- a/spring-shell2-jcommander-adapter/src/test/java/org/springframework/shell2/jcommander/JCommanderParameterResolverTest.java +++ b/spring-shell2-jcommander-adapter/src/test/java/org/springframework/shell2/jcommander/JCommanderParameterResolverTest.java @@ -17,13 +17,18 @@ package org.springframework.shell2.jcommander; import static java.util.Arrays.asList; +import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Collections; +import java.util.stream.Stream; import org.junit.Test; import org.springframework.core.MethodParameter; +import org.springframework.shell2.ParameterDescription; import org.springframework.shell2.Utils; import org.springframework.util.ReflectionUtils; @@ -61,4 +66,23 @@ public class JCommanderParameterResolverTest { assertThat(resolved.getLevel()).isEqualTo(2); assertThat(resolved.getRest()).containsOnlyOnce("something-else", "yet-something-else"); } + + @Test + public void testDescribe() { + MethodParameter methodParameter = Utils.createMethodParameter(COMMAND_METHOD, 0); + + Stream desciptions = resolver.describe(methodParameter); + ParameterDescription name = new ParameterDescription(methodParameter, "string") + .keys(Arrays.asList("--name", "-n")) + .help("what's in a name?") + .defaultValue(""); + ParameterDescription level = new ParameterDescription(methodParameter, "int") + .keys(singletonList("-level")) + .defaultValue("3"); + ParameterDescription rest = new ParameterDescription(methodParameter, "list") + .defaultValue("[]") + .mandatoryKey(false) + .help("rest"); + assertThat(desciptions).contains(name, level, rest); + } } diff --git a/spring-shell2-standard-commands/src/main/java/org/springframework/shell2/standard/commands/Help.java b/spring-shell2-standard-commands/src/main/java/org/springframework/shell2/standard/commands/Help.java index 48bdc156..78697014 100644 --- a/spring-shell2-standard-commands/src/main/java/org/springframework/shell2/standard/commands/Help.java +++ b/spring-shell2-standard-commands/src/main/java/org/springframework/shell2/standard/commands/Help.java @@ -238,18 +238,10 @@ public class Help { } private List getParameterDescriptions(MethodTarget methodTarget) { - Parameter[] parameters = methodTarget.getMethod().getParameters(); - List parameterDescriptions = new ArrayList<>(); - for (int i = 0, parametersLength = parameters.length; i < parametersLength; i++) { - for (ParameterResolver parameterResolver : parameterResolvers) { - MethodParameter methodParameter = Utils.createMethodParameter(methodTarget.getMethod(), i); - if (parameterResolver.supports(methodParameter)) { - parameterDescriptions.add(parameterResolver.describe(methodParameter).findFirst().get()); - break; - } - } - } - return parameterDescriptions; + return Utils.createMethodParameters(methodTarget.getMethod()) + .flatMap(mp -> parameterResolvers.stream().filter(pr -> pr.supports(mp)).limit(1L).flatMap(pr -> pr.describe(mp))) + .collect(Collectors.toList()); + } }