Skip to content

Commit 18e6c1f

Browse files
committed
split out type detectin
1 parent 9113f0a commit 18e6c1f

File tree

6 files changed

+87
-87
lines changed

6 files changed

+87
-87
lines changed

TODO.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ Ordered roughly in priority:
88
- General cleanup of weird/disgusting code areas
99
- Address all FIXME/TODOs
1010
- Write Contributing guide
11-
- Setup CI
1211

1312
- Make website tool prettier
1413
- Throttle website tool/prepare for HN

core/src/main/kotlin/com/fractalwrench/json2kotlin/ClassTypeHolder.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import java.util.*
77
internal class ClassTypeHolder(val delegate: SourceBuildDelegate, groupingStrategy: GroupingStrategy) { // TODO rename, bad ontology
88

99
internal val stack = Stack<TypeSpec>()
10-
private val jsonProcessor = JsonProcessor()
10+
private val jsonProcessor = JsonProcessor(JsonTypeDetector())
1111
private val jsonFieldGrouper = JsonFieldGrouper(groupingStrategy)
1212

1313

@@ -38,7 +38,7 @@ internal class ClassTypeHolder(val delegate: SourceBuildDelegate, groupingStrate
3838
/**
3939
* Processes a single level in the tree
4040
*/
41-
fun processTreeLevel(levelQueue: LinkedList<TypedJsonElement>) {
41+
private fun processTreeLevel(levelQueue: LinkedList<TypedJsonElement>) {
4242
val fieldValues = levelQueue.filter { it.isJsonObject }.toMutableList()
4343

4444
jsonFieldGrouper.groupCommonFieldValues(fieldValues)

core/src/main/kotlin/com/fractalwrench/json2kotlin/GsonBuildDelegate.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ import com.squareup.kotlinpoet.TypeSpec
77

88
class GsonBuildDelegate: SourceBuildDelegate {
99

10-
// TODO pattern compilation
11-
// FIXME pattern super hacky
10+
private val regex = "%".toRegex()
11+
1212
// TODO should pass TypedJsonElement (and as much info as possible,
1313
// maybe in a wrapper class e.g. PropBuildParams/ClassBuildParams)
1414

@@ -17,7 +17,7 @@ class GsonBuildDelegate: SourceBuildDelegate {
1717
jsonKey: String?) {
1818
if (kotlinIdentifier != jsonKey && jsonKey != null) {
1919
val serializedNameBuilder = AnnotationSpec.builder(SerializedName::class)
20-
serializedNameBuilder.addMember("value=\"${jsonKey.replace("%".toRegex(), "%%")}\"", "")
20+
serializedNameBuilder.addMember("value=\"${jsonKey.replace(regex, "%%")}\"", "")
2121
propertyBuilder.addAnnotation(serializedNameBuilder.build())
2222
}
2323
}

core/src/main/kotlin/com/fractalwrench/json2kotlin/JsonProcessor.kt

Lines changed: 3 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,13 @@
11
package com.fractalwrench.json2kotlin
22

3-
import com.google.gson.JsonArray
43
import com.google.gson.JsonElement
5-
import com.google.gson.JsonObject
6-
import com.google.gson.JsonPrimitive
74
import com.squareup.kotlinpoet.*
85
import java.util.HashMap
9-
import java.util.HashSet
106

117

12-
internal class JsonProcessor { // TODO crappy name
8+
internal class JsonProcessor(private val typeDetector: JsonTypeDetector) { // TODO crappy name
139

14-
internal val jsonElementMap = HashMap<JsonElement, TypeSpec>() // FIXME feels wrong having this exposed
10+
internal val jsonElementMap = HashMap<JsonElement, TypeSpec>() // FIXME feels wrong having this exposed, return instead?
1511

1612
// FIXME should take TypedJsonElement rather than String as a param!
1713
fun findDistinctTypesForFields(fields: Collection<String>,
@@ -28,83 +24,10 @@ internal class JsonProcessor { // TODO crappy name
2824
private fun findDistinctTypesForField(commonElements: List<TypedJsonElement>, key: String): List<TypeName?> {
2925
return commonElements.map {
3026
val fieldValue = it.asJsonObject.get(key)
31-
if (fieldValue != null) typeForJsonField(fieldValue, key) else null
27+
if (fieldValue != null) typeDetector.typeForJsonField(fieldValue, key, jsonElementMap) else null
3228
}.distinct()
3329
}
3430

35-
36-
37-
// TODO: refactor all the (simple) type deduction methods. They can take an extra Map parameter. This will greatly simplify any unit
38-
// testing and pass Single-Responsibility test
39-
40-
41-
private fun typeForJsonField(jsonElement: JsonElement, key: String): TypeName {
42-
with(jsonElement) {
43-
return when {
44-
isJsonPrimitive -> typeForJsonPrimitive(asJsonPrimitive)
45-
isJsonArray -> typeForJsonArray(asJsonArray, key)
46-
isJsonObject -> typeForJsonObject(asJsonObject, key)
47-
isJsonNull -> Any::class.asTypeName().asNullable()
48-
else -> throw IllegalStateException("Expected a JSON value")
49-
}
50-
}
51-
}
52-
53-
private fun typeForJsonPrimitive(primitive: JsonPrimitive): TypeName {
54-
return when {
55-
primitive.isBoolean -> Boolean::class
56-
primitive.isNumber -> Number::class
57-
primitive.isString -> String::class
58-
else -> throw IllegalStateException("No type found for JSON primitive " + primitive)
59-
}.asTypeName()
60-
}
61-
62-
63-
// FIXME feels really messy from here on out
64-
65-
66-
private fun typeForJsonObject(jsonObject: JsonObject, key: String): TypeName {
67-
val existingTypeName = jsonElementMap[jsonObject]
68-
if (existingTypeName != null) {
69-
return ClassName.bestGuess(existingTypeName.name!!)
70-
}
71-
72-
val identifier = key.toKotlinIdentifier().capitalize() // FIXME check symbol pool!
73-
return ClassName.bestGuess(identifier)
74-
}
75-
76-
private fun typeForJsonArray(jsonArray: JsonArray, key: String): TypeName {
77-
val arrayTypes = HashSet<TypeName>()
78-
var nullable = false
79-
80-
jsonArray.withIndex().forEach {
81-
val sanitisedName = key.toKotlinIdentifier() // FIXME check symbol pool!
82-
with(it.value) {
83-
when {
84-
isJsonPrimitive -> arrayTypes.add(typeForJsonPrimitive(asJsonPrimitive))
85-
isJsonArray -> arrayTypes.add(typeForJsonArray(asJsonArray, nameForArrayField(it.index, sanitisedName)))
86-
isJsonObject -> arrayTypes.add(typeForJsonObject(asJsonObject, nameForArrayField(it.index, sanitisedName)))
87-
isJsonNull -> nullable = true
88-
else -> throw IllegalStateException("Unexpected state in array")
89-
}
90-
}
91-
}
92-
val arrayType = deduceArrayType(arrayTypes, nullable)
93-
return ParameterizedTypeName.get(Array<Any>::class.asClassName(), arrayType)
94-
}
95-
96-
private fun deduceArrayType(arrayTypes: HashSet<TypeName>, nullable: Boolean): TypeName {
97-
val hasMultipleType = arrayTypes.size > 1 || arrayTypes.isEmpty()
98-
val arrayTypeName = when {
99-
hasMultipleType -> Any::class.asTypeName()
100-
else -> arrayTypes.asIterable().first()
101-
}
102-
return when {
103-
nullable -> arrayTypeName.asNullable()
104-
else -> arrayTypeName
105-
}
106-
}
107-
10831
/**
10932
* Determines a single type which fits multiple types.
11033
*/
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package com.fractalwrench.json2kotlin
2+
3+
import com.google.gson.JsonArray
4+
import com.google.gson.JsonElement
5+
import com.google.gson.JsonObject
6+
import com.google.gson.JsonPrimitive
7+
import com.squareup.kotlinpoet.*
8+
import java.util.HashSet
9+
10+
internal class JsonTypeDetector {
11+
12+
internal fun typeForJsonField(jsonElement: JsonElement, key: String, jsonElementMap: Map<JsonElement, TypeSpec>): TypeName {
13+
with(jsonElement) {
14+
return when {
15+
isJsonPrimitive -> typeForJsonPrimitive(asJsonPrimitive)
16+
isJsonArray -> typeForJsonArray(asJsonArray, key, jsonElementMap)
17+
isJsonObject -> typeForJsonObject(asJsonObject, key, jsonElementMap)
18+
isJsonNull -> Any::class.asTypeName().asNullable()
19+
else -> throw IllegalStateException("Expected a JSON value")
20+
}
21+
}
22+
}
23+
24+
internal fun typeForJsonPrimitive(primitive: JsonPrimitive): TypeName {
25+
return when {
26+
primitive.isBoolean -> Boolean::class
27+
primitive.isNumber -> Number::class
28+
primitive.isString -> String::class
29+
else -> throw IllegalStateException("No type found for JSON primitive " + primitive)
30+
}.asTypeName()
31+
}
32+
33+
34+
// FIXME feels really messy from here on out
35+
36+
37+
internal fun typeForJsonObject(jsonObject: JsonObject, key: String, jsonElementMap: Map<JsonElement, TypeSpec>): TypeName {
38+
val existingTypeName = jsonElementMap[jsonObject]
39+
if (existingTypeName != null) {
40+
return ClassName.bestGuess(existingTypeName.name!!)
41+
}
42+
43+
val identifier = key.toKotlinIdentifier().capitalize() // FIXME check symbol pool!
44+
return ClassName.bestGuess(identifier)
45+
}
46+
47+
internal fun typeForJsonArray(jsonArray: JsonArray, key: String, jsonElementMap: Map<JsonElement, TypeSpec>): TypeName {
48+
val arrayTypes = HashSet<TypeName>()
49+
var nullable = false
50+
51+
jsonArray.withIndex().forEach {
52+
val sanitisedName = key.toKotlinIdentifier() // FIXME check symbol pool!
53+
with(it.value) {
54+
when {
55+
isJsonPrimitive -> arrayTypes.add(typeForJsonPrimitive(asJsonPrimitive))
56+
isJsonArray -> arrayTypes.add(typeForJsonArray(asJsonArray, nameForArrayField(it.index, sanitisedName), jsonElementMap))
57+
isJsonObject -> arrayTypes.add(typeForJsonObject(asJsonObject, nameForArrayField(it.index, sanitisedName), jsonElementMap))
58+
isJsonNull -> nullable = true
59+
else -> throw IllegalStateException("Unexpected state in array")
60+
}
61+
}
62+
}
63+
val arrayType = deduceArrayType(arrayTypes, nullable)
64+
return ParameterizedTypeName.get(Array<Any>::class.asClassName(), arrayType)
65+
}
66+
67+
internal fun deduceArrayType(arrayTypes: HashSet<TypeName>, nullable: Boolean): TypeName {
68+
val hasMultipleType = arrayTypes.size > 1 || arrayTypes.isEmpty()
69+
val arrayTypeName = when {
70+
hasMultipleType -> Any::class.asTypeName()
71+
else -> arrayTypes.asIterable().first()
72+
}
73+
return when {
74+
nullable -> arrayTypeName.asNullable()
75+
else -> arrayTypeName
76+
}
77+
}
78+
}

core/src/main/kotlin/com/fractalwrench/json2kotlin/TypedJsonElement.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import com.google.gson.*
44
import java.math.BigDecimal
55
import java.math.BigInteger
66

7-
internal class TypedJsonElement : JsonElement {
7+
class TypedJsonElement : JsonElement {
88

99
val jsonElement: JsonElement
1010
val jsonKey: String

0 commit comments

Comments
 (0)