Add automatic command grouping
Fixes #163 Introduce Command and Command.Help
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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.shell.standard;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* Used to indicate the default group of shell commands, either at the package or class level.
|
||||
*
|
||||
* @author Eric Bottard
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.PACKAGE, ElementType.TYPE})
|
||||
@Documented
|
||||
public @interface ShellCommandGroup {
|
||||
|
||||
/**
|
||||
* The default value for the group label, which when set<ul>
|
||||
* <li>on a class, will mean to look at the package level</li>
|
||||
* <li>on a package, to go back at the class level and infer a name from the class name.</li>
|
||||
* </ul>
|
||||
*/
|
||||
String INHERIT_AND_INFER = "";
|
||||
|
||||
/**
|
||||
* @return
|
||||
* An explicit value for the group, which will apply to all commands in the owning class or package, depending
|
||||
* on where this annotation is set.
|
||||
*/
|
||||
String value() default INHERIT_AND_INFER;
|
||||
|
||||
}
|
||||
@@ -33,6 +33,13 @@ import java.lang.annotation.Target;
|
||||
@Documented
|
||||
public @interface ShellMethod {
|
||||
|
||||
/**
|
||||
* The default value for {@link #group()}, meaning that the group will be inherited from the explicit value set
|
||||
* on the containing element (class then package) or ultimately inferred.
|
||||
* @see ShellCommandGroup
|
||||
*/
|
||||
String INHERITED = "";
|
||||
|
||||
/**
|
||||
* The name(s) by which this method can be invoked via Spring Shell. If not specified, the actual method name
|
||||
* will be used (turning camelCase humps into "-").
|
||||
@@ -55,9 +62,10 @@ public @interface ShellMethod {
|
||||
|
||||
/**
|
||||
* The command group which this command belongs to. The command group is used when printing a list of
|
||||
* commands to group related commands.
|
||||
* commands to group related commands. By default, group is first looked up from owning class then package,
|
||||
* and if not explicitly set, is inferred from class name.
|
||||
* @return name of the command group
|
||||
*/
|
||||
String group() default "";
|
||||
String group() default INHERITED;
|
||||
|
||||
}
|
||||
|
||||
@@ -21,14 +21,15 @@ import static org.springframework.util.StringUtils.collectionToDelimitedString;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collector;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.shell.*;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* The standard implementation of {@link MethodTargetRegistrar} for new shell
|
||||
@@ -61,10 +62,10 @@ public class StandardMethodTargetRegistrar implements MethodTargetRegistrar {
|
||||
if (keys.length == 0) {
|
||||
keys = new String[] { Utils.unCamelify(method.getName()) };
|
||||
}
|
||||
String group = shellMapping.group();
|
||||
String group = getOrInferGroup(method);
|
||||
for (String key : keys) {
|
||||
Supplier<Availability> availabilityIndicator = findAvailabilityIndicator(keys, bean, method);
|
||||
MethodTarget target = new MethodTarget(method, bean, shellMapping.value(), group, availabilityIndicator);
|
||||
MethodTarget target = new MethodTarget(method, bean, new Command.Help(shellMapping.value(), group), availabilityIndicator);
|
||||
registry.register(key, target);
|
||||
commands.put(key, target);
|
||||
}
|
||||
@@ -72,6 +73,32 @@ public class StandardMethodTargetRegistrar implements MethodTargetRegistrar {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the group from the following places, in order:<ul>
|
||||
* <li>explicit annotation at the method level</li>
|
||||
* <li>explicit annotation at the class level</li>
|
||||
* <li>explicit annotation at the package level</li>
|
||||
* <li>implicit from the class name</li>
|
||||
* </ul>
|
||||
*/
|
||||
private String getOrInferGroup(Method method) {
|
||||
ShellMethod methodAnn = AnnotationUtils.getAnnotation(method, ShellMethod.class);
|
||||
if (!methodAnn.group().equals(ShellMethod.INHERITED)) {
|
||||
return methodAnn.group();
|
||||
}
|
||||
Class<?> clazz = method.getDeclaringClass();
|
||||
ShellCommandGroup classAnn = AnnotationUtils.getAnnotation(clazz, ShellCommandGroup.class);
|
||||
if (classAnn != null && !classAnn.value().equals(ShellCommandGroup.INHERIT_AND_INFER)) {
|
||||
return classAnn.value();
|
||||
}
|
||||
ShellCommandGroup packageAnn = AnnotationUtils.getAnnotation(clazz.getPackage(), ShellCommandGroup.class);
|
||||
if (packageAnn != null && !packageAnn.value().equals(ShellCommandGroup.INHERIT_AND_INFER)) {
|
||||
return packageAnn.value();
|
||||
}
|
||||
// Shameful copy/paste from https://stackoverflow.com/questions/7593969/regex-to-split-camelcase-or-titlecase-advanced
|
||||
return StringUtils.arrayToDelimitedString(clazz.getSimpleName().split("(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])"), " ");
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to locate an availability indicator (a no-arg method that returns
|
||||
* {@link Availability}) for the given command method. The following are tried in order
|
||||
|
||||
Reference in New Issue
Block a user