mirror of
https://gitee.com/dcren/initializr.git
synced 2026-02-26 05:32:58 +08:00
Prevent invalid application name
Previously, if a user choose 'SpringBoot' or 'Spring' as the name of the project, the service will generate a `SpringBootApplication` or `SpringApplication` respectively. Both of which leads to a compilation failure since those names are already used in the current context. The generation of the application name based on the project's name have been moved to InitializrMetadata and two new properties have been introduced: * env.fallbackApplicationName defines the name of the application if the one that was generated was invalid for some reasons * env.invalidApplicationNames defines a list of application names that should flagged as invalid. When the current candidate is equal to one of them, the fallback should be used instead These properties have default values that prevent such issue to happen by default. Fixes gh-79
This commit is contained in:
@@ -92,8 +92,7 @@ class CommandLineHelpGenerator {
|
||||
defaults[it.key] = it.value
|
||||
}
|
||||
}
|
||||
defaults['applicationName'] = ProjectRequest.generateApplicationName(metadata.defaults.name,
|
||||
ProjectRequest.DEFAULT_APPLICATION_NAME)
|
||||
defaults['applicationName'] = metadata.generateApplicationName(metadata.defaults.name)
|
||||
defaults['baseDir'] = 'no base dir'
|
||||
defaults['dependencies'] = 'none'
|
||||
|
||||
|
||||
@@ -102,6 +102,34 @@ class InitializrMetadata {
|
||||
"$defaults.bootVersion/spring-boot-cli-$defaults.bootVersion-bin.$extension"
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a suitable application mame based on the specified name. If no suitable
|
||||
* application name can be generated from the specified {@code name}, the
|
||||
* {@link Env#fallbackApplicationName} is used instead.
|
||||
* <p>No suitable application name can be generated if the name is {@code null} or
|
||||
* if it contains an invalid character for a class identifier.
|
||||
* @see Env#fallbackApplicationName
|
||||
* @see Env#invalidApplicationNames
|
||||
*/
|
||||
String generateApplicationName(String name) {
|
||||
if (!name) {
|
||||
return env.fallbackApplicationName
|
||||
}
|
||||
String text = splitCamelCase(name.trim())
|
||||
String result = text.replaceAll("(_|-| |:)+([A-Za-z0-9])", { Object[] it ->
|
||||
it[2].toUpperCase()
|
||||
})
|
||||
if (!result.endsWith('Application')) {
|
||||
result += 'Application'
|
||||
}
|
||||
String candidate = result.capitalize();
|
||||
if (hasInvalidChar(candidate) || env.invalidApplicationNames.contains(candidate)) {
|
||||
return env.fallbackApplicationName
|
||||
} else {
|
||||
return candidate
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a {@link ProjectRequest} instance with the defaults
|
||||
* defined in this instance.
|
||||
@@ -223,6 +251,27 @@ class InitializrMetadata {
|
||||
}
|
||||
}
|
||||
|
||||
private static String splitCamelCase(String text) {
|
||||
text.split('(?<!(^|[A-Z]))(?=[A-Z])|(?<!^)(?=[A-Z][a-z])').collect {
|
||||
String s = it.toLowerCase()
|
||||
s.capitalize()
|
||||
}.join("")
|
||||
}
|
||||
|
||||
private static boolean hasInvalidChar(String text) {
|
||||
if (!Character.isJavaIdentifierStart(text.charAt(0))) {
|
||||
return true
|
||||
}
|
||||
if (text.length() > 1) {
|
||||
for (int i = 1; i < text.length(); i++) {
|
||||
if (!Character.isJavaIdentifierPart(text.charAt(i))) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
static class DependencyGroup {
|
||||
|
||||
String name
|
||||
@@ -359,6 +408,17 @@ class InitializrMetadata {
|
||||
|
||||
String springBootMetadataUrl = 'https://spring.io/project_metadata/spring-boot'
|
||||
|
||||
/**
|
||||
* The application name to use if none could be generated.
|
||||
*/
|
||||
String fallbackApplicationName = 'Application'
|
||||
|
||||
/**
|
||||
* The list of invalid application names. If such name is chosen or generated,
|
||||
* the {@link #fallbackApplicationName} should be used instead.
|
||||
*/
|
||||
List<String> invalidApplicationNames = ['SpringApplication', 'SpringBootApplication']
|
||||
|
||||
boolean forceSsl = true
|
||||
|
||||
void validate() {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2014 the original author or authors.
|
||||
* Copyright 2012-2015 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.
|
||||
@@ -35,11 +35,6 @@ class ProjectRequest {
|
||||
*/
|
||||
static final DEFAULT_STARTER = 'root_starter'
|
||||
|
||||
/**
|
||||
* The default name of the application class.
|
||||
*/
|
||||
static final String DEFAULT_APPLICATION_NAME = 'Application'
|
||||
|
||||
List<String> style = []
|
||||
List<String> dependencies = []
|
||||
String name
|
||||
@@ -110,7 +105,7 @@ class ProjectRequest {
|
||||
}
|
||||
|
||||
if (!applicationName) {
|
||||
this.applicationName = generateApplicationName(this.name, DEFAULT_APPLICATION_NAME)
|
||||
this.applicationName = metadata.generateApplicationName(this.name)
|
||||
}
|
||||
|
||||
afterResolution(metadata)
|
||||
@@ -155,51 +150,4 @@ class ProjectRequest {
|
||||
facets.contains(facet)
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a suitable application mame based on the specified name. If no suitable
|
||||
* application name can be generated from the specified {@code name}, the
|
||||
* {@code defaultApplicationName} is used instead.
|
||||
* <p>No suitable application name can be generated if the name is {@code null} or
|
||||
* if it contains an invalid character for a class identifier.
|
||||
*/
|
||||
static String generateApplicationName(String name, String defaultApplicationName) {
|
||||
if (!name) {
|
||||
return defaultApplicationName
|
||||
}
|
||||
String text = splitCamelCase(name.trim())
|
||||
String result = text.replaceAll("(_|-| |:)+([A-Za-z0-9])", { Object[] it ->
|
||||
it[2].toUpperCase()
|
||||
})
|
||||
if (!result.endsWith('Application')) {
|
||||
result += 'Application'
|
||||
}
|
||||
String candidate = result.capitalize();
|
||||
if (hasInvalidChar(candidate)) {
|
||||
return defaultApplicationName
|
||||
} else {
|
||||
return candidate
|
||||
}
|
||||
}
|
||||
|
||||
private static String splitCamelCase(String text) {
|
||||
text.split('(?<!(^|[A-Z]))(?=[A-Z])|(?<!^)(?=[A-Z][a-z])').collect {
|
||||
String s = it.toLowerCase()
|
||||
s.capitalize()
|
||||
}.join("")
|
||||
}
|
||||
|
||||
private static boolean hasInvalidChar(String text) {
|
||||
if (!Character.isJavaIdentifierStart(text.charAt(0))) {
|
||||
return true
|
||||
}
|
||||
if (text.length() > 1) {
|
||||
for (int i = 1; i < text.length(); i++) {
|
||||
if (!Character.isJavaIdentifierPart(text.charAt(i))) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -230,6 +230,81 @@ class InitializrMetadataTests {
|
||||
assertEquals 'two', InitializrMetadata.getDefault(elements)
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateApplicationNameSimple() {
|
||||
assertEquals 'DemoApplication', this.metadata.generateApplicationName('demo')
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateApplicationNameSimpleApplication() {
|
||||
assertEquals 'DemoApplication', this.metadata.generateApplicationName('demoApplication')
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateApplicationNameSimpleCamelCase() {
|
||||
assertEquals 'MyDemoApplication', this.metadata.generateApplicationName('myDemo')
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateApplicationNameSimpleUnderscore() {
|
||||
assertEquals 'MyDemoApplication', this.metadata.generateApplicationName('my_demo')
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateApplicationNameSimpleColon() {
|
||||
assertEquals 'MyDemoApplication', this.metadata.generateApplicationName('my:demo')
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateApplicationNameSimpleSpace() {
|
||||
assertEquals 'MyDemoApplication', this.metadata.generateApplicationName('my demo')
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateApplicationNamSsimpleDash() {
|
||||
assertEquals 'MyDemoApplication', this.metadata.generateApplicationName('my-demo')
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateApplicationNameUpperCaseUnderscore() {
|
||||
assertEquals 'MyDemoApplication', this.metadata.generateApplicationName('MY_DEMO')
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateApplicationNameUpperCaseDash() {
|
||||
assertEquals 'MyDemoApplication', this.metadata.generateApplicationName('MY-DEMO')
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateApplicationNameMultiSpaces() {
|
||||
assertEquals 'MyDemoApplication', this.metadata.generateApplicationName(' my demo ')
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateApplicationNameMultiSpacesUpperCase() {
|
||||
assertEquals 'MyDemoApplication', this.metadata.generateApplicationName(' MY DEMO ')
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateApplicationNameInvalidStartCharacter() {
|
||||
assertEquals this.metadata.env.fallbackApplicationName, this.metadata.generateApplicationName('1MyDemo')
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateApplicationNameInvalidPartCharacter() {
|
||||
assertEquals this.metadata.env.fallbackApplicationName, this.metadata.generateApplicationName('MyDe|mo')
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateApplicationNameInvalidApplicationName() {
|
||||
assertEquals this.metadata.env.fallbackApplicationName, this.metadata.generateApplicationName('SpringBoot')
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateApplicationNameAnotherInvalidApplicationName() {
|
||||
assertEquals this.metadata.env.fallbackApplicationName, this.metadata.generateApplicationName('Spring')
|
||||
}
|
||||
|
||||
private static ProjectRequest doCreateProjectRequest(InitializrMetadata metadata) {
|
||||
def request = new ProjectRequest()
|
||||
metadata.initializeProjectRequest(request)
|
||||
|
||||
@@ -166,7 +166,7 @@ class ProjectRequestTests {
|
||||
def metadata = InitializrMetadataBuilder.withDefaults().validateAndGet()
|
||||
|
||||
request.resolve(metadata)
|
||||
assertEquals ProjectRequest.DEFAULT_APPLICATION_NAME, request.applicationName
|
||||
assertEquals metadata.env.fallbackApplicationName, request.applicationName
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -190,74 +190,6 @@ class ProjectRequestTests {
|
||||
assertEquals 'MyApplicationName', request.applicationName
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateApplicationNameSimple() {
|
||||
assertEquals 'DemoApplication', generateApplicationName('demo')
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateApplicationNameSimpleApplication() {
|
||||
assertEquals 'DemoApplication', generateApplicationName('demoApplication')
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateApplicationNameSimpleCamelCase() {
|
||||
assertEquals 'MyDemoApplication', generateApplicationName('myDemo')
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateApplicationNameSimpleUnderscore() {
|
||||
assertEquals 'MyDemoApplication', generateApplicationName('my_demo')
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateApplicationNameSimpleColon() {
|
||||
assertEquals 'MyDemoApplication', generateApplicationName('my:demo')
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateApplicationNameSimpleSpace() {
|
||||
assertEquals 'MyDemoApplication', generateApplicationName('my demo')
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateApplicationNamSsimpleDash() {
|
||||
assertEquals 'MyDemoApplication', generateApplicationName('my-demo')
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateApplicationNameUpperCaseUnderscore() {
|
||||
assertEquals 'MyDemoApplication', generateApplicationName('MY_DEMO')
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateApplicationNameUpperCaseDash() {
|
||||
assertEquals 'MyDemoApplication', generateApplicationName('MY-DEMO')
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateApplicationNameMultiSpaces() {
|
||||
assertEquals 'MyDemoApplication', generateApplicationName(' my demo ')
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateApplicationNameMultiSpacesUpperCase() {
|
||||
assertEquals 'MyDemoApplication', generateApplicationName(' MY DEMO ')
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateApplicationNameInvalidStartCharacter() {
|
||||
assertEquals DEFAULT_APPLICATION_NAME, generateApplicationName('1MyDemo')
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateApplicationNameInvalidPartCharacter() {
|
||||
assertEquals DEFAULT_APPLICATION_NAME, generateApplicationName('MyDe|mo')
|
||||
}
|
||||
|
||||
private static generateApplicationName(String text) {
|
||||
ProjectRequest.generateApplicationName(text, DEFAULT_APPLICATION_NAME)
|
||||
}
|
||||
|
||||
private static void assertBootStarter(InitializrMetadata.Dependency actual, String name) {
|
||||
def expected = new InitializrMetadata.Dependency()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2014 the original author or authors.
|
||||
* Copyright 2012-2015 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.
|
||||
@@ -44,4 +44,15 @@ class MainControllerEnvIntegrationTests extends AbstractInitializrControllerInte
|
||||
assertTrue "Force SSL should be disabled", body.contains("http://localhost:$port/install.sh")
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateProjectWithInvalidName() {
|
||||
downloadZip('/starter.zip?style=data-jpa&name=Invalid')
|
||||
.isJavaProject('FooBarApplication')
|
||||
.isMavenProject()
|
||||
.hasStaticAndTemplatesResources(false).pomAssert()
|
||||
.hasDependenciesCount(2)
|
||||
.hasSpringBootStarterDependency('data-jpa')
|
||||
.hasSpringBootStarterDependency('test')
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
initializr:
|
||||
env:
|
||||
artifactRepository: https://repo.spring.io/lib-release
|
||||
forceSsl: false
|
||||
forceSsl: false
|
||||
fallbackApplicationName: FooBarApplication
|
||||
invalidApplicationNames:
|
||||
- InvalidApplication
|
||||
Reference in New Issue
Block a user