Skip to content

Commit d28250c

Browse files
committed
add self-review comments
1 parent 0a635f5 commit d28250c

File tree

18 files changed

+106
-83
lines changed

18 files changed

+106
-83
lines changed

TODO.md

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ Ordered roughly in priority:
66
- Add more real world payloads
77
- Symbol pool for class names
88
- General cleanup of weird/disgusting code areas
9+
- Address all FIXME/TODOs
10+
- Write Contributing guide
911

1012
- Make website tool prettier
1113
- Throttle website tool/prepare for HN

cmdline/src/main/kotlin/com/fractalwrench/json2kotlin/App.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ fun main(args: Array<String>) {
2222

2323
if (inputFile.exists()) {
2424
val outputFile = findOutputFile(inputFile)
25-
KotlinJsonConverter().convert(inputFile.readText(), outputFile.outputStream(), ConversionArgs())
25+
Kotlin2JsonConverter().convert(inputFile.readText(), outputFile.outputStream(), ConversionArgs())
2626
println("Generated source available at '$outputFile'")
2727
} else {
2828
println("Failed to find file '$inputFile'")

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

+7-7
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import com.squareup.kotlinpoet.*
44
import java.util.*
55

66

7-
internal class ClassTypeHolder(val delegate: SourceBuildDelegate) : TraversalDelegate {
7+
internal class ClassTypeHolder(val delegate: SourceBuildDelegate) : TraversalDelegate { // TODO rename, bad ontology
88

99
internal val stack = Stack<TypeSpec>()
1010
private val jsonProcessor = JsonProcessor()
@@ -13,9 +13,9 @@ internal class ClassTypeHolder(val delegate: SourceBuildDelegate) : TraversalDel
1313
/**
1414
* Processes a single level in the tree
1515
*/
16-
override fun processTreeLevel(levelQueue: LinkedList<TypedJsonElement>) {
16+
override fun processTreeLevel(levelQueue: LinkedList<TypedJsonElement>) { // FIXME not ll, generify?
1717
val fieldValues = levelQueue.filter { it.isJsonObject }.toMutableList()
18-
fieldValues.forEach { println(it) }
18+
fieldValues.forEach { println(it) } // TODO add verbose flag to config, default to false
1919

2020
jsonFieldGrouper.groupCommonFieldValues(fieldValues)
2121
.flatMap { convertFieldsToTypes(it) }
@@ -40,17 +40,17 @@ internal class ClassTypeHolder(val delegate: SourceBuildDelegate) : TraversalDel
4040

4141
return commonElements.filterNot { // reuse any types which already exist in the map
4242
val containsValue = jsonProcessor.jsonElementMap.containsValue(classType)
43-
jsonProcessor.jsonElementMap.put(it.jsonElement, classType) // FIXME weird
43+
jsonProcessor.jsonElementMap.put(it.jsonElement, classType) // FIXME feels weird
4444
containsValue
4545
}.map { classType }
4646
}
4747

4848
private fun buildClass(commonElements: List<TypedJsonElement>, fields: Collection<String>): TypeSpec.Builder {
49-
val identifier = commonElements.last().kotlinIdentifier
49+
val identifier = commonElements.last().kotlinIdentifier // FIXME should only pass in one if that's all that's needed!
5050
val classBuilder = TypeSpec.classBuilder(identifier.capitalize()) // FIXME check symbol pool!
5151
val constructor = FunSpec.constructorBuilder()
5252

53-
if (fields.isEmpty()) {
53+
if (fields.isEmpty()) { // FIXME misses delegate!
5454
return classBuilder
5555
}
5656

@@ -67,7 +67,7 @@ internal class ClassTypeHolder(val delegate: SourceBuildDelegate) : TraversalDel
6767
val sanitisedName = field.toKotlinIdentifier() // FIXME should be done before this
6868
val typeName = fieldTypeMap[field]
6969
val initializer = PropertySpec.builder(sanitisedName, typeName!!).initializer(sanitisedName)
70-
delegate.prepareClassProperty(initializer, sanitisedName, field) // FIXME pass in original name
70+
delegate.prepareClassProperty(initializer, sanitisedName, field)
7171
classBuilder.addProperty(initializer.build())
7272
constructor.addParameter(sanitisedName, typeName)
7373
}
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
package com.fractalwrench.json2kotlin
22

3-
class ConversionArgs(val rootClassName: String = "Example")
3+
class ConversionArgs(val rootClassName: String = "Example") // TODO support a few other options (package name, build delegate)

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

+5
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ import com.squareup.kotlinpoet.TypeSpec
77

88
class GsonBuildDelegate: SourceBuildDelegate {
99

10+
// TODO pattern compilation
11+
// FIXME pattern super hacky
12+
// TODO should pass TypedJsonElement (and as much info as possible,
13+
// maybe in a wrapper class e.g. PropBuildParams/ClassBuildParams)
14+
1015
override fun prepareClassProperty(propertyBuilder: PropertySpec.Builder,
1116
kotlinIdentifier: String,
1217
jsonKey: String?) {

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

+3-1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ internal class JsonFieldGrouper {
3535
}
3636
}
3737

38+
// TODO this should be exposed as a constructor parameter, will allow greater extensibility
39+
3840
/**
3941
* Determines whether two JSON Objects on the same level of a JSON tree share the same class type.
4042
*
@@ -52,5 +54,5 @@ internal class JsonFieldGrouper {
5254
val keySize = if (lhsKeys.size > rhsKeys.size) lhsKeys.size else rhsKeys.size
5355
val commonKeyCount = if (emptyClasses) 1 else lhsKeys.intersect(rhsKeys).size
5456
return (commonKeyCount * 5) >= keySize // at least a fifth of keys must match
55-
}
57+
} // FIXME should also consider relative size of objects (if lhs has 99 keys, and rhs has 1 key, then they shouldn't match)
5658
}

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

+58
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,66 @@ package com.fractalwrench.json2kotlin
22

33
import com.google.gson.JsonElement
44

5+
// TODO move to a symbol pool class (or equivalent)
6+
57
internal fun nameForArrayField(sanitisedName: String) = "${sanitisedName}Array"
68

79
internal fun nameForObjectInArray(it: IndexedValue<JsonElement>, sanitisedName: String): String {
810
return if (it.index > 0) "$sanitisedName${it.index + 1}" else sanitisedName
911
}
12+
13+
14+
// TODO move to a symbol pool class (or equivalent)
15+
16+
// FIXME pattern compilation
17+
18+
fun String.standardiseNewline(): String {
19+
return this.replace("\r\n", "\n")
20+
}
21+
22+
fun String.toKotlinIdentifier(): String {
23+
return when {
24+
KEYWORDS.contains(this) -> "`$this`" // escape
25+
else -> {
26+
val sanitisedOutput = this.replace("[^0-9A-Za-z_]+".toRegex(), "_")
27+
val regex = "^[^A-Za-z_].*".toRegex()
28+
29+
when {
30+
regex.matches(sanitisedOutput) -> "_$sanitisedOutput"
31+
else -> sanitisedOutput
32+
}
33+
}
34+
}
35+
}
36+
37+
private val KEYWORDS = listOf(
38+
"as",
39+
"as?",
40+
"break",
41+
"class",
42+
"continue",
43+
"do",
44+
"else",
45+
"false",
46+
"for",
47+
"fun",
48+
"if",
49+
"in",
50+
"!in",
51+
"interface",
52+
"is",
53+
"!is",
54+
"null",
55+
"object",
56+
"package",
57+
"return",
58+
"super",
59+
"this",
60+
"throw",
61+
"try",
62+
"typealias",
63+
"val",
64+
"var",
65+
"when",
66+
"while"
67+
)

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

+9-2
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,11 @@ import java.util.HashMap
99
import java.util.HashSet
1010

1111

12-
internal class JsonProcessor {
12+
internal class JsonProcessor { // TODO crappy name
1313

14-
internal val jsonElementMap = HashMap<JsonElement, TypeSpec>()
14+
internal val jsonElementMap = HashMap<JsonElement, TypeSpec>() // FIXME feels wrong having this exposed
1515

16+
// FIXME should take TypedJsonElement rather than String as a param!
1617
fun findDistinctTypesForFields(fields: Collection<String>,
1718
commonElements: List<TypedJsonElement>): Map<String, TypeName> {
1819
val fieldMap = HashMap<String, TypeName>()
@@ -31,6 +32,12 @@ internal class JsonProcessor {
3132
}.distinct()
3233
}
3334

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+
3441
private fun typeForJsonField(jsonElement: JsonElement, key: String): TypeName {
3542
with(jsonElement) {
3643
return when {

core/src/main/kotlin/com/fractalwrench/json2kotlin/KotlinJsonConverter.kt renamed to core/src/main/kotlin/com/fractalwrench/json2kotlin/Kotlin2JsonConverter.kt

+6-3
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,12 @@ import java.io.OutputStream
66
/**
77
* Converts JSON to Kotlin
88
*/
9-
class KotlinJsonConverter {
9+
class Kotlin2JsonConverter(buildDelegate: SourceBuildDelegate = GsonBuildDelegate()) {
10+
11+
// TODO (general: update KDocs!)
12+
13+
// TODO expose grouping class as a parameter, add to config
1014

11-
private val buildDelegate: SourceBuildDelegate = GsonBuildDelegate()
1215
private val jsonReader = JsonReader(JsonParser())
1316
private val sourceFileWriter = SourceFileWriter()
1417
private val typeHolder = ClassTypeHolder(buildDelegate)
@@ -17,7 +20,7 @@ class KotlinJsonConverter {
1720
/**
1821
* Converts a JSON string to Kotlin, writing it to the OutputStream.
1922
*/
20-
fun convert(input: String, output: OutputStream, args: ConversionArgs) {
23+
fun convert(input: String, output: OutputStream, args: ConversionArgs) { // FIXME should take an InputStream as input
2124
try {
2225
if (input.isEmpty()) {
2326
throw IllegalArgumentException("Json input empty")

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

+4-4
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,13 @@ internal class ReverseJsonTreeTraverser(delegate: TraversalDelegate) : Traversal
1313

1414
private val bfsStack: Stack<TypedJsonElement> = Stack()
1515

16-
fun traverse(element: JsonElement, rootName: String) {
16+
fun traverse(element: JsonElement, rootName: String) { // TODO should return a stack instead and not use a delegate
1717
buildStack(element, rootName)
1818
validateStackOrder()
1919
processQueue()
2020
}
2121

22-
private fun validateStackOrder() {
22+
private fun validateStackOrder() { // TODO add to a test instead (perf)
2323
var maxLevel = 0
2424
bfsStack.forEach {
2525
if (maxLevel > it.level) {
@@ -56,7 +56,7 @@ internal class ReverseJsonTreeTraverser(delegate: TraversalDelegate) : Traversal
5656
* Processes JSON nodes in a reverse level order traversal,
5757
* by building class types for each level of the tree.
5858
*/
59-
private fun processQueue() {
59+
private fun processQueue() { // TODO split into two separate classes, as separate responsibilities.
6060
var level = -1
6161
val levelQueue = LinkedList<TypedJsonElement>()
6262

@@ -89,7 +89,7 @@ internal class ReverseJsonTreeTraverser(delegate: TraversalDelegate) : Traversal
8989
}
9090

9191
private fun nameForArrayField(index: Int, identifier: String): String =
92-
if (index == 0) identifier else "$identifier${index + 1}"
92+
if (index == 0) identifier else "$identifier${index + 1}" // FIXME duplication
9393

9494
private fun shouldAddToStack(element: JsonElement) = element.isJsonArray || element.isJsonObject
9595

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package com.fractalwrench.json2kotlin
33
import com.squareup.kotlinpoet.PropertySpec
44
import com.squareup.kotlinpoet.TypeSpec
55

6-
interface SourceBuildDelegate {
6+
interface SourceBuildDelegate { // FIXME ensure names are correct, as this will be public API!
77
fun prepareClassProperty(propertyBuilder: PropertySpec.Builder,
88
kotlinIdentifier: String,
99
jsonKey: String?)

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

+3-3
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,19 @@ import java.util.*
88
/**
99
* Writes a collection of types to a source file OutputStream.
1010
*/
11-
internal class SourceFileWriter {
11+
internal class SourceFileWriter { // TODO rename once functionality has changed
1212

1313
/**
1414
* Writes a collection of types to a source file OutputStream.
1515
*/
1616
fun writeSourceFile(stack: Stack<TypeSpec>, args: ConversionArgs, output: OutputStream) {
17-
val sourceFile = FileSpec.builder("", args.rootClassName)
17+
val sourceFile = FileSpec.builder("", args.rootClassName) // FIXME set package name
1818

1919
while (stack.isNotEmpty()) {
2020
sourceFile.addType(stack.pop())
2121
}
2222

23-
with(StringBuilder()) {
23+
with(StringBuilder()) { // FIXME inefficient use of resources
2424
sourceFile.build().writeTo(this)
2525
output.write(toString().toByteArray())
2626
}

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

-53
This file was deleted.

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@ package com.fractalwrench.json2kotlin
22

33
import java.util.*
44

5-
interface TraversalDelegate {
5+
interface TraversalDelegate { // TODO remove
66
fun processTreeLevel(levelQueue: LinkedList<TypedJsonElement>)
77
}

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ import com.google.gson.*
44
import java.math.BigDecimal
55
import java.math.BigInteger
66

7-
class TypedJsonElement : JsonElement {
7+
class TypedJsonElement : JsonElement { // TODO check visibility
88

99
val jsonElement: JsonElement
1010
val jsonKey: String
1111
val level: Int
12-
val kotlinIdentifier: String
12+
val kotlinIdentifier: String // FIXME symbol pooling
1313

1414
constructor(jsonElement: JsonElement, jsonKey: String, level: Int) : super() {
1515
this.jsonElement = jsonElement

core/src/test/kotlin/com/fractalwrench/json2kotlin/InvalidJsonConverterTest.kt

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package com.fractalwrench.json2kotlin
22

3-
import com.google.gson.JsonParser
43
import org.junit.Test
54
import org.junit.runner.RunWith
65
import org.junit.runners.Parameterized
@@ -10,7 +9,7 @@ import java.io.ByteArrayOutputStream
109
class InvalidJsonConverterTest(val jsonFilename: String) {
1110

1211
private val fileReader = ResourceFileReader()
13-
private val jsonConverter = KotlinJsonConverter()
12+
private val jsonConverter = Kotlin2JsonConverter()
1413

1514
companion object {
1615
@JvmStatic

core/src/test/kotlin/com/fractalwrench/json2kotlin/JsonConverterTest.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import java.io.File
1212
open class JsonConverterTest(val expectedFilename: String, val jsonFilename: String) {
1313

1414
private val fileReader = ResourceFileReader()
15-
private val jsonConverter = KotlinJsonConverter()
15+
private val jsonConverter = Kotlin2JsonConverter()
1616
internal lateinit var json: String
1717

1818
@Before

0 commit comments

Comments
 (0)