Skip to content

Commit

Permalink
Fix creator ClassFqn suggestion and validation
Browse files Browse the repository at this point in the history
This catches more invalid FQNs parts
  • Loading branch information
RedNesto committed Aug 23, 2024
1 parent c279880 commit 8443d3b
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 2 deletions.
6 changes: 5 additions & 1 deletion src/main/kotlin/creator/custom/BuiltinValidations.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,13 @@ package com.demonwav.mcdev.creator.custom
import com.demonwav.mcdev.asset.MCDevBundle
import com.demonwav.mcdev.platform.fabric.util.FabricVersions
import com.demonwav.mcdev.util.SemanticVersion
import com.intellij.lang.java.lexer.JavaLexer
import com.intellij.openapi.ui.ComboBox
import com.intellij.openapi.ui.ValidationInfo
import com.intellij.openapi.ui.validation.DialogValidation
import com.intellij.openapi.ui.validation.validationErrorIf
import com.intellij.openapi.util.text.StringUtil
import com.intellij.pom.java.LanguageLevel
import javax.swing.JComponent

object BuiltinValidations {
Expand Down Expand Up @@ -58,7 +60,9 @@ object BuiltinValidations {
}

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

fun byRegex(regex: Regex): DialogValidation.WithParameter<() -> String> =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,14 @@ class SuggestClassNamePropertyDerivation : PreparedDerivation {
override fun derive(parentValues: List<Any?>): Any? {
val coords = parentValues[0] as BuildSystemCoordinates
val name = parentValues[1] as String
return ClassFqn("${coords.groupId}.${name.decapitalize()}.${name.capitalize()}")
val sanitizedName = name.split(NOT_JAVA_IDENTIFIER).joinToString("", transform = String::capitalize)
return ClassFqn("${coords.groupId}.${sanitizedName.decapitalize()}.$sanitizedName")
}

companion object : PropertyDerivationFactory {

private val NOT_JAVA_IDENTIFIER = Regex("\\P{javaJavaIdentifierPart}+")

override fun create(
reporter: TemplateValidationReporter,
parents: List<CreatorProperty<*>?>?,
Expand Down
101 changes: 101 additions & 0 deletions src/test/kotlin/creator/ClassFqnCreatorPropertyTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*
* Minecraft Development for IntelliJ
*
* https://mcdev.io/
*
* Copyright (C) 2024 minecraft-dev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, version 3.0 only.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

package com.demonwav.mcdev.creator

import com.demonwav.mcdev.creator.custom.BuiltinValidations
import com.demonwav.mcdev.creator.custom.TemplateDescriptor
import com.demonwav.mcdev.creator.custom.model.BuildSystemCoordinates
import com.demonwav.mcdev.creator.custom.model.ClassFqn
import com.intellij.openapi.ui.validation.invoke
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertNotNull
import org.junit.jupiter.api.Assertions.assertNull
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test

@DisplayName("Class FQN Creator Property Tests")
class ClassFqnCreatorPropertyTest : CreatorTemplateProcessorTestBase() {

private fun checkValidation(input: String, expectedMessage: String?) {
val validation = BuiltinValidations.validClassFqn { input }
val validationInfo = validation.validate()
if (expectedMessage == null) {
assertNull(validationInfo?.message) { "Expected input to be valid: '$input'" }
} else {
assertNotNull(validationInfo, "Expected input to be invalid: '$input'")
assertEquals(expectedMessage, validationInfo!!.message)
}
}

@Test
@DisplayName("Validation")
fun validation() {
checkValidation("test.TestName", null)
val invalidFqns = listOf(
"test.0InvalidName",
"test.Invalid-Name",
"test.package.InvalidPackage",
"test..InvalidPackage",
"test."
)
for (fqn in invalidFqns) {
checkValidation(fqn, "Must be a valid class fully qualified name")
}
}

@Test
@DisplayName("Class Name Suggestion")
fun classNameSuggestion() {
makeTemplate(
"""
{
"version": ${TemplateDescriptor.FORMAT_VERSION},
"properties": [
{
"name": "BUILD_COORDS",
"type": "build_system_coordinates"
},
{
"name": "NAME",
"type": "string"
},
{
"name": "FQN",
"type": "class_fqn",
"derives": {
"method": "suggestClassName",
"parents": ["BUILD_COORDS", "NAME"]
}
}
]
}
""".trimIndent()
)

val buildCoordsProperty = processor.context.property<BuildSystemCoordinates>("BUILD_COORDS")
val nameProperty = processor.context.property<String>("NAME")
val fqnProperty = processor.context.property<ClassFqn>("FQN")

buildCoordsProperty.graphProperty.set(BuildSystemCoordinates("com.example.project", "example-project", "1.0"))
nameProperty.graphProperty.set("My Project")
assertEquals(ClassFqn("com.example.project.myProject.MyProject"), fqnProperty.get())
}
}

0 comments on commit 8443d3b

Please sign in to comment.