diff --git a/spring-boot-project/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/init/InitCommand.java b/spring-boot-project/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/init/InitCommand.java index 0c62b487f42a..b604d450d2cf 100644 --- a/spring-boot-project/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/init/InitCommand.java +++ b/spring-boot-project/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/init/InitCommand.java @@ -20,7 +20,10 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import joptsimple.OptionSet; import joptsimple.OptionSpec; @@ -38,6 +41,7 @@ * * @author Stephane Nicoll * @author Eddú Meléndez + * @author Vignesh Thangavel Ilangovan * @since 1.2.0 */ public class InitCommand extends OptionParsingCommand { @@ -71,6 +75,21 @@ public Collection getExamples() { */ static class InitOptionHandler extends OptionHandler { + /** + * Mapping from camelCase options advertised by the service to our kebab-case + * options. + */ + private static final Map CAMEL_CASE_OPTIONS; + static { + Map options = new HashMap<>(); + options.put("--groupId", "--group-id"); + options.put("--artifactId", "--artifact-id"); + options.put("--packageName", "--package-name"); + options.put("--javaVersion", "--java-version"); + options.put("--bootVersion", "--boot-version"); + CAMEL_CASE_OPTIONS = Collections.unmodifiableMap(options); + } + private final ServiceCapabilitiesReportGenerator serviceCapabilitiesReport; private final ProjectGenerator projectGenerator; @@ -112,9 +131,9 @@ static class InitOptionHandler extends OptionHandler { private OptionSpec force; InitOptionHandler(InitializrService initializrService) { + super(InitOptionHandler::processArgument); this.serviceCapabilitiesReport = new ServiceCapabilitiesReportGenerator(initializrService); this.projectGenerator = new ProjectGenerator(initializrService); - } @Override @@ -129,15 +148,15 @@ protected void options() { } private void projectGenerationOptions() { - this.groupId = option(Arrays.asList("groupId", "g"), "Project coordinates (for example 'org.test')") + this.groupId = option(Arrays.asList("group-id", "g"), "Project coordinates (for example 'org.test')") .withRequiredArg(); - this.artifactId = option(Arrays.asList("artifactId", "a"), + this.artifactId = option(Arrays.asList("artifact-id", "a"), "Project coordinates; infer archive name (for example 'test')").withRequiredArg(); this.version = option(Arrays.asList("version", "v"), "Project version (for example '0.0.1-SNAPSHOT')") .withRequiredArg(); this.name = option(Arrays.asList("name", "n"), "Project name; infer application name").withRequiredArg(); this.description = option("description", "Project description").withRequiredArg(); - this.packageName = option("package-name", "Package name").withRequiredArg(); + this.packageName = option(Arrays.asList("package-name"), "Package name").withRequiredArg(); this.type = option(Arrays.asList("type", "t"), "Project type. Not normally needed if you use --build " + "and/or --format. Check the capabilities of the service (--list) for more details") @@ -249,6 +268,16 @@ protected ProjectGenerationRequest createProjectGenerationRequest(OptionSet opti return request; } + private static String processArgument(String argument) { + for (Map.Entry entry : CAMEL_CASE_OPTIONS.entrySet()) { + String name = entry.getKey(); + if (argument.startsWith(name + " ") || argument.startsWith(name + "=")) { + return entry.getValue() + argument.substring(name.length()); + } + } + return argument; + } + } } diff --git a/spring-boot-project/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/options/OptionHandler.java b/spring-boot-project/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/options/OptionHandler.java index a532ce92d405..f7de09b2a930 100644 --- a/spring-boot-project/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/options/OptionHandler.java +++ b/spring-boot-project/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/options/OptionHandler.java @@ -28,6 +28,7 @@ import java.util.Map; import java.util.Set; import java.util.TreeSet; +import java.util.function.Function; import joptsimple.BuiltinHelpFormatter; import joptsimple.HelpFormatter; @@ -49,12 +50,30 @@ */ public class OptionHandler { + private final Function argumentProcessor; + private OptionParser parser; private String help; private Collection optionHelp; + /** + * Create a new {@link OptionHandler} instance. + */ + public OptionHandler() { + this(Function.identity()); + } + + /** + * Create a new {@link OptionHandler} instance with an argument processor. + * @param argumentProcessor strategy that can be used to manipulate arguments before + * they are used. + */ + public OptionHandler(Function argumentProcessor) { + this.argumentProcessor = argumentProcessor; + } + public OptionSpecBuilder option(String name, String description) { return getParser().accepts(name, description); } @@ -80,6 +99,7 @@ public final ExitStatus run(String... args) throws Exception { if ("-cp".equals(argsToUse[i])) { argsToUse[i] = "--cp"; } + argsToUse[i] = this.argumentProcessor.apply(argsToUse[i]); } OptionSet options = getParser().parse(argsToUse); return run(options); diff --git a/spring-boot-project/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/init/InitCommandTests.java b/spring-boot-project/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/init/InitCommandTests.java index cbdfb1664f14..72748c575b9a 100644 --- a/spring-boot-project/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/init/InitCommandTests.java +++ b/spring-boot-project/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/init/InitCommandTests.java @@ -44,6 +44,7 @@ * * @author Stephane Nicoll * @author Eddú Meléndez + * @author Vignesh Thangavel Ilangovan */ @ExtendWith(MockitoExtension.class) class InitCommandTests extends AbstractHttpClientMockTests { @@ -272,6 +273,58 @@ void parseProjectOptions() throws Exception { assertThat(dependencies.contains("data-jpa")).isTrue(); } + @Test + void parseProjectWithCamelCaseOptions() throws Exception { + this.handler.disableProjectGeneration(); + this.command.run("--groupId=org.demo", "--artifactId=acme", "--version=1.2.3-SNAPSHOT", "--name=acme-sample", + "--description=Acme sample project", "--packageName=demo.foo", "--type=ant-project", "--build=grunt", + "--format=web", "--packaging=war", "--javaVersion=1.9", "--language=groovy", + "--bootVersion=1.2.0.RELEASE", "--dependencies=web,data-jpa"); + assertThat(this.handler.lastRequest.getGroupId()).isEqualTo("org.demo"); + assertThat(this.handler.lastRequest.getArtifactId()).isEqualTo("acme"); + assertThat(this.handler.lastRequest.getVersion()).isEqualTo("1.2.3-SNAPSHOT"); + assertThat(this.handler.lastRequest.getName()).isEqualTo("acme-sample"); + assertThat(this.handler.lastRequest.getDescription()).isEqualTo("Acme sample project"); + assertThat(this.handler.lastRequest.getPackageName()).isEqualTo("demo.foo"); + assertThat(this.handler.lastRequest.getType()).isEqualTo("ant-project"); + assertThat(this.handler.lastRequest.getBuild()).isEqualTo("grunt"); + assertThat(this.handler.lastRequest.getFormat()).isEqualTo("web"); + assertThat(this.handler.lastRequest.getPackaging()).isEqualTo("war"); + assertThat(this.handler.lastRequest.getJavaVersion()).isEqualTo("1.9"); + assertThat(this.handler.lastRequest.getLanguage()).isEqualTo("groovy"); + assertThat(this.handler.lastRequest.getBootVersion()).isEqualTo("1.2.0.RELEASE"); + List dependencies = this.handler.lastRequest.getDependencies(); + assertThat(dependencies).hasSize(2); + assertThat(dependencies.contains("web")).isTrue(); + assertThat(dependencies.contains("data-jpa")).isTrue(); + } + + @Test + void parseProjectWithKebabCaseOptions() throws Exception { + this.handler.disableProjectGeneration(); + this.command.run("--group-id=org.demo", "--artifact-id=acme", "--version=1.2.3-SNAPSHOT", "--name=acme-sample", + "--description=Acme sample project", "--package-name=demo.foo", "--type=ant-project", "--build=grunt", + "--format=web", "--packaging=war", "--java-version=1.9", "--language=groovy", + "--boot-version=1.2.0.RELEASE", "--dependencies=web,data-jpa"); + assertThat(this.handler.lastRequest.getGroupId()).isEqualTo("org.demo"); + assertThat(this.handler.lastRequest.getArtifactId()).isEqualTo("acme"); + assertThat(this.handler.lastRequest.getVersion()).isEqualTo("1.2.3-SNAPSHOT"); + assertThat(this.handler.lastRequest.getName()).isEqualTo("acme-sample"); + assertThat(this.handler.lastRequest.getDescription()).isEqualTo("Acme sample project"); + assertThat(this.handler.lastRequest.getPackageName()).isEqualTo("demo.foo"); + assertThat(this.handler.lastRequest.getType()).isEqualTo("ant-project"); + assertThat(this.handler.lastRequest.getBuild()).isEqualTo("grunt"); + assertThat(this.handler.lastRequest.getFormat()).isEqualTo("web"); + assertThat(this.handler.lastRequest.getPackaging()).isEqualTo("war"); + assertThat(this.handler.lastRequest.getJavaVersion()).isEqualTo("1.9"); + assertThat(this.handler.lastRequest.getLanguage()).isEqualTo("groovy"); + assertThat(this.handler.lastRequest.getBootVersion()).isEqualTo("1.2.0.RELEASE"); + List dependencies = this.handler.lastRequest.getDependencies(); + assertThat(dependencies).hasSize(2); + assertThat(dependencies.contains("web")).isTrue(); + assertThat(dependencies.contains("data-jpa")).isTrue(); + } + @Test void overwriteFileInArchive(@TempDir File tempDir) throws Exception { File conflict = new File(tempDir, "test.txt");