refactor: replace Any type with PropertyTypeValue in generator and deployer classes

This commit is contained in:
2025-11-13 20:59:34 +08:00
parent 3552786ae3
commit a884be697c
6 changed files with 143 additions and 79 deletions

View File

@@ -28,6 +28,9 @@ import com.highcapable.gropify.plugin.config.type.GropifyLocation
import com.highcapable.gropify.plugin.deployer.BuildscriptDeployer
import com.highcapable.gropify.plugin.deployer.SourceCodeDeployer
import com.highcapable.gropify.plugin.generator.extension.PropertyMap
import com.highcapable.gropify.plugin.generator.extension.PropertyTypeValue
import com.highcapable.gropify.plugin.generator.extension.createTypeValue
import com.highcapable.gropify.plugin.generator.extension.createTypeValueByType
import com.highcapable.gropify.utils.extension.hasInterpolation
import com.highcapable.gropify.utils.extension.removeSurroundingQuotes
import com.highcapable.gropify.utils.extension.replaceInterpolation
@@ -100,10 +103,10 @@ internal object DefaultDeployer {
* @return [PropertyMap]
*/
fun generateMap(config: GropifyConfig.CommonGenerateConfig, descriptor: ProjectDescriptor): PropertyMap {
val properties = mutableMapOf<String, Any>()
val properties = mutableMapOf<String, PropertyTypeValue>()
val resolveProperties = mutableMapOf<Any?, Any?>()
config.permanentKeyValues.forEach { (key, value) -> properties[key] = value }
config.permanentKeyValues.forEach { (key, value) -> properties[key] = value.createTypeValueByType(config.useTypeAutoConversion) }
config.locations.forEach { location ->
when (location) {
GropifyLocation.CurrentProject -> createProperties(config, descriptor.currentDir).forEach { resolveProperties.putAll(it) }
@@ -156,11 +159,13 @@ internal object DefaultDeployer {
config.keyValuesRules[key]?.also { resolveKeyValues[key] = it(value) }
}
properties.putAll(resolveKeyValues)
properties.putAll(resolveKeyValues.map { (key, value) ->
key to value.createTypeValue(config.useTypeAutoConversion)
})
}
// Replace all key-values if exists.
config.replacementKeyValues.forEach { (key, value) -> properties[key] = value }
config.replacementKeyValues.forEach { (key, value) -> properties[key] = value.createTypeValueByType(config.useTypeAutoConversion) }
return properties
}

View File

@@ -29,7 +29,7 @@ import com.highcapable.gropify.plugin.Gropify
import com.highcapable.gropify.plugin.config.proxy.GropifyConfig
import com.highcapable.gropify.plugin.extension.accessors.proxy.ExtensionAccessors
import com.highcapable.gropify.plugin.generator.extension.PropertyMap
import com.highcapable.gropify.plugin.generator.extension.createTypedValue
import com.highcapable.gropify.plugin.generator.extension.PropertyTypeValue
import com.highcapable.gropify.plugin.generator.extension.toOptimize
import com.highcapable.gropify.plugin.generator.extension.toPoetNoEscape
import com.highcapable.gropify.utils.extension.capitalize
@@ -43,7 +43,6 @@ import com.palantir.javapoet.JavaFile
import com.palantir.javapoet.MethodSpec
import com.palantir.javapoet.TypeSpec
import javax.lang.model.element.Modifier
import kotlin.properties.Delegates
/**
* Generator for buildscript accessors classes.
@@ -70,8 +69,6 @@ internal class BuildscriptGenerator {
}
}
private var config by Delegates.notNull<GropifyConfig.BuildscriptGenerateConfig>()
private val classSpecs = mutableMapOf<String, TypeSpec.Builder>()
private val constructorSpecs = mutableMapOf<String, MethodSpec.Builder>()
private val preAddConstructorSpecNames = mutableListOf<Pair<String, String>>()
@@ -135,16 +132,15 @@ internal class BuildscriptGenerator {
}.build()
)
private fun TypeSpec.Builder.addFinalValueMethod(accessorsName: String, methodName: String, className: String, value: Any) =
private fun TypeSpec.Builder.addFinalValueMethod(accessorsName: String, methodName: String, className: String, value: PropertyTypeValue) =
addMethod(
MethodSpec.methodBuilder("get${getOrCreateUsedSuccessiveMethodName(methodName, className).capitalize()}").apply {
val typedValue = value.createTypedValue(config.useTypeAutoConversion)
val safeValueForJavadoc = typedValue.second.replace("$", "$$")
val safeValueForJavadoc = value.codeValue.replace("$", "$$")
addJavadoc("Resolve the \"$accessorsName\" value \"${value.toString().toPoetNoEscape()}\".")
addJavadoc("Resolve the \"$accessorsName\" value \"${value.raw.toPoetNoEscape()}\".")
addModifiers(Modifier.PUBLIC, Modifier.FINAL)
returns(typedValue.first.java)
returns(value.type.java)
addStatement("return $safeValueForJavadoc")
}.build()
)
@@ -176,7 +172,7 @@ internal class BuildscriptGenerator {
*
* After the parsing is completed, we need to call [releaseParseTypeSpec] to complete the parsing.
*/
private fun parseTypeSpec(successiveName: String, key: String, value: Any) {
private fun parseTypeSpec(successiveName: String, key: String, value: PropertyTypeValue) {
fun String.duplicateGrandSuccessiveIndex() = lowercase().let { name ->
if (grandSuccessiveDuplicateIndexes.contains(name)) {
grandSuccessiveDuplicateIndexes[name] = (grandSuccessiveDuplicateIndexes[name] ?: 1) + 1
@@ -274,8 +270,6 @@ internal class BuildscriptGenerator {
"Class name cannot be empty or blank."
}
this.config = config
val topClassName = "${config.name.replace(":", "_").upperCamelcase()}$TOP_CLASS_SUFFIX_NAME"
memoryExtensionClasses[config.name] = "$ACCESSORS_PACKAGE_NAME.$topClassName"

View File

@@ -27,7 +27,6 @@ import com.highcapable.gropify.plugin.config.proxy.GropifyConfig
import com.highcapable.gropify.plugin.generator.config.GenerateConfig
import com.highcapable.gropify.plugin.generator.config.SourceCodeSpec
import com.highcapable.gropify.plugin.generator.extension.PropertyMap
import com.highcapable.gropify.plugin.generator.extension.createTypedValue
import com.highcapable.gropify.plugin.generator.extension.toOptimize
import com.highcapable.gropify.plugin.generator.extension.toPoetNoEscape
import com.highcapable.gropify.plugin.generator.extension.toPoetSpace
@@ -62,17 +61,18 @@ internal class JavaCodeGenerator {
if (!config.isRestrictedAccessEnabled) addModifiers(Modifier.PUBLIC)
keyValues.toOptimize().toUnderscores().forEach { (key, value) ->
val typedValue = value.second.createTypedValue(config.useTypeAutoConversion)
val currentKey = value.first
val currentValue = value.second
addField(
FieldSpec.builder(typedValue.first.java, key.firstNumberToLetter()).apply {
addJavadoc("Resolve the \"${value.first.toPoetNoEscape()}\" value \"${value.second.toString().toPoetNoEscape()}\".")
FieldSpec.builder(currentValue.type.java, key.firstNumberToLetter()).apply {
addJavadoc("Resolve the \"${currentKey.toPoetNoEscape()}\" value \"${currentValue.raw.toPoetNoEscape()}\".")
if (!config.isRestrictedAccessEnabled)
addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)
else addModifiers(Modifier.STATIC, Modifier.FINAL)
initializer(typedValue.second.toPoetNoEscape().toPoetSpace())
initializer(currentValue.codeValue.toPoetNoEscape().toPoetSpace())
}.build()
)
}

View File

@@ -27,7 +27,6 @@ import com.highcapable.gropify.plugin.config.proxy.GropifyConfig
import com.highcapable.gropify.plugin.generator.config.GenerateConfig
import com.highcapable.gropify.plugin.generator.config.SourceCodeSpec
import com.highcapable.gropify.plugin.generator.extension.PropertyMap
import com.highcapable.gropify.plugin.generator.extension.createTypedValue
import com.highcapable.gropify.plugin.generator.extension.toOptimize
import com.highcapable.gropify.plugin.generator.extension.toPoetNoEscape
import com.highcapable.gropify.plugin.generator.extension.toPoetSpace
@@ -66,14 +65,15 @@ internal class KotlinCodeGenerator {
if (config.isRestrictedAccessEnabled) addModifiers(KModifier.INTERNAL)
keyValues.toOptimize().toUnderscores().forEach { (key, value) ->
val typedValue = value.second.createTypedValue(config.useTypeAutoConversion)
val currentKey = value.first
val currentValue = value.second
addProperty(PropertySpec.builder(key.firstNumberToLetter(), typedValue.first).apply {
addKdoc("Resolve the \"${value.first.toPoetNoEscape()}\" value \"${value.second.toString().toPoetNoEscape()}\".")
addProperty(PropertySpec.builder(key.firstNumberToLetter(), currentValue.type).apply {
addKdoc("Resolve the \"${currentKey.toPoetNoEscape()}\" value \"${currentValue.raw.toPoetNoEscape()}\".")
if (config.isRestrictedAccessEnabled) addModifiers(KModifier.INTERNAL)
addModifiers(KModifier.CONST)
initializer(typedValue.second.toPoetNoEscape().toPoetSpace())
initializer(currentValue.codeValue.toPoetNoEscape().toPoetSpace())
}.build())
}
}.build())

View File

@@ -21,63 +21,12 @@
*/
package com.highcapable.gropify.plugin.generator.extension
import com.highcapable.gropify.utils.extension.isNumeric
import com.highcapable.gropify.utils.extension.underscore
import kotlin.reflect.KClass
internal typealias PropertyMap = MutableMap<String, Any>
internal typealias PropertyOptimizeMap = MutableMap<String, Pair<String, Any>>
internal typealias PropertyMap = MutableMap<String, PropertyTypeValue>
internal typealias PropertyOptimizeMap = MutableMap<String, Pair<String, PropertyTypeValue>>
internal typealias PropertyValueRule = (value: String) -> String
/**
* Create typed value from [Any] value.
* @param autoConversion whether to enable auto conversion.
* @return [Pair]<[KClass], [String]>
*/
internal fun Any.createTypedValue(autoConversion: Boolean): Pair<KClass<*>, String> {
var isStringType = false
val valueString = toString()
.replace("\n", "\\n")
.replace("\r", "\\r")
.replace("\\", "\\\\")
.let {
if (autoConversion && (it.startsWith("\"") && it.endsWith("\"") || it.startsWith("'") && it.endsWith("'"))) {
isStringType = true
it.drop(1).dropLast(1)
} else it.replace("\"", "\\\"")
}
if (!autoConversion) return String::class to "\"$valueString\""
val trimmed = valueString.trim()
val typeSpec = when {
isStringType -> String::class
trimmed.toBooleanStrictOrNull() != null -> Boolean::class
trimmed.isNumeric() ->
if (!trimmed.contains(".")) {
val longValue = trimmed.toLongOrNull()
when (longValue) {
null -> String::class
in Int.MIN_VALUE..Int.MAX_VALUE -> Int::class
else -> Long::class
}
} else {
val doubleValue = trimmed.toDoubleOrNull()
if (doubleValue == null || doubleValue.isInfinite())
String::class
else Double::class
}
else -> String::class
}
val finalValue = when (typeSpec) {
String::class -> "\"$valueString\""
Long::class -> if (trimmed.endsWith("L")) trimmed else "${trimmed}L"
else -> trimmed
}
return typeSpec to finalValue
}
/**
* Optimize property keys for code generation.
* @receiver [PropertyMap]

View File

@@ -0,0 +1,116 @@
/*
* Gropify - A type-safe and modern properties plugin for Gradle.
* Copyright (C) 2019 HighCapable
* https://github.com/HighCapable/Gropify
*
* Apache License Version 2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* This file is created by fankes on 2025/11/13.
*/
package com.highcapable.gropify.plugin.generator.extension
import com.highcapable.gropify.internal.error
import com.highcapable.gropify.plugin.Gropify
import com.highcapable.gropify.utils.extension.isNumeric
import kotlin.reflect.KClass
/**
* Create [PropertyTypeValue] from [String] value.
* @receiver [String]
* @param autoConversion whether to enable auto conversion.
* @return [PropertyTypeValue]
*/
internal fun String.createTypeValue(autoConversion: Boolean): PropertyTypeValue {
var isStringType = false
val valueString = replace("\n", "\\n")
.replace("\r", "\\r")
.replace("\\", "\\\\")
.let {
if (autoConversion && (it.startsWith("\"") && it.endsWith("\"") || it.startsWith("'") && it.endsWith("'"))) {
isStringType = true
it.drop(1).dropLast(1)
} else it.replace("\"", "\\\"")
}
if (!autoConversion) return PropertyTypeValue(this, "\"$valueString\"", String::class)
val trimmed = valueString.trim()
val typeSpec = when {
isStringType -> String::class
trimmed.toBooleanStrictOrNull() != null -> Boolean::class
trimmed.isNumeric() ->
if (!trimmed.contains(".")) {
val longValue = trimmed.toLongOrNull()
when (longValue) {
null -> String::class
in Int.MIN_VALUE..Int.MAX_VALUE -> Int::class
else -> Long::class
}
} else {
val doubleValue = trimmed.toDoubleOrNull()
if (doubleValue == null || doubleValue.isInfinite())
String::class
else Double::class
}
else -> String::class
}
val finalValue = when (typeSpec) {
String::class -> "\"$valueString\""
Long::class -> if (trimmed.endsWith("L")) trimmed else "${trimmed}L"
else -> trimmed
}
return PropertyTypeValue(this, finalValue, typeSpec)
}
/**
* Create [PropertyTypeValue] from [Any] value's type.
* @receiver [Any]
* @param autoConversion whether to enable auto conversion.
* @return [PropertyTypeValue]
*/
internal fun Any.createTypeValueByType(autoConversion: Boolean): PropertyTypeValue {
val typeSpec = this.javaClass.kotlin
val valueString = toString()
.replace("\n", "\\n")
.replace("\r", "\\r")
.replace("\\", "\\\\")
.replace("\"", "\\\"")
val trimmed = valueString.trim()
val finalValue = if (autoConversion) when (typeSpec) {
String::class, CharSequence::class -> "\"$trimmed\""
Char::class -> "'$trimmed'"
Long::class -> if (trimmed.endsWith("L")) trimmed else "${trimmed}L"
Float::class -> if (trimmed.endsWith("f") || trimmed.endsWith("F")) trimmed else "${trimmed}f"
Int::class, Double::class -> trimmed
else -> Gropify.error(
"Unsupported property value type: ${typeSpec.qualifiedName}, " +
"only String, CharSequence, Char, Int, Long, Float, Double are supported."
)
} else "\"$valueString\""
return PropertyTypeValue(this.toString(), finalValue, typeSpec)
}
/**
* Property type value entity.
*/
internal data class PropertyTypeValue(
val raw: String,
val codeValue: String,
val type: KClass<*>
)