Skip to content

Commit a392d80

Browse files
committed
Merge pull request #28138 from vignesh1992
* pr/28138: Polish 'Support both kebab-case and camelCase as Spring init CLI Options' Support both kebab-case and camelCase as Spring init CLI Options Closes gh-28138
2 parents b9f0ec5 + c384fbd commit a392d80

File tree

3 files changed

+106
-4
lines changed

3 files changed

+106
-4
lines changed

spring-boot-project/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/init/InitCommand.java

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@
2020
import java.util.ArrayList;
2121
import java.util.Arrays;
2222
import java.util.Collection;
23+
import java.util.Collections;
24+
import java.util.HashMap;
2325
import java.util.List;
26+
import java.util.Map;
2427

2528
import joptsimple.OptionSet;
2629
import joptsimple.OptionSpec;
@@ -38,6 +41,7 @@
3841
*
3942
* @author Stephane Nicoll
4043
* @author Eddú Meléndez
44+
* @author Vignesh Thangavel Ilangovan
4145
* @since 1.2.0
4246
*/
4347
public class InitCommand extends OptionParsingCommand {
@@ -71,6 +75,21 @@ public Collection<HelpExample> getExamples() {
7175
*/
7276
static class InitOptionHandler extends OptionHandler {
7377

78+
/**
79+
* Mapping from camelCase options advertised by the service to our kebab-case
80+
* options.
81+
*/
82+
private static final Map<String, String> CAMEL_CASE_OPTIONS;
83+
static {
84+
Map<String, String> options = new HashMap<>();
85+
options.put("--groupId", "--group-id");
86+
options.put("--artifactId", "--artifact-id");
87+
options.put("--packageName", "--package-name");
88+
options.put("--javaVersion", "--java-version");
89+
options.put("--bootVersion", "--boot-version");
90+
CAMEL_CASE_OPTIONS = Collections.unmodifiableMap(options);
91+
}
92+
7493
private final ServiceCapabilitiesReportGenerator serviceCapabilitiesReport;
7594

7695
private final ProjectGenerator projectGenerator;
@@ -112,9 +131,9 @@ static class InitOptionHandler extends OptionHandler {
112131
private OptionSpec<Void> force;
113132

114133
InitOptionHandler(InitializrService initializrService) {
134+
super(InitOptionHandler::processArgument);
115135
this.serviceCapabilitiesReport = new ServiceCapabilitiesReportGenerator(initializrService);
116136
this.projectGenerator = new ProjectGenerator(initializrService);
117-
118137
}
119138

120139
@Override
@@ -129,15 +148,15 @@ protected void options() {
129148
}
130149

131150
private void projectGenerationOptions() {
132-
this.groupId = option(Arrays.asList("groupId", "g"), "Project coordinates (for example 'org.test')")
151+
this.groupId = option(Arrays.asList("group-id", "g"), "Project coordinates (for example 'org.test')")
133152
.withRequiredArg();
134-
this.artifactId = option(Arrays.asList("artifactId", "a"),
153+
this.artifactId = option(Arrays.asList("artifact-id", "a"),
135154
"Project coordinates; infer archive name (for example 'test')").withRequiredArg();
136155
this.version = option(Arrays.asList("version", "v"), "Project version (for example '0.0.1-SNAPSHOT')")
137156
.withRequiredArg();
138157
this.name = option(Arrays.asList("name", "n"), "Project name; infer application name").withRequiredArg();
139158
this.description = option("description", "Project description").withRequiredArg();
140-
this.packageName = option("package-name", "Package name").withRequiredArg();
159+
this.packageName = option(Arrays.asList("package-name"), "Package name").withRequiredArg();
141160
this.type = option(Arrays.asList("type", "t"),
142161
"Project type. Not normally needed if you use --build "
143162
+ "and/or --format. Check the capabilities of the service (--list) for more details")
@@ -249,6 +268,16 @@ protected ProjectGenerationRequest createProjectGenerationRequest(OptionSet opti
249268
return request;
250269
}
251270

271+
private static String processArgument(String argument) {
272+
for (Map.Entry<String, String> entry : CAMEL_CASE_OPTIONS.entrySet()) {
273+
String name = entry.getKey();
274+
if (argument.startsWith(name + " ") || argument.startsWith(name + "=")) {
275+
return entry.getValue() + argument.substring(name.length());
276+
}
277+
}
278+
return argument;
279+
}
280+
252281
}
253282

254283
}

spring-boot-project/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/options/OptionHandler.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import java.util.Map;
2929
import java.util.Set;
3030
import java.util.TreeSet;
31+
import java.util.function.Function;
3132

3233
import joptsimple.BuiltinHelpFormatter;
3334
import joptsimple.HelpFormatter;
@@ -49,12 +50,30 @@
4950
*/
5051
public class OptionHandler {
5152

53+
private final Function<String, String> argumentProcessor;
54+
5255
private OptionParser parser;
5356

5457
private String help;
5558

5659
private Collection<OptionHelp> optionHelp;
5760

61+
/**
62+
* Create a new {@link OptionHandler} instance.
63+
*/
64+
public OptionHandler() {
65+
this(Function.identity());
66+
}
67+
68+
/**
69+
* Create a new {@link OptionHandler} instance with an argument processor.
70+
* @param argumentProcessor strategy that can be used to manipulate arguments before
71+
* they are used.
72+
*/
73+
public OptionHandler(Function<String, String> argumentProcessor) {
74+
this.argumentProcessor = argumentProcessor;
75+
}
76+
5877
public OptionSpecBuilder option(String name, String description) {
5978
return getParser().accepts(name, description);
6079
}
@@ -80,6 +99,7 @@ public final ExitStatus run(String... args) throws Exception {
8099
if ("-cp".equals(argsToUse[i])) {
81100
argsToUse[i] = "--cp";
82101
}
102+
argsToUse[i] = this.argumentProcessor.apply(argsToUse[i]);
83103
}
84104
OptionSet options = getParser().parse(argsToUse);
85105
return run(options);

spring-boot-project/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/init/InitCommandTests.java

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
*
4545
* @author Stephane Nicoll
4646
* @author Eddú Meléndez
47+
* @author Vignesh Thangavel Ilangovan
4748
*/
4849
@ExtendWith(MockitoExtension.class)
4950
class InitCommandTests extends AbstractHttpClientMockTests {
@@ -272,6 +273,58 @@ void parseProjectOptions() throws Exception {
272273
assertThat(dependencies.contains("data-jpa")).isTrue();
273274
}
274275

276+
@Test
277+
void parseProjectWithCamelCaseOptions() throws Exception {
278+
this.handler.disableProjectGeneration();
279+
this.command.run("--groupId=org.demo", "--artifactId=acme", "--version=1.2.3-SNAPSHOT", "--name=acme-sample",
280+
"--description=Acme sample project", "--packageName=demo.foo", "--type=ant-project", "--build=grunt",
281+
"--format=web", "--packaging=war", "--javaVersion=1.9", "--language=groovy",
282+
"--bootVersion=1.2.0.RELEASE", "--dependencies=web,data-jpa");
283+
assertThat(this.handler.lastRequest.getGroupId()).isEqualTo("org.demo");
284+
assertThat(this.handler.lastRequest.getArtifactId()).isEqualTo("acme");
285+
assertThat(this.handler.lastRequest.getVersion()).isEqualTo("1.2.3-SNAPSHOT");
286+
assertThat(this.handler.lastRequest.getName()).isEqualTo("acme-sample");
287+
assertThat(this.handler.lastRequest.getDescription()).isEqualTo("Acme sample project");
288+
assertThat(this.handler.lastRequest.getPackageName()).isEqualTo("demo.foo");
289+
assertThat(this.handler.lastRequest.getType()).isEqualTo("ant-project");
290+
assertThat(this.handler.lastRequest.getBuild()).isEqualTo("grunt");
291+
assertThat(this.handler.lastRequest.getFormat()).isEqualTo("web");
292+
assertThat(this.handler.lastRequest.getPackaging()).isEqualTo("war");
293+
assertThat(this.handler.lastRequest.getJavaVersion()).isEqualTo("1.9");
294+
assertThat(this.handler.lastRequest.getLanguage()).isEqualTo("groovy");
295+
assertThat(this.handler.lastRequest.getBootVersion()).isEqualTo("1.2.0.RELEASE");
296+
List<String> dependencies = this.handler.lastRequest.getDependencies();
297+
assertThat(dependencies).hasSize(2);
298+
assertThat(dependencies.contains("web")).isTrue();
299+
assertThat(dependencies.contains("data-jpa")).isTrue();
300+
}
301+
302+
@Test
303+
void parseProjectWithKebabCaseOptions() throws Exception {
304+
this.handler.disableProjectGeneration();
305+
this.command.run("--group-id=org.demo", "--artifact-id=acme", "--version=1.2.3-SNAPSHOT", "--name=acme-sample",
306+
"--description=Acme sample project", "--package-name=demo.foo", "--type=ant-project", "--build=grunt",
307+
"--format=web", "--packaging=war", "--java-version=1.9", "--language=groovy",
308+
"--boot-version=1.2.0.RELEASE", "--dependencies=web,data-jpa");
309+
assertThat(this.handler.lastRequest.getGroupId()).isEqualTo("org.demo");
310+
assertThat(this.handler.lastRequest.getArtifactId()).isEqualTo("acme");
311+
assertThat(this.handler.lastRequest.getVersion()).isEqualTo("1.2.3-SNAPSHOT");
312+
assertThat(this.handler.lastRequest.getName()).isEqualTo("acme-sample");
313+
assertThat(this.handler.lastRequest.getDescription()).isEqualTo("Acme sample project");
314+
assertThat(this.handler.lastRequest.getPackageName()).isEqualTo("demo.foo");
315+
assertThat(this.handler.lastRequest.getType()).isEqualTo("ant-project");
316+
assertThat(this.handler.lastRequest.getBuild()).isEqualTo("grunt");
317+
assertThat(this.handler.lastRequest.getFormat()).isEqualTo("web");
318+
assertThat(this.handler.lastRequest.getPackaging()).isEqualTo("war");
319+
assertThat(this.handler.lastRequest.getJavaVersion()).isEqualTo("1.9");
320+
assertThat(this.handler.lastRequest.getLanguage()).isEqualTo("groovy");
321+
assertThat(this.handler.lastRequest.getBootVersion()).isEqualTo("1.2.0.RELEASE");
322+
List<String> dependencies = this.handler.lastRequest.getDependencies();
323+
assertThat(dependencies).hasSize(2);
324+
assertThat(dependencies.contains("web")).isTrue();
325+
assertThat(dependencies.contains("data-jpa")).isTrue();
326+
}
327+
275328
@Test
276329
void overwriteFileInArchive(@TempDir File tempDir) throws Exception {
277330
File conflict = new File(tempDir, "test.txt");

0 commit comments

Comments
 (0)