diff --git a/initializr/src/main/groovy/io/spring/initializr/InitializrMetadata.groovy b/initializr/src/main/groovy/io/spring/initializr/InitializrMetadata.groovy index 4a798737..d1172281 100644 --- a/initializr/src/main/groovy/io/spring/initializr/InitializrMetadata.groovy +++ b/initializr/src/main/groovy/io/spring/initializr/InitializrMetadata.groovy @@ -297,10 +297,13 @@ class InitializrMetadata { } static class Defaults { + + static final String DEFAULT_NAME = 'demo' + String groupId = 'org.test' String artifactId String version = '0.0.1-SNAPSHOT' - String name = 'demo' + String name = DEFAULT_NAME String description = 'Demo project for Spring Boot' String packageName String type diff --git a/initializr/src/main/groovy/io/spring/initializr/ProjectGenerator.groovy b/initializr/src/main/groovy/io/spring/initializr/ProjectGenerator.groovy index b7db3506..f42865c5 100644 --- a/initializr/src/main/groovy/io/spring/initializr/ProjectGenerator.groovy +++ b/initializr/src/main/groovy/io/spring/initializr/ProjectGenerator.groovy @@ -84,14 +84,16 @@ class ProjectGenerator { new File(dir, 'pom.xml').write(pom) } + def applicationName = request.applicationName def language = request.language def src = new File(new File(dir, "src/main/$language"), request.packageName.replace('.', '/')) src.mkdirs() - write(src, "Application.$language", model) + write(new File(src, "${applicationName}.${language}"), "Application.$language", model) if (request.packaging == 'war') { - write(src, "ServletInitializer.$language", model) + def fileName = "ServletInitializer.$language" + write(new File(src, fileName), fileName, model) } def test = new File(new File(dir, "src/test/$language"), request.packageName.replace('.', '/')) @@ -103,7 +105,7 @@ class ProjectGenerator { model.testAnnotations = '' model.testImports = '' } - write(test, "ApplicationTests.$language", model) + write(new File(test, "${applicationName}Tests.${language}"), "ApplicationTests.$language", model) def resources = new File(dir, 'src/main/resources') resources.mkdirs() @@ -174,10 +176,10 @@ class ProjectGenerator { template 'starter-build.gradle', model } - def write(File src, String name, def model) { - def tmpl = name.endsWith('.groovy') ? name + '.tmpl' : name + def write(File target, String templateName, def model) { + def tmpl = templateName.endsWith('.groovy') ? templateName + '.tmpl' : templateName def body = template tmpl, model - new File(src, name).write(body) + target.write(body) } private void addTempFile(String group, File file) { diff --git a/initializr/src/main/groovy/io/spring/initializr/ProjectRequest.groovy b/initializr/src/main/groovy/io/spring/initializr/ProjectRequest.groovy index 7b22d7b6..e9fc3465 100644 --- a/initializr/src/main/groovy/io/spring/initializr/ProjectRequest.groovy +++ b/initializr/src/main/groovy/io/spring/initializr/ProjectRequest.groovy @@ -33,6 +33,11 @@ class ProjectRequest { */ static final DEFAULT_STARTER = 'root_starter' + /** + * The default name of the application class. + */ + static final String DEFAULT_APPLICATION_NAME = 'Application' + List style = [] List dependencies = [] String name @@ -43,6 +48,7 @@ class ProjectRequest { String version String bootVersion String packaging + String applicationName String language String packageName String javaVersion @@ -88,6 +94,11 @@ class ProjectRequest { this.build = buildTag } } + + if (!applicationName) { + this.applicationName = generateApplicationName(this.name, DEFAULT_APPLICATION_NAME) + } + afterResolution(metadata) } @@ -114,7 +125,6 @@ class ProjectRequest { root.id = DEFAULT_STARTER root.asSpringBootStarter('') resolvedDependencies << root - } /** @@ -131,4 +141,51 @@ 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. + *

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('(? 1) { + for (int i = 1; i < text.length(); i++) { + if (!Character.isJavaIdentifierPart(text.charAt(i))) { + return true + } + } + } + return false + } + } diff --git a/initializr/src/main/resources/templates/Application.groovy.tmpl b/initializr/src/main/resources/templates/Application.groovy.tmpl index d31ef8d6..ac2f49a8 100644 --- a/initializr/src/main/resources/templates/Application.groovy.tmpl +++ b/initializr/src/main/resources/templates/Application.groovy.tmpl @@ -8,9 +8,9 @@ import org.springframework.context.annotation.Configuration @Configuration @ComponentScan @EnableAutoConfiguration -class Application { +class ${applicationName} { static void main(String[] args) { - SpringApplication.run Application, args + SpringApplication.run ${applicationName}, args } } diff --git a/initializr/src/main/resources/templates/Application.java b/initializr/src/main/resources/templates/Application.java index d3dac649..20029b2d 100644 --- a/initializr/src/main/resources/templates/Application.java +++ b/initializr/src/main/resources/templates/Application.java @@ -8,9 +8,9 @@ import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan @EnableAutoConfiguration -public class Application { +public class ${applicationName} { public static void main(String[] args) { - SpringApplication.run(Application.class, args); + SpringApplication.run(${applicationName}.class, args); } } diff --git a/initializr/src/main/resources/templates/ApplicationTests.groovy.tmpl b/initializr/src/main/resources/templates/ApplicationTests.groovy.tmpl index 749f35ad..1ba99874 100644 --- a/initializr/src/main/resources/templates/ApplicationTests.groovy.tmpl +++ b/initializr/src/main/resources/templates/ApplicationTests.groovy.tmpl @@ -6,8 +6,8 @@ ${testImports}import org.springframework.boot.test.SpringApplicationConfiguratio import org.springframework.test.context.junit4.SpringJUnit4ClassRunner @RunWith(SpringJUnit4ClassRunner) -@SpringApplicationConfiguration(classes = Application) -${testAnnotations}class ApplicationTests { +@SpringApplicationConfiguration(classes = ${applicationName}) +${testAnnotations}class ${applicationName}Tests { @Test void contextLoads() { diff --git a/initializr/src/main/resources/templates/ApplicationTests.java b/initializr/src/main/resources/templates/ApplicationTests.java index 87e985ab..a9dfc644 100644 --- a/initializr/src/main/resources/templates/ApplicationTests.java +++ b/initializr/src/main/resources/templates/ApplicationTests.java @@ -6,8 +6,8 @@ ${testImports}import org.springframework.boot.test.SpringApplicationConfiguratio import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) -@SpringApplicationConfiguration(classes = Application.class) -${testAnnotations}public class ApplicationTests { +@SpringApplicationConfiguration(classes = ${applicationName}.class) +${testAnnotations}public class ${applicationName}Tests { @Test public void contextLoads() { diff --git a/initializr/src/main/resources/templates/ServletInitializer.groovy.tmpl b/initializr/src/main/resources/templates/ServletInitializer.groovy.tmpl index b490c903..793260d4 100644 --- a/initializr/src/main/resources/templates/ServletInitializer.groovy.tmpl +++ b/initializr/src/main/resources/templates/ServletInitializer.groovy.tmpl @@ -3,11 +3,11 @@ package ${packageName} import org.springframework.boot.builder.SpringApplicationBuilder import org.springframework.boot.context.web.SpringBootServletInitializer -class ServletInitializer extends SpringBootServletInitializer { +class ServletInitializer extends SpringBootServletInitializer { @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { - application.sources(Application) + application.sources(${applicationName}) } } diff --git a/initializr/src/main/resources/templates/ServletInitializer.java b/initializr/src/main/resources/templates/ServletInitializer.java index f21b7a25..eca3ef88 100644 --- a/initializr/src/main/resources/templates/ServletInitializer.java +++ b/initializr/src/main/resources/templates/ServletInitializer.java @@ -7,7 +7,7 @@ public class ServletInitializer extends SpringBootServletInitializer { @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { - return application.sources(Application.class); + return application.sources(${applicationName}.class); } } diff --git a/initializr/src/main/resources/templates/starter-pom.xml b/initializr/src/main/resources/templates/starter-pom.xml index e6410d1e..ca941e0f 100644 --- a/initializr/src/main/resources/templates/starter-pom.xml +++ b/initializr/src/main/resources/templates/starter-pom.xml @@ -20,7 +20,7 @@ UTF-8 - ${packageName}.Application + ${packageName}.${applicationName} ${javaVersion} diff --git a/initializr/src/test/groovy/io/spring/initializr/ProjectGeneratorTests.groovy b/initializr/src/test/groovy/io/spring/initializr/ProjectGeneratorTests.groovy index cdca72d6..09738d3b 100644 --- a/initializr/src/test/groovy/io/spring/initializr/ProjectGeneratorTests.groovy +++ b/initializr/src/test/groovy/io/spring/initializr/ProjectGeneratorTests.groovy @@ -51,7 +51,7 @@ class ProjectGeneratorTests { projectGenerator.listeners << listener def request = createProjectRequest('web') - generateMavenPom(request).hasStartClass('demo.Application') + generateMavenPom(request).hasStartClass('demo.DemoApplication') .hasNoRepository().hasSpringBootStarterDependency('web') verify(listener, times(1)).onGeneratedProject(request) } @@ -73,7 +73,7 @@ class ProjectGeneratorTests { def request = createProjectRequest('web') generateProject(request).isJavaProject().isMavenProject().pomAssert() - .hasStartClass('demo.Application').hasNoRepository().hasSpringBootStarterDependency('web') + .hasStartClass('demo.DemoApplication').hasNoRepository().hasSpringBootStarterDependency('web') verify(listener, times(1)).onGeneratedProject(request) } @@ -88,7 +88,7 @@ class ProjectGeneratorTests { void mavenPomWithBootSnapshot() { def request = createProjectRequest('web') request.bootVersion = '1.0.1.BUILD-SNAPSHOT' - generateMavenPom(request).hasStartClass('demo.Application') + generateMavenPom(request).hasStartClass('demo.DemoApplication') .hasSnapshotRepository().hasSpringBootStarterDependency('web') } @@ -105,7 +105,7 @@ class ProjectGeneratorTests { projectGenerator.metadata = metadata def request = createProjectRequest('thymeleaf') - generateMavenPom(request).hasStartClass('demo.Application') + generateMavenPom(request).hasStartClass('demo.DemoApplication') .hasDependency('org.foo', 'thymeleaf') .hasDependenciesCount(2) @@ -125,7 +125,7 @@ class ProjectGeneratorTests { def request = createProjectRequest('thymeleaf') request.packaging = 'war' - generateMavenPom(request).hasStartClass('demo.Application') + generateMavenPom(request).hasStartClass('demo.DemoApplication') .hasSpringBootStarterDependency('tomcat') .hasDependency('org.foo', 'thymeleaf') // This is tagged as web facet so it brings the web one .hasSpringBootStarterDependency('test') @@ -137,7 +137,7 @@ class ProjectGeneratorTests { void mavenWarPomWithoutWebFacet() { def request = createProjectRequest('data-jpa') request.packaging = 'war' - generateMavenPom(request).hasStartClass('demo.Application') + generateMavenPom(request).hasStartClass('demo.DemoApplication') .hasSpringBootStarterDependency('tomcat') .hasSpringBootStarterDependency('data-jpa') .hasSpringBootStarterDependency('web') // Added by war packaging diff --git a/initializr/src/test/groovy/io/spring/initializr/ProjectRequestTests.groovy b/initializr/src/test/groovy/io/spring/initializr/ProjectRequestTests.groovy index 8abaca99..d4510e72 100644 --- a/initializr/src/test/groovy/io/spring/initializr/ProjectRequestTests.groovy +++ b/initializr/src/test/groovy/io/spring/initializr/ProjectRequestTests.groovy @@ -29,6 +29,8 @@ import static org.junit.Assert.assertNull */ class ProjectRequestTests { + private static final String DEFAULT_APPLICATION_NAME = 'FooBarApplication' + @Rule public final ExpectedException thrown = ExpectedException.none() @@ -127,6 +129,105 @@ class ProjectRequestTests { request.resolve(metadata) } + @Test + void resolveApplicationNameWithNoName() { + def request = new ProjectRequest() + def metadata = InitializrMetadataBuilder.withDefaults().validateAndGet() + + request.resolve(metadata) + assertEquals ProjectRequest.DEFAULT_APPLICATION_NAME, request.applicationName + } + + @Test + void resolveApplicationName() { + def request = new ProjectRequest() + request.name = 'Foo2' + def metadata = InitializrMetadataBuilder.withDefaults().validateAndGet() + + request.resolve(metadata) + assertEquals 'Foo2Application', request.applicationName + } + + @Test + void resolveApplicationNameWithApplicationNameSet() { + def request = new ProjectRequest() + request.name = 'Foo2' + request.applicationName ='MyApplicationName' + def metadata = InitializrMetadataBuilder.withDefaults().validateAndGet() + + request.resolve(metadata) + 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() expected.asSpringBootStarter(name) diff --git a/initializr/src/test/groovy/io/spring/initializr/support/ProjectAssert.groovy b/initializr/src/test/groovy/io/spring/initializr/support/ProjectAssert.groovy index 045792bf..90c874d7 100644 --- a/initializr/src/test/groovy/io/spring/initializr/support/ProjectAssert.groovy +++ b/initializr/src/test/groovy/io/spring/initializr/support/ProjectAssert.groovy @@ -16,6 +16,8 @@ package io.spring.initializr.support +import io.spring.initializr.InitializrMetadata + import static org.junit.Assert.assertEquals /** @@ -26,6 +28,8 @@ import static org.junit.Assert.assertEquals */ class ProjectAssert { + private static final DEFAULT_APPLICATION_NAME = generateDefaultApplicationName() + final File dir /** @@ -51,17 +55,25 @@ class ProjectAssert { hasFile('build.gradle').hasNoFile('pom.xml') } - ProjectAssert isJavaProject() { - hasFile('src/main/java/demo/Application.java', - 'src/test/java/demo/ApplicationTests.java', + ProjectAssert isJavaProject(String expectedApplicationName) { + hasFile("src/main/java/demo/${expectedApplicationName}.java", + "src/test/java/demo/${expectedApplicationName}Tests.java", 'src/main/resources/application.properties') } - ProjectAssert isJavaWarProject() { - isJavaProject().hasStaticAndTemplatesResources(true) + ProjectAssert isJavaProject() { + isJavaProject(DEFAULT_APPLICATION_NAME) + } + + ProjectAssert isJavaWarProject(String expectedApplicationName) { + isJavaProject(expectedApplicationName).hasStaticAndTemplatesResources(true) .hasFile('src/main/java/demo/ServletInitializer.java') } + ProjectAssert isJavaWarProject() { + isJavaWarProject(DEFAULT_APPLICATION_NAME) + } + ProjectAssert hasStaticAndTemplatesResources(boolean web) { assertFile('src/main/resources/templates', web) assertFile('src/main/resources/static', web) @@ -91,4 +103,8 @@ class ProjectAssert { new File(dir, localPath) } + private static generateDefaultApplicationName() { + InitializrMetadata.Defaults.DEFAULT_NAME.capitalize() + 'Application' + } + } diff --git a/initializr/src/test/groovy/io/spring/initializr/web/AbstractInitializerControllerFormIntegrationTests.groovy b/initializr/src/test/groovy/io/spring/initializr/web/AbstractInitializerControllerFormIntegrationTests.groovy index 3911d605..92622b4e 100644 --- a/initializr/src/test/groovy/io/spring/initializr/web/AbstractInitializerControllerFormIntegrationTests.groovy +++ b/initializr/src/test/groovy/io/spring/initializr/web/AbstractInitializerControllerFormIntegrationTests.groovy @@ -86,7 +86,8 @@ abstract class AbstractInitializerControllerFormIntegrationTests extends Abstrac assertEquals 'attachment; filename="foo-bar.zip"', value def projectAssert = zipProjectAssert(webResponse) - projectAssert.isMavenProject().isJavaProject().hasStaticAndTemplatesResources(true) + projectAssert.isMavenProject().isJavaProject('MyProjectApplication') + .hasStaticAndTemplatesResources(true) projectAssert.pomAssert().hasGroupId('com.acme').hasArtifactId('foo-bar') .hasName('My project').hasDescription('A description for my project') diff --git a/initializr/src/test/groovy/io/spring/initializr/web/MainControllerDefaultsIntegrationTests.groovy b/initializr/src/test/groovy/io/spring/initializr/web/MainControllerDefaultsIntegrationTests.groovy index 0cea557f..53692153 100644 --- a/initializr/src/test/groovy/io/spring/initializr/web/MainControllerDefaultsIntegrationTests.groovy +++ b/initializr/src/test/groovy/io/spring/initializr/web/MainControllerDefaultsIntegrationTests.groovy @@ -20,7 +20,7 @@ class MainControllerDefaultsIntegrationTests extends AbstractInitializrControlle def content = restTemplate.getForObject(createUrl('/pom.xml?style=web'), String) def pomAssert = new PomAssert(content) pomAssert.hasGroupId('org.foo').hasArtifactId('foo-bar').hasVersion('1.2.4-SNAPSHOT').hasPackaging('jar') - .hasName('FooBar').hasDescription('FooBar Project').hasStartClass('org.foo.demo.Application') + .hasName('FooBar').hasDescription('FooBar Project').hasStartClass('org.foo.demo.FooBarApplication') } @Test