diff --git a/gropify-gradle-plugin/src/main/kotlin/com/highcapable/gropify/plugin/DefaultDeployer.kt b/gropify-gradle-plugin/src/main/kotlin/com/highcapable/gropify/plugin/DefaultDeployer.kt index e6c9cc2..95f8122 100644 --- a/gropify-gradle-plugin/src/main/kotlin/com/highcapable/gropify/plugin/DefaultDeployer.kt +++ b/gropify-gradle-plugin/src/main/kotlin/com/highcapable/gropify/plugin/DefaultDeployer.kt @@ -106,7 +106,9 @@ internal object DefaultDeployer { val properties = mutableMapOf() val resolveProperties = mutableMapOf() - config.permanentKeyValues.forEach { (key, value) -> properties[key] = value.createTypeValueByType(config.useTypeAutoConversion) } + config.permanentKeyValues.forEach { (key, value) -> + properties[key] = value.createTypeValueByType(config.useTypeAutoConversion, key) + } config.locations.forEach { location -> when (location) { GropifyLocation.CurrentProject -> createProperties(config, descriptor.currentDir).forEach { resolveProperties.putAll(it) } @@ -136,7 +138,7 @@ internal object DefaultDeployer { } } ?: true }.toMutableMap().also { resolveKeyValues -> - resolveKeyValues.onEach { (key, value) -> + resolveKeyValues.forEach { (key, value) -> val resolveKeys = mutableListOf() fun String.resolveValue(): String = replaceInterpolation { matchKey -> @@ -155,17 +157,25 @@ internal object DefaultDeployer { } if (value.hasInterpolation()) resolveKeyValues[key] = value.resolveValue() - }.takeIf { config.keyValuesRules.isNotEmpty() }?.forEach { (key, value) -> - config.keyValuesRules[key]?.also { resolveKeyValues[key] = it(value) } } properties.putAll(resolveKeyValues.map { (key, value) -> - key to value.createTypeValue(config.useTypeAutoConversion) + key to value.createTypeValue(config.useTypeAutoConversion, key) }) } // Replace all key-values if exists. - config.replacementKeyValues.forEach { (key, value) -> properties[key] = value.createTypeValueByType(config.useTypeAutoConversion) } + config.replacementKeyValues.forEach { (key, value) -> + properties[key] = value.createTypeValueByType(config.useTypeAutoConversion, key) + } + + // Apply key-values rules. + properties.forEach { (key, value) -> + val (mapper, type) = config.keyValuesRules[key] ?: return@forEach + + val resolveValue = mapper(value.raw).createTypeValue(config.useTypeAutoConversion, key, type) + properties[key] = resolveValue + } return properties } diff --git a/gropify-gradle-plugin/src/main/kotlin/com/highcapable/gropify/plugin/extension/dsl/configure/GropifyConfigureExtension.kt b/gropify-gradle-plugin/src/main/kotlin/com/highcapable/gropify/plugin/extension/dsl/configure/GropifyConfigureExtension.kt index de7c177..ef585ab 100644 --- a/gropify-gradle-plugin/src/main/kotlin/com/highcapable/gropify/plugin/extension/dsl/configure/GropifyConfigureExtension.kt +++ b/gropify-gradle-plugin/src/main/kotlin/com/highcapable/gropify/plugin/extension/dsl/configure/GropifyConfigureExtension.kt @@ -30,11 +30,13 @@ import com.highcapable.gropify.plugin.Gropify import com.highcapable.gropify.plugin.config.extension.create import com.highcapable.gropify.plugin.config.proxy.GropifyConfig import com.highcapable.gropify.plugin.config.type.GropifyLocation +import com.highcapable.gropify.plugin.generator.extension.PropertyValueMapper import com.highcapable.gropify.plugin.generator.extension.PropertyValueRule import com.highcapable.gropify.utils.KeywordsDetector import com.highcapable.gropify.utils.extension.isStartsWithLetter import org.gradle.api.Action import org.gradle.api.initialization.Settings +import kotlin.reflect.KClass /** * Configure extension for Gropify. @@ -447,8 +449,13 @@ open class GropifyConfigureExtension internal constructor() { * * ```kotlin * keyValuesRules( - * "some.key1" to createValueRule { if (it.contains("_")) it.replace("_", "-") else it }, - * "some.key2" to createValueRule { "$it-value" } + * "some.key1" to ValueRule { if (it.contains("_")) it.replace("_", "-") else it }, + * "some.key2" to ValueRule { "$it-value" }, + * // You can also specify the expected type class, + * // the type you specify will be used during generation, + * // and an exception will be thrown if the type cannot be converted correctly. + * // If the [useTypeAutoConversion] is not enabled, this parameter will be ignored. + * "some.key3" to ValueRule(Int::class) * ) * ``` * @@ -483,10 +490,13 @@ open class GropifyConfigureExtension internal constructor() { /** * Create a new properties' values rule. + * @param type specify the expected type class, or null to auto-detect, + * if the [useTypeAutoConversion] is not enabled, this parameter will be ignored. * @param rule callback current rule. * @return [PropertyValueRule] */ - fun ValueRule(rule: PropertyValueRule) = rule + @JvmOverloads + fun ValueRule(type: KClass<*>? = null, rule: PropertyValueMapper = { it }) = rule to type /** * Set where to find properties' key-values. diff --git a/gropify-gradle-plugin/src/main/kotlin/com/highcapable/gropify/plugin/generator/extension/Generator.kt b/gropify-gradle-plugin/src/main/kotlin/com/highcapable/gropify/plugin/generator/extension/Generator.kt index 685fff3..8d4a3b4 100644 --- a/gropify-gradle-plugin/src/main/kotlin/com/highcapable/gropify/plugin/generator/extension/Generator.kt +++ b/gropify-gradle-plugin/src/main/kotlin/com/highcapable/gropify/plugin/generator/extension/Generator.kt @@ -22,10 +22,12 @@ package com.highcapable.gropify.plugin.generator.extension import com.highcapable.gropify.utils.extension.underscore +import kotlin.reflect.KClass internal typealias PropertyMap = MutableMap internal typealias PropertyOptimizeMap = MutableMap> -internal typealias PropertyValueRule = (value: String) -> String +internal typealias PropertyValueMapper = (value: String) -> String +internal typealias PropertyValueRule = Pair?> /** * Optimize property keys for code generation. diff --git a/gropify-gradle-plugin/src/main/kotlin/com/highcapable/gropify/plugin/generator/extension/PropertyType.kt b/gropify-gradle-plugin/src/main/kotlin/com/highcapable/gropify/plugin/generator/extension/PropertyType.kt index d82553b..1c818fc 100644 --- a/gropify-gradle-plugin/src/main/kotlin/com/highcapable/gropify/plugin/generator/extension/PropertyType.kt +++ b/gropify-gradle-plugin/src/main/kotlin/com/highcapable/gropify/plugin/generator/extension/PropertyType.kt @@ -22,6 +22,7 @@ package com.highcapable.gropify.plugin.generator.extension import com.highcapable.gropify.internal.error +import com.highcapable.gropify.internal.require import com.highcapable.gropify.plugin.Gropify import com.highcapable.gropify.utils.extension.isNumeric import kotlin.reflect.KClass @@ -30,9 +31,12 @@ import kotlin.reflect.KClass * Create [PropertyTypeValue] from [String] value. * @receiver [String] * @param autoConversion whether to enable auto conversion. + * @param key the property key name. + * @param type specify the expected type class, or null to auto-detect, + * if the [autoConversion] is not `true`, this parameter will be ignored. * @return [PropertyTypeValue] */ -internal fun String.createTypeValue(autoConversion: Boolean): PropertyTypeValue { +internal fun String.createTypeValue(autoConversion: Boolean, key: String, type: KClass<*>? = null): PropertyTypeValue { var isStringType = false val valueString = replace("\n", "\\n") .replace("\r", "\\r") @@ -47,6 +51,56 @@ internal fun String.createTypeValue(autoConversion: Boolean): PropertyTypeValue if (!autoConversion) return PropertyTypeValue(this, "\"$valueString\"", String::class) val trimmed = valueString.trim() + + if (type != null) { + val finalValue = when (type) { + String::class -> "\"$valueString\"" + CharSequence::class -> "\"$valueString\"" + Char::class -> trimmed.firstOrNull()?.let { "'$it'" } + ?: Gropify.error("The \"$key\" value is empty and cannot be converted to Char type.") + Boolean::class -> if (trimmed.toBooleanStrictOrNull() != null) + trimmed + else ((trimmed.toIntOrNull() ?: 0) > 0).toString() + Int::class -> { + val intValue = trimmed.toIntOrNull() + Gropify.require(intValue != null && intValue in Int.MIN_VALUE..Int.MAX_VALUE) { + "The \"$key\" value \"$this\" cannot be converted to Int type." + } + + trimmed + } + Long::class -> { + Gropify.require(trimmed.toLongOrNull() != null) { + "The \"$key\" value \"$this\" cannot be converted to Long type." + } + + if (trimmed.endsWith("L")) trimmed else "${trimmed}L" + } + Double::class -> { + val doubleValue = trimmed.toDoubleOrNull() + Gropify.require(doubleValue != null && !doubleValue.isInfinite()) { + "The \"$key\" value \"$this\" cannot be converted to Double type." + } + + trimmed + } + Float::class -> { + val floatValue = trimmed.toFloatOrNull() + Gropify.require(floatValue != null && !floatValue.isInfinite()) { + "The \"$key\" value \"$this\" cannot be converted to Float type." + } + + if (trimmed.endsWith("f") || trimmed.endsWith("F")) trimmed else "${trimmed}f" + } + else -> Gropify.error( + "Unsupported property \"$key\" value type: ${type.qualifiedName}, " + + "only String, CharSequence, Char, Boolean, Int, Long, Float, Double are supported." + ) + } + + return PropertyTypeValue(this, finalValue, type) + } + val typeSpec = when { isStringType -> String::class trimmed.toBooleanStrictOrNull() != null -> Boolean::class @@ -79,9 +133,10 @@ internal fun String.createTypeValue(autoConversion: Boolean): PropertyTypeValue * Create [PropertyTypeValue] from [Any] value's type. * @receiver [Any] * @param autoConversion whether to enable auto conversion. + * @param key the property key name. * @return [PropertyTypeValue] */ -internal fun Any.createTypeValueByType(autoConversion: Boolean): PropertyTypeValue { +internal fun Any.createTypeValueByType(autoConversion: Boolean, key: String): PropertyTypeValue { val typeSpec = this.javaClass.kotlin val valueString = toString() .replace("\n", "\\n") @@ -94,12 +149,13 @@ internal fun Any.createTypeValueByType(autoConversion: Boolean): PropertyTypeVal val finalValue = if (autoConversion) when (typeSpec) { String::class, CharSequence::class -> "\"$trimmed\"" Char::class -> "'$trimmed'" + Boolean::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." + "Unsupported property \"$key\" value type: ${typeSpec.qualifiedName}, " + + "only String, CharSequence, Char, Boolean, Int, Long, Float, Double are supported." ) } else "\"$valueString\""