Use JCommander resolver in combination with others.
Prepare for implementation of describe()
This commit is contained in:
@@ -17,6 +17,7 @@
|
||||
package org.springframework.shell2;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
|
||||
@@ -39,8 +40,10 @@ public interface ParameterResolver {
|
||||
|
||||
/**
|
||||
* Describe a supported parameter, so that integrated help can be generated.
|
||||
* <p>Typical implementations will return a one element stream result, but some may return several (for
|
||||
* example if binding several words to a POJO).</p>
|
||||
*/
|
||||
ParameterDescription describe(MethodParameter parameter);
|
||||
Stream<ParameterDescription> describe(MethodParameter parameter);
|
||||
|
||||
/**
|
||||
* Invoked during TAB completion. If the {@link CompletionContext} can be interpreted as the start
|
||||
|
||||
@@ -21,6 +21,7 @@ import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.beust.jcommander.DynamicParameter;
|
||||
import com.beust.jcommander.JCommander;
|
||||
@@ -33,6 +34,7 @@ import org.springframework.shell2.CompletionContext;
|
||||
import org.springframework.shell2.CompletionProposal;
|
||||
import org.springframework.shell2.ParameterDescription;
|
||||
import org.springframework.shell2.ParameterResolver;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
@@ -40,6 +42,7 @@ import org.springframework.util.ReflectionUtils;
|
||||
*
|
||||
* @author Eric Bottard
|
||||
*/
|
||||
@Component
|
||||
public class JCommanderParameterResolver implements ParameterResolver {
|
||||
|
||||
private static final Collection<Class<? extends Annotation>> JCOMMANDER_ANNOTATIONS =
|
||||
@@ -80,7 +83,7 @@ public class JCommanderParameterResolver implements ParameterResolver {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ParameterDescription describe(MethodParameter parameter) {
|
||||
public Stream<ParameterDescription> describe(MethodParameter parameter) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,10 @@ import java.util.List;
|
||||
import com.beust.jcommander.Parameter;
|
||||
|
||||
/**
|
||||
* Created by ericbottard on 15/12/15.
|
||||
* A POJO with fields annotated with JCommander annotations.
|
||||
*
|
||||
* @author Eric Bottard
|
||||
* @see MyLordCommands#genesis(FieldCollins)
|
||||
*/
|
||||
public class FieldCollins {
|
||||
|
||||
|
||||
@@ -28,7 +28,10 @@ import org.springframework.shell2.Utils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* Created by ericbottard on 15/12/15.
|
||||
* Unit test for {@link JCommanderParameterResolver}.
|
||||
*
|
||||
* @author Eric Bottard
|
||||
* @author Florent Biville
|
||||
*/
|
||||
public class JCommanderParameterResolverTest {
|
||||
|
||||
|
||||
@@ -17,14 +17,22 @@
|
||||
package org.springframework.shell2.jcommander;
|
||||
|
||||
/**
|
||||
* Created by ericbottard on 15/12/15.
|
||||
* An hypothetical command class, with one method using JCommander args, and the other not.
|
||||
*
|
||||
* @author Eric Bottard
|
||||
*/
|
||||
public class MyLordCommands {
|
||||
|
||||
/**
|
||||
* This method should be supported.
|
||||
*/
|
||||
public void genesis(FieldCollins fieldCollins) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is not.
|
||||
*/
|
||||
public void apocalypse(String param) {
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
*
|
||||
* 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 org.springframework.shell2.samples.jcommander;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
|
||||
/**
|
||||
* An example straight from the JCommander documentation.
|
||||
*
|
||||
* @author Eric Bottard
|
||||
* @author Cédric Beust
|
||||
*/
|
||||
public class Args {
|
||||
@Parameter
|
||||
private List<String> parameters = new ArrayList<>();
|
||||
|
||||
@Parameter(names = { "-log", "-verbose" }, description = "Level of verbosity")
|
||||
private Integer verbose = 1;
|
||||
|
||||
@Parameter(names = "-groups", description = "Comma-separated list of group names to be run")
|
||||
private String groups;
|
||||
|
||||
@Parameter(names = "-debug", description = "Debug mode")
|
||||
private boolean debug = false;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Args{" +
|
||||
"parameters=" + parameters +
|
||||
", verbose=" + verbose +
|
||||
", groups='" + groups + '\'' +
|
||||
", debug=" + debug +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
*
|
||||
* 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 org.springframework.shell2.samples.jcommander;
|
||||
|
||||
import org.springframework.shell2.standard.ShellComponent;
|
||||
import org.springframework.shell2.standard.ShellMethod;
|
||||
import org.springframework.shell2.standard.ShellOption;
|
||||
|
||||
/**
|
||||
* A class with JCommander commands.
|
||||
*
|
||||
* @author Eric Bottard
|
||||
*/
|
||||
@ShellComponent
|
||||
public class JCommanderCommands {
|
||||
|
||||
@ShellMethod(help = "bind parameters to JCommander POJO")
|
||||
public String jcommander(@ShellOption(optOut = true) Args args) {
|
||||
return "You said " + args;
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,7 @@ import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.MethodParameter;
|
||||
@@ -90,7 +91,7 @@ public class LegacyParameterResolver implements ParameterResolver {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ParameterDescription describe(MethodParameter parameter) {
|
||||
public Stream<ParameterDescription> describe(MethodParameter parameter) {
|
||||
Parameter jlrParameter = parameter.getMethod().getParameters()[parameter.getParameterIndex()];
|
||||
CliOption option = jlrParameter.getAnnotation(CliOption.class);
|
||||
ParameterDescription result = ParameterDescription.outOf(parameter);
|
||||
@@ -108,7 +109,7 @@ public class LegacyParameterResolver implements ParameterResolver {
|
||||
}
|
||||
boolean containsEmptyKey = keys.contains("");
|
||||
result.mandatoryKey(!containsEmptyKey);
|
||||
return result;
|
||||
return Stream.of(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -160,7 +160,7 @@ public class LegacyParameterResolverTest {
|
||||
public void testDescribeBothDefaultsNotDeclared() {
|
||||
MethodParameter methodParameter = Utils.createMethodParameter(LegacyCommands.REGISTER_METHOD, 1);
|
||||
|
||||
ParameterDescription description = parameterResolver.describe(methodParameter);
|
||||
ParameterDescription description = parameterResolver.describe(methodParameter).findFirst().get();
|
||||
|
||||
assertThat(description.keys()).containsExactly("--type");
|
||||
assertThat(description.formal()).isEqualTo(Utils.unCamelify(ArtifactType.class.getSimpleName()));
|
||||
@@ -175,7 +175,7 @@ public class LegacyParameterResolverTest {
|
||||
public void testDescribeBothDefaultsDeclared() {
|
||||
MethodParameter methodParameter = Utils.createMethodParameter(LegacyCommands.SOME_METHOD, 1);
|
||||
|
||||
ParameterDescription description = parameterResolver.describe(methodParameter);
|
||||
ParameterDescription description = parameterResolver.describe(methodParameter).findFirst().get();
|
||||
|
||||
assertThat(description.keys()).containsExactly("--option");
|
||||
assertThat(description.formal()).isEqualTo(boolean.class.getName());
|
||||
@@ -191,7 +191,7 @@ public class LegacyParameterResolverTest {
|
||||
public void testDescribeOnlySpecifiedDefaultDeclared() {
|
||||
MethodParameter methodParameter = Utils.createMethodParameter(LegacyCommands.SUM_METHOD, 1);
|
||||
|
||||
ParameterDescription description = parameterResolver.describe(methodParameter);
|
||||
ParameterDescription description = parameterResolver.describe(methodParameter).findFirst().get();
|
||||
|
||||
assertThat(description.keys()).containsExactly("--v2");
|
||||
assertThat(description.formal()).isEqualTo(int.class.getName());
|
||||
@@ -207,7 +207,7 @@ public class LegacyParameterResolverTest {
|
||||
public void testDescribeOnlyUnspecifiedDefaultDeclared() {
|
||||
MethodParameter methodParameter = Utils.createMethodParameter(LegacyCommands.SUM_METHOD, 0);
|
||||
|
||||
ParameterDescription description = parameterResolver.describe(methodParameter);
|
||||
ParameterDescription description = parameterResolver.describe(methodParameter).findFirst().get();
|
||||
|
||||
assertThat(description.keys()).containsExactly("--v1");
|
||||
assertThat(description.formal()).isEqualTo(int.class.getName());
|
||||
@@ -222,7 +222,7 @@ public class LegacyParameterResolverTest {
|
||||
public void testDescribeDefaultKey() {
|
||||
MethodParameter methodParameter = Utils.createMethodParameter(LegacyCommands.LEGACY_ECHO_METHOD, 0);
|
||||
|
||||
ParameterDescription description = parameterResolver.describe(methodParameter);
|
||||
ParameterDescription description = parameterResolver.describe(methodParameter).findFirst().get();
|
||||
|
||||
assertThat(description.keys()).isEmpty();
|
||||
assertThat(description.formal()).isEqualTo(Utils.unCamelify(String.class.getSimpleName()));
|
||||
@@ -237,7 +237,7 @@ public class LegacyParameterResolverTest {
|
||||
public void testDescribeNonMandatoryNoDefaults() {
|
||||
MethodParameter methodParameter = Utils.createMethodParameter(LegacyCommands.SOME_METHOD, 0);
|
||||
|
||||
ParameterDescription description = parameterResolver.describe(methodParameter);
|
||||
ParameterDescription description = parameterResolver.describe(methodParameter).findFirst().get();
|
||||
|
||||
assertThat(description.keys()).containsExactly("--key");
|
||||
assertThat(description.formal()).isEqualTo(Utils.unCamelify(String.class.getSimpleName()));
|
||||
|
||||
@@ -244,7 +244,7 @@ public class Help {
|
||||
for (ParameterResolver parameterResolver : parameterResolvers) {
|
||||
MethodParameter methodParameter = Utils.createMethodParameter(methodTarget.getMethod(), i);
|
||||
if (parameterResolver.supports(methodParameter)) {
|
||||
parameterDescriptions.add(parameterResolver.describe(methodParameter));
|
||||
parameterDescriptions.add(parameterResolver.describe(methodParameter).findFirst().get());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,6 +66,14 @@ public @interface ShellOption {
|
||||
|
||||
Class<? extends ValueProvider> valueProvider() default NoValueProvider.class;
|
||||
|
||||
/**
|
||||
* Used to indicate to the framework that the given parameter should NOT be resolved by
|
||||
* {@link StandardParameterResolver}. This is useful if several implementations of
|
||||
* {@link org.springframework.shell2.ParameterResolver} are present, given that the standard one can work with no
|
||||
* annotation at all.
|
||||
*/
|
||||
boolean optOut() default false;
|
||||
|
||||
interface NoValueProvider extends ValueProvider {
|
||||
|
||||
}
|
||||
|
||||
@@ -100,7 +100,8 @@ public class StandardParameterResolver implements ParameterResolver {
|
||||
|
||||
@Override
|
||||
public boolean supports(MethodParameter parameter) {
|
||||
return parameter.getMethodAnnotation(ShellMethod.class) != null;
|
||||
boolean optOut = parameter.hasParameterAnnotation(ShellOption.class) && parameter.getParameterAnnotation(ShellOption.class).optOut();
|
||||
return !optOut && parameter.getMethodAnnotation(ShellMethod.class) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -126,7 +127,7 @@ public class StandardParameterResolver implements ParameterResolver {
|
||||
|
||||
if (i + 1 + arity > words.size()) {
|
||||
String input = words.subList(i, words.size()).stream().collect(Collectors.joining(" "));
|
||||
throw new UnfinishedParameterResolutionException(describe(Utils.createMethodParameter(parameter)), input);
|
||||
throw new UnfinishedParameterResolutionException(describe(Utils.createMethodParameter(parameter)).findFirst().get(), input);
|
||||
}
|
||||
Assert.isTrue(i + 1 + arity <= words.size(), String.format("Not enough input for parameter '%s'", word));
|
||||
String raw = words.subList(i + 1, i + 1 + arity).stream().collect(Collectors.joining(","));
|
||||
@@ -178,7 +179,7 @@ public class StandardParameterResolver implements ParameterResolver {
|
||||
|
||||
Parameter param = methodParameter.getMethod().getParameters()[methodParameter.getParameterIndex()];
|
||||
if (!resolved.containsKey(param)) {
|
||||
throw new ParameterMissingResolutionException(describe(methodParameter));
|
||||
throw new ParameterMissingResolutionException(describe(methodParameter).findFirst().get());
|
||||
}
|
||||
ParameterRawValue parameterRawValue = resolved.get(param);
|
||||
return convertRawValue(parameterRawValue, methodParameter);
|
||||
@@ -233,7 +234,7 @@ public class StandardParameterResolver implements ParameterResolver {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ParameterDescription describe(MethodParameter parameter) {
|
||||
public Stream<ParameterDescription> describe(MethodParameter parameter) {
|
||||
Parameter jlrParameter = parameter.getMethod().getParameters()[parameter.getParameterIndex()];
|
||||
int arity = getArity(jlrParameter);
|
||||
Class<?> type = parameter.getParameterType();
|
||||
@@ -259,7 +260,7 @@ public class StandardParameterResolver implements ParameterResolver {
|
||||
.collect(Collectors.toList()))
|
||||
.mandatoryKey(false);
|
||||
|
||||
return result;
|
||||
return Stream.of(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -332,7 +333,7 @@ public class StandardParameterResolver implements ParameterResolver {
|
||||
|
||||
private List<CompletionProposal> argumentKeysThatStartWithContextPrefix(MethodParameter methodParameter, CompletionContext context) {
|
||||
String prefix = context.currentWordUpToCursor() != null ? context.currentWordUpToCursor() : "";
|
||||
return describe(methodParameter).keys().stream()
|
||||
return describe(methodParameter).flatMap(pd -> pd.keys().stream())
|
||||
.filter(k -> k.startsWith(prefix))
|
||||
.map(CompletionProposal::new)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
Reference in New Issue
Block a user