Skip to content

Commit 8443d3b

Browse files
committed
Fix creator ClassFqn suggestion and validation
This catches more invalid FQNs parts
1 parent c279880 commit 8443d3b

File tree

3 files changed

+110
-2
lines changed

3 files changed

+110
-2
lines changed

src/main/kotlin/creator/custom/BuiltinValidations.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,13 @@ package com.demonwav.mcdev.creator.custom
2323
import com.demonwav.mcdev.asset.MCDevBundle
2424
import com.demonwav.mcdev.platform.fabric.util.FabricVersions
2525
import com.demonwav.mcdev.util.SemanticVersion
26+
import com.intellij.lang.java.lexer.JavaLexer
2627
import com.intellij.openapi.ui.ComboBox
2728
import com.intellij.openapi.ui.ValidationInfo
2829
import com.intellij.openapi.ui.validation.DialogValidation
2930
import com.intellij.openapi.ui.validation.validationErrorIf
3031
import com.intellij.openapi.util.text.StringUtil
32+
import com.intellij.pom.java.LanguageLevel
3133
import javax.swing.JComponent
3234

3335
object BuiltinValidations {
@@ -58,7 +60,9 @@ object BuiltinValidations {
5860
}
5961

6062
val validClassFqn = validationErrorIf<String>(MCDevBundle("creator.validation.class_fqn")) {
61-
it.isBlank() || it.split('.').any { part -> !StringUtil.isJavaIdentifier(part) }
63+
it.isBlank() || it.split('.').any { part ->
64+
!StringUtil.isJavaIdentifier(part) || JavaLexer.isKeyword(part, LanguageLevel.HIGHEST)
65+
}
6266
}
6367

6468
fun byRegex(regex: Regex): DialogValidation.WithParameter<() -> String> =

src/main/kotlin/creator/custom/derivation/SuggestClassNamePropertyDerivation.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,14 @@ class SuggestClassNamePropertyDerivation : PreparedDerivation {
3333
override fun derive(parentValues: List<Any?>): Any? {
3434
val coords = parentValues[0] as BuildSystemCoordinates
3535
val name = parentValues[1] as String
36-
return ClassFqn("${coords.groupId}.${name.decapitalize()}.${name.capitalize()}")
36+
val sanitizedName = name.split(NOT_JAVA_IDENTIFIER).joinToString("", transform = String::capitalize)
37+
return ClassFqn("${coords.groupId}.${sanitizedName.decapitalize()}.$sanitizedName")
3738
}
3839

3940
companion object : PropertyDerivationFactory {
4041

42+
private val NOT_JAVA_IDENTIFIER = Regex("\\P{javaJavaIdentifierPart}+")
43+
4144
override fun create(
4245
reporter: TemplateValidationReporter,
4346
parents: List<CreatorProperty<*>?>?,
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/*
2+
* Minecraft Development for IntelliJ
3+
*
4+
* https://mcdev.io/
5+
*
6+
* Copyright (C) 2024 minecraft-dev
7+
*
8+
* This program is free software: you can redistribute it and/or modify
9+
* it under the terms of the GNU Lesser General Public License as published
10+
* by the Free Software Foundation, version 3.0 only.
11+
*
12+
* This program is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU Lesser General Public License
18+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
19+
*/
20+
21+
package com.demonwav.mcdev.creator
22+
23+
import com.demonwav.mcdev.creator.custom.BuiltinValidations
24+
import com.demonwav.mcdev.creator.custom.TemplateDescriptor
25+
import com.demonwav.mcdev.creator.custom.model.BuildSystemCoordinates
26+
import com.demonwav.mcdev.creator.custom.model.ClassFqn
27+
import com.intellij.openapi.ui.validation.invoke
28+
import org.junit.jupiter.api.Assertions.assertEquals
29+
import org.junit.jupiter.api.Assertions.assertNotNull
30+
import org.junit.jupiter.api.Assertions.assertNull
31+
import org.junit.jupiter.api.DisplayName
32+
import org.junit.jupiter.api.Test
33+
34+
@DisplayName("Class FQN Creator Property Tests")
35+
class ClassFqnCreatorPropertyTest : CreatorTemplateProcessorTestBase() {
36+
37+
private fun checkValidation(input: String, expectedMessage: String?) {
38+
val validation = BuiltinValidations.validClassFqn { input }
39+
val validationInfo = validation.validate()
40+
if (expectedMessage == null) {
41+
assertNull(validationInfo?.message) { "Expected input to be valid: '$input'" }
42+
} else {
43+
assertNotNull(validationInfo, "Expected input to be invalid: '$input'")
44+
assertEquals(expectedMessage, validationInfo!!.message)
45+
}
46+
}
47+
48+
@Test
49+
@DisplayName("Validation")
50+
fun validation() {
51+
checkValidation("test.TestName", null)
52+
val invalidFqns = listOf(
53+
"test.0InvalidName",
54+
"test.Invalid-Name",
55+
"test.package.InvalidPackage",
56+
"test..InvalidPackage",
57+
"test."
58+
)
59+
for (fqn in invalidFqns) {
60+
checkValidation(fqn, "Must be a valid class fully qualified name")
61+
}
62+
}
63+
64+
@Test
65+
@DisplayName("Class Name Suggestion")
66+
fun classNameSuggestion() {
67+
makeTemplate(
68+
"""
69+
{
70+
"version": ${TemplateDescriptor.FORMAT_VERSION},
71+
"properties": [
72+
{
73+
"name": "BUILD_COORDS",
74+
"type": "build_system_coordinates"
75+
},
76+
{
77+
"name": "NAME",
78+
"type": "string"
79+
},
80+
{
81+
"name": "FQN",
82+
"type": "class_fqn",
83+
"derives": {
84+
"method": "suggestClassName",
85+
"parents": ["BUILD_COORDS", "NAME"]
86+
}
87+
}
88+
]
89+
}
90+
""".trimIndent()
91+
)
92+
93+
val buildCoordsProperty = processor.context.property<BuildSystemCoordinates>("BUILD_COORDS")
94+
val nameProperty = processor.context.property<String>("NAME")
95+
val fqnProperty = processor.context.property<ClassFqn>("FQN")
96+
97+
buildCoordsProperty.graphProperty.set(BuildSystemCoordinates("com.example.project", "example-project", "1.0"))
98+
nameProperty.graphProperty.set("My Project")
99+
assertEquals(ClassFqn("com.example.project.myProject.MyProject"), fqnProperty.get())
100+
}
101+
}

0 commit comments

Comments
 (0)