diff --git a/initializr-generator-spring/src/main/java/io/spring/initializr/generator/spring/code/kotlin/KotlinProjectGenerationConfiguration.java b/initializr-generator-spring/src/main/java/io/spring/initializr/generator/spring/code/kotlin/KotlinProjectGenerationConfiguration.java index 964297ea7c..fe9f19c804 100644 --- a/initializr-generator-spring/src/main/java/io/spring/initializr/generator/spring/code/kotlin/KotlinProjectGenerationConfiguration.java +++ b/initializr-generator-spring/src/main/java/io/spring/initializr/generator/spring/code/kotlin/KotlinProjectGenerationConfiguration.java @@ -71,8 +71,8 @@ public MainSourceCodeProjectContributor> mainCompilationUnitCustomizers, ObjectProvider> mainSourceCodeCustomizers) { return new MainSourceCodeProjectContributor<>(this.description, KotlinSourceCode::new, - new KotlinSourceCodeWriter(this.indentingWriterFactory), mainApplicationTypeCustomizers, - mainCompilationUnitCustomizers, mainSourceCodeCustomizers); + new KotlinSourceCodeWriter(this.description.getLanguage(), this.indentingWriterFactory), + mainApplicationTypeCustomizers, mainCompilationUnitCustomizers, mainSourceCodeCustomizers); } @Bean @@ -80,8 +80,8 @@ public TestSourceCodeProjectContributor> testApplicationTypeCustomizers, ObjectProvider> testSourceCodeCustomizers) { return new TestSourceCodeProjectContributor<>(this.description, KotlinSourceCode::new, - new KotlinSourceCodeWriter(this.indentingWriterFactory), testApplicationTypeCustomizers, - testSourceCodeCustomizers); + new KotlinSourceCodeWriter(this.description.getLanguage(), this.indentingWriterFactory), + testApplicationTypeCustomizers, testSourceCodeCustomizers); } @Bean diff --git a/initializr-generator/src/main/java/io/spring/initializr/generator/language/Language.java b/initializr-generator/src/main/java/io/spring/initializr/generator/language/Language.java index 6b1e1fde83..98b49808db 100644 --- a/initializr-generator/src/main/java/io/spring/initializr/generator/language/Language.java +++ b/initializr-generator/src/main/java/io/spring/initializr/generator/language/Language.java @@ -24,6 +24,7 @@ * A language in which a generated project can be written. * * @author Andy Wilkinson + * @author Moritz Halbritter */ public interface Language { @@ -50,6 +51,19 @@ public interface Language { */ String sourceFileExtension(); + /** + * Whether the language supports escaping keywords in package declarations. + * @return whether the language supports escaping keywords in package declarations. + */ + boolean supportsEscapingKeywordsInPackage(); + + /** + * Whether the given {@code input} is a keyword. + * @param input the input + * @return whether the input is a keyword + */ + boolean isKeyword(String input); + static Language forId(String id, String jvmVersion) { return SpringFactoriesLoader.loadFactories(LanguageFactory.class, LanguageFactory.class.getClassLoader()) .stream() diff --git a/initializr-generator/src/main/java/io/spring/initializr/generator/language/groovy/GroovyLanguage.java b/initializr-generator/src/main/java/io/spring/initializr/generator/language/groovy/GroovyLanguage.java index 309e1b71f2..90857172ff 100644 --- a/initializr-generator/src/main/java/io/spring/initializr/generator/language/groovy/GroovyLanguage.java +++ b/initializr-generator/src/main/java/io/spring/initializr/generator/language/groovy/GroovyLanguage.java @@ -16,6 +16,8 @@ package io.spring.initializr.generator.language.groovy; +import java.util.Set; + import io.spring.initializr.generator.language.AbstractLanguage; import io.spring.initializr.generator.language.Language; @@ -23,9 +25,17 @@ * Groovy {@link Language}. * * @author Stephane Nicoll + * @author Moritz Halbritter */ public final class GroovyLanguage extends AbstractLanguage { + // See https://docs.groovy-lang.org/latest/html/documentation/#_keywords + private static final Set KEYWORDS = Set.of("abstract", "assert", "break", "case", "catch", "class", "const", + "continue", "def", "default", "do", "else", "enum", "extends", "final", "finally", "for", "goto", "if", + "implements", "import", "instanceof", "interface", "native", "new", "null", "non-sealed", "package", + "public", "protected", "private", "return", "static", "strictfp", "super", "switch", "synchronized", "this", + "threadsafe", "throw", "throws", "transient", "try", "while"); + /** * Groovy {@link Language} identifier. */ @@ -39,4 +49,14 @@ public GroovyLanguage(String jvmVersion) { super(ID, jvmVersion, "groovy"); } + @Override + public boolean supportsEscapingKeywordsInPackage() { + return false; + } + + @Override + public boolean isKeyword(String input) { + return KEYWORDS.contains(input); + } + } diff --git a/initializr-generator/src/main/java/io/spring/initializr/generator/language/java/JavaLanguage.java b/initializr-generator/src/main/java/io/spring/initializr/generator/language/java/JavaLanguage.java index 31068d4f90..0f5d5c86ab 100644 --- a/initializr-generator/src/main/java/io/spring/initializr/generator/language/java/JavaLanguage.java +++ b/initializr-generator/src/main/java/io/spring/initializr/generator/language/java/JavaLanguage.java @@ -16,6 +16,8 @@ package io.spring.initializr.generator.language.java; +import javax.lang.model.SourceVersion; + import io.spring.initializr.generator.language.AbstractLanguage; import io.spring.initializr.generator.language.Language; @@ -40,4 +42,14 @@ public JavaLanguage(String jvmVersion) { super(ID, jvmVersion, "java"); } + @Override + public boolean supportsEscapingKeywordsInPackage() { + return false; + } + + @Override + public boolean isKeyword(String input) { + return SourceVersion.isKeyword(input); + } + } diff --git a/initializr-generator/src/main/java/io/spring/initializr/generator/language/kotlin/KotlinLanguage.java b/initializr-generator/src/main/java/io/spring/initializr/generator/language/kotlin/KotlinLanguage.java index 74e9a63998..4c2dade3a7 100644 --- a/initializr-generator/src/main/java/io/spring/initializr/generator/language/kotlin/KotlinLanguage.java +++ b/initializr-generator/src/main/java/io/spring/initializr/generator/language/kotlin/KotlinLanguage.java @@ -16,6 +16,8 @@ package io.spring.initializr.generator.language.kotlin; +import java.util.Set; + import io.spring.initializr.generator.language.AbstractLanguage; import io.spring.initializr.generator.language.Language; @@ -23,9 +25,17 @@ * Kotlin {@link Language}. * * @author Stephane Nicoll + * @author Moritz Halbritter */ public final class KotlinLanguage extends AbstractLanguage { + // Taken from https://kotlinlang.org/docs/keyword-reference.html#hard-keywords + // except keywords contains `!` or `?` because they should be handled as invalid + // package names already + private static final Set KEYWORDS = Set.of("package", "as", "typealias", "class", "this", "super", "val", + "var", "fun", "for", "null", "true", "false", "is", "in", "throw", "return", "break", "continue", "object", + "if", "try", "else", "while", "do", "when", "interface", "typeof"); + /** * Kotlin {@link Language} identifier. */ @@ -39,4 +49,14 @@ public KotlinLanguage(String jvmVersion) { super(ID, jvmVersion, "kt"); } + @Override + public boolean supportsEscapingKeywordsInPackage() { + return true; + } + + @Override + public boolean isKeyword(String input) { + return KEYWORDS.contains(input); + } + } diff --git a/initializr-generator/src/main/java/io/spring/initializr/generator/language/kotlin/KotlinSourceCodeWriter.java b/initializr-generator/src/main/java/io/spring/initializr/generator/language/kotlin/KotlinSourceCodeWriter.java index b156576c8e..e7def73772 100644 --- a/initializr-generator/src/main/java/io/spring/initializr/generator/language/kotlin/KotlinSourceCodeWriter.java +++ b/initializr-generator/src/main/java/io/spring/initializr/generator/language/kotlin/KotlinSourceCodeWriter.java @@ -40,6 +40,7 @@ import io.spring.initializr.generator.language.CodeBlock; import io.spring.initializr.generator.language.CodeBlock.FormattingOptions; import io.spring.initializr.generator.language.CompilationUnit; +import io.spring.initializr.generator.language.Language; import io.spring.initializr.generator.language.Parameter; import io.spring.initializr.generator.language.SourceCode; import io.spring.initializr.generator.language.SourceCodeWriter; @@ -55,9 +56,12 @@ public class KotlinSourceCodeWriter implements SourceCodeWriter imports = determineImports(compilationUnit); if (!imports.isEmpty()) { @@ -127,6 +131,12 @@ private void writeTo(SourceStructure structure, KotlinCompilationUnit compilatio } } + private String escapeKotlinKeywords(String packageName) { + return Arrays.stream(packageName.split("\\.")) + .map((segment) -> this.language.isKeyword(segment) ? "`" + segment + "`" : segment) + .collect(Collectors.joining(".")); + } + private void writeProperty(IndentingWriter writer, KotlinPropertyDeclaration propertyDeclaration) { writer.println(); writeModifiers(writer, propertyDeclaration.getModifiers()); diff --git a/initializr-generator/src/test/java/io/spring/initializr/generator/language/kotlin/KotlinSourceCodeWriterTests.java b/initializr-generator/src/test/java/io/spring/initializr/generator/language/kotlin/KotlinSourceCodeWriterTests.java index 46ce49c6f2..5e5f21ed5b 100644 --- a/initializr-generator/src/test/java/io/spring/initializr/generator/language/kotlin/KotlinSourceCodeWriterTests.java +++ b/initializr-generator/src/test/java/io/spring/initializr/generator/language/kotlin/KotlinSourceCodeWriterTests.java @@ -55,7 +55,7 @@ class KotlinSourceCodeWriterTests { @TempDir Path directory; - private final KotlinSourceCodeWriter writer = new KotlinSourceCodeWriter( + private final KotlinSourceCodeWriter writer = new KotlinSourceCodeWriter(new KotlinLanguage(), IndentingWriterFactory.withDefaultSettings()); @Test @@ -361,6 +361,54 @@ void functionWithParameterAnnotation() throws IOException { " fun something(@Service service: MyService) {", " }", "", "}"); } + @Test + void reservedKeywordsStartPackageName() throws IOException { + KotlinSourceCode sourceCode = new KotlinSourceCode(); + sourceCode.createCompilationUnit("fun.example.demo", "Test"); + List lines = writeSingleType(sourceCode, "fun/example/demo/Test.kt"); + assertThat(lines).containsExactly("package `fun`.example.demo"); + } + + @Test + void reservedKeywordsMiddlePackageName() throws IOException { + KotlinSourceCode sourceCode = new KotlinSourceCode(); + sourceCode.createCompilationUnit("com.false.demo", "Test"); + List lines = writeSingleType(sourceCode, "com/false/demo/Test.kt"); + assertThat(lines).containsExactly("package com.`false`.demo"); + } + + @Test + void reservedKeywordsEndPackageName() throws IOException { + KotlinSourceCode sourceCode = new KotlinSourceCode(); + sourceCode.createCompilationUnit("com.example.in", "Test"); + List lines = writeSingleType(sourceCode, "com/example/in/Test.kt"); + assertThat(lines).containsExactly("package com.example.`in`"); + } + + @Test + void reservedJavaKeywordsStartPackageName() throws IOException { + KotlinSourceCode sourceCode = new KotlinSourceCode(); + sourceCode.createCompilationUnit("package.fun.example.demo", "Test"); + List lines = writeSingleType(sourceCode, "package/fun/example/demo/Test.kt"); + assertThat(lines).containsExactly("package `package`.`fun`.example.demo"); + } + + @Test + void reservedJavaKeywordsMiddlePackageName() throws IOException { + KotlinSourceCode sourceCode = new KotlinSourceCode(); + sourceCode.createCompilationUnit("com.package.demo", "Test"); + List lines = writeSingleType(sourceCode, "com/package/demo/Test.kt"); + assertThat(lines).containsExactly("package com.`package`.demo"); + } + + @Test + void reservedJavaKeywordsEndPackageName() throws IOException { + KotlinSourceCode sourceCode = new KotlinSourceCode(); + sourceCode.createCompilationUnit("com.example.package", "Test"); + List lines = writeSingleType(sourceCode, "com/example/package/Test.kt"); + assertThat(lines).containsExactly("package com.example.`package`"); + } + private List writeSingleType(KotlinSourceCode sourceCode, String location) throws IOException { Path source = writeSourceCode(sourceCode).resolve(location); try (InputStream stream = Files.newInputStream(source)) { diff --git a/initializr-metadata/src/main/java/io/spring/initializr/metadata/InitializrConfiguration.java b/initializr-metadata/src/main/java/io/spring/initializr/metadata/InitializrConfiguration.java index 2a2e2c0d30..b403fb4f74 100644 --- a/initializr-metadata/src/main/java/io/spring/initializr/metadata/InitializrConfiguration.java +++ b/initializr-metadata/src/main/java/io/spring/initializr/metadata/InitializrConfiguration.java @@ -25,9 +25,8 @@ import java.util.List; import java.util.Map; -import javax.lang.model.SourceVersion; - import com.fasterxml.jackson.annotation.JsonIgnore; +import io.spring.initializr.generator.language.Language; import io.spring.initializr.generator.version.InvalidVersionException; import io.spring.initializr.generator.version.Version; import io.spring.initializr.generator.version.Version.Format; @@ -103,11 +102,12 @@ public String generateApplicationName(String name) { * The package name cannot be cleaned if the specified {@code packageName} is * {@code null} or if it contains an invalid character for a class identifier. * @param packageName the package name + * @param language the project language * @param defaultPackageName the default package name * @return the cleaned package name * @see Env#getInvalidPackageNames() */ - public String cleanPackageName(String packageName, String defaultPackageName) { + public String cleanPackageName(String packageName, Language language, String defaultPackageName) { if (!StringUtils.hasText(packageName)) { return defaultPackageName; } @@ -118,12 +118,16 @@ public String cleanPackageName(String packageName, String defaultPackageName) { if (hasInvalidChar(candidate.replace(".", "")) || this.env.invalidPackageNames.contains(candidate)) { return defaultPackageName; } - if (hasReservedKeyword(candidate)) { - return defaultPackageName; - } - else { - return candidate; + if (!supportsEscapingKeywordsInPackage(language)) { + if (hasReservedKeyword(language, candidate)) { + return defaultPackageName; + } } + return candidate; + } + + private boolean supportsEscapingKeywordsInPackage(Language language) { + return (language != null) ? language.supportsEscapingKeywordsInPackage() : false; } static String cleanPackageName(String packageName) { @@ -165,8 +169,11 @@ private static boolean hasInvalidChar(String text) { return false; } - private static boolean hasReservedKeyword(final String packageName) { - return Arrays.stream(packageName.split("\\.")).anyMatch(SourceVersion::isKeyword); + private static boolean hasReservedKeyword(Language language, String packageName) { + if (language == null) { + return false; + } + return Arrays.stream(packageName.split("\\.")).anyMatch(language::isKeyword); } /** diff --git a/initializr-metadata/src/test/java/io/spring/initializr/metadata/InitializrConfigurationTests.java b/initializr-metadata/src/test/java/io/spring/initializr/metadata/InitializrConfigurationTests.java index 5ff9bf096f..e872de7710 100755 --- a/initializr-metadata/src/test/java/io/spring/initializr/metadata/InitializrConfigurationTests.java +++ b/initializr-metadata/src/test/java/io/spring/initializr/metadata/InitializrConfigurationTests.java @@ -16,6 +16,9 @@ package io.spring.initializr.metadata; +import io.spring.initializr.generator.language.Language; +import io.spring.initializr.generator.language.java.JavaLanguage; +import io.spring.initializr.generator.language.kotlin.KotlinLanguage; import io.spring.initializr.generator.version.Version; import io.spring.initializr.metadata.InitializrConfiguration.Env.Kotlin; import org.junit.jupiter.api.Test; @@ -30,6 +33,10 @@ */ class InitializrConfigurationTests { + private static final JavaLanguage JAVA = new JavaLanguage(); + + private static final Language KOTLIN = new KotlinLanguage(); + private final InitializrConfiguration properties = new InitializrConfiguration(); @Test @@ -119,77 +126,168 @@ void generateApplicationNameAnotherInvalidApplicationName() { @Test void generatePackageNameSimple() { - assertThat(this.properties.cleanPackageName("com.foo", "com.example")).isEqualTo("com.foo"); + assertThat(this.properties.cleanPackageName("com.foo", JAVA, "com.example")).isEqualTo("com.foo"); } @Test void generatePackageNameSimpleUnderscore() { - assertThat(this.properties.cleanPackageName("com.my_foo", "com.example")).isEqualTo("com.my_foo"); + assertThat(this.properties.cleanPackageName("com.my_foo", JAVA, "com.example")).isEqualTo("com.my_foo"); } @Test void generatePackageNameSimpleColon() { - assertThat(this.properties.cleanPackageName("com:foo", "com.example")).isEqualTo("com.foo"); + assertThat(this.properties.cleanPackageName("com:foo", JAVA, "com.example")).isEqualTo("com.foo"); } @Test void generatePackageNameMultipleDashes() { - assertThat(this.properties.cleanPackageName("com.foo--bar", "com.example")).isEqualTo("com.foo__bar"); + assertThat(this.properties.cleanPackageName("com.foo--bar", JAVA, "com.example")).isEqualTo("com.foo__bar"); } @Test void generatePackageNameMultipleSpaces() { - assertThat(this.properties.cleanPackageName(" com foo ", "com.example")).isEqualTo("com.foo"); + assertThat(this.properties.cleanPackageName(" com foo ", JAVA, "com.example")).isEqualTo("com.foo"); } @Test void generatePackageNameNull() { - assertThat(this.properties.cleanPackageName(null, "com.example")).isEqualTo("com.example"); + assertThat(this.properties.cleanPackageName(null, JAVA, "com.example")).isEqualTo("com.example"); } @Test void generatePackageNameDot() { - assertThat(this.properties.cleanPackageName(".", "com.example")).isEqualTo("com.example"); + assertThat(this.properties.cleanPackageName(".", JAVA, "com.example")).isEqualTo("com.example"); } @Test void generatePackageNameWhitespaces() { - assertThat(this.properties.cleanPackageName(" ", "com.example")).isEqualTo("com.example"); + assertThat(this.properties.cleanPackageName(" ", JAVA, "com.example")).isEqualTo("com.example"); } @Test void generatePackageNameInvalidStartCharacter() { - assertThat(this.properties.cleanPackageName("0com.foo", "com.example")).isEqualTo("_com.foo"); + assertThat(this.properties.cleanPackageName("0com.foo", JAVA, "com.example")).isEqualTo("_com.foo"); } @Test void generatePackageNameVersion() { - assertThat(this.properties.cleanPackageName("com.foo.test-1.4.5", "com.example")).isEqualTo("com.foo.test_145"); + assertThat(this.properties.cleanPackageName("com.foo.test-1.4.5", JAVA, "com.example")) + .isEqualTo("com.foo.test_145"); } @Test void generatePackageNameInvalidPackageName() { - assertThat(this.properties.cleanPackageName("org.springframework", "com.example")).isEqualTo("com.example"); + assertThat(this.properties.cleanPackageName("org.springframework", JAVA, "com.example")) + .isEqualTo("com.example"); } @Test void generatePackageNameReservedKeywordsMiddleOfPackageName() { - assertThat(this.properties.cleanPackageName("com.return.foo", "com.example")).isEqualTo("com.example"); + assertThat(this.properties.cleanPackageName("com.return.foo", JAVA, "com.example")).isEqualTo("com.example"); } @Test void generatePackageNameReservedKeywordsStartOfPackageName() { - assertThat(this.properties.cleanPackageName("false.com.foo", "com.example")).isEqualTo("com.example"); + assertThat(this.properties.cleanPackageName("false.com.foo", JAVA, "com.example")).isEqualTo("com.example"); } @Test void generatePackageNameReservedKeywordsEndOfPackageName() { - assertThat(this.properties.cleanPackageName("com.foo.null", "com.example")).isEqualTo("com.example"); + assertThat(this.properties.cleanPackageName("com.foo.null", JAVA, "com.example")).isEqualTo("com.example"); } @Test void generatePackageNameReservedKeywordsEntirePackageName() { - assertThat(this.properties.cleanPackageName("public", "com.example")).isEqualTo("com.example"); + assertThat(this.properties.cleanPackageName("public", JAVA, "com.example")).isEqualTo("com.example"); + } + + @Test + void generateKotlinPackageNameSimple() { + assertThat(this.properties.cleanPackageName("com.foo", KOTLIN, "com.example")).isEqualTo("com.foo"); + } + + @Test + void generateKotlinPackageNameSimpleUnderscore() { + assertThat(this.properties.cleanPackageName("com.my_foo", KOTLIN, "com.example")).isEqualTo("com.my_foo"); + } + + @Test + void generateKotlinPackageNameSimpleColon() { + assertThat(this.properties.cleanPackageName("com:foo", KOTLIN, "com.example")).isEqualTo("com.foo"); + } + + @Test + void generateKotlinPackageNameMultipleDashes() { + assertThat(this.properties.cleanPackageName("com.foo--bar", KOTLIN, "com.example")).isEqualTo("com.foo__bar"); + } + + @Test + void generateKotlinPackageNameMultipleSpaces() { + assertThat(this.properties.cleanPackageName(" com foo ", KOTLIN, "com.example")).isEqualTo("com.foo"); + } + + @Test + void generateKotlinPackageNameNull() { + assertThat(this.properties.cleanPackageName(null, KOTLIN, "com.example")).isEqualTo("com.example"); + } + + @Test + void generateKotlinPackageNameDot() { + assertThat(this.properties.cleanPackageName(".", KOTLIN, "com.example")).isEqualTo("com.example"); + } + + @Test + void generateKotlinPackageNameWhitespaces() { + assertThat(this.properties.cleanPackageName(" ", KOTLIN, "com.example")).isEqualTo("com.example"); + } + + @Test + void generateKotlinPackageNameInvalidStartCharacter() { + assertThat(this.properties.cleanPackageName("0com.foo", KOTLIN, "com.example")).isEqualTo("_com.foo"); + } + + @Test + void generateKotlinPackageNameVersion() { + assertThat(this.properties.cleanPackageName("com.foo.test-1.4.5", KOTLIN, "com.example")) + .isEqualTo("com.foo.test_145"); + } + + @Test + void generateKotlinPackageNameInvalidPackageName() { + assertThat(this.properties.cleanPackageName("org.springframework", KOTLIN, "com.example")) + .isEqualTo("com.example"); + } + + @Test + void generateKotlinPackageNameReservedKeywordsMiddleOfPackageName() { + assertThat(this.properties.cleanPackageName("com.return.foo", KOTLIN, "com.example")) + .isEqualTo("com.return.foo"); + } + + @Test + void generateKotlinPackageNameReservedKeywordsStartOfPackageName() { + assertThat(this.properties.cleanPackageName("false.com.foo", KOTLIN, "com.example")).isEqualTo("false.com.foo"); + } + + @Test + void generateKotlinPackageNameReservedKeywordsEndOfPackageName() { + assertThat(this.properties.cleanPackageName("com.foo.null", KOTLIN, "com.example")).isEqualTo("com.foo.null"); + } + + @Test + void generateKotlinPackageNameReservedChar() { + assertThat(this.properties.cleanPackageName("com._foo.null", KOTLIN, "com.example")).isEqualTo("com._foo.null"); + } + + @Test + void generateKotlinPackageNameJavaReservedKeywords() { + assertThat(this.properties.cleanPackageName("public", KOTLIN, "com.example")).isEqualTo("public"); + } + + @Test + void generateKotlinPackageNameJavaReservedKeywordsEntirePackageName() { + assertThat(this.properties.cleanPackageName("public.package", KOTLIN, "com.example")) + .isEqualTo("public.package"); } @Test diff --git a/initializr-web/src/main/java/io/spring/initializr/web/project/MetadataProjectDescriptionCustomizer.java b/initializr-web/src/main/java/io/spring/initializr/web/project/MetadataProjectDescriptionCustomizer.java index 9ffa6d722c..bb6be1de77 100644 --- a/initializr-web/src/main/java/io/spring/initializr/web/project/MetadataProjectDescriptionCustomizer.java +++ b/initializr-web/src/main/java/io/spring/initializr/web/project/MetadataProjectDescriptionCustomizer.java @@ -65,7 +65,8 @@ else if (targetArtifactId.equals(description.getName())) { description.setName(cleanMavenCoordinate(targetArtifactId, "-")); } description.setPackageName(this.metadata.getConfiguration() - .cleanPackageName(description.getPackageName(), this.metadata.getPackageName().getContent())); + .cleanPackageName(description.getPackageName(), description.getLanguage(), + this.metadata.getPackageName().getContent())); if (description.getPlatformVersion() == null) { description.setPlatformVersion(Version.parse(this.metadata.getBootVersions().getDefault().getId())); }