From 3e50bcc101e3fa4effb820689b39496584b0ae66 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Sat, 21 Feb 2015 17:34:53 +0100 Subject: [PATCH] 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 --- .../CommandLineHelpGenerator.groovy | 3 +- .../initializr/InitializrMetadata.groovy | 60 +++++++++++++++ .../spring/initializr/ProjectRequest.groovy | 56 +------------- .../initializr/InitializrMetadataTests.groovy | 75 +++++++++++++++++++ .../initializr/ProjectRequestTests.groovy | 70 +---------------- .../MainControllerEnvIntegrationTests.groovy | 13 +++- .../resources/application-test-custom-env.yml | 5 +- 7 files changed, 155 insertions(+), 127 deletions(-) diff --git a/initializr/src/main/groovy/io/spring/initializr/CommandLineHelpGenerator.groovy b/initializr/src/main/groovy/io/spring/initializr/CommandLineHelpGenerator.groovy index 5807c511..008d6f36 100644 --- a/initializr/src/main/groovy/io/spring/initializr/CommandLineHelpGenerator.groovy +++ b/initializr/src/main/groovy/io/spring/initializr/CommandLineHelpGenerator.groovy @@ -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' diff --git a/initializr/src/main/groovy/io/spring/initializr/InitializrMetadata.groovy b/initializr/src/main/groovy/io/spring/initializr/InitializrMetadata.groovy index 4271e393..1df6b411 100644 --- a/initializr/src/main/groovy/io/spring/initializr/InitializrMetadata.groovy +++ b/initializr/src/main/groovy/io/spring/initializr/InitializrMetadata.groovy @@ -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. + *

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('(? 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 invalidApplicationNames = ['SpringApplication', 'SpringBootApplication'] + boolean forceSsl = true void validate() { diff --git a/initializr/src/main/groovy/io/spring/initializr/ProjectRequest.groovy b/initializr/src/main/groovy/io/spring/initializr/ProjectRequest.groovy index 737b5321..101b20c4 100644 --- a/initializr/src/main/groovy/io/spring/initializr/ProjectRequest.groovy +++ b/initializr/src/main/groovy/io/spring/initializr/ProjectRequest.groovy @@ -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 style = [] List 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. - *

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/test/groovy/io/spring/initializr/InitializrMetadataTests.groovy b/initializr/src/test/groovy/io/spring/initializr/InitializrMetadataTests.groovy index 932d8472..9deef4ee 100644 --- a/initializr/src/test/groovy/io/spring/initializr/InitializrMetadataTests.groovy +++ b/initializr/src/test/groovy/io/spring/initializr/InitializrMetadataTests.groovy @@ -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) diff --git a/initializr/src/test/groovy/io/spring/initializr/ProjectRequestTests.groovy b/initializr/src/test/groovy/io/spring/initializr/ProjectRequestTests.groovy index 8762f839..2166ce17 100644 --- a/initializr/src/test/groovy/io/spring/initializr/ProjectRequestTests.groovy +++ b/initializr/src/test/groovy/io/spring/initializr/ProjectRequestTests.groovy @@ -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() diff --git a/initializr/src/test/groovy/io/spring/initializr/web/MainControllerEnvIntegrationTests.groovy b/initializr/src/test/groovy/io/spring/initializr/web/MainControllerEnvIntegrationTests.groovy index 504faa5b..75379db7 100644 --- a/initializr/src/test/groovy/io/spring/initializr/web/MainControllerEnvIntegrationTests.groovy +++ b/initializr/src/test/groovy/io/spring/initializr/web/MainControllerEnvIntegrationTests.groovy @@ -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') + } + } diff --git a/initializr/src/test/resources/application-test-custom-env.yml b/initializr/src/test/resources/application-test-custom-env.yml index aaace38d..ddce24a7 100644 --- a/initializr/src/test/resources/application-test-custom-env.yml +++ b/initializr/src/test/resources/application-test-custom-env.yml @@ -1,4 +1,7 @@ initializr: env: artifactRepository: https://repo.spring.io/lib-release - forceSsl: false \ No newline at end of file + forceSsl: false + fallbackApplicationName: FooBarApplication + invalidApplicationNames: + - InvalidApplication \ No newline at end of file