Skip to content

Commit

Permalink
New annotation model
Browse files Browse the repository at this point in the history
- This is a first commit to add new annotation model
  which eventually will replace old legacy annotations
  like ShellComponent, ShellMethod, @ShellOption, etc.
- Adds subset of features needed for parity with manual
  use of CommandRegistration.
- Relates spring-projects#637
- Relates spring-projects#638
- Relates spring-projects#639
- Relates spring-projects#640
- Relates spring-projects#641
  • Loading branch information
jvalkeal committed Jan 27, 2023
1 parent d1c482c commit de1a3ba
Show file tree
Hide file tree
Showing 22 changed files with 1,781 additions and 15 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
/*
* Copyright 2023 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
*
* https://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.command.annotation;

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.shell.context.InteractionMode;

/**
* Annotation marking a method to be a candicate for a shell command target.
*
* @author Janne Valkealahti
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Documented
public @interface Command {

/**
* Define command as an array. Given that command should be
* {@code command1 sub1} it can be defined as:
*
* <pre class="code">
* command = { "command1", "sub1" }
* command = "command1 sub1"
* </pre>
*
* Values are split and trimmed meaning spaces doesn't matter.
*
* <p>
* <b>Supported at the type level as well as at the method level!</b>
* When used at the type level, all method-level mappings inherit this primary
* command to use it as a prefix.
*
* <pre class="code">
* &#64;Command(command = "command1")
* class MyCommands {
*
* &#64;Command(command = "sub1")
* void sub1(){}
* }
* </pre>
*
* @return the command as an array
*/
String[] command() default {};

/**
* Define alias as an array. Given that alias should be
* {@code alias1 sub1} it can be defined as:
*
* <pre class="code">
* command = { "alias1", "sub1" }
* command = "alias1 sub1"
* </pre>
*
* Values are split and trimmed meaning spaces doesn't matter.
*
* <p>
* <b>Supported at the type level as well as at the method level!</b>
* When used at the type level, all method-level mappings inherit this primary
* alias to use it as a prefix.
*
* <pre class="code">
* &#64;Command(alias = "alias1")
* class MyCommands {
*
* &#64;Command(alias = "sub1")
* void sub1(){}
* }
* </pre>
*
* @return the aliases as an array
*/
String[] alias() default {};

/**
* Define a command group.
*
* <p>
* <b>Supported at the type level as well as at the method level!</b>
* When used at the type level, all method-level group inherit this primary
* group. Can be overridden on method-level.
*
* @return the command group
*/
String group() default "";

/**
* Define a command description.
*
* <p>
* <b>Supported at the type level as well as at the method level!</b>
* When used at the type level, all method-level descriptions inherit this primary
* field. Can be overridden on method-level.
*
* @return the command description
*/
String description() default "";

/**
* Define command to be hidden.
*
* <p>
* <b>Supported at the type level as well as at the method level!</b>
* When used at the type level, all method-level mappings inherit this primary
* hidden field.
*
* <pre class="code">
* &#64;Command(hidden = true)
* class MyCommands {
*
* &#64;Command
* void sub1(){
* // sub1 command is hidden
* }
* }
* </pre>
*
* @return true if command should be hidden
*/
boolean hidden() default false;

/**
* Define interaction mode for a command as a hint when command should be
* available. For example presense of some commands doesn't make sense if shell
* is running as non-interactive mode and vice versa.
*
* <p>
* <b>Supported at the type level as well as at the method level!</b>
* When used at the type level, all method-level mappings inherit this primary
* field.
*
* Type is an array to be able to indicate that default don't have anyting defined.
*
* @return interaction modes
*/
InteractionMode[] interactionMode() default {};

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* Copyright 2023 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
*
* https://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.command.annotation;

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.context.annotation.Import;
import org.springframework.core.annotation.AliasFor;
import org.springframework.shell.command.annotation.support.CommandScanRegistrar;

/**
* Configures the base packages used when scanning for {@link Command @Comamnd}
* classes. One of {@link #basePackageClasses()}, {@link #basePackages()} or its
* alias {@link #value()} may be specified to define specific packages to scan.
* If specific packages are not defined scanning will occur from the package of
* the class with this annotation.
*
* @author Janne Valkealahti
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(CommandScanRegistrar.class)
@EnableCommand
public @interface CommandScan {

/**
* Alias for the {@link #basePackages()} attribute. Allows for more concise
* annotation declarations e.g.: {@code @CommandScan("org.my.pkg")} instead of
* {@code @CommandScan(basePackages="org.my.pkg")}.
*
* @return the base packages to scan
*/
@AliasFor("basePackages")
String[] value() default {};

/**
* Base packages to scan for commands. {@link #value()} is an alias for (and
* mutually exclusive with) this attribute.
* <p>
* Use {@link #basePackageClasses()} for a type-safe alternative to String-based
* package names.
*
* @return the base packages to scan
*/
@AliasFor("value")
String[] basePackages() default {};

/**
* Type-safe alternative to {@link #basePackages()} for specifying the packages
* to scan for commands. The package of each class specified will be scanned.
* <p>
* Consider creating a special no-op marker class or interface in each package
* that serves no purpose other than being referenced by this attribute.
*
* @return classes from the base packages to scan
*/
Class<?>[] basePackageClasses() default {};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright 2023 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
*
* https://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.command.annotation;

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.context.annotation.Import;
import org.springframework.shell.command.annotation.support.EnableCommandRegistrar;

/**
* Enable support for {@link Command @Command} annotated classes.
* {@code @Command} classes can be registered directly on this annotation.
*
* @author Janne Valkealahti
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(EnableCommandRegistrar.class)
public @interface EnableCommand {

/**
* Defines candicate classes for shell commands.
*
* @return candidate classes for shell commands
*/
Class<?>[] value() default {};
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.springframework.aot.hint.annotation.Reflective;

/**
* Annotation for handling exceptions in specific command classes and/or its methods.
*
Expand All @@ -31,7 +29,6 @@
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
@Reflective
public @interface ExceptionResolver {

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* Copyright 2023 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
*
* https://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.command.annotation;

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.shell.command.CommandRegistration.OptionArity;

/**
* Annotation marking a method parameter to be a candicate for an option.
*
* @author Janne Valkealahti
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
@Documented
public @interface Option {

/**
* Long names of an option. There can be multiple names where first is primary
* one and other are aliases.
*
* @return Option long names, defaults to empty.
*/
String[] longNames() default {};

/**
* Short names of an option. There can be multiple names where first is primary
* one and other are aliases.
*
* @return Option short names, defaults to empty.
*/
char[] shortNames() default {};

/**
* Mark option required.
*
* @return true if option is required, defaults to false.
*/
boolean required() default false;

/**
* Define option default value.
*
* @return default value
*/
String defaultValue() default "";

/**
* Return a short description of the option.
*
* @return description of the option
*/
String description() default "";

/**
* Define option arity.
*
* @return option arity
*/
OptionArity arity() default OptionArity.NONE;
}
Loading

0 comments on commit de1a3ba

Please sign in to comment.