Skip to content

Commit

Permalink
Added support native for Map type
Browse files Browse the repository at this point in the history
  • Loading branch information
gmazzo committed Dec 28, 2023
1 parent 0212c2b commit 09501d0
Show file tree
Hide file tree
Showing 10 changed files with 120 additions and 46 deletions.
13 changes: 10 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ buildConfig {
buildConfigField("FEATURE_ENABLED", true)
buildConfigField("MAGIC_NUMBERS", intArrayOf(1, 2, 3, 4))
buildConfigField("STRING_LIST", arrayOf("a", "b", "c"))
buildConfigField<Map<String, Int>>("MAP", expression("mapOf(\"a\" to 1, \"b\" to 2)"))
buildConfigField("MAP", mapOf("a" to 1, "b" to 2))
buildConfigField("FILE", File("aFile"))
buildConfigField("URI", uri("https://example.io"))
buildConfigField("com.github.gmazzo.buildconfig.demos.kts.SomeData", "DATA", "SomeData(\"a\", 1)")

}
Expand All @@ -43,6 +45,8 @@ internal object BuildConfig {
internal val MAGIC_NUMBERS: IntArray = intArrayOf(1, 2, 3)
internal val STRING_LIST: Array<String> = arrayOf("a", "b", "c")
internal val MAP: Map<String, Int> = mapOf("a" to 1, "b" to 2)
internal val FILE: File = java.io.File("aFile")
internal val URI: URI = java.net.URI.create("https://example.io")
internal val DATA: SomeData = SomeData("a", 1)
}
```
Expand All @@ -64,8 +68,9 @@ buildConfig {
buildConfigField(boolean, 'FEATURE_ENABLED', true)
buildConfigField(int[], "MAGIC_NUMBERS", [1, 2, 3])
buildConfigField('List<String>', "STRING_LIST", ["a", "b", "c"])
buildConfigField("java.util.Map<String, Integer>", "MAP", "java.util.Map.of(\"a\", 1, \"b\", 2)")
buildConfigField(Map.class, "GENERIC_MAP", expression("java.util.Map.of(\"a\", 1, \"b\", 2)"))
buildConfigField(Map.class, "MAP", [a: 1, b: 2])
buildConfigField(File.class, "FILE", new File("aFile"))
buildConfigField(URI.class, "URI", uri("https://example.io"))
buildConfigField("com.github.gmazzo.buildconfig.demos.groovy.SomeData", "DATA", "new SomeData(\"a\", 1)")
}
```
Expand All @@ -82,6 +87,8 @@ final class BuildConfig {
public static final int[] MAGIC_NUMBERS = {1, 2, 3};
public static final String[] STRING_LIST = {"a", "b", "c"};
public static final Map<String, Integer> MAP = java.util.Map.of("a", 1, "b", 2);
public static final File FILE = new java.io.File("aFile");
public static final URI URI = java.net.URI.create("https://example.io");
public static final SomeData DATA = new SomeData("a", 1);
}
```
Expand Down
6 changes: 4 additions & 2 deletions demo-project/groovy-gen-kotlin/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -160,14 +160,16 @@ buildConfig {
buildConfigField('Set<Boolean>', "BOOLEAN_SET_PROVIDER", provider { [true, null, false] })

// custom formats with expressions, including Map and custom types
buildConfigField(Map.class, "MAP", [a: 1, b: 2])
buildConfigField(Map.class, "MAP_PROVIDER", [a: 1, b: 2])
buildConfigField(
"Map<String, Integer>",
"MAP",
"MAP_BY_EXPRESSION",
"mapOf(\"a\" to 1, \"b\" to 2)"
)
buildConfigField(
"Map<String, Integer>",
"MAP_PROVIDER",
"MAP_BY_EXPRESSION_PROVIDER",
provider { "mapOf(\"a\" to 1, \"b\" to 2)" }
)
buildConfigField(Map.class, "MAP_GENERIC", expression("mapOf(\"a\" to 1, \"b\" to 2)"))
Expand Down
6 changes: 4 additions & 2 deletions demo-project/groovy/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -160,14 +160,16 @@ buildConfig {
buildConfigField('Set<Boolean>', "BOOLEAN_SET_PROVIDER", provider { [true, null, false] })

// custom formats with expressions, including Map and custom types
buildConfigField(Map.class, "MAP", [a: 1, b: 2])
buildConfigField(Map.class, "MAP_PROVIDER", [a: 1, b: 2])
buildConfigField(
"java.util.Map<String, Integer>",
"MAP",
"MAP_BY_EXPRESSION",
"java.util.Map.of(\"a\", 1, \"b\", 2)"
)
buildConfigField(
"java.util.Map<String, Integer>",
"MAP_PROVIDER",
"MAP_BY_EXPRESSION_PROVIDER",
provider { "java.util.Map.of(\"a\", 1, \"b\", 2)" }
)
buildConfigField(Map.class, "MAP_GENERIC", expression("java.util.Map.of(\"a\", 1, \"b\", 2)"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,8 @@ public void testBooleans() {
public void testCustomTypes() {
assertEquals(Map.of("a", 1, "b", 2), BuildConfig.MAP);
assertEquals(Map.of("a", 1, "b", 2), BuildConfig.MAP_PROVIDER);
assertEquals(Map.of("a", 1, "b", 2), BuildConfig.MAP_BY_EXPRESSION);
assertEquals(Map.of("a", 1, "b", 2), BuildConfig.MAP_BY_EXPRESSION_PROVIDER);
assertEquals(Map.of("a", 1, "b", 2), BuildConfig.MAP_GENERIC);
assertEquals(Map.of("a", 1, "b", 2), BuildConfig.MAP_GENERIC_PROVIDER);
assertEquals(new File("aFile"), BuildConfig.FILE);
Expand Down
6 changes: 4 additions & 2 deletions demo-project/kts-gen-java/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -157,8 +157,10 @@ buildConfig {
buildConfigField("BOOLEAN_SET_PROVIDER", provider { setOf(true, null, false) })

// custom formats with expressions, including Map and custom types
buildConfigField<Map<String, Int>>("MAP", expression("java.util.Map.of(\"a\", 1, \"b\", 2)"))
buildConfigField<Map<String, Int>>("MAP_PROVIDER", provider { expression("java.util.Map.of(\"a\", 1, \"b\", 2)") })
buildConfigField("MAP", mapOf("a" to 1, "b" to 2))
buildConfigField("MAP_PROVIDER", provider { mapOf("a" to 1, "b" to 2) })
buildConfigField<Map<String, Int>>("MAP_BY_EXPRESSION", expression("java.util.Map.of(\"a\", 1, \"b\", 2)"))
buildConfigField<Map<String, Int>>("MAP_BY_EXPRESSION_PROVIDER", provider { expression("java.util.Map.of(\"a\", 1, \"b\", 2)") })
buildConfigField<Map<*, *>>("MAP_GENERIC", expression("java.util.Map.of(\"a\", 1, \"b\", 2)"))
buildConfigField<Map<*, *>>("MAP_GENERIC_PROVIDER", provider { expression("java.util.Map.of(\"a\", 1, \"b\", 2)") })
buildConfigField("FILE", File("aFile"))
Expand Down
6 changes: 4 additions & 2 deletions demo-project/kts/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -172,8 +172,10 @@ buildConfig {
buildConfigField("BOOLEAN_SET_PROVIDER", provider { setOf(true, null, false) })

// custom formats with expressions, including Map and custom types
buildConfigField<Map<String, Int>>("MAP", expression("mapOf(\"a\" to 1, \"b\" to 2)"))
buildConfigField<Map<String, Int>>("MAP_PROVIDER", provider { expression("mapOf(\"a\" to 1, \"b\" to 2)") })
buildConfigField("MAP", mapOf("a" to 1, "b" to 2))
buildConfigField("MAP_PROVIDER", provider { mapOf("a" to 1, "b" to 2) })
buildConfigField<Map<String, Int>>("MAP_BY_EXPRESSION", expression("mapOf(\"a\" to 1, \"b\" to 2)"))
buildConfigField<Map<String, Int>>("MAP_BY_EXPRESSION_PROVIDER", provider { expression("mapOf(\"a\" to 1, \"b\" to 2)") })
buildConfigField<Map<*, *>>("MAP_GENERIC", expression("mapOf(\"a\" to 1, \"b\" to 2)"))
buildConfigField<Map<*, *>>("MAP_GENERIC_PROVIDER", provider { expression("mapOf(\"a\" to 1, \"b\" to 2)") })
buildConfigField("FILE", File("aFile"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ internal val Any?.elements: List<Any?>
is DoubleArray -> toList()
is BooleanArray -> toList()
is Collection<*> -> toList()
is Map<*, *> -> entries.toList()
else -> listOf(this)
}

Expand All @@ -158,5 +159,6 @@ internal fun Any?.asVarArg(): Array<*> = when (this) {
is BooleanArray -> toTypedArray()
is List<*> -> toTypedArray()
is Iterable<*> -> toList().toTypedArray()
is Map<*, *> -> entries.asSequence().flatMap { (k, v) -> sequenceOf(k, v) }.toList().toTypedArray()
else -> arrayOf(this)
}
Original file line number Diff line number Diff line change
Expand Up @@ -129,35 +129,50 @@ data class BuildConfigJavaGenerator(
else -> "\$L"
}

fun List<Any?>.format(prefix: String, postfix: String, item: (Any) -> TypeName) = joinToString(
fun List<Any?>.format(prefix: String, postfix: String, elementType: TypeName?) = joinToString(
prefix = prefix,
separator = ", ",
postfix = postfix,
transform = { it?.let(item).format() }
transform = { it?.let { elementType ?: TypeName.get(it::class.java) }.format() }
) to size

val elements = forValue.elements

fun singleFormat() =
elements.single()?.let { TypeName.get(it::class.java) }.format() to 1

fun arrayFormat(item: (Any) -> TypeName) =
elements.format("{", "}", item)
fun listFormat(elementType: TypeName?) =
elements.format("java.util.Arrays.asList(", ")", elementType)

fun listFormat(item: (Any) -> TypeName) =
elements.format("java.util.Arrays.asList(", ")", item)
fun setFormat(elementType: TypeName?) =
elements.format("new java.util.LinkedHashSet<>(java.util.Arrays.asList(", "))", elementType)

fun setFormat(item: (Any) -> TypeName) =
elements.format("new java.util.LinkedHashSet<>(java.util.Arrays.asList(", "))", item)
fun mapFormat(keyType: TypeName?, valueType: TypeName?) =
elements.joinToString(
prefix = "java.util.Map.of(",
separator = ", ",
postfix = ")",
transform = {
val (key, value) = (it as Map.Entry<Any?, Any?>)
val keyFormat = (keyType ?: key?.let { TypeName.get(key::class.java) }).format()
val valueFormat = (valueType ?: value?.let { TypeName.get(value::class.java) }).format()

"$keyFormat, $valueFormat"
}
) to elements.size * 2

return when (this) {
TypeName.LONG, ClassName.get(String::class.java) -> singleFormat()
is ArrayTypeName -> arrayFormat { componentType }
LIST, GENERIC_LIST -> listFormat { TypeName.get(it::class.java) }
SET, GENERIC_SET -> setFormat { TypeName.get(it::class.java) }
is ArrayTypeName ->elements.format("{", "}", componentType)
LIST, GENERIC_LIST -> listFormat(null)
SET, GENERIC_SET -> setFormat(null)
MAP, GENERIC_MAP -> mapFormat(null, null)
is ParameterizedTypeName -> when (rawType) {
LIST, GENERIC_LIST -> listFormat { typeArguments.first() }
SET, GENERIC_SET -> setFormat { typeArguments.first() }
LIST, GENERIC_LIST -> listFormat(typeArguments[0].takeIf { it.isBoxedPrimitive })
SET, GENERIC_SET -> setFormat(typeArguments[0].takeIf { it.isBoxedPrimitive })
MAP, GENERIC_MAP -> mapFormat(
typeArguments[0].takeIf { it.isBoxedPrimitive },
typeArguments[1].takeIf { it.isBoxedPrimitive })
else -> singleFormat()
}

Expand All @@ -169,10 +184,12 @@ data class BuildConfigJavaGenerator(
private val STRING = ClassName.get(String::class.java)
private val LIST = ClassName.get(List::class.java)
private val SET = ClassName.get(Set::class.java)
private val MAP = ClassName.get(Map::class.java)
private val FILE = ClassName.get(File::class.java)
private val URI = ClassName.get(URI::class.java)
private val GENERIC_LIST = ClassName.get("", "List")
private val GENERIC_SET = ClassName.get("", "Set")
private val GENERIC_MAP = ClassName.get("", "Map")
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import com.squareup.kotlinpoet.KModifier
import com.squareup.kotlinpoet.LIST
import com.squareup.kotlinpoet.LONG
import com.squareup.kotlinpoet.LONG_ARRAY
import com.squareup.kotlinpoet.MAP
import com.squareup.kotlinpoet.ParameterizedTypeName
import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
import com.squareup.kotlinpoet.PropertySpec
Expand Down Expand Up @@ -149,44 +150,60 @@ data class BuildConfigKotlinGenerator(
else -> "%L"
}

fun List<Any?>.format(function: String, item: (Any) -> TypeName) = joinToString(
fun List<Any?>.format(function: String, elementType: TypeName?) = joinToString(
prefix = "$function(",
separator = ", ",
postfix = ")",
transform = { it?.let(item).format() }
transform = { it?.let { elementType ?: it::class.asTypeName() }.format() }
) to size

val elements = forValue.elements

fun singleFormat() =
elements.single()?.let { it::class.asTypeName() }.format() to 1

fun arrayFormat(item: (Any) -> TypeName) =
elements.format("arrayOf", item)
fun arrayFormat(elementType: TypeName?) =
elements.format("arrayOf", elementType)

fun listFormat(item: (Any) -> TypeName) =
elements.format("listOf", item)
fun listFormat(elementType: TypeName?) =
elements.format("listOf", elementType)

fun setFormat(item: (Any) -> TypeName) =
elements.format("setOf", item)
fun setFormat(elementType: TypeName?) =
elements.format("setOf", elementType)

fun mapFormat(keyType: TypeName?, valueType: TypeName?) =
elements.joinToString(
prefix = "mapOf(",
separator = ", ",
postfix = ")",
transform = {
val (key, value) = (it as Map.Entry<Any?, Any?>)
val keyFormat = (keyType ?: key?.let { key::class.asTypeName() }).format()
val valueFormat = (valueType ?: value?.let { value::class.asTypeName() }).format()

"$keyFormat to $valueFormat"
}
) to elements.size * 2

return when (val nonNullable = copy(nullable = false)) {
LONG, STRING -> singleFormat()
ARRAY -> arrayFormat { it::class.asTypeName() }
BYTE_ARRAY -> elements.format("byteArrayOf") { BYTE }
SHORT_ARRAY -> elements.format("shortArrayOf") { SHORT }
CHAR_ARRAY -> elements.format("charArrayOf") { CHAR }
INT_ARRAY -> elements.format("intArrayOf") { INT }
LONG_ARRAY -> elements.format("longArrayOf") { LONG }
FLOAT_ARRAY -> elements.format("floatArrayOf") { FLOAT }
DOUBLE_ARRAY -> elements.format("doubleArrayOf") { DOUBLE }
BOOLEAN_ARRAY -> elements.format("booleanArrayOf") { BOOLEAN }
LIST, GENERIC_LIST -> listFormat { it::class.asTypeName() }
SET, GENERIC_SET -> setFormat { it::class.asTypeName() }
ARRAY -> arrayFormat(null)
BYTE_ARRAY -> elements.format("byteArrayOf", BYTE)
SHORT_ARRAY -> elements.format("shortArrayOf", SHORT)
CHAR_ARRAY -> elements.format("charArrayOf", CHAR)
INT_ARRAY -> elements.format("intArrayOf", INT)
LONG_ARRAY -> elements.format("longArrayOf", LONG)
FLOAT_ARRAY -> elements.format("floatArrayOf", FLOAT)
DOUBLE_ARRAY -> elements.format("doubleArrayOf", DOUBLE)
BOOLEAN_ARRAY -> elements.format("booleanArrayOf", BOOLEAN)
LIST, GENERIC_LIST -> listFormat(null)
SET, GENERIC_SET -> setFormat(null)
MAP, GENERIC_MAP -> mapFormat(null, null)
is ParameterizedTypeName -> when (nonNullable.rawType) {
ARRAY -> arrayFormat { nonNullable.typeArguments.first() }
LIST, GENERIC_LIST -> listFormat { nonNullable.typeArguments.first() }
SET, GENERIC_SET -> setFormat { nonNullable.typeArguments.first() }
ARRAY -> arrayFormat(nonNullable.typeArguments[0])
LIST, GENERIC_LIST -> listFormat(nonNullable.typeArguments[0])
SET, GENERIC_SET -> setFormat(nonNullable.typeArguments[0])
MAP, GENERIC_MAP -> mapFormat(nonNullable.typeArguments[0], nonNullable.typeArguments[1])
else -> singleFormat()
}

Expand All @@ -204,6 +221,7 @@ data class BuildConfigKotlinGenerator(
private val CONST_TYPES = setOf(STRING, BOOLEAN, BYTE, SHORT, INT, LONG, CHAR, FLOAT, DOUBLE)
private val GENERIC_LIST = ClassName("", "List")
private val GENERIC_SET = ClassName("", "Set")
private val GENERIC_MAP = ClassName("", "Map")
private val FILE = File::class.asClassName()
private val URI = URI::class.asClassName()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,16 @@ inline fun <reified Type : Serializable?> BuildConfigClassSpec.buildConfigField(
it.value(LinkedHashSet(value))
})

@BuildConfigDsl
@JvmName("buildConfigFieldMap")
inline fun <reified Key : Serializable?, reified Value : Serializable?> BuildConfigClassSpec.buildConfigField(
name: String,
value: Map<Key, Value>,
) = buildConfigField(name, Action {
it.type(typeOf<Map<Key, Value>>())
it.value(LinkedHashMap(value))
})

@BuildConfigDsl
@JvmName("buildConfigFieldArray")
inline fun <reified Type : Serializable?> BuildConfigClassSpec.buildConfigField(
Expand Down Expand Up @@ -116,3 +126,13 @@ inline fun <reified Type : Serializable?> BuildConfigClassSpec.buildConfigField(
it.type(typeOf<Set<Type>>())
it.value(value.map(::LinkedHashSet))
})

@BuildConfigDsl
@JvmName("buildConfigFieldMap")
inline fun <reified Key : Serializable?, reified Value : Serializable?> BuildConfigClassSpec.buildConfigField(
name: String,
value: Provider<out Map<Key, Value>>,
) = buildConfigField(name, Action {
it.type(typeOf<Map<Key, Value>>())
it.value(value.map(::LinkedHashMap))
})

0 comments on commit 09501d0

Please sign in to comment.